@vellumai/assistant 0.7.1 → 0.7.2
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 +32 -49
- package/Dockerfile +1 -0
- package/README.md +1 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
- package/bun.lock +26 -26
- package/docs/architecture/security.md +20 -0
- package/docs/plugins.md +7 -9
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
- package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
- package/node_modules/@vellumai/twilio-client/package.json +18 -0
- package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
- package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
- package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
- package/openapi.yaml +565 -12
- package/package.json +6 -3
- package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
- package/src/__tests__/app-bundler.test.ts +170 -1
- package/src/__tests__/app-control-flow.test.ts +374 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
- package/src/__tests__/app-executors.test.ts +30 -43
- package/src/__tests__/approval-routes-http.test.ts +23 -6
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
- package/src/__tests__/assistant-event-hub.test.ts +109 -2
- package/src/__tests__/assistant-event.test.ts +10 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
- package/src/__tests__/background-shell-host-bash.test.ts +14 -15
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
- package/src/__tests__/btw-routes.test.ts +13 -4
- package/src/__tests__/call-controller.test.ts +49 -1
- package/src/__tests__/call-domain.test.ts +0 -2
- package/src/__tests__/call-routes-http.test.ts +0 -2
- package/src/__tests__/channel-readiness-service.test.ts +59 -1
- package/src/__tests__/checker.test.ts +3 -4
- package/src/__tests__/config-loader-backfill.test.ts +90 -155
- package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-set-platform-guard.test.ts +48 -4
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-lifecycle.test.ts +36 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-slash-commands.test.ts +0 -4
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
- package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
- package/src/__tests__/credentials-cli.test.ts +5 -12
- package/src/__tests__/cu-unified-flow.test.ts +185 -23
- package/src/__tests__/daemon-credential-client.test.ts +101 -19
- package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
- package/src/__tests__/heartbeat-service.test.ts +718 -1
- package/src/__tests__/helpers/call-route-handler.ts +7 -1
- package/src/__tests__/host-app-control-proxy.test.ts +602 -0
- package/src/__tests__/host-app-control-routes.test.ts +263 -0
- package/src/__tests__/host-bash-proxy.test.ts +246 -47
- package/src/__tests__/host-bash-routes.test.ts +294 -0
- package/src/__tests__/host-browser-proxy.test.ts +24 -22
- package/src/__tests__/host-browser-routes.test.ts +39 -13
- package/src/__tests__/host-cu-proxy.test.ts +41 -52
- package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
- package/src/__tests__/host-file-edit-tool.test.ts +47 -1
- package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
- package/src/__tests__/host-file-proxy.test.ts +37 -43
- package/src/__tests__/host-file-read-tool.test.ts +17 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
- package/src/__tests__/host-file-write-tool.test.ts +42 -1
- package/src/__tests__/host-proxy-base.test.ts +312 -0
- package/src/__tests__/host-shell-tool.test.ts +22 -4
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
- package/src/__tests__/host-transfer-proxy.test.ts +121 -22
- package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +29 -0
- package/src/__tests__/identity-routes.test.ts +103 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
- package/src/__tests__/inline-command-runner.test.ts +0 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
- package/src/__tests__/integration-status.test.ts +85 -5
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/mcp-auth-routes.test.ts +197 -0
- package/src/__tests__/mcp-cli.test.ts +338 -2
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
- package/src/__tests__/migration-import-commit-http.test.ts +108 -2
- package/src/__tests__/mock-gateway-ipc.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +0 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
- package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +97 -0
- package/src/__tests__/require-fresh-approval.test.ts +0 -1
- package/src/__tests__/retry-backoff.test.ts +87 -0
- package/src/__tests__/runtime-events-sse.test.ts +10 -6
- package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
- package/src/__tests__/schedule-retry.test.ts +715 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
- package/src/__tests__/skill-feature-flags.test.ts +43 -41
- package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
- package/src/__tests__/skill-load-inline-command.test.ts +0 -51
- package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +9 -14
- package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-config.test.ts +0 -1
- package/src/__tests__/test-preload.ts +8 -0
- package/src/__tests__/tool-approval-handler.test.ts +3 -4
- package/src/__tests__/tool-audit-listener.test.ts +48 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +3 -16
- package/src/__tests__/twilio-routes.test.ts +3 -5
- package/src/__tests__/twilio-validation.test.ts +93 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
- package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
- package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
- package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
- package/src/backup/__tests__/paths.test.ts +0 -22
- package/src/backup/__tests__/restore.test.ts +51 -151
- package/src/backup/paths.ts +2 -18
- package/src/backup/restore.ts +107 -231
- package/src/bundler/app-bundler.ts +51 -3
- package/src/calls/relay-server.ts +4 -44
- package/src/calls/twilio-config.ts +2 -17
- package/src/calls/twilio-rest.ts +33 -105
- package/src/calls/twilio-routes.ts +11 -12
- package/src/channels/types.ts +8 -7
- package/src/cli/commands/__tests__/backup.test.ts +6 -277
- package/src/cli/commands/__tests__/gateway.test.ts +288 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
- package/src/cli/commands/backup.ts +6 -331
- package/src/cli/commands/clients.ts +36 -37
- package/src/cli/commands/contacts.ts +73 -0
- package/src/cli/commands/conversations.ts +2 -5
- package/src/cli/commands/credentials.ts +15 -7
- package/src/cli/commands/domain.ts +66 -15
- package/src/cli/commands/gateway.ts +183 -0
- package/src/cli/commands/keys.ts +9 -6
- package/src/cli/commands/mcp.ts +116 -156
- package/src/cli/commands/memory-v2.ts +296 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
- package/src/cli/commands/platform/disconnect.ts +5 -4
- package/src/cli/commands/platform/index.ts +0 -18
- package/src/cli/lib/daemon-credential-client.ts +110 -28
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +67 -10
- package/src/config/bundled-skills/acp/SKILL.md +6 -0
- package/src/config/bundled-skills/acp/TOOLS.json +1 -22
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
- package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
- package/src/config/bundled-skills/app-control/SKILL.md +75 -0
- package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
- package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
- package/src/config/bundled-skills/document/TOOLS.json +0 -8
- package/src/config/bundled-skills/followups/TOOLS.json +0 -12
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
- package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
- package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
- package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
- package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
- package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
- package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
- package/src/config/bundled-skills/settings/SKILL.md +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +0 -12
- package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
- package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
- package/src/config/bundled-skills/subagent/SKILL.md +6 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
- package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
- package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
- package/src/config/bundled-tool-registry.ts +21 -0
- package/src/config/env-registry.ts +0 -2
- package/src/config/env.ts +19 -12
- package/src/config/feature-flag-registry.json +21 -133
- package/src/config/loader.ts +73 -99
- package/src/config/sanitize-for-transfer.ts +2 -0
- package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
- package/src/config/schemas/calls.ts +0 -9
- package/src/config/schemas/heartbeat.ts +63 -0
- package/src/config/schemas/ingress.ts +10 -6
- package/src/config/schemas/llm.ts +5 -10
- package/src/config/schemas/memory-lifecycle.ts +77 -24
- package/src/config/schemas/memory-v2.ts +48 -4
- package/src/config/schemas/platform.ts +6 -0
- package/src/config/schemas/services.ts +1 -15
- package/src/config/schemas/skills.ts +0 -6
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/contacts/contact-store.ts +0 -30
- package/src/contacts/contacts-write.ts +0 -27
- package/src/context/window-manager.ts +1 -2
- package/src/credential-execution/feature-gates.ts +10 -10
- package/src/credential-execution/process-manager.ts +12 -41
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
- package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
- package/src/daemon/config-watcher.ts +4 -3
- package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
- package/src/daemon/conversation-agent-loop.ts +32 -28
- package/src/daemon/conversation-lifecycle.ts +8 -1
- package/src/daemon/conversation-process.ts +16 -11
- package/src/daemon/conversation-runtime-assembly.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +125 -4
- package/src/daemon/conversation-tool-setup.ts +16 -55
- package/src/daemon/conversation.ts +21 -2
- package/src/daemon/doordash-steps.ts +1 -1
- package/src/daemon/handlers/shared.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +293 -0
- package/src/daemon/host-bash-proxy.ts +84 -74
- package/src/daemon/host-browser-proxy.ts +67 -82
- package/src/daemon/host-cu-proxy.ts +81 -86
- package/src/daemon/host-file-proxy.ts +93 -69
- package/src/daemon/host-proxy-base.ts +294 -0
- package/src/daemon/host-proxy-preactivation.ts +82 -0
- package/src/daemon/host-transfer-proxy.ts +247 -129
- package/src/daemon/lifecycle.ts +115 -117
- package/src/daemon/message-protocol.ts +3 -8
- package/src/daemon/message-types/contacts.ts +23 -1
- package/src/daemon/message-types/conversations.ts +11 -8
- package/src/daemon/message-types/host-app-control.ts +150 -0
- package/src/daemon/message-types/host-bash.ts +4 -0
- package/src/daemon/message-types/host-cu.ts +2 -0
- package/src/daemon/message-types/host-file.ts +4 -0
- package/src/daemon/message-types/host-transfer.ts +3 -0
- package/src/daemon/message-types/schedules.ts +8 -3
- package/src/daemon/message-types/skills.ts +2 -2
- package/src/daemon/process-message.ts +18 -1
- package/src/daemon/shutdown-handlers.ts +0 -3
- package/src/daemon/tool-setup-types.ts +51 -0
- package/src/daemon/tool-side-effects.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -1
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
- package/src/heartbeat/heartbeat-run-store.ts +236 -0
- package/src/heartbeat/heartbeat-service.ts +280 -49
- package/src/home/__tests__/post-connect-feed.test.ts +99 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
- package/src/home/__tests__/suggested-prompts.test.ts +89 -0
- package/src/home/post-connect-feed.ts +68 -0
- package/src/home/relationship-state-writer.ts +17 -92
- package/src/home/suggested-prompts.ts +46 -10
- package/src/inbound/public-ingress-urls.ts +32 -34
- package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
- package/src/ipc/assistant-server.ts +14 -1
- package/src/ipc/cli-client.ts +32 -1
- package/src/live-voice/live-voice-metrics.ts +10 -10
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
- package/src/mcp/mcp-auth-orchestrator.ts +213 -0
- package/src/mcp/mcp-auth-state.ts +133 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
- package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
- package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
- package/src/memory/anisotropy.test.ts +247 -0
- package/src/memory/anisotropy.ts +443 -0
- package/src/memory/auto-analysis-constants.ts +17 -0
- package/src/memory/auto-analysis-guard.ts +5 -15
- package/src/memory/canonical-guardian-store.ts +7 -7
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
- package/src/memory/context-search/agent-protocol.ts +6 -6
- package/src/memory/context-search/agent-runner.ts +32 -7
- package/src/memory/context-search/sources/memory-v2.ts +17 -5
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-key-store.ts +2 -15
- package/src/memory/db-init.ts +4 -0
- package/src/memory/embedding-backend.ts +9 -21
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
- package/src/memory/graph/conversation-graph-memory.ts +1 -24
- package/src/memory/graph/graph-search.ts +8 -0
- package/src/memory/graph/retriever.ts +28 -0
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
- package/src/memory/jobs/embed-concept-page.ts +28 -2
- package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
- package/src/memory/jobs-store.ts +66 -22
- package/src/memory/jobs-worker.ts +112 -63
- package/src/memory/memory-v2-activation-log-store.ts +1 -1
- package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
- package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/pkb/pkb-search.ts +7 -0
- package/src/memory/qdrant-client.ts +50 -20
- package/src/memory/schema/infrastructure.ts +15 -0
- package/src/memory/search/semantic.ts +7 -0
- package/src/memory/sparse-tokenize.ts +49 -0
- package/src/memory/v2/__tests__/activation.test.ts +77 -95
- package/src/memory/v2/__tests__/injection.test.ts +43 -21
- package/src/memory/v2/__tests__/sim.test.ts +166 -6
- package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
- package/src/memory/v2/__tests__/static-context.test.ts +0 -1
- package/src/memory/v2/activation.ts +69 -88
- package/src/memory/v2/consolidation-job.ts +3 -5
- package/src/memory/v2/constants.ts +7 -0
- package/src/memory/v2/injection.ts +86 -53
- package/src/memory/v2/prompts/consolidation.ts +312 -91
- package/src/memory/v2/qdrant.ts +99 -1
- package/src/memory/v2/sim.ts +126 -16
- package/src/memory/v2/skill-qdrant.ts +12 -3
- package/src/memory/v2/skill-store.ts +16 -1
- package/src/memory/v2/sparse-bm25.ts +245 -0
- package/src/memory/v2/static-context.ts +6 -5
- package/src/messaging/providers/gmail/types.ts +0 -49
- package/src/messaging/providers/slack/adapter.ts +1 -31
- package/src/messaging/providers/slack/types.ts +0 -32
- package/src/notifications/README.md +10 -10
- package/src/notifications/broadcaster.ts +1 -1
- package/src/notifications/guardian-question-mode.ts +5 -5
- package/src/oauth/connect-orchestrator.ts +4 -0
- package/src/oauth/credential-token-resolver.ts +1 -3
- package/src/oauth/manual-token-connection.ts +0 -4
- package/src/outbound-proxy/index.ts +1 -37
- package/src/outbound-proxy/logging.ts +1 -1
- package/src/outbound-proxy/policy.ts +6 -5
- package/src/outbound-proxy/router.ts +2 -1
- package/src/permissions/approval-policy.test.ts +6 -275
- package/src/permissions/approval-policy.ts +0 -51
- package/src/permissions/checker.test.ts +0 -1
- package/src/permissions/checker.ts +3 -17
- package/src/permissions/gateway-threshold-reader.ts +2 -0
- package/src/permissions/prompter.ts +34 -1
- package/src/permissions/secret-prompter.ts +6 -2
- package/src/prompts/bootstrap-cleanup.ts +27 -0
- package/src/prompts/system-prompt.ts +3 -18
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/providers/speech-to-text/provider-catalog.ts +7 -8
- package/src/runtime/assistant-event-hub.ts +118 -96
- package/src/runtime/assistant-event.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
- package/src/runtime/auth/middleware.ts +0 -96
- package/src/runtime/auth/route-policy.ts +19 -0
- package/src/runtime/btw-sidechain.ts +2 -3
- package/src/runtime/channel-invite-transport.ts +2 -48
- package/src/runtime/channel-invite-transports/email.ts +1 -1
- package/src/runtime/channel-invite-transports/slack.ts +1 -1
- package/src/runtime/channel-invite-transports/telegram.ts +1 -1
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
- package/src/runtime/channel-invite-types.ts +54 -0
- package/src/runtime/channel-readiness-service.ts +32 -13
- package/src/runtime/http-server.ts +3 -329
- package/src/runtime/http-types.ts +0 -5
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
- package/src/runtime/migrations/migration-transport.ts +7 -7
- package/src/runtime/migrations/vbundle-builder.ts +327 -60
- package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
- package/src/runtime/migrations/vbundle-importer.ts +245 -68
- package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
- package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
- package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
- package/src/runtime/migrations/vbundle-validator.ts +114 -0
- package/src/runtime/pending-interactions.ts +35 -9
- package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
- package/src/runtime/routes/approval-interception-types.ts +13 -0
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
- package/src/runtime/routes/backup-routes.ts +15 -38
- package/src/runtime/routes/btw-routes.ts +14 -37
- package/src/runtime/routes/client-routes.ts +1 -0
- package/src/runtime/routes/contact-prompt-routes.ts +183 -0
- package/src/runtime/routes/conversation-query-routes.ts +36 -1
- package/src/runtime/routes/conversation-routes.ts +30 -13
- package/src/runtime/routes/document-pdf-renderer.ts +165 -0
- package/src/runtime/routes/documents-routes.ts +30 -0
- package/src/runtime/routes/errors.ts +19 -4
- package/src/runtime/routes/events-routes.ts +12 -6
- package/src/runtime/routes/gateway-log-routes.ts +79 -0
- package/src/runtime/routes/guardian-approval-interception.ts +2 -8
- package/src/runtime/routes/heartbeat-routes.ts +103 -38
- package/src/runtime/routes/host-app-control-routes.ts +134 -0
- package/src/runtime/routes/host-bash-routes.ts +36 -6
- package/src/runtime/routes/host-browser-routes.ts +108 -13
- package/src/runtime/routes/host-cu-routes.ts +44 -14
- package/src/runtime/routes/host-file-routes.ts +33 -10
- package/src/runtime/routes/host-transfer-routes.ts +64 -24
- package/src/runtime/routes/http-adapter.ts +1 -0
- package/src/runtime/routes/identity-intro-cache.ts +30 -0
- package/src/runtime/routes/identity-routes.ts +15 -43
- package/src/runtime/routes/inbound-message-handler.ts +1 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/mcp-auth-routes.ts +132 -0
- package/src/runtime/routes/memory-item-routes.ts +10 -12
- package/src/runtime/routes/memory-v2-routes.ts +441 -1
- package/src/runtime/routes/migration-routes.ts +96 -0
- package/src/runtime/routes/schedule-routes.ts +7 -0
- package/src/runtime/verification-templates.ts +4 -7
- package/src/schedule/integration-status.ts +66 -2
- package/src/schedule/recurrence-engine.ts +4 -1
- package/src/schedule/retry-backoff.ts +18 -0
- package/src/schedule/retry-policy.ts +82 -0
- package/src/schedule/schedule-recovery.ts +64 -0
- package/src/schedule/schedule-store.ts +106 -2
- package/src/schedule/scheduler-types.ts +25 -0
- package/src/schedule/scheduler.ts +63 -38
- package/src/security/oauth-callback-registry.ts +8 -0
- package/src/sequence/analytics.ts +5 -5
- package/src/sequence/engine.ts +1 -1
- package/src/skills/catalog-files.ts +2 -8
- package/src/skills/include-graph.ts +5 -5
- package/src/skills/remote-skill-policy.ts +5 -5
- package/src/skills/skill-file-provider.ts +1 -1
- package/src/skills/skill-file-types.ts +13 -0
- package/src/skills/skillssh-audit-types.ts +28 -0
- package/src/skills/skillssh-registry.ts +8 -21
- package/src/telemetry/types.ts +2 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
- package/src/tools/apps/executors.ts +56 -69
- package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
- package/src/tools/browser/browser-execution.ts +2 -2
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
- package/src/tools/browser/cdp-client/factory.ts +23 -24
- package/src/tools/browser/cdp-client/index.ts +1 -14
- package/src/tools/computer-use/definitions.ts +42 -20
- package/src/tools/executor.ts +2 -0
- package/src/tools/host-filesystem/edit.ts +26 -0
- package/src/tools/host-filesystem/read.ts +26 -0
- package/src/tools/host-filesystem/transfer.ts +31 -1
- package/src/tools/host-filesystem/write.ts +26 -0
- package/src/tools/host-terminal/host-shell.ts +58 -0
- package/src/tools/schedule/create.ts +6 -0
- package/src/tools/schedule/list.ts +2 -0
- package/src/tools/schedule/update.ts +10 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
- package/src/tools/shared/filesystem/path-policy.ts +25 -1
- package/src/tools/skills/load.ts +0 -32
- package/src/tools/tool-approval-handler.ts +1 -5
- package/src/tools/types.ts +4 -0
- package/src/usage/pricing.ts +1 -1
- package/src/workspace/hatched-date.ts +86 -0
- package/src/workspace/migrations/003-seed-device-id.ts +1 -1
- package/src/workspace/migrations/006-services-config.ts +8 -5
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
- package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
- package/src/workspace/migrations/utils.ts +21 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
- package/src/__tests__/twilio-rest.test.ts +0 -34
- package/src/backup/__tests__/backup-key.test.ts +0 -152
- package/src/backup/__tests__/backup-worker.test.ts +0 -782
- package/src/backup/__tests__/offsite-writer.test.ts +0 -641
- package/src/backup/__tests__/stream-crypt.test.ts +0 -228
- package/src/backup/backup-key.ts +0 -137
- package/src/backup/backup-worker.ts +0 -472
- package/src/backup/offsite-writer.ts +0 -222
- package/src/backup/stream-crypt.ts +0 -263
- package/src/daemon/message-types/pairing.ts +0 -58
- package/src/outbound-proxy/config.ts +0 -20
- package/src/outbound-proxy/health.ts +0 -18
- package/src/outbound-proxy/types.ts +0 -150
- package/src/runtime/capability-tokens.ts +0 -190
- package/src/signals/mcp-reload.ts +0 -18
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* End-to-end WebSocket dispatch test for the PR10 envelopes:
|
|
3
|
-
*
|
|
4
|
-
* - `host_browser_event` — the extension forwards every
|
|
5
|
-
* `chrome.debugger.onEvent` firing to the runtime over the
|
|
6
|
-
* browser-relay WebSocket. This test asserts that the runtime's
|
|
7
|
-
* inbound frame handler fans the event out to subscribers of the
|
|
8
|
-
* module-level browser-session event bus with the method + params
|
|
9
|
-
* + cdpSessionId preserved.
|
|
10
|
-
*
|
|
11
|
-
* - `host_browser_session_invalidated` — the extension forwards a
|
|
12
|
-
* detach notification over the same socket. This test asserts
|
|
13
|
-
* that the runtime-side `BrowserSessionManager` evicts any stale
|
|
14
|
-
* session whose `targetId` matches the invalidated envelope and
|
|
15
|
-
* that the next CDP command against that session throws, forcing
|
|
16
|
-
* the owning tool to create a fresh session (which in production
|
|
17
|
-
* triggers a reattach on the extension's dispatcher).
|
|
18
|
-
*
|
|
19
|
-
* Unlike the unit test in `host-browser-event-routes.test.ts`, this
|
|
20
|
-
* file stands up the full `RuntimeHttpServer` so the WS upgrade,
|
|
21
|
-
* frame parse, dispatch switch, and resolver helpers all run through
|
|
22
|
-
* their production code paths. The capability-token transport is
|
|
23
|
-
* used so the test does not depend on a valid guardian-bound JWT.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
27
|
-
|
|
28
|
-
// ── Module mocks (must be declared before the real imports below) ───
|
|
29
|
-
|
|
30
|
-
mock.module("../util/logger.js", () => ({
|
|
31
|
-
getLogger: () =>
|
|
32
|
-
new Proxy({} as Record<string, unknown>, {
|
|
33
|
-
get: () => () => {},
|
|
34
|
-
}),
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
mock.module("../config/loader.js", () => ({
|
|
38
|
-
getConfig: () => ({
|
|
39
|
-
ui: {},
|
|
40
|
-
model: "test",
|
|
41
|
-
provider: "test",
|
|
42
|
-
memory: { enabled: false },
|
|
43
|
-
rateLimit: { maxRequestsPerMinute: 0 },
|
|
44
|
-
secretDetection: { enabled: false },
|
|
45
|
-
contextWindow: { maxInputTokens: 200000 },
|
|
46
|
-
services: {
|
|
47
|
-
inference: {
|
|
48
|
-
mode: "your-own",
|
|
49
|
-
provider: "anthropic",
|
|
50
|
-
model: "claude-opus-4-6",
|
|
51
|
-
},
|
|
52
|
-
"image-generation": {
|
|
53
|
-
mode: "your-own",
|
|
54
|
-
provider: "gemini",
|
|
55
|
-
model: "gemini-3.1-flash-image-preview",
|
|
56
|
-
},
|
|
57
|
-
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
58
|
-
},
|
|
59
|
-
}),
|
|
60
|
-
}));
|
|
61
|
-
|
|
62
|
-
// ── Real imports (after mocks) ──────────────────────────────────────
|
|
63
|
-
|
|
64
|
-
import {
|
|
65
|
-
__resetBrowserSessionEventsForTests,
|
|
66
|
-
BrowserSessionManager,
|
|
67
|
-
createExtensionBackend,
|
|
68
|
-
type ForwardedCdpEvent,
|
|
69
|
-
onCdpEvent,
|
|
70
|
-
} from "../browser-session/index.js";
|
|
71
|
-
import { HostBrowserProxy } from "../daemon/host-browser-proxy.js";
|
|
72
|
-
import { getDb } from "../memory/db-connection.js";
|
|
73
|
-
import { initializeDb } from "../memory/db-init.js";
|
|
74
|
-
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
75
|
-
import { mintToken } from "../runtime/auth/token-service.js";
|
|
76
|
-
import {
|
|
77
|
-
mintHostBrowserCapability,
|
|
78
|
-
resetCapabilityTokenSecretForTests,
|
|
79
|
-
setCapabilityTokenSecretForTests,
|
|
80
|
-
} from "../runtime/capability-tokens.js";
|
|
81
|
-
import { RuntimeHttpServer } from "../runtime/http-server.js";
|
|
82
|
-
|
|
83
|
-
initializeDb();
|
|
84
|
-
|
|
85
|
-
function mintSseToken(guardianId: string): string {
|
|
86
|
-
return mintToken({
|
|
87
|
-
aud: "vellum-daemon",
|
|
88
|
-
sub: `actor:self:${guardianId}`,
|
|
89
|
-
scope_profile: "actor_client_v1",
|
|
90
|
-
policy_epoch: 1,
|
|
91
|
-
ttlSeconds: 3600,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ── Helpers ─────────────────────────────────────────────────────────
|
|
96
|
-
|
|
97
|
-
async function waitFor(
|
|
98
|
-
predicate: () => boolean,
|
|
99
|
-
timeoutMs = 2000,
|
|
100
|
-
): Promise<void> {
|
|
101
|
-
const deadline = Date.now() + timeoutMs;
|
|
102
|
-
while (!predicate()) {
|
|
103
|
-
if (Date.now() > deadline) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
`waitFor: predicate did not become true within ${timeoutMs}ms`,
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async function waitForRegistryEntry(
|
|
113
|
-
_guardianId: string,
|
|
114
|
-
timeoutMs = 2000,
|
|
115
|
-
): Promise<void> {
|
|
116
|
-
await waitFor(
|
|
117
|
-
() =>
|
|
118
|
-
assistantEventHub.getMostRecentClientByCapability("host_browser") != null,
|
|
119
|
-
timeoutMs,
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ── Tests ───────────────────────────────────────────────────────────
|
|
124
|
-
|
|
125
|
-
describe("host_browser WS event + invalidation e2e", () => {
|
|
126
|
-
let server: RuntimeHttpServer;
|
|
127
|
-
let port: number;
|
|
128
|
-
let runtimeBaseUrl: string;
|
|
129
|
-
|
|
130
|
-
beforeEach(async () => {
|
|
131
|
-
setCapabilityTokenSecretForTests(Buffer.alloc(32, 0xab));
|
|
132
|
-
|
|
133
|
-
const db = getDb();
|
|
134
|
-
db.run("DELETE FROM contact_channels");
|
|
135
|
-
db.run("DELETE FROM contacts");
|
|
136
|
-
HostBrowserProxy.reset();
|
|
137
|
-
__resetBrowserSessionEventsForTests();
|
|
138
|
-
|
|
139
|
-
// Pick a non-colliding port in the same band as the other
|
|
140
|
-
// host-browser e2e tests but offset so parallel runs don't
|
|
141
|
-
// step on one another.
|
|
142
|
-
port = 19900 + Math.floor(Math.random() * 200);
|
|
143
|
-
runtimeBaseUrl = `http://127.0.0.1:${port}`;
|
|
144
|
-
server = new RuntimeHttpServer({ port });
|
|
145
|
-
await server.start();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
afterEach(async () => {
|
|
149
|
-
await server?.stop();
|
|
150
|
-
HostBrowserProxy.reset();
|
|
151
|
-
__resetBrowserSessionEventsForTests();
|
|
152
|
-
resetCapabilityTokenSecretForTests();
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("host_browser_event frame fans out to browser-session event bus subscribers", async () => {
|
|
156
|
-
const guardianId = `guardian-${crypto.randomUUID()}`;
|
|
157
|
-
const { token } = mintHostBrowserCapability(guardianId);
|
|
158
|
-
|
|
159
|
-
const { createMockChromeExtension } =
|
|
160
|
-
await import("./fixtures/mock-chrome-extension.js");
|
|
161
|
-
const mockExt = createMockChromeExtension({
|
|
162
|
-
runtimeBaseUrl,
|
|
163
|
-
token,
|
|
164
|
-
sseToken: mintSseToken(guardianId),
|
|
165
|
-
resultTransport: "ws",
|
|
166
|
-
});
|
|
167
|
-
await mockExt.start();
|
|
168
|
-
await mockExt.waitForConnection();
|
|
169
|
-
await waitForRegistryEntry(guardianId);
|
|
170
|
-
|
|
171
|
-
// Subscribe BEFORE sending the frame so we're guaranteed to see
|
|
172
|
-
// the fanout. The subscription is module-level so it survives
|
|
173
|
-
// across the WS round-trip naturally.
|
|
174
|
-
const observed: ForwardedCdpEvent[] = [];
|
|
175
|
-
const unsubscribe = onCdpEvent((event) => observed.push(event));
|
|
176
|
-
|
|
177
|
-
mockExt.sendHostBrowserEvent({
|
|
178
|
-
method: "Page.frameNavigated",
|
|
179
|
-
params: { frame: { id: "frame-1", url: "https://example.com" } },
|
|
180
|
-
cdpSessionId: "target-abc",
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// The WS dispatch hop is asynchronous — poll until the event
|
|
184
|
-
// lands or the test times out.
|
|
185
|
-
await waitFor(() => observed.length === 1);
|
|
186
|
-
|
|
187
|
-
expect(observed[0].method).toBe("Page.frameNavigated");
|
|
188
|
-
expect(observed[0].params).toEqual({
|
|
189
|
-
frame: { id: "frame-1", url: "https://example.com" },
|
|
190
|
-
});
|
|
191
|
-
expect(observed[0].cdpSessionId).toBe("target-abc");
|
|
192
|
-
|
|
193
|
-
unsubscribe();
|
|
194
|
-
await mockExt.stop();
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("host_browser_event frames with no params are still routed", async () => {
|
|
198
|
-
const guardianId = `guardian-${crypto.randomUUID()}`;
|
|
199
|
-
const { token } = mintHostBrowserCapability(guardianId);
|
|
200
|
-
|
|
201
|
-
const { createMockChromeExtension } =
|
|
202
|
-
await import("./fixtures/mock-chrome-extension.js");
|
|
203
|
-
const mockExt = createMockChromeExtension({
|
|
204
|
-
runtimeBaseUrl,
|
|
205
|
-
token,
|
|
206
|
-
sseToken: mintSseToken(guardianId),
|
|
207
|
-
resultTransport: "ws",
|
|
208
|
-
});
|
|
209
|
-
await mockExt.start();
|
|
210
|
-
await mockExt.waitForConnection();
|
|
211
|
-
await waitForRegistryEntry(guardianId);
|
|
212
|
-
|
|
213
|
-
const observed: ForwardedCdpEvent[] = [];
|
|
214
|
-
const unsubscribe = onCdpEvent((event) => observed.push(event));
|
|
215
|
-
|
|
216
|
-
mockExt.sendHostBrowserEvent({ method: "Target.targetDestroyed" });
|
|
217
|
-
|
|
218
|
-
await waitFor(() => observed.length === 1);
|
|
219
|
-
expect(observed[0].method).toBe("Target.targetDestroyed");
|
|
220
|
-
expect(observed[0].params).toBeUndefined();
|
|
221
|
-
expect(observed[0].cdpSessionId).toBeUndefined();
|
|
222
|
-
|
|
223
|
-
unsubscribe();
|
|
224
|
-
await mockExt.stop();
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test("host_browser_session_invalidated frame evicts stale sessions and the next command forces reattach", async () => {
|
|
228
|
-
const guardianId = `guardian-${crypto.randomUUID()}`;
|
|
229
|
-
const { token } = mintHostBrowserCapability(guardianId);
|
|
230
|
-
|
|
231
|
-
const { createMockChromeExtension } =
|
|
232
|
-
await import("./fixtures/mock-chrome-extension.js");
|
|
233
|
-
const mockExt = createMockChromeExtension({
|
|
234
|
-
runtimeBaseUrl,
|
|
235
|
-
token,
|
|
236
|
-
sseToken: mintSseToken(guardianId),
|
|
237
|
-
resultTransport: "ws",
|
|
238
|
-
});
|
|
239
|
-
await mockExt.start();
|
|
240
|
-
await mockExt.waitForConnection();
|
|
241
|
-
await waitForRegistryEntry(guardianId);
|
|
242
|
-
|
|
243
|
-
// Stand up a BrowserSessionManager that mirrors what a tool
|
|
244
|
-
// invocation would build. The backend counts dispatch attempts
|
|
245
|
-
// so we can assert the first post-invalidation send never
|
|
246
|
-
// reached the backend while the second (after reattach) did.
|
|
247
|
-
const sent: Array<{ method: string }> = [];
|
|
248
|
-
const backend = createExtensionBackend({
|
|
249
|
-
isAvailable: () => true,
|
|
250
|
-
sendCdp: async (command) => {
|
|
251
|
-
sent.push({ method: command.method });
|
|
252
|
-
return { result: { ok: true } };
|
|
253
|
-
},
|
|
254
|
-
dispose: () => {},
|
|
255
|
-
});
|
|
256
|
-
const manager = new BrowserSessionManager({ backends: [backend] });
|
|
257
|
-
const session = manager.createSession();
|
|
258
|
-
session.targetId = "tab-77";
|
|
259
|
-
|
|
260
|
-
// Fire the invalidation envelope from the extension side.
|
|
261
|
-
mockExt.sendSessionInvalidated({
|
|
262
|
-
targetId: "tab-77",
|
|
263
|
-
reason: "target_closed",
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
// Wait until the WS dispatch hop lands — `isTargetInvalidated`
|
|
267
|
-
// peeks at the registry without consuming the entry, so we can
|
|
268
|
-
// poll safely.
|
|
269
|
-
const { isTargetInvalidated } =
|
|
270
|
-
await import("../browser-session/events.js");
|
|
271
|
-
await waitFor(() => isTargetInvalidated("tab-77"));
|
|
272
|
-
|
|
273
|
-
// The next send against the invalidated session MUST throw —
|
|
274
|
-
// the manager consumes the invalidation flag, evicts the
|
|
275
|
-
// session, and rejects the command so the caller can create a
|
|
276
|
-
// fresh session (which triggers a reattach on the extension
|
|
277
|
-
// side).
|
|
278
|
-
await expect(
|
|
279
|
-
manager.send(session.id, { method: "Page.navigate" }),
|
|
280
|
-
).rejects.toThrow(/invalidated/);
|
|
281
|
-
|
|
282
|
-
// Sanity: the backend never saw the doomed command.
|
|
283
|
-
expect(sent).toHaveLength(0);
|
|
284
|
-
|
|
285
|
-
// The evicted session is gone — sending again throws
|
|
286
|
-
// "Unknown browser session", which is the signal a tool uses
|
|
287
|
-
// to rebuild a fresh session.
|
|
288
|
-
await expect(
|
|
289
|
-
manager.send(session.id, { method: "Page.navigate" }),
|
|
290
|
-
).rejects.toThrow(/Unknown browser session/);
|
|
291
|
-
|
|
292
|
-
// Creating a fresh session proves the reattach path works:
|
|
293
|
-
// the caller bounces through `createSession` and a subsequent
|
|
294
|
-
// send dispatches normally through the backend.
|
|
295
|
-
const fresh = manager.createSession();
|
|
296
|
-
const result = await manager.send(fresh.id, {
|
|
297
|
-
method: "Page.navigate",
|
|
298
|
-
});
|
|
299
|
-
expect(result.result).toEqual({ ok: true });
|
|
300
|
-
expect(sent).toEqual([{ method: "Page.navigate" }]);
|
|
301
|
-
|
|
302
|
-
await mockExt.stop();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test("keepalive frames are accepted without closing the socket or producing warnings", async () => {
|
|
306
|
-
const guardianId = `guardian-${crypto.randomUUID()}`;
|
|
307
|
-
const { token } = mintHostBrowserCapability(guardianId);
|
|
308
|
-
|
|
309
|
-
const { createMockChromeExtension } =
|
|
310
|
-
await import("./fixtures/mock-chrome-extension.js");
|
|
311
|
-
const mockExt = createMockChromeExtension({
|
|
312
|
-
runtimeBaseUrl,
|
|
313
|
-
token,
|
|
314
|
-
sseToken: mintSseToken(guardianId),
|
|
315
|
-
resultTransport: "ws",
|
|
316
|
-
});
|
|
317
|
-
await mockExt.start();
|
|
318
|
-
await mockExt.waitForConnection();
|
|
319
|
-
await waitForRegistryEntry(guardianId);
|
|
320
|
-
|
|
321
|
-
// Send a keepalive frame (the extension sends these periodically
|
|
322
|
-
// to prevent the runtime from considering the connection stale).
|
|
323
|
-
// The frame may contain extra keys (e.g. timestamp) that the
|
|
324
|
-
// runtime should silently ignore (lenient validation).
|
|
325
|
-
mockExt.sendRaw(JSON.stringify({ type: "keepalive", ts: Date.now() }));
|
|
326
|
-
|
|
327
|
-
// Small delay to let the keepalive frame process.
|
|
328
|
-
await new Promise((r) => setTimeout(r, 15));
|
|
329
|
-
|
|
330
|
-
// Verify the socket is still alive by sending a normal host_browser_event
|
|
331
|
-
// frame after the keepalive — if the socket had been torn down, this
|
|
332
|
-
// would never arrive.
|
|
333
|
-
const observed: ForwardedCdpEvent[] = [];
|
|
334
|
-
const unsubscribe = onCdpEvent((event) => observed.push(event));
|
|
335
|
-
|
|
336
|
-
mockExt.sendHostBrowserEvent({ method: "Page.loadEventFired" });
|
|
337
|
-
await waitFor(() => observed.length === 1);
|
|
338
|
-
expect(observed[0].method).toBe("Page.loadEventFired");
|
|
339
|
-
|
|
340
|
-
unsubscribe();
|
|
341
|
-
await mockExt.stop();
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
test("normal host_browser flows still pass after keepalive traffic", async () => {
|
|
345
|
-
const guardianId = `guardian-${crypto.randomUUID()}`;
|
|
346
|
-
const { token } = mintHostBrowserCapability(guardianId);
|
|
347
|
-
|
|
348
|
-
const { createMockChromeExtension } =
|
|
349
|
-
await import("./fixtures/mock-chrome-extension.js");
|
|
350
|
-
const mockExt = createMockChromeExtension({
|
|
351
|
-
runtimeBaseUrl,
|
|
352
|
-
token,
|
|
353
|
-
sseToken: mintSseToken(guardianId),
|
|
354
|
-
resultTransport: "ws",
|
|
355
|
-
});
|
|
356
|
-
await mockExt.start();
|
|
357
|
-
await mockExt.waitForConnection();
|
|
358
|
-
await waitForRegistryEntry(guardianId);
|
|
359
|
-
|
|
360
|
-
// Simulate a burst of keepalive frames (as would happen during an
|
|
361
|
-
// idle period with the extension's alarm-based keepalive ticker).
|
|
362
|
-
for (let i = 0; i < 5; i++) {
|
|
363
|
-
mockExt.sendRaw(JSON.stringify({ type: "keepalive" }));
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Small delay to let all keepalive frames process.
|
|
367
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
368
|
-
|
|
369
|
-
// Now send a host_browser_event and verify it still fans out
|
|
370
|
-
// correctly — proving keepalive traffic does not interfere with
|
|
371
|
-
// normal message processing.
|
|
372
|
-
const observed: ForwardedCdpEvent[] = [];
|
|
373
|
-
const unsubscribe = onCdpEvent((event) => observed.push(event));
|
|
374
|
-
|
|
375
|
-
mockExt.sendHostBrowserEvent({
|
|
376
|
-
method: "Network.requestWillBeSent",
|
|
377
|
-
params: { requestId: "req-42", url: "https://example.com/api" },
|
|
378
|
-
cdpSessionId: "session-xyz",
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
await waitFor(() => observed.length === 1);
|
|
382
|
-
expect(observed[0].method).toBe("Network.requestWillBeSent");
|
|
383
|
-
expect(observed[0].params).toEqual({
|
|
384
|
-
requestId: "req-42",
|
|
385
|
-
url: "https://example.com/api",
|
|
386
|
-
});
|
|
387
|
-
expect(observed[0].cdpSessionId).toBe("session-xyz");
|
|
388
|
-
|
|
389
|
-
unsubscribe();
|
|
390
|
-
await mockExt.stop();
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
test("malformed host_browser_event frames are dropped without tearing down the socket", async () => {
|
|
394
|
-
const guardianId = `guardian-${crypto.randomUUID()}`;
|
|
395
|
-
const { token } = mintHostBrowserCapability(guardianId);
|
|
396
|
-
|
|
397
|
-
const { createMockChromeExtension } =
|
|
398
|
-
await import("./fixtures/mock-chrome-extension.js");
|
|
399
|
-
const mockExt = createMockChromeExtension({
|
|
400
|
-
runtimeBaseUrl,
|
|
401
|
-
token,
|
|
402
|
-
sseToken: mintSseToken(guardianId),
|
|
403
|
-
resultTransport: "ws",
|
|
404
|
-
});
|
|
405
|
-
await mockExt.start();
|
|
406
|
-
await mockExt.waitForConnection();
|
|
407
|
-
await waitForRegistryEntry(guardianId);
|
|
408
|
-
|
|
409
|
-
const observed: ForwardedCdpEvent[] = [];
|
|
410
|
-
const unsubscribe = onCdpEvent((event) => observed.push(event));
|
|
411
|
-
|
|
412
|
-
// Send a frame with no method — the resolver must reject it
|
|
413
|
-
// and the WS dispatcher must swallow the rejection.
|
|
414
|
-
mockExt.sendHostBrowserEvent({ method: "" });
|
|
415
|
-
|
|
416
|
-
// Follow up with a valid frame and assert that ONLY the valid
|
|
417
|
-
// frame was published — proving the socket survived the bad
|
|
418
|
-
// frame and the dispatcher kept processing subsequent messages.
|
|
419
|
-
mockExt.sendHostBrowserEvent({ method: "Page.loadEventFired" });
|
|
420
|
-
|
|
421
|
-
await waitFor(() => observed.length === 1);
|
|
422
|
-
expect(observed[0].method).toBe("Page.loadEventFired");
|
|
423
|
-
|
|
424
|
-
unsubscribe();
|
|
425
|
-
await mockExt.stop();
|
|
426
|
-
});
|
|
427
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { twilioAuthHeader, twilioBaseUrl } from "../calls/twilio-rest.js";
|
|
4
|
-
|
|
5
|
-
describe("twilioAuthHeader", () => {
|
|
6
|
-
test("returns a valid Basic auth header", () => {
|
|
7
|
-
const header = twilioAuthHeader("AC_test_sid", "test_token");
|
|
8
|
-
const expected =
|
|
9
|
-
"Basic " + Buffer.from("AC_test_sid:test_token").toString("base64");
|
|
10
|
-
expect(header).toBe(expected);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("encodes special characters correctly", () => {
|
|
14
|
-
const header = twilioAuthHeader("AC_special!@#", "tok$%^&");
|
|
15
|
-
const decoded = Buffer.from(
|
|
16
|
-
header.replace("Basic ", ""),
|
|
17
|
-
"base64",
|
|
18
|
-
).toString();
|
|
19
|
-
expect(decoded).toBe("AC_special!@#:tok$%^&");
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe("twilioBaseUrl", () => {
|
|
24
|
-
test("constructs correct base URL for a given account SID", () => {
|
|
25
|
-
const url = twilioBaseUrl("AC_abc123");
|
|
26
|
-
expect(url).toBe("https://api.twilio.com/2010-04-01/Accounts/AC_abc123");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("handles different account SIDs", () => {
|
|
30
|
-
const url = twilioBaseUrl("AC_xyz789");
|
|
31
|
-
expect(url).toContain("AC_xyz789");
|
|
32
|
-
expect(url).toStartWith("https://api.twilio.com/2010-04-01/Accounts/");
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the backup key read/generate helpers. All tests run against a
|
|
3
|
-
* temp directory and explicitly pass the key path -- nothing touches the
|
|
4
|
-
* real `~/.vellum/` tree or depends on daemon startup state.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
mkdirSync,
|
|
9
|
-
mkdtempSync,
|
|
10
|
-
readdirSync,
|
|
11
|
-
rmSync,
|
|
12
|
-
statSync,
|
|
13
|
-
writeFileSync,
|
|
14
|
-
} from "node:fs";
|
|
15
|
-
import * as fsPromises from "node:fs/promises";
|
|
16
|
-
import { tmpdir } from "node:os";
|
|
17
|
-
import { basename, join } from "node:path";
|
|
18
|
-
import {
|
|
19
|
-
afterEach,
|
|
20
|
-
beforeEach,
|
|
21
|
-
describe,
|
|
22
|
-
expect,
|
|
23
|
-
spyOn,
|
|
24
|
-
test,
|
|
25
|
-
} from "bun:test";
|
|
26
|
-
|
|
27
|
-
import { ensureBackupKey, readBackupKey } from "../backup-key.js";
|
|
28
|
-
|
|
29
|
-
describe("backup-key", () => {
|
|
30
|
-
let root: string;
|
|
31
|
-
let keyPath: string;
|
|
32
|
-
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
root = mkdtempSync(join(tmpdir(), "vellum-backup-key-"));
|
|
35
|
-
// Nest the key file one level down so we can verify that the
|
|
36
|
-
// parent directory is created on demand with the expected mode.
|
|
37
|
-
keyPath = join(root, "backup", "backup.key");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
afterEach(() => {
|
|
41
|
-
try {
|
|
42
|
-
rmSync(root, { recursive: true, force: true });
|
|
43
|
-
} catch {
|
|
44
|
-
// best-effort
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("ensureBackupKey", () => {
|
|
49
|
-
test("first call generates a fresh 32-byte key and writes it with mode 0600", async () => {
|
|
50
|
-
const key = await ensureBackupKey(keyPath);
|
|
51
|
-
|
|
52
|
-
expect(key).toBeInstanceOf(Buffer);
|
|
53
|
-
expect(key.length).toBe(32);
|
|
54
|
-
|
|
55
|
-
const fileMode = statSync(keyPath).mode & 0o777;
|
|
56
|
-
expect(fileMode).toBe(0o600);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("creates the parent directory with mode 0700 when missing", async () => {
|
|
60
|
-
await ensureBackupKey(keyPath);
|
|
61
|
-
|
|
62
|
-
const parent = join(root, "backup");
|
|
63
|
-
const dirMode = statSync(parent).mode & 0o777;
|
|
64
|
-
// On some platforms umask can strip bits further, but it must
|
|
65
|
-
// never be more permissive than 0o700.
|
|
66
|
-
expect(dirMode & ~0o700).toBe(0);
|
|
67
|
-
expect(dirMode & 0o700).toBe(0o700);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("second call returns the same bytes persisted on the first call", async () => {
|
|
71
|
-
const first = await ensureBackupKey(keyPath);
|
|
72
|
-
const second = await ensureBackupKey(keyPath);
|
|
73
|
-
expect(Buffer.compare(first, second)).toBe(0);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("throws when an existing key file has the wrong size", async () => {
|
|
77
|
-
mkdirSync(join(root, "backup"), { recursive: true, mode: 0o700 });
|
|
78
|
-
// 16 bytes is half the expected length -- simulate a truncated or
|
|
79
|
-
// otherwise corrupt key file.
|
|
80
|
-
writeFileSync(keyPath, Buffer.alloc(16, 0xaa), { mode: 0o600 });
|
|
81
|
-
|
|
82
|
-
await expect(ensureBackupKey(keyPath)).rejects.toThrow(
|
|
83
|
-
/invalid length 16/,
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("does not leave any .tmp file behind after a successful write", async () => {
|
|
88
|
-
await ensureBackupKey(keyPath);
|
|
89
|
-
const parent = join(root, "backup");
|
|
90
|
-
const base = basename(keyPath);
|
|
91
|
-
const entries = readdirSync(parent);
|
|
92
|
-
const tmpEntries = entries.filter((e) => e.startsWith(`${base}.tmp.`));
|
|
93
|
-
expect(tmpEntries).toEqual([]);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("two concurrent callers converge on the same persisted bytes", async () => {
|
|
97
|
-
// Race two ensureBackupKey calls. Exactly one caller's bytes win
|
|
98
|
-
// the rename; the other caller must re-read the file and return
|
|
99
|
-
// those same bytes rather than the key it generated locally.
|
|
100
|
-
const [a, b] = await Promise.all([
|
|
101
|
-
ensureBackupKey(keyPath),
|
|
102
|
-
ensureBackupKey(keyPath),
|
|
103
|
-
]);
|
|
104
|
-
expect(Buffer.compare(a, b)).toBe(0);
|
|
105
|
-
const onDisk = statSync(keyPath);
|
|
106
|
-
expect(onDisk.size).toBe(32);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test("propagates non-ENOENT stat errors instead of treating them as missing", async () => {
|
|
110
|
-
// Simulate flaky storage (EIO / ESTALE). If pathExists swallowed
|
|
111
|
-
// these, the key file would appear "missing" and be silently
|
|
112
|
-
// regenerated, rotating away bytes used to encrypt existing data.
|
|
113
|
-
const statSpy = spyOn(fsPromises, "stat").mockImplementation(
|
|
114
|
-
async () => {
|
|
115
|
-
const err = new Error("simulated EIO") as NodeJS.ErrnoException;
|
|
116
|
-
err.code = "EIO";
|
|
117
|
-
throw err;
|
|
118
|
-
},
|
|
119
|
-
);
|
|
120
|
-
try {
|
|
121
|
-
await expect(ensureBackupKey(keyPath)).rejects.toThrow(
|
|
122
|
-
/simulated EIO/,
|
|
123
|
-
);
|
|
124
|
-
// And the key file must NOT have been created as a side effect.
|
|
125
|
-
expect(() => statSync(keyPath)).toThrow();
|
|
126
|
-
} finally {
|
|
127
|
-
statSpy.mockRestore();
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe("readBackupKey", () => {
|
|
133
|
-
test("returns null when the key file is missing", async () => {
|
|
134
|
-
const result = await readBackupKey(keyPath);
|
|
135
|
-
expect(result).toBeNull();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
test("returns the same bytes that ensureBackupKey wrote", async () => {
|
|
139
|
-
const generated = await ensureBackupKey(keyPath);
|
|
140
|
-
const read = await readBackupKey(keyPath);
|
|
141
|
-
expect(read).not.toBeNull();
|
|
142
|
-
expect(Buffer.compare(read!, generated)).toBe(0);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("throws when the existing key file has the wrong size", async () => {
|
|
146
|
-
mkdirSync(join(root, "backup"), { recursive: true, mode: 0o700 });
|
|
147
|
-
writeFileSync(keyPath, Buffer.alloc(8, 0xff), { mode: 0o600 });
|
|
148
|
-
|
|
149
|
-
await expect(readBackupKey(keyPath)).rejects.toThrow(/invalid length 8/);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
});
|