@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
|
@@ -71,38 +71,7 @@ function mockStore(
|
|
|
71
71
|
// ---------------------------------------------------------------------------
|
|
72
72
|
|
|
73
73
|
describe("executeAppCreate", () => {
|
|
74
|
-
test("
|
|
75
|
-
const files: Record<string, string> = {};
|
|
76
|
-
let createdParams: Record<string, unknown> | undefined;
|
|
77
|
-
const app = makeLegacyApp();
|
|
78
|
-
const store: AppStore = {
|
|
79
|
-
...mockStore(app, files),
|
|
80
|
-
createApp: (params) => {
|
|
81
|
-
createdParams = params as unknown as Record<string, unknown>;
|
|
82
|
-
return app;
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const result = await executeAppCreate(
|
|
87
|
-
{
|
|
88
|
-
name: "Test App",
|
|
89
|
-
html: "<html><body>Hello</body></html>",
|
|
90
|
-
},
|
|
91
|
-
store,
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
expect(result.isError).toBe(false);
|
|
95
|
-
// Legacy path: no formatVersion set, htmlDefinition is the provided html
|
|
96
|
-
expect(createdParams?.formatVersion).toBeUndefined();
|
|
97
|
-
expect(createdParams?.htmlDefinition).toBe(
|
|
98
|
-
"<html><body>Hello</body></html>",
|
|
99
|
-
);
|
|
100
|
-
// No src/ files should be written
|
|
101
|
-
expect(files["src/index.html"]).toBeUndefined();
|
|
102
|
-
expect(files["src/main.tsx"]).toBeUndefined();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("flag on: creates multifile app with src/ scaffold", async () => {
|
|
74
|
+
test("creates multifile app with src/ scaffold", async () => {
|
|
106
75
|
const files: Record<string, string> = {};
|
|
107
76
|
let createdParams: Record<string, unknown> | undefined;
|
|
108
77
|
const app = makeMultifileApp({ name: "New App" });
|
|
@@ -117,7 +86,6 @@ describe("executeAppCreate", () => {
|
|
|
117
86
|
const result = await executeAppCreate(
|
|
118
87
|
{
|
|
119
88
|
name: "New App",
|
|
120
|
-
featureFlags: { multifileEnabled: true },
|
|
121
89
|
},
|
|
122
90
|
store,
|
|
123
91
|
);
|
|
@@ -136,7 +104,7 @@ describe("executeAppCreate", () => {
|
|
|
136
104
|
expect(files["src/main.tsx"]).toContain('{"Hello, New App!"}');
|
|
137
105
|
});
|
|
138
106
|
|
|
139
|
-
test("
|
|
107
|
+
test("rejects retired html shortcut", async () => {
|
|
140
108
|
const files: Record<string, string> = {};
|
|
141
109
|
const app = makeMultifileApp({ name: "Custom App" });
|
|
142
110
|
const store: AppStore = {
|
|
@@ -144,22 +112,41 @@ describe("executeAppCreate", () => {
|
|
|
144
112
|
createApp: () => app,
|
|
145
113
|
};
|
|
146
114
|
|
|
147
|
-
const customHtml =
|
|
148
|
-
'<!DOCTYPE html><html><head></head><body><div id="root"></div></body></html>';
|
|
149
115
|
const result = await executeAppCreate(
|
|
150
116
|
{
|
|
151
117
|
name: "Custom App",
|
|
152
|
-
html:
|
|
153
|
-
featureFlags: { multifileEnabled: true },
|
|
118
|
+
html: "<!DOCTYPE html><html><body></body></html>",
|
|
154
119
|
},
|
|
155
120
|
store,
|
|
156
121
|
);
|
|
157
122
|
|
|
158
|
-
expect(result.isError).toBe(
|
|
159
|
-
|
|
160
|
-
expect(
|
|
161
|
-
|
|
162
|
-
expect(files["src/main.tsx"]).
|
|
123
|
+
expect(result.isError).toBe(true);
|
|
124
|
+
const parsed = JSON.parse(result.content);
|
|
125
|
+
expect(parsed.error).toContain("app_create no longer accepts html");
|
|
126
|
+
expect(files["src/index.html"]).toBeUndefined();
|
|
127
|
+
expect(files["src/main.tsx"]).toBeUndefined();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("rejects retired pages shortcut", async () => {
|
|
131
|
+
const files: Record<string, string> = {};
|
|
132
|
+
const app = makeMultifileApp({ name: "Custom App" });
|
|
133
|
+
const store = mockStore(app, files);
|
|
134
|
+
|
|
135
|
+
const result = await executeAppCreate(
|
|
136
|
+
{
|
|
137
|
+
name: "Custom App",
|
|
138
|
+
pages: {
|
|
139
|
+
"settings.html": "<!DOCTYPE html><html><body></body></html>",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
store,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
expect(result.isError).toBe(true);
|
|
146
|
+
const parsed = JSON.parse(result.content);
|
|
147
|
+
expect(parsed.error).toContain("app_create no longer accepts pages");
|
|
148
|
+
expect(files["src/index.html"]).toBeUndefined();
|
|
149
|
+
expect(files["src/main.tsx"]).toBeUndefined();
|
|
163
150
|
});
|
|
164
151
|
});
|
|
165
152
|
|
|
@@ -178,6 +178,7 @@ function makeIdleSession(opts?: {
|
|
|
178
178
|
updateClient: () => {},
|
|
179
179
|
setHostBrowserProxy: () => {},
|
|
180
180
|
setHostCuProxy: () => {},
|
|
181
|
+
setHostAppControlProxy: () => {},
|
|
181
182
|
addPreactivatedSkillId: () => {},
|
|
182
183
|
enqueueMessage: () => ({ queued: false, requestId: "noop" }),
|
|
183
184
|
hasAnyPendingConfirmation: () => false,
|
|
@@ -204,8 +205,9 @@ function makeIdleSession(opts?: {
|
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
/**
|
|
207
|
-
* Conversation whose agent loop emits a confirmation_request
|
|
208
|
-
*
|
|
208
|
+
* Conversation whose agent loop emits a confirmation_request. The mock
|
|
209
|
+
* self-registers in pendingInteractions (as PermissionPrompter.prompt() does)
|
|
210
|
+
* so the /v1/confirm endpoint can route the response.
|
|
209
211
|
*/
|
|
210
212
|
function makeConfirmationEmittingSession(opts?: {
|
|
211
213
|
onConfirmation?: (requestId: string, decision: string) => void;
|
|
@@ -241,6 +243,7 @@ function makeConfirmationEmittingSession(opts?: {
|
|
|
241
243
|
updateClient: () => {},
|
|
242
244
|
setHostBrowserProxy: () => {},
|
|
243
245
|
setHostCuProxy: () => {},
|
|
246
|
+
setHostAppControlProxy: () => {},
|
|
244
247
|
addPreactivatedSkillId: () => {},
|
|
245
248
|
enqueueMessage: () => ({ queued: false, requestId: "noop" }),
|
|
246
249
|
hasAnyPendingConfirmation: () => false,
|
|
@@ -249,8 +252,22 @@ function makeConfirmationEmittingSession(opts?: {
|
|
|
249
252
|
_messageId: string,
|
|
250
253
|
onEvent: (msg: ServerMessage) => void,
|
|
251
254
|
) => {
|
|
252
|
-
//
|
|
253
|
-
// the
|
|
255
|
+
// Simulate PermissionPrompter.prompt(): self-register in pendingInteractions
|
|
256
|
+
// before emitting the SSE event (registration no longer happens via broadcastMessage).
|
|
257
|
+
pendingInteractions.register(reqId, {
|
|
258
|
+
conversationId: "conv-auto",
|
|
259
|
+
kind: "confirmation",
|
|
260
|
+
confirmationDetails: {
|
|
261
|
+
toolName: tool,
|
|
262
|
+
input: { command: "ls" },
|
|
263
|
+
riskLevel: "medium",
|
|
264
|
+
allowlistOptions: [
|
|
265
|
+
{ label: "Allow ls", description: "Allow ls command", pattern: "ls" },
|
|
266
|
+
],
|
|
267
|
+
scopeOptions: [{ label: "This conversation", scope: "session" }],
|
|
268
|
+
persistentDecisionsAllowed: true,
|
|
269
|
+
},
|
|
270
|
+
});
|
|
254
271
|
onEvent({
|
|
255
272
|
type: "confirmation_request",
|
|
256
273
|
requestId: reqId,
|
|
@@ -620,8 +637,8 @@ describe("standalone approval endpoints — HTTP layer", () => {
|
|
|
620
637
|
|
|
621
638
|
// ── Hub publisher integration ────────────────────────────────────────
|
|
622
639
|
|
|
623
|
-
describe("
|
|
624
|
-
test("confirmation_request
|
|
640
|
+
describe("full round-trip: emit → register → confirm", () => {
|
|
641
|
+
test("confirmation_request: self-registered interaction resolves via /v1/confirm", async () => {
|
|
625
642
|
const confirmReceived: Array<{
|
|
626
643
|
requestId: string;
|
|
627
644
|
decision: string;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for machineName field in AssistantEventHub client registration.
|
|
3
|
+
*
|
|
4
|
+
* Validates:
|
|
5
|
+
* - subscribing with machineName set results in listClients() returning the name
|
|
6
|
+
* - subscribing without machineName results in listClients() returning undefined
|
|
7
|
+
*/
|
|
8
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
mock.module("../util/logger.js", () => ({
|
|
11
|
+
getLogger: () =>
|
|
12
|
+
new Proxy({} as Record<string, unknown>, {
|
|
13
|
+
get: () => () => {},
|
|
14
|
+
}),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
mock.module("../config/loader.js", () => ({
|
|
18
|
+
getConfig: () => ({
|
|
19
|
+
ui: {},
|
|
20
|
+
model: "test",
|
|
21
|
+
provider: "test",
|
|
22
|
+
memory: { enabled: false },
|
|
23
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
24
|
+
secretDetection: { enabled: false },
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
29
|
+
import { AssistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
30
|
+
import { handleSubscribeAssistantEvents } from "../runtime/routes/events-routes.js";
|
|
31
|
+
|
|
32
|
+
initializeDb();
|
|
33
|
+
|
|
34
|
+
describe("AssistantEventHub — machineName", () => {
|
|
35
|
+
test("subscribing with machineName returns it from listClients()", () => {
|
|
36
|
+
const ac = new AbortController();
|
|
37
|
+
const hub = new AssistantEventHub();
|
|
38
|
+
|
|
39
|
+
handleSubscribeAssistantEvents(
|
|
40
|
+
{
|
|
41
|
+
headers: {
|
|
42
|
+
"x-vellum-client-id": "client-with-name-001",
|
|
43
|
+
"x-vellum-interface-id": "macos",
|
|
44
|
+
"x-vellum-machine-name": "alice-mbp.local",
|
|
45
|
+
},
|
|
46
|
+
abortSignal: ac.signal,
|
|
47
|
+
},
|
|
48
|
+
{ hub },
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const clients = hub.listClients();
|
|
52
|
+
const entry = clients.find((c) => c.clientId === "client-with-name-001");
|
|
53
|
+
expect(entry).toBeDefined();
|
|
54
|
+
expect(entry?.machineName).toBe("alice-mbp.local");
|
|
55
|
+
|
|
56
|
+
ac.abort();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("subscribing without machineName returns undefined from listClients()", () => {
|
|
60
|
+
const ac = new AbortController();
|
|
61
|
+
const hub = new AssistantEventHub();
|
|
62
|
+
|
|
63
|
+
handleSubscribeAssistantEvents(
|
|
64
|
+
{
|
|
65
|
+
headers: {
|
|
66
|
+
"x-vellum-client-id": "client-without-name-001",
|
|
67
|
+
"x-vellum-interface-id": "macos",
|
|
68
|
+
},
|
|
69
|
+
abortSignal: ac.signal,
|
|
70
|
+
},
|
|
71
|
+
{ hub },
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const clients = hub.listClients();
|
|
75
|
+
const entry = clients.find(
|
|
76
|
+
(c) => c.clientId === "client-without-name-001",
|
|
77
|
+
);
|
|
78
|
+
expect(entry).toBeDefined();
|
|
79
|
+
expect(entry?.machineName).toBeUndefined();
|
|
80
|
+
|
|
81
|
+
ac.abort();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("machineName is trimmed when set", () => {
|
|
85
|
+
const ac = new AbortController();
|
|
86
|
+
const hub = new AssistantEventHub();
|
|
87
|
+
|
|
88
|
+
handleSubscribeAssistantEvents(
|
|
89
|
+
{
|
|
90
|
+
headers: {
|
|
91
|
+
"x-vellum-client-id": "client-with-trimmed-name-001",
|
|
92
|
+
"x-vellum-interface-id": "macos",
|
|
93
|
+
"x-vellum-machine-name": " bob-mbp.local ",
|
|
94
|
+
},
|
|
95
|
+
abortSignal: ac.signal,
|
|
96
|
+
},
|
|
97
|
+
{ hub },
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const clients = hub.listClients();
|
|
101
|
+
const entry = clients.find(
|
|
102
|
+
(c) => c.clientId === "client-with-trimmed-name-001",
|
|
103
|
+
);
|
|
104
|
+
expect(entry).toBeDefined();
|
|
105
|
+
expect(entry?.machineName).toBe("bob-mbp.local");
|
|
106
|
+
|
|
107
|
+
ac.abort();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("direct hub subscribe with machineName returns it from listClients()", () => {
|
|
111
|
+
const hub = new AssistantEventHub();
|
|
112
|
+
|
|
113
|
+
hub.subscribe({
|
|
114
|
+
type: "client",
|
|
115
|
+
clientId: "direct-client-001",
|
|
116
|
+
interfaceId: "macos",
|
|
117
|
+
capabilities: ["host_bash"],
|
|
118
|
+
machineName: "charlie-mbp.local",
|
|
119
|
+
callback: () => {},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const clients = hub.listClients();
|
|
123
|
+
const entry = clients.find((c) => c.clientId === "direct-client-001");
|
|
124
|
+
expect(entry).toBeDefined();
|
|
125
|
+
expect(entry?.machineName).toBe("charlie-mbp.local");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("direct hub subscribe without machineName returns undefined from listClients()", () => {
|
|
129
|
+
const hub = new AssistantEventHub();
|
|
130
|
+
|
|
131
|
+
hub.subscribe({
|
|
132
|
+
type: "client",
|
|
133
|
+
clientId: "direct-client-no-name-001",
|
|
134
|
+
interfaceId: "macos",
|
|
135
|
+
capabilities: ["host_bash"],
|
|
136
|
+
callback: () => {},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const clients = hub.listClients();
|
|
140
|
+
const entry = clients.find(
|
|
141
|
+
(c) => c.clientId === "direct-client-no-name-001",
|
|
142
|
+
);
|
|
143
|
+
expect(entry).toBeDefined();
|
|
144
|
+
expect(entry?.machineName).toBeUndefined();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for targeted delivery in AssistantEventHub.
|
|
3
|
+
*
|
|
4
|
+
* Validates:
|
|
5
|
+
* - hub.publish(event, { targetClientId }) delivers only to the named client,
|
|
6
|
+
* even when that subscriber's filter.conversationId doesn't match.
|
|
7
|
+
* - hub.publish(event, { targetClientId }) does NOT deliver to other clients.
|
|
8
|
+
* - hub.publish(event, { targetClientId, targetCapability }) skips subscribers
|
|
9
|
+
* that don't have the required capability.
|
|
10
|
+
* - hub.publish(event, { targetCapability }) (untargeted) still applies
|
|
11
|
+
* conversation scoping normally.
|
|
12
|
+
* - getClientById() returns the correct entry or undefined.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, expect, test } from "bun:test";
|
|
15
|
+
|
|
16
|
+
import type { AssistantEvent } from "../runtime/assistant-event.js";
|
|
17
|
+
import { AssistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
18
|
+
|
|
19
|
+
function makeEvent(overrides: Partial<AssistantEvent> = {}): AssistantEvent {
|
|
20
|
+
return {
|
|
21
|
+
id: "evt_test",
|
|
22
|
+
conversationId: "sess_web",
|
|
23
|
+
emittedAt: "2026-05-03T00:00:00.000Z",
|
|
24
|
+
message: {
|
|
25
|
+
type: "assistant_text_delta",
|
|
26
|
+
conversationId: "sess_web",
|
|
27
|
+
text: "hi",
|
|
28
|
+
},
|
|
29
|
+
...overrides,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Targeted delivery ─────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
describe("AssistantEventHub — targeted delivery (targetClientId)", () => {
|
|
36
|
+
test("delivers only to the named client, bypassing conversation filter", async () => {
|
|
37
|
+
const hub = new AssistantEventHub();
|
|
38
|
+
const receivedA: AssistantEvent[] = [];
|
|
39
|
+
const receivedB: AssistantEvent[] = [];
|
|
40
|
+
|
|
41
|
+
// client-a is subscribed to "sess_macos" — different from the event's "sess_web"
|
|
42
|
+
hub.subscribe({
|
|
43
|
+
type: "client",
|
|
44
|
+
clientId: "client-a",
|
|
45
|
+
interfaceId: "macos",
|
|
46
|
+
capabilities: ["host_bash"],
|
|
47
|
+
filter: { conversationId: "sess_macos" },
|
|
48
|
+
callback: (e) => {
|
|
49
|
+
receivedA.push(e);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// client-b is subscribed to "sess_web" — same as the event's conversationId
|
|
54
|
+
hub.subscribe({
|
|
55
|
+
type: "client",
|
|
56
|
+
clientId: "client-b",
|
|
57
|
+
interfaceId: "macos",
|
|
58
|
+
capabilities: ["host_bash"],
|
|
59
|
+
filter: { conversationId: "sess_web" },
|
|
60
|
+
callback: (e) => {
|
|
61
|
+
receivedB.push(e);
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Target client-a specifically — should bypass its conversation filter
|
|
66
|
+
await hub.publish(makeEvent({ conversationId: "sess_web" }), {
|
|
67
|
+
targetClientId: "client-a",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// client-a receives it despite mismatched conversationId
|
|
71
|
+
expect(receivedA).toHaveLength(1);
|
|
72
|
+
// client-b does NOT receive it even though its conversationId matches
|
|
73
|
+
expect(receivedB).toHaveLength(0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("does not deliver to a client with a different clientId", async () => {
|
|
77
|
+
const hub = new AssistantEventHub();
|
|
78
|
+
const receivedA: AssistantEvent[] = [];
|
|
79
|
+
const receivedB: AssistantEvent[] = [];
|
|
80
|
+
|
|
81
|
+
hub.subscribe({
|
|
82
|
+
type: "client",
|
|
83
|
+
clientId: "client-a",
|
|
84
|
+
interfaceId: "macos",
|
|
85
|
+
capabilities: ["host_bash"],
|
|
86
|
+
callback: (e) => {
|
|
87
|
+
receivedA.push(e);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
hub.subscribe({
|
|
92
|
+
type: "client",
|
|
93
|
+
clientId: "client-b",
|
|
94
|
+
interfaceId: "macos",
|
|
95
|
+
capabilities: ["host_bash"],
|
|
96
|
+
callback: (e) => {
|
|
97
|
+
receivedB.push(e);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await hub.publish(makeEvent(), { targetClientId: "client-a" });
|
|
102
|
+
|
|
103
|
+
expect(receivedA).toHaveLength(1);
|
|
104
|
+
expect(receivedB).toHaveLength(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("targeted delivery with wrong capability does not deliver", async () => {
|
|
108
|
+
const hub = new AssistantEventHub();
|
|
109
|
+
const receivedA: AssistantEvent[] = [];
|
|
110
|
+
|
|
111
|
+
// client-a only has host_file capability, NOT host_bash
|
|
112
|
+
hub.subscribe({
|
|
113
|
+
type: "client",
|
|
114
|
+
clientId: "client-a",
|
|
115
|
+
interfaceId: "macos",
|
|
116
|
+
capabilities: ["host_file"],
|
|
117
|
+
callback: (e) => {
|
|
118
|
+
receivedA.push(e);
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await hub.publish(makeEvent(), {
|
|
123
|
+
targetClientId: "client-a",
|
|
124
|
+
targetCapability: "host_bash",
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// client-a is the target but lacks the required capability — not delivered
|
|
128
|
+
expect(receivedA).toHaveLength(0);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("targeted delivery with matching capability delivers", async () => {
|
|
132
|
+
const hub = new AssistantEventHub();
|
|
133
|
+
const receivedA: AssistantEvent[] = [];
|
|
134
|
+
|
|
135
|
+
hub.subscribe({
|
|
136
|
+
type: "client",
|
|
137
|
+
clientId: "client-a",
|
|
138
|
+
interfaceId: "macos",
|
|
139
|
+
capabilities: ["host_bash"],
|
|
140
|
+
callback: (e) => {
|
|
141
|
+
receivedA.push(e);
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await hub.publish(makeEvent(), {
|
|
146
|
+
targetClientId: "client-a",
|
|
147
|
+
targetCapability: "host_bash",
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(receivedA).toHaveLength(1);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("process-type subscriber is never matched by targetClientId", async () => {
|
|
154
|
+
const hub = new AssistantEventHub();
|
|
155
|
+
const received: AssistantEvent[] = [];
|
|
156
|
+
|
|
157
|
+
hub.subscribe({
|
|
158
|
+
type: "process",
|
|
159
|
+
callback: (e) => {
|
|
160
|
+
received.push(e);
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
await hub.publish(makeEvent(), { targetClientId: "some-client" });
|
|
165
|
+
|
|
166
|
+
// Process subscribers have no clientId — they should never receive targeted events
|
|
167
|
+
expect(received).toHaveLength(0);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// ── Untargeted delivery unchanged ─────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
describe("AssistantEventHub — untargeted capability targeting is unchanged", () => {
|
|
174
|
+
test("targetCapability without targetClientId still applies conversation scoping", async () => {
|
|
175
|
+
const hub = new AssistantEventHub();
|
|
176
|
+
const receivedA: AssistantEvent[] = [];
|
|
177
|
+
const receivedB: AssistantEvent[] = [];
|
|
178
|
+
|
|
179
|
+
hub.subscribe({
|
|
180
|
+
type: "client",
|
|
181
|
+
clientId: "client-a",
|
|
182
|
+
interfaceId: "macos",
|
|
183
|
+
capabilities: ["host_bash"],
|
|
184
|
+
filter: { conversationId: "sess_A" },
|
|
185
|
+
callback: (e) => {
|
|
186
|
+
receivedA.push(e);
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
hub.subscribe({
|
|
191
|
+
type: "client",
|
|
192
|
+
clientId: "client-b",
|
|
193
|
+
interfaceId: "macos",
|
|
194
|
+
capabilities: ["host_bash"],
|
|
195
|
+
filter: { conversationId: "sess_B" },
|
|
196
|
+
callback: (e) => {
|
|
197
|
+
receivedB.push(e);
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await hub.publish(makeEvent({ conversationId: "sess_A" }), {
|
|
202
|
+
targetCapability: "host_bash",
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect(receivedA).toHaveLength(1);
|
|
206
|
+
expect(receivedB).toHaveLength(0);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// ── getClientById ─────────────────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
describe("AssistantEventHub — getClientById()", () => {
|
|
213
|
+
test("returns the client entry for the given clientId", () => {
|
|
214
|
+
const hub = new AssistantEventHub();
|
|
215
|
+
|
|
216
|
+
hub.subscribe({
|
|
217
|
+
type: "client",
|
|
218
|
+
clientId: "client-x",
|
|
219
|
+
interfaceId: "macos",
|
|
220
|
+
capabilities: ["host_bash"],
|
|
221
|
+
callback: () => {},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const entry = hub.getClientById("client-x");
|
|
225
|
+
expect(entry).toBeDefined();
|
|
226
|
+
expect(entry?.clientId).toBe("client-x");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("returns undefined when no client has the given clientId", () => {
|
|
230
|
+
const hub = new AssistantEventHub();
|
|
231
|
+
|
|
232
|
+
hub.subscribe({
|
|
233
|
+
type: "client",
|
|
234
|
+
clientId: "client-x",
|
|
235
|
+
interfaceId: "macos",
|
|
236
|
+
capabilities: ["host_bash"],
|
|
237
|
+
callback: () => {},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(hub.getClientById("client-y")).toBeUndefined();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("returns undefined after the subscriber is disposed", () => {
|
|
244
|
+
const hub = new AssistantEventHub();
|
|
245
|
+
|
|
246
|
+
const sub = hub.subscribe({
|
|
247
|
+
type: "client",
|
|
248
|
+
clientId: "client-x",
|
|
249
|
+
interfaceId: "macos",
|
|
250
|
+
capabilities: ["host_bash"],
|
|
251
|
+
callback: () => {},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
sub.dispose();
|
|
255
|
+
expect(hub.getClientById("client-x")).toBeUndefined();
|
|
256
|
+
});
|
|
257
|
+
});
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
import type { AssistantEvent } from "../runtime/assistant-event.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AssistantEventHub,
|
|
6
|
+
broadcastMessage,
|
|
7
|
+
capabilityForMessageType,
|
|
8
|
+
} from "../runtime/assistant-event-hub.js";
|
|
9
|
+
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
5
10
|
|
|
6
11
|
function makeEvent(overrides: Partial<AssistantEvent> = {}): AssistantEvent {
|
|
7
12
|
return {
|
|
@@ -350,3 +355,105 @@ describe("AssistantEventHub — re-entrancy / snapshot isolation", () => {
|
|
|
350
355
|
expect(received).toHaveLength(1);
|
|
351
356
|
});
|
|
352
357
|
});
|
|
358
|
+
|
|
359
|
+
// ── capabilityForMessageType — host-prefix routing ───────────────────────────
|
|
360
|
+
|
|
361
|
+
describe("capabilityForMessageType — host-prefix routing", () => {
|
|
362
|
+
test("two-segment domains map to their capability", () => {
|
|
363
|
+
expect(capabilityForMessageType("host_bash_request")).toBe("host_bash");
|
|
364
|
+
expect(capabilityForMessageType("host_bash_cancel")).toBe("host_bash");
|
|
365
|
+
expect(capabilityForMessageType("host_file_request")).toBe("host_file");
|
|
366
|
+
expect(capabilityForMessageType("host_cu_request")).toBe("host_cu");
|
|
367
|
+
expect(capabilityForMessageType("host_cu_cancel")).toBe("host_cu");
|
|
368
|
+
expect(capabilityForMessageType("host_browser_request")).toBe(
|
|
369
|
+
"host_browser",
|
|
370
|
+
);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test("host_transfer_* piggybacks on host_file capability", () => {
|
|
374
|
+
expect(capabilityForMessageType("host_transfer_request")).toBe("host_file");
|
|
375
|
+
expect(capabilityForMessageType("host_transfer_cancel")).toBe("host_file");
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("three-segment host_app_control routes to its own capability (longest-prefix wins)", () => {
|
|
379
|
+
expect(capabilityForMessageType("host_app_control_request")).toBe(
|
|
380
|
+
"host_app_control",
|
|
381
|
+
);
|
|
382
|
+
expect(capabilityForMessageType("host_app_control_cancel")).toBe(
|
|
383
|
+
"host_app_control",
|
|
384
|
+
);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test("non-host messages return undefined (broadcast)", () => {
|
|
388
|
+
expect(capabilityForMessageType("assistant_text_delta")).toBeUndefined();
|
|
389
|
+
expect(capabilityForMessageType("confirmation_request")).toBeUndefined();
|
|
390
|
+
expect(
|
|
391
|
+
capabilityForMessageType("conversation_list_invalidated"),
|
|
392
|
+
).toBeUndefined();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test("unknown host_<domain>_* prefixes return undefined", () => {
|
|
396
|
+
expect(capabilityForMessageType("host_unknown_request")).toBeUndefined();
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// ── broadcastMessage — pending interaction registration ─────────────────────
|
|
401
|
+
//
|
|
402
|
+
// Host proxy interactions (host_bash, host_cu, host_file, host_browser,
|
|
403
|
+
// host_app_control, host_transfer) are registered in pendingInteractions by
|
|
404
|
+
// the proxy itself (in its request() method), not by the event hub. This
|
|
405
|
+
// avoids overwriting the RPC lifecycle state (rpcResolve/rpcReject/timer)
|
|
406
|
+
// that the proxy stores alongside conversationId/kind.
|
|
407
|
+
//
|
|
408
|
+
// confirmation_request and secret_request are also NOT registered here — the
|
|
409
|
+
// prompters (PermissionPrompter, SecretPrompter) self-register in their
|
|
410
|
+
// prompt() methods, just like the host proxies. broadcastMessage is purely a
|
|
411
|
+
// message-delivery mechanism; it has no registration side effects.
|
|
412
|
+
|
|
413
|
+
describe("broadcastMessage — pending interaction registration", () => {
|
|
414
|
+
beforeEach(() => {
|
|
415
|
+
pendingInteractions.clear();
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("does NOT register confirmation_request — PermissionPrompter self-registers", () => {
|
|
419
|
+
broadcastMessage({
|
|
420
|
+
type: "confirmation_request",
|
|
421
|
+
requestId: "req-confirm-1",
|
|
422
|
+
conversationId: "conv-1",
|
|
423
|
+
toolName: "bash",
|
|
424
|
+
input: { command: "rm -rf /" },
|
|
425
|
+
riskLevel: "high",
|
|
426
|
+
executionTarget: "sandbox",
|
|
427
|
+
allowlistOptions: [],
|
|
428
|
+
scopeOptions: [],
|
|
429
|
+
} as never);
|
|
430
|
+
|
|
431
|
+
expect(pendingInteractions.get("req-confirm-1")).toBeUndefined();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("does NOT register secret_request — SecretPrompter self-registers", () => {
|
|
435
|
+
broadcastMessage({
|
|
436
|
+
type: "secret_request",
|
|
437
|
+
requestId: "req-secret-1",
|
|
438
|
+
conversationId: "conv-1",
|
|
439
|
+
service: "github",
|
|
440
|
+
field: "token",
|
|
441
|
+
} as never);
|
|
442
|
+
|
|
443
|
+
expect(pendingInteractions.get("req-secret-1")).toBeUndefined();
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("does NOT register host proxy requests — proxies self-register", () => {
|
|
447
|
+
// host_bash, host_cu, host_file, host_browser, host_app_control, and
|
|
448
|
+
// host_transfer are registered by the proxy in request(), not here.
|
|
449
|
+
broadcastMessage({
|
|
450
|
+
type: "host_bash_request",
|
|
451
|
+
requestId: "req-bash-1",
|
|
452
|
+
conversationId: "conv-1",
|
|
453
|
+
command: "echo hi",
|
|
454
|
+
timeout_ms: 1000,
|
|
455
|
+
} as never);
|
|
456
|
+
|
|
457
|
+
expect(pendingInteractions.get("req-bash-1")).toBeUndefined();
|
|
458
|
+
});
|
|
459
|
+
});
|