@vellumai/assistant 0.7.1 → 0.7.3
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 +48 -50
- 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/memory.md +5 -2
- 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 +52 -5
- 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/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- 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 +1020 -40
- 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 +384 -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 +157 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -7
- package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
- package/src/__tests__/background-shell-host-bash.test.ts +14 -15
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- 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-conversation-messages.test.ts +8 -2
- package/src/__tests__/call-domain.test.ts +0 -2
- package/src/__tests__/call-routes-http.test.ts +0 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +62 -2
- package/src/__tests__/checker.test.ts +3 -4
- package/src/__tests__/config-loader-backfill.test.ts +461 -147
- 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-schema.test.ts +1 -0
- package/src/__tests__/config-set-platform-guard.test.ts +48 -4
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +20 -11
- package/src/__tests__/config-watcher.test.ts +142 -71
- package/src/__tests__/context-search-agent-runner.test.ts +61 -3
- package/src/__tests__/context-search-conversations-source.test.ts +0 -24
- package/src/__tests__/context-search-fanout.test.ts +0 -1
- package/src/__tests__/context-search-memory-source.test.ts +3 -7
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop.test.ts +454 -5
- 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-error.test.ts +150 -3
- 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-process-callsite.test.ts +43 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -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-runtime-assembly.test.ts +65 -0
- package/src/__tests__/conversation-slash-commands.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-store.test.ts +0 -18
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +328 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -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 +12 -12
- package/src/__tests__/cu-unified-flow.test.ts +351 -23
- package/src/__tests__/daemon-credential-client.test.ts +101 -19
- package/src/__tests__/date-context.test.ts +164 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
- package/src/__tests__/disk-pressure-guard.test.ts +262 -0
- package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
- package/src/__tests__/disk-pressure-policy.test.ts +241 -0
- package/src/__tests__/disk-pressure-routes.test.ts +379 -0
- package/src/__tests__/disk-pressure-tools.test.ts +277 -0
- package/src/__tests__/disk-usage.test.ts +150 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/events-client-registration.test.ts +52 -0
- package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
- package/src/__tests__/file-write-tool.test.ts +4 -10
- package/src/__tests__/filing-service.test.ts +3 -4
- 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-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +968 -2
- package/src/__tests__/helpers/call-route-handler.ts +7 -1
- package/src/__tests__/host-app-control-proxy.test.ts +772 -0
- package/src/__tests__/host-app-control-routes.test.ts +263 -0
- package/src/__tests__/host-bash-proxy.test.ts +439 -47
- package/src/__tests__/host-bash-routes.test.ts +459 -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 +248 -52
- package/src/__tests__/host-cu-routes-targeted.test.ts +429 -0
- package/src/__tests__/host-file-edit-tool.test.ts +47 -1
- package/src/__tests__/host-file-proxy-targeted.test.ts +378 -0
- package/src/__tests__/host-file-proxy.test.ts +301 -45
- package/src/__tests__/host-file-read-tool.test.ts +17 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +420 -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 +932 -0
- package/src/__tests__/host-transfer-proxy.test.ts +121 -22
- package/src/__tests__/host-transfer-routes-targeted.test.ts +662 -0
- package/src/__tests__/http-user-message-parity.test.ts +108 -1
- 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__/injector-chain.test.ts +18 -6
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- 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-profile-guard.test.ts +18 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/mcp-auth-routes.test.ts +197 -0
- package/src/__tests__/mcp-cli.test.ts +338 -2
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/migration-import-commit-http.test.ts +108 -2
- package/src/__tests__/mock-gateway-ipc.ts +1 -0
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/oauth-cli.test.ts +0 -2
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
- package/src/__tests__/onboarding-persona-write.test.ts +308 -0
- package/src/__tests__/openai-provider.test.ts +45 -8
- package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
- package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
- package/src/__tests__/platform-callback-registration.test.ts +21 -4
- package/src/__tests__/platform.test.ts +2 -1
- package/src/__tests__/playbook-execution.test.ts +0 -43
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +214 -25
- 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__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/public-ingress-urls.test.ts +97 -0
- package/src/__tests__/relay-server.test.ts +15 -4
- 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 +2 -2
- package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
- package/src/__tests__/schedule-retry.test.ts +715 -0
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- 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__/suggestion-routes.test.ts +46 -0
- 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-065-bump-stale-heartbeat-interval.test.ts +122 -0
- package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
- package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
- 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-safe-storage-limits-release.test.ts +90 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
- package/src/approvals/guardian-decision-primitive.ts +13 -0
- package/src/approvals/guardian-request-resolvers.ts +16 -17
- 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/backup/snapshot-lock.ts +2 -27
- package/src/bundler/app-bundler.ts +51 -3
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-conversation-messages.ts +46 -10
- 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 -5
- package/src/cli/commands/backup.ts +6 -331
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/clients.ts +36 -37
- package/src/cli/commands/contacts.ts +137 -25
- package/src/cli/commands/conversations.ts +2 -5
- package/src/cli/commands/credentials.ts +71 -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 +303 -7
- package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
- package/src/cli/commands/oauth/connect.ts +127 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -4
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -3
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -3
- package/src/cli/commands/platform/__tests__/status.test.ts +116 -21
- package/src/cli/commands/platform/disconnect.ts +5 -4
- package/src/cli/commands/platform/index.ts +16 -25
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/lib/daemon-credential-client.ts +110 -28
- package/src/cli/program.ts +6 -2
- package/src/config/assistant-feature-flags.ts +79 -12
- 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/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +25 -4
- package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
- 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 -20
- package/src/config/feature-flag-registry.json +47 -135
- package/src/config/loader.ts +197 -104
- 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 +17 -9
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/calls.ts +0 -9
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +64 -1
- package/src/config/schemas/ingress.ts +10 -6
- package/src/config/schemas/llm.ts +7 -10
- package/src/config/schemas/memory-lifecycle.ts +90 -24
- package/src/config/schemas/memory-v2.ts +121 -13
- package/src/config/schemas/platform.ts +49 -3
- package/src/config/schemas/services.ts +29 -15
- package/src/config/schemas/skills.ts +0 -6
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -55
- 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 +187 -5
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
- package/src/daemon/config-watcher.ts +89 -60
- package/src/daemon/conversation-agent-loop-handlers.ts +27 -3
- package/src/daemon/conversation-agent-loop.ts +202 -61
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +9 -4
- package/src/daemon/conversation-process.ts +24 -11
- package/src/daemon/conversation-runtime-assembly.ts +28 -2
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +305 -4
- package/src/daemon/conversation-tool-setup.ts +66 -62
- package/src/daemon/conversation.ts +38 -24
- package/src/daemon/date-context.ts +71 -22
- package/src/daemon/disk-pressure-background-gate.ts +73 -0
- package/src/daemon/disk-pressure-guard.ts +343 -0
- package/src/daemon/disk-pressure-policy.ts +163 -0
- package/src/daemon/doordash-steps.ts +1 -1
- package/src/daemon/handlers/shared.ts +4 -2
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +389 -0
- package/src/daemon/host-bash-proxy.ts +117 -82
- package/src/daemon/host-browser-proxy.ts +67 -82
- package/src/daemon/host-cu-proxy.ts +127 -86
- package/src/daemon/host-file-proxy.ts +129 -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 +338 -129
- package/src/daemon/lifecycle.ts +194 -145
- package/src/daemon/meet-host-supervisor.ts +4 -4
- package/src/daemon/meet-manifest-loader.ts +0 -1
- package/src/daemon/memory-v2-startup.ts +14 -4
- package/src/daemon/message-protocol.ts +6 -8
- package/src/daemon/message-types/contacts.ts +23 -1
- package/src/daemon/message-types/conversations.ts +15 -8
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- 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/messages.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/profiler-run-store.ts +5 -5
- 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/documents/document-store.ts +85 -0
- package/src/events/tool-audit-listener.ts +2 -1
- package/src/filing/filing-service.ts +30 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +24 -23
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +252 -0
- package/src/heartbeat/heartbeat-run-store.ts +249 -0
- package/src/heartbeat/heartbeat-service.ts +459 -54
- 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/feed-scheduler.ts +18 -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/platform-callback-registration.ts +8 -15
- package/src/inbound/public-ingress-urls.ts +32 -34
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
- package/src/ipc/assistant-server.ts +70 -3
- package/src/ipc/cli-client.ts +32 -1
- package/src/ipc/gateway-client.ts +37 -3
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/live-voice-metrics.ts +10 -10
- package/src/live-voice/protocol.ts +5 -7
- 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/media/image-service.ts +1 -7
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -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/admin.ts +5 -9
- 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 +51 -9
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +22 -9
- package/src/memory/context-search/sources/memory.ts +0 -1
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +5 -13
- package/src/memory/conversation-key-store.ts +2 -15
- package/src/memory/db-init.ts +6 -0
- package/src/memory/embedding-backend.ts +9 -21
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +81 -25
- package/src/memory/graph/conversation-graph-memory.ts +43 -78
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +10 -67
- package/src/memory/graph/graph-search.ts +9 -20
- package/src/memory/graph/retriever.test.ts +6 -0
- package/src/memory/graph/retriever.ts +34 -10
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/indexer.ts +54 -45
- package/src/memory/job-handlers/backfill.ts +2 -11
- package/src/memory/job-handlers/cleanup.ts +43 -0
- package/src/memory/job-handlers/embedding.ts +6 -8
- package/src/memory/job-handlers/summarization.ts +2 -7
- 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 +114 -22
- package/src/memory/jobs-worker.ts +193 -106
- package/src/memory/memory-v2-activation-log-store.ts +33 -15
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- 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/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/pkb/pkb-search.test.ts +6 -0
- package/src/memory/pkb/pkb-search.ts +7 -0
- package/src/memory/qdrant-client.ts +49 -32
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/schema/infrastructure.ts +15 -0
- package/src/memory/search/semantic.ts +13 -67
- package/src/memory/sparse-tokenize.ts +49 -0
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +387 -344
- package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
- package/src/memory/v2/__tests__/injection.test.ts +181 -169
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +154 -188
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
- package/src/memory/v2/__tests__/static-context.test.ts +76 -2
- package/src/memory/v2/activation.ts +213 -239
- package/src/memory/v2/consolidation-job.ts +65 -17
- package/src/memory/v2/constants.ts +7 -0
- package/src/memory/v2/injection.ts +123 -103
- package/src/memory/v2/prompts/consolidation.ts +348 -92
- package/src/memory/v2/qdrant.ts +198 -1
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +113 -77
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +91 -53
- package/src/memory/v2/sparse-bm25.ts +245 -0
- package/src/memory/v2/static-context.ts +28 -5
- package/src/memory/v2/types.ts +10 -10
- 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/copy-composer.ts +13 -0
- package/src/notifications/guardian-question-mode.ts +5 -5
- package/src/notifications/signal.ts +4 -0
- package/src/oauth/AGENTS.md +3 -1
- package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
- package/src/oauth/connect-orchestrator.ts +6 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/credential-token-resolver.ts +1 -3
- package/src/oauth/manual-token-connection.ts +0 -4
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- 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/plugins/defaults/injectors.ts +35 -2
- package/src/plugins/defaults/memory-retrieval.ts +5 -6
- package/src/plugins/types.ts +7 -0
- package/src/proactive-artifact/aux-message-injector.ts +74 -0
- package/src/proactive-artifact/decision.test.ts +226 -0
- package/src/proactive-artifact/decision.ts +165 -0
- package/src/proactive-artifact/index.ts +7 -0
- package/src/proactive-artifact/job.test.ts +867 -0
- package/src/proactive-artifact/job.ts +352 -0
- package/src/proactive-artifact/message-copy.ts +41 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/bootstrap-cleanup.ts +27 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +23 -24
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/providers/__tests__/retry-callsite.test.ts +222 -1
- package/src/providers/model-intents.ts +7 -0
- package/src/providers/openrouter/client.ts +8 -0
- package/src/providers/retry.ts +50 -0
- package/src/providers/speech-to-text/provider-catalog.ts +7 -8
- package/src/providers/types.ts +1 -0
- package/src/runtime/__tests__/agent-wake.test.ts +456 -3
- package/src/runtime/agent-wake.ts +238 -100
- package/src/runtime/assistant-event-hub.ts +151 -99
- package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/middleware.ts +0 -96
- package/src/runtime/auth/route-policy.ts +32 -0
- package/src/runtime/auth/same-actor.ts +216 -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/channel-retry-sweep.ts +65 -1
- package/src/runtime/guardian-reply-router.ts +10 -0
- package/src/runtime/http-server.ts +3 -329
- package/src/runtime/http-types.ts +0 -5
- package/src/runtime/local-actor-identity.ts +52 -11
- 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 +43 -9
- package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -5
- 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 +21 -2
- package/src/runtime/routes/contact-prompt-routes.ts +183 -0
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-query-routes.ts +36 -1
- package/src/runtime/routes/conversation-routes.ts +65 -39
- package/src/runtime/routes/debug-bash-routes.ts +163 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +169 -0
- package/src/runtime/routes/documents-routes.ts +32 -75
- package/src/runtime/routes/errors.ts +19 -4
- package/src/runtime/routes/events-routes.ts +38 -0
- 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 +56 -6
- package/src/runtime/routes/host-browser-routes.ts +108 -13
- package/src/runtime/routes/host-cu-routes.ts +66 -9
- package/src/runtime/routes/host-file-routes.ts +54 -5
- package/src/runtime/routes/host-transfer-routes.ts +122 -19
- 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 +21 -180
- package/src/runtime/routes/inbound-message-handler.ts +78 -21
- 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/guardian-reply-intercept.ts +3 -0
- 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 +14 -0
- package/src/runtime/routes/mcp-auth-routes.ts +132 -0
- package/src/runtime/routes/memory-item-routes.test.ts +41 -15
- package/src/runtime/routes/memory-item-routes.ts +10 -12
- package/src/runtime/routes/memory-v2-routes.ts +474 -1
- package/src/runtime/routes/migration-routes.ts +96 -0
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/routes/schedule-routes.ts +7 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- 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/run-script.ts +37 -5
- 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 +83 -39
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/oauth-callback-registry.ts +8 -0
- package/src/security/secure-keys.ts +55 -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 +10 -16
- 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/subagent/index.ts +1 -7
- package/src/subagent/manager.ts +1 -15
- package/src/tasks/task-runner.ts +0 -1
- package/src/tasks/task-store.ts +0 -3
- 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/background-tool-registry.ts +17 -3
- 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.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +68 -0
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +68 -0
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +78 -3
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +68 -0
- package/src/tools/host-terminal/host-shell.ts +66 -1
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +12 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- 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/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +32 -11
- package/src/tools/types.ts +28 -2
- package/src/tts/provider-catalog.ts +3 -5
- package/src/usage/pricing.ts +1 -1
- package/src/util/disk-usage.ts +138 -0
- package/src/util/platform.ts +21 -11
- package/src/util/process-liveness.ts +26 -0
- package/src/workspace/hatched-date.ts +86 -0
- package/src/workspace/heartbeat-service.ts +19 -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/065-bump-stale-heartbeat-interval.ts +60 -0
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/utils.ts +21 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- 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/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -395
- 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/bash.ts +0 -198
- package/src/signals/mcp-reload.ts +0 -18
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
5
|
+
|
|
6
|
+
import type { ToolContext } from "../types.js";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Singleton mocks — must precede the tool import so bun's module mock applies.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
let mockProxyAvailable = false;
|
|
13
|
+
|
|
14
|
+
mock.module("../../daemon/host-file-proxy.js", () => ({
|
|
15
|
+
HostFileProxy: {
|
|
16
|
+
get instance() {
|
|
17
|
+
return {
|
|
18
|
+
isAvailable: () => mockProxyAvailable,
|
|
19
|
+
request: () => Promise.resolve({ content: "ok", isError: false }),
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
mock.module("../../runtime/assistant-event-hub.js", () => ({
|
|
26
|
+
assistantEventHub: {
|
|
27
|
+
listClientsByCapability: () => [],
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const { hostFileReadTool } = await import("./read.js");
|
|
32
|
+
|
|
33
|
+
const testDirs: string[] = [];
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
mockProxyAvailable = false;
|
|
37
|
+
for (const dir of testDirs.splice(0)) {
|
|
38
|
+
rmSync(dir, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function makeTempDir(): string {
|
|
43
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "host-read-test-")));
|
|
44
|
+
testDirs.push(dir);
|
|
45
|
+
return dir;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function makeContext(
|
|
49
|
+
workingDir: string,
|
|
50
|
+
transportInterface: ToolContext["transportInterface"],
|
|
51
|
+
): ToolContext {
|
|
52
|
+
return {
|
|
53
|
+
workingDir,
|
|
54
|
+
conversationId: "test-conv",
|
|
55
|
+
trustClass: "guardian",
|
|
56
|
+
transportInterface,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe("host_file_read cross-client guards", () => {
|
|
61
|
+
test("returns 'no client' error on web transport when proxy unavailable and no targetClientId", async () => {
|
|
62
|
+
const workingDir = makeTempDir();
|
|
63
|
+
const result = await hostFileReadTool.execute(
|
|
64
|
+
{ path: "/some/host/path.txt" },
|
|
65
|
+
makeContext(workingDir, "web"),
|
|
66
|
+
);
|
|
67
|
+
expect(result.isError).toBe(true);
|
|
68
|
+
expect(result.content).toContain(
|
|
69
|
+
"no client with host_file capability is connected",
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("returns 'specified client disconnected' error when targetClientId set but proxy unavailable on web", async () => {
|
|
74
|
+
const workingDir = makeTempDir();
|
|
75
|
+
const result = await hostFileReadTool.execute(
|
|
76
|
+
{ path: "/some/host/path.txt", target_client_id: "abc-123" },
|
|
77
|
+
makeContext(workingDir, "web"),
|
|
78
|
+
);
|
|
79
|
+
expect(result.isError).toBe(true);
|
|
80
|
+
expect(result.content).toContain(
|
|
81
|
+
'target client "abc-123" is no longer connected',
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("falls through to local fs on macos transport when proxy unavailable and path is non-image", async () => {
|
|
86
|
+
const workingDir = makeTempDir();
|
|
87
|
+
const result = await hostFileReadTool.execute(
|
|
88
|
+
{ path: "/nonexistent/x.txt" },
|
|
89
|
+
makeContext(workingDir, "macos"),
|
|
90
|
+
);
|
|
91
|
+
// Proves the guard did NOT fire on macOS — instead we got the
|
|
92
|
+
// local FileSystemOps NOT_FOUND error.
|
|
93
|
+
expect(result.isError).toBe(true);
|
|
94
|
+
expect(result.content).toContain("File not found");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("does NOT reject on macos transport with a stale target_client_id when proxy unavailable (regression: P2 fix)", async () => {
|
|
98
|
+
const workingDir = makeTempDir();
|
|
99
|
+
const result = await hostFileReadTool.execute(
|
|
100
|
+
{ path: "/nonexistent/x.txt", target_client_id: "stale-mac" },
|
|
101
|
+
makeContext(workingDir, "macos"),
|
|
102
|
+
);
|
|
103
|
+
// The disconnected-target guard is scoped to non-host-proxy transports
|
|
104
|
+
// (!supportsHostProxy). On macos, a stale target_client_id auto-filled
|
|
105
|
+
// from a prior cross-client turn must be silently ignored and the call
|
|
106
|
+
// must fall through to local FileSystemOps (NOT_FOUND for a fake path),
|
|
107
|
+
// NOT reject with "target client ... is no longer connected".
|
|
108
|
+
expect(result.isError).toBe(true);
|
|
109
|
+
expect(result.content).toContain("File not found");
|
|
110
|
+
expect(result.content).not.toContain("is no longer connected");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("rejects when target_client_id is set but transport metadata is missing (legacy/backwards-compat path)", async () => {
|
|
114
|
+
const workingDir = makeTempDir();
|
|
115
|
+
const result = await hostFileReadTool.execute(
|
|
116
|
+
{ path: "/some/host/path.txt", target_client_id: "abc-123" },
|
|
117
|
+
// transportInterface intentionally undefined (legacy callers).
|
|
118
|
+
makeContext(workingDir, undefined),
|
|
119
|
+
);
|
|
120
|
+
// When transport metadata is missing we cannot rule out a non-host-proxy
|
|
121
|
+
// turn, so falling through to local fs would silently target the daemon
|
|
122
|
+
// container. The guard fires for both undefined transport AND
|
|
123
|
+
// non-host-proxy transports — only macos turns skip it.
|
|
124
|
+
expect(result.isError).toBe(true);
|
|
125
|
+
expect(result.content).toContain(
|
|
126
|
+
'target client "abc-123" is no longer connected',
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { extname } from "node:path";
|
|
2
2
|
|
|
3
|
+
import { supportsHostProxy } from "../../channels/types.js";
|
|
3
4
|
import { HostFileProxy } from "../../daemon/host-file-proxy.js";
|
|
4
5
|
import { RiskLevel } from "../../permissions/types.js";
|
|
5
6
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
7
|
+
import { assistantEventHub } from "../../runtime/assistant-event-hub.js";
|
|
6
8
|
import { FileSystemOps } from "../shared/filesystem/file-ops-service.js";
|
|
7
9
|
import {
|
|
8
10
|
IMAGE_EXTENSIONS,
|
|
@@ -37,6 +39,11 @@ class HostFileReadTool implements Tool {
|
|
|
37
39
|
type: "number",
|
|
38
40
|
description: "Maximum number of lines to read",
|
|
39
41
|
},
|
|
42
|
+
target_client_id: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description:
|
|
45
|
+
"ID of the specific client to execute this on. Required when multiple clients support host_file; omit when only one is connected. Obtain IDs from `assistant clients list --capability host_file`.",
|
|
46
|
+
},
|
|
40
47
|
},
|
|
41
48
|
required: ["path"],
|
|
42
49
|
},
|
|
@@ -55,6 +62,64 @@ class HostFileReadTool implements Tool {
|
|
|
55
62
|
};
|
|
56
63
|
}
|
|
57
64
|
|
|
65
|
+
const targetClientId =
|
|
66
|
+
typeof input.target_client_id === "string" &&
|
|
67
|
+
input.target_client_id !== ""
|
|
68
|
+
? input.target_client_id
|
|
69
|
+
: undefined;
|
|
70
|
+
|
|
71
|
+
const transportInterface = context.transportInterface;
|
|
72
|
+
if (
|
|
73
|
+
targetClientId == null &&
|
|
74
|
+
transportInterface != null &&
|
|
75
|
+
!supportsHostProxy(transportInterface) &&
|
|
76
|
+
assistantEventHub.listClientsByCapability("host_file").length > 1
|
|
77
|
+
) {
|
|
78
|
+
return {
|
|
79
|
+
content: `Error: multiple clients support host_file. Specify which client to use with \`target_client_id\`. Run \`assistant clients list --capability host_file\` to see client IDs and labels.`,
|
|
80
|
+
isError: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Guard: non-host-proxy interfaces with no capable clients connected.
|
|
85
|
+
// Without this guard, the request would fall through to local
|
|
86
|
+
// FileSystemOps below and read the daemon container's filesystem
|
|
87
|
+
// instead of the user's host machine.
|
|
88
|
+
if (
|
|
89
|
+
targetClientId == null &&
|
|
90
|
+
transportInterface != null &&
|
|
91
|
+
!supportsHostProxy(transportInterface) &&
|
|
92
|
+
!HostFileProxy.instance.isAvailable()
|
|
93
|
+
) {
|
|
94
|
+
return {
|
|
95
|
+
content:
|
|
96
|
+
"Error: no client with host_file capability is connected. Connect a macOS client to use host_file from a non-desktop interface.",
|
|
97
|
+
isError: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Guard: explicit targetClientId provided but proxy is unavailable.
|
|
102
|
+
// Fires on non-host-proxy transports (web, ios) AND on legacy callers
|
|
103
|
+
// without transport metadata, where falling through to local fs would
|
|
104
|
+
// silently target the daemon container's filesystem instead of the
|
|
105
|
+
// intended host client. Skips only when transport is explicitly
|
|
106
|
+
// host-proxy-capable (macos), where local-fs fallback IS the intended
|
|
107
|
+
// offline behavior — a stale target_client_id auto-filled from a prior
|
|
108
|
+
// cross-client turn is silently ignored on those turns.
|
|
109
|
+
// Note: this scoping deliberately differs from host_bash
|
|
110
|
+
// (host-shell.ts:239-247), which rejects unconditionally for any
|
|
111
|
+
// stale target_client_id regardless of transport.
|
|
112
|
+
if (
|
|
113
|
+
targetClientId != null &&
|
|
114
|
+
!HostFileProxy.instance.isAvailable() &&
|
|
115
|
+
(transportInterface == null || !supportsHostProxy(transportInterface))
|
|
116
|
+
) {
|
|
117
|
+
return {
|
|
118
|
+
content: `Error: target client "${targetClientId}" is no longer connected. The specified client may have disconnected since the tool was called. Run \`assistant clients list --capability host_file\` to see currently connected clients.`,
|
|
119
|
+
isError: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
58
123
|
// Proxy to connected client for execution on the user's machine
|
|
59
124
|
// when a capable client is available (managed/cloud-hosted mode),
|
|
60
125
|
// including image reads that need the host filesystem view.
|
|
@@ -65,9 +130,12 @@ class HostFileReadTool implements Tool {
|
|
|
65
130
|
path: rawPath,
|
|
66
131
|
offset: typeof input.offset === "number" ? input.offset : undefined,
|
|
67
132
|
limit: typeof input.limit === "number" ? input.limit : undefined,
|
|
133
|
+
targetClientId,
|
|
68
134
|
},
|
|
69
135
|
context.conversationId,
|
|
70
136
|
context.signal,
|
|
137
|
+
targetClientId,
|
|
138
|
+
context.sourceActorPrincipalId,
|
|
71
139
|
);
|
|
72
140
|
}
|
|
73
141
|
|
|
@@ -31,6 +31,15 @@ mock.module("../../daemon/host-transfer-proxy.js", () => ({
|
|
|
31
31
|
},
|
|
32
32
|
}));
|
|
33
33
|
|
|
34
|
+
// Mirror read/write/edit test files: stub the event hub so the multi-client
|
|
35
|
+
// guard at line ~100 of transfer.ts is exercised against an isolated stub
|
|
36
|
+
// rather than the live process-wide singleton.
|
|
37
|
+
mock.module("../../runtime/assistant-event-hub.js", () => ({
|
|
38
|
+
assistantEventHub: {
|
|
39
|
+
listClientsByCapability: () => [],
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
|
|
34
43
|
const { hostFileTransferTool } = await import("./transfer.js");
|
|
35
44
|
|
|
36
45
|
const testDirs: string[] = [];
|
|
@@ -50,8 +59,16 @@ function makeTempDir(): string {
|
|
|
50
59
|
return dir;
|
|
51
60
|
}
|
|
52
61
|
|
|
53
|
-
function makeContext(
|
|
54
|
-
|
|
62
|
+
function makeContext(
|
|
63
|
+
workingDir: string,
|
|
64
|
+
transportInterface?: ToolContext["transportInterface"],
|
|
65
|
+
): ToolContext {
|
|
66
|
+
return {
|
|
67
|
+
workingDir,
|
|
68
|
+
conversationId: "test-conv",
|
|
69
|
+
trustClass: "guardian",
|
|
70
|
+
transportInterface,
|
|
71
|
+
};
|
|
55
72
|
}
|
|
56
73
|
|
|
57
74
|
// ---------------------------------------------------------------------------
|
|
@@ -269,3 +286,111 @@ describe("host_file_transfer managed mode", () => {
|
|
|
269
286
|
expect(toSandboxCalls.length).toBe(0);
|
|
270
287
|
});
|
|
271
288
|
});
|
|
289
|
+
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
// Cross-client guard tests
|
|
292
|
+
// ---------------------------------------------------------------------------
|
|
293
|
+
|
|
294
|
+
describe("host_file_transfer cross-client guards", () => {
|
|
295
|
+
test("returns 'no client' error on web transport when proxy unavailable and no targetClientId", async () => {
|
|
296
|
+
// mockProxyAvailable defaults to false.
|
|
297
|
+
const workingDir = makeTempDir();
|
|
298
|
+
const srcDir = makeTempDir();
|
|
299
|
+
const srcFile = join(srcDir, "source.txt");
|
|
300
|
+
writeFileSync(srcFile, "content");
|
|
301
|
+
|
|
302
|
+
const result = await hostFileTransferTool.execute(
|
|
303
|
+
{
|
|
304
|
+
source_path: srcFile,
|
|
305
|
+
dest_path: "out.txt",
|
|
306
|
+
direction: "to_sandbox",
|
|
307
|
+
},
|
|
308
|
+
makeContext(workingDir, "web"),
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
expect(result.isError).toBe(true);
|
|
312
|
+
expect(result.content).toContain(
|
|
313
|
+
"no client with host_file capability is connected",
|
|
314
|
+
);
|
|
315
|
+
expect(toSandboxCalls.length).toBe(0);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("returns 'specified client disconnected' error when targetClientId set but proxy unavailable on web", async () => {
|
|
319
|
+
const workingDir = makeTempDir();
|
|
320
|
+
const srcDir = makeTempDir();
|
|
321
|
+
const srcFile = join(srcDir, "source.txt");
|
|
322
|
+
writeFileSync(srcFile, "content");
|
|
323
|
+
|
|
324
|
+
const result = await hostFileTransferTool.execute(
|
|
325
|
+
{
|
|
326
|
+
source_path: srcFile,
|
|
327
|
+
dest_path: "out.txt",
|
|
328
|
+
direction: "to_sandbox",
|
|
329
|
+
target_client_id: "abc-123",
|
|
330
|
+
},
|
|
331
|
+
makeContext(workingDir, "web"),
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
expect(result.isError).toBe(true);
|
|
335
|
+
expect(result.content).toContain(
|
|
336
|
+
'target client "abc-123" is no longer connected',
|
|
337
|
+
);
|
|
338
|
+
expect(toSandboxCalls.length).toBe(0);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("rejects when target_client_id is set but transport metadata is missing (legacy/backwards-compat path)", async () => {
|
|
342
|
+
const workingDir = makeTempDir();
|
|
343
|
+
const srcDir = makeTempDir();
|
|
344
|
+
const srcFile = join(srcDir, "source.txt");
|
|
345
|
+
writeFileSync(srcFile, "content");
|
|
346
|
+
const destFile = join(workingDir, "should-not-exist.txt");
|
|
347
|
+
|
|
348
|
+
const result = await hostFileTransferTool.execute(
|
|
349
|
+
{
|
|
350
|
+
source_path: srcFile,
|
|
351
|
+
dest_path: destFile,
|
|
352
|
+
direction: "to_sandbox",
|
|
353
|
+
target_client_id: "abc-123",
|
|
354
|
+
},
|
|
355
|
+
// transportInterface intentionally omitted (legacy callers).
|
|
356
|
+
makeContext(workingDir),
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// Without transport metadata, falling through to executeLocal would
|
|
360
|
+
// silently target the daemon container. The guard fires for undefined
|
|
361
|
+
// transport AND non-host-proxy transports — only macos turns skip it.
|
|
362
|
+
expect(result.isError).toBe(true);
|
|
363
|
+
expect(result.content).toContain(
|
|
364
|
+
'target client "abc-123" is no longer connected',
|
|
365
|
+
);
|
|
366
|
+
expect(existsSync(destFile)).toBe(false);
|
|
367
|
+
expect(toSandboxCalls.length).toBe(0);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("does NOT reject on macos transport with a stale target_client_id when proxy unavailable (regression: Devin-flagged scope drift fix)", async () => {
|
|
371
|
+
const workingDir = makeTempDir();
|
|
372
|
+
const srcDir = makeTempDir();
|
|
373
|
+
const srcFile = join(srcDir, "source.txt");
|
|
374
|
+
writeFileSync(srcFile, "content");
|
|
375
|
+
const destFile = join(workingDir, "stale-target.txt");
|
|
376
|
+
|
|
377
|
+
const result = await hostFileTransferTool.execute(
|
|
378
|
+
{
|
|
379
|
+
source_path: srcFile,
|
|
380
|
+
dest_path: destFile,
|
|
381
|
+
direction: "to_sandbox",
|
|
382
|
+
target_client_id: "stale-mac",
|
|
383
|
+
},
|
|
384
|
+
makeContext(workingDir, "macos"),
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
// The disconnected-target guard is scoped to non-host-proxy transports
|
|
388
|
+
// (!supportsHostProxy). On macos, a stale target_client_id auto-filled
|
|
389
|
+
// from a prior cross-client turn must be silently ignored and the local
|
|
390
|
+
// copy must succeed, NOT reject with "target client ... is no longer
|
|
391
|
+
// connected" or the older "target_client_id was specified but no host
|
|
392
|
+
// client is available" message.
|
|
393
|
+
expect(result.isError).toBe(false);
|
|
394
|
+
expect(existsSync(destFile)).toBe(true);
|
|
395
|
+
});
|
|
396
|
+
});
|
|
@@ -2,16 +2,18 @@ import { constants } from "node:fs";
|
|
|
2
2
|
import { copyFile, lstat, mkdir, realpath } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute } from "node:path";
|
|
4
4
|
|
|
5
|
+
import { supportsHostProxy } from "../../channels/types.js";
|
|
5
6
|
import { HostTransferProxy } from "../../daemon/host-transfer-proxy.js";
|
|
6
7
|
import { RiskLevel } from "../../permissions/types.js";
|
|
7
8
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
9
|
+
import { assistantEventHub } from "../../runtime/assistant-event-hub.js";
|
|
8
10
|
import { sandboxPolicy } from "../shared/filesystem/path-policy.js";
|
|
9
11
|
import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
10
12
|
|
|
11
13
|
class HostFileTransferTool implements Tool {
|
|
12
14
|
name = "host_file_transfer";
|
|
13
15
|
description =
|
|
14
|
-
"Copy a file between the assistant's workspace and the user's host machine. Set direction to 'to_host' to send a workspace file to the host, or 'to_sandbox' to pull a host file into the workspace.";
|
|
16
|
+
"Copy a file between the assistant's workspace and the user's host machine. Set direction to 'to_host' to send a workspace file to the host, or 'to_sandbox' to pull a host file into the workspace. When multiple clients support host_file, specify which one to use with target_client_id.";
|
|
15
17
|
category = "host-filesystem";
|
|
16
18
|
defaultRiskLevel = RiskLevel.Medium;
|
|
17
19
|
|
|
@@ -48,6 +50,11 @@ class HostFileTransferTool implements Tool {
|
|
|
48
50
|
description:
|
|
49
51
|
"Brief description of why the file is being transferred (for audit logging)",
|
|
50
52
|
},
|
|
53
|
+
target_client_id: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description:
|
|
56
|
+
"ID of the specific client to transfer files to/from. Required when multiple clients support host_file; omit when only one is connected. Obtain IDs from `assistant clients list --capability host_file`.",
|
|
57
|
+
},
|
|
51
58
|
},
|
|
52
59
|
required: ["source_path", "dest_path", "direction"],
|
|
53
60
|
},
|
|
@@ -85,6 +92,65 @@ class HostFileTransferTool implements Tool {
|
|
|
85
92
|
|
|
86
93
|
const overwrite = input.overwrite === true;
|
|
87
94
|
|
|
95
|
+
const targetClientId =
|
|
96
|
+
typeof input.target_client_id === "string" &&
|
|
97
|
+
input.target_client_id !== ""
|
|
98
|
+
? input.target_client_id
|
|
99
|
+
: undefined;
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
targetClientId == null &&
|
|
103
|
+
context.transportInterface != null &&
|
|
104
|
+
!supportsHostProxy(context.transportInterface) &&
|
|
105
|
+
assistantEventHub.listClientsByCapability("host_file").length > 1
|
|
106
|
+
) {
|
|
107
|
+
return {
|
|
108
|
+
content: `Error: multiple clients support host_file. Specify which client to use with \`target_client_id\`. Run \`assistant clients list --capability host_file\` to see client IDs and labels.`,
|
|
109
|
+
isError: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Guard: non-host-proxy interfaces with no capable clients connected.
|
|
114
|
+
// Without this guard, a web/ios turn whose host_file client has
|
|
115
|
+
// disconnected since projection would fall through to executeLocal
|
|
116
|
+
// below and act on the daemon container's filesystem instead of
|
|
117
|
+
// the user's host machine.
|
|
118
|
+
if (
|
|
119
|
+
targetClientId == null &&
|
|
120
|
+
context.transportInterface != null &&
|
|
121
|
+
!supportsHostProxy(context.transportInterface) &&
|
|
122
|
+
!HostTransferProxy.instance.isAvailable()
|
|
123
|
+
) {
|
|
124
|
+
return {
|
|
125
|
+
content:
|
|
126
|
+
"Error: no client with host_file capability is connected. Connect a macOS client to use host_file from a non-desktop interface.",
|
|
127
|
+
isError: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Guard: explicit targetClientId provided but proxy is unavailable.
|
|
132
|
+
// Fires on non-host-proxy transports (web, ios) AND on legacy callers
|
|
133
|
+
// without transport metadata, where falling through to executeLocal
|
|
134
|
+
// would silently target the daemon container's filesystem instead of
|
|
135
|
+
// the intended host client. Skips only when transport is explicitly
|
|
136
|
+
// host-proxy-capable (macos), where local-fs fallback IS the intended
|
|
137
|
+
// offline behavior — a stale target_client_id auto-filled from a prior
|
|
138
|
+
// cross-client turn is silently ignored on those turns.
|
|
139
|
+
// Note: this scoping deliberately differs from host_bash
|
|
140
|
+
// (host-shell.ts:239-247), which rejects unconditionally for any
|
|
141
|
+
// stale target_client_id regardless of transport.
|
|
142
|
+
if (
|
|
143
|
+
targetClientId != null &&
|
|
144
|
+
!HostTransferProxy.instance.isAvailable() &&
|
|
145
|
+
(context.transportInterface == null ||
|
|
146
|
+
!supportsHostProxy(context.transportInterface))
|
|
147
|
+
) {
|
|
148
|
+
return {
|
|
149
|
+
content: `Error: target client "${targetClientId}" is no longer connected. The specified client may have disconnected since the tool was called. Run \`assistant clients list --capability host_file\` to see currently connected clients.`,
|
|
150
|
+
isError: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
88
154
|
// Validate that host-side paths are absolute.
|
|
89
155
|
if (direction === "to_host" && !isAbsolute(destPath)) {
|
|
90
156
|
return {
|
|
@@ -115,7 +181,9 @@ class HostFileTransferTool implements Tool {
|
|
|
115
181
|
|
|
116
182
|
let resolvedDestPath = destPath;
|
|
117
183
|
if (direction === "to_sandbox") {
|
|
118
|
-
const pathCheck = sandboxPolicy(destPath, context.workingDir, {
|
|
184
|
+
const pathCheck = sandboxPolicy(destPath, context.workingDir, {
|
|
185
|
+
mustExist: false,
|
|
186
|
+
});
|
|
119
187
|
if (!pathCheck.ok) {
|
|
120
188
|
return {
|
|
121
189
|
content: `Invalid destination path: ${pathCheck.error}`,
|
|
@@ -134,8 +202,10 @@ class HostFileTransferTool implements Tool {
|
|
|
134
202
|
destPath,
|
|
135
203
|
overwrite,
|
|
136
204
|
conversationId: context.conversationId,
|
|
205
|
+
targetClientId,
|
|
137
206
|
},
|
|
138
207
|
context.signal,
|
|
208
|
+
context.sourceActorPrincipalId,
|
|
139
209
|
);
|
|
140
210
|
}
|
|
141
211
|
return HostTransferProxy.instance.requestToSandbox(
|
|
@@ -144,12 +214,17 @@ class HostFileTransferTool implements Tool {
|
|
|
144
214
|
destPath: resolvedDestPath,
|
|
145
215
|
overwrite,
|
|
146
216
|
conversationId: context.conversationId,
|
|
217
|
+
targetClientId,
|
|
147
218
|
},
|
|
148
219
|
context.signal,
|
|
220
|
+
context.sourceActorPrincipalId,
|
|
149
221
|
);
|
|
150
222
|
}
|
|
151
223
|
|
|
152
|
-
// Local mode: direct filesystem copy.
|
|
224
|
+
// Local mode: direct filesystem copy. The non-host-proxy + stale
|
|
225
|
+
// target_client_id case is caught by the scoped guard at the top of
|
|
226
|
+
// execute(); on macos a stale target_client_id is silently ignored
|
|
227
|
+
// here, matching the read/write/edit pattern.
|
|
153
228
|
return this.executeLocal(resolvedSourcePath, resolvedDestPath, overwrite);
|
|
154
229
|
}
|
|
155
230
|
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { existsSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
5
|
+
|
|
6
|
+
import type { ToolContext } from "../types.js";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Singleton mocks — must precede the tool import so bun's module mock applies.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
let mockProxyAvailable = false;
|
|
13
|
+
|
|
14
|
+
mock.module("../../daemon/host-file-proxy.js", () => ({
|
|
15
|
+
HostFileProxy: {
|
|
16
|
+
get instance() {
|
|
17
|
+
return {
|
|
18
|
+
isAvailable: () => mockProxyAvailable,
|
|
19
|
+
request: () => Promise.resolve({ content: "ok", isError: false }),
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
mock.module("../../runtime/assistant-event-hub.js", () => ({
|
|
26
|
+
assistantEventHub: {
|
|
27
|
+
listClientsByCapability: () => [],
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const { hostFileWriteTool } = await import("./write.js");
|
|
32
|
+
|
|
33
|
+
const testDirs: string[] = [];
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
mockProxyAvailable = false;
|
|
37
|
+
for (const dir of testDirs.splice(0)) {
|
|
38
|
+
rmSync(dir, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function makeTempDir(): string {
|
|
43
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "host-write-test-")));
|
|
44
|
+
testDirs.push(dir);
|
|
45
|
+
return dir;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function makeContext(
|
|
49
|
+
workingDir: string,
|
|
50
|
+
transportInterface: ToolContext["transportInterface"],
|
|
51
|
+
): ToolContext {
|
|
52
|
+
return {
|
|
53
|
+
workingDir,
|
|
54
|
+
conversationId: "test-conv",
|
|
55
|
+
trustClass: "guardian",
|
|
56
|
+
transportInterface,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe("host_file_write cross-client guards", () => {
|
|
61
|
+
test("returns 'no client' error on web transport when proxy unavailable and no targetClientId", async () => {
|
|
62
|
+
const workingDir = makeTempDir();
|
|
63
|
+
const result = await hostFileWriteTool.execute(
|
|
64
|
+
{ path: "/some/host/path.txt", content: "hello" },
|
|
65
|
+
makeContext(workingDir, "web"),
|
|
66
|
+
);
|
|
67
|
+
expect(result.isError).toBe(true);
|
|
68
|
+
expect(result.content).toContain(
|
|
69
|
+
"no client with host_file capability is connected",
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("returns 'specified client disconnected' error when targetClientId set but proxy unavailable on web", async () => {
|
|
74
|
+
const workingDir = makeTempDir();
|
|
75
|
+
const result = await hostFileWriteTool.execute(
|
|
76
|
+
{
|
|
77
|
+
path: "/some/host/path.txt",
|
|
78
|
+
content: "hello",
|
|
79
|
+
target_client_id: "abc-123",
|
|
80
|
+
},
|
|
81
|
+
makeContext(workingDir, "web"),
|
|
82
|
+
);
|
|
83
|
+
expect(result.isError).toBe(true);
|
|
84
|
+
expect(result.content).toContain(
|
|
85
|
+
'target client "abc-123" is no longer connected',
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("falls through to local fs on macos transport when proxy unavailable", async () => {
|
|
90
|
+
const workingDir = makeTempDir();
|
|
91
|
+
const destFile = join(workingDir, "out.txt");
|
|
92
|
+
const result = await hostFileWriteTool.execute(
|
|
93
|
+
{ path: destFile, content: "hello" },
|
|
94
|
+
makeContext(workingDir, "macos"),
|
|
95
|
+
);
|
|
96
|
+
// Proves the guard did NOT fire on macOS — local write succeeded.
|
|
97
|
+
expect(result.isError).toBe(false);
|
|
98
|
+
expect(existsSync(destFile)).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("does NOT reject on macos transport with a stale target_client_id when proxy unavailable (regression: P2 fix)", async () => {
|
|
102
|
+
const workingDir = makeTempDir();
|
|
103
|
+
const destFile = join(workingDir, "stale-target.txt");
|
|
104
|
+
const result = await hostFileWriteTool.execute(
|
|
105
|
+
{ path: destFile, content: "hello", target_client_id: "stale-mac" },
|
|
106
|
+
makeContext(workingDir, "macos"),
|
|
107
|
+
);
|
|
108
|
+
// The disconnected-target guard is scoped to non-host-proxy transports
|
|
109
|
+
// (!supportsHostProxy). On macos, a stale target_client_id auto-filled
|
|
110
|
+
// from a prior cross-client turn must be silently ignored and the local
|
|
111
|
+
// write must succeed, NOT reject with "target client ... is no longer
|
|
112
|
+
// connected".
|
|
113
|
+
expect(result.isError).toBe(false);
|
|
114
|
+
expect(existsSync(destFile)).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("rejects when target_client_id is set but transport metadata is missing (legacy/backwards-compat path)", async () => {
|
|
118
|
+
const workingDir = makeTempDir();
|
|
119
|
+
const destFile = join(workingDir, "should-not-exist.txt");
|
|
120
|
+
const result = await hostFileWriteTool.execute(
|
|
121
|
+
{ path: destFile, content: "hello", target_client_id: "abc-123" },
|
|
122
|
+
// transportInterface intentionally undefined (legacy callers).
|
|
123
|
+
makeContext(workingDir, undefined),
|
|
124
|
+
);
|
|
125
|
+
// Without transport metadata, falling through to local fs would
|
|
126
|
+
// silently target the daemon container. The guard fires for undefined
|
|
127
|
+
// transport AND non-host-proxy transports — only macos turns skip it.
|
|
128
|
+
expect(result.isError).toBe(true);
|
|
129
|
+
expect(result.content).toContain(
|
|
130
|
+
'target client "abc-123" is no longer connected',
|
|
131
|
+
);
|
|
132
|
+
expect(existsSync(destFile)).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
});
|