@vellumai/assistant 0.4.56 → 0.4.57
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 +10 -10
- package/Dockerfile +3 -0
- package/README.md +11 -11
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/memory.md +3 -4
- package/docs/credential-execution-service.md +13 -20
- package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +7 -7
- package/src/__tests__/anthropic-provider.test.ts +172 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
- package/src/__tests__/approval-cascade.test.ts +2 -2
- package/src/__tests__/approval-routes-http.test.ts +3 -4
- package/src/__tests__/asset-materialize-tool.test.ts +5 -5
- package/src/__tests__/asset-search-tool.test.ts +1 -1
- package/src/__tests__/assistant-attachments.test.ts +5 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
- package/src/__tests__/attachments-store.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +5 -3
- package/src/__tests__/browser-skill-endstate.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +2 -2
- package/src/__tests__/callback-handoff-copy.test.ts +1 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +31 -32
- package/src/__tests__/chrome-cdp.test.ts +47 -18
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +9 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop.test.ts +11 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +33 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
- package/src/__tests__/conversation-queue.test.ts +23 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +185 -173
- package/src/__tests__/conversation-seed-composer.test.ts +1 -1
- package/src/__tests__/conversation-slash-queue.test.ts +4 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
- package/src/__tests__/conversation-starter-routes.test.ts +291 -0
- package/src/__tests__/conversation-wipe.test.ts +438 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
- package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
- package/src/__tests__/credential-security-e2e.test.ts +20 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +227 -0
- package/src/__tests__/credentials-cli.test.ts +3 -0
- package/src/__tests__/date-context.test.ts +59 -377
- package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
- package/src/__tests__/encrypted-store.test.ts +237 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +4 -4
- package/src/__tests__/gemini-provider.test.ts +6 -9
- package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/intent-routing.test.ts +51 -99
- package/src/__tests__/invite-routes-http.test.ts +5 -0
- package/src/__tests__/list-messages-attachments.test.ts +1 -1
- package/src/__tests__/managed-proxy-context.test.ts +2 -5
- package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
- package/src/__tests__/media-generate-image.test.ts +32 -15
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
- package/src/__tests__/memory-recall-quality.test.ts +4 -3
- package/src/__tests__/memory-regressions.test.ts +86 -90
- package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
- package/src/__tests__/migration-export-http.test.ts +26 -27
- package/src/__tests__/migration-import-commit-http.test.ts +165 -37
- package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
- package/src/__tests__/migration-validate-http.test.ts +16 -16
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/notification-broadcaster.test.ts +1 -1
- package/src/__tests__/notification-decision-fallback.test.ts +2 -2
- package/src/__tests__/notification-decision-identity.test.ts +8 -9
- package/src/__tests__/notification-decision-strategy.test.ts +1 -1
- package/src/__tests__/notification-deep-link.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
- package/src/__tests__/onboarding-template-contract.test.ts +23 -59
- package/src/__tests__/provider-error-scenarios.test.ts +154 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
- package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
- package/src/__tests__/provider-registry-ollama.test.ts +5 -2
- package/src/__tests__/qdrant-manager.test.ts +7 -7
- package/src/__tests__/ratelimit.test.ts +0 -74
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/scheduler-recurrence.test.ts +46 -2
- package/src/__tests__/schema-transforms.test.ts +114 -54
- package/src/__tests__/secret-onetime-send.test.ts +20 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
- package/src/__tests__/secret-scanner-executor.test.ts +1 -2
- package/src/__tests__/send-endpoint-busy.test.ts +63 -4
- package/src/__tests__/send-notification-tool.test.ts +2 -2
- package/src/__tests__/shell-credential-ref.test.ts +0 -1
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
- package/src/__tests__/skill-memory.test.ts +547 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
- package/src/__tests__/slack-channel-config.test.ts +109 -94
- package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
- package/src/__tests__/swarm-recursion.test.ts +2 -2
- package/src/__tests__/swarm-tool.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +19 -66
- package/src/__tests__/telegram-config.test.ts +121 -0
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trace-emitter.test.ts +8 -1
- package/src/__tests__/trust-store.test.ts +7 -8
- package/src/__tests__/twilio-routes.test.ts +1 -18
- package/src/__tests__/user-reference.test.ts +82 -2
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +3 -3
- package/src/avatar/ascii-renderer.ts +2 -2
- package/src/avatar/png-renderer.ts +2 -2
- package/src/avatar/resvg-lazy.ts +21 -0
- package/src/calls/guardian-dispatch.ts +1 -1
- package/src/calls/relay-access-wait.ts +2 -2
- package/src/calls/twilio-rest.ts +0 -248
- package/src/cli/AGENTS.md +5 -8
- package/src/cli/__tests__/notifications.test.ts +5 -5
- package/src/cli/commands/avatar.ts +64 -2
- package/src/cli/commands/conversations.ts +131 -1
- package/src/cli/commands/credentials.ts +2 -0
- package/src/cli/commands/notifications.ts +3 -3
- package/src/cli.ts +10 -0
- package/src/config/bundled-skills/acp/SKILL.md +5 -5
- package/src/config/bundled-skills/acp/TOOLS.json +6 -6
- package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
- package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
- package/src/config/bundled-skills/browser/SKILL.md +15 -15
- package/src/config/bundled-skills/browser/TOOLS.json +14 -14
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
- package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
- package/src/config/bundled-skills/contacts/SKILL.md +3 -3
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
- package/src/config/bundled-skills/document/SKILL.md +4 -4
- package/src/config/bundled-skills/document/TOOLS.json +2 -2
- package/src/config/bundled-skills/followups/TOOLS.json +3 -3
- package/src/config/bundled-skills/gmail/SKILL.md +32 -32
- package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
- package/src/config/bundled-skills/google-calendar/types.ts +1 -1
- package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
- package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
- package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
- package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +29 -25
- package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +3 -3
- package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
- package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
- package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
- package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
- package/src/config/bundled-skills/schedule/SKILL.md +26 -26
- package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
- package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
- package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +2 -2
- package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
- package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
- package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
- package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
- package/src/config/bundled-skills/slack/SKILL.md +2 -2
- package/src/config/bundled-skills/slack/TOOLS.json +8 -8
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
- package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
- package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
- package/src/config/bundled-skills/watcher/SKILL.md +4 -4
- package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
- package/src/config/feature-flag-registry.json +33 -17
- package/src/config/schemas/sandbox.ts +1 -1
- package/src/config/schemas/services.ts +13 -3
- package/src/config/schemas/timeouts.ts +0 -10
- package/src/contacts/contact-store.ts +63 -0
- package/src/contacts/contacts-write.ts +1 -1
- package/src/daemon/assistant-attachments.ts +2 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
- package/src/daemon/conversation-agent-loop.ts +7 -30
- package/src/daemon/conversation-error.ts +24 -0
- package/src/daemon/conversation-memory.ts +8 -7
- package/src/daemon/conversation-runtime-assembly.ts +139 -274
- package/src/daemon/conversation-slash.ts +7 -26
- package/src/daemon/conversation-surfaces.ts +14 -0
- package/src/daemon/conversation-tool-setup.ts +9 -8
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +10 -83
- package/src/daemon/handlers/config-channels.ts +12 -2
- package/src/daemon/handlers/config-slack-channel.ts +7 -1
- package/src/daemon/handlers/config-telegram.ts +6 -1
- package/src/daemon/handlers/conversations.ts +2 -2
- package/src/daemon/handlers/skills.ts +4 -0
- package/src/daemon/lifecycle.ts +28 -4
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +1 -5
- package/src/daemon/shutdown-handlers.ts +9 -3
- package/src/daemon/tool-side-effects.ts +40 -0
- package/src/daemon/trace-emitter.ts +25 -2
- package/src/events/domain-events.ts +1 -1
- package/src/events/tool-permission-telemetry-listener.ts +46 -0
- package/src/inbound/platform-callback-registration.ts +0 -18
- package/src/media/app-icon-generator.ts +15 -8
- package/src/media/avatar-router.ts +15 -8
- package/src/media/gemini-image-service.ts +125 -21
- package/src/memory/attachments-store.ts +3 -3
- package/src/memory/channel-verification-sessions.ts +6 -6
- package/src/memory/conversation-crud.ts +196 -1
- package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
- package/src/memory/conversation-title-service.ts +2 -3
- package/src/memory/db-init.ts +25 -1
- package/src/memory/invite-store.ts +4 -4
- package/src/memory/items-extractor.ts +4 -4
- package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/jobs-worker.ts +7 -5
- package/src/memory/lifecycle-events-store.ts +63 -0
- package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
- package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
- package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
- package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
- package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
- package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +223 -96
- package/src/memory/retriever.ts +115 -138
- package/src/memory/schema/calls.ts +1 -1
- package/src/memory/schema/contacts.ts +1 -1
- package/src/memory/schema/infrastructure.ts +29 -0
- package/src/memory/schema/memory-core.ts +7 -17
- package/src/memory/schema/notifications.ts +1 -1
- package/src/memory/search/formatting.ts +23 -6
- package/src/memory/search/lexical.ts +2 -0
- package/src/memory/search/semantic.ts +2 -0
- package/src/memory/search/staleness.ts +1 -0
- package/src/memory/search/types.ts +4 -0
- package/src/memory/task-memory-cleanup.ts +96 -6
- package/src/memory/trace-event-store.ts +148 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/emit-signal.ts +4 -4
- package/src/notifications/events-store.ts +4 -4
- package/src/notifications/signal.ts +1 -1
- package/src/oauth/manual-token-connection.ts +49 -25
- package/src/permissions/checker.ts +6 -5
- package/src/permissions/defaults.ts +4 -4
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
- package/src/prompts/cache-boundary.ts +8 -0
- package/src/prompts/system-prompt.ts +105 -634
- package/src/prompts/templates/BOOTSTRAP.md +166 -33
- package/src/prompts/templates/IDENTITY.md +8 -23
- package/src/prompts/templates/SOUL.md +20 -41
- package/src/prompts/templates/USER.md +3 -19
- package/src/prompts/user-reference.ts +14 -16
- package/src/providers/anthropic/client.ts +46 -2
- package/src/providers/gemini/client.ts +6 -9
- package/src/providers/managed-proxy/constants.ts +1 -7
- package/src/providers/managed-proxy/context.ts +0 -1
- package/src/providers/model-intents.ts +5 -5
- package/src/providers/openai/client.ts +10 -1
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/ratelimit.ts +0 -35
- package/src/providers/registry.ts +3 -5
- package/src/providers/retry.ts +18 -1
- package/src/runtime/access-request-helper.ts +1 -1
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/channel-verification-service.ts +1 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/guardian-vellum-migration.ts +63 -1
- package/src/runtime/http-server.ts +8 -4
- package/src/runtime/migrations/vbundle-builder.ts +212 -32
- package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
- package/src/runtime/migrations/vbundle-importer.ts +66 -1
- package/src/runtime/migrations/vbundle-validator.ts +17 -3
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
- package/src/runtime/routes/attachment-routes.ts +2 -2
- package/src/runtime/routes/btw-routes.ts +9 -0
- package/src/runtime/routes/channel-verification-routes.ts +19 -2
- package/src/runtime/routes/conversation-management-routes.ts +55 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +49 -5
- package/src/runtime/routes/conversation-starter-routes.ts +207 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +25 -13
- package/src/runtime/routes/secret-routes.ts +18 -0
- package/src/runtime/routes/settings-routes.ts +8 -8
- package/src/runtime/routes/telemetry-routes.ts +53 -0
- package/src/runtime/routes/trace-event-routes.ts +62 -0
- package/src/runtime/tool-grant-request-helper.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +47 -31
- package/src/security/encrypted-store.ts +263 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +220 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
- package/src/telemetry/usage-telemetry-reporter.ts +51 -4
- package/src/tools/AGENTS.md +11 -11
- package/src/tools/acp/spawn.ts +1 -1
- package/src/tools/apps/executors.ts +8 -8
- package/src/tools/apps/registry.ts +1 -1
- package/src/tools/assets/materialize.ts +6 -6
- package/src/tools/assets/search.ts +10 -10
- package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
- package/src/tools/browser/auth-detector.ts +6 -6
- package/src/tools/browser/browser-execution.ts +13 -13
- package/src/tools/browser/browser-manager.ts +3 -3
- package/src/tools/browser/chrome-cdp.ts +5 -5
- package/src/tools/browser/jit-auth.ts +2 -2
- package/src/tools/browser/network-recorder.test.ts +2 -2
- package/src/tools/browser/network-recorder.ts +3 -3
- package/src/tools/browser/runtime-check.ts +3 -3
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/computer-use/definitions.ts +18 -18
- package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
- package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
- package/src/tools/credentials/broker-types.ts +5 -5
- package/src/tools/credentials/broker.ts +15 -15
- package/src/tools/credentials/metadata-store.ts +2 -2
- package/src/tools/credentials/resolve.ts +1 -1
- package/src/tools/credentials/selection.ts +1 -1
- package/src/tools/credentials/tool-policy.ts +1 -1
- package/src/tools/credentials/vault.ts +115 -25
- package/src/tools/execution-target.ts +2 -2
- package/src/tools/executor.ts +7 -7
- package/src/tools/filesystem/edit.ts +2 -2
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -1
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +9 -8
- package/src/tools/mcp/mcp-tool-factory.ts +7 -6
- package/src/tools/memory/definitions.ts +6 -5
- package/src/tools/memory/handlers.test.ts +1 -1
- package/src/tools/network/__tests__/web-search.test.ts +3 -3
- package/src/tools/network/domain-normalize.ts +2 -2
- package/src/tools/network/script-proxy/session-manager.ts +10 -10
- package/src/tools/network/web-fetch.ts +1 -1
- package/src/tools/network/web-search.ts +3 -3
- package/src/tools/permission-checker.ts +8 -8
- package/src/tools/registry.ts +7 -7
- package/src/tools/schedule/list.ts +2 -2
- package/src/tools/schema-transforms.ts +31 -21
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/sensitive-output-placeholders.ts +1 -1
- package/src/tools/shared/filesystem/edit-engine.ts +1 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
- package/src/tools/shared/filesystem/image-read.ts +25 -5
- package/src/tools/shared/filesystem/path-policy.ts +2 -2
- package/src/tools/shared/shell-output.ts +1 -1
- package/src/tools/side-effects.ts +1 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +3 -3
- package/src/tools/skills/sandbox-runner.ts +3 -3
- package/src/tools/subagent/read.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -2
- package/src/tools/swarm/delegate.ts +3 -3
- package/src/tools/system/request-permission.ts +5 -4
- package/src/tools/terminal/backends/native.ts +4 -4
- package/src/tools/terminal/parser.ts +6 -6
- package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
- package/src/tools/terminal/shell.ts +16 -16
- package/src/tools/tool-approval-handler.ts +21 -12
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +3 -3
- package/src/tools/ui-surface/definitions.ts +9 -37
- package/src/tools/watcher/list.ts +1 -1
- package/src/util/logger.ts +7 -2
- package/src/util/retry.ts +29 -1
- package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
- package/src/cli/reference.ts +0 -38
- package/src/memory/job-handlers/capability-cards.ts +0 -420
- package/src/runtime/routes/thread-starter-routes.ts +0 -294
|
@@ -53,13 +53,14 @@ function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
|
53
53
|
const testDir = realpathSync(
|
|
54
54
|
mkdtempSync(join(tmpdir(), "migration-cross-version-test-")),
|
|
55
55
|
);
|
|
56
|
-
const testDbDir = join(testDir, "db");
|
|
56
|
+
const testDbDir = join(testDir, "data", "db");
|
|
57
57
|
const testDbPath = join(testDbDir, "assistant.db");
|
|
58
58
|
const testConfigPath = join(testDir, "config.json");
|
|
59
59
|
|
|
60
60
|
mock.module("../util/platform.js", () => ({
|
|
61
61
|
getRootDir: () => testDir,
|
|
62
|
-
getDataDir: () => testDir,
|
|
62
|
+
getDataDir: () => join(testDir, "data"),
|
|
63
|
+
getWorkspaceDir: () => testDir,
|
|
63
64
|
getWorkspaceConfigPath: () => testConfigPath,
|
|
64
65
|
isMacOS: () => process.platform === "darwin",
|
|
65
66
|
isLinux: () => process.platform === "linux",
|
|
@@ -83,9 +84,8 @@ mock.module("../config/loader.js", () => ({
|
|
|
83
84
|
model: "test",
|
|
84
85
|
provider: "test",
|
|
85
86
|
memory: { enabled: false },
|
|
86
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
87
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
87
88
|
secretDetection: { enabled: false },
|
|
88
|
-
sandbox: { enabled: false },
|
|
89
89
|
}),
|
|
90
90
|
}));
|
|
91
91
|
|
|
@@ -445,7 +445,7 @@ describe("schema version compatibility", () => {
|
|
|
445
445
|
{ schema_version: "3.0" },
|
|
446
446
|
);
|
|
447
447
|
|
|
448
|
-
const resolver = new DefaultPathResolver(
|
|
448
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
449
449
|
const result = commitImport({
|
|
450
450
|
archiveData: vbundle,
|
|
451
451
|
pathResolver: resolver,
|
|
@@ -463,7 +463,7 @@ describe("schema version compatibility", () => {
|
|
|
463
463
|
schema_version: "5.0-beta",
|
|
464
464
|
});
|
|
465
465
|
|
|
466
|
-
const resolver = new DefaultPathResolver(
|
|
466
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
467
467
|
const validationResult = validateVBundle(vbundle);
|
|
468
468
|
expect(validationResult.manifest).toBeDefined();
|
|
469
469
|
|
|
@@ -774,9 +774,10 @@ describe("round-trip: export -> validate -> preflight -> import", () => {
|
|
|
774
774
|
expect(preflightBody.files).toBeDefined();
|
|
775
775
|
expect(preflightBody.manifest).toBeDefined();
|
|
776
776
|
|
|
777
|
-
// The files should show "unchanged" since we exported from the same disk
|
|
777
|
+
// The files should show "unchanged" since we exported from the same disk.
|
|
778
|
+
// New format uses workspace/ prefix for archive paths.
|
|
778
779
|
const dbFile = preflightBody.files!.find(
|
|
779
|
-
(f) => f.path === "data/db/assistant.db",
|
|
780
|
+
(f) => f.path === "workspace/data/db/assistant.db",
|
|
780
781
|
);
|
|
781
782
|
expect(dbFile).toBeDefined();
|
|
782
783
|
expect(dbFile!.action).toBe("unchanged");
|
|
@@ -829,7 +830,7 @@ describe("round-trip: export -> validate -> preflight -> import", () => {
|
|
|
829
830
|
);
|
|
830
831
|
|
|
831
832
|
// Step 3: Analyze (preflight)
|
|
832
|
-
const resolver = new DefaultPathResolver(
|
|
833
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
833
834
|
const report = analyzeImport({
|
|
834
835
|
manifest: validationResult.manifest!,
|
|
835
836
|
pathResolver: resolver,
|
|
@@ -888,13 +889,14 @@ describe("partial failure scenarios", () => {
|
|
|
888
889
|
{ path: "data/db/assistant.db", data: newDbData },
|
|
889
890
|
]);
|
|
890
891
|
|
|
891
|
-
// Point to a path
|
|
892
|
-
//
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
892
|
+
// Point to a workspace path where data/db cannot be created
|
|
893
|
+
// (a regular file blocks directory creation)
|
|
894
|
+
const blockerWorkspace = join(testDir, "blocker-workspace");
|
|
895
|
+
mkdirSync(blockerWorkspace, { recursive: true });
|
|
896
|
+
// Create "data" as a regular file — mkdirSync("data/db") will fail
|
|
897
|
+
writeFileSync(join(blockerWorkspace, "data"), "I am a file, not a directory");
|
|
896
898
|
|
|
897
|
-
const resolver = new DefaultPathResolver(
|
|
899
|
+
const resolver = new DefaultPathResolver(undefined, blockerWorkspace);
|
|
898
900
|
const result = commitImport({
|
|
899
901
|
archiveData: vbundle,
|
|
900
902
|
pathResolver: resolver,
|
|
@@ -908,11 +910,11 @@ describe("partial failure scenarios", () => {
|
|
|
908
910
|
}
|
|
909
911
|
|
|
910
912
|
// Clean up
|
|
911
|
-
rmSync(
|
|
913
|
+
rmSync(blockerWorkspace, { recursive: true, force: true });
|
|
912
914
|
});
|
|
913
915
|
|
|
914
916
|
test("commitImport with invalid archive returns validation_failed", () => {
|
|
915
|
-
const resolver = new DefaultPathResolver(
|
|
917
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
916
918
|
const result = commitImport({
|
|
917
919
|
archiveData: new Uint8Array([0xba, 0xad, 0xf0, 0x0d]),
|
|
918
920
|
pathResolver: resolver,
|
|
@@ -1000,7 +1002,7 @@ describe("partial failure scenarios", () => {
|
|
|
1000
1002
|
]);
|
|
1001
1003
|
const corruptVBundle = gzipSync(tar);
|
|
1002
1004
|
|
|
1003
|
-
const resolver = new DefaultPathResolver(
|
|
1005
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
1004
1006
|
const result = commitImport({
|
|
1005
1007
|
archiveData: corruptVBundle,
|
|
1006
1008
|
pathResolver: resolver,
|
|
@@ -1032,7 +1034,7 @@ describe("edge cases", () => {
|
|
|
1032
1034
|
expect(result.manifest?.files[0].size).toBe(0);
|
|
1033
1035
|
|
|
1034
1036
|
// Import should also succeed
|
|
1035
|
-
const resolver = new DefaultPathResolver(
|
|
1037
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
1036
1038
|
const importResult = commitImport({
|
|
1037
1039
|
archiveData: vbundle,
|
|
1038
1040
|
pathResolver: resolver,
|
|
@@ -1186,7 +1188,10 @@ describe("edge cases", () => {
|
|
|
1186
1188
|
expect(missingError!.message).toContain("data/extra/ghost.bin");
|
|
1187
1189
|
});
|
|
1188
1190
|
|
|
1189
|
-
test("bundle with only manifest (no
|
|
1191
|
+
test("bundle with only manifest (no data files) is valid", () => {
|
|
1192
|
+
// After the workspace walk refactor, only manifest.json is required.
|
|
1193
|
+
// A bundle with no data files is structurally valid — it just won't
|
|
1194
|
+
// restore anything meaningful.
|
|
1190
1195
|
const manifestWithoutChecksum = {
|
|
1191
1196
|
schema_version: "1.0",
|
|
1192
1197
|
created_at: new Date().toISOString(),
|
|
@@ -1205,12 +1210,7 @@ describe("edge cases", () => {
|
|
|
1205
1210
|
const vbundle = gzipSync(tar);
|
|
1206
1211
|
const result = validateVBundle(vbundle);
|
|
1207
1212
|
|
|
1208
|
-
expect(result.is_valid).toBe(
|
|
1209
|
-
expect(
|
|
1210
|
-
result.errors.some(
|
|
1211
|
-
(e) => e.code === "MISSING_ENTRY" && e.path === "data/db/assistant.db",
|
|
1212
|
-
),
|
|
1213
|
-
).toBe(true);
|
|
1213
|
+
expect(result.is_valid).toBe(true);
|
|
1214
1214
|
});
|
|
1215
1215
|
|
|
1216
1216
|
test("completely empty gzip content (no tar entries) fails gracefully", () => {
|
|
@@ -1424,7 +1424,7 @@ describe("diagnostic quality", () => {
|
|
|
1424
1424
|
});
|
|
1425
1425
|
|
|
1426
1426
|
test("import commit validation_failed response includes error codes and messages", () => {
|
|
1427
|
-
const resolver = new DefaultPathResolver(
|
|
1427
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
1428
1428
|
const result = commitImport({
|
|
1429
1429
|
archiveData: new Uint8Array([0x00]),
|
|
1430
1430
|
pathResolver: resolver,
|
|
@@ -1633,7 +1633,7 @@ describe("builder -> validator consistency", () => {
|
|
|
1633
1633
|
|
|
1634
1634
|
describe("import analyzer edge cases", () => {
|
|
1635
1635
|
test("all-unchanged files produce correct summary", () => {
|
|
1636
|
-
const resolver = new DefaultPathResolver(
|
|
1636
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
1637
1637
|
const existingConfig = new Uint8Array(readFileSync(testConfigPath));
|
|
1638
1638
|
|
|
1639
1639
|
const report = analyzeImport({
|
|
@@ -1666,8 +1666,8 @@ describe("import analyzer edge cases", () => {
|
|
|
1666
1666
|
|
|
1667
1667
|
test("all-create scenario (fresh install) produces correct summary", () => {
|
|
1668
1668
|
const resolver = new DefaultPathResolver(
|
|
1669
|
-
|
|
1670
|
-
join(testDir, "nonexistent-
|
|
1669
|
+
undefined,
|
|
1670
|
+
join(testDir, "nonexistent-workspace"),
|
|
1671
1671
|
);
|
|
1672
1672
|
|
|
1673
1673
|
const report = analyzeImport({
|
|
@@ -1698,7 +1698,7 @@ describe("import analyzer edge cases", () => {
|
|
|
1698
1698
|
});
|
|
1699
1699
|
|
|
1700
1700
|
test("mixed create/overwrite/unchanged produces accurate counts", () => {
|
|
1701
|
-
const resolver = new DefaultPathResolver(
|
|
1701
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
1702
1702
|
|
|
1703
1703
|
// db: different from disk -> overwrite
|
|
1704
1704
|
// config: same as disk -> unchanged
|
|
@@ -1731,7 +1731,7 @@ describe("import analyzer edge cases", () => {
|
|
|
1731
1731
|
});
|
|
1732
1732
|
|
|
1733
1733
|
test("unknown archive path produces UNKNOWN_ARCHIVE_PATH conflict", () => {
|
|
1734
|
-
const resolver = new DefaultPathResolver(
|
|
1734
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
1735
1735
|
|
|
1736
1736
|
const report = analyzeImport({
|
|
1737
1737
|
manifest: {
|
|
@@ -27,13 +27,14 @@ import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test";
|
|
|
27
27
|
const testDir = realpathSync(
|
|
28
28
|
mkdtempSync(join(tmpdir(), "migration-export-http-test-")),
|
|
29
29
|
);
|
|
30
|
-
const testDbDir = join(testDir, "db");
|
|
30
|
+
const testDbDir = join(testDir, "data", "db");
|
|
31
31
|
const testDbPath = join(testDbDir, "assistant.db");
|
|
32
32
|
const testConfigPath = join(testDir, "config.json");
|
|
33
33
|
|
|
34
34
|
mock.module("../util/platform.js", () => ({
|
|
35
35
|
getRootDir: () => testDir,
|
|
36
|
-
getDataDir: () => testDir,
|
|
36
|
+
getDataDir: () => join(testDir, "data"),
|
|
37
|
+
getWorkspaceDir: () => testDir,
|
|
37
38
|
getWorkspaceConfigPath: () => testConfigPath,
|
|
38
39
|
isMacOS: () => process.platform === "darwin",
|
|
39
40
|
isLinux: () => process.platform === "linux",
|
|
@@ -57,9 +58,8 @@ mock.module("../config/loader.js", () => ({
|
|
|
57
58
|
model: "test",
|
|
58
59
|
provider: "test",
|
|
59
60
|
memory: { enabled: false },
|
|
60
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
61
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
61
62
|
secretDetection: { enabled: false },
|
|
62
|
-
sandbox: { enabled: false },
|
|
63
63
|
}),
|
|
64
64
|
}));
|
|
65
65
|
|
|
@@ -206,10 +206,10 @@ describe("handleMigrationExport", () => {
|
|
|
206
206
|
expect(manifest.created_at).toBeDefined();
|
|
207
207
|
expect(manifest.manifest_sha256).toBeDefined();
|
|
208
208
|
|
|
209
|
-
// Verify file entries
|
|
209
|
+
// Verify file entries — workspace walk uses workspace/ prefix
|
|
210
210
|
const filePaths = manifest.files.map((f) => f.path);
|
|
211
|
-
expect(filePaths).toContain("data/db/assistant.db");
|
|
212
|
-
expect(filePaths).toContain("config
|
|
211
|
+
expect(filePaths).toContain("workspace/data/db/assistant.db");
|
|
212
|
+
expect(filePaths).toContain("workspace/config.json");
|
|
213
213
|
|
|
214
214
|
// Verify each file entry has proper sha256 and size
|
|
215
215
|
for (const file of manifest.files) {
|
|
@@ -299,7 +299,7 @@ describe("export data population", () => {
|
|
|
299
299
|
const archiveData = new Uint8Array(await res.arrayBuffer());
|
|
300
300
|
const entries = parseTarEntries(archiveData);
|
|
301
301
|
|
|
302
|
-
const dbEntry = entries.find((e) => e.name === "data/db/assistant.db");
|
|
302
|
+
const dbEntry = entries.find((e) => e.name === "workspace/data/db/assistant.db");
|
|
303
303
|
expect(dbEntry).toBeDefined();
|
|
304
304
|
expect(dbEntry!.data.length).toBe(SQLITE_HEADER.length);
|
|
305
305
|
// Verify the exported data matches the test fixture exactly
|
|
@@ -315,7 +315,7 @@ describe("export data population", () => {
|
|
|
315
315
|
const archiveData = new Uint8Array(await res.arrayBuffer());
|
|
316
316
|
const entries = parseTarEntries(archiveData);
|
|
317
317
|
|
|
318
|
-
const configEntry = entries.find((e) => e.name === "config
|
|
318
|
+
const configEntry = entries.find((e) => e.name === "workspace/config.json");
|
|
319
319
|
expect(configEntry).toBeDefined();
|
|
320
320
|
|
|
321
321
|
const configContent = new TextDecoder().decode(configEntry!.data);
|
|
@@ -336,13 +336,13 @@ describe("export data population", () => {
|
|
|
336
336
|
const manifest = validationResult.manifest!;
|
|
337
337
|
|
|
338
338
|
const dbFile = manifest.files.find(
|
|
339
|
-
(f) => f.path === "data/db/assistant.db",
|
|
339
|
+
(f) => f.path === "workspace/data/db/assistant.db",
|
|
340
340
|
);
|
|
341
341
|
expect(dbFile).toBeDefined();
|
|
342
342
|
expect(dbFile!.size).toBe(SQLITE_HEADER.length);
|
|
343
343
|
|
|
344
344
|
const configFile = manifest.files.find(
|
|
345
|
-
(f) => f.path === "config
|
|
345
|
+
(f) => f.path === "workspace/config.json",
|
|
346
346
|
);
|
|
347
347
|
expect(configFile).toBeDefined();
|
|
348
348
|
const expectedConfigSize = Buffer.byteLength(
|
|
@@ -382,7 +382,7 @@ describe("export data population", () => {
|
|
|
382
382
|
const manifest = validationResult.manifest!;
|
|
383
383
|
|
|
384
384
|
const dbFile = manifest.files.find(
|
|
385
|
-
(f) => f.path === "data/db/assistant.db",
|
|
385
|
+
(f) => f.path === "workspace/data/db/assistant.db",
|
|
386
386
|
);
|
|
387
387
|
expect(dbFile).toBeDefined();
|
|
388
388
|
// The skeleton used size 0 — real export should have actual content
|
|
@@ -398,7 +398,7 @@ describe("export data population", () => {
|
|
|
398
398
|
const archiveData = new Uint8Array(await res.arrayBuffer());
|
|
399
399
|
const entries = parseTarEntries(archiveData);
|
|
400
400
|
|
|
401
|
-
const configEntry = entries.find((e) => e.name === "config
|
|
401
|
+
const configEntry = entries.find((e) => e.name === "workspace/config.json");
|
|
402
402
|
expect(configEntry).toBeDefined();
|
|
403
403
|
|
|
404
404
|
const configContent = new TextDecoder().decode(configEntry!.data);
|
|
@@ -413,42 +413,41 @@ describe("export data population", () => {
|
|
|
413
413
|
// ---------------------------------------------------------------------------
|
|
414
414
|
|
|
415
415
|
describe("export graceful fallback", () => {
|
|
416
|
-
test("
|
|
416
|
+
test("nonexistent workspace produces valid archive with no files", async () => {
|
|
417
417
|
const { buildExportVBundle } =
|
|
418
418
|
await import("../runtime/migrations/vbundle-builder.js");
|
|
419
419
|
|
|
420
420
|
const result = buildExportVBundle({
|
|
421
|
-
|
|
422
|
-
configPath: testConfigPath,
|
|
421
|
+
workspaceDir: join(testDir, "nonexistent-workspace"),
|
|
423
422
|
});
|
|
424
423
|
|
|
425
424
|
const validationResult = validateVBundle(result.archive);
|
|
426
425
|
expect(validationResult.is_valid).toBe(true);
|
|
427
|
-
|
|
428
|
-
const dbFile = result.manifest.files.find(
|
|
429
|
-
(f) => f.path === "data/db/assistant.db",
|
|
430
|
-
);
|
|
431
|
-
expect(dbFile).toBeDefined();
|
|
432
|
-
expect(dbFile!.size).toBe(0);
|
|
426
|
+
expect(result.manifest.files).toHaveLength(0);
|
|
433
427
|
});
|
|
434
428
|
|
|
435
|
-
test("
|
|
429
|
+
test("workspace walk includes db and config under workspace/ prefix", async () => {
|
|
436
430
|
const { buildExportVBundle } =
|
|
437
431
|
await import("../runtime/migrations/vbundle-builder.js");
|
|
438
432
|
|
|
439
433
|
const result = buildExportVBundle({
|
|
440
|
-
|
|
441
|
-
configPath: join(testDir, "nonexistent-config.json"),
|
|
434
|
+
workspaceDir: testDir,
|
|
442
435
|
});
|
|
443
436
|
|
|
444
437
|
const validationResult = validateVBundle(result.archive);
|
|
445
438
|
expect(validationResult.is_valid).toBe(true);
|
|
446
439
|
|
|
440
|
+
const dbFile = result.manifest.files.find(
|
|
441
|
+
(f) => f.path === "workspace/data/db/assistant.db",
|
|
442
|
+
);
|
|
443
|
+
expect(dbFile).toBeDefined();
|
|
444
|
+
expect(dbFile!.size).toBeGreaterThan(0);
|
|
445
|
+
|
|
447
446
|
const configFile = result.manifest.files.find(
|
|
448
|
-
(f) => f.path === "config
|
|
447
|
+
(f) => f.path === "workspace/config.json",
|
|
449
448
|
);
|
|
450
449
|
expect(configFile).toBeDefined();
|
|
451
|
-
expect(configFile!.size).
|
|
450
|
+
expect(configFile!.size).toBeGreaterThan(0);
|
|
452
451
|
});
|
|
453
452
|
});
|
|
454
453
|
|
|
@@ -48,13 +48,14 @@ function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
|
48
48
|
const testDir = realpathSync(
|
|
49
49
|
mkdtempSync(join(tmpdir(), "migration-import-commit-http-test-")),
|
|
50
50
|
);
|
|
51
|
-
const testDbDir = join(testDir, "db");
|
|
51
|
+
const testDbDir = join(testDir, "data", "db");
|
|
52
52
|
const testDbPath = join(testDbDir, "assistant.db");
|
|
53
53
|
const testConfigPath = join(testDir, "config.json");
|
|
54
54
|
|
|
55
55
|
mock.module("../util/platform.js", () => ({
|
|
56
56
|
getRootDir: () => testDir,
|
|
57
|
-
getDataDir: () => testDir,
|
|
57
|
+
getDataDir: () => join(testDir, "data"),
|
|
58
|
+
getWorkspaceDir: () => testDir,
|
|
58
59
|
getWorkspaceConfigPath: () => testConfigPath,
|
|
59
60
|
isMacOS: () => process.platform === "darwin",
|
|
60
61
|
isLinux: () => process.platform === "linux",
|
|
@@ -78,9 +79,8 @@ mock.module("../config/loader.js", () => ({
|
|
|
78
79
|
model: "test",
|
|
79
80
|
provider: "test",
|
|
80
81
|
memory: { enabled: false },
|
|
81
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
82
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
82
83
|
secretDetection: { enabled: false },
|
|
83
|
-
sandbox: { enabled: false },
|
|
84
84
|
}),
|
|
85
85
|
}));
|
|
86
86
|
|
|
@@ -377,10 +377,13 @@ describe("handleMigrationImport", () => {
|
|
|
377
377
|
expect(writtenData).toEqual(newDbData);
|
|
378
378
|
});
|
|
379
379
|
|
|
380
|
-
test("
|
|
380
|
+
test("workspace is cleared before restore — files are created fresh", async () => {
|
|
381
|
+
// Only new-format bundles (workspace/ prefix) trigger workspace clearing.
|
|
382
|
+
// With clearing, existing files are removed before writing, so all files
|
|
383
|
+
// in the bundle are "created" (not "overwritten") and no backups are made.
|
|
381
384
|
const newDbData = new Uint8Array([0x01, 0x02, 0x03]);
|
|
382
385
|
const vbundle = createValidVBundle([
|
|
383
|
-
{ path: "data/db/assistant.db", data: newDbData },
|
|
386
|
+
{ path: "workspace/data/db/assistant.db", data: newDbData },
|
|
384
387
|
]);
|
|
385
388
|
const req = new Request("http://localhost/v1/migrations/import", {
|
|
386
389
|
method: "POST",
|
|
@@ -392,29 +395,22 @@ describe("handleMigrationImport", () => {
|
|
|
392
395
|
const body = (await res.json()) as ImportCommitResponse;
|
|
393
396
|
|
|
394
397
|
expect(body.success).toBe(true);
|
|
395
|
-
expect(body.summary.backups_created).
|
|
398
|
+
expect(body.summary.backups_created).toBe(0);
|
|
396
399
|
|
|
397
|
-
|
|
398
|
-
|
|
400
|
+
const dbFile = body.files.find(
|
|
401
|
+
(f) => f.path === "workspace/data/db/assistant.db",
|
|
402
|
+
);
|
|
399
403
|
expect(dbFile).toBeDefined();
|
|
400
|
-
expect(dbFile!.action).toBe("
|
|
401
|
-
expect(dbFile!.backup_path).not.toBeNull();
|
|
402
|
-
expect(existsSync(dbFile!.backup_path!)).toBe(true);
|
|
403
|
-
|
|
404
|
-
// Verify backup contains the original data
|
|
405
|
-
const backupData = new Uint8Array(readFileSync(dbFile!.backup_path!));
|
|
406
|
-
expect(backupData).toEqual(EXISTING_DB_DATA);
|
|
404
|
+
expect(dbFile!.action).toBe("created");
|
|
407
405
|
});
|
|
408
406
|
|
|
409
|
-
test("reports
|
|
410
|
-
//
|
|
411
|
-
rmSync(testConfigPath, { force: true });
|
|
412
|
-
|
|
407
|
+
test("reports all files as created after workspace clear", async () => {
|
|
408
|
+
// New-format workspace/ paths trigger clearing — both files are fresh.
|
|
413
409
|
const newDbData = new Uint8Array([0xaa, 0xbb]);
|
|
414
410
|
const newConfigData = new TextEncoder().encode('{"provider":"openai"}');
|
|
415
411
|
const vbundle = createValidVBundle([
|
|
416
|
-
{ path: "data/db/assistant.db", data: newDbData },
|
|
417
|
-
{ path: "config
|
|
412
|
+
{ path: "workspace/data/db/assistant.db", data: newDbData },
|
|
413
|
+
{ path: "workspace/config.json", data: newConfigData },
|
|
418
414
|
]);
|
|
419
415
|
const req = new Request("http://localhost/v1/migrations/import", {
|
|
420
416
|
method: "POST",
|
|
@@ -427,14 +423,16 @@ describe("handleMigrationImport", () => {
|
|
|
427
423
|
|
|
428
424
|
expect(body.success).toBe(true);
|
|
429
425
|
|
|
430
|
-
const dbFile = body.files.find(
|
|
426
|
+
const dbFile = body.files.find(
|
|
427
|
+
(f) => f.path === "workspace/data/db/assistant.db",
|
|
428
|
+
);
|
|
431
429
|
const configFile = body.files.find(
|
|
432
|
-
(f) => f.path === "config
|
|
430
|
+
(f) => f.path === "workspace/config.json",
|
|
433
431
|
);
|
|
434
432
|
|
|
435
|
-
|
|
433
|
+
// Both are "created" because workspace was cleared before writing
|
|
434
|
+
expect(dbFile!.action).toBe("created");
|
|
436
435
|
expect(configFile!.action).toBe("created");
|
|
437
|
-
expect(configFile!.backup_path).toBeNull();
|
|
438
436
|
});
|
|
439
437
|
|
|
440
438
|
test("summary counts match file details", async () => {
|
|
@@ -641,7 +639,7 @@ describe("commitImport", () => {
|
|
|
641
639
|
{ path: "data/db/assistant.db", data: newDbData },
|
|
642
640
|
]);
|
|
643
641
|
|
|
644
|
-
const resolver = new DefaultPathResolver(
|
|
642
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
645
643
|
const result = commitImport({
|
|
646
644
|
archiveData: vbundle,
|
|
647
645
|
pathResolver: resolver,
|
|
@@ -655,7 +653,7 @@ describe("commitImport", () => {
|
|
|
655
653
|
});
|
|
656
654
|
|
|
657
655
|
test("returns validation_failed for invalid bundles", () => {
|
|
658
|
-
const resolver = new DefaultPathResolver(
|
|
656
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
659
657
|
const result = commitImport({
|
|
660
658
|
archiveData: new Uint8Array([0xba, 0xad]),
|
|
661
659
|
pathResolver: resolver,
|
|
@@ -671,17 +669,16 @@ describe("commitImport", () => {
|
|
|
671
669
|
});
|
|
672
670
|
|
|
673
671
|
test("creates parent directories if they do not exist", () => {
|
|
674
|
-
// Use a
|
|
675
|
-
const
|
|
676
|
-
const
|
|
677
|
-
const newConfigPath = join(testDir, "new-config.json");
|
|
672
|
+
// Use a workspace that does not exist yet
|
|
673
|
+
const nonexistentWorkspace = join(testDir, "new-workspace");
|
|
674
|
+
const expectedDbPath = join(nonexistentWorkspace, "data", "db", "assistant.db");
|
|
678
675
|
|
|
679
676
|
const dbData = new Uint8Array([0x01, 0x02, 0x03]);
|
|
680
677
|
const vbundle = createValidVBundle([
|
|
681
678
|
{ path: "data/db/assistant.db", data: dbData },
|
|
682
679
|
]);
|
|
683
680
|
|
|
684
|
-
const resolver = new DefaultPathResolver(
|
|
681
|
+
const resolver = new DefaultPathResolver(undefined, nonexistentWorkspace);
|
|
685
682
|
const result = commitImport({
|
|
686
683
|
archiveData: vbundle,
|
|
687
684
|
pathResolver: resolver,
|
|
@@ -690,11 +687,11 @@ describe("commitImport", () => {
|
|
|
690
687
|
expect(result.ok).toBe(true);
|
|
691
688
|
if (result.ok) {
|
|
692
689
|
expect(result.report.files[0].action).toBe("created");
|
|
693
|
-
expect(existsSync(
|
|
690
|
+
expect(existsSync(expectedDbPath)).toBe(true);
|
|
694
691
|
}
|
|
695
692
|
|
|
696
693
|
// Clean up
|
|
697
|
-
rmSync(
|
|
694
|
+
rmSync(nonexistentWorkspace, { recursive: true, force: true });
|
|
698
695
|
});
|
|
699
696
|
|
|
700
697
|
test("post-write integrity: written file SHA-256 matches expected", () => {
|
|
@@ -705,7 +702,7 @@ describe("commitImport", () => {
|
|
|
705
702
|
{ path: "data/db/assistant.db", data: newDbData },
|
|
706
703
|
]);
|
|
707
704
|
|
|
708
|
-
const resolver = new DefaultPathResolver(
|
|
705
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
709
706
|
const result = commitImport({
|
|
710
707
|
archiveData: vbundle,
|
|
711
708
|
pathResolver: resolver,
|
|
@@ -735,7 +732,7 @@ describe("commitImport", () => {
|
|
|
735
732
|
{ path: "config/settings.json", data: newConfigData },
|
|
736
733
|
]);
|
|
737
734
|
|
|
738
|
-
const resolver = new DefaultPathResolver(
|
|
735
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
739
736
|
const result = commitImport({
|
|
740
737
|
archiveData: vbundle,
|
|
741
738
|
pathResolver: resolver,
|
|
@@ -755,6 +752,137 @@ describe("commitImport", () => {
|
|
|
755
752
|
});
|
|
756
753
|
});
|
|
757
754
|
|
|
755
|
+
// ---------------------------------------------------------------------------
|
|
756
|
+
// Workspace clearing tests
|
|
757
|
+
// ---------------------------------------------------------------------------
|
|
758
|
+
|
|
759
|
+
describe("commitImport — workspace clearing", () => {
|
|
760
|
+
const skillsDir = join(testDir, "skills");
|
|
761
|
+
const hooksDir = join(testDir, "hooks");
|
|
762
|
+
|
|
763
|
+
afterEach(() => {
|
|
764
|
+
// Restore test fixture files for subsequent tests
|
|
765
|
+
mkdirSync(testDbDir, { recursive: true });
|
|
766
|
+
writeFileSync(testDbPath, EXISTING_DB_DATA);
|
|
767
|
+
writeFileSync(testConfigPath, JSON.stringify(EXISTING_CONFIG, null, 2));
|
|
768
|
+
// Clean up skills/hooks dirs
|
|
769
|
+
for (const dir of [skillsDir, hooksDir]) {
|
|
770
|
+
if (existsSync(dir)) rmSync(dir, { recursive: true, force: true });
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
test("clears stale files via workspace clearing (new-format workspace/ entries)", () => {
|
|
775
|
+
mkdirSync(join(skillsDir, "stale-skill"), { recursive: true });
|
|
776
|
+
writeFileSync(join(skillsDir, "stale-skill", "SKILL.md"), "stale");
|
|
777
|
+
|
|
778
|
+
const skillData = new TextEncoder().encode("# New Skill");
|
|
779
|
+
const vbundle = createValidVBundle([
|
|
780
|
+
{ path: "workspace/skills/new-skill/SKILL.md", data: skillData },
|
|
781
|
+
]);
|
|
782
|
+
|
|
783
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
784
|
+
const result = commitImport({
|
|
785
|
+
archiveData: vbundle,
|
|
786
|
+
pathResolver: resolver,
|
|
787
|
+
workspaceDir: testDir,
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
expect(result.ok).toBe(true);
|
|
791
|
+
if (!result.ok) return;
|
|
792
|
+
|
|
793
|
+
// New skill written
|
|
794
|
+
expect(existsSync(join(skillsDir, "new-skill", "SKILL.md"))).toBe(true);
|
|
795
|
+
expect(readFileSync(join(skillsDir, "new-skill", "SKILL.md"), "utf8")).toBe(
|
|
796
|
+
"# New Skill",
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
// Stale skill removed by workspace clearing
|
|
800
|
+
expect(existsSync(join(skillsDir, "stale-skill"))).toBe(false);
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
test("old-format skills/ entries do not trigger workspace clearing", () => {
|
|
804
|
+
mkdirSync(join(skillsDir, "stale-skill"), { recursive: true });
|
|
805
|
+
writeFileSync(join(skillsDir, "stale-skill", "SKILL.md"), "stale");
|
|
806
|
+
|
|
807
|
+
const skillData = new TextEncoder().encode("# New Skill");
|
|
808
|
+
const vbundle = createValidVBundle([
|
|
809
|
+
{ path: "skills/new-skill/SKILL.md", data: skillData },
|
|
810
|
+
]);
|
|
811
|
+
|
|
812
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
813
|
+
const result = commitImport({
|
|
814
|
+
archiveData: vbundle,
|
|
815
|
+
pathResolver: resolver,
|
|
816
|
+
workspaceDir: testDir,
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
expect(result.ok).toBe(true);
|
|
820
|
+
if (!result.ok) return;
|
|
821
|
+
|
|
822
|
+
// New skill written
|
|
823
|
+
expect(existsSync(join(skillsDir, "new-skill", "SKILL.md"))).toBe(true);
|
|
824
|
+
|
|
825
|
+
// Stale skill survives — old-format bundles don't trigger workspace clearing
|
|
826
|
+
expect(existsSync(join(skillsDir, "stale-skill", "SKILL.md"))).toBe(true);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
test("hooks/ entries import to hooksDir (not workspace/hooks/)", () => {
|
|
830
|
+
// Use a separate hooks dir outside the workspace, like production layout
|
|
831
|
+
const externalHooksDir = join(testDir, ".hooks-external");
|
|
832
|
+
mkdirSync(externalHooksDir, { recursive: true });
|
|
833
|
+
|
|
834
|
+
const hookData = new TextEncoder().encode("#!/bin/sh\necho new");
|
|
835
|
+
const vbundle = createValidVBundle([
|
|
836
|
+
{ path: "hooks/new-hook/hook.sh", data: hookData },
|
|
837
|
+
]);
|
|
838
|
+
|
|
839
|
+
const resolver = new DefaultPathResolver(undefined, testDir, externalHooksDir);
|
|
840
|
+
const result = commitImport({
|
|
841
|
+
archiveData: vbundle,
|
|
842
|
+
pathResolver: resolver,
|
|
843
|
+
workspaceDir: testDir,
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
expect(result.ok).toBe(true);
|
|
847
|
+
if (!result.ok) return;
|
|
848
|
+
|
|
849
|
+
// Hook written to the external hooks dir, not workspace/hooks/
|
|
850
|
+
expect(existsSync(join(externalHooksDir, "new-hook", "hook.sh"))).toBe(true);
|
|
851
|
+
expect(
|
|
852
|
+
readFileSync(join(externalHooksDir, "new-hook", "hook.sh"), "utf8"),
|
|
853
|
+
).toBe("#!/bin/sh\necho new");
|
|
854
|
+
|
|
855
|
+
// Cleanup
|
|
856
|
+
rmSync(externalHooksDir, { recursive: true, force: true });
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
test("without workspaceDir, no clearing happens", () => {
|
|
860
|
+
mkdirSync(join(skillsDir, "existing-skill"), { recursive: true });
|
|
861
|
+
writeFileSync(
|
|
862
|
+
join(skillsDir, "existing-skill", "SKILL.md"),
|
|
863
|
+
"should survive",
|
|
864
|
+
);
|
|
865
|
+
|
|
866
|
+
const vbundle = createValidVBundle([
|
|
867
|
+
{ path: "skills/new-skill/SKILL.md", data: new TextEncoder().encode("new") },
|
|
868
|
+
]);
|
|
869
|
+
|
|
870
|
+
const resolver = new DefaultPathResolver(undefined, testDir);
|
|
871
|
+
// No workspaceDir — no clearing
|
|
872
|
+
const result = commitImport({
|
|
873
|
+
archiveData: vbundle,
|
|
874
|
+
pathResolver: resolver,
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
expect(result.ok).toBe(true);
|
|
878
|
+
|
|
879
|
+
// Existing skill survives (no workspace clearing without workspaceDir)
|
|
880
|
+
expect(existsSync(join(skillsDir, "existing-skill", "SKILL.md"))).toBe(
|
|
881
|
+
true,
|
|
882
|
+
);
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
|
|
758
886
|
// ---------------------------------------------------------------------------
|
|
759
887
|
// Auth policy registration tests
|
|
760
888
|
// ---------------------------------------------------------------------------
|