@vellumai/assistant 0.5.13 → 0.5.15
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/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +4 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +64 -88
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +3 -14
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +71 -33
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
|
@@ -41,11 +41,70 @@ mock.module("../config/bundled-skills/messaging/tools/shared.js", () => ({
|
|
|
41
41
|
extractEmail: (a: string) => a.toLowerCase(),
|
|
42
42
|
}));
|
|
43
43
|
|
|
44
|
+
// ── Cross-post dependency mocks ──
|
|
45
|
+
|
|
46
|
+
const addMessageMock = mock(
|
|
47
|
+
async (
|
|
48
|
+
conversationId: string,
|
|
49
|
+
role: string,
|
|
50
|
+
content: string,
|
|
51
|
+
_metadata?: Record<string, unknown>,
|
|
52
|
+
_opts?: { skipIndexing?: boolean },
|
|
53
|
+
) => ({
|
|
54
|
+
id: "xpost-msg-1",
|
|
55
|
+
conversationId,
|
|
56
|
+
role,
|
|
57
|
+
content,
|
|
58
|
+
createdAt: Date.now(),
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const getConversationMock = mock(
|
|
63
|
+
(_id: string) => null as { id: string; createdAt: number } | null,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const syncMessageToDiskMock = mock(
|
|
67
|
+
(
|
|
68
|
+
_conversationId: string,
|
|
69
|
+
_messageId: string,
|
|
70
|
+
_createdAtMs: number,
|
|
71
|
+
) => {},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const getBindingByChannelChatMock = mock(
|
|
75
|
+
(_sourceChannel: string, _externalChatId: string) =>
|
|
76
|
+
null as { conversationId: string; sourceChannel: string; externalChatId: string } | null,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
mock.module("../memory/conversation-crud.js", () => ({
|
|
80
|
+
addMessage: addMessageMock,
|
|
81
|
+
getConversation: getConversationMock,
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
mock.module("../memory/conversation-disk-view.js", () => ({
|
|
85
|
+
syncMessageToDisk: syncMessageToDiskMock,
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
mock.module("../memory/external-conversation-store.js", () => ({
|
|
89
|
+
getBindingByChannelChat: getBindingByChannelChatMock,
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
mock.module("../util/logger.js", () => ({
|
|
93
|
+
getLogger: () =>
|
|
94
|
+
new Proxy({} as Record<string, unknown>, {
|
|
95
|
+
get: () => () => {},
|
|
96
|
+
}),
|
|
97
|
+
}));
|
|
98
|
+
|
|
44
99
|
import { run } from "../config/bundled-skills/messaging/tools/messaging-send.js";
|
|
45
100
|
|
|
46
101
|
describe("messaging-send tool", () => {
|
|
47
102
|
beforeEach(() => {
|
|
48
103
|
sendMessageMock.mockClear();
|
|
104
|
+
addMessageMock.mockClear();
|
|
105
|
+
getConversationMock.mockClear();
|
|
106
|
+
syncMessageToDiskMock.mockClear();
|
|
107
|
+
getBindingByChannelChatMock.mockClear();
|
|
49
108
|
});
|
|
50
109
|
|
|
51
110
|
test("passes assistantId from tool context to provider send options", async () => {
|
|
@@ -106,4 +165,146 @@ describe("messaging-send tool", () => {
|
|
|
106
165
|
},
|
|
107
166
|
);
|
|
108
167
|
});
|
|
168
|
+
|
|
169
|
+
test("cross-posts outbound message to bound conversation", async () => {
|
|
170
|
+
getBindingByChannelChatMock.mockImplementation(() => ({
|
|
171
|
+
conversationId: "bound-conv-99",
|
|
172
|
+
sourceChannel: "phone",
|
|
173
|
+
externalChatId: "+15550004444",
|
|
174
|
+
}));
|
|
175
|
+
getConversationMock.mockImplementation(() => ({
|
|
176
|
+
id: "bound-conv-99",
|
|
177
|
+
createdAt: 1700000000000,
|
|
178
|
+
}));
|
|
179
|
+
|
|
180
|
+
const result = await run(
|
|
181
|
+
{
|
|
182
|
+
platform: "phone",
|
|
183
|
+
conversation_id: "+15550004444",
|
|
184
|
+
text: "hello from A",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
workingDir: "/tmp",
|
|
188
|
+
conversationId: "conv-A",
|
|
189
|
+
assistantId: "ast-1",
|
|
190
|
+
trustClass: "guardian" as const,
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
expect(result.isError).toBe(false);
|
|
195
|
+
expect(addMessageMock).toHaveBeenCalledWith(
|
|
196
|
+
"bound-conv-99",
|
|
197
|
+
"assistant",
|
|
198
|
+
JSON.stringify([{ type: "text", text: "hello from A" }]),
|
|
199
|
+
{ automated: true, crossPostedFrom: "conv-A" },
|
|
200
|
+
{ skipIndexing: true },
|
|
201
|
+
);
|
|
202
|
+
expect(syncMessageToDiskMock).toHaveBeenCalledWith(
|
|
203
|
+
"bound-conv-99",
|
|
204
|
+
"xpost-msg-1",
|
|
205
|
+
1700000000000,
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("does not cross-post when bound conversation is the sender", async () => {
|
|
210
|
+
getBindingByChannelChatMock.mockImplementation(() => ({
|
|
211
|
+
conversationId: "conv-A",
|
|
212
|
+
sourceChannel: "phone",
|
|
213
|
+
externalChatId: "+15550004444",
|
|
214
|
+
}));
|
|
215
|
+
|
|
216
|
+
await run(
|
|
217
|
+
{
|
|
218
|
+
platform: "phone",
|
|
219
|
+
conversation_id: "+15550004444",
|
|
220
|
+
text: "hello",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
workingDir: "/tmp",
|
|
224
|
+
conversationId: "conv-A",
|
|
225
|
+
assistantId: "ast-1",
|
|
226
|
+
trustClass: "guardian" as const,
|
|
227
|
+
},
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
expect(addMessageMock).not.toHaveBeenCalled();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("does not cross-post when no binding exists", async () => {
|
|
234
|
+
getBindingByChannelChatMock.mockImplementation(() => null);
|
|
235
|
+
|
|
236
|
+
await run(
|
|
237
|
+
{
|
|
238
|
+
platform: "phone",
|
|
239
|
+
conversation_id: "+15550004444",
|
|
240
|
+
text: "hello",
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
workingDir: "/tmp",
|
|
244
|
+
conversationId: "conv-A",
|
|
245
|
+
assistantId: "ast-1",
|
|
246
|
+
trustClass: "guardian" as const,
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
expect(addMessageMock).not.toHaveBeenCalled();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("cross-post failure does not fail the send", async () => {
|
|
254
|
+
getBindingByChannelChatMock.mockImplementation(() => ({
|
|
255
|
+
conversationId: "bound-conv-99",
|
|
256
|
+
sourceChannel: "phone",
|
|
257
|
+
externalChatId: "+15550004444",
|
|
258
|
+
}));
|
|
259
|
+
getConversationMock.mockImplementation(() => ({
|
|
260
|
+
id: "bound-conv-99",
|
|
261
|
+
createdAt: 1700000000000,
|
|
262
|
+
}));
|
|
263
|
+
addMessageMock.mockImplementation(async () => {
|
|
264
|
+
throw new Error("DB write failed");
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const result = await run(
|
|
268
|
+
{
|
|
269
|
+
platform: "phone",
|
|
270
|
+
conversation_id: "+15550004444",
|
|
271
|
+
text: "hello",
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
workingDir: "/tmp",
|
|
275
|
+
conversationId: "conv-A",
|
|
276
|
+
assistantId: "ast-1",
|
|
277
|
+
trustClass: "guardian" as const,
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
expect(result.isError).toBe(false);
|
|
282
|
+
expect(result.content).toContain("Message sent");
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("does not cross-post when bound conversation no longer exists", async () => {
|
|
286
|
+
getBindingByChannelChatMock.mockImplementation(() => ({
|
|
287
|
+
conversationId: "deleted-conv",
|
|
288
|
+
sourceChannel: "phone",
|
|
289
|
+
externalChatId: "+15550004444",
|
|
290
|
+
}));
|
|
291
|
+
getConversationMock.mockImplementation(() => null);
|
|
292
|
+
|
|
293
|
+
await run(
|
|
294
|
+
{
|
|
295
|
+
platform: "phone",
|
|
296
|
+
conversation_id: "+15550004444",
|
|
297
|
+
text: "hello",
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
workingDir: "/tmp",
|
|
301
|
+
conversationId: "conv-A",
|
|
302
|
+
assistantId: "ast-1",
|
|
303
|
+
trustClass: "guardian" as const,
|
|
304
|
+
},
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
expect(getBindingByChannelChatMock).toHaveBeenCalled();
|
|
308
|
+
expect(addMessageMock).not.toHaveBeenCalled();
|
|
309
|
+
});
|
|
109
310
|
});
|
|
@@ -58,7 +58,7 @@ 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
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
62
62
|
getDataDir: () => join(testDir, "data"),
|
|
63
63
|
getWorkspaceDir: () => testDir,
|
|
64
64
|
getWorkspaceConfigPath: () => testConfigPath,
|
|
@@ -78,6 +78,12 @@ mock.module("../util/logger.js", () => ({
|
|
|
78
78
|
}),
|
|
79
79
|
}));
|
|
80
80
|
|
|
81
|
+
mock.module("../permissions/trust-store.js", () => ({
|
|
82
|
+
getAllRules: () => [],
|
|
83
|
+
isStarterBundleAccepted: () => false,
|
|
84
|
+
clearCache: () => {},
|
|
85
|
+
}));
|
|
86
|
+
|
|
81
87
|
mock.module("../config/loader.js", () => ({
|
|
82
88
|
getConfig: () => ({
|
|
83
89
|
ui: {},
|
|
@@ -445,7 +451,7 @@ describe("schema version compatibility", () => {
|
|
|
445
451
|
{ schema_version: "3.0" },
|
|
446
452
|
);
|
|
447
453
|
|
|
448
|
-
const resolver = new DefaultPathResolver(
|
|
454
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
449
455
|
const result = commitImport({
|
|
450
456
|
archiveData: vbundle,
|
|
451
457
|
pathResolver: resolver,
|
|
@@ -463,7 +469,7 @@ describe("schema version compatibility", () => {
|
|
|
463
469
|
schema_version: "5.0-beta",
|
|
464
470
|
});
|
|
465
471
|
|
|
466
|
-
const resolver = new DefaultPathResolver(
|
|
472
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
467
473
|
const validationResult = validateVBundle(vbundle);
|
|
468
474
|
expect(validationResult.manifest).toBeDefined();
|
|
469
475
|
|
|
@@ -830,7 +836,7 @@ describe("round-trip: export -> validate -> preflight -> import", () => {
|
|
|
830
836
|
);
|
|
831
837
|
|
|
832
838
|
// Step 3: Analyze (preflight)
|
|
833
|
-
const resolver = new DefaultPathResolver(
|
|
839
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
834
840
|
const report = analyzeImport({
|
|
835
841
|
manifest: validationResult.manifest!,
|
|
836
842
|
pathResolver: resolver,
|
|
@@ -899,7 +905,7 @@ describe("partial failure scenarios", () => {
|
|
|
899
905
|
"I am a file, not a directory",
|
|
900
906
|
);
|
|
901
907
|
|
|
902
|
-
const resolver = new DefaultPathResolver(
|
|
908
|
+
const resolver = new DefaultPathResolver(blockerWorkspace);
|
|
903
909
|
const result = commitImport({
|
|
904
910
|
archiveData: vbundle,
|
|
905
911
|
pathResolver: resolver,
|
|
@@ -917,7 +923,7 @@ describe("partial failure scenarios", () => {
|
|
|
917
923
|
});
|
|
918
924
|
|
|
919
925
|
test("commitImport with invalid archive returns validation_failed", () => {
|
|
920
|
-
const resolver = new DefaultPathResolver(
|
|
926
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
921
927
|
const result = commitImport({
|
|
922
928
|
archiveData: new Uint8Array([0xba, 0xad, 0xf0, 0x0d]),
|
|
923
929
|
pathResolver: resolver,
|
|
@@ -1005,7 +1011,7 @@ describe("partial failure scenarios", () => {
|
|
|
1005
1011
|
]);
|
|
1006
1012
|
const corruptVBundle = gzipSync(tar);
|
|
1007
1013
|
|
|
1008
|
-
const resolver = new DefaultPathResolver(
|
|
1014
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
1009
1015
|
const result = commitImport({
|
|
1010
1016
|
archiveData: corruptVBundle,
|
|
1011
1017
|
pathResolver: resolver,
|
|
@@ -1037,7 +1043,7 @@ describe("edge cases", () => {
|
|
|
1037
1043
|
expect(result.manifest?.files[0].size).toBe(0);
|
|
1038
1044
|
|
|
1039
1045
|
// Import should also succeed
|
|
1040
|
-
const resolver = new DefaultPathResolver(
|
|
1046
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
1041
1047
|
const importResult = commitImport({
|
|
1042
1048
|
archiveData: vbundle,
|
|
1043
1049
|
pathResolver: resolver,
|
|
@@ -1427,7 +1433,7 @@ describe("diagnostic quality", () => {
|
|
|
1427
1433
|
});
|
|
1428
1434
|
|
|
1429
1435
|
test("import commit validation_failed response includes error codes and messages", () => {
|
|
1430
|
-
const resolver = new DefaultPathResolver(
|
|
1436
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
1431
1437
|
const result = commitImport({
|
|
1432
1438
|
archiveData: new Uint8Array([0x00]),
|
|
1433
1439
|
pathResolver: resolver,
|
|
@@ -1636,7 +1642,7 @@ describe("builder -> validator consistency", () => {
|
|
|
1636
1642
|
|
|
1637
1643
|
describe("import analyzer edge cases", () => {
|
|
1638
1644
|
test("all-unchanged files produce correct summary", () => {
|
|
1639
|
-
const resolver = new DefaultPathResolver(
|
|
1645
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
1640
1646
|
const existingConfig = new Uint8Array(readFileSync(testConfigPath));
|
|
1641
1647
|
|
|
1642
1648
|
const report = analyzeImport({
|
|
@@ -1669,7 +1675,6 @@ describe("import analyzer edge cases", () => {
|
|
|
1669
1675
|
|
|
1670
1676
|
test("all-create scenario (fresh install) produces correct summary", () => {
|
|
1671
1677
|
const resolver = new DefaultPathResolver(
|
|
1672
|
-
undefined,
|
|
1673
1678
|
join(testDir, "nonexistent-workspace"),
|
|
1674
1679
|
);
|
|
1675
1680
|
|
|
@@ -1701,7 +1706,7 @@ describe("import analyzer edge cases", () => {
|
|
|
1701
1706
|
});
|
|
1702
1707
|
|
|
1703
1708
|
test("mixed create/overwrite/unchanged produces accurate counts", () => {
|
|
1704
|
-
const resolver = new DefaultPathResolver(
|
|
1709
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
1705
1710
|
|
|
1706
1711
|
// db: different from disk -> overwrite
|
|
1707
1712
|
// config: same as disk -> unchanged
|
|
@@ -1734,7 +1739,7 @@ describe("import analyzer edge cases", () => {
|
|
|
1734
1739
|
});
|
|
1735
1740
|
|
|
1736
1741
|
test("unknown archive path produces UNKNOWN_ARCHIVE_PATH conflict", () => {
|
|
1737
|
-
const resolver = new DefaultPathResolver(
|
|
1742
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
1738
1743
|
|
|
1739
1744
|
const report = analyzeImport({
|
|
1740
1745
|
manifest: {
|
|
@@ -32,7 +32,7 @@ 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
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
36
36
|
getDataDir: () => join(testDir, "data"),
|
|
37
37
|
getWorkspaceDir: () => testDir,
|
|
38
38
|
getWorkspaceConfigPath: () => testConfigPath,
|
|
@@ -52,6 +52,12 @@ mock.module("../util/logger.js", () => ({
|
|
|
52
52
|
}),
|
|
53
53
|
}));
|
|
54
54
|
|
|
55
|
+
mock.module("../permissions/trust-store.js", () => ({
|
|
56
|
+
getAllRules: () => [],
|
|
57
|
+
isStarterBundleAccepted: () => false,
|
|
58
|
+
clearCache: () => {},
|
|
59
|
+
}));
|
|
60
|
+
|
|
55
61
|
mock.module("../config/loader.js", () => ({
|
|
56
62
|
getConfig: () => ({
|
|
57
63
|
ui: {},
|
|
@@ -53,7 +53,7 @@ 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
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
57
57
|
getDataDir: () => join(testDir, "data"),
|
|
58
58
|
getWorkspaceDir: () => testDir,
|
|
59
59
|
getWorkspaceConfigPath: () => testConfigPath,
|
|
@@ -73,6 +73,12 @@ mock.module("../util/logger.js", () => ({
|
|
|
73
73
|
}),
|
|
74
74
|
}));
|
|
75
75
|
|
|
76
|
+
mock.module("../permissions/trust-store.js", () => ({
|
|
77
|
+
getAllRules: () => [],
|
|
78
|
+
isStarterBundleAccepted: () => false,
|
|
79
|
+
clearCache: () => {},
|
|
80
|
+
}));
|
|
81
|
+
|
|
76
82
|
mock.module("../config/loader.js", () => ({
|
|
77
83
|
getConfig: () => ({
|
|
78
84
|
ui: {},
|
|
@@ -639,7 +645,7 @@ describe("commitImport", () => {
|
|
|
639
645
|
{ path: "data/db/assistant.db", data: newDbData },
|
|
640
646
|
]);
|
|
641
647
|
|
|
642
|
-
const resolver = new DefaultPathResolver(
|
|
648
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
643
649
|
const result = commitImport({
|
|
644
650
|
archiveData: vbundle,
|
|
645
651
|
pathResolver: resolver,
|
|
@@ -653,7 +659,7 @@ describe("commitImport", () => {
|
|
|
653
659
|
});
|
|
654
660
|
|
|
655
661
|
test("returns validation_failed for invalid bundles", () => {
|
|
656
|
-
const resolver = new DefaultPathResolver(
|
|
662
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
657
663
|
const result = commitImport({
|
|
658
664
|
archiveData: new Uint8Array([0xba, 0xad]),
|
|
659
665
|
pathResolver: resolver,
|
|
@@ -683,7 +689,7 @@ describe("commitImport", () => {
|
|
|
683
689
|
{ path: "data/db/assistant.db", data: dbData },
|
|
684
690
|
]);
|
|
685
691
|
|
|
686
|
-
const resolver = new DefaultPathResolver(
|
|
692
|
+
const resolver = new DefaultPathResolver(nonexistentWorkspace);
|
|
687
693
|
const result = commitImport({
|
|
688
694
|
archiveData: vbundle,
|
|
689
695
|
pathResolver: resolver,
|
|
@@ -707,7 +713,7 @@ describe("commitImport", () => {
|
|
|
707
713
|
{ path: "data/db/assistant.db", data: newDbData },
|
|
708
714
|
]);
|
|
709
715
|
|
|
710
|
-
const resolver = new DefaultPathResolver(
|
|
716
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
711
717
|
const result = commitImport({
|
|
712
718
|
archiveData: vbundle,
|
|
713
719
|
pathResolver: resolver,
|
|
@@ -737,7 +743,7 @@ describe("commitImport", () => {
|
|
|
737
743
|
{ path: "config/settings.json", data: newConfigData },
|
|
738
744
|
]);
|
|
739
745
|
|
|
740
|
-
const resolver = new DefaultPathResolver(
|
|
746
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
741
747
|
const result = commitImport({
|
|
742
748
|
archiveData: vbundle,
|
|
743
749
|
pathResolver: resolver,
|
|
@@ -785,7 +791,7 @@ describe("commitImport — workspace clearing", () => {
|
|
|
785
791
|
{ path: "workspace/skills/new-skill/SKILL.md", data: skillData },
|
|
786
792
|
]);
|
|
787
793
|
|
|
788
|
-
const resolver = new DefaultPathResolver(
|
|
794
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
789
795
|
const result = commitImport({
|
|
790
796
|
archiveData: vbundle,
|
|
791
797
|
pathResolver: resolver,
|
|
@@ -814,7 +820,7 @@ describe("commitImport — workspace clearing", () => {
|
|
|
814
820
|
{ path: "skills/new-skill/SKILL.md", data: skillData },
|
|
815
821
|
]);
|
|
816
822
|
|
|
817
|
-
const resolver = new DefaultPathResolver(
|
|
823
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
818
824
|
const result = commitImport({
|
|
819
825
|
archiveData: vbundle,
|
|
820
826
|
pathResolver: resolver,
|
|
@@ -841,11 +847,7 @@ describe("commitImport — workspace clearing", () => {
|
|
|
841
847
|
{ path: "hooks/new-hook/hook.sh", data: hookData },
|
|
842
848
|
]);
|
|
843
849
|
|
|
844
|
-
const resolver = new DefaultPathResolver(
|
|
845
|
-
undefined,
|
|
846
|
-
testDir,
|
|
847
|
-
externalHooksDir,
|
|
848
|
-
);
|
|
850
|
+
const resolver = new DefaultPathResolver(testDir, externalHooksDir);
|
|
849
851
|
const result = commitImport({
|
|
850
852
|
archiveData: vbundle,
|
|
851
853
|
pathResolver: resolver,
|
|
@@ -881,7 +883,7 @@ describe("commitImport — workspace clearing", () => {
|
|
|
881
883
|
},
|
|
882
884
|
]);
|
|
883
885
|
|
|
884
|
-
const resolver = new DefaultPathResolver(
|
|
886
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
885
887
|
// No workspaceDir — no clearing
|
|
886
888
|
const result = commitImport({
|
|
887
889
|
archiveData: vbundle,
|
|
@@ -39,7 +39,7 @@ const testDbPath = join(testDbDir, "assistant.db");
|
|
|
39
39
|
const testConfigPath = join(testDir, "config.json");
|
|
40
40
|
|
|
41
41
|
mock.module("../util/platform.js", () => ({
|
|
42
|
-
|
|
42
|
+
getProtectedDir: () => join(testDir, "protected"),
|
|
43
43
|
getDataDir: () => join(testDir, "data"),
|
|
44
44
|
getWorkspaceDir: () => testDir,
|
|
45
45
|
getWorkspaceConfigPath: () => testConfigPath,
|
|
@@ -59,6 +59,12 @@ mock.module("../util/logger.js", () => ({
|
|
|
59
59
|
}),
|
|
60
60
|
}));
|
|
61
61
|
|
|
62
|
+
mock.module("../permissions/trust-store.js", () => ({
|
|
63
|
+
getAllRules: () => [],
|
|
64
|
+
isStarterBundleAccepted: () => false,
|
|
65
|
+
clearCache: () => {},
|
|
66
|
+
}));
|
|
67
|
+
|
|
62
68
|
mock.module("../config/loader.js", () => ({
|
|
63
69
|
getConfig: () => ({
|
|
64
70
|
ui: {},
|
|
@@ -525,7 +531,6 @@ describe("handleMigrationImportPreflight — validation failures", () => {
|
|
|
525
531
|
describe("analyzeImport", () => {
|
|
526
532
|
test("detects create when file does not exist on disk", () => {
|
|
527
533
|
const resolver = new DefaultPathResolver(
|
|
528
|
-
undefined,
|
|
529
534
|
join(testDir, "nonexistent-workspace"),
|
|
530
535
|
);
|
|
531
536
|
|
|
@@ -553,7 +558,7 @@ describe("analyzeImport", () => {
|
|
|
553
558
|
});
|
|
554
559
|
|
|
555
560
|
test("detects unchanged when file on disk matches bundle", () => {
|
|
556
|
-
const resolver = new DefaultPathResolver(
|
|
561
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
557
562
|
|
|
558
563
|
const report = analyzeImport({
|
|
559
564
|
manifest: {
|
|
@@ -577,7 +582,7 @@ describe("analyzeImport", () => {
|
|
|
577
582
|
});
|
|
578
583
|
|
|
579
584
|
test("detects overwrite when file on disk differs from bundle", () => {
|
|
580
|
-
const resolver = new DefaultPathResolver(
|
|
585
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
581
586
|
|
|
582
587
|
const report = analyzeImport({
|
|
583
588
|
manifest: {
|
|
@@ -602,7 +607,7 @@ describe("analyzeImport", () => {
|
|
|
602
607
|
});
|
|
603
608
|
|
|
604
609
|
test("flags unknown archive paths as conflicts with skip action", () => {
|
|
605
|
-
const resolver = new DefaultPathResolver(
|
|
610
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
606
611
|
|
|
607
612
|
const report = analyzeImport({
|
|
608
613
|
manifest: {
|
|
@@ -640,7 +645,7 @@ describe("analyzeImport", () => {
|
|
|
640
645
|
});
|
|
641
646
|
|
|
642
647
|
test("includes manifest in report", () => {
|
|
643
|
-
const resolver = new DefaultPathResolver(
|
|
648
|
+
const resolver = new DefaultPathResolver(testDir);
|
|
644
649
|
const manifest = {
|
|
645
650
|
schema_version: "1.0",
|
|
646
651
|
created_at: "2024-01-01T00:00:00.000Z",
|
|
@@ -667,82 +672,60 @@ describe("analyzeImport", () => {
|
|
|
667
672
|
// DefaultPathResolver unit tests
|
|
668
673
|
// ---------------------------------------------------------------------------
|
|
669
674
|
|
|
675
|
+
const WORKSPACE_DIR = "/home/user/.vellum/workspace";
|
|
676
|
+
|
|
670
677
|
describe("DefaultPathResolver", () => {
|
|
671
678
|
test("resolves data/db/assistant.db to workspace db path (backward compat)", () => {
|
|
672
|
-
const resolver = new DefaultPathResolver(
|
|
673
|
-
undefined,
|
|
674
|
-
"/home/user/.vellum/workspace",
|
|
675
|
-
);
|
|
679
|
+
const resolver = new DefaultPathResolver(WORKSPACE_DIR);
|
|
676
680
|
expect(resolver.resolve("data/db/assistant.db")).toBe(
|
|
677
|
-
|
|
681
|
+
`${WORKSPACE_DIR}/data/db/assistant.db`,
|
|
678
682
|
);
|
|
679
683
|
});
|
|
680
684
|
|
|
681
685
|
test("resolves config/settings.json to workspace config path (backward compat)", () => {
|
|
682
|
-
const resolver = new DefaultPathResolver(
|
|
683
|
-
undefined,
|
|
684
|
-
"/home/user/.vellum/workspace",
|
|
685
|
-
);
|
|
686
|
+
const resolver = new DefaultPathResolver(WORKSPACE_DIR);
|
|
686
687
|
expect(resolver.resolve("config/settings.json")).toBe(
|
|
687
|
-
|
|
688
|
+
`${WORKSPACE_DIR}/config.json`,
|
|
688
689
|
);
|
|
689
690
|
});
|
|
690
691
|
|
|
691
692
|
test("returns null for unknown paths", () => {
|
|
692
|
-
const resolver = new DefaultPathResolver(
|
|
693
|
-
undefined,
|
|
694
|
-
"/home/user/.vellum/workspace",
|
|
695
|
-
);
|
|
693
|
+
const resolver = new DefaultPathResolver(WORKSPACE_DIR);
|
|
696
694
|
expect(resolver.resolve("unknown/path.txt")).toBeNull();
|
|
697
695
|
});
|
|
698
696
|
|
|
699
697
|
test("resolves valid skills path via backward compat", () => {
|
|
700
|
-
const resolver = new DefaultPathResolver(
|
|
701
|
-
undefined,
|
|
702
|
-
"/home/user/.vellum/workspace",
|
|
703
|
-
);
|
|
698
|
+
const resolver = new DefaultPathResolver(WORKSPACE_DIR);
|
|
704
699
|
expect(resolver.resolve("skills/my-skill/SKILL.md")).toBe(
|
|
705
|
-
|
|
700
|
+
`${WORKSPACE_DIR}/skills/my-skill/SKILL.md`,
|
|
706
701
|
);
|
|
707
702
|
});
|
|
708
703
|
|
|
709
704
|
test("resolves workspace/ prefix paths", () => {
|
|
710
|
-
const resolver = new DefaultPathResolver(
|
|
711
|
-
undefined,
|
|
712
|
-
"/home/user/.vellum/workspace",
|
|
713
|
-
);
|
|
705
|
+
const resolver = new DefaultPathResolver(WORKSPACE_DIR);
|
|
714
706
|
expect(resolver.resolve("workspace/data/db/assistant.db")).toBe(
|
|
715
|
-
|
|
707
|
+
`${WORKSPACE_DIR}/data/db/assistant.db`,
|
|
716
708
|
);
|
|
717
709
|
expect(resolver.resolve("workspace/config.json")).toBe(
|
|
718
|
-
|
|
710
|
+
`${WORKSPACE_DIR}/config.json`,
|
|
719
711
|
);
|
|
720
712
|
expect(resolver.resolve("workspace/skills/my-skill/SKILL.md")).toBe(
|
|
721
|
-
|
|
713
|
+
`${WORKSPACE_DIR}/skills/my-skill/SKILL.md`,
|
|
722
714
|
);
|
|
723
715
|
});
|
|
724
716
|
|
|
725
717
|
test("returns null for workspace/ path traversal attempt", () => {
|
|
726
|
-
const resolver = new DefaultPathResolver(
|
|
727
|
-
undefined,
|
|
728
|
-
"/home/user/.vellum/workspace",
|
|
729
|
-
);
|
|
718
|
+
const resolver = new DefaultPathResolver(WORKSPACE_DIR);
|
|
730
719
|
expect(resolver.resolve("workspace/../../etc/passwd")).toBeNull();
|
|
731
720
|
});
|
|
732
721
|
|
|
733
722
|
test("returns null for skills path traversal attempt (../../etc/passwd)", () => {
|
|
734
|
-
const resolver = new DefaultPathResolver(
|
|
735
|
-
undefined,
|
|
736
|
-
"/home/user/.vellum/workspace",
|
|
737
|
-
);
|
|
723
|
+
const resolver = new DefaultPathResolver(WORKSPACE_DIR);
|
|
738
724
|
expect(resolver.resolve("skills/../../etc/passwd")).toBeNull();
|
|
739
725
|
});
|
|
740
726
|
|
|
741
727
|
test("returns null for skills path traversal attempt (../../../.ssh/authorized_keys)", () => {
|
|
742
|
-
const resolver = new DefaultPathResolver(
|
|
743
|
-
undefined,
|
|
744
|
-
"/home/user/.vellum/workspace",
|
|
745
|
-
);
|
|
728
|
+
const resolver = new DefaultPathResolver(WORKSPACE_DIR);
|
|
746
729
|
expect(resolver.resolve("skills/../../../.ssh/authorized_keys")).toBeNull();
|
|
747
730
|
});
|
|
748
731
|
|
|
@@ -9,11 +9,8 @@
|
|
|
9
9
|
* - Integration: existing routes are unaffected by the new endpoint
|
|
10
10
|
*/
|
|
11
11
|
import { createHash } from "node:crypto";
|
|
12
|
-
import { mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
14
|
-
import { join } from "node:path";
|
|
15
12
|
import { gzipSync } from "node:zlib";
|
|
16
|
-
import {
|
|
13
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
17
14
|
|
|
18
15
|
/** Convert a Uint8Array to an ArrayBuffer for BodyInit compatibility. */
|
|
19
16
|
function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
@@ -24,22 +21,6 @@ function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
|
24
21
|
) as ArrayBuffer;
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
const testDir = realpathSync(
|
|
28
|
-
mkdtempSync(join(tmpdir(), "migration-validate-http-test-")),
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
mock.module("../util/platform.js", () => ({
|
|
32
|
-
getRootDir: () => testDir,
|
|
33
|
-
getDataDir: () => testDir,
|
|
34
|
-
isMacOS: () => process.platform === "darwin",
|
|
35
|
-
isLinux: () => process.platform === "linux",
|
|
36
|
-
isWindows: () => process.platform === "win32",
|
|
37
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
38
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
39
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
40
|
-
ensureDataDir: () => {},
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
24
|
mock.module("../util/logger.js", () => ({
|
|
44
25
|
getLogger: () =>
|
|
45
26
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -73,14 +54,6 @@ mock.module("../config/env.js", () => ({
|
|
|
73
54
|
import { validateVBundle } from "../runtime/migrations/vbundle-validator.js";
|
|
74
55
|
import { handleMigrationValidate } from "../runtime/routes/migration-routes.js";
|
|
75
56
|
|
|
76
|
-
afterAll(() => {
|
|
77
|
-
try {
|
|
78
|
-
rmSync(testDir, { recursive: true });
|
|
79
|
-
} catch {
|
|
80
|
-
/* best effort */
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
57
|
// ---------------------------------------------------------------------------
|
|
85
58
|
// Tar archive builder helpers
|
|
86
59
|
// ---------------------------------------------------------------------------
|