@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
|
@@ -115,8 +115,6 @@ describe("resolveSlash command contract", () => {
|
|
|
115
115
|
"/context foo",
|
|
116
116
|
"/models foo",
|
|
117
117
|
"/status foo",
|
|
118
|
-
"/pair foo",
|
|
119
|
-
"/pair",
|
|
120
118
|
"/btw",
|
|
121
119
|
];
|
|
122
120
|
|
|
@@ -138,7 +136,6 @@ describe("classifySlash is a pure classifier matching resolveSlash kinds", () =>
|
|
|
138
136
|
input: string;
|
|
139
137
|
kind: "passthrough" | "compact" | "unknown";
|
|
140
138
|
}> = [
|
|
141
|
-
{ input: "/pair", kind: "passthrough" },
|
|
142
139
|
{ input: "/models", kind: "unknown" },
|
|
143
140
|
{ input: "/context", kind: "unknown" },
|
|
144
141
|
{ input: "/status", kind: "unknown" },
|
|
@@ -149,7 +146,6 @@ describe("classifySlash is a pure classifier matching resolveSlash kinds", () =>
|
|
|
149
146
|
{ input: "/opus", kind: "unknown" },
|
|
150
147
|
{ input: "hello", kind: "passthrough" },
|
|
151
148
|
{ input: " /compact ", kind: "compact" },
|
|
152
|
-
{ input: "/pair foo", kind: "passthrough" },
|
|
153
149
|
{ input: "/models foo", kind: "passthrough" },
|
|
154
150
|
];
|
|
155
151
|
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createSurfaceMutex,
|
|
5
|
+
handleSurfaceAction,
|
|
6
|
+
type SurfaceConversationContext,
|
|
7
|
+
surfaceProxyResolver,
|
|
8
|
+
} from "../daemon/conversation-surfaces.js";
|
|
9
|
+
import type {
|
|
10
|
+
ServerMessage,
|
|
11
|
+
SurfaceData,
|
|
12
|
+
SurfaceType,
|
|
13
|
+
UiSurfaceShow,
|
|
14
|
+
} from "../daemon/message-protocol.js";
|
|
15
|
+
import type { UserMessageAttachment } from "../daemon/message-types/shared.js";
|
|
16
|
+
|
|
17
|
+
interface ProcessMessageCall {
|
|
18
|
+
content: string;
|
|
19
|
+
attachments: UserMessageAttachment[];
|
|
20
|
+
requestId?: string;
|
|
21
|
+
activeSurfaceId?: string;
|
|
22
|
+
displayContent?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function makeContext(sent: ServerMessage[] = []): SurfaceConversationContext & {
|
|
26
|
+
processMessageCalls: ProcessMessageCall[];
|
|
27
|
+
} {
|
|
28
|
+
const processMessageCalls: ProcessMessageCall[] = [];
|
|
29
|
+
return {
|
|
30
|
+
conversationId: "conv-1",
|
|
31
|
+
traceEmitter: { emit: () => {} },
|
|
32
|
+
sendToClient: (msg: ServerMessage) => sent.push(msg),
|
|
33
|
+
pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
|
|
34
|
+
lastSurfaceAction: new Map<
|
|
35
|
+
string,
|
|
36
|
+
{ actionId: string; data?: Record<string, unknown> }
|
|
37
|
+
>(),
|
|
38
|
+
surfaceState: new Map<
|
|
39
|
+
string,
|
|
40
|
+
{
|
|
41
|
+
surfaceType: SurfaceType;
|
|
42
|
+
data: SurfaceData;
|
|
43
|
+
title?: string;
|
|
44
|
+
actions?: Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
label: string;
|
|
47
|
+
style?: string;
|
|
48
|
+
data?: Record<string, unknown>;
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
>(),
|
|
52
|
+
surfaceUndoStacks: new Map<string, string[]>(),
|
|
53
|
+
accumulatedSurfaceState: new Map<string, Record<string, unknown>>(),
|
|
54
|
+
surfaceActionRequestIds: new Set<string>(),
|
|
55
|
+
currentTurnSurfaces: [],
|
|
56
|
+
isProcessing: () => false,
|
|
57
|
+
enqueueMessage: () => ({ queued: false, requestId: "req-1" }),
|
|
58
|
+
getQueueDepth: () => 0,
|
|
59
|
+
processMessage: async (
|
|
60
|
+
content: string,
|
|
61
|
+
attachments: UserMessageAttachment[],
|
|
62
|
+
_onEvent: (msg: ServerMessage) => void,
|
|
63
|
+
requestId?: string,
|
|
64
|
+
activeSurfaceId?: string,
|
|
65
|
+
_currentPage?: string,
|
|
66
|
+
_options?: { isInteractive?: boolean },
|
|
67
|
+
displayContent?: string,
|
|
68
|
+
) => {
|
|
69
|
+
processMessageCalls.push({
|
|
70
|
+
content,
|
|
71
|
+
attachments,
|
|
72
|
+
requestId,
|
|
73
|
+
activeSurfaceId,
|
|
74
|
+
displayContent,
|
|
75
|
+
});
|
|
76
|
+
return "msg-1";
|
|
77
|
+
},
|
|
78
|
+
withSurface: createSurfaceMutex(),
|
|
79
|
+
processMessageCalls,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
describe("surface action delivery to assistant", () => {
|
|
84
|
+
test("table action button click triggers processMessage with action content", async () => {
|
|
85
|
+
const sent: ServerMessage[] = [];
|
|
86
|
+
const ctx = makeContext(sent);
|
|
87
|
+
|
|
88
|
+
// Step 1: Show a table surface with actions
|
|
89
|
+
const showResult = await surfaceProxyResolver(ctx, "ui_show", {
|
|
90
|
+
surface_type: "table",
|
|
91
|
+
title: "Newsletters",
|
|
92
|
+
data: {
|
|
93
|
+
columns: [
|
|
94
|
+
{ id: "sender", label: "Sender" },
|
|
95
|
+
{ id: "count", label: "Count" },
|
|
96
|
+
],
|
|
97
|
+
rows: [
|
|
98
|
+
{ id: "row-1", cells: { sender: "Newsletter A", count: "5" } },
|
|
99
|
+
{ id: "row-2", cells: { sender: "Newsletter B", count: "3" } },
|
|
100
|
+
],
|
|
101
|
+
selectionMode: "multiple",
|
|
102
|
+
},
|
|
103
|
+
actions: [
|
|
104
|
+
{ id: "archive", label: "Archive", style: "primary" },
|
|
105
|
+
{ id: "unsubscribe", label: "Unsubscribe", style: "destructive" },
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(showResult.isError).toBe(false);
|
|
110
|
+
expect(showResult.yieldToUser).toBe(true);
|
|
111
|
+
|
|
112
|
+
// Verify surface was shown and pending action was registered
|
|
113
|
+
const showMessage = sent.find(
|
|
114
|
+
(msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
|
|
115
|
+
) as UiSurfaceShow;
|
|
116
|
+
expect(showMessage).toBeDefined();
|
|
117
|
+
const surfaceId = showMessage.surfaceId;
|
|
118
|
+
expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(true);
|
|
119
|
+
expect(ctx.surfaceState.has(surfaceId)).toBe(true);
|
|
120
|
+
|
|
121
|
+
// Step 2: Simulate user clicking "Archive" with selected rows
|
|
122
|
+
const actionData = {
|
|
123
|
+
selectedIds: ["row-1", "row-2"],
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
await handleSurfaceAction(ctx, surfaceId, "archive", actionData);
|
|
127
|
+
|
|
128
|
+
// Step 3: Verify processMessage was called
|
|
129
|
+
expect(ctx.processMessageCalls.length).toBe(1);
|
|
130
|
+
const call = ctx.processMessageCalls[0];
|
|
131
|
+
expect(call.content).toContain("[User action on table surface:");
|
|
132
|
+
expect(call.content).toContain("archive");
|
|
133
|
+
expect(call.content).toContain("selectedIds");
|
|
134
|
+
expect(call.content).toContain("row-1");
|
|
135
|
+
expect(call.content).toContain("row-2");
|
|
136
|
+
expect(call.activeSurfaceId).toBe(surfaceId);
|
|
137
|
+
|
|
138
|
+
// Verify pending action was cleared
|
|
139
|
+
expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(false);
|
|
140
|
+
|
|
141
|
+
// Verify the requestId was tracked as a surface action
|
|
142
|
+
expect(ctx.surfaceActionRequestIds.size).toBe(1);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("table action without selection data still triggers processMessage", async () => {
|
|
146
|
+
const sent: ServerMessage[] = [];
|
|
147
|
+
const ctx = makeContext(sent);
|
|
148
|
+
|
|
149
|
+
// Show table surface
|
|
150
|
+
await surfaceProxyResolver(ctx, "ui_show", {
|
|
151
|
+
surface_type: "table",
|
|
152
|
+
title: "Emails",
|
|
153
|
+
data: {
|
|
154
|
+
columns: [{ id: "subject", label: "Subject" }],
|
|
155
|
+
rows: [{ id: "r1", cells: { subject: "Hello" } }],
|
|
156
|
+
},
|
|
157
|
+
actions: [{ id: "archive", label: "Archive" }],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const showMessage = sent.find(
|
|
161
|
+
(msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
|
|
162
|
+
) as UiSurfaceShow;
|
|
163
|
+
const surfaceId = showMessage.surfaceId;
|
|
164
|
+
|
|
165
|
+
// Click action WITHOUT selection data (data is undefined)
|
|
166
|
+
await handleSurfaceAction(ctx, surfaceId, "archive", undefined);
|
|
167
|
+
|
|
168
|
+
// processMessage must still be called
|
|
169
|
+
expect(ctx.processMessageCalls.length).toBe(1);
|
|
170
|
+
expect(ctx.processMessageCalls[0].content).toContain(
|
|
171
|
+
"[User action on table surface:",
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("action on history-restored surface (no pending) still processes", async () => {
|
|
176
|
+
const sent: ServerMessage[] = [];
|
|
177
|
+
const ctx = makeContext(sent);
|
|
178
|
+
|
|
179
|
+
// Simulate a history-restored surface: surfaceState exists, but
|
|
180
|
+
// pendingSurfaceActions does NOT have an entry.
|
|
181
|
+
ctx.surfaceState.set("hist-surface-1", {
|
|
182
|
+
surfaceType: "table",
|
|
183
|
+
data: {
|
|
184
|
+
columns: [{ id: "col", label: "Col" }],
|
|
185
|
+
rows: [],
|
|
186
|
+
} as unknown as SurfaceData,
|
|
187
|
+
title: "History Table",
|
|
188
|
+
actions: [{ id: "delete", label: "Delete" }],
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Click the action — should go through the history-restored path
|
|
192
|
+
await handleSurfaceAction(ctx, "hist-surface-1", "delete", {
|
|
193
|
+
selectedIds: ["row-1"],
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// processMessage should still be called
|
|
197
|
+
expect(ctx.processMessageCalls.length).toBe(1);
|
|
198
|
+
expect(ctx.processMessageCalls[0].content).toContain(
|
|
199
|
+
"[User action on app:",
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the surfaceProxyResolver's app_control_* dispatch branch.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the structure of cu-unified-flow.test.ts but exercises the
|
|
5
|
+
* sibling branch added for app-control: unavailability when no proxy is
|
|
6
|
+
* attached, end-to-end dispatch through HostAppControlProxy.request, and
|
|
7
|
+
* the local short-circuit for app_control_stop (no client round-trip).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
const sentMessages: unknown[] = [];
|
|
13
|
+
let mockHasClient = true;
|
|
14
|
+
|
|
15
|
+
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
16
|
+
broadcastMessage: (msg: unknown) => sentMessages.push(msg),
|
|
17
|
+
assistantEventHub: {
|
|
18
|
+
getMostRecentClientByCapability: (cap: string) =>
|
|
19
|
+
cap === "host_app_control" && mockHasClient
|
|
20
|
+
? { id: "mock-client" }
|
|
21
|
+
: null,
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
mock.module("../runtime/pending-interactions.js", () => ({
|
|
26
|
+
register: () => undefined,
|
|
27
|
+
resolve: () => undefined,
|
|
28
|
+
get: () => undefined,
|
|
29
|
+
getByKind: () => [],
|
|
30
|
+
getByConversation: () => [],
|
|
31
|
+
removeByConversation: () => {},
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
const { surfaceProxyResolver } =
|
|
35
|
+
await import("../daemon/conversation-surfaces.js");
|
|
36
|
+
const { HostAppControlProxy, _resetActiveAppControlConversationId } =
|
|
37
|
+
await import("../daemon/host-app-control-proxy.js");
|
|
38
|
+
type SurfaceConversationContext =
|
|
39
|
+
import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Test helpers
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Build a minimal SurfaceConversationContext with an optional
|
|
47
|
+
* hostAppControlProxy. Only the fields required by the app-control routing
|
|
48
|
+
* path are populated.
|
|
49
|
+
*/
|
|
50
|
+
function buildMockContext(
|
|
51
|
+
hostAppControlProxy?: InstanceType<typeof HostAppControlProxy>,
|
|
52
|
+
conversationId = "test-session",
|
|
53
|
+
setHostAppControlProxy?: (
|
|
54
|
+
proxy: InstanceType<typeof HostAppControlProxy> | undefined,
|
|
55
|
+
) => void,
|
|
56
|
+
): SurfaceConversationContext {
|
|
57
|
+
return {
|
|
58
|
+
conversationId,
|
|
59
|
+
traceEmitter: { emit: () => {} },
|
|
60
|
+
sendToClient: () => {},
|
|
61
|
+
pendingSurfaceActions: new Map(),
|
|
62
|
+
lastSurfaceAction: new Map(),
|
|
63
|
+
surfaceState: new Map(),
|
|
64
|
+
surfaceUndoStacks: new Map(),
|
|
65
|
+
accumulatedSurfaceState: new Map(),
|
|
66
|
+
surfaceActionRequestIds: new Set(),
|
|
67
|
+
currentTurnSurfaces: [],
|
|
68
|
+
hostAppControlProxy,
|
|
69
|
+
setHostAppControlProxy,
|
|
70
|
+
isProcessing: () => false,
|
|
71
|
+
enqueueMessage: () => ({ queued: false, requestId: "r1" }),
|
|
72
|
+
getQueueDepth: () => 0,
|
|
73
|
+
processMessage: async () => "",
|
|
74
|
+
withSurface: async (_id, fn) => fn(),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Tests
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
describe("surfaceProxyResolver — app-control tool routing", () => {
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
sentMessages.length = 0;
|
|
85
|
+
mockHasClient = true;
|
|
86
|
+
_resetActiveAppControlConversationId();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
afterEach(() => {
|
|
90
|
+
_resetActiveAppControlConversationId();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// -------------------------------------------------------------------------
|
|
94
|
+
// Unavailability
|
|
95
|
+
// -------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
describe("no app-control proxy attached", () => {
|
|
98
|
+
test("returns isError result when ctx.hostAppControlProxy is undefined", async () => {
|
|
99
|
+
const ctx = buildMockContext(/* no proxy */);
|
|
100
|
+
|
|
101
|
+
const result = await surfaceProxyResolver(ctx, "app_control_observe", {
|
|
102
|
+
tool: "observe",
|
|
103
|
+
app: "com.example.editor",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(result.isError).toBe(true);
|
|
107
|
+
expect(result.content).toContain("not available");
|
|
108
|
+
expect(result.content).toContain("app-control");
|
|
109
|
+
// No envelope dispatched.
|
|
110
|
+
expect(sentMessages).toHaveLength(0);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("returns isError when proxy exists but no client is connected", async () => {
|
|
114
|
+
mockHasClient = false;
|
|
115
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
116
|
+
const ctx = buildMockContext(proxy);
|
|
117
|
+
|
|
118
|
+
const result = await surfaceProxyResolver(ctx, "app_control_observe", {
|
|
119
|
+
tool: "observe",
|
|
120
|
+
app: "com.example.editor",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(result.isError).toBe(true);
|
|
124
|
+
expect(result.content).toContain("not available");
|
|
125
|
+
expect(sentMessages).toHaveLength(0);
|
|
126
|
+
|
|
127
|
+
proxy.dispose();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("returns isError for app_control_stop when no proxy is attached", async () => {
|
|
131
|
+
const ctx = buildMockContext();
|
|
132
|
+
|
|
133
|
+
const result = await surfaceProxyResolver(ctx, "app_control_stop", {
|
|
134
|
+
tool: "stop",
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(result.isError).toBe(true);
|
|
138
|
+
expect(result.content).toContain("not available");
|
|
139
|
+
expect(sentMessages).toHaveLength(0);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// -------------------------------------------------------------------------
|
|
144
|
+
// Dispatch through proxy.request
|
|
145
|
+
// -------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
describe("non-stop tools dispatch through proxy.request", () => {
|
|
148
|
+
test("app_control_observe routes through proxy and returns observation", async () => {
|
|
149
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
150
|
+
const ctx = buildMockContext(proxy, "conv-1");
|
|
151
|
+
|
|
152
|
+
const resultPromise = surfaceProxyResolver(ctx, "app_control_observe", {
|
|
153
|
+
tool: "observe",
|
|
154
|
+
app: "com.example.editor",
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// The proxy fired exactly one host_app_control_request envelope.
|
|
158
|
+
expect(sentMessages).toHaveLength(1);
|
|
159
|
+
const sent = sentMessages[0] as Record<string, unknown>;
|
|
160
|
+
expect(sent.type).toBe("host_app_control_request");
|
|
161
|
+
expect(sent.toolName).toBe("app_control_observe");
|
|
162
|
+
expect(sent.conversationId).toBe("conv-1");
|
|
163
|
+
expect(sent.input).toEqual({
|
|
164
|
+
tool: "observe",
|
|
165
|
+
app: "com.example.editor",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const requestId = sent.requestId as string;
|
|
169
|
+
proxy.resolve(requestId, {
|
|
170
|
+
requestId: "ignored-by-proxy",
|
|
171
|
+
state: "running",
|
|
172
|
+
executionResult: "Window observed",
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const result = await resultPromise;
|
|
176
|
+
expect(result.isError).toBe(false);
|
|
177
|
+
expect(result.content).toContain("State: running");
|
|
178
|
+
expect(result.content).toContain("Window observed");
|
|
179
|
+
|
|
180
|
+
proxy.dispose();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// -------------------------------------------------------------------------
|
|
185
|
+
// Local short-circuit on app_control_stop
|
|
186
|
+
// -------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
describe("app_control_stop short-circuits locally", () => {
|
|
189
|
+
test("calls proxy.dispose() and returns a stopped summary without a client round-trip", async () => {
|
|
190
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
191
|
+
const ctx = buildMockContext(proxy);
|
|
192
|
+
|
|
193
|
+
let disposeCalls = 0;
|
|
194
|
+
const realDispose = proxy.dispose.bind(proxy);
|
|
195
|
+
proxy.dispose = () => {
|
|
196
|
+
disposeCalls++;
|
|
197
|
+
realDispose();
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
let requestCalls = 0;
|
|
201
|
+
const realRequest = proxy.request.bind(proxy);
|
|
202
|
+
proxy.request = (...args) => {
|
|
203
|
+
requestCalls++;
|
|
204
|
+
return realRequest(...args);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const result = await surfaceProxyResolver(ctx, "app_control_stop", {
|
|
208
|
+
tool: "stop",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
expect(result.isError).toBe(false);
|
|
212
|
+
expect(result.content.toLowerCase()).toContain("stopped");
|
|
213
|
+
expect(disposeCalls).toBe(1);
|
|
214
|
+
expect(requestCalls).toBe(0);
|
|
215
|
+
// No envelope dispatched for the local short-circuit.
|
|
216
|
+
expect(sentMessages).toHaveLength(0);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("clears the conversation reference via setHostAppControlProxy(undefined) when the setter is provided", async () => {
|
|
220
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
221
|
+
|
|
222
|
+
// Capture how the resolver clears the proxy reference. The setter
|
|
223
|
+
// mirrors Conversation.setHostAppControlProxy: dispose the existing
|
|
224
|
+
// proxy when transitioning to undefined.
|
|
225
|
+
const setterCalls: Array<unknown> = [];
|
|
226
|
+
let attached: InstanceType<typeof HostAppControlProxy> | undefined =
|
|
227
|
+
proxy;
|
|
228
|
+
const setter = (
|
|
229
|
+
next: InstanceType<typeof HostAppControlProxy> | undefined,
|
|
230
|
+
) => {
|
|
231
|
+
setterCalls.push(next);
|
|
232
|
+
if (attached && attached !== next) attached.dispose();
|
|
233
|
+
attached = next;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const ctx = buildMockContext(proxy, "conv-1", setter);
|
|
237
|
+
|
|
238
|
+
const result = await surfaceProxyResolver(ctx, "app_control_stop", {
|
|
239
|
+
tool: "stop",
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
expect(result.isError).toBe(false);
|
|
243
|
+
// The resolver invoked the setter with undefined exactly once.
|
|
244
|
+
expect(setterCalls).toEqual([undefined]);
|
|
245
|
+
expect(attached).toBeUndefined();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// -------------------------------------------------------------------------
|
|
250
|
+
// Discriminator injection (Gap A)
|
|
251
|
+
// -------------------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
describe("tool discriminator injection", () => {
|
|
254
|
+
test("injects `tool` derived from toolName when the agent input omits it", async () => {
|
|
255
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
256
|
+
const ctx = buildMockContext(proxy, "conv-1");
|
|
257
|
+
|
|
258
|
+
// Agent inputs do not carry the discriminator — the resolver has to
|
|
259
|
+
// synthesize it from `toolName` ("app_control_observe" → "observe")
|
|
260
|
+
// before forwarding to the proxy / desktop client.
|
|
261
|
+
const resultPromise = surfaceProxyResolver(ctx, "app_control_observe", {
|
|
262
|
+
app: "com.example.editor",
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
expect(sentMessages).toHaveLength(1);
|
|
266
|
+
const sent = sentMessages[0] as Record<string, unknown>;
|
|
267
|
+
expect(sent.input).toEqual({
|
|
268
|
+
tool: "observe",
|
|
269
|
+
app: "com.example.editor",
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const requestId = sent.requestId as string;
|
|
273
|
+
proxy.resolve(requestId, {
|
|
274
|
+
requestId: "ignored-by-proxy",
|
|
275
|
+
state: "running",
|
|
276
|
+
});
|
|
277
|
+
await resultPromise;
|
|
278
|
+
|
|
279
|
+
proxy.dispose();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('injects `tool: "start"` so the singleton-lock guard fires', async () => {
|
|
283
|
+
// Establish a lock owned by conv-other.
|
|
284
|
+
const ownerProxy = new HostAppControlProxy("conv-other");
|
|
285
|
+
const ownerCtrl = new AbortController();
|
|
286
|
+
const ownerPromise = ownerProxy.request(
|
|
287
|
+
"app_control_start",
|
|
288
|
+
{ tool: "start", app: "com.example.editor" },
|
|
289
|
+
"conv-other",
|
|
290
|
+
ownerCtrl.signal,
|
|
291
|
+
);
|
|
292
|
+
const ownerSent = sentMessages[0] as Record<string, unknown>;
|
|
293
|
+
ownerProxy.resolve(ownerSent.requestId as string, {
|
|
294
|
+
requestId: "ignored-by-proxy",
|
|
295
|
+
state: "running",
|
|
296
|
+
});
|
|
297
|
+
await ownerPromise;
|
|
298
|
+
sentMessages.length = 0;
|
|
299
|
+
|
|
300
|
+
// conv-1 attempts to start without a discriminator in its input. The
|
|
301
|
+
// resolver must inject `tool: "start"`, which causes the proxy's
|
|
302
|
+
// singleton-lock guard to fire and reject without dispatching.
|
|
303
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
304
|
+
const ctx = buildMockContext(proxy, "conv-1");
|
|
305
|
+
const result = await surfaceProxyResolver(ctx, "app_control_start", {
|
|
306
|
+
app: "com.example.editor",
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
expect(result.isError).toBe(true);
|
|
310
|
+
expect(result.content).toContain("conv-other");
|
|
311
|
+
expect(sentMessages).toHaveLength(0); // No envelope dispatched.
|
|
312
|
+
|
|
313
|
+
proxy.dispose();
|
|
314
|
+
ownerProxy.dispose();
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
});
|
|
@@ -17,12 +17,10 @@ import {
|
|
|
17
17
|
import type { AssistantConfig } from "../config/schema.js";
|
|
18
18
|
import {
|
|
19
19
|
CES_GRANT_AUDIT_FLAG_KEY,
|
|
20
|
-
CES_MANAGED_SIDECAR_FLAG_KEY,
|
|
21
20
|
CES_SECURE_INSTALL_FLAG_KEY,
|
|
22
21
|
CES_SHELL_LOCKDOWN_FLAG_KEY,
|
|
23
22
|
CES_TOOLS_FLAG_KEY,
|
|
24
23
|
isCesGrantAuditEnabled,
|
|
25
|
-
isCesManagedSidecarEnabled,
|
|
26
24
|
isCesSecureInstallEnabled,
|
|
27
25
|
isCesShellLockdownEnabled,
|
|
28
26
|
isCesToolsEnabled,
|
|
@@ -51,7 +49,6 @@ const ALL_CES_FLAG_KEYS = [
|
|
|
51
49
|
CES_SHELL_LOCKDOWN_FLAG_KEY,
|
|
52
50
|
CES_SECURE_INSTALL_FLAG_KEY,
|
|
53
51
|
CES_GRANT_AUDIT_FLAG_KEY,
|
|
54
|
-
CES_MANAGED_SIDECAR_FLAG_KEY,
|
|
55
52
|
] as const;
|
|
56
53
|
|
|
57
54
|
/** All CES predicate functions paired with their flag keys and expected defaults. */
|
|
@@ -80,12 +77,6 @@ const ALL_CES_PREDICATES = [
|
|
|
80
77
|
key: CES_GRANT_AUDIT_FLAG_KEY,
|
|
81
78
|
defaultEnabled: false,
|
|
82
79
|
},
|
|
83
|
-
{
|
|
84
|
-
name: "isCesManagedSidecarEnabled",
|
|
85
|
-
fn: isCesManagedSidecarEnabled,
|
|
86
|
-
key: CES_MANAGED_SIDECAR_FLAG_KEY,
|
|
87
|
-
defaultEnabled: true,
|
|
88
|
-
},
|
|
89
80
|
] as const;
|
|
90
81
|
|
|
91
82
|
// ---------------------------------------------------------------------------
|
|
@@ -180,7 +171,7 @@ describe("CES flags do not affect unrelated flags", () => {
|
|
|
180
171
|
expect(isAssistantFeatureFlagEnabled("browser", config)).toBe(true);
|
|
181
172
|
});
|
|
182
173
|
|
|
183
|
-
test("enabling all CES flags does not change
|
|
174
|
+
test("enabling all CES flags does not change unrelated default-open flags", () => {
|
|
184
175
|
const overrides: Record<string, boolean> = {};
|
|
185
176
|
for (const key of ALL_CES_FLAG_KEYS) {
|
|
186
177
|
overrides[key] = true;
|
|
@@ -188,7 +179,9 @@ describe("CES flags do not affect unrelated flags", () => {
|
|
|
188
179
|
_setOverridesForTesting(overrides);
|
|
189
180
|
const config = makeConfig();
|
|
190
181
|
|
|
191
|
-
//
|
|
192
|
-
expect(
|
|
182
|
+
// Unknown flags default open unless explicitly overridden.
|
|
183
|
+
expect(
|
|
184
|
+
isAssistantFeatureFlagEnabled("unrelated-default-open", config),
|
|
185
|
+
).toBe(true);
|
|
193
186
|
});
|
|
194
187
|
});
|