@vellumai/assistant 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +40 -40
- package/bunfig.toml +3 -0
- package/docker-entrypoint.sh +12 -2
- package/docs/architecture/memory.md +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +184 -69
- package/package.json +41 -41
- package/scripts/generate-openapi.ts +1 -2
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/browser-fill-credential.test.ts +229 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/catalog-files.test.ts +862 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/checker.test.ts +104 -170
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +125 -48
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/context-overflow-approval.test.ts +21 -6
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +1 -1
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-queue.test.ts +45 -2
- package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
- package/src/__tests__/conversation-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -4
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +2 -2
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/gemini-provider.test.ts +2 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +707 -371
- package/src/__tests__/headless-browser-navigate.test.ts +389 -47
- package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
- package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
- package/src/__tests__/host-bash-proxy.test.ts +150 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
- package/src/__tests__/host-cu-proxy.test.ts +171 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/log-export-workspace.test.ts +190 -0
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +61 -2
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +101 -1
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/oauth-apps-routes.test.ts +17 -12
- package/src/__tests__/oauth-cli.test.ts +707 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +50 -14
- package/src/__tests__/oauth-store.test.ts +1386 -182
- package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
- package/src/__tests__/onboarding-template-contract.test.ts +74 -55
- package/src/__tests__/openai-provider.test.ts +2 -2
- package/src/__tests__/outlook-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/require-fresh-approval.test.ts +40 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
- package/src/__tests__/slack-channel-config.test.ts +12 -15
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +72 -1
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +11 -5
- package/src/__tests__/test-preload.ts +14 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +62 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -35
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -0
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +55 -0
- package/src/cli/__tests__/run-assistant-command.ts +34 -7
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +18 -13
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +16 -2
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +200 -36
- package/src/cli/commands/oauth/shared.ts +5 -5
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -0
- package/src/cli/program.ts +10 -3
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- package/src/config/bundled-skills/contacts/SKILL.md +3 -0
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +12 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -0
- package/src/config/env-registry.ts +14 -0
- package/src/config/env.ts +21 -0
- package/src/config/feature-flag-registry.json +46 -7
- package/src/config/loader.ts +56 -1
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +46 -5
- package/src/config/schemas/host-browser.ts +66 -0
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +16 -0
- package/src/config/types.ts +0 -1
- package/src/context/post-turn-tool-result-truncation.ts +176 -0
- package/src/context/window-manager.ts +19 -1
- package/src/credential-execution/approval-bridge.ts +49 -16
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +6 -2
- package/src/daemon/context-overflow-approval.ts +5 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +74 -19
- package/src/daemon/conversation-attachments.ts +40 -1
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +66 -3
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +159 -20
- package/src/daemon/conversation-surfaces.ts +78 -12
- package/src/daemon/conversation-tool-setup.ts +74 -11
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +227 -11
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -139
- package/src/daemon/handlers/shared.ts +65 -0
- package/src/daemon/handlers/skills.ts +232 -37
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +191 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +86 -12
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +59 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -6
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/settings.ts +12 -0
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +112 -35
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +14 -0
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +38 -10
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +65 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +177 -18
- package/src/memory/graph/capability-seed.ts +3 -5
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/retriever.ts +40 -22
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/llm-usage-store.ts +45 -4
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +1 -0
- package/src/memory/schema/oauth.ts +18 -13
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +8 -8
- package/src/oauth/byo-connection.ts +7 -7
- package/src/oauth/connect-orchestrator.ts +23 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +16 -16
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +214 -100
- package/src/oauth/platform-connection.test.ts +5 -5
- package/src/oauth/platform-connection.ts +4 -4
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +127 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -96
- package/src/prompts/templates/SOUL.md +11 -11
- package/src/providers/anthropic/client.ts +1 -0
- package/src/providers/types.ts +1 -1
- package/src/runtime/AGENTS.md +23 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
- package/src/runtime/assistant-event-hub.ts +24 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +6 -7
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/chrome-extension-registry.ts +332 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +425 -70
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
- package/src/runtime/migrations/migration-transport.ts +6 -0
- package/src/runtime/migrations/migration-wizard.ts +22 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
- package/src/runtime/migrations/vbundle-importer.ts +55 -5
- package/src/runtime/pending-interactions.ts +29 -13
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/browser-cdp-routes.ts +229 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
- package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +308 -28
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/host-browser-routes.ts +279 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-routes.ts +259 -16
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +60 -25
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +87 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +9 -97
- package/src/runtime/routes/skills-routes.ts +52 -2
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/usage-routes.ts +8 -7
- package/src/runtime/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- package/src/schedule/scheduler.ts +7 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +42 -25
- package/src/security/secure-keys.ts +118 -25
- package/src/security/token-manager.ts +23 -10
- package/src/skills/catalog-files.ts +492 -0
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/subagent/manager.ts +131 -26
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +645 -340
- package/src/tools/browser/browser-manager.ts +36 -12
- package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
- package/src/tools/browser/cdp-client/errors.ts +34 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
- package/src/tools/browser/cdp-client/factory.ts +204 -0
- package/src/tools/browser/cdp-client/index.ts +14 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +52 -0
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/permission-checker.ts +77 -100
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -1
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +24 -21
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -3
- package/src/util/platform.ts +14 -19
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/029-seed-pkb.ts +1 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/top-level-renderer.ts +19 -1
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -194,18 +194,63 @@ export function markTaskPending(
|
|
|
194
194
|
|
|
195
195
|
/**
|
|
196
196
|
* Build the task list from definitions and completion state.
|
|
197
|
+
*
|
|
198
|
+
* When credential import results are provided:
|
|
199
|
+
* - If all credentials were imported successfully, the re-enter-secrets task
|
|
200
|
+
* is marked as auto-completed with an updated description.
|
|
201
|
+
* - If some credentials failed, the description is updated to list only the
|
|
202
|
+
* failed credentials that need manual re-entry.
|
|
203
|
+
* - If no credentials were in the bundle (legacy), the original behavior is kept.
|
|
197
204
|
*/
|
|
198
|
-
function buildTasks(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
function buildTasks(
|
|
206
|
+
completionState: RebindTaskCompletionState,
|
|
207
|
+
credentialsImported?: MigrationWizardState["credentialsImported"],
|
|
208
|
+
): RebindTask[] {
|
|
209
|
+
return TASK_DEFINITIONS.map((def) => {
|
|
210
|
+
// Apply credential import awareness to the re-enter-secrets task
|
|
211
|
+
if (def.id === "re-enter-secrets" && credentialsImported) {
|
|
212
|
+
if (credentialsImported.failed === 0 && credentialsImported.total > 0) {
|
|
213
|
+
// All credentials imported successfully — auto-complete this task
|
|
214
|
+
return {
|
|
215
|
+
id: def.id,
|
|
216
|
+
title: "API keys and secrets transferred",
|
|
217
|
+
description: `All ${credentialsImported.total} credential(s) were automatically imported from the bundle. No manual re-entry needed.`,
|
|
218
|
+
status: "complete" as const,
|
|
219
|
+
required: def.required,
|
|
220
|
+
helpText:
|
|
221
|
+
"Credentials were securely transferred as part of the migration bundle. You can verify them in Settings > Models & Services.",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (credentialsImported.failed > 0) {
|
|
226
|
+
// Partial failure — show only the failed credentials
|
|
227
|
+
const failedList = credentialsImported.failedAccounts
|
|
228
|
+
.map((a) => `"${a}"`)
|
|
229
|
+
.join(", ");
|
|
230
|
+
return {
|
|
231
|
+
id: def.id,
|
|
232
|
+
title: "Re-enter failed credentials",
|
|
233
|
+
description: `${credentialsImported.succeeded} of ${credentialsImported.total} credential(s) were imported automatically. The following failed and need manual re-entry: ${failedList}.`,
|
|
234
|
+
status: completionState[def.id]
|
|
235
|
+
? ("complete" as const)
|
|
236
|
+
: ("pending" as const),
|
|
237
|
+
required: def.required,
|
|
238
|
+
helpText: `Navigate to Settings > Models & Services to re-enter the failed credential(s): ${failedList}.`,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
id: def.id,
|
|
245
|
+
title: def.title,
|
|
246
|
+
description: def.description,
|
|
247
|
+
status: completionState[def.id]
|
|
248
|
+
? ("complete" as const)
|
|
249
|
+
: ("pending" as const),
|
|
250
|
+
required: def.required,
|
|
251
|
+
...(def.helpText !== undefined ? { helpText: def.helpText } : {}),
|
|
252
|
+
};
|
|
253
|
+
});
|
|
209
254
|
}
|
|
210
255
|
|
|
211
256
|
/**
|
|
@@ -235,6 +280,7 @@ export function deriveRebindSecretsScreenState(
|
|
|
235
280
|
completionState: RebindTaskCompletionState,
|
|
236
281
|
): RebindSecretsScreenState {
|
|
237
282
|
const rebindStep = wizardState.steps["rebind-secrets"];
|
|
283
|
+
const credInfo = wizardState.credentialsImported;
|
|
238
284
|
|
|
239
285
|
// Not yet accessible -- earlier steps incomplete
|
|
240
286
|
if (
|
|
@@ -246,15 +292,22 @@ export function deriveRebindSecretsScreenState(
|
|
|
246
292
|
}
|
|
247
293
|
}
|
|
248
294
|
|
|
295
|
+
// Apply credential-aware completion state: if all credentials imported
|
|
296
|
+
// successfully, treat re-enter-secrets as auto-completed.
|
|
297
|
+
let effectiveCompletion = completionState;
|
|
298
|
+
if (credInfo && credInfo.total > 0 && credInfo.failed === 0) {
|
|
299
|
+
effectiveCompletion = markTaskComplete(completionState, "re-enter-secrets");
|
|
300
|
+
}
|
|
301
|
+
|
|
249
302
|
// Already completed (viewing from a later step or after completion)
|
|
250
303
|
if (rebindStep.status === "success") {
|
|
251
|
-
const tasks = buildTasks(
|
|
304
|
+
const tasks = buildTasks(effectiveCompletion, credInfo);
|
|
252
305
|
return { phase: "complete", tasks };
|
|
253
306
|
}
|
|
254
307
|
|
|
255
308
|
// Active -- show the checklist
|
|
256
309
|
if (wizardState.currentStep === "rebind-secrets") {
|
|
257
|
-
const tasks = buildTasks(
|
|
310
|
+
const tasks = buildTasks(effectiveCompletion, credInfo);
|
|
258
311
|
const requiredTasks = tasks.filter((t) => t.required);
|
|
259
312
|
const completedTasks = tasks.filter((t) => t.status === "complete");
|
|
260
313
|
const requiredCompletedTasks = requiredTasks.filter(
|
|
@@ -264,7 +317,7 @@ export function deriveRebindSecretsScreenState(
|
|
|
264
317
|
return {
|
|
265
318
|
phase: "active",
|
|
266
319
|
tasks,
|
|
267
|
-
allRequiredComplete: areAllRequiredTasksComplete(
|
|
320
|
+
allRequiredComplete: areAllRequiredTasksComplete(effectiveCompletion),
|
|
268
321
|
completedCount: completedTasks.length,
|
|
269
322
|
totalCount: tasks.length,
|
|
270
323
|
requiredCount: requiredTasks.length,
|
|
@@ -289,7 +342,15 @@ export function completeMigration(
|
|
|
289
342
|
wizardState: MigrationWizardState,
|
|
290
343
|
completionState: RebindTaskCompletionState,
|
|
291
344
|
): MigrationWizardState {
|
|
292
|
-
if
|
|
345
|
+
// Apply credential-aware effective completion: if all credentials were
|
|
346
|
+
// imported successfully, treat re-enter-secrets as auto-completed
|
|
347
|
+
// (mirrors the logic in deriveRebindSecretsScreenState).
|
|
348
|
+
let effectiveCompletion = completionState;
|
|
349
|
+
const credInfo = wizardState?.credentialsImported;
|
|
350
|
+
if (credInfo && credInfo.total > 0 && credInfo.failed === 0) {
|
|
351
|
+
effectiveCompletion = { ...completionState, "re-enter-secrets": true };
|
|
352
|
+
}
|
|
353
|
+
if (!areAllRequiredTasksComplete(effectiveCompletion)) {
|
|
293
354
|
throw new Error(
|
|
294
355
|
"Cannot complete migration: not all required tasks are done",
|
|
295
356
|
);
|
|
@@ -28,6 +28,7 @@ import { Readable } from "node:stream";
|
|
|
28
28
|
import { pipeline } from "node:stream/promises";
|
|
29
29
|
import { createGzip, gzipSync } from "node:zlib";
|
|
30
30
|
|
|
31
|
+
import { sanitizeConfigForTransfer } from "../../config/sanitize-for-transfer.js";
|
|
31
32
|
import type {
|
|
32
33
|
ManifestFileEntryType,
|
|
33
34
|
ManifestType,
|
|
@@ -66,6 +67,20 @@ interface FileMetadata {
|
|
|
66
67
|
size: number;
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
/** In-memory entry for data not backed by a file on disk (e.g. credentials). */
|
|
71
|
+
interface InMemoryEntry {
|
|
72
|
+
archivePath: string;
|
|
73
|
+
data: Uint8Array;
|
|
74
|
+
size: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Union of disk-backed and in-memory tar stream entries. */
|
|
78
|
+
type TarStreamEntry = FileMetadata | InMemoryEntry;
|
|
79
|
+
|
|
80
|
+
function isInMemoryEntry(entry: TarStreamEntry): entry is InMemoryEntry {
|
|
81
|
+
return "data" in entry;
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
// ---------------------------------------------------------------------------
|
|
70
85
|
// Hash helpers
|
|
71
86
|
// ---------------------------------------------------------------------------
|
|
@@ -439,6 +454,8 @@ export interface BuildExportVBundleOptions {
|
|
|
439
454
|
* Called before the workspace walk so the DB file is up to date.
|
|
440
455
|
*/
|
|
441
456
|
checkpoint?: () => void;
|
|
457
|
+
/** Optional credential entries to include in the archive under credentials/ prefix. */
|
|
458
|
+
credentials?: Array<{ account: string; value: string }>;
|
|
442
459
|
}
|
|
443
460
|
|
|
444
461
|
/**
|
|
@@ -456,8 +473,15 @@ export interface BuildExportVBundleOptions {
|
|
|
456
473
|
export function buildExportVBundle(
|
|
457
474
|
options: BuildExportVBundleOptions,
|
|
458
475
|
): BuildVBundleResult {
|
|
459
|
-
const {
|
|
460
|
-
|
|
476
|
+
const {
|
|
477
|
+
source,
|
|
478
|
+
description,
|
|
479
|
+
checkpoint,
|
|
480
|
+
trustPath,
|
|
481
|
+
workspaceDir,
|
|
482
|
+
hooksDir,
|
|
483
|
+
credentials,
|
|
484
|
+
} = options;
|
|
461
485
|
|
|
462
486
|
// Flush WAL to the main database file before reading so the export
|
|
463
487
|
// captures all committed rows (SQLite WAL mode keeps recent writes
|
|
@@ -483,6 +507,14 @@ export function buildExportVBundle(
|
|
|
483
507
|
);
|
|
484
508
|
}
|
|
485
509
|
|
|
510
|
+
// Sanitize workspace/config.json to strip environment-specific fields
|
|
511
|
+
const configEntry = files.find((f) => f.path === "workspace/config.json");
|
|
512
|
+
if (configEntry) {
|
|
513
|
+
const configJson = new TextDecoder().decode(configEntry.data);
|
|
514
|
+
const sanitized = sanitizeConfigForTransfer(configJson);
|
|
515
|
+
configEntry.data = new TextEncoder().encode(sanitized);
|
|
516
|
+
}
|
|
517
|
+
|
|
486
518
|
// Include hooks directory if it exists (lives at ~/.vellum/hooks/, outside workspace).
|
|
487
519
|
if (hooksDir && existsSync(hooksDir) && lstatSync(hooksDir).isDirectory()) {
|
|
488
520
|
files.push(...walkDirectory(hooksDir, "hooks"));
|
|
@@ -494,6 +526,14 @@ export function buildExportVBundle(
|
|
|
494
526
|
files.push({ path: "trust/trust.json", data: trustData });
|
|
495
527
|
}
|
|
496
528
|
|
|
529
|
+
// Include credential entries if provided
|
|
530
|
+
if (credentials?.length) {
|
|
531
|
+
for (const { account, value } of credentials) {
|
|
532
|
+
const data = new TextEncoder().encode(value);
|
|
533
|
+
files.push({ path: `credentials/${account}`, data });
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
497
537
|
return buildVBundle({
|
|
498
538
|
files,
|
|
499
539
|
source: source ?? "runtime-export",
|
|
@@ -688,7 +728,7 @@ function tarPaddingBytes(dataSize: number): Uint8Array {
|
|
|
688
728
|
*/
|
|
689
729
|
async function* generateTarStream(
|
|
690
730
|
manifestJson: Uint8Array,
|
|
691
|
-
files:
|
|
731
|
+
files: TarStreamEntry[],
|
|
692
732
|
): AsyncGenerator<Uint8Array> {
|
|
693
733
|
// Manifest entry
|
|
694
734
|
yield createPaxAndHeaderBlocks("manifest.json", manifestJson.length);
|
|
@@ -697,43 +737,52 @@ async function* generateTarStream(
|
|
|
697
737
|
|
|
698
738
|
// File entries
|
|
699
739
|
for (const file of files) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
740
|
+
const entrySize = isInMemoryEntry(file) ? file.size : file.size;
|
|
741
|
+
yield createPaxAndHeaderBlocks(file.archivePath, entrySize);
|
|
742
|
+
|
|
743
|
+
if (isInMemoryEntry(file)) {
|
|
744
|
+
// In-memory entry — yield data directly
|
|
745
|
+
if (file.size > 0) {
|
|
746
|
+
yield file.data;
|
|
747
|
+
}
|
|
748
|
+
} else {
|
|
749
|
+
// Disk-backed entry — stream from disk
|
|
750
|
+
// Stream exactly file.size bytes from disk. Capping the read at the
|
|
751
|
+
// declared size keeps the tar structure valid even if the file grows
|
|
752
|
+
// between passes (common for log files on active assistants). If the
|
|
753
|
+
// file shrinks below the declared size, zero-pad to maintain block
|
|
754
|
+
// alignment. The WAL checkpoint before export is the primary
|
|
755
|
+
// consistency mechanism for the database.
|
|
756
|
+
let bytesWritten = 0;
|
|
757
|
+
if (file.size > 0) {
|
|
758
|
+
try {
|
|
759
|
+
const stream = createReadStream(file.diskPath, {
|
|
760
|
+
start: 0,
|
|
761
|
+
end: file.size - 1,
|
|
762
|
+
});
|
|
763
|
+
for await (const chunk of stream) {
|
|
764
|
+
const data =
|
|
765
|
+
chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
|
|
766
|
+
bytesWritten += data.length;
|
|
767
|
+
yield data;
|
|
768
|
+
}
|
|
769
|
+
} catch {
|
|
770
|
+
// File was deleted or rotated between passes — emit zeros for
|
|
771
|
+
// the full declared size so the tar structure stays valid
|
|
720
772
|
}
|
|
721
|
-
} catch {
|
|
722
|
-
// File was deleted or rotated between passes — emit zeros for
|
|
723
|
-
// the full declared size so the tar structure stays valid
|
|
724
773
|
}
|
|
725
|
-
}
|
|
726
774
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
775
|
+
// If the file shrank, pad with zeros in bounded chunks to reach
|
|
776
|
+
// the declared size without a large single allocation
|
|
777
|
+
let remaining = file.size - bytesWritten;
|
|
778
|
+
while (remaining > 0) {
|
|
779
|
+
const chunkSize = Math.min(remaining, 65536);
|
|
780
|
+
yield new Uint8Array(chunkSize);
|
|
781
|
+
remaining -= chunkSize;
|
|
782
|
+
}
|
|
734
783
|
}
|
|
735
784
|
|
|
736
|
-
yield tarPaddingBytes(
|
|
785
|
+
yield tarPaddingBytes(entrySize);
|
|
737
786
|
}
|
|
738
787
|
|
|
739
788
|
// End-of-archive: two zero blocks
|
|
@@ -765,8 +814,15 @@ export interface StreamExportVBundleResult {
|
|
|
765
814
|
export async function streamExportVBundle(
|
|
766
815
|
options: BuildExportVBundleOptions,
|
|
767
816
|
): Promise<StreamExportVBundleResult> {
|
|
768
|
-
const {
|
|
769
|
-
|
|
817
|
+
const {
|
|
818
|
+
source,
|
|
819
|
+
description,
|
|
820
|
+
checkpoint,
|
|
821
|
+
trustPath,
|
|
822
|
+
workspaceDir,
|
|
823
|
+
hooksDir,
|
|
824
|
+
credentials,
|
|
825
|
+
} = options;
|
|
770
826
|
|
|
771
827
|
// Flush WAL to the main database file before reading
|
|
772
828
|
if (checkpoint) {
|
|
@@ -806,6 +862,42 @@ export async function streamExportVBundle(
|
|
|
806
862
|
}
|
|
807
863
|
}
|
|
808
864
|
|
|
865
|
+
// Sanitize workspace/config.json: read from disk, sanitize, and replace the
|
|
866
|
+
// disk-backed metadata entry with an in-memory entry so the streaming tar
|
|
867
|
+
// writes sanitized content instead of the raw file.
|
|
868
|
+
const configMetadataIdx = allFileMetadata.findIndex(
|
|
869
|
+
(f) => f.archivePath === "workspace/config.json",
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
const sanitizedConfigEntries: InMemoryEntry[] = [];
|
|
873
|
+
if (configMetadataIdx !== -1) {
|
|
874
|
+
const configMeta = allFileMetadata[configMetadataIdx];
|
|
875
|
+
const rawConfigData = readFileSync(configMeta.diskPath, "utf8");
|
|
876
|
+
const sanitized = sanitizeConfigForTransfer(rawConfigData);
|
|
877
|
+
const sanitizedData = new TextEncoder().encode(sanitized);
|
|
878
|
+
|
|
879
|
+
// Remove the disk-backed entry and replace with an in-memory entry
|
|
880
|
+
allFileMetadata.splice(configMetadataIdx, 1);
|
|
881
|
+
sanitizedConfigEntries.push({
|
|
882
|
+
archivePath: "workspace/config.json",
|
|
883
|
+
data: sanitizedData,
|
|
884
|
+
size: sanitizedData.length,
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Build in-memory entries for credentials (not disk-backed)
|
|
889
|
+
const inMemoryEntries: InMemoryEntry[] = [];
|
|
890
|
+
if (credentials?.length) {
|
|
891
|
+
for (const { account, value } of credentials) {
|
|
892
|
+
const data = new TextEncoder().encode(value);
|
|
893
|
+
inMemoryEntries.push({
|
|
894
|
+
archivePath: `credentials/${account}`,
|
|
895
|
+
data,
|
|
896
|
+
size: data.length,
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
809
901
|
// ------------------------------------------------------------------
|
|
810
902
|
// Pass 1: Compute SHA-256 checksums to build the manifest
|
|
811
903
|
// ------------------------------------------------------------------
|
|
@@ -820,6 +912,16 @@ export async function streamExportVBundle(
|
|
|
820
912
|
});
|
|
821
913
|
}
|
|
822
914
|
|
|
915
|
+
// Add in-memory entries (sanitized config, credentials) to the manifest
|
|
916
|
+
for (const entry of [...sanitizedConfigEntries, ...inMemoryEntries]) {
|
|
917
|
+
const sha256 = sha256Hex(entry.data);
|
|
918
|
+
fileEntries.push({
|
|
919
|
+
path: entry.archivePath,
|
|
920
|
+
sha256,
|
|
921
|
+
size: entry.size,
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
|
|
823
925
|
const manifestWithoutChecksum = {
|
|
824
926
|
schema_version: "1.0",
|
|
825
927
|
created_at: new Date().toISOString(),
|
|
@@ -842,7 +944,12 @@ export async function streamExportVBundle(
|
|
|
842
944
|
|
|
843
945
|
const tempPath = join(tmpdir(), `vbundle-export-${randomUUID()}.tmp`);
|
|
844
946
|
|
|
845
|
-
const
|
|
947
|
+
const allEntries: TarStreamEntry[] = [
|
|
948
|
+
...allFileMetadata,
|
|
949
|
+
...sanitizedConfigEntries,
|
|
950
|
+
...inMemoryEntries,
|
|
951
|
+
];
|
|
952
|
+
const tarGenerator = generateTarStream(manifestData, allEntries);
|
|
846
953
|
const tarReadable = Readable.from(tarGenerator);
|
|
847
954
|
const gzipStream = createGzip();
|
|
848
955
|
const writeStream = createWriteStream(tempPath, { mode: 0o600 });
|
|
@@ -93,6 +93,11 @@ export class DefaultPathResolver implements PathResolver {
|
|
|
93
93
|
) {}
|
|
94
94
|
|
|
95
95
|
resolve(archivePath: string): string | null {
|
|
96
|
+
// Skip credential entries — handled separately by the credential import step
|
|
97
|
+
if (archivePath.startsWith("credentials/")) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
96
101
|
// New format: workspace/ prefix — maps directly into the workspace dir
|
|
97
102
|
if (archivePath.startsWith("workspace/") && this.workspaceDir) {
|
|
98
103
|
const relPath = archivePath.slice("workspace/".length);
|
|
@@ -190,6 +195,20 @@ export function analyzeImport(
|
|
|
190
195
|
for (const fileEntry of manifest.files) {
|
|
191
196
|
const diskPath = pathResolver.resolve(fileEntry.path);
|
|
192
197
|
|
|
198
|
+
// Credential entries are handled separately by the credential import
|
|
199
|
+
// step — skip them without flagging as unknown/conflict.
|
|
200
|
+
if (fileEntry.path.startsWith("credentials/")) {
|
|
201
|
+
files.push({
|
|
202
|
+
path: fileEntry.path,
|
|
203
|
+
action: "skip",
|
|
204
|
+
bundle_size: fileEntry.size,
|
|
205
|
+
bundle_sha256: fileEntry.sha256,
|
|
206
|
+
current_size: null,
|
|
207
|
+
current_sha256: null,
|
|
208
|
+
});
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
193
212
|
if (!diskPath) {
|
|
194
213
|
// Unknown archive path — would have nowhere to write
|
|
195
214
|
conflicts.push({
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "node:fs";
|
|
25
25
|
import { dirname, join } from "node:path";
|
|
26
26
|
|
|
27
|
+
import { sanitizeConfigForTransfer } from "../../config/sanitize-for-transfer.js";
|
|
27
28
|
import type { PathResolver } from "./vbundle-import-analyzer.js";
|
|
28
29
|
import type { ManifestType, VBundleTarEntry } from "./vbundle-validator.js";
|
|
29
30
|
import { validateVBundle } from "./vbundle-validator.js";
|
|
@@ -227,6 +228,12 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
227
228
|
let backupsCreated = 0;
|
|
228
229
|
|
|
229
230
|
for (const fileEntry of manifest.files) {
|
|
231
|
+
// Credential entries are handled separately by extractCredentialsFromBundle()
|
|
232
|
+
// in migration-routes.ts — skip them silently without warnings or skip counts.
|
|
233
|
+
if (fileEntry.path.startsWith("credentials/")) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
|
|
230
237
|
const diskPath = pathResolver.resolve(fileEntry.path);
|
|
231
238
|
|
|
232
239
|
if (!diskPath) {
|
|
@@ -315,9 +322,20 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
315
322
|
}
|
|
316
323
|
}
|
|
317
324
|
|
|
325
|
+
// Sanitize config files to strip environment-specific fields (defense-in-depth)
|
|
326
|
+
let dataToWrite: Uint8Array = archiveEntry.data;
|
|
327
|
+
if (
|
|
328
|
+
fileEntry.path === "workspace/config.json" ||
|
|
329
|
+
fileEntry.path === "config/settings.json"
|
|
330
|
+
) {
|
|
331
|
+
const configJson = new TextDecoder().decode(archiveEntry.data);
|
|
332
|
+
const sanitized = sanitizeConfigForTransfer(configJson);
|
|
333
|
+
dataToWrite = new TextEncoder().encode(sanitized);
|
|
334
|
+
}
|
|
335
|
+
|
|
318
336
|
// Write the file
|
|
319
337
|
try {
|
|
320
|
-
writeFileSync(diskPath,
|
|
338
|
+
writeFileSync(diskPath, dataToWrite);
|
|
321
339
|
} catch (err) {
|
|
322
340
|
return {
|
|
323
341
|
ok: false,
|
|
@@ -335,14 +353,17 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
335
353
|
}
|
|
336
354
|
|
|
337
355
|
// Step 3: Post-write integrity check — verify the written file
|
|
356
|
+
// Use the SHA of the data we actually wrote (which may differ from the
|
|
357
|
+
// manifest SHA if the config was sanitized during import).
|
|
358
|
+
const expectedSha256 = sha256Hex(dataToWrite);
|
|
338
359
|
try {
|
|
339
360
|
const writtenData = new Uint8Array(readFileSync(diskPath));
|
|
340
361
|
const writtenSha256 = sha256Hex(writtenData);
|
|
341
362
|
|
|
342
|
-
if (writtenSha256 !==
|
|
363
|
+
if (writtenSha256 !== expectedSha256) {
|
|
343
364
|
warnings.push(
|
|
344
365
|
`Post-write integrity warning for "${fileEntry.path}": ` +
|
|
345
|
-
`expected SHA-256 ${
|
|
366
|
+
`expected SHA-256 ${expectedSha256}, got ${writtenSha256}`,
|
|
346
367
|
);
|
|
347
368
|
}
|
|
348
369
|
} catch {
|
|
@@ -355,8 +376,8 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
355
376
|
path: fileEntry.path,
|
|
356
377
|
disk_path: diskPath,
|
|
357
378
|
action,
|
|
358
|
-
size:
|
|
359
|
-
sha256:
|
|
379
|
+
size: dataToWrite.length,
|
|
380
|
+
sha256: expectedSha256,
|
|
360
381
|
backup_path: backupPath,
|
|
361
382
|
});
|
|
362
383
|
}
|
|
@@ -380,6 +401,35 @@ export function commitImport(options: ImportCommitOptions): ImportCommitResult {
|
|
|
380
401
|
return { ok: true, report };
|
|
381
402
|
}
|
|
382
403
|
|
|
404
|
+
// ---------------------------------------------------------------------------
|
|
405
|
+
// Credential extraction
|
|
406
|
+
// ---------------------------------------------------------------------------
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Extract credential entries from a validated vbundle tar entries map.
|
|
410
|
+
*
|
|
411
|
+
* Credentials are stored under the `credentials/` prefix in the archive,
|
|
412
|
+
* where the remainder of the path is the account name and the entry data
|
|
413
|
+
* is the credential value.
|
|
414
|
+
*/
|
|
415
|
+
export function extractCredentialsFromBundle(
|
|
416
|
+
entries: Map<string, VBundleTarEntry>,
|
|
417
|
+
manifest: ManifestType,
|
|
418
|
+
): Array<{ account: string; value: string }> {
|
|
419
|
+
const manifestPaths = new Set(manifest.files.map((f) => f.path));
|
|
420
|
+
const credentials: Array<{ account: string; value: string }> = [];
|
|
421
|
+
for (const [path, entry] of entries) {
|
|
422
|
+
if (path.startsWith("credentials/") && manifestPaths.has(path)) {
|
|
423
|
+
const account = path.slice("credentials/".length);
|
|
424
|
+
if (account) {
|
|
425
|
+
const value = new TextDecoder().decode(entry.data);
|
|
426
|
+
credentials.push({ account, value });
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return credentials;
|
|
431
|
+
}
|
|
432
|
+
|
|
383
433
|
// ---------------------------------------------------------------------------
|
|
384
434
|
// Helpers
|
|
385
435
|
// ---------------------------------------------------------------------------
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* In-memory tracker that maps requestId to conversation info for pending
|
|
3
|
-
* confirmation, secret, host_bash, host_file, and
|
|
3
|
+
* confirmation, secret, host_bash, host_file, host_cu, and host_browser
|
|
4
|
+
* interactions.
|
|
4
5
|
*
|
|
5
6
|
* When the agent loop emits a confirmation_request, secret_request,
|
|
6
|
-
* host_bash_request, host_file_request,
|
|
7
|
-
* callback registers the interaction here.
|
|
8
|
-
* (/v1/confirm, /v1/secret, /v1/trust-rules,
|
|
9
|
-
* /v1/host-file-result, /v1/host-cu-result
|
|
10
|
-
*
|
|
7
|
+
* host_bash_request, host_file_request, host_cu_request, or
|
|
8
|
+
* host_browser_request, the onEvent callback registers the interaction here.
|
|
9
|
+
* Standalone HTTP endpoints (/v1/confirm, /v1/secret, /v1/trust-rules,
|
|
10
|
+
* /v1/host-bash-result, /v1/host-file-result, /v1/host-cu-result,
|
|
11
|
+
* /v1/host-browser-result) look up the conversation from this tracker to
|
|
12
|
+
* resolve the interaction.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
import type { Conversation } from "../daemon/conversation.js";
|
|
@@ -45,10 +47,22 @@ export interface PendingInteraction {
|
|
|
45
47
|
| "host_bash"
|
|
46
48
|
| "host_file"
|
|
47
49
|
| "host_cu"
|
|
50
|
+
| "host_browser"
|
|
48
51
|
| "acp_confirmation";
|
|
49
52
|
confirmationDetails?: ConfirmationDetails;
|
|
50
53
|
/** For ACP permissions: resolves directly without a Conversation object. */
|
|
51
54
|
directResolve?: (decision: UserDecision) => void;
|
|
55
|
+
/**
|
|
56
|
+
* For host_browser interactions originating outside an agent loop
|
|
57
|
+
* (e.g. the `assistant browser chrome relay` CLI shim that POSTs to
|
|
58
|
+
* /v1/browser-cdp). Resolves the CDP round-trip directly without
|
|
59
|
+
* touching a Conversation. When set, /v1/host-browser-result invokes
|
|
60
|
+
* this instead of `interaction.conversation.resolveHostBrowser`.
|
|
61
|
+
*/
|
|
62
|
+
directBrowserResolve?: (response: {
|
|
63
|
+
content: string;
|
|
64
|
+
isError: boolean;
|
|
65
|
+
}) => void;
|
|
52
66
|
}
|
|
53
67
|
|
|
54
68
|
const pending = new Map<string, PendingInteraction>();
|
|
@@ -82,7 +96,7 @@ export function get(requestId: string): PendingInteraction | undefined {
|
|
|
82
96
|
|
|
83
97
|
/**
|
|
84
98
|
* Return all pending interactions for a given conversation.
|
|
85
|
-
* Needed by channel approval migration
|
|
99
|
+
* Needed by channel approval migration.
|
|
86
100
|
*/
|
|
87
101
|
export function getByConversation(
|
|
88
102
|
conversationId: string,
|
|
@@ -100,12 +114,13 @@ export function getByConversation(
|
|
|
100
114
|
* Remove pending confirmation and secret interactions for a given conversation.
|
|
101
115
|
* Used when auto-denying all pending interactions (e.g. new user message).
|
|
102
116
|
*
|
|
103
|
-
* host_bash, host_file, and
|
|
104
|
-
* — they represent in-flight tool executions proxied to
|
|
105
|
-
* confirmations to auto-deny. Removing them would orphan the
|
|
106
|
-
* client would POST to /v1/host-bash-result,
|
|
107
|
-
* /v1/host-cu-result
|
|
108
|
-
* proxy timer would fire with a
|
|
117
|
+
* host_bash, host_file, host_cu, and host_browser interactions are
|
|
118
|
+
* intentionally skipped — they represent in-flight tool executions proxied to
|
|
119
|
+
* the client, not confirmations to auto-deny. Removing them would orphan the
|
|
120
|
+
* request: the client would POST to /v1/host-bash-result,
|
|
121
|
+
* /v1/host-file-result, /v1/host-cu-result, or /v1/host-browser-result after
|
|
122
|
+
* completing the operation, get a 404, and the proxy timer would fire with a
|
|
123
|
+
* spurious timeout error.
|
|
109
124
|
*/
|
|
110
125
|
export function removeByConversation(conversation: Conversation): void {
|
|
111
126
|
for (const [requestId, interaction] of pending) {
|
|
@@ -114,6 +129,7 @@ export function removeByConversation(conversation: Conversation): void {
|
|
|
114
129
|
interaction.kind !== "host_bash" &&
|
|
115
130
|
interaction.kind !== "host_file" &&
|
|
116
131
|
interaction.kind !== "host_cu" &&
|
|
132
|
+
interaction.kind !== "host_browser" &&
|
|
117
133
|
interaction.kind !== "acp_confirmation"
|
|
118
134
|
) {
|
|
119
135
|
pending.delete(requestId);
|