@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
|
@@ -14,16 +14,20 @@ import {
|
|
|
14
14
|
getMessages,
|
|
15
15
|
updateMessageContent,
|
|
16
16
|
} from "../memory/conversation-crud.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
assistantEventHub,
|
|
19
|
+
broadcastMessage,
|
|
20
|
+
} from "../runtime/assistant-event-hub.js";
|
|
18
21
|
import type {
|
|
19
22
|
InteractiveUiRequest,
|
|
20
23
|
InteractiveUiResult,
|
|
21
|
-
} from "../runtime/interactive-ui.js";
|
|
24
|
+
} from "../runtime/interactive-ui-types.js";
|
|
22
25
|
import type { ToolExecutionResult } from "../tools/types.js";
|
|
23
26
|
import { getLogger } from "../util/logger.js";
|
|
24
27
|
import { isPlainObject } from "../util/object.js";
|
|
25
28
|
import { buildConversationErrorMessage } from "./conversation-error.js";
|
|
26
29
|
import { launchConversation } from "./conversation-launch.js";
|
|
30
|
+
import type { HostAppControlProxy } from "./host-app-control-proxy.js";
|
|
27
31
|
import type { HostCuProxy } from "./host-cu-proxy.js";
|
|
28
32
|
import type {
|
|
29
33
|
CardSurfaceData,
|
|
@@ -41,6 +45,7 @@ import type {
|
|
|
41
45
|
} from "./message-protocol.js";
|
|
42
46
|
import { INTERACTIVE_SURFACE_TYPES } from "./message-protocol.js";
|
|
43
47
|
import type { ConversationTransportMetadata } from "./message-types/conversations.js";
|
|
48
|
+
import type { HostAppControlInput } from "./message-types/host-app-control.js";
|
|
44
49
|
import type { UserMessageAttachment } from "./message-types/shared.js";
|
|
45
50
|
import type { TrustContext } from "./trust-context.js";
|
|
46
51
|
|
|
@@ -320,6 +325,15 @@ export interface SurfaceConversationContext {
|
|
|
320
325
|
}>;
|
|
321
326
|
/** Optional proxy for delegating computer-use actions to a connected desktop client. */
|
|
322
327
|
hostCuProxy?: HostCuProxy;
|
|
328
|
+
/** Optional proxy for delegating per-app app-control actions to a connected desktop client. */
|
|
329
|
+
hostAppControlProxy?: HostAppControlProxy;
|
|
330
|
+
/**
|
|
331
|
+
* Setter that lets the resolver detach the conversation's app-control proxy
|
|
332
|
+
* after `app_control_stop`. Disposes the existing proxy when transitioning
|
|
333
|
+
* to undefined so subsequent tool calls cleanly fail with "unavailable"
|
|
334
|
+
* rather than dispatching to a torn-down proxy.
|
|
335
|
+
*/
|
|
336
|
+
setHostAppControlProxy?(proxy: HostAppControlProxy | undefined): void;
|
|
323
337
|
/** True when no interactive client is connected (headless / channel-only). */
|
|
324
338
|
readonly hasNoClient?: boolean;
|
|
325
339
|
isProcessing(): boolean;
|
|
@@ -1206,7 +1220,11 @@ export async function handleSurfaceAction(
|
|
|
1206
1220
|
|
|
1207
1221
|
const requestId = uuid();
|
|
1208
1222
|
ctx.surfaceActionRequestIds.add(requestId);
|
|
1209
|
-
|
|
1223
|
+
// Pass conversationId so events without an inline conversationId (e.g.
|
|
1224
|
+
// text_delta) are published with the correct conversation scope and
|
|
1225
|
+
// reach the SSE subscriber filtered to this conversation.
|
|
1226
|
+
const onEvent = (msg: ServerMessage) =>
|
|
1227
|
+
broadcastMessage(msg, ctx.conversationId);
|
|
1210
1228
|
|
|
1211
1229
|
ctx.traceEmitter.emit("request_received", "Surface action received", {
|
|
1212
1230
|
requestId,
|
|
@@ -1440,7 +1458,11 @@ export async function handleSurfaceAction(
|
|
|
1440
1458
|
|
|
1441
1459
|
const requestId = uuid();
|
|
1442
1460
|
ctx.surfaceActionRequestIds.add(requestId);
|
|
1443
|
-
|
|
1461
|
+
// Pass conversationId so events without an inline conversationId (e.g.
|
|
1462
|
+
// text_delta) are published with the correct conversation scope and
|
|
1463
|
+
// reach the SSE subscriber filtered to this conversation.
|
|
1464
|
+
const onEvent = (msg: ServerMessage) =>
|
|
1465
|
+
broadcastMessage(msg, ctx.conversationId);
|
|
1444
1466
|
|
|
1445
1467
|
ctx.traceEmitter.emit("request_received", "Surface action received", {
|
|
1446
1468
|
requestId,
|
|
@@ -1761,6 +1783,56 @@ export async function surfaceProxyResolver(
|
|
|
1761
1783
|
// Record the action and proxy to the connected desktop client
|
|
1762
1784
|
const reasoning =
|
|
1763
1785
|
typeof input.reasoning === "string" ? input.reasoning : undefined;
|
|
1786
|
+
const targetClientId =
|
|
1787
|
+
typeof input.target_client_id === "string" && input.target_client_id !== ""
|
|
1788
|
+
? input.target_client_id
|
|
1789
|
+
: undefined;
|
|
1790
|
+
|
|
1791
|
+
// Validate targetClientId before recordAction so an invalid ID does not
|
|
1792
|
+
// burn a step or pollute action history. (HostBashProxy / HostFileProxy
|
|
1793
|
+
// both validate at the tool-resolution layer before incrementing any
|
|
1794
|
+
// state; this mirrors that behaviour for CU.)
|
|
1795
|
+
if (targetClientId != null) {
|
|
1796
|
+
const client = assistantEventHub.getClientById(targetClientId);
|
|
1797
|
+
if (!client) {
|
|
1798
|
+
return {
|
|
1799
|
+
content: `No connected client with id '${targetClientId}'. Run \`assistant clients list --capability host_cu\` to see available clients.`,
|
|
1800
|
+
isError: true,
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
if (!client.capabilities.includes("host_cu")) {
|
|
1804
|
+
return {
|
|
1805
|
+
content: `Client '${targetClientId}' does not support host_cu. Run \`assistant clients list --capability host_cu\` to see available clients.`,
|
|
1806
|
+
isError: true,
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// Guard: require explicit targeting when multiple CU-capable clients are
|
|
1812
|
+
// connected. The tool schemas document target_client_id as "required when
|
|
1813
|
+
// multiple clients support host_cu" but nothing enforced it at runtime
|
|
1814
|
+
// until now. Without this guard, the request would broadcast to all
|
|
1815
|
+
// capable clients simultaneously, causing the same CU action to execute
|
|
1816
|
+
// on multiple machines.
|
|
1817
|
+
//
|
|
1818
|
+
// Asymmetry with host_bash / host_file (host-shell.ts): the bash/file
|
|
1819
|
+
// guard additionally checks `transportInterface != null &&
|
|
1820
|
+
// !supportsHostProxy(transportInterface)` and so only fires for non-host-
|
|
1821
|
+
// proxy transports (web, Slack). For CU that check would be a no-op:
|
|
1822
|
+
// every host_cu-capable client is host-proxy-capable by definition
|
|
1823
|
+
// (host_cu only ships on macOS and the Chrome extension), so there is no
|
|
1824
|
+
// host_cu-capable transport for which auto-routing-to-self would be
|
|
1825
|
+
// appropriate. We therefore fire whenever there is genuine ambiguity.
|
|
1826
|
+
if (targetClientId == null) {
|
|
1827
|
+
const cuClients = assistantEventHub.listClientsByCapability("host_cu");
|
|
1828
|
+
if (cuClients.length > 1) {
|
|
1829
|
+
return {
|
|
1830
|
+
content: `Error: multiple clients support host_cu. Specify which client to target with \`target_client_id\`. Run \`assistant clients list --capability host_cu\` to see client IDs and labels.`,
|
|
1831
|
+
isError: true,
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1764
1836
|
ctx.hostCuProxy.recordAction(toolName, input, reasoning);
|
|
1765
1837
|
return ctx.hostCuProxy.request(
|
|
1766
1838
|
toolName,
|
|
@@ -1769,6 +1841,55 @@ export async function surfaceProxyResolver(
|
|
|
1769
1841
|
ctx.hostCuProxy.stepCount,
|
|
1770
1842
|
reasoning,
|
|
1771
1843
|
signal,
|
|
1844
|
+
targetClientId,
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
// Route app-control proxy tools (all app_control_* tool variants)
|
|
1849
|
+
if (toolName.startsWith("app_control_")) {
|
|
1850
|
+
if (!ctx.hostAppControlProxy || !ctx.hostAppControlProxy.isAvailable()) {
|
|
1851
|
+
return {
|
|
1852
|
+
content:
|
|
1853
|
+
"App control is not available — enable the `app-control` feature flag and connect a macOS client.",
|
|
1854
|
+
isError: true,
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
// `app_control_stop` resolves immediately: tear down the proxy without
|
|
1859
|
+
// a client round-trip. Mirrors CU's terminal-tool short-circuit
|
|
1860
|
+
// (`computer_use_done` / `computer_use_respond`). Clear the
|
|
1861
|
+
// conversation's reference (setter disposes the existing proxy) so a
|
|
1862
|
+
// later `app_control_observe`/etc. cleanly fails with "unavailable"
|
|
1863
|
+
// instead of dispatching against a torn-down proxy, and so a sibling
|
|
1864
|
+
// conversation can acquire the released singleton lock without the
|
|
1865
|
+
// disposed proxy still being addressable.
|
|
1866
|
+
if (toolName === "app_control_stop") {
|
|
1867
|
+
if (ctx.setHostAppControlProxy) {
|
|
1868
|
+
ctx.setHostAppControlProxy(undefined);
|
|
1869
|
+
} else {
|
|
1870
|
+
ctx.hostAppControlProxy.dispose();
|
|
1871
|
+
}
|
|
1872
|
+
return { content: "App control stopped.", isError: false };
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
// The TS `HostAppControlInput` (and the Swift mirror) is a discriminated
|
|
1876
|
+
// union on `tool` ("start" | "observe" | "press" | …). The agent's raw
|
|
1877
|
+
// tool input only carries the action-specific payload (app, x/y, text,
|
|
1878
|
+
// …) — the discriminator is implied by `toolName` (`app_control_<tool>`).
|
|
1879
|
+
// Inject it here so the proxy's singleton-lock guard (`input.tool ===
|
|
1880
|
+
// "start"`) and the Swift client's discriminated-union decoder both see
|
|
1881
|
+
// the field they require.
|
|
1882
|
+
const tool = toolName.slice("app_control_".length);
|
|
1883
|
+
const inputWithTool = {
|
|
1884
|
+
...input,
|
|
1885
|
+
tool,
|
|
1886
|
+
} as unknown as HostAppControlInput;
|
|
1887
|
+
|
|
1888
|
+
return ctx.hostAppControlProxy.request(
|
|
1889
|
+
toolName,
|
|
1890
|
+
inputWithTool,
|
|
1891
|
+
ctx.conversationId,
|
|
1892
|
+
signal ?? new AbortController().signal,
|
|
1772
1893
|
);
|
|
1773
1894
|
}
|
|
1774
1895
|
|
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
} from "../channels/types.js";
|
|
14
14
|
import { isHttpAuthDisabled } from "../config/env.js";
|
|
15
15
|
import { getIsPlatform } from "../config/env-registry.js";
|
|
16
|
-
import type { CesClient } from "../credential-execution/client.js";
|
|
17
16
|
import { getBindingByConversation } from "../memory/external-conversation-store.js";
|
|
18
17
|
import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
19
18
|
import type { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
20
19
|
import type { Message, ToolDefinition } from "../providers/types.js";
|
|
21
20
|
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
21
|
+
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
22
22
|
import { coreAppProxyTools } from "../tools/apps/definitions.js";
|
|
23
23
|
import { registerConversationSender } from "../tools/browser/browser-screencast.js";
|
|
24
24
|
import type { ToolExecutor } from "../tools/executor.js";
|
|
@@ -43,7 +43,6 @@ import {
|
|
|
43
43
|
projectSkillTools,
|
|
44
44
|
type SkillProjectionCache,
|
|
45
45
|
} from "./conversation-skill-tools.js";
|
|
46
|
-
import type { SurfaceConversationContext } from "./conversation-surfaces.js";
|
|
47
46
|
import { surfaceProxyResolver } from "./conversation-surfaces.js";
|
|
48
47
|
import {
|
|
49
48
|
isDoordashCommand,
|
|
@@ -72,49 +71,8 @@ export function resolveTrustClass(
|
|
|
72
71
|
return trustContext?.trustClass ?? "unknown";
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Subset of Conversation state that the tool executor callback reads at
|
|
79
|
-
* call time (not construction time). These are captured by the
|
|
80
|
-
* returned closure, so they must be live references.
|
|
81
|
-
*/
|
|
82
|
-
export interface ToolSetupContext extends SurfaceConversationContext {
|
|
83
|
-
readonly conversationId: string;
|
|
84
|
-
assistantId?: string;
|
|
85
|
-
currentRequestId?: string;
|
|
86
|
-
workingDir: string;
|
|
87
|
-
abortController: AbortController | null;
|
|
88
|
-
/** When set, only tools in this set may execute during the current turn. */
|
|
89
|
-
allowedToolNames?: Set<string>;
|
|
90
|
-
/** Conversation memory policy used to propagate scopeId into ToolContext. */
|
|
91
|
-
memoryPolicy: { scopeId: string };
|
|
92
|
-
/** True when the conversation has no connected client (HTTP-only path). */
|
|
93
|
-
hasNoClient?: boolean;
|
|
94
|
-
/** When true, the conversation is executing a task run and must not become interactive. */
|
|
95
|
-
headlessLock?: boolean;
|
|
96
|
-
/** When set, this conversation is executing a task run. Used to retrieve ephemeral permission rules. */
|
|
97
|
-
taskRunId?: string;
|
|
98
|
-
/** Guardian runtime context for the conversation — trustClass is propagated into ToolContext for control-plane policy enforcement. */
|
|
99
|
-
trustContext?: TrustContext;
|
|
100
|
-
/** Voice/call session ID, if the conversation originates from a call. Propagated into ToolContext for scoped grant consumption. */
|
|
101
|
-
callSessionId?: string;
|
|
102
|
-
/** CES RPC client for credential execution operations. Injected when CES tools are enabled and the CES process is available. */
|
|
103
|
-
cesClient?: CesClient;
|
|
104
|
-
/** The interface ID of the connected client driving the current turn (e.g. "macos", "chrome-extension"). Propagated into ToolContext for browser backend selection. */
|
|
105
|
-
readonly transportInterface?: InterfaceId;
|
|
106
|
-
|
|
107
|
-
/** Turn-scoped flag: true when any tool call in the current turn received explicit user approval via interactive prompt. Cleared at turn end. */
|
|
108
|
-
approvedViaPromptThisTurn?: boolean;
|
|
109
|
-
/**
|
|
110
|
-
* Per-turn snapshot of the resolved inference-profile override, set by
|
|
111
|
-
* `runAgentLoopImpl`. Propagated into `ToolContext.overrideProfile` so
|
|
112
|
-
* tools that spawn nested invocations (e.g. `subagent_spawn`) can forward
|
|
113
|
-
* the override without round-tripping through a row read that would
|
|
114
|
-
* return `undefined` for the in-flight (background) subagent.
|
|
115
|
-
*/
|
|
116
|
-
currentTurnOverrideProfile?: string;
|
|
117
|
-
}
|
|
74
|
+
import type { ToolSetupContext } from "./tool-setup-types.js";
|
|
75
|
+
export type { ToolSetupContext } from "./tool-setup-types.js";
|
|
118
76
|
|
|
119
77
|
// ── buildToolDefinitions ─────────────────────────────────────────────
|
|
120
78
|
|
|
@@ -258,16 +216,6 @@ export function createToolExecutor(
|
|
|
258
216
|
// Clone to avoid mutating shared input objects
|
|
259
217
|
const toolInput = { ...rawToolInput };
|
|
260
218
|
|
|
261
|
-
// Propagate outer activity when inner input lacks a valid one
|
|
262
|
-
if (
|
|
263
|
-
typeof input.activity === "string" &&
|
|
264
|
-
input.activity &&
|
|
265
|
-
(typeof toolInput.activity !== "string" ||
|
|
266
|
-
toolInput.activity.length === 0)
|
|
267
|
-
) {
|
|
268
|
-
toolInput.activity = input.activity;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
219
|
if (!toolName) {
|
|
272
220
|
return {
|
|
273
221
|
content:
|
|
@@ -437,6 +385,19 @@ export function isToolActiveForContext(
|
|
|
437
385
|
// Per-capability check is authoritative for structural support: if the
|
|
438
386
|
// transport cannot service this capability, the tool is filtered out.
|
|
439
387
|
if (transport && capability && !supportsHostProxy(transport, capability)) {
|
|
388
|
+
// Cross-client exception: allow host_bash for non-host-proxy interfaces when
|
|
389
|
+
// at least one capable client is connected via the event hub.
|
|
390
|
+
// Only applies to host_bash (not host_file, host_cu, host_browser — Phase 2).
|
|
391
|
+
// Excludes chrome-extension (security boundary: extension only gets host_browser)
|
|
392
|
+
// and hasNoClient turns (no interactive approval UI available).
|
|
393
|
+
if (
|
|
394
|
+
capability === "host_bash" &&
|
|
395
|
+
transport !== "chrome-extension" &&
|
|
396
|
+
!ctx.hasNoClient &&
|
|
397
|
+
assistantEventHub.listClientsByCapability("host_bash").length > 0
|
|
398
|
+
) {
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
440
401
|
return false;
|
|
441
402
|
}
|
|
442
403
|
|
|
@@ -117,6 +117,7 @@ import {
|
|
|
117
117
|
createToolExecutor,
|
|
118
118
|
} from "./conversation-tool-setup.js";
|
|
119
119
|
import { refreshWorkspaceTopLevelContextIfNeeded as refreshWorkspaceImpl } from "./conversation-workspace.js";
|
|
120
|
+
import type { HostAppControlProxy } from "./host-app-control-proxy.js";
|
|
120
121
|
import { HostCuProxy } from "./host-cu-proxy.js";
|
|
121
122
|
import type {
|
|
122
123
|
ServerMessage,
|
|
@@ -204,6 +205,14 @@ export class Conversation {
|
|
|
204
205
|
/** @internal */ taskRunId?: string;
|
|
205
206
|
/** @internal */ callSessionId?: string;
|
|
206
207
|
/** @internal */ hostCuProxy?: HostCuProxy;
|
|
208
|
+
/**
|
|
209
|
+
* Per-conversation host app-control proxy. Set via
|
|
210
|
+
* `setHostAppControlProxy` and disposed in `dispose()`. The
|
|
211
|
+
* `/v1/host-app-control-result` route forwards result payloads to the
|
|
212
|
+
* awaiting promise via this reference.
|
|
213
|
+
* @internal
|
|
214
|
+
*/
|
|
215
|
+
hostAppControlProxy?: HostAppControlProxy;
|
|
207
216
|
/** @internal */ cesClient?: CesClient;
|
|
208
217
|
/** @internal */ readonly queue = new MessageQueue();
|
|
209
218
|
/** @internal */ currentActiveSurfaceId?: string;
|
|
@@ -755,9 +764,12 @@ export class Conversation {
|
|
|
755
764
|
clearTimeout(timer);
|
|
756
765
|
}
|
|
757
766
|
this.recentlyCompletedStandaloneSurfaces.clear();
|
|
758
|
-
// Only dispose the per-conversation CU
|
|
759
|
-
// singletons — their lifecycle is managed by
|
|
767
|
+
// Only dispose the per-conversation CU and app-control proxies.
|
|
768
|
+
// Bash/File/Transfer are singletons — their lifecycle is managed by
|
|
769
|
+
// static disposeInstance().
|
|
760
770
|
this.hostCuProxy?.dispose();
|
|
771
|
+
this.hostAppControlProxy?.dispose();
|
|
772
|
+
this.hostAppControlProxy = undefined;
|
|
761
773
|
// CES client is owned by DaemonServer — just drop the reference.
|
|
762
774
|
// Do NOT close it here; the server manages the CES lifecycle.
|
|
763
775
|
this.cesClient = undefined;
|
|
@@ -936,6 +948,13 @@ export class Conversation {
|
|
|
936
948
|
this.hostCuProxy = proxy;
|
|
937
949
|
}
|
|
938
950
|
|
|
951
|
+
setHostAppControlProxy(proxy: HostAppControlProxy | undefined): void {
|
|
952
|
+
if (this.hostAppControlProxy && this.hostAppControlProxy !== proxy) {
|
|
953
|
+
this.hostAppControlProxy.dispose();
|
|
954
|
+
}
|
|
955
|
+
this.hostAppControlProxy = proxy;
|
|
956
|
+
}
|
|
957
|
+
|
|
939
958
|
// ── Server-authoritative state signals ─────────────────────────────
|
|
940
959
|
|
|
941
960
|
emitConfirmationStateChanged(
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { isPlainObject } from "../util/object.js";
|
|
9
|
-
import type { ToolSetupContext } from "./conversation-tool-setup.js";
|
|
10
9
|
import type { CardSurfaceData } from "./message-protocol.js";
|
|
10
|
+
import type { ToolSetupContext } from "./tool-setup-types.js";
|
|
11
11
|
|
|
12
12
|
interface DoordashStep {
|
|
13
13
|
label: string;
|
|
@@ -52,7 +52,10 @@ export interface HistoryToolCall {
|
|
|
52
52
|
riskReason?: string;
|
|
53
53
|
/** ID of the trust rule that matched this invocation (if any). */
|
|
54
54
|
matchedTrustRuleId?: string;
|
|
55
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* @deprecated Use `approvalMode` and `approvalReason` instead.
|
|
57
|
+
* Kept for backward compatibility during the migration window.
|
|
58
|
+
*/
|
|
56
59
|
autoApproved?: boolean;
|
|
57
60
|
/** How the approval decision was reached: prompted, auto, blocked, or unknown (legacy). */
|
|
58
61
|
approvalMode?: string;
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host app-control proxy.
|
|
3
|
+
*
|
|
4
|
+
* Proxies app-control actions (start, observe, press, combo, type, click,
|
|
5
|
+
* drag, stop) to the desktop client. Targets a specific application by
|
|
6
|
+
* bundle ID or process name — distinct from the system-wide computer-use
|
|
7
|
+
* proxy ({@link HostCuProxy}).
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle (pending map, timeout, abort SSE, dispose, isAvailable) lives
|
|
10
|
+
* in {@link HostProxyBase}; this class layers app-control-specific state
|
|
11
|
+
* (PNG-hash loop guard) and the result-payload → ToolExecutionResult
|
|
12
|
+
* translation on top.
|
|
13
|
+
*
|
|
14
|
+
* **Singleton lock.** Only one conversation may hold an active app-control
|
|
15
|
+
* session at a time. The lock is module-level (`activeAppControlConversationId`)
|
|
16
|
+
* because a session targets the user's actual desktop application, which
|
|
17
|
+
* is a host-wide resource. The lock is acquired on a successful
|
|
18
|
+
* `app_control_start` and released when the owning proxy's `dispose()`
|
|
19
|
+
* fires. A second conversation that calls `start` while the lock is held
|
|
20
|
+
* receives an `isError: true` tool result naming the holding conversation.
|
|
21
|
+
*
|
|
22
|
+
* **No step cap.** Unlike {@link HostCuProxy} which enforces a per-session
|
|
23
|
+
* step ceiling via `loadConfig().maxStepsPerSession`, app-control sessions
|
|
24
|
+
* are not capped. App-control flows are typically narrower (single-app,
|
|
25
|
+
* shorter horizons) and the loop guard plus user oversight are the
|
|
26
|
+
* intended safeguards.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { createHash } from "node:crypto";
|
|
30
|
+
|
|
31
|
+
import type { ContentBlock } from "../providers/types.js";
|
|
32
|
+
import type { ToolExecutionResult } from "../tools/types.js";
|
|
33
|
+
import { getLogger } from "../util/logger.js";
|
|
34
|
+
import { HostProxyBase, HostProxyRequestError } from "./host-proxy-base.js";
|
|
35
|
+
import type {
|
|
36
|
+
HostAppControlInput,
|
|
37
|
+
HostAppControlResultPayload,
|
|
38
|
+
} from "./message-types/host-app-control.js";
|
|
39
|
+
|
|
40
|
+
const log = getLogger("host-app-control-proxy");
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Constants
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
const REQUEST_TIMEOUT_MS = 60 * 1000;
|
|
47
|
+
// Threshold of 4 means the warning fires on the 5th identical observation:
|
|
48
|
+
// the first observation establishes the baseline (count = 0), each
|
|
49
|
+
// subsequent identical observation increments the counter, so count = 4 is
|
|
50
|
+
// reached on the 5th total observation.
|
|
51
|
+
const STUCK_REPEAT_THRESHOLD = 4;
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Tool name constants
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
//
|
|
57
|
+
// Kept here (rather than imported from PR 5's tool registrations) so the
|
|
58
|
+
// proxy is independently testable. PR 5 must use these same string values.
|
|
59
|
+
|
|
60
|
+
const TOOL_START = "app_control_start";
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Module-level singleton lock
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Conversation id that currently owns the active app-control session, or
|
|
68
|
+
* `undefined` if no session is active. Set on a successful
|
|
69
|
+
* `app_control_start`; cleared by the owning proxy's `dispose()`.
|
|
70
|
+
*
|
|
71
|
+
* Exported for test inspection only. Production code paths must not read
|
|
72
|
+
* or mutate this directly — use the proxy methods.
|
|
73
|
+
*/
|
|
74
|
+
let activeAppControlConversationId: string | undefined;
|
|
75
|
+
|
|
76
|
+
/** Test-only helper: read current lock owner. */
|
|
77
|
+
export function _getActiveAppControlConversationId(): string | undefined {
|
|
78
|
+
return activeAppControlConversationId;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Test-only helper: clear lock between test cases. */
|
|
82
|
+
export function _resetActiveAppControlConversationId(): void {
|
|
83
|
+
activeAppControlConversationId = undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// HostAppControlProxy
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
export class HostAppControlProxy extends HostProxyBase<
|
|
91
|
+
HostAppControlInput,
|
|
92
|
+
HostAppControlResultPayload
|
|
93
|
+
> {
|
|
94
|
+
/** Conversation that owns this proxy instance. Used by `dispose()` to release the singleton lock only when this proxy is the holder. */
|
|
95
|
+
private readonly conversationId: string;
|
|
96
|
+
|
|
97
|
+
/** sha256 hex of the most recent observation's `pngBase64`, or undefined. */
|
|
98
|
+
private lastObservationHash?: string;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Number of consecutive observations whose PNG hash matched the previous
|
|
102
|
+
* one. Reset to 0 when a different hash is observed. When this reaches
|
|
103
|
+
* {@link STUCK_REPEAT_THRESHOLD}, results carry a `"stuck"` warning.
|
|
104
|
+
*/
|
|
105
|
+
private observationHashRepeatCount = 0;
|
|
106
|
+
|
|
107
|
+
constructor(conversationId: string) {
|
|
108
|
+
super({
|
|
109
|
+
capabilityName: "host_app_control",
|
|
110
|
+
requestEventName: "host_app_control_request",
|
|
111
|
+
cancelEventName: "host_app_control_cancel",
|
|
112
|
+
resultPendingKind: "host_app_control",
|
|
113
|
+
timeoutMs: REQUEST_TIMEOUT_MS,
|
|
114
|
+
disposedMessage: "Host app-control proxy disposed",
|
|
115
|
+
});
|
|
116
|
+
this.conversationId = conversationId;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// State accessors (testing / external inspection)
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
get observationRepeatCount(): number {
|
|
124
|
+
return this.observationHashRepeatCount;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Public request entry point
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Dispatch an app-control tool call to the desktop client. Catches the
|
|
133
|
+
* base's typed lifecycle errors (timeout/aborted/disposed) and returns
|
|
134
|
+
* a `ToolExecutionResult` instead of letting them bubble.
|
|
135
|
+
*/
|
|
136
|
+
async request(
|
|
137
|
+
toolName: string,
|
|
138
|
+
input: HostAppControlInput,
|
|
139
|
+
conversationId: string,
|
|
140
|
+
signal: AbortSignal,
|
|
141
|
+
): Promise<ToolExecutionResult> {
|
|
142
|
+
if (signal.aborted) {
|
|
143
|
+
return { content: "Aborted", isError: true };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Singleton-lock guard for `start`. Other tools assume a session
|
|
147
|
+
// already exists and are not gated here.
|
|
148
|
+
if (toolName === TOOL_START) {
|
|
149
|
+
if (
|
|
150
|
+
activeAppControlConversationId != null &&
|
|
151
|
+
activeAppControlConversationId !== conversationId
|
|
152
|
+
) {
|
|
153
|
+
return {
|
|
154
|
+
content:
|
|
155
|
+
`Another conversation (${activeAppControlConversationId}) currently holds the ` +
|
|
156
|
+
`app-control session. Wait for it to finish, or call app_control_stop ` +
|
|
157
|
+
`from that conversation first.`,
|
|
158
|
+
isError: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const payload = await this.dispatchRequest(
|
|
165
|
+
toolName,
|
|
166
|
+
input,
|
|
167
|
+
conversationId,
|
|
168
|
+
signal,
|
|
169
|
+
);
|
|
170
|
+
return this.handleSuccess(toolName, payload);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
if (err instanceof HostProxyRequestError) {
|
|
173
|
+
if (err.reason === "timeout") {
|
|
174
|
+
log.warn({ toolName }, "Host app-control proxy request timed out");
|
|
175
|
+
return {
|
|
176
|
+
content:
|
|
177
|
+
"Host app-control proxy timed out waiting for client response",
|
|
178
|
+
isError: true,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (err.reason === "aborted") {
|
|
182
|
+
return { content: "Aborted", isError: true };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// `disposed` and any other unexpected errors propagate.
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// Result handling
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
private handleSuccess(
|
|
195
|
+
toolName: string,
|
|
196
|
+
payload: HostAppControlResultPayload,
|
|
197
|
+
): ToolExecutionResult {
|
|
198
|
+
// Update PNG-hash loop tracking only for the "running" state — other
|
|
199
|
+
// states (missing/minimized) intentionally won't carry a
|
|
200
|
+
// representative window screenshot, so they should not feed the guard.
|
|
201
|
+
let stuck = false;
|
|
202
|
+
if (payload.state === "running" && payload.pngBase64) {
|
|
203
|
+
const hash = createHash("sha256").update(payload.pngBase64).digest("hex");
|
|
204
|
+
if (hash === this.lastObservationHash) {
|
|
205
|
+
this.observationHashRepeatCount++;
|
|
206
|
+
} else {
|
|
207
|
+
this.observationHashRepeatCount = 0;
|
|
208
|
+
}
|
|
209
|
+
this.lastObservationHash = hash;
|
|
210
|
+
if (this.observationHashRepeatCount >= STUCK_REPEAT_THRESHOLD) {
|
|
211
|
+
stuck = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Acquire the singleton lock on a successful `start`.
|
|
216
|
+
if (toolName === TOOL_START && payload.state === "running") {
|
|
217
|
+
activeAppControlConversationId = this.conversationId;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return this.formatResult(payload, stuck);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private formatResult(
|
|
224
|
+
payload: HostAppControlResultPayload,
|
|
225
|
+
stuck: boolean,
|
|
226
|
+
): ToolExecutionResult {
|
|
227
|
+
const parts: string[] = [];
|
|
228
|
+
|
|
229
|
+
if (stuck) {
|
|
230
|
+
parts.push(
|
|
231
|
+
`WARNING: ${this.observationHashRepeatCount} consecutive observations ` +
|
|
232
|
+
`produced an identical screenshot — the app appears stuck. Try a ` +
|
|
233
|
+
`different action or call app_control_stop and restart.`,
|
|
234
|
+
);
|
|
235
|
+
parts.push("");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
parts.push(`State: ${payload.state}`);
|
|
239
|
+
|
|
240
|
+
if (payload.windowBounds) {
|
|
241
|
+
const { x, y, width, height } = payload.windowBounds;
|
|
242
|
+
parts.push(`Window bounds: ${width}x${height} at (${x}, ${y})`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (payload.executionResult) {
|
|
246
|
+
parts.push("");
|
|
247
|
+
parts.push(payload.executionResult);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const isError = payload.executionError != null;
|
|
251
|
+
const errorPrefix = isError
|
|
252
|
+
? `Action failed: ${payload.executionError}`
|
|
253
|
+
: null;
|
|
254
|
+
|
|
255
|
+
const baseContent = parts.join("\n").trim() || `State: ${payload.state}`;
|
|
256
|
+
const content = errorPrefix
|
|
257
|
+
? `${errorPrefix}\n\n${baseContent}`
|
|
258
|
+
: baseContent;
|
|
259
|
+
|
|
260
|
+
const contentBlocks: ContentBlock[] = [];
|
|
261
|
+
if (payload.pngBase64) {
|
|
262
|
+
contentBlocks.push({
|
|
263
|
+
type: "image",
|
|
264
|
+
source: {
|
|
265
|
+
type: "base64",
|
|
266
|
+
media_type: "image/png",
|
|
267
|
+
data: payload.pngBase64,
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
content,
|
|
274
|
+
isError,
|
|
275
|
+
...(contentBlocks.length > 0 ? { contentBlocks } : {}),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ---------------------------------------------------------------------------
|
|
280
|
+
// Lifecycle
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Reject pending requests via the base, then release the singleton lock
|
|
285
|
+
* if this proxy is the holder. Idempotent: safe to call multiple times.
|
|
286
|
+
*/
|
|
287
|
+
override dispose(): void {
|
|
288
|
+
super.dispose();
|
|
289
|
+
if (activeAppControlConversationId === this.conversationId) {
|
|
290
|
+
activeAppControlConversationId = undefined;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|