@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
|
@@ -1,26 +1,66 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// ── Module mocks ─────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
mock.module("../util/logger.js", () => ({
|
|
6
|
+
getLogger: () =>
|
|
7
|
+
new Proxy({} as Record<string, unknown>, {
|
|
8
|
+
get: () => () => {},
|
|
9
|
+
}),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
/** Events published through the mock event hub. */
|
|
13
|
+
let publishedEvents: unknown[] = [];
|
|
14
|
+
let mockHasConnection = true;
|
|
15
|
+
|
|
16
|
+
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
17
|
+
assistantEventHub: {
|
|
18
|
+
publish: async (event: unknown, _options?: unknown) => {
|
|
19
|
+
publishedEvents.push(event);
|
|
20
|
+
},
|
|
21
|
+
getMostRecentClientByCapability: (cap: string) =>
|
|
22
|
+
cap === "host_browser" && mockHasConnection
|
|
23
|
+
? {
|
|
24
|
+
type: "client",
|
|
25
|
+
clientId: "test-client",
|
|
26
|
+
interfaceId: "macos",
|
|
27
|
+
capabilities: ["host_browser"],
|
|
28
|
+
}
|
|
29
|
+
: undefined,
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
mock.module("../runtime/assistant-event.js", () => ({
|
|
34
|
+
buildAssistantEvent: (message: unknown) => ({ message }),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
// ── Real imports (after mocks) ───────────────────────────────────────
|
|
2
38
|
|
|
3
39
|
const { HostBrowserProxy } = await import("../daemon/host-browser-proxy.js");
|
|
4
40
|
|
|
41
|
+
/** Extract the ServerMessage payloads from published events. */
|
|
42
|
+
function getPublishedMessages(): unknown[] {
|
|
43
|
+
return publishedEvents.map((e) => (e as { message: unknown }).message);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
5
48
|
describe("HostBrowserProxy", () => {
|
|
6
49
|
let proxy: InstanceType<typeof HostBrowserProxy>;
|
|
7
|
-
let sentMessages: unknown[];
|
|
8
|
-
let sendToClient: (msg: unknown) => void;
|
|
9
50
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
HostBrowserProxy.reset();
|
|
53
|
+
publishedEvents = [];
|
|
54
|
+
mockHasConnection = true;
|
|
55
|
+
proxy = HostBrowserProxy.instance;
|
|
56
|
+
});
|
|
15
57
|
|
|
16
58
|
afterEach(() => {
|
|
17
|
-
|
|
59
|
+
HostBrowserProxy.reset();
|
|
18
60
|
});
|
|
19
61
|
|
|
20
62
|
describe("request/resolve lifecycle (happy path)", () => {
|
|
21
63
|
test("sends host_browser_request and resolves with content", async () => {
|
|
22
|
-
setup();
|
|
23
|
-
|
|
24
64
|
const resultPromise = proxy.request(
|
|
25
65
|
{
|
|
26
66
|
cdpMethod: "Page.navigate",
|
|
@@ -29,9 +69,8 @@ describe("HostBrowserProxy", () => {
|
|
|
29
69
|
"session-1",
|
|
30
70
|
);
|
|
31
71
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const sent = sentMessages[0] as Record<string, unknown>;
|
|
72
|
+
expect(getPublishedMessages()).toHaveLength(1);
|
|
73
|
+
const sent = getPublishedMessages()[0] as Record<string, unknown>;
|
|
35
74
|
expect(sent.type).toBe("host_browser_request");
|
|
36
75
|
expect(sent.conversationId).toBe("session-1");
|
|
37
76
|
expect(sent.cdpMethod).toBe("Page.navigate");
|
|
@@ -41,11 +80,7 @@ describe("HostBrowserProxy", () => {
|
|
|
41
80
|
const requestId = sent.requestId as string;
|
|
42
81
|
expect(proxy.hasPendingRequest(requestId)).toBe(true);
|
|
43
82
|
|
|
44
|
-
|
|
45
|
-
proxy.resolve(requestId, {
|
|
46
|
-
content: "ok",
|
|
47
|
-
isError: false,
|
|
48
|
-
});
|
|
83
|
+
proxy.resolve(requestId, { content: "ok", isError: false });
|
|
49
84
|
|
|
50
85
|
const result = await resultPromise;
|
|
51
86
|
expect(result.content).toBe("ok");
|
|
@@ -54,8 +89,6 @@ describe("HostBrowserProxy", () => {
|
|
|
54
89
|
});
|
|
55
90
|
|
|
56
91
|
test("forwards cdpParams and cdpSessionId on the emitted envelope", async () => {
|
|
57
|
-
setup();
|
|
58
|
-
|
|
59
92
|
const resultPromise = proxy.request(
|
|
60
93
|
{
|
|
61
94
|
cdpMethod: "Runtime.evaluate",
|
|
@@ -65,8 +98,8 @@ describe("HostBrowserProxy", () => {
|
|
|
65
98
|
"session-1",
|
|
66
99
|
);
|
|
67
100
|
|
|
68
|
-
expect(
|
|
69
|
-
const sent =
|
|
101
|
+
expect(getPublishedMessages()).toHaveLength(1);
|
|
102
|
+
const sent = getPublishedMessages()[0] as Record<string, unknown>;
|
|
70
103
|
expect(sent.type).toBe("host_browser_request");
|
|
71
104
|
expect(sent.cdpMethod).toBe("Runtime.evaluate");
|
|
72
105
|
expect(sent.cdpParams).toEqual({
|
|
@@ -75,8 +108,7 @@ describe("HostBrowserProxy", () => {
|
|
|
75
108
|
});
|
|
76
109
|
expect(sent.cdpSessionId).toBe("session-abc");
|
|
77
110
|
|
|
78
|
-
|
|
79
|
-
proxy.resolve(requestId, {
|
|
111
|
+
proxy.resolve(sent.requestId as string, {
|
|
80
112
|
content: "Example Domain",
|
|
81
113
|
isError: false,
|
|
82
114
|
});
|
|
@@ -85,17 +117,13 @@ describe("HostBrowserProxy", () => {
|
|
|
85
117
|
});
|
|
86
118
|
|
|
87
119
|
test("resolves error responses correctly", async () => {
|
|
88
|
-
setup();
|
|
89
|
-
|
|
90
120
|
const resultPromise = proxy.request(
|
|
91
121
|
{ cdpMethod: "Page.navigate", cdpParams: { url: "invalid://" } },
|
|
92
122
|
"session-1",
|
|
93
123
|
);
|
|
94
124
|
|
|
95
|
-
const sent =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
proxy.resolve(requestId, {
|
|
125
|
+
const sent = getPublishedMessages()[0] as Record<string, unknown>;
|
|
126
|
+
proxy.resolve(sent.requestId as string, {
|
|
99
127
|
content: "Navigation failed",
|
|
100
128
|
isError: true,
|
|
101
129
|
});
|
|
@@ -108,14 +136,12 @@ describe("HostBrowserProxy", () => {
|
|
|
108
136
|
|
|
109
137
|
describe("pending tracking", () => {
|
|
110
138
|
test("hasPendingRequest returns true after request and false after resolve", async () => {
|
|
111
|
-
setup();
|
|
112
|
-
|
|
113
139
|
const resultPromise = proxy.request(
|
|
114
140
|
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
|
|
115
141
|
"session-1",
|
|
116
142
|
);
|
|
117
143
|
|
|
118
|
-
const sent =
|
|
144
|
+
const sent = getPublishedMessages()[0] as Record<string, unknown>;
|
|
119
145
|
const requestId = sent.requestId as string;
|
|
120
146
|
expect(proxy.hasPendingRequest(requestId)).toBe(true);
|
|
121
147
|
|
|
@@ -128,38 +154,30 @@ describe("HostBrowserProxy", () => {
|
|
|
128
154
|
|
|
129
155
|
describe("timeout", () => {
|
|
130
156
|
test("resolves with timeout error when proxy timeout fires", async () => {
|
|
131
|
-
const resolvedIds: string[] = [];
|
|
132
|
-
setup((id) => resolvedIds.push(id));
|
|
133
|
-
|
|
134
157
|
const resultPromise = proxy.request(
|
|
135
158
|
{
|
|
136
159
|
cdpMethod: "Page.navigate",
|
|
137
160
|
cdpParams: { url: "https://slow.test" },
|
|
138
|
-
// Sub-second timeout to trigger the timer quickly.
|
|
139
161
|
timeout_seconds: 0.01,
|
|
140
162
|
},
|
|
141
163
|
"session-1",
|
|
142
164
|
);
|
|
143
165
|
|
|
144
|
-
const sent =
|
|
166
|
+
const sent = getPublishedMessages()[0] as Record<string, unknown>;
|
|
145
167
|
const requestId = sent.requestId as string;
|
|
146
168
|
expect(proxy.hasPendingRequest(requestId)).toBe(true);
|
|
147
169
|
|
|
148
|
-
// Wait long enough for the timer (10ms) to fire.
|
|
149
170
|
await new Promise((r) => setTimeout(r, 50));
|
|
150
171
|
|
|
151
172
|
const result = await resultPromise;
|
|
152
173
|
expect(result.isError).toBe(true);
|
|
153
174
|
expect(result.content).toContain("Host browser proxy timed out");
|
|
154
175
|
expect(proxy.hasPendingRequest(requestId)).toBe(false);
|
|
155
|
-
expect(resolvedIds).toEqual([requestId]);
|
|
156
176
|
});
|
|
157
177
|
});
|
|
158
178
|
|
|
159
179
|
describe("abort signal", () => {
|
|
160
180
|
test("returns immediately if signal already aborted", async () => {
|
|
161
|
-
setup();
|
|
162
|
-
|
|
163
181
|
const controller = new AbortController();
|
|
164
182
|
controller.abort();
|
|
165
183
|
|
|
@@ -171,13 +189,10 @@ describe("HostBrowserProxy", () => {
|
|
|
171
189
|
|
|
172
190
|
expect(result.content).toBe("Aborted");
|
|
173
191
|
expect(result.isError).toBe(true);
|
|
174
|
-
expect(
|
|
192
|
+
expect(getPublishedMessages()).toHaveLength(0);
|
|
175
193
|
});
|
|
176
194
|
|
|
177
195
|
test("mid-flight abort resolves with Aborted and emits host_browser_cancel", async () => {
|
|
178
|
-
const resolvedIds: string[] = [];
|
|
179
|
-
setup((id) => resolvedIds.push(id));
|
|
180
|
-
|
|
181
196
|
const controller = new AbortController();
|
|
182
197
|
const resultPromise = proxy.request(
|
|
183
198
|
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
|
|
@@ -185,7 +200,7 @@ describe("HostBrowserProxy", () => {
|
|
|
185
200
|
controller.signal,
|
|
186
201
|
);
|
|
187
202
|
|
|
188
|
-
const sent =
|
|
203
|
+
const sent = getPublishedMessages()[0] as Record<string, unknown>;
|
|
189
204
|
const requestId = sent.requestId as string;
|
|
190
205
|
expect(proxy.hasPendingRequest(requestId)).toBe(true);
|
|
191
206
|
|
|
@@ -196,68 +211,28 @@ describe("HostBrowserProxy", () => {
|
|
|
196
211
|
expect(result.isError).toBe(true);
|
|
197
212
|
expect(proxy.hasPendingRequest(requestId)).toBe(false);
|
|
198
213
|
|
|
199
|
-
//
|
|
200
|
-
expect(
|
|
201
|
-
const cancelMsg =
|
|
214
|
+
// Cancel envelope should have been sent.
|
|
215
|
+
expect(getPublishedMessages()).toHaveLength(2);
|
|
216
|
+
const cancelMsg = getPublishedMessages()[1] as Record<string, unknown>;
|
|
202
217
|
expect(cancelMsg.type).toBe("host_browser_cancel");
|
|
203
218
|
expect(cancelMsg.requestId).toBe(requestId);
|
|
204
|
-
|
|
205
|
-
// onInternalResolve should have been invoked.
|
|
206
|
-
expect(resolvedIds).toEqual([requestId]);
|
|
207
219
|
});
|
|
208
220
|
});
|
|
209
221
|
|
|
210
222
|
describe("isAvailable", () => {
|
|
211
|
-
test("returns
|
|
212
|
-
|
|
213
|
-
expect(proxy.isAvailable()).toBe(false);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
test("returns true after updateSender with clientConnected=true", () => {
|
|
217
|
-
setup();
|
|
218
|
-
proxy.updateSender(sendToClient, true);
|
|
223
|
+
test("returns true when a connection exists in the registry", () => {
|
|
224
|
+
mockHasConnection = true;
|
|
219
225
|
expect(proxy.isAvailable()).toBe(true);
|
|
220
226
|
});
|
|
221
227
|
|
|
222
|
-
test("returns false
|
|
223
|
-
|
|
224
|
-
proxy.updateSender(sendToClient, true);
|
|
225
|
-
expect(proxy.isAvailable()).toBe(true);
|
|
226
|
-
proxy.updateSender(sendToClient, false);
|
|
228
|
+
test("returns false when no connection exists", () => {
|
|
229
|
+
mockHasConnection = false;
|
|
227
230
|
expect(proxy.isAvailable()).toBe(false);
|
|
228
231
|
});
|
|
229
232
|
});
|
|
230
233
|
|
|
231
|
-
describe("updateSender", () => {
|
|
232
|
-
test("uses updated sender for new requests", async () => {
|
|
233
|
-
setup();
|
|
234
|
-
|
|
235
|
-
const newMessages: unknown[] = [];
|
|
236
|
-
proxy.updateSender((msg) => newMessages.push(msg), true);
|
|
237
|
-
|
|
238
|
-
const resultPromise = proxy.request(
|
|
239
|
-
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
|
|
240
|
-
"session-1",
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
expect(sentMessages).toHaveLength(0); // Old sender not used.
|
|
244
|
-
expect(newMessages).toHaveLength(1); // New sender used.
|
|
245
|
-
|
|
246
|
-
const sent = newMessages[0] as Record<string, unknown>;
|
|
247
|
-
proxy.resolve(sent.requestId as string, {
|
|
248
|
-
content: "ok",
|
|
249
|
-
isError: false,
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
await resultPromise;
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
234
|
describe("dispose", () => {
|
|
257
|
-
test("rejects all pending requests
|
|
258
|
-
const resolvedIds: string[] = [];
|
|
259
|
-
setup((id) => resolvedIds.push(id));
|
|
260
|
-
|
|
235
|
+
test("rejects all pending requests and emits cancels", async () => {
|
|
261
236
|
const p1 = proxy.request(
|
|
262
237
|
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
|
|
263
238
|
"session-1",
|
|
@@ -266,63 +241,70 @@ describe("HostBrowserProxy", () => {
|
|
|
266
241
|
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://b.test" } },
|
|
267
242
|
"session-1",
|
|
268
243
|
);
|
|
269
|
-
// Attach rejection handlers immediately so Bun doesn't flag the
|
|
270
|
-
// promises as unhandled before the awaited assertions run.
|
|
271
244
|
const p1Swallowed = p1.catch(() => {});
|
|
272
245
|
const p2Swallowed = p2.catch(() => {});
|
|
273
246
|
|
|
274
|
-
const requestIds = (
|
|
275
|
-
(
|
|
276
|
-
);
|
|
247
|
+
const requestIds = (
|
|
248
|
+
getPublishedMessages() as Array<Record<string, unknown>>
|
|
249
|
+
).map((m) => m.requestId as string);
|
|
277
250
|
expect(requestIds).toHaveLength(2);
|
|
278
|
-
expect(proxy.hasPendingRequest(requestIds[0]!)).toBe(true);
|
|
279
|
-
expect(proxy.hasPendingRequest(requestIds[1]!)).toBe(true);
|
|
280
251
|
|
|
281
252
|
proxy.dispose();
|
|
282
253
|
|
|
283
|
-
// Both pending requests should no longer be tracked.
|
|
284
254
|
expect(proxy.hasPendingRequest(requestIds[0]!)).toBe(false);
|
|
285
255
|
expect(proxy.hasPendingRequest(requestIds[1]!)).toBe(false);
|
|
286
256
|
|
|
287
|
-
// Both promises should reject with AssistantError message.
|
|
288
257
|
await expect(p1).rejects.toThrow("Host browser proxy disposed");
|
|
289
258
|
await expect(p2).rejects.toThrow("Host browser proxy disposed");
|
|
290
|
-
// Drain the swallowed copies so the unhandled-rejection guard clears.
|
|
291
259
|
await p1Swallowed;
|
|
292
260
|
await p2Swallowed;
|
|
293
261
|
|
|
294
|
-
|
|
295
|
-
const cancelMessages = sentMessages
|
|
262
|
+
const cancelMessages = getPublishedMessages()
|
|
296
263
|
.slice(2)
|
|
297
264
|
.filter(
|
|
298
265
|
(m) => (m as Record<string, unknown>).type === "host_browser_cancel",
|
|
299
266
|
) as Array<Record<string, unknown>>;
|
|
300
267
|
expect(cancelMessages).toHaveLength(2);
|
|
301
|
-
expect(cancelMessages.map((m) => m.requestId)).toContain(requestIds[0]);
|
|
302
|
-
expect(cancelMessages.map((m) => m.requestId)).toContain(requestIds[1]);
|
|
303
|
-
|
|
304
|
-
// onInternalResolve fired for each pending request on dispose.
|
|
305
|
-
expect(resolvedIds).toHaveLength(2);
|
|
306
|
-
expect(resolvedIds).toContain(requestIds[0]!);
|
|
307
|
-
expect(resolvedIds).toContain(requestIds[1]!);
|
|
308
268
|
});
|
|
309
269
|
});
|
|
310
270
|
|
|
311
271
|
describe("resolve with unknown requestId", () => {
|
|
312
272
|
test("silently ignores unknown requestId", () => {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
273
|
+
proxy.resolve("nonexistent", { content: "stale", isError: false });
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe("send failure", () => {
|
|
278
|
+
test("rejects when no connection exists at send time", async () => {
|
|
279
|
+
mockHasConnection = false;
|
|
280
|
+
|
|
281
|
+
const resultPromise = proxy.request(
|
|
282
|
+
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://x.test" } },
|
|
283
|
+
"session-1",
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
await expect(resultPromise).rejects.toThrow(
|
|
287
|
+
"no active extension connection",
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("singleton", () => {
|
|
293
|
+
test("instance always returns the same proxy", () => {
|
|
294
|
+
const a = HostBrowserProxy.instance;
|
|
295
|
+
const b = HostBrowserProxy.instance;
|
|
296
|
+
expect(a).toBe(b);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("reset() clears the singleton", () => {
|
|
300
|
+
const before = HostBrowserProxy.instance;
|
|
301
|
+
HostBrowserProxy.reset();
|
|
302
|
+
const after = HostBrowserProxy.instance;
|
|
303
|
+
expect(before).not.toBe(after);
|
|
319
304
|
});
|
|
320
305
|
});
|
|
321
306
|
|
|
322
307
|
describe("abort listener lifecycle", () => {
|
|
323
|
-
// Helper that wraps an AbortSignal to observe add/removeEventListener
|
|
324
|
-
// invocations without tripping over tsc's strict overload matching on
|
|
325
|
-
// AbortSignal itself.
|
|
326
308
|
type Spied = {
|
|
327
309
|
signal: AbortSignal;
|
|
328
310
|
addCalls: string[];
|
|
@@ -334,17 +316,11 @@ describe("HostBrowserProxy", () => {
|
|
|
334
316
|
const s = source as any;
|
|
335
317
|
const origAdd = source.addEventListener.bind(source);
|
|
336
318
|
const origRemove = source.removeEventListener.bind(source);
|
|
337
|
-
s.addEventListener = (
|
|
338
|
-
type: string,
|
|
339
|
-
...rest: any[]
|
|
340
|
-
) => {
|
|
319
|
+
s.addEventListener = (type: string, ...rest: any[]) => {
|
|
341
320
|
addCalls.push(type);
|
|
342
321
|
return (origAdd as any)(type, ...rest);
|
|
343
322
|
};
|
|
344
|
-
s.removeEventListener = (
|
|
345
|
-
type: string,
|
|
346
|
-
...rest: any[]
|
|
347
|
-
) => {
|
|
323
|
+
s.removeEventListener = (type: string, ...rest: any[]) => {
|
|
348
324
|
removeCalls.push(type);
|
|
349
325
|
return (origRemove as any)(type, ...rest);
|
|
350
326
|
};
|
|
@@ -352,7 +328,6 @@ describe("HostBrowserProxy", () => {
|
|
|
352
328
|
}
|
|
353
329
|
|
|
354
330
|
test("removes abort listener from signal after resolve completes", async () => {
|
|
355
|
-
setup();
|
|
356
331
|
const controller = new AbortController();
|
|
357
332
|
const spy = spySignal(controller.signal);
|
|
358
333
|
|
|
@@ -365,22 +340,18 @@ describe("HostBrowserProxy", () => {
|
|
|
365
340
|
expect(spy.addCalls).toEqual(["abort"]);
|
|
366
341
|
expect(spy.removeCalls).toEqual([]);
|
|
367
342
|
|
|
368
|
-
const requestId = (
|
|
343
|
+
const requestId = (getPublishedMessages()[0] as Record<string, unknown>)
|
|
369
344
|
.requestId as string;
|
|
370
345
|
proxy.resolve(requestId, { content: "ok", isError: false });
|
|
371
346
|
await resultPromise;
|
|
372
347
|
|
|
373
|
-
// Listener is detached after normal completion.
|
|
374
348
|
expect(spy.removeCalls).toEqual(["abort"]);
|
|
375
349
|
|
|
376
|
-
// Subsequent aborts are harmless no-ops (no side effects on the proxy).
|
|
377
350
|
controller.abort();
|
|
378
|
-
|
|
379
|
-
expect(sentMessages).toHaveLength(1);
|
|
351
|
+
expect(getPublishedMessages()).toHaveLength(1);
|
|
380
352
|
});
|
|
381
353
|
|
|
382
354
|
test("removes abort listener from signal after dispose", () => {
|
|
383
|
-
setup();
|
|
384
355
|
const controller = new AbortController();
|
|
385
356
|
const spy = spySignal(controller.signal);
|
|
386
357
|
|
|
@@ -389,56 +360,11 @@ describe("HostBrowserProxy", () => {
|
|
|
389
360
|
"session-1",
|
|
390
361
|
spy.signal,
|
|
391
362
|
);
|
|
392
|
-
p.catch(() => {});
|
|
363
|
+
p.catch(() => {});
|
|
393
364
|
|
|
394
365
|
proxy.dispose();
|
|
395
366
|
|
|
396
367
|
expect(spy.removeCalls).toEqual(["abort"]);
|
|
397
368
|
});
|
|
398
369
|
});
|
|
399
|
-
|
|
400
|
-
describe("sender throws synchronously", () => {
|
|
401
|
-
test("rejects the promise, clears pending state and timer, invokes onInternalResolve", async () => {
|
|
402
|
-
const resolvedIds: string[] = [];
|
|
403
|
-
sentMessages = [];
|
|
404
|
-
sendToClient = () => {
|
|
405
|
-
throw new Error("transport down");
|
|
406
|
-
};
|
|
407
|
-
proxy = new HostBrowserProxy(sendToClient, (id) => resolvedIds.push(id));
|
|
408
|
-
|
|
409
|
-
// request() synchronously calls sendToClient inside the Promise
|
|
410
|
-
// executor. A throw there surfaces as a rejected promise.
|
|
411
|
-
const resultPromise = proxy.request(
|
|
412
|
-
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://x.test" } },
|
|
413
|
-
"session-1",
|
|
414
|
-
);
|
|
415
|
-
|
|
416
|
-
await expect(resultPromise).rejects.toThrow("transport down");
|
|
417
|
-
|
|
418
|
-
// No entries should have leaked into the pending map.
|
|
419
|
-
// (We can't assert against a specific requestId because the sender
|
|
420
|
-
// threw before any message was observed, so there's nothing to read
|
|
421
|
-
// the id from. We can instead assert the internal resolve fired once
|
|
422
|
-
// and that no pending entries remain for any id we issue next.)
|
|
423
|
-
expect(resolvedIds).toHaveLength(1);
|
|
424
|
-
|
|
425
|
-
// Issue a new request on a fresh (non-throwing) sender and verify
|
|
426
|
-
// the proxy is still functional — no stale timers or bookkeeping
|
|
427
|
-
// from the failed request.
|
|
428
|
-
sentMessages = [];
|
|
429
|
-
proxy.updateSender((msg) => sentMessages.push(msg), true);
|
|
430
|
-
const okPromise = proxy.request(
|
|
431
|
-
{ cdpMethod: "Page.reload" },
|
|
432
|
-
"session-1",
|
|
433
|
-
);
|
|
434
|
-
expect(sentMessages).toHaveLength(1);
|
|
435
|
-
const okRequestId = (sentMessages[0] as Record<string, unknown>)
|
|
436
|
-
.requestId as string;
|
|
437
|
-
expect(proxy.hasPendingRequest(okRequestId)).toBe(true);
|
|
438
|
-
proxy.resolve(okRequestId, { content: "reloaded", isError: false });
|
|
439
|
-
const okResult = await okPromise;
|
|
440
|
-
expect(okResult.content).toBe("reloaded");
|
|
441
|
-
expect(okResult.isError).toBe(false);
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
370
|
});
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unit tests for the /v1/host-browser-result route handler.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Resolution goes through HostBrowserProxy.instance (singleton). The
|
|
5
|
+
* mock below controls the proxy's pending request map and resolve spy.
|
|
6
6
|
*/
|
|
7
7
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
8
8
|
|
|
9
|
-
import type { Conversation } from "../daemon/conversation.js";
|
|
10
|
-
|
|
11
9
|
// ── Module mocks ─────────────────────────────────────────────────────
|
|
12
10
|
|
|
13
11
|
mock.module("../config/env.js", () => ({
|
|
@@ -15,10 +13,36 @@ mock.module("../config/env.js", () => ({
|
|
|
15
13
|
hasUngatedHttpAuthDisabled: () => false,
|
|
16
14
|
}));
|
|
17
15
|
|
|
16
|
+
interface ResolveCall {
|
|
17
|
+
requestId: string;
|
|
18
|
+
response: { content: string; isError: boolean };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const resolveSpy: ResolveCall[] = [];
|
|
22
|
+
const pendingRequests = new Set<string>();
|
|
23
|
+
|
|
24
|
+
mock.module("../daemon/host-browser-proxy.js", () => ({
|
|
25
|
+
HostBrowserProxy: {
|
|
26
|
+
get instance() {
|
|
27
|
+
return {
|
|
28
|
+
hasPendingRequest(requestId: string) {
|
|
29
|
+
return pendingRequests.has(requestId);
|
|
30
|
+
},
|
|
31
|
+
resolve(
|
|
32
|
+
requestId: string,
|
|
33
|
+
response: { content: string; isError: boolean },
|
|
34
|
+
) {
|
|
35
|
+
pendingRequests.delete(requestId);
|
|
36
|
+
resolveSpy.push({ requestId, response });
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
|
|
18
43
|
// ── Real imports (after mocks) ───────────────────────────────────────
|
|
19
44
|
|
|
20
|
-
import
|
|
21
|
-
import { BadRequestError, ConflictError, NotFoundError } from "../runtime/routes/errors.js";
|
|
45
|
+
import { BadRequestError, NotFoundError } from "../runtime/routes/errors.js";
|
|
22
46
|
import { ROUTES } from "../runtime/routes/host-browser-routes.js";
|
|
23
47
|
|
|
24
48
|
afterAll(() => {
|
|
@@ -29,53 +53,28 @@ const handleHostBrowserResult = ROUTES.find(
|
|
|
29
53
|
(r) => r.endpoint === "host-browser-result",
|
|
30
54
|
)!.handler;
|
|
31
55
|
|
|
32
|
-
// ── Helpers ──────────────────────────────────────────────────────────
|
|
33
|
-
|
|
34
|
-
interface ResolveHostBrowserCall {
|
|
35
|
-
requestId: string;
|
|
36
|
-
response: { content: string; isError: boolean };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function makeStubConversation(spy: ResolveHostBrowserCall[]): Conversation {
|
|
40
|
-
return {
|
|
41
|
-
resolveHostBrowser(
|
|
42
|
-
requestId: string,
|
|
43
|
-
response: { content: string; isError: boolean },
|
|
44
|
-
) {
|
|
45
|
-
spy.push({ requestId, response });
|
|
46
|
-
},
|
|
47
|
-
} as unknown as Conversation;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
56
|
// ── Tests ────────────────────────────────────────────────────────────
|
|
51
57
|
|
|
52
58
|
describe("handleHostBrowserResult", () => {
|
|
53
59
|
beforeEach(() => {
|
|
54
|
-
|
|
60
|
+
pendingRequests.clear();
|
|
61
|
+
resolveSpy.length = 0;
|
|
55
62
|
});
|
|
56
63
|
|
|
57
|
-
test("happy path: resolves a pending host_browser
|
|
58
|
-
const spy: ResolveHostBrowserCall[] = [];
|
|
59
|
-
const conversation = makeStubConversation(spy);
|
|
64
|
+
test("happy path: resolves a pending host_browser request via singleton", async () => {
|
|
60
65
|
const requestId = "browser-req-happy";
|
|
61
|
-
|
|
62
|
-
pendingInteractions.register(requestId, {
|
|
63
|
-
conversation,
|
|
64
|
-
conversationId: "conv-1",
|
|
65
|
-
kind: "host_browser",
|
|
66
|
-
});
|
|
66
|
+
pendingRequests.add(requestId);
|
|
67
67
|
|
|
68
68
|
const result = await handleHostBrowserResult({
|
|
69
69
|
body: { requestId, content: "ok", isError: false },
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
expect(result).toEqual({ accepted: true });
|
|
73
|
-
expect(
|
|
74
|
-
expect(
|
|
75
|
-
expect(
|
|
73
|
+
expect(resolveSpy).toHaveLength(1);
|
|
74
|
+
expect(resolveSpy[0].requestId).toBe(requestId);
|
|
75
|
+
expect(resolveSpy[0].response).toEqual({ content: "ok", isError: false });
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
expect(pendingInteractions.get(requestId)).toBeUndefined();
|
|
77
|
+
expect(pendingRequests.has(requestId)).toBe(false);
|
|
79
78
|
});
|
|
80
79
|
|
|
81
80
|
test("missing body: throws BadRequestError", () => {
|
|
@@ -83,9 +82,9 @@ describe("handleHostBrowserResult", () => {
|
|
|
83
82
|
});
|
|
84
83
|
|
|
85
84
|
test("missing requestId: throws BadRequestError", () => {
|
|
86
|
-
expect(() =>
|
|
87
|
-
|
|
88
|
-
)
|
|
85
|
+
expect(() => handleHostBrowserResult({ body: { content: "x" } })).toThrow(
|
|
86
|
+
BadRequestError,
|
|
87
|
+
);
|
|
89
88
|
});
|
|
90
89
|
|
|
91
90
|
test("unknown requestId: throws NotFoundError", () => {
|
|
@@ -100,44 +99,15 @@ describe("handleHostBrowserResult", () => {
|
|
|
100
99
|
).toThrow(NotFoundError);
|
|
101
100
|
});
|
|
102
101
|
|
|
103
|
-
test("wrong kind: throws ConflictError with mismatch message", () => {
|
|
104
|
-
const spy: ResolveHostBrowserCall[] = [];
|
|
105
|
-
const conversation = makeStubConversation(spy);
|
|
106
|
-
const requestId = "browser-req-wrong-kind";
|
|
107
|
-
|
|
108
|
-
pendingInteractions.register(requestId, {
|
|
109
|
-
conversation,
|
|
110
|
-
conversationId: "conv-1",
|
|
111
|
-
kind: "host_bash",
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
expect(() =>
|
|
115
|
-
handleHostBrowserResult({
|
|
116
|
-
body: { requestId, content: "x", isError: false },
|
|
117
|
-
}),
|
|
118
|
-
).toThrow(ConflictError);
|
|
119
|
-
|
|
120
|
-
// Pending interaction should NOT have been consumed
|
|
121
|
-
expect(pendingInteractions.get(requestId)).toBeDefined();
|
|
122
|
-
expect(spy).toHaveLength(0);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
102
|
test("defaults: missing content/isError default to '' and false", async () => {
|
|
126
|
-
const spy: ResolveHostBrowserCall[] = [];
|
|
127
|
-
const conversation = makeStubConversation(spy);
|
|
128
103
|
const requestId = "browser-req-defaults";
|
|
129
|
-
|
|
130
|
-
pendingInteractions.register(requestId, {
|
|
131
|
-
conversation,
|
|
132
|
-
conversationId: "conv-1",
|
|
133
|
-
kind: "host_browser",
|
|
134
|
-
});
|
|
104
|
+
pendingRequests.add(requestId);
|
|
135
105
|
|
|
136
106
|
const result = await handleHostBrowserResult({ body: { requestId } });
|
|
137
107
|
|
|
138
108
|
expect(result).toEqual({ accepted: true });
|
|
139
|
-
expect(
|
|
140
|
-
expect(
|
|
141
|
-
expect(
|
|
109
|
+
expect(resolveSpy).toHaveLength(1);
|
|
110
|
+
expect(resolveSpy[0].requestId).toBe(requestId);
|
|
111
|
+
expect(resolveSpy[0].response).toEqual({ content: "", isError: false });
|
|
142
112
|
});
|
|
143
113
|
});
|