@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
|
@@ -19,15 +19,17 @@ import {
|
|
|
19
19
|
readdirSync,
|
|
20
20
|
readFileSync,
|
|
21
21
|
readSync,
|
|
22
|
+
realpathSync,
|
|
22
23
|
} from "node:fs";
|
|
23
24
|
import { stat, unlink } from "node:fs/promises";
|
|
24
25
|
import { tmpdir } from "node:os";
|
|
25
|
-
import { join, relative } from "node:path";
|
|
26
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
26
27
|
import { Readable } from "node:stream";
|
|
27
28
|
import { pipeline } from "node:stream/promises";
|
|
28
29
|
import { createGzip, gzipSync } from "node:zlib";
|
|
29
30
|
|
|
30
31
|
import { sanitizeConfigForTransfer } from "../../config/sanitize-for-transfer.js";
|
|
32
|
+
import { getLogger } from "../../util/logger.js";
|
|
31
33
|
import type { VBundleOriginMode } from "./origin-mode.js";
|
|
32
34
|
import type {
|
|
33
35
|
ManifestFileEntryType,
|
|
@@ -41,6 +43,8 @@ import type {
|
|
|
41
43
|
export interface VBundleFileEntry {
|
|
42
44
|
path: string;
|
|
43
45
|
data: Uint8Array;
|
|
46
|
+
/** When set, `data` is ignored: the entry is emitted as a tar typeflag-2 (symlink) record with empty body, and `linkTarget` is the symlink target encoded relative to the symlink's own directory inside the archive. */
|
|
47
|
+
linkTarget?: string;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/** v1 manifest `assistant` block. */
|
|
@@ -109,13 +113,24 @@ interface InMemoryEntry {
|
|
|
109
113
|
size: number;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
/**
|
|
113
|
-
|
|
116
|
+
/** Symlink entry — emitted as a tar typeflag-2 record with empty body. */
|
|
117
|
+
interface SymlinkMetadata {
|
|
118
|
+
archivePath: string;
|
|
119
|
+
linkTarget: string;
|
|
120
|
+
size: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Union of disk-backed, in-memory, and symlink tar stream entries. */
|
|
124
|
+
type TarStreamEntry = FileMetadata | InMemoryEntry | SymlinkMetadata;
|
|
114
125
|
|
|
115
126
|
function isInMemoryEntry(entry: TarStreamEntry): entry is InMemoryEntry {
|
|
116
127
|
return "data" in entry;
|
|
117
128
|
}
|
|
118
129
|
|
|
130
|
+
function isSymlinkEntry(entry: TarStreamEntry): entry is SymlinkMetadata {
|
|
131
|
+
return "linkTarget" in entry;
|
|
132
|
+
}
|
|
133
|
+
|
|
119
134
|
// ---------------------------------------------------------------------------
|
|
120
135
|
// Hash helpers
|
|
121
136
|
// ---------------------------------------------------------------------------
|
|
@@ -234,7 +249,11 @@ function createPaxPathEntry(name: string): Uint8Array {
|
|
|
234
249
|
return result;
|
|
235
250
|
}
|
|
236
251
|
|
|
237
|
-
function createTarEntry(
|
|
252
|
+
function createTarEntry(
|
|
253
|
+
name: string,
|
|
254
|
+
data: Uint8Array,
|
|
255
|
+
linkTarget?: string,
|
|
256
|
+
): Uint8Array {
|
|
238
257
|
const encoder = new TextEncoder();
|
|
239
258
|
const nameBytes = encoder.encode(name);
|
|
240
259
|
|
|
@@ -243,6 +262,14 @@ function createTarEntry(name: string, data: Uint8Array): Uint8Array {
|
|
|
243
262
|
const needsPax = nameBytes.length > 100;
|
|
244
263
|
const paxEntry = needsPax ? createPaxPathEntry(name) : null;
|
|
245
264
|
|
|
265
|
+
const isSymlink = linkTarget !== undefined;
|
|
266
|
+
const linkTargetBytes = isSymlink ? encoder.encode(linkTarget) : null;
|
|
267
|
+
if (linkTargetBytes && linkTargetBytes.length > 100) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Symlink target "${linkTarget}" is ${linkTargetBytes.length} bytes, exceeding the ustar linkname-field 100-byte limit. The walker should guard against this case before calling createTarEntry.`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
246
273
|
const header = new Uint8Array(BLOCK_SIZE);
|
|
247
274
|
|
|
248
275
|
// File name (0-99) — truncated if >100 bytes; PAX header carries the full name
|
|
@@ -257,14 +284,19 @@ function createTarEntry(name: string, data: Uint8Array): Uint8Array {
|
|
|
257
284
|
// Group ID (116-123)
|
|
258
285
|
writeOctal(header, 116, 8, 0);
|
|
259
286
|
|
|
260
|
-
// File size (124-135)
|
|
261
|
-
writeOctal(header, 124, 12, data.length);
|
|
287
|
+
// File size (124-135) — symlink entries always carry size 0
|
|
288
|
+
writeOctal(header, 124, 12, isSymlink ? 0 : data.length);
|
|
262
289
|
|
|
263
290
|
// Modification time (136-147)
|
|
264
291
|
writeOctal(header, 136, 12, Math.floor(Date.now() / 1000));
|
|
265
292
|
|
|
266
|
-
// Type flag (156): regular file
|
|
267
|
-
header[156] = "0".charCodeAt(0);
|
|
293
|
+
// Type flag (156): regular file ("0") or symlink ("2")
|
|
294
|
+
header[156] = (isSymlink ? "2" : "0").charCodeAt(0);
|
|
295
|
+
|
|
296
|
+
// Linkname (157-256) — only set for symlinks; null-padded by default
|
|
297
|
+
if (linkTargetBytes) {
|
|
298
|
+
header.set(linkTargetBytes, 157);
|
|
299
|
+
}
|
|
268
300
|
|
|
269
301
|
// USTAR magic (257-262)
|
|
270
302
|
const magic = encoder.encode("ustar\0");
|
|
@@ -274,16 +306,22 @@ function createTarEntry(name: string, data: Uint8Array): Uint8Array {
|
|
|
274
306
|
header[263] = "0".charCodeAt(0);
|
|
275
307
|
header[264] = "0".charCodeAt(0);
|
|
276
308
|
|
|
277
|
-
// Compute and write checksum (148-155)
|
|
309
|
+
// Compute and write checksum (148-155) — must be last so the linkname
|
|
310
|
+
// (and every other field) contributes to the sum.
|
|
278
311
|
const checksum = computeHeaderChecksum(header);
|
|
279
312
|
writeOctal(header, 148, 7, checksum);
|
|
280
313
|
header[155] = 0x20; // trailing space
|
|
281
314
|
|
|
282
|
-
//
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
315
|
+
// Symlink entries are header-only — no body, no padding.
|
|
316
|
+
const fileEntry = isSymlink
|
|
317
|
+
? header
|
|
318
|
+
: (() => {
|
|
319
|
+
const paddedData = padToBlock(data);
|
|
320
|
+
const combined = new Uint8Array(header.length + paddedData.length);
|
|
321
|
+
combined.set(header, 0);
|
|
322
|
+
combined.set(paddedData, header.length);
|
|
323
|
+
return combined;
|
|
324
|
+
})();
|
|
287
325
|
|
|
288
326
|
if (paxEntry) {
|
|
289
327
|
const result = new Uint8Array(paxEntry.length + fileEntry.length);
|
|
@@ -296,11 +334,11 @@ function createTarEntry(name: string, data: Uint8Array): Uint8Array {
|
|
|
296
334
|
}
|
|
297
335
|
|
|
298
336
|
function createTarArchive(
|
|
299
|
-
entries: Array<{ name: string; data: Uint8Array }>,
|
|
337
|
+
entries: Array<{ name: string; data: Uint8Array; linkTarget?: string }>,
|
|
300
338
|
): Uint8Array {
|
|
301
339
|
const parts: Uint8Array[] = [];
|
|
302
340
|
for (const entry of entries) {
|
|
303
|
-
parts.push(createTarEntry(entry.name, entry.data));
|
|
341
|
+
parts.push(createTarEntry(entry.name, entry.data, entry.linkTarget));
|
|
304
342
|
}
|
|
305
343
|
// End-of-archive: two zero blocks
|
|
306
344
|
parts.push(new Uint8Array(BLOCK_SIZE * 2));
|
|
@@ -374,12 +412,22 @@ export function buildVBundle(options: BuildVBundleOptions): BuildVBundleResult {
|
|
|
374
412
|
secretsRedacted,
|
|
375
413
|
} = options;
|
|
376
414
|
|
|
377
|
-
// Build file entries for the manifest
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
415
|
+
// Build file entries for the manifest. Symlink entries hash the link target
|
|
416
|
+
// string (not the empty data buffer) and declare size_bytes: 0.
|
|
417
|
+
const fileEntries: ManifestFileEntryType[] = files.map((f) =>
|
|
418
|
+
f.linkTarget !== undefined
|
|
419
|
+
? {
|
|
420
|
+
path: f.path,
|
|
421
|
+
sha256: sha256Hex(f.linkTarget),
|
|
422
|
+
size_bytes: 0,
|
|
423
|
+
link_target: f.linkTarget,
|
|
424
|
+
}
|
|
425
|
+
: {
|
|
426
|
+
path: f.path,
|
|
427
|
+
sha256: sha256Hex(f.data),
|
|
428
|
+
size_bytes: f.data.length,
|
|
429
|
+
},
|
|
430
|
+
);
|
|
383
431
|
|
|
384
432
|
const { manifest, manifestData } = buildManifestObject({
|
|
385
433
|
contents: fileEntries,
|
|
@@ -391,10 +439,16 @@ export function buildVBundle(options: BuildVBundleOptions): BuildVBundleResult {
|
|
|
391
439
|
now: new Date(),
|
|
392
440
|
});
|
|
393
441
|
|
|
394
|
-
// Build tar entries: manifest first, then all files
|
|
442
|
+
// Build tar entries: manifest first, then all files. Symlink entries forward
|
|
443
|
+
// `linkTarget` so createTarEntry emits a typeflag-2 header; `data` is unused
|
|
444
|
+
// in that branch but must still be a valid Uint8Array.
|
|
395
445
|
const tarEntries = [
|
|
396
446
|
{ name: "manifest.json", data: manifestData },
|
|
397
|
-
...files.map((f) =>
|
|
447
|
+
...files.map((f) =>
|
|
448
|
+
f.linkTarget !== undefined
|
|
449
|
+
? { name: f.path, data: new Uint8Array(0), linkTarget: f.linkTarget }
|
|
450
|
+
: { name: f.path, data: f.data },
|
|
451
|
+
),
|
|
398
452
|
];
|
|
399
453
|
|
|
400
454
|
const tar = createTarArchive(tarEntries);
|
|
@@ -410,33 +464,143 @@ export function buildVBundle(options: BuildVBundleOptions): BuildVBundleResult {
|
|
|
410
464
|
interface WalkDirectoryOptions {
|
|
411
465
|
/** Include binary files (files containing null bytes). Default: false. */
|
|
412
466
|
includeBinary?: boolean;
|
|
413
|
-
/** Directory names to skip (matched against
|
|
467
|
+
/** Directory names to skip (matched against relative path from walk root). */
|
|
414
468
|
skipDirs?: string[];
|
|
469
|
+
/** File names to skip (matched against the entry basename). */
|
|
470
|
+
skipFiles?: string[];
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Resolve and classify a symlink encountered during a walk.
|
|
475
|
+
*
|
|
476
|
+
* Returns one of:
|
|
477
|
+
* { kind: "class1", linkTarget } — emit as a tar typeflag-2 entry whose
|
|
478
|
+
* `linkname` field holds `linkTarget` (the symlink target encoded as a
|
|
479
|
+
* POSIX path relative to the symlink's own directory).
|
|
480
|
+
* { kind: "drop", reason } — drop the symlink. Reasons cover broken
|
|
481
|
+
* links, targets outside the workspace (class 2), targets inside a
|
|
482
|
+
* skipped directory (class 3), directory targets (out of scope), and
|
|
483
|
+
* link targets whose UTF-8 encoding exceeds the 100-byte ustar
|
|
484
|
+
* `linkname` field limit.
|
|
485
|
+
*/
|
|
486
|
+
type SymlinkClassification =
|
|
487
|
+
| { kind: "class1"; linkTarget: string }
|
|
488
|
+
| { kind: "drop"; reason: string };
|
|
489
|
+
|
|
490
|
+
function classifySymlink(args: {
|
|
491
|
+
fullPath: string;
|
|
492
|
+
walkRoot: string;
|
|
493
|
+
skipDirs: readonly string[];
|
|
494
|
+
}): SymlinkClassification {
|
|
495
|
+
const { fullPath, walkRoot, skipDirs } = args;
|
|
496
|
+
|
|
497
|
+
let absoluteTarget: string;
|
|
498
|
+
try {
|
|
499
|
+
absoluteTarget = realpathSync(fullPath);
|
|
500
|
+
} catch {
|
|
501
|
+
return { kind: "drop", reason: "broken symlink (realpath failed)" };
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
let targetStat;
|
|
505
|
+
try {
|
|
506
|
+
targetStat = lstatSync(absoluteTarget);
|
|
507
|
+
} catch {
|
|
508
|
+
return { kind: "drop", reason: "broken symlink (target stat failed)" };
|
|
509
|
+
}
|
|
510
|
+
if (!targetStat.isFile()) {
|
|
511
|
+
return { kind: "drop", reason: "target is not a regular file" };
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
let dirAbs: string;
|
|
515
|
+
try {
|
|
516
|
+
dirAbs = realpathSync(walkRoot);
|
|
517
|
+
} catch {
|
|
518
|
+
dirAbs = resolve(walkRoot);
|
|
519
|
+
}
|
|
520
|
+
const targetAbs = resolve(absoluteTarget);
|
|
521
|
+
const insideWorkspace =
|
|
522
|
+
targetAbs === dirAbs || targetAbs.startsWith(dirAbs + sep);
|
|
523
|
+
if (!insideWorkspace) {
|
|
524
|
+
return { kind: "drop", reason: "target outside workspace" };
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const targetRelToWorkspace = relative(dirAbs, targetAbs);
|
|
528
|
+
if (
|
|
529
|
+
skipDirs.some(
|
|
530
|
+
(s) =>
|
|
531
|
+
targetRelToWorkspace === s || targetRelToWorkspace.startsWith(s + "/"),
|
|
532
|
+
)
|
|
533
|
+
) {
|
|
534
|
+
return { kind: "drop", reason: "target inside skipDir" };
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Canonicalize the symlink's parent directory so the relative linkTarget
|
|
538
|
+
// computation lines up with `absoluteTarget` (which is canonical from
|
|
539
|
+
// realpathSync). On macOS, walking through /var/folders/... and resolving
|
|
540
|
+
// the target through /private/var/folders/... would otherwise produce a
|
|
541
|
+
// long ../../../private/... path that exceeds the 100-byte ustar limit.
|
|
542
|
+
let parentAbs: string;
|
|
543
|
+
try {
|
|
544
|
+
parentAbs = realpathSync(dirname(fullPath));
|
|
545
|
+
} catch {
|
|
546
|
+
parentAbs = resolve(dirname(fullPath));
|
|
547
|
+
}
|
|
548
|
+
const linkTarget = relative(parentAbs, absoluteTarget);
|
|
549
|
+
if (new TextEncoder().encode(linkTarget).length > 100) {
|
|
550
|
+
return {
|
|
551
|
+
kind: "drop",
|
|
552
|
+
reason: "encoded link target exceeds 100-byte ustar limit",
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return { kind: "class1", linkTarget };
|
|
415
557
|
}
|
|
416
558
|
|
|
417
559
|
/**
|
|
418
|
-
* Recursively walk a directory and return all
|
|
419
|
-
* VBundleFileEntry objects with paths prefixed by
|
|
560
|
+
* Recursively walk a directory and return all regular files (and bundleable
|
|
561
|
+
* symlinks) as VBundleFileEntry objects with paths prefixed by
|
|
562
|
+
* `archivePrefix`. Symlinks that resolve to a regular file inside the walk
|
|
563
|
+
* root and outside any skipDir are emitted as typeflag-2 entries (data
|
|
564
|
+
* empty, `linkTarget` populated). All other symlinks (broken, directory
|
|
565
|
+
* target, target outside workspace, target inside skipDir, encoded
|
|
566
|
+
* linkTarget over 100 bytes) are reported via the returned `droppedSymlinks`
|
|
567
|
+
* array as workspace-relative paths of the symlink itself.
|
|
420
568
|
*
|
|
421
569
|
* By default, binary files (detected via null-byte heuristic in the first
|
|
422
570
|
* 8 KB) are skipped. Pass `includeBinary: true` to include them.
|
|
423
571
|
*/
|
|
424
|
-
function walkDirectory(
|
|
572
|
+
export function walkDirectory(
|
|
425
573
|
dir: string,
|
|
426
574
|
archivePrefix: string,
|
|
427
575
|
options: WalkDirectoryOptions = {},
|
|
428
|
-
): VBundleFileEntry[] {
|
|
429
|
-
const { includeBinary = false, skipDirs = [] } = options;
|
|
576
|
+
): { files: VBundleFileEntry[]; droppedSymlinks: string[] } {
|
|
577
|
+
const { includeBinary = false, skipDirs = [], skipFiles = [] } = options;
|
|
430
578
|
const entries: VBundleFileEntry[] = [];
|
|
579
|
+
const droppedSymlinks: string[] = [];
|
|
431
580
|
|
|
432
581
|
function walk(currentDir: string): void {
|
|
433
582
|
const dirEntries = readdirSync(currentDir, { withFileTypes: true });
|
|
434
583
|
for (const entry of dirEntries) {
|
|
435
584
|
const fullPath = join(currentDir, entry.name);
|
|
436
585
|
|
|
437
|
-
// Skip symlinks
|
|
438
586
|
const stat = lstatSync(fullPath);
|
|
439
|
-
if (stat.isSymbolicLink())
|
|
587
|
+
if (stat.isSymbolicLink()) {
|
|
588
|
+
const classification = classifySymlink({
|
|
589
|
+
fullPath,
|
|
590
|
+
walkRoot: dir,
|
|
591
|
+
skipDirs,
|
|
592
|
+
});
|
|
593
|
+
if (classification.kind === "class1") {
|
|
594
|
+
entries.push({
|
|
595
|
+
path: `${archivePrefix}/${relative(dir, fullPath)}`,
|
|
596
|
+
data: new Uint8Array(0),
|
|
597
|
+
linkTarget: classification.linkTarget,
|
|
598
|
+
});
|
|
599
|
+
} else {
|
|
600
|
+
droppedSymlinks.push(relative(dir, fullPath));
|
|
601
|
+
}
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
440
604
|
|
|
441
605
|
if (stat.isDirectory()) {
|
|
442
606
|
// Check skip list against the relative path from the walk root
|
|
@@ -446,6 +610,9 @@ function walkDirectory(
|
|
|
446
610
|
}
|
|
447
611
|
walk(fullPath);
|
|
448
612
|
} else if (stat.isFile()) {
|
|
613
|
+
// Skip files by basename (e.g. backup key)
|
|
614
|
+
if (skipFiles.includes(entry.name)) continue;
|
|
615
|
+
|
|
449
616
|
// Skip SQLite auxiliary files — these are ephemeral and race-prone
|
|
450
617
|
// with the live DB connection. The WAL is checkpointed before the
|
|
451
618
|
// walk, so the main .db file has all committed rows.
|
|
@@ -482,7 +649,7 @@ function walkDirectory(
|
|
|
482
649
|
}
|
|
483
650
|
|
|
484
651
|
walk(dir);
|
|
485
|
-
return entries;
|
|
652
|
+
return { files: entries, droppedSymlinks };
|
|
486
653
|
}
|
|
487
654
|
|
|
488
655
|
// ---------------------------------------------------------------------------
|
|
@@ -561,12 +728,22 @@ export function buildExportVBundle(
|
|
|
561
728
|
existsSync(workspaceDir) &&
|
|
562
729
|
lstatSync(workspaceDir).isDirectory()
|
|
563
730
|
) {
|
|
564
|
-
files
|
|
565
|
-
|
|
731
|
+
const { files: walkedFiles, droppedSymlinks } = walkDirectory(
|
|
732
|
+
workspaceDir,
|
|
733
|
+
"workspace",
|
|
734
|
+
{
|
|
566
735
|
includeBinary: true,
|
|
567
736
|
skipDirs: ["embedding-models", "data/qdrant", "signals", "deprecated"],
|
|
568
|
-
|
|
737
|
+
skipFiles: [".backup.key"],
|
|
738
|
+
},
|
|
569
739
|
);
|
|
740
|
+
files.push(...walkedFiles);
|
|
741
|
+
if (droppedSymlinks.length > 0) {
|
|
742
|
+
getLogger("vbundle-builder").warn(
|
|
743
|
+
{ count: droppedSymlinks.length, paths: droppedSymlinks },
|
|
744
|
+
`Dropped ${droppedSymlinks.length} symlinks pointing outside workspace or into skipped directories`,
|
|
745
|
+
);
|
|
746
|
+
}
|
|
570
747
|
}
|
|
571
748
|
|
|
572
749
|
// Sanitize workspace/config.json to strip environment-specific fields
|
|
@@ -601,26 +778,48 @@ export function buildExportVBundle(
|
|
|
601
778
|
|
|
602
779
|
/**
|
|
603
780
|
* Walk a directory tree and collect file metadata (paths + sizes) without
|
|
604
|
-
* reading file contents into memory.
|
|
605
|
-
*
|
|
606
|
-
*
|
|
781
|
+
* reading file contents into memory. Mirrors `walkDirectory`'s filtering
|
|
782
|
+
* logic (SQLite auxiliary skip, binary detection, skipDirs) and symlink
|
|
783
|
+
* classification — bundleable symlinks are emitted as `SymlinkMetadata`
|
|
784
|
+
* entries; non-bundleable symlinks are reported via `droppedSymlinks`.
|
|
607
785
|
*/
|
|
608
|
-
function walkDirectoryForMetadata(
|
|
786
|
+
export function walkDirectoryForMetadata(
|
|
609
787
|
dir: string,
|
|
610
788
|
archivePrefix: string,
|
|
611
789
|
options: WalkDirectoryOptions = {},
|
|
612
|
-
):
|
|
613
|
-
|
|
790
|
+
): {
|
|
791
|
+
files: FileMetadata[];
|
|
792
|
+
symlinks: SymlinkMetadata[];
|
|
793
|
+
droppedSymlinks: string[];
|
|
794
|
+
} {
|
|
795
|
+
const { includeBinary = false, skipDirs = [], skipFiles = [] } = options;
|
|
614
796
|
const entries: FileMetadata[] = [];
|
|
797
|
+
const symlinks: SymlinkMetadata[] = [];
|
|
798
|
+
const droppedSymlinks: string[] = [];
|
|
615
799
|
|
|
616
800
|
function walk(currentDir: string): void {
|
|
617
801
|
const dirEntries = readdirSync(currentDir, { withFileTypes: true });
|
|
618
802
|
for (const entry of dirEntries) {
|
|
619
803
|
const fullPath = join(currentDir, entry.name);
|
|
620
804
|
|
|
621
|
-
// Skip symlinks
|
|
622
805
|
const fileStat = lstatSync(fullPath);
|
|
623
|
-
if (fileStat.isSymbolicLink())
|
|
806
|
+
if (fileStat.isSymbolicLink()) {
|
|
807
|
+
const classification = classifySymlink({
|
|
808
|
+
fullPath,
|
|
809
|
+
walkRoot: dir,
|
|
810
|
+
skipDirs,
|
|
811
|
+
});
|
|
812
|
+
if (classification.kind === "class1") {
|
|
813
|
+
symlinks.push({
|
|
814
|
+
archivePath: `${archivePrefix}/${relative(dir, fullPath)}`,
|
|
815
|
+
linkTarget: classification.linkTarget,
|
|
816
|
+
size: 0,
|
|
817
|
+
});
|
|
818
|
+
} else {
|
|
819
|
+
droppedSymlinks.push(relative(dir, fullPath));
|
|
820
|
+
}
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
624
823
|
|
|
625
824
|
if (fileStat.isDirectory()) {
|
|
626
825
|
// Check skip list against the relative path from the walk root
|
|
@@ -630,6 +829,9 @@ function walkDirectoryForMetadata(
|
|
|
630
829
|
}
|
|
631
830
|
walk(fullPath);
|
|
632
831
|
} else if (fileStat.isFile()) {
|
|
832
|
+
// Skip files by basename (e.g. backup key)
|
|
833
|
+
if (skipFiles.includes(entry.name)) continue;
|
|
834
|
+
|
|
633
835
|
// Skip SQLite auxiliary files — these are ephemeral and race-prone
|
|
634
836
|
if (
|
|
635
837
|
entry.name.endsWith(".db-wal") ||
|
|
@@ -673,7 +875,7 @@ function walkDirectoryForMetadata(
|
|
|
673
875
|
}
|
|
674
876
|
|
|
675
877
|
walk(dir);
|
|
676
|
-
return entries;
|
|
878
|
+
return { files: entries, symlinks, droppedSymlinks };
|
|
677
879
|
}
|
|
678
880
|
|
|
679
881
|
/**
|
|
@@ -699,8 +901,18 @@ async function computeFileSha256(
|
|
|
699
901
|
/**
|
|
700
902
|
* Create just the 512-byte tar header block for a regular file entry.
|
|
701
903
|
* Extracted from `createTarEntry` logic — does NOT include data or padding.
|
|
904
|
+
*
|
|
905
|
+
* When `linkTarget` is provided, the header is emitted as a tar typeflag-2
|
|
906
|
+
* (symlink) record: typeflag is "2", the link target is written into the
|
|
907
|
+
* `linkname` field (header[157..256], 100-byte limit), and `size` is forced
|
|
908
|
+
* to 0 in the header field. Caller is responsible for not yielding any body
|
|
909
|
+
* or padding bytes for symlink entries.
|
|
702
910
|
*/
|
|
703
|
-
function createTarHeaderBlock(
|
|
911
|
+
function createTarHeaderBlock(
|
|
912
|
+
name: string,
|
|
913
|
+
size: number,
|
|
914
|
+
linkTarget?: string,
|
|
915
|
+
): Uint8Array {
|
|
704
916
|
const encoder = new TextEncoder();
|
|
705
917
|
const nameBytes = encoder.encode(name);
|
|
706
918
|
|
|
@@ -718,14 +930,26 @@ function createTarHeaderBlock(name: string, size: number): Uint8Array {
|
|
|
718
930
|
// Group ID (116-123)
|
|
719
931
|
writeOctal(header, 116, 8, 0);
|
|
720
932
|
|
|
721
|
-
// File size (124-135)
|
|
722
|
-
writeOctal(header, 124, 12, size);
|
|
933
|
+
// File size (124-135) — symlink entries always declare size=0
|
|
934
|
+
writeOctal(header, 124, 12, linkTarget !== undefined ? 0 : size);
|
|
723
935
|
|
|
724
936
|
// Modification time (136-147)
|
|
725
937
|
writeOctal(header, 136, 12, Math.floor(Date.now() / 1000));
|
|
726
938
|
|
|
727
|
-
// Type flag (156): regular file
|
|
728
|
-
header[156] =
|
|
939
|
+
// Type flag (156): regular file ("0") or symlink ("2")
|
|
940
|
+
header[156] =
|
|
941
|
+
linkTarget !== undefined ? "2".charCodeAt(0) : "0".charCodeAt(0);
|
|
942
|
+
|
|
943
|
+
// Linkname (157-256, 100 bytes) — only set for symlink entries
|
|
944
|
+
if (linkTarget !== undefined) {
|
|
945
|
+
const linkBytes = encoder.encode(linkTarget);
|
|
946
|
+
if (linkBytes.length > 100) {
|
|
947
|
+
throw new Error(
|
|
948
|
+
`symlink target exceeds 100-byte ustar linkname limit (${linkBytes.length} bytes): ${linkTarget}`,
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
header.set(linkBytes, 157);
|
|
952
|
+
}
|
|
729
953
|
|
|
730
954
|
// USTAR magic (257-262)
|
|
731
955
|
const magic = encoder.encode("ustar\0");
|
|
@@ -735,7 +959,8 @@ function createTarHeaderBlock(name: string, size: number): Uint8Array {
|
|
|
735
959
|
header[263] = "0".charCodeAt(0);
|
|
736
960
|
header[264] = "0".charCodeAt(0);
|
|
737
961
|
|
|
738
|
-
// Compute and write checksum (148-155)
|
|
962
|
+
// Compute and write checksum (148-155). Must run AFTER linkname is set
|
|
963
|
+
// so the checksum covers the symlink target bytes.
|
|
739
964
|
const checksum = computeHeaderChecksum(header);
|
|
740
965
|
writeOctal(header, 148, 7, checksum);
|
|
741
966
|
header[155] = 0x20; // trailing space
|
|
@@ -747,13 +972,21 @@ function createTarHeaderBlock(name: string, size: number): Uint8Array {
|
|
|
747
972
|
* If name exceeds 100 bytes, returns the PAX extended header entry
|
|
748
973
|
* concatenated with the regular header block. Otherwise returns just
|
|
749
974
|
* the header block.
|
|
975
|
+
*
|
|
976
|
+
* `linkTarget` is forwarded to `createTarHeaderBlock` so symlink entries
|
|
977
|
+
* still get a PAX path header for long names while emitting a typeflag-2
|
|
978
|
+
* ustar block.
|
|
750
979
|
*/
|
|
751
|
-
function createPaxAndHeaderBlocks(
|
|
980
|
+
function createPaxAndHeaderBlocks(
|
|
981
|
+
name: string,
|
|
982
|
+
size: number,
|
|
983
|
+
linkTarget?: string,
|
|
984
|
+
): Uint8Array {
|
|
752
985
|
const encoder = new TextEncoder();
|
|
753
986
|
const nameBytes = encoder.encode(name);
|
|
754
987
|
const needsPax = nameBytes.length > 100;
|
|
755
988
|
|
|
756
|
-
const header = createTarHeaderBlock(name, size);
|
|
989
|
+
const header = createTarHeaderBlock(name, size, linkTarget);
|
|
757
990
|
|
|
758
991
|
if (needsPax) {
|
|
759
992
|
const paxEntry = createPaxPathEntry(name);
|
|
@@ -791,7 +1024,15 @@ async function* generateTarStream(
|
|
|
791
1024
|
|
|
792
1025
|
// File entries
|
|
793
1026
|
for (const file of files) {
|
|
794
|
-
|
|
1027
|
+
if (isSymlinkEntry(file)) {
|
|
1028
|
+
// Symlink entry: typeflag-2 header carries the linkname; no body, no
|
|
1029
|
+
// padding. Skip the entrySize/body/padding logic entirely so the
|
|
1030
|
+
// surrounding stream stays block-aligned.
|
|
1031
|
+
yield createPaxAndHeaderBlocks(file.archivePath, 0, file.linkTarget);
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const entrySize = file.size;
|
|
795
1036
|
yield createPaxAndHeaderBlocks(file.archivePath, entrySize);
|
|
796
1037
|
|
|
797
1038
|
if (isInMemoryEntry(file)) {
|
|
@@ -885,6 +1126,7 @@ export async function streamExportVBundle(
|
|
|
885
1126
|
}
|
|
886
1127
|
|
|
887
1128
|
const allFileMetadata: FileMetadata[] = [];
|
|
1129
|
+
const symlinkEntries: SymlinkMetadata[] = [];
|
|
888
1130
|
|
|
889
1131
|
// Walk the entire workspace directory, including binary files
|
|
890
1132
|
if (
|
|
@@ -892,12 +1134,23 @@ export async function streamExportVBundle(
|
|
|
892
1134
|
existsSync(workspaceDir) &&
|
|
893
1135
|
lstatSync(workspaceDir).isDirectory()
|
|
894
1136
|
) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1137
|
+
const {
|
|
1138
|
+
files: walkedFiles,
|
|
1139
|
+
symlinks: walkedSymlinks,
|
|
1140
|
+
droppedSymlinks,
|
|
1141
|
+
} = walkDirectoryForMetadata(workspaceDir, "workspace", {
|
|
1142
|
+
includeBinary: true,
|
|
1143
|
+
skipDirs: ["embedding-models", "data/qdrant", "signals", "deprecated"],
|
|
1144
|
+
skipFiles: [".backup.key"],
|
|
1145
|
+
});
|
|
1146
|
+
allFileMetadata.push(...walkedFiles);
|
|
1147
|
+
symlinkEntries.push(...walkedSymlinks);
|
|
1148
|
+
if (droppedSymlinks.length > 0) {
|
|
1149
|
+
getLogger("vbundle-builder").warn(
|
|
1150
|
+
{ count: droppedSymlinks.length, paths: droppedSymlinks },
|
|
1151
|
+
`Dropped ${droppedSymlinks.length} symlinks pointing outside workspace or into skipped directories`,
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
901
1154
|
}
|
|
902
1155
|
|
|
903
1156
|
// Sanitize workspace/config.json: read from disk, sanitize, and replace the
|
|
@@ -960,6 +1213,19 @@ export async function streamExportVBundle(
|
|
|
960
1213
|
});
|
|
961
1214
|
}
|
|
962
1215
|
|
|
1216
|
+
// Add symlink entries to the manifest. The sha256 is computed over the
|
|
1217
|
+
// link target string (UTF-8 encoded) so the streaming validator can
|
|
1218
|
+
// verify the manifest declared the same target the tar header carries.
|
|
1219
|
+
// size_bytes is always 0 for symlink entries.
|
|
1220
|
+
for (const entry of symlinkEntries) {
|
|
1221
|
+
fileEntries.push({
|
|
1222
|
+
path: entry.archivePath,
|
|
1223
|
+
sha256: sha256Hex(entry.linkTarget),
|
|
1224
|
+
size_bytes: 0,
|
|
1225
|
+
link_target: entry.linkTarget,
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
|
|
963
1229
|
const { manifest, manifestData } = buildManifestObject({
|
|
964
1230
|
contents: fileEntries,
|
|
965
1231
|
assistant,
|
|
@@ -980,6 +1246,7 @@ export async function streamExportVBundle(
|
|
|
980
1246
|
...allFileMetadata,
|
|
981
1247
|
...sanitizedConfigEntries,
|
|
982
1248
|
...inMemoryEntries,
|
|
1249
|
+
...symlinkEntries,
|
|
983
1250
|
];
|
|
984
1251
|
const tarGenerator = generateTarStream(manifestData, allEntries);
|
|
985
1252
|
const tarReadable = Readable.from(tarGenerator);
|
|
@@ -44,9 +44,9 @@ const LEGACY_USER_MD_ARCHIVE_PATH = "prompts/USER.md";
|
|
|
44
44
|
// Public types
|
|
45
45
|
// ---------------------------------------------------------------------------
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
type ImportAction = "create" | "overwrite" | "unchanged" | "skip";
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
interface ImportFileReport {
|
|
50
50
|
/** Archive path (e.g. "data/db/assistant.db") */
|
|
51
51
|
path: string;
|
|
52
52
|
/** What would happen to this file on import */
|
|
@@ -61,7 +61,7 @@ export interface ImportFileReport {
|
|
|
61
61
|
current_sha256: string | null;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
interface ImportConflict {
|
|
65
65
|
code: string;
|
|
66
66
|
message: string;
|
|
67
67
|
path?: string;
|
|
@@ -231,7 +231,7 @@ function sha256Hex(data: Uint8Array): string {
|
|
|
231
231
|
// Core analyzer
|
|
232
232
|
// ---------------------------------------------------------------------------
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
interface AnalyzeImportOptions {
|
|
235
235
|
/** The parsed and validated manifest from the bundle */
|
|
236
236
|
manifest: ManifestType;
|
|
237
237
|
/** Resolves archive paths to disk paths for comparison */
|