@vellumai/assistant 0.9.0 → 0.10.0-dev.202606192226.8ed8a6a
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 +19 -35
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +1128 -119
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +6 -0
- package/src/__tests__/approval-routes-http.test.ts +128 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compaction-events.test.ts +6 -0
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-process-callsite.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/conversation-queue.test.ts +6 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-slash-queue.test.ts +6 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
- package/src/__tests__/conversation-speed-override.test.ts +6 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-pressure-tools.test.ts +41 -1
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/document-tool-security.test.ts +9 -6
- package/src/__tests__/document-update-default-surface.test.ts +202 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/gemini-count-tokens.test.ts +70 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-disk-pressure.test.ts +10 -5
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/secret-response-routing.test.ts +35 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
- package/src/__tests__/skill-execute-input.test.ts +177 -1
- package/src/__tests__/skill-load-tool.test.ts +72 -0
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +75 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/db/status.ts +37 -3
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +400 -14
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +31 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +128 -19
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-marketplace.ts +11 -0
- package/src/cli/lib/plugin-pin-history.ts +257 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +53 -19
- package/src/daemon/conversation.ts +131 -20
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/memory-v2-startup.test.ts +131 -0
- package/src/daemon/memory-v2-startup.ts +100 -1
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/daemon/wake-conversation-ops.ts +1 -0
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/db-logs-attach.test.ts +122 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-async-query.ts +20 -2
- package/src/memory/db-connection.ts +49 -5
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/permissions/secret-prompter.ts +15 -1
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/compaction/window-manager.ts +18 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/anthropic/client.ts +230 -175
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/call-site-routing.ts +10 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/gemini/client.ts +43 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/ratelimit.ts +9 -0
- package/src/providers/retry.ts +62 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/types.ts +16 -0
- package/src/providers/usage-tracking.ts +7 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +227 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +71 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +126 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +35 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +383 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-routes.ts +40 -9
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +366 -11
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +15 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +30 -6
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +99 -2
- package/src/tools/skills/load.ts +43 -2
- package/src/tools/subagent/spawn.ts +64 -15
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +10 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/logs-db-path.ts +22 -0
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for {@link mergePluginTree}.
|
|
3
|
+
*
|
|
4
|
+
* The merge is exercised against three real temp directories (base/ours/theirs)
|
|
5
|
+
* and the merged tree is read back from a fourth (dest). `git merge-file` runs
|
|
6
|
+
* for real — these tests assert the line-level merge behavior (non-conflicting
|
|
7
|
+
* hunks from both sides survive; conflicts resolve toward `ours`/`theirs` or
|
|
8
|
+
* get conflict markers under `assistant`), the file-level add/delete
|
|
9
|
+
* resolution, and binary whole-file handling.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
mkdirSync,
|
|
14
|
+
mkdtempSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
rmSync,
|
|
17
|
+
writeFileSync,
|
|
18
|
+
} from "node:fs";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
22
|
+
|
|
23
|
+
import { mergePluginTree } from "../merge-plugin-tree.js";
|
|
24
|
+
|
|
25
|
+
/** A file tree keyed by POSIX-relative path; Buffer values are binary files. */
|
|
26
|
+
type Tree = Record<string, string | Buffer>;
|
|
27
|
+
|
|
28
|
+
let scratch: string;
|
|
29
|
+
let baseDir: string;
|
|
30
|
+
let oursDir: string;
|
|
31
|
+
let theirsDir: string;
|
|
32
|
+
let destDir: string;
|
|
33
|
+
|
|
34
|
+
function writeTree(root: string, tree: Tree): void {
|
|
35
|
+
for (const [rel, content] of Object.entries(tree)) {
|
|
36
|
+
const abs = join(root, rel);
|
|
37
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
38
|
+
writeFileSync(abs, content);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Read a merged file as UTF-8, or null when absent from the dest tree. */
|
|
43
|
+
function read(rel: string): string | null {
|
|
44
|
+
const abs = join(destDir, rel);
|
|
45
|
+
try {
|
|
46
|
+
return readFileSync(abs, "utf-8");
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
scratch = mkdtempSync(join(tmpdir(), "merge-plugin-tree-"));
|
|
54
|
+
baseDir = join(scratch, "base");
|
|
55
|
+
oursDir = join(scratch, "ours");
|
|
56
|
+
theirsDir = join(scratch, "theirs");
|
|
57
|
+
destDir = join(scratch, "dest");
|
|
58
|
+
for (const d of [baseDir, oursDir, theirsDir, destDir]) {
|
|
59
|
+
mkdirSync(d, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("mergePluginTree", () => {
|
|
68
|
+
test("keeps non-conflicting edits from both sides and resolves conflicts toward ours", async () => {
|
|
69
|
+
// GIVEN a file edited on disjoint lines by each side, a true conflict, and
|
|
70
|
+
// a one-sided addition on each side
|
|
71
|
+
writeTree(baseDir, {
|
|
72
|
+
"common.txt": "a\nb\nc\n",
|
|
73
|
+
"conflict.txt": "base\n",
|
|
74
|
+
});
|
|
75
|
+
writeTree(oursDir, {
|
|
76
|
+
"common.txt": "A\nb\nc\n",
|
|
77
|
+
"conflict.txt": "ours\n",
|
|
78
|
+
"local-only.txt": "local\n",
|
|
79
|
+
});
|
|
80
|
+
writeTree(theirsDir, {
|
|
81
|
+
"common.txt": "a\nb\nC\n",
|
|
82
|
+
"conflict.txt": "theirs\n",
|
|
83
|
+
"remote-only.txt": "remote\n",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// WHEN merged with the `ours` strategy
|
|
87
|
+
const count = await mergePluginTree({
|
|
88
|
+
baseDir,
|
|
89
|
+
oursDir,
|
|
90
|
+
theirsDir,
|
|
91
|
+
destDir,
|
|
92
|
+
strategy: "ours",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// THEN both disjoint edits survive, both additions land, and the conflict
|
|
96
|
+
// resolves toward the local edit
|
|
97
|
+
expect(read("common.txt")).toBe("A\nb\nC\n");
|
|
98
|
+
expect(read("local-only.txt")).toBe("local\n");
|
|
99
|
+
expect(read("remote-only.txt")).toBe("remote\n");
|
|
100
|
+
expect(read("conflict.txt")).toBe("ours\n");
|
|
101
|
+
expect(count.fileCount).toBe(4);
|
|
102
|
+
expect(count.conflicts).toEqual([]);
|
|
103
|
+
expect(count.binaryConflicts).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("resolves conflicts toward the pin under the theirs strategy", async () => {
|
|
107
|
+
// GIVEN a file edited differently on both sides
|
|
108
|
+
writeTree(baseDir, { "conflict.txt": "base\n" });
|
|
109
|
+
writeTree(oursDir, { "conflict.txt": "ours\n" });
|
|
110
|
+
writeTree(theirsDir, { "conflict.txt": "theirs\n" });
|
|
111
|
+
|
|
112
|
+
// WHEN merged with the `theirs` strategy
|
|
113
|
+
await mergePluginTree({
|
|
114
|
+
baseDir,
|
|
115
|
+
oursDir,
|
|
116
|
+
theirsDir,
|
|
117
|
+
destDir,
|
|
118
|
+
strategy: "theirs",
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// THEN the conflicting hunk resolves toward the pin
|
|
122
|
+
expect(read("conflict.txt")).toBe("theirs\n");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("never writes conflict markers", async () => {
|
|
126
|
+
// GIVEN a hard conflict on every line
|
|
127
|
+
writeTree(baseDir, { "f.txt": "1\n2\n3\n" });
|
|
128
|
+
writeTree(oursDir, { "f.txt": "1\nOURS\n3\n" });
|
|
129
|
+
writeTree(theirsDir, { "f.txt": "1\nTHEIRS\n3\n" });
|
|
130
|
+
|
|
131
|
+
// WHEN merged
|
|
132
|
+
await mergePluginTree({
|
|
133
|
+
baseDir,
|
|
134
|
+
oursDir,
|
|
135
|
+
theirsDir,
|
|
136
|
+
destDir,
|
|
137
|
+
strategy: "ours",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// THEN the result carries no git conflict markers
|
|
141
|
+
const merged = read("f.txt") ?? "";
|
|
142
|
+
expect(merged).not.toContain("<<<<<<<");
|
|
143
|
+
expect(merged).not.toContain("=======");
|
|
144
|
+
expect(merged).not.toContain(">>>>>>>");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("drops a file deleted upstream when it is unchanged locally", async () => {
|
|
148
|
+
// GIVEN a file present at base and locally but removed at the pin
|
|
149
|
+
writeTree(baseDir, { "gone.txt": "keep\n", "stay.txt": "x\n" });
|
|
150
|
+
writeTree(oursDir, { "gone.txt": "keep\n", "stay.txt": "x\n" });
|
|
151
|
+
writeTree(theirsDir, { "stay.txt": "x\n" });
|
|
152
|
+
|
|
153
|
+
// WHEN merged
|
|
154
|
+
await mergePluginTree({
|
|
155
|
+
baseDir,
|
|
156
|
+
oursDir,
|
|
157
|
+
theirsDir,
|
|
158
|
+
destDir,
|
|
159
|
+
strategy: "theirs",
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// THEN the upstream deletion is honored
|
|
163
|
+
expect(read("gone.txt")).toBeNull();
|
|
164
|
+
expect(read("stay.txt")).toBe("x\n");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("keeps a locally-edited file the pin deleted under the ours strategy", async () => {
|
|
168
|
+
// GIVEN a modify/delete conflict: edited locally, deleted at the pin
|
|
169
|
+
writeTree(baseDir, { "f.txt": "base\n" });
|
|
170
|
+
writeTree(oursDir, { "f.txt": "local edit\n" });
|
|
171
|
+
writeTree(theirsDir, {});
|
|
172
|
+
|
|
173
|
+
// WHEN merged with `ours`
|
|
174
|
+
await mergePluginTree({
|
|
175
|
+
baseDir,
|
|
176
|
+
oursDir,
|
|
177
|
+
theirsDir,
|
|
178
|
+
destDir,
|
|
179
|
+
strategy: "ours",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// THEN the local edit wins over the deletion
|
|
183
|
+
expect(read("f.txt")).toBe("local edit\n");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("honors the pin's deletion of a locally-edited file under the theirs strategy", async () => {
|
|
187
|
+
// GIVEN the same modify/delete conflict
|
|
188
|
+
writeTree(baseDir, { "f.txt": "base\n" });
|
|
189
|
+
writeTree(oursDir, { "f.txt": "local edit\n" });
|
|
190
|
+
writeTree(theirsDir, {});
|
|
191
|
+
|
|
192
|
+
// WHEN merged with `theirs`
|
|
193
|
+
await mergePluginTree({
|
|
194
|
+
baseDir,
|
|
195
|
+
oursDir,
|
|
196
|
+
theirsDir,
|
|
197
|
+
destDir,
|
|
198
|
+
strategy: "theirs",
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// THEN the deletion wins over the local edit
|
|
202
|
+
expect(read("f.txt")).toBeNull();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("resolves a binary conflict whole-file by strategy rather than line-merging", async () => {
|
|
206
|
+
// GIVEN a binary file (NUL bytes) diverged on both sides
|
|
207
|
+
const base = Buffer.from([0x00, 0x01, 0x02]);
|
|
208
|
+
const ours = Buffer.from([0x00, 0xaa, 0xbb]);
|
|
209
|
+
const theirs = Buffer.from([0x00, 0xcc, 0xdd]);
|
|
210
|
+
writeTree(baseDir, { "img.bin": base });
|
|
211
|
+
writeTree(oursDir, { "img.bin": ours });
|
|
212
|
+
writeTree(theirsDir, { "img.bin": theirs });
|
|
213
|
+
|
|
214
|
+
// WHEN merged with `theirs`
|
|
215
|
+
await mergePluginTree({
|
|
216
|
+
baseDir,
|
|
217
|
+
oursDir,
|
|
218
|
+
theirsDir,
|
|
219
|
+
destDir,
|
|
220
|
+
strategy: "theirs",
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// THEN the whole pin blob is taken, byte-for-byte (no markers, no corruption)
|
|
224
|
+
expect(readFileSync(join(destDir, "img.bin")).equals(theirs)).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("keeps a one-sided binary change rather than resolving by strategy", async () => {
|
|
228
|
+
// GIVEN a binary file the pin updated but the local install left untouched
|
|
229
|
+
const base = Buffer.from([0x00, 0x01, 0x02]);
|
|
230
|
+
const theirs = Buffer.from([0x00, 0xcc, 0xdd]);
|
|
231
|
+
writeTree(baseDir, { "img.bin": base });
|
|
232
|
+
writeTree(oursDir, { "img.bin": base });
|
|
233
|
+
writeTree(theirsDir, { "img.bin": theirs });
|
|
234
|
+
|
|
235
|
+
// WHEN merged with `ours` (which only governs true conflicts)
|
|
236
|
+
await mergePluginTree({
|
|
237
|
+
baseDir,
|
|
238
|
+
oursDir,
|
|
239
|
+
theirsDir,
|
|
240
|
+
destDir,
|
|
241
|
+
strategy: "ours",
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// THEN the upstream-only update survives instead of being dropped
|
|
245
|
+
expect(readFileSync(join(destDir, "img.bin")).equals(theirs)).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("keeps a one-sided local binary edit under the theirs strategy", async () => {
|
|
249
|
+
// GIVEN a binary file edited locally but unchanged in the pin
|
|
250
|
+
const base = Buffer.from([0x00, 0x01, 0x02]);
|
|
251
|
+
const ours = Buffer.from([0x00, 0xaa, 0xbb]);
|
|
252
|
+
writeTree(baseDir, { "img.bin": base });
|
|
253
|
+
writeTree(oursDir, { "img.bin": ours });
|
|
254
|
+
writeTree(theirsDir, { "img.bin": base });
|
|
255
|
+
|
|
256
|
+
// WHEN merged with `theirs` (which only governs true conflicts)
|
|
257
|
+
await mergePluginTree({
|
|
258
|
+
baseDir,
|
|
259
|
+
oursDir,
|
|
260
|
+
theirsDir,
|
|
261
|
+
destDir,
|
|
262
|
+
strategy: "theirs",
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// THEN the local-only edit survives instead of being reverted to the pin
|
|
266
|
+
expect(readFileSync(join(destDir, "img.bin")).equals(ours)).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("excludes the provenance sidecar from the merge", async () => {
|
|
270
|
+
// GIVEN an install-meta sidecar present on every side with differing content
|
|
271
|
+
writeTree(baseDir, {
|
|
272
|
+
"install-meta.json": '{"commit":"base"}',
|
|
273
|
+
"f.txt": "x\n",
|
|
274
|
+
});
|
|
275
|
+
writeTree(oursDir, {
|
|
276
|
+
"install-meta.json": '{"commit":"ours"}',
|
|
277
|
+
"f.txt": "x\n",
|
|
278
|
+
});
|
|
279
|
+
writeTree(theirsDir, {
|
|
280
|
+
"install-meta.json": '{"commit":"theirs"}',
|
|
281
|
+
"f.txt": "x\n",
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// WHEN merged
|
|
285
|
+
const count = await mergePluginTree({
|
|
286
|
+
baseDir,
|
|
287
|
+
oursDir,
|
|
288
|
+
theirsDir,
|
|
289
|
+
destDir,
|
|
290
|
+
strategy: "ours",
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// THEN the sidecar is never carried through (the caller rewrites it)
|
|
294
|
+
expect(read("install-meta.json")).toBeNull();
|
|
295
|
+
expect(count.fileCount).toBe(1);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("merges files in nested directories", async () => {
|
|
299
|
+
// GIVEN a conflict on a deeply-nested file
|
|
300
|
+
writeTree(baseDir, { "src/a/b.txt": "base\n" });
|
|
301
|
+
writeTree(oursDir, { "src/a/b.txt": "ours\n" });
|
|
302
|
+
writeTree(theirsDir, { "src/a/b.txt": "theirs\n" });
|
|
303
|
+
|
|
304
|
+
// WHEN merged
|
|
305
|
+
await mergePluginTree({
|
|
306
|
+
baseDir,
|
|
307
|
+
oursDir,
|
|
308
|
+
theirsDir,
|
|
309
|
+
destDir,
|
|
310
|
+
strategy: "ours",
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// THEN the nested path is preserved and resolved
|
|
314
|
+
expect(read("src/a/b.txt")).toBe("ours\n");
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("writes git conflict markers and reports the path under the assistant strategy", async () => {
|
|
318
|
+
// GIVEN a file edited on disjoint lines by each side plus one true conflict
|
|
319
|
+
writeTree(baseDir, { "common.txt": "a\nb\nc\n", "conflict.txt": "base\n" });
|
|
320
|
+
writeTree(oursDir, { "common.txt": "A\nb\nc\n", "conflict.txt": "ours\n" });
|
|
321
|
+
writeTree(theirsDir, {
|
|
322
|
+
"common.txt": "a\nb\nC\n",
|
|
323
|
+
"conflict.txt": "theirs\n",
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// WHEN merged with the `assistant` strategy
|
|
327
|
+
const result = await mergePluginTree({
|
|
328
|
+
baseDir,
|
|
329
|
+
oursDir,
|
|
330
|
+
theirsDir,
|
|
331
|
+
destDir,
|
|
332
|
+
strategy: "assistant",
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// THEN the non-conflicting edits still auto-merge and only the true
|
|
336
|
+
// conflict carries markers naming both sides, and its path is reported
|
|
337
|
+
expect(read("common.txt")).toBe("A\nb\nC\n");
|
|
338
|
+
const conflict = read("conflict.txt") ?? "";
|
|
339
|
+
expect(conflict).toContain("<<<<<<<");
|
|
340
|
+
expect(conflict).toContain("=======");
|
|
341
|
+
expect(conflict).toContain(">>>>>>>");
|
|
342
|
+
expect(conflict).toContain("ours\n");
|
|
343
|
+
expect(conflict).toContain("theirs\n");
|
|
344
|
+
expect(result.conflicts).toEqual(["conflict.txt"]);
|
|
345
|
+
expect(result.binaryConflicts).toEqual([]);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test("uses the supplied conflict labels in the markers", async () => {
|
|
349
|
+
// GIVEN a true conflict and custom marker labels
|
|
350
|
+
writeTree(baseDir, { "f.txt": "base\n" });
|
|
351
|
+
writeTree(oursDir, { "f.txt": "ours\n" });
|
|
352
|
+
writeTree(theirsDir, { "f.txt": "theirs\n" });
|
|
353
|
+
|
|
354
|
+
// WHEN merged with the `assistant` strategy and explicit labels
|
|
355
|
+
await mergePluginTree({
|
|
356
|
+
baseDir,
|
|
357
|
+
oursDir,
|
|
358
|
+
theirsDir,
|
|
359
|
+
destDir,
|
|
360
|
+
strategy: "assistant",
|
|
361
|
+
conflictLabels: {
|
|
362
|
+
ours: "local edits (was abc1234)",
|
|
363
|
+
base: "install baseline abc1234",
|
|
364
|
+
theirs: "upgrade pin def5678",
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// THEN the marker lines carry those labels
|
|
369
|
+
const merged = read("f.txt") ?? "";
|
|
370
|
+
expect(merged).toContain("<<<<<<< local edits (was abc1234)");
|
|
371
|
+
expect(merged).toContain(">>>>>>> upgrade pin def5678");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("auto-merges with no markers when the assistant strategy hits no true conflict", async () => {
|
|
375
|
+
// GIVEN edits on disjoint lines of the same file (no overlapping hunk)
|
|
376
|
+
writeTree(baseDir, { "f.txt": "a\nb\nc\n" });
|
|
377
|
+
writeTree(oursDir, { "f.txt": "A\nb\nc\n" });
|
|
378
|
+
writeTree(theirsDir, { "f.txt": "a\nb\nC\n" });
|
|
379
|
+
|
|
380
|
+
// WHEN merged with the `assistant` strategy
|
|
381
|
+
const result = await mergePluginTree({
|
|
382
|
+
baseDir,
|
|
383
|
+
oursDir,
|
|
384
|
+
theirsDir,
|
|
385
|
+
destDir,
|
|
386
|
+
strategy: "assistant",
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// THEN both edits merge cleanly with no markers and nothing is reported
|
|
390
|
+
const merged = read("f.txt") ?? "";
|
|
391
|
+
expect(merged).toBe("A\nb\nC\n");
|
|
392
|
+
expect(merged).not.toContain("<<<<<<<");
|
|
393
|
+
expect(result.conflicts).toEqual([]);
|
|
394
|
+
expect(result.binaryConflicts).toEqual([]);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test("keeps the local copy and reports a binary conflict under the assistant strategy", async () => {
|
|
398
|
+
// GIVEN a binary file (NUL bytes) diverged on both sides
|
|
399
|
+
const base = Buffer.from([0x00, 0x01, 0x02]);
|
|
400
|
+
const ours = Buffer.from([0x00, 0xaa, 0xbb]);
|
|
401
|
+
const theirs = Buffer.from([0x00, 0xcc, 0xdd]);
|
|
402
|
+
writeTree(baseDir, { "img.bin": base });
|
|
403
|
+
writeTree(oursDir, { "img.bin": ours });
|
|
404
|
+
writeTree(theirsDir, { "img.bin": theirs });
|
|
405
|
+
|
|
406
|
+
// WHEN merged with the `assistant` strategy
|
|
407
|
+
const result = await mergePluginTree({
|
|
408
|
+
baseDir,
|
|
409
|
+
oursDir,
|
|
410
|
+
theirsDir,
|
|
411
|
+
destDir,
|
|
412
|
+
strategy: "assistant",
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// THEN the local copy is kept byte-for-byte (markers are impossible) and
|
|
416
|
+
// the path is surfaced as a binary conflict, not a marker conflict
|
|
417
|
+
expect(readFileSync(join(destDir, "img.bin")).equals(ours)).toBe(true);
|
|
418
|
+
expect(result.binaryConflicts).toEqual(["img.bin"]);
|
|
419
|
+
expect(result.conflicts).toEqual([]);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test("keeps the surviving content and reports a modify/delete conflict under the assistant strategy", async () => {
|
|
423
|
+
// GIVEN a file edited locally but deleted at the pin
|
|
424
|
+
writeTree(baseDir, { "f.txt": "base\n" });
|
|
425
|
+
writeTree(oursDir, { "f.txt": "local edit\n" });
|
|
426
|
+
writeTree(theirsDir, {});
|
|
427
|
+
|
|
428
|
+
// WHEN merged with the `assistant` strategy
|
|
429
|
+
const result = await mergePluginTree({
|
|
430
|
+
baseDir,
|
|
431
|
+
oursDir,
|
|
432
|
+
theirsDir,
|
|
433
|
+
destDir,
|
|
434
|
+
strategy: "assistant",
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// THEN the local edit is preserved (never silently dropped) and the
|
|
438
|
+
// modify/delete divergence is reported for resolution
|
|
439
|
+
expect(read("f.txt")).toBe("local edit\n");
|
|
440
|
+
expect(result.conflicts).toEqual(["f.txt"]);
|
|
441
|
+
expect(result.binaryConflicts).toEqual([]);
|
|
442
|
+
});
|
|
443
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for {@link listPinHistory} and {@link resolvePinToMarketplaceCommit}.
|
|
3
|
+
*
|
|
4
|
+
* The marketplace manifest stores only the current pin, so pin history is
|
|
5
|
+
* reconstructed by walking the manifest's commit history and reading the
|
|
6
|
+
* plugin's pin at each commit. The fixtures model that timeline with an
|
|
7
|
+
* in-memory `fetch`: a commits-list endpoint plus a manifest-at-ref endpoint
|
|
8
|
+
* keyed by commit SHA.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, expect, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
import type { FetchLike } from "../install-from-github.js";
|
|
14
|
+
import {
|
|
15
|
+
listPinHistory,
|
|
16
|
+
resolvePinToMarketplaceCommit,
|
|
17
|
+
} from "../plugin-pin-history.js";
|
|
18
|
+
|
|
19
|
+
const PIN_A = "a".repeat(40);
|
|
20
|
+
const PIN_B = "b".repeat(40);
|
|
21
|
+
const PIN_C = "c".repeat(40);
|
|
22
|
+
|
|
23
|
+
// Marketplace-manifest commits, newest → oldest. `C2B` touches the manifest
|
|
24
|
+
// without changing this plugin's pin (a bump for some other plugin), so it must
|
|
25
|
+
// collapse into the same distinct pin as `C3`.
|
|
26
|
+
const C3 = "3".repeat(40);
|
|
27
|
+
const C2B = "d".repeat(40);
|
|
28
|
+
const C2 = "2".repeat(40);
|
|
29
|
+
const C1 = "1".repeat(40);
|
|
30
|
+
|
|
31
|
+
const COMMIT_DATES: Record<string, string> = {
|
|
32
|
+
[C3]: "2026-06-10T00:00:00.000Z",
|
|
33
|
+
[C2B]: "2026-06-08T00:00:00.000Z",
|
|
34
|
+
[C2]: "2026-06-05T00:00:00.000Z",
|
|
35
|
+
[C1]: "2026-06-01T00:00:00.000Z",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Plugin pin resolved from the manifest at each ref (`main` = current tip). */
|
|
39
|
+
const PIN_BY_REF: Record<string, string> = {
|
|
40
|
+
main: PIN_C,
|
|
41
|
+
[C3]: PIN_C,
|
|
42
|
+
[C2B]: PIN_C,
|
|
43
|
+
[C2]: PIN_B,
|
|
44
|
+
[C1]: PIN_A,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function manifest(pin: string): string {
|
|
48
|
+
return JSON.stringify({
|
|
49
|
+
name: "vellum",
|
|
50
|
+
plugins: [
|
|
51
|
+
{
|
|
52
|
+
name: "level-up",
|
|
53
|
+
source: { source: "github", repo: "example-org/level-up", ref: pin },
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build a `fetch` serving the commits list and the manifest at each ref.
|
|
61
|
+
* `commits` is the newest-first commit list the API returns.
|
|
62
|
+
*/
|
|
63
|
+
function makeFetch(commits: string[] = [C3, C2B, C2, C1]): FetchLike {
|
|
64
|
+
return (async (input: RequestInfo | URL) => {
|
|
65
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
66
|
+
if (url.includes("/commits?")) {
|
|
67
|
+
const body = commits.map((sha) => ({
|
|
68
|
+
sha,
|
|
69
|
+
commit: { committer: { date: COMMIT_DATES[sha] } },
|
|
70
|
+
}));
|
|
71
|
+
return new Response(JSON.stringify(body), { status: 200 });
|
|
72
|
+
}
|
|
73
|
+
const m = url.match(/contents\/plugins\/marketplace\.json\?ref=([^&]+)/);
|
|
74
|
+
if (m) {
|
|
75
|
+
const ref = decodeURIComponent(m[1]!);
|
|
76
|
+
const pin = PIN_BY_REF[ref];
|
|
77
|
+
if (!pin) return new Response("not found", { status: 404 });
|
|
78
|
+
return new Response(manifest(pin), { status: 200 });
|
|
79
|
+
}
|
|
80
|
+
return new Response("unexpected url: " + url, { status: 500 });
|
|
81
|
+
}) as FetchLike;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
describe("listPinHistory", () => {
|
|
85
|
+
test("lists distinct pins newest-first, collapsing unchanged revisions", async () => {
|
|
86
|
+
// GIVEN a manifest history with three distinct pins (one commit left the
|
|
87
|
+
// plugin's pin unchanged)
|
|
88
|
+
const history = await listPinHistory("level-up", { fetch: makeFetch() });
|
|
89
|
+
|
|
90
|
+
// THEN one entry per distinct pin, newest first, each tagged with the
|
|
91
|
+
// newest marketplace commit that carries it
|
|
92
|
+
expect(history).toEqual([
|
|
93
|
+
{
|
|
94
|
+
pin: PIN_C,
|
|
95
|
+
marketplaceCommit: C3,
|
|
96
|
+
promotedAt: COMMIT_DATES[C3]!,
|
|
97
|
+
current: true,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
pin: PIN_B,
|
|
101
|
+
marketplaceCommit: C2,
|
|
102
|
+
promotedAt: COMMIT_DATES[C2]!,
|
|
103
|
+
current: false,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
pin: PIN_A,
|
|
107
|
+
marketplaceCommit: C1,
|
|
108
|
+
promotedAt: COMMIT_DATES[C1]!,
|
|
109
|
+
current: false,
|
|
110
|
+
},
|
|
111
|
+
]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("honors the limit (newest pins win)", async () => {
|
|
115
|
+
const history = await listPinHistory(
|
|
116
|
+
"level-up",
|
|
117
|
+
{ fetch: makeFetch() },
|
|
118
|
+
{ limit: 2 },
|
|
119
|
+
);
|
|
120
|
+
expect(history.map((e) => e.pin)).toEqual([PIN_C, PIN_B]);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("returns an empty list for a plugin absent from every revision", async () => {
|
|
124
|
+
const history = await listPinHistory("ghost", { fetch: makeFetch() });
|
|
125
|
+
expect(history).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("returns an empty list when the manifest has no commit history", async () => {
|
|
129
|
+
const history = await listPinHistory("level-up", {
|
|
130
|
+
fetch: makeFetch([]),
|
|
131
|
+
});
|
|
132
|
+
expect(history).toEqual([]);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe("resolvePinToMarketplaceCommit", () => {
|
|
137
|
+
test("maps a reviewed pin to the marketplace commit that introduced it", async () => {
|
|
138
|
+
const entry = await resolvePinToMarketplaceCommit("level-up", PIN_B, {
|
|
139
|
+
fetch: makeFetch(),
|
|
140
|
+
});
|
|
141
|
+
expect(entry?.marketplaceCommit).toBe(C2);
|
|
142
|
+
expect(entry?.pin).toBe(PIN_B);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("is case-insensitive on the pin SHA", async () => {
|
|
146
|
+
const entry = await resolvePinToMarketplaceCommit(
|
|
147
|
+
"level-up",
|
|
148
|
+
PIN_B.toUpperCase(),
|
|
149
|
+
{ fetch: makeFetch() },
|
|
150
|
+
);
|
|
151
|
+
expect(entry?.marketplaceCommit).toBe(C2);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("returns null for a pin not in the reviewed history", async () => {
|
|
155
|
+
const entry = await resolvePinToMarketplaceCommit(
|
|
156
|
+
"level-up",
|
|
157
|
+
"f".repeat(40),
|
|
158
|
+
{ fetch: makeFetch() },
|
|
159
|
+
);
|
|
160
|
+
expect(entry).toBeNull();
|
|
161
|
+
});
|
|
162
|
+
});
|