@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
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared invariants and predicate functions consumed by both the buffer-
|
|
3
|
+
* based `commitImport` and the streaming `streamCommitImport`. These
|
|
4
|
+
* decisions must stay in lockstep across both importers — moving them
|
|
5
|
+
* here removes the parallel-implementation skew risk that would otherwise
|
|
6
|
+
* grow as either importer evolves.
|
|
7
|
+
*
|
|
8
|
+
* Pure: no `node:fs`, no I/O, no async. Functions over strings + manifest
|
|
9
|
+
* data shapes only.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const LEGACY_USER_MD_ARCHIVE_PATH = "prompts/USER.md";
|
|
13
|
+
|
|
14
|
+
export const CONFIG_ARCHIVE_PATHS: ReadonlySet<string> = new Set([
|
|
15
|
+
"workspace/config.json",
|
|
16
|
+
"config/settings.json",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
export const CREDENTIAL_METADATA_ARCHIVE_PATH =
|
|
20
|
+
"workspace/data/credentials/metadata.json";
|
|
21
|
+
|
|
22
|
+
export const WORKSPACE_PRESERVE_PATHS: readonly string[] = [
|
|
23
|
+
"embedding-models",
|
|
24
|
+
"deprecated",
|
|
25
|
+
"data/db",
|
|
26
|
+
"data/qdrant",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export function isWorkspaceNamespacedArchivePath(archivePath: string): boolean {
|
|
30
|
+
return archivePath.startsWith("workspace/");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isLegacyPersonaArchivePath(archivePath: string): boolean {
|
|
34
|
+
return archivePath === LEGACY_USER_MD_ARCHIVE_PATH;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isConfigArchivePath(archivePath: string): boolean {
|
|
38
|
+
return CONFIG_ARCHIVE_PATHS.has(archivePath);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isCredentialMetadataArchivePath(archivePath: string): boolean {
|
|
42
|
+
return archivePath === CREDENTIAL_METADATA_ARCHIVE_PATH;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Partition `WORKSPACE_PRESERVE_PATHS` into the two skip sets the buffer
|
|
47
|
+
* importer's selective-clear loop consumes:
|
|
48
|
+
*
|
|
49
|
+
* - `topLevelSkipDirs`: single-segment preserve-paths (e.g. "embedding-models").
|
|
50
|
+
* - `dataSubdirSkipDirs`: second segment of `data/<x>` preserve-paths
|
|
51
|
+
* (e.g. "db" for "data/db").
|
|
52
|
+
*
|
|
53
|
+
* Stays in sync with WORKSPACE_PRESERVE_PATHS automatically — adding a
|
|
54
|
+
* new entry of either shape doesn't require touching the buffer importer.
|
|
55
|
+
* Multi-segment paths outside the `data/` subtree are intentionally
|
|
56
|
+
* unsupported here; the buffer importer's walk doesn't recurse into
|
|
57
|
+
* arbitrary subdirs. If a future preserve-path needs deeper coverage,
|
|
58
|
+
* widen this helper and the buffer importer's walk together.
|
|
59
|
+
*/
|
|
60
|
+
export function partitionWorkspacePreserveSkipDirs(): {
|
|
61
|
+
topLevelSkipDirs: ReadonlySet<string>;
|
|
62
|
+
dataSubdirSkipDirs: ReadonlySet<string>;
|
|
63
|
+
} {
|
|
64
|
+
const topLevelSkipDirs = new Set<string>();
|
|
65
|
+
const dataSubdirSkipDirs = new Set<string>();
|
|
66
|
+
for (const rel of WORKSPACE_PRESERVE_PATHS) {
|
|
67
|
+
const parts = rel.split("/");
|
|
68
|
+
if (parts.length === 1) {
|
|
69
|
+
topLevelSkipDirs.add(parts[0]!);
|
|
70
|
+
} else if (parts.length === 2 && parts[0] === "data") {
|
|
71
|
+
dataSubdirSkipDirs.add(parts[1]!);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { topLevelSkipDirs, dataSubdirSkipDirs };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const LEGACY_RUNTIME_VERSION_SENTINEL = "0.0.0-legacy";
|
|
78
|
+
|
|
79
|
+
type SemverTriple = readonly [number, number, number];
|
|
80
|
+
|
|
81
|
+
function parseSemverTriple(version: string): SemverTriple | null {
|
|
82
|
+
// Strip optional prerelease/build suffix ("-foo", "+sha"). We treat
|
|
83
|
+
// "0.7.1-staging.1" as equal-to-release "0.7.1" for gating purposes.
|
|
84
|
+
// The platform-side check uses packaging.version.Version, which sorts
|
|
85
|
+
// prereleases BEFORE the corresponding release; matching that exactly
|
|
86
|
+
// would require a fuller parser — for runtime-side defense-in-depth
|
|
87
|
+
// this conservative read is sufficient (matches release of the same
|
|
88
|
+
// base triple).
|
|
89
|
+
const base = version.split(/[-+]/)[0] ?? version;
|
|
90
|
+
const parts = base.split(".");
|
|
91
|
+
if (parts.length !== 3) return null;
|
|
92
|
+
// Reject components with anything other than ASCII digits to avoid
|
|
93
|
+
// Number.parseInt's lenient prefix-parse — e.g. "0.8.0foo" would
|
|
94
|
+
// otherwise coerce to [0, 8, 0] and silently pass the gate, defeating
|
|
95
|
+
// the parse-failure-fail-open contract in evaluateRuntimeCompatibility.
|
|
96
|
+
// Leading zeros (e.g. "01.02.03") are intentionally accepted since
|
|
97
|
+
// they round-trip to the same numeric triple as the un-padded form.
|
|
98
|
+
if (!parts.every((p) => /^\d+$/.test(p))) return null;
|
|
99
|
+
const [maj, min, pat] = parts.map((p) => Number(p));
|
|
100
|
+
if (![maj, min, pat].every((n) => Number.isFinite(n) && n >= 0)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return [maj!, min!, pat!] as const;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// -1 if a < b, 0 if equal, +1 if a > b. Returns null on parse failure.
|
|
107
|
+
export function compareSemver(a: string, b: string): -1 | 0 | 1 | null {
|
|
108
|
+
const ta = parseSemverTriple(a);
|
|
109
|
+
const tb = parseSemverTriple(b);
|
|
110
|
+
if (!ta || !tb) return null;
|
|
111
|
+
for (let i = 0; i < 3; i++) {
|
|
112
|
+
if (ta[i]! < tb[i]!) return -1;
|
|
113
|
+
if (ta[i]! > tb[i]!) return +1;
|
|
114
|
+
}
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface RuntimeCompatibility {
|
|
119
|
+
min_runtime_version: string;
|
|
120
|
+
max_runtime_version: string | null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type RuntimeCompatibilityResult =
|
|
124
|
+
| { ok: true }
|
|
125
|
+
| {
|
|
126
|
+
ok: false;
|
|
127
|
+
reason: "version_incompatible";
|
|
128
|
+
bundle_compat: RuntimeCompatibility;
|
|
129
|
+
runtime_version: string;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export function formatRuntimeCompatibilityMessage(
|
|
133
|
+
compat: RuntimeCompatibility,
|
|
134
|
+
runtimeVersion: string,
|
|
135
|
+
): string {
|
|
136
|
+
const range = compat.max_runtime_version
|
|
137
|
+
? `${compat.min_runtime_version}–${compat.max_runtime_version}`
|
|
138
|
+
: `${compat.min_runtime_version}+`;
|
|
139
|
+
return `Cannot import: bundle requires runtime ${range}, but this runtime is ${runtimeVersion}. Update your runtime before importing.`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function evaluateRuntimeCompatibility(
|
|
143
|
+
compat: RuntimeCompatibility,
|
|
144
|
+
runtimeVersion: string,
|
|
145
|
+
): RuntimeCompatibilityResult {
|
|
146
|
+
if (compat.min_runtime_version === LEGACY_RUNTIME_VERSION_SENTINEL) {
|
|
147
|
+
return { ok: true };
|
|
148
|
+
}
|
|
149
|
+
const minCmp = compareSemver(runtimeVersion, compat.min_runtime_version);
|
|
150
|
+
if (minCmp === null) return { ok: true };
|
|
151
|
+
if (minCmp < 0) {
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
reason: "version_incompatible",
|
|
155
|
+
bundle_compat: compat,
|
|
156
|
+
runtime_version: runtimeVersion,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (compat.max_runtime_version !== null) {
|
|
160
|
+
const maxCmp = compareSemver(runtimeVersion, compat.max_runtime_version);
|
|
161
|
+
if (maxCmp === null) return { ok: true };
|
|
162
|
+
if (maxCmp > 0) {
|
|
163
|
+
return {
|
|
164
|
+
ok: false,
|
|
165
|
+
reason: "version_incompatible",
|
|
166
|
+
bundle_compat: compat,
|
|
167
|
+
runtime_version: runtimeVersion,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return { ok: true };
|
|
172
|
+
}
|
|
@@ -16,72 +16,30 @@ import { createHash } from "node:crypto";
|
|
|
16
16
|
import {
|
|
17
17
|
copyFileSync,
|
|
18
18
|
existsSync,
|
|
19
|
+
lstatSync,
|
|
19
20
|
mkdirSync,
|
|
20
21
|
readdirSync,
|
|
21
22
|
readFileSync,
|
|
23
|
+
readlinkSync,
|
|
22
24
|
rmSync,
|
|
25
|
+
symlinkSync,
|
|
23
26
|
writeFileSync,
|
|
24
27
|
} from "node:fs";
|
|
25
|
-
import { dirname, join } from "node:path";
|
|
28
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
26
29
|
|
|
27
30
|
import { sanitizeConfigForTransfer } from "../../config/sanitize-for-transfer.js";
|
|
28
31
|
import { isGuardianPersonaCustomized } from "../../prompts/persona-resolver.js";
|
|
29
32
|
import { getLogger } from "../../util/logger.js";
|
|
33
|
+
import { APP_VERSION } from "../../version.js";
|
|
30
34
|
import type { PathResolver } from "./vbundle-import-analyzer.js";
|
|
35
|
+
import type { RuntimeCompatibility } from "./vbundle-import-policy.js";
|
|
36
|
+
import * as policy from "./vbundle-import-policy.js";
|
|
31
37
|
import { mergeMetadataPreservingVellum } from "./vbundle-metadata-merge.js";
|
|
32
38
|
import type { ManifestType, VBundleTarEntry } from "./vbundle-validator.js";
|
|
33
39
|
import { validateVBundle } from "./vbundle-validator.js";
|
|
34
40
|
|
|
35
41
|
const log = getLogger("vbundle-importer");
|
|
36
42
|
|
|
37
|
-
/** Archive path for the legacy guardian user persona file. */
|
|
38
|
-
export const LEGACY_USER_MD_ARCHIVE_PATH = "prompts/USER.md";
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Archive paths recognized as JSON config files that must be run through
|
|
42
|
-
* `sanitizeConfigForTransfer` before writing to disk. Exported so the
|
|
43
|
-
* streaming importer can apply the same defense-in-depth treatment.
|
|
44
|
-
*/
|
|
45
|
-
export const CONFIG_ARCHIVE_PATHS: ReadonlySet<string> = new Set([
|
|
46
|
-
"workspace/config.json",
|
|
47
|
-
"config/settings.json",
|
|
48
|
-
]);
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Archive path for the credential metadata file. On import, bundle contents
|
|
52
|
-
* must be merged with the target's live `vellum:*` entries so the gateway's
|
|
53
|
-
* `readServiceCredentials` can still locate the platform API key after a
|
|
54
|
-
* local→platform teleport. Both importers consult this constant.
|
|
55
|
-
*/
|
|
56
|
-
const CREDENTIAL_METADATA_ARCHIVE_PATH =
|
|
57
|
-
"workspace/data/credentials/metadata.json";
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Paths inside the workspace directory that must be preserved across an
|
|
61
|
-
* import when the bundle does not carry entries for them.
|
|
62
|
-
*
|
|
63
|
-
* Each entry is a path RELATIVE to the workspace root. Two kinds of live
|
|
64
|
-
* data warrant carry-over:
|
|
65
|
-
*
|
|
66
|
-
* - `embedding-models` / `deprecated`: large local caches / quarantine
|
|
67
|
-
* directories that are never shipped inside bundles but are expensive
|
|
68
|
-
* or impossible to reconstruct from an import.
|
|
69
|
-
* - `data/db` / `data/qdrant`: user-critical state (SQLite assistant DB;
|
|
70
|
-
* Qdrant vector store). If the bundle omits them — e.g. a partial
|
|
71
|
-
* bundle covering only prompts/config — the live copies must survive.
|
|
72
|
-
*
|
|
73
|
-
* Both the buffer-based `commitImport` (which selectively clears the
|
|
74
|
-
* workspace in place) and the streaming importer (which swaps the
|
|
75
|
-
* workspace with a freshly-populated temp tree) consult this list so
|
|
76
|
-
* their behavior stays in sync.
|
|
77
|
-
*/
|
|
78
|
-
export const WORKSPACE_PRESERVE_PATHS: readonly string[] = [
|
|
79
|
-
"embedding-models",
|
|
80
|
-
"deprecated",
|
|
81
|
-
"data/db",
|
|
82
|
-
"data/qdrant",
|
|
83
|
-
];
|
|
84
|
-
|
|
85
43
|
// ---------------------------------------------------------------------------
|
|
86
44
|
// Public types
|
|
87
45
|
// ---------------------------------------------------------------------------
|
|
@@ -130,6 +88,12 @@ export type ImportCommitResult =
|
|
|
130
88
|
errors: Array<{ code: string; message: string; path?: string }>;
|
|
131
89
|
}
|
|
132
90
|
| { ok: false; reason: "extraction_failed"; message: string }
|
|
91
|
+
| {
|
|
92
|
+
ok: false;
|
|
93
|
+
reason: "version_incompatible";
|
|
94
|
+
bundle_compat: RuntimeCompatibility;
|
|
95
|
+
runtime_version: string;
|
|
96
|
+
}
|
|
133
97
|
| {
|
|
134
98
|
ok: false;
|
|
135
99
|
reason: "write_failed";
|
|
@@ -154,6 +118,28 @@ function generateBackupPath(diskPath: string): string {
|
|
|
154
118
|
return `${diskPath}.backup-${timestamp}`;
|
|
155
119
|
}
|
|
156
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Defense-in-depth: returns true if `linkTarget`, when resolved relative to
|
|
123
|
+
* the symlink's own directory (`dirname(diskPath)`), lands outside the
|
|
124
|
+
* supplied `workspaceDir`. The validator (`validateVBundle`) already enforces
|
|
125
|
+
* archive-relative containment, but we re-check here so the buffer importer
|
|
126
|
+
* is safe even if a caller passes a hand-built `preValidatedManifest`.
|
|
127
|
+
*
|
|
128
|
+
* Returns false when `workspaceDir` is undefined — the importer is permitted
|
|
129
|
+
* to write outside any workspace in that mode (e.g. legacy hooks-only
|
|
130
|
+
* imports).
|
|
131
|
+
*/
|
|
132
|
+
function isOutsideWorkspace(
|
|
133
|
+
diskPath: string,
|
|
134
|
+
linkTarget: string,
|
|
135
|
+
workspaceDir: string | undefined,
|
|
136
|
+
): boolean {
|
|
137
|
+
if (!workspaceDir) return false;
|
|
138
|
+
const resolved = resolve(dirname(diskPath), linkTarget);
|
|
139
|
+
const ws = resolve(workspaceDir);
|
|
140
|
+
return resolved !== ws && !resolved.startsWith(ws + sep);
|
|
141
|
+
}
|
|
142
|
+
|
|
157
143
|
// ---------------------------------------------------------------------------
|
|
158
144
|
// Core importer
|
|
159
145
|
// ---------------------------------------------------------------------------
|
|
@@ -217,19 +203,29 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
217
203
|
entryMap = validation.entries;
|
|
218
204
|
}
|
|
219
205
|
|
|
206
|
+
// Defense-in-depth: refuse to import a bundle whose declared compat range
|
|
207
|
+
// excludes this runtime BEFORE any state mutation. The platform-side gate
|
|
208
|
+
// is the primary check; this catches legacy bundles whose ExportJob row
|
|
209
|
+
// predates PR #5470 (compat columns NULL → platform gate skipped) and
|
|
210
|
+
// any caller that bypasses the platform-issued signed URL flow.
|
|
211
|
+
const compatResult = policy.evaluateRuntimeCompatibility(
|
|
212
|
+
manifest.compatibility,
|
|
213
|
+
APP_VERSION,
|
|
214
|
+
);
|
|
215
|
+
if (!compatResult.ok) {
|
|
216
|
+
return {
|
|
217
|
+
ok: false,
|
|
218
|
+
reason: "version_incompatible",
|
|
219
|
+
bundle_compat: compatResult.bundle_compat,
|
|
220
|
+
runtime_version: compatResult.runtime_version,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
220
224
|
// Directories to preserve when clearing the workspace. Derived from the
|
|
221
225
|
// shared WORKSPACE_PRESERVE_PATHS list so the streaming importer's
|
|
222
226
|
// carry-over logic and this in-place clear stay in sync.
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
for (const rel of WORKSPACE_PRESERVE_PATHS) {
|
|
226
|
-
const parts = rel.split("/");
|
|
227
|
-
if (parts.length === 1) {
|
|
228
|
-
WORKSPACE_SKIP_DIRS.add(parts[0]);
|
|
229
|
-
} else if (parts.length === 2 && parts[0] === "data") {
|
|
230
|
-
DATA_SKIP_DIRS.add(parts[1]);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
227
|
+
const { topLevelSkipDirs, dataSubdirSkipDirs } =
|
|
228
|
+
policy.partitionWorkspacePreserveSkipDirs();
|
|
233
229
|
|
|
234
230
|
// Step 1b: Clear the workspace directory before restore if the bundle
|
|
235
231
|
// contains new-format workspace/ entries. This ensures an exact-match
|
|
@@ -247,7 +243,9 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
247
243
|
// "workspace/../../etc/passwd") from triggering a workspace purge while
|
|
248
244
|
// resolving to nothing.
|
|
249
245
|
const hasWorkspaceEntries = manifest.contents.some(
|
|
250
|
-
(f) =>
|
|
246
|
+
(f) =>
|
|
247
|
+
policy.isWorkspaceNamespacedArchivePath(f.path) &&
|
|
248
|
+
!!pathResolver.resolve(f.path),
|
|
251
249
|
);
|
|
252
250
|
|
|
253
251
|
// Capture the target's credential metadata BEFORE the workspace clear
|
|
@@ -257,7 +255,7 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
257
255
|
// (`vellum:*`) entries across the overwrite.
|
|
258
256
|
let liveCredentialMetadataJson: string | null = null;
|
|
259
257
|
const credentialMetadataDiskPath = pathResolver.resolve(
|
|
260
|
-
CREDENTIAL_METADATA_ARCHIVE_PATH,
|
|
258
|
+
policy.CREDENTIAL_METADATA_ARCHIVE_PATH,
|
|
261
259
|
);
|
|
262
260
|
if (credentialMetadataDiskPath && existsSync(credentialMetadataDiskPath)) {
|
|
263
261
|
try {
|
|
@@ -279,7 +277,7 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
279
277
|
// Clear workspace contents selectively, preserving skip dirs
|
|
280
278
|
const topEntries = readdirSync(workspaceDir, { withFileTypes: true });
|
|
281
279
|
for (const entry of topEntries) {
|
|
282
|
-
if (
|
|
280
|
+
if (topLevelSkipDirs.has(entry.name)) continue;
|
|
283
281
|
|
|
284
282
|
const entryPath = join(workspaceDir, entry.name);
|
|
285
283
|
if (entry.name === "data" && entry.isDirectory()) {
|
|
@@ -287,7 +285,7 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
287
285
|
// (critical user data) but clear everything else
|
|
288
286
|
const dataEntries = readdirSync(entryPath, { withFileTypes: true });
|
|
289
287
|
for (const dataEntry of dataEntries) {
|
|
290
|
-
if (
|
|
288
|
+
if (dataSubdirSkipDirs.has(dataEntry.name)) continue;
|
|
291
289
|
rmSync(join(entryPath, dataEntry.name), {
|
|
292
290
|
recursive: true,
|
|
293
291
|
force: true,
|
|
@@ -339,6 +337,185 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
339
337
|
continue;
|
|
340
338
|
}
|
|
341
339
|
|
|
340
|
+
// Symlink branch: recreate the entry on disk as a real symlink so the
|
|
341
|
+
// post-import workspace mirrors the source's link topology rather than
|
|
342
|
+
// duplicating bytes. The validator already enforces archive-relative
|
|
343
|
+
// containment, sha256-over-target, and size==0 — we still reapply
|
|
344
|
+
// absolute-target and workspace-escape gates here so a hand-built
|
|
345
|
+
// `preValidatedManifest` cannot bypass them.
|
|
346
|
+
if (fileEntry.link_target !== undefined) {
|
|
347
|
+
const archiveEntry = entryMap.get(fileEntry.path);
|
|
348
|
+
if (!archiveEntry) {
|
|
349
|
+
importedFiles.push({
|
|
350
|
+
path: fileEntry.path,
|
|
351
|
+
disk_path: diskPath,
|
|
352
|
+
action: "skipped",
|
|
353
|
+
size: 0,
|
|
354
|
+
sha256: fileEntry.sha256,
|
|
355
|
+
backup_path: null,
|
|
356
|
+
});
|
|
357
|
+
warnings.push(
|
|
358
|
+
`Skipped "${fileEntry.path}": declared in manifest but not found in archive`,
|
|
359
|
+
);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Legacy guardian persona (prompts/USER.md) is translated to the
|
|
364
|
+
// current guardian's users/<slug>.md by DefaultPathResolver. If the
|
|
365
|
+
// bundle ships USER.md as a symlink and the target already holds
|
|
366
|
+
// user-authored content, skip rather than clobber — mirrors the
|
|
367
|
+
// protection in the regular-file branch below.
|
|
368
|
+
if (
|
|
369
|
+
policy.isLegacyPersonaArchivePath(fileEntry.path) &&
|
|
370
|
+
isGuardianPersonaCustomized(diskPath)
|
|
371
|
+
) {
|
|
372
|
+
log.warn(
|
|
373
|
+
{ archivePath: fileEntry.path, diskPath },
|
|
374
|
+
"Skipping legacy prompts/USER.md symlink import: guardian persona is already customized",
|
|
375
|
+
);
|
|
376
|
+
warnings.push(
|
|
377
|
+
`Skipped "${fileEntry.path}": guardian persona at "${diskPath}" is already customized`,
|
|
378
|
+
);
|
|
379
|
+
importedFiles.push({
|
|
380
|
+
path: fileEntry.path,
|
|
381
|
+
disk_path: diskPath,
|
|
382
|
+
action: "skipped",
|
|
383
|
+
size: 0,
|
|
384
|
+
sha256: fileEntry.sha256,
|
|
385
|
+
backup_path: null,
|
|
386
|
+
});
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Defense-in-depth path-traversal gate.
|
|
391
|
+
if (
|
|
392
|
+
fileEntry.link_target.startsWith("/") ||
|
|
393
|
+
isOutsideWorkspace(diskPath, fileEntry.link_target, workspaceDir)
|
|
394
|
+
) {
|
|
395
|
+
importedFiles.push({
|
|
396
|
+
path: fileEntry.path,
|
|
397
|
+
disk_path: diskPath,
|
|
398
|
+
action: "skipped",
|
|
399
|
+
size: 0,
|
|
400
|
+
sha256: fileEntry.sha256,
|
|
401
|
+
backup_path: null,
|
|
402
|
+
});
|
|
403
|
+
warnings.push(
|
|
404
|
+
`Skipped "${fileEntry.path}": symlink target "${fileEntry.link_target}" escapes workspace`,
|
|
405
|
+
);
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Back up an existing entry at diskPath, if any. Use `lstatSync` so we
|
|
410
|
+
// detect a pre-existing dangling symlink (which `existsSync` reports
|
|
411
|
+
// as missing) — `symlinkSync` would otherwise fail with EEXIST. For
|
|
412
|
+
// regular files and resolvable symlinks we copy the file contents into
|
|
413
|
+
// the backup, matching the existing contract; for dangling symlinks
|
|
414
|
+
// we preserve the linkname via `readlinkSync`+`symlinkSync` so the
|
|
415
|
+
// original entry can be inspected after the import. The pre-existing
|
|
416
|
+
// entry is removed before `symlinkSync` so the new symlink can land.
|
|
417
|
+
let backupPath: string | null = null;
|
|
418
|
+
let action: ImportFileAction;
|
|
419
|
+
let preExistingEntry = false;
|
|
420
|
+
let preExistingIsSymlink = false;
|
|
421
|
+
try {
|
|
422
|
+
const stats = lstatSync(diskPath);
|
|
423
|
+
preExistingEntry = true;
|
|
424
|
+
preExistingIsSymlink = stats.isSymbolicLink();
|
|
425
|
+
} catch {
|
|
426
|
+
// ENOENT — no pre-existing entry at this path.
|
|
427
|
+
}
|
|
428
|
+
if (preExistingEntry) {
|
|
429
|
+
backupPath = generateBackupPath(diskPath);
|
|
430
|
+
try {
|
|
431
|
+
if (preExistingIsSymlink) {
|
|
432
|
+
const oldTarget = readlinkSync(diskPath);
|
|
433
|
+
symlinkSync(oldTarget, backupPath);
|
|
434
|
+
} else {
|
|
435
|
+
copyFileSync(diskPath, backupPath);
|
|
436
|
+
}
|
|
437
|
+
backupsCreated++;
|
|
438
|
+
} catch (err) {
|
|
439
|
+
return {
|
|
440
|
+
ok: false,
|
|
441
|
+
reason: "write_failed",
|
|
442
|
+
message: `Failed to back up "${diskPath}": ${
|
|
443
|
+
err instanceof Error ? err.message : String(err)
|
|
444
|
+
}`,
|
|
445
|
+
partial_report: buildPartialReport(
|
|
446
|
+
importedFiles,
|
|
447
|
+
manifest,
|
|
448
|
+
warnings,
|
|
449
|
+
backupsCreated,
|
|
450
|
+
),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
action = "overwritten";
|
|
454
|
+
try {
|
|
455
|
+
rmSync(diskPath, { force: true });
|
|
456
|
+
} catch {
|
|
457
|
+
/* best effort — symlinkSync below will surface the real error */
|
|
458
|
+
}
|
|
459
|
+
} else {
|
|
460
|
+
action = "created";
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Ensure parent directory exists.
|
|
464
|
+
const parentDir = dirname(diskPath);
|
|
465
|
+
if (!existsSync(parentDir)) {
|
|
466
|
+
try {
|
|
467
|
+
mkdirSync(parentDir, { recursive: true });
|
|
468
|
+
} catch (err) {
|
|
469
|
+
return {
|
|
470
|
+
ok: false,
|
|
471
|
+
reason: "write_failed",
|
|
472
|
+
message: `Failed to create directory "${parentDir}": ${
|
|
473
|
+
err instanceof Error ? err.message : String(err)
|
|
474
|
+
}`,
|
|
475
|
+
partial_report: buildPartialReport(
|
|
476
|
+
importedFiles,
|
|
477
|
+
manifest,
|
|
478
|
+
warnings,
|
|
479
|
+
backupsCreated,
|
|
480
|
+
),
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Create the symlink. The target is stored verbatim — OS symlink
|
|
486
|
+
// semantics resolve it relative to the symlink's own directory at
|
|
487
|
+
// use time.
|
|
488
|
+
try {
|
|
489
|
+
symlinkSync(fileEntry.link_target, diskPath);
|
|
490
|
+
} catch (err) {
|
|
491
|
+
return {
|
|
492
|
+
ok: false,
|
|
493
|
+
reason: "write_failed",
|
|
494
|
+
message: `Failed to create symlink "${diskPath}" -> "${fileEntry.link_target}": ${
|
|
495
|
+
err instanceof Error ? err.message : String(err)
|
|
496
|
+
}`,
|
|
497
|
+
partial_report: buildPartialReport(
|
|
498
|
+
importedFiles,
|
|
499
|
+
manifest,
|
|
500
|
+
warnings,
|
|
501
|
+
backupsCreated,
|
|
502
|
+
),
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
importedFiles.push({
|
|
507
|
+
path: fileEntry.path,
|
|
508
|
+
disk_path: diskPath,
|
|
509
|
+
action,
|
|
510
|
+
size: 0,
|
|
511
|
+
sha256: fileEntry.sha256,
|
|
512
|
+
backup_path: backupPath,
|
|
513
|
+
});
|
|
514
|
+
// Skip the regular-file branches (and the post-write integrity check,
|
|
515
|
+
// which would dereference the symlink and read the target's bytes).
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
|
|
342
519
|
const archiveEntry = entryMap.get(fileEntry.path);
|
|
343
520
|
if (!archiveEntry) {
|
|
344
521
|
// File declared in manifest but not found in archive — should not
|
|
@@ -363,7 +540,7 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
363
540
|
// than clobber — the user has curated their persona since the
|
|
364
541
|
// bundle was exported.
|
|
365
542
|
if (
|
|
366
|
-
fileEntry.path
|
|
543
|
+
policy.isLegacyPersonaArchivePath(fileEntry.path) &&
|
|
367
544
|
isGuardianPersonaCustomized(diskPath)
|
|
368
545
|
) {
|
|
369
546
|
log.warn(
|
|
@@ -438,7 +615,7 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
438
615
|
|
|
439
616
|
// Sanitize config files to strip environment-specific fields (defense-in-depth)
|
|
440
617
|
let dataToWrite: Uint8Array = archiveEntry.data;
|
|
441
|
-
if (
|
|
618
|
+
if (policy.isConfigArchivePath(fileEntry.path)) {
|
|
442
619
|
const configJson = new TextDecoder().decode(archiveEntry.data);
|
|
443
620
|
const sanitized = sanitizeConfigForTransfer(configJson);
|
|
444
621
|
dataToWrite = new TextEncoder().encode(sanitized);
|
|
@@ -450,7 +627,7 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
450
627
|
// would wipe them and break the gateway's vellum credential read.
|
|
451
628
|
// We use the snapshot captured BEFORE the workspace clear because
|
|
452
629
|
// Step 1b may have already removed the live file.
|
|
453
|
-
if (fileEntry.path
|
|
630
|
+
if (policy.isCredentialMetadataArchivePath(fileEntry.path)) {
|
|
454
631
|
const bundleJson = new TextDecoder().decode(archiveEntry.data);
|
|
455
632
|
const merged = mergeMetadataPreservingVellum(
|
|
456
633
|
bundleJson,
|
|
@@ -536,8 +713,8 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
536
713
|
// run (e.g. workspaceDir unset) the live metadata.json is still on
|
|
537
714
|
// disk untouched — we must not rewrite it here or we would drop the
|
|
538
715
|
// non-vellum entries the caller chose to keep.
|
|
539
|
-
const bundleHadMetadata = manifest.contents.some(
|
|
540
|
-
(f
|
|
716
|
+
const bundleHadMetadata = manifest.contents.some((f) =>
|
|
717
|
+
policy.isCredentialMetadataArchivePath(f.path),
|
|
541
718
|
);
|
|
542
719
|
if (
|
|
543
720
|
workspaceWasCleared &&
|