@vellumai/assistant 0.9.0 → 0.10.0-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +18 -34
- 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 +976 -63
- 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__/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__/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__/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-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-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -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-usage.test.ts +65 -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__/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-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__/skill-execute-input.test.ts +51 -1
- 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__/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 +49 -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/{__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 +197 -4
- 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__/merge-plugin-tree.test.ts +443 -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 +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -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/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/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/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 +27 -14
- package/src/daemon/conversation.ts +66 -14
- 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/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/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__/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-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/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/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/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__/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/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -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/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -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 +181 -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 +52 -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 +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -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 +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- 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 +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +10 -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 +2 -3
- 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 +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- 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 +9 -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/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,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for {@link detectPluginSurfaces}.
|
|
3
|
+
*
|
|
4
|
+
* Surface detection is a pure walk of an installed plugin's on-disk tree, so
|
|
5
|
+
* the fixtures materialize the `hooks/`, `tools/`, and `skills/` directory
|
|
6
|
+
* conventions in a real temp dir and assert the derived listing matches what
|
|
7
|
+
* the runtime loader / skills catalog would discover.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
14
|
+
|
|
15
|
+
import { detectPluginSurfaces } from "../plugin-surfaces.js";
|
|
16
|
+
|
|
17
|
+
let pluginDir: string;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
pluginDir = mkdtempSync(join(tmpdir(), "plugin-surfaces-"));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
rmSync(pluginDir, { recursive: true, force: true });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/** Create `<pluginDir>/<rel>` with empty contents, making parents as needed. */
|
|
28
|
+
function touch(rel: string): void {
|
|
29
|
+
const path = join(pluginDir, rel);
|
|
30
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
31
|
+
writeFileSync(path, "");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("detectPluginSurfaces", () => {
|
|
35
|
+
test("lists hooks, tools, and skills from the directory conventions", () => {
|
|
36
|
+
// GIVEN a plugin shipping two hooks, one tool, and two skills
|
|
37
|
+
touch("hooks/post-model-call.ts");
|
|
38
|
+
touch("hooks/init.ts");
|
|
39
|
+
touch("tools/summarize.ts");
|
|
40
|
+
touch("skills/first-skill/SKILL.md");
|
|
41
|
+
touch("skills/second-skill/SKILL.md");
|
|
42
|
+
|
|
43
|
+
// WHEN its surfaces are detected
|
|
44
|
+
const surfaces = detectPluginSurfaces(pluginDir);
|
|
45
|
+
|
|
46
|
+
// THEN each surface type lists its items, sorted
|
|
47
|
+
expect(surfaces.hooks).toEqual(["init", "post-model-call"]);
|
|
48
|
+
expect(surfaces.tools).toEqual(["summarize"]);
|
|
49
|
+
expect(surfaces.skills).toEqual(["first-skill", "second-skill"]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("reports tools under their registered name, not the raw filename", () => {
|
|
53
|
+
// GIVEN a tool whose filename is not already a valid tool-name segment
|
|
54
|
+
touch("tools/create-issue.ts");
|
|
55
|
+
|
|
56
|
+
// WHEN its surfaces are detected
|
|
57
|
+
const surfaces = detectPluginSurfaces(pluginDir);
|
|
58
|
+
|
|
59
|
+
// THEN the derived (registered) name is reported, matching the loader
|
|
60
|
+
expect(surfaces.tools).toEqual(["create_issue"]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("omits surface types the plugin does not contribute", () => {
|
|
64
|
+
// GIVEN a plugin that ships only a hook
|
|
65
|
+
touch("hooks/post-model-call.ts");
|
|
66
|
+
|
|
67
|
+
// WHEN its surfaces are detected
|
|
68
|
+
const surfaces = detectPluginSurfaces(pluginDir);
|
|
69
|
+
|
|
70
|
+
// THEN the contributed type is listed and the others are empty
|
|
71
|
+
expect(surfaces.hooks).toEqual(["post-model-call"]);
|
|
72
|
+
expect(surfaces.tools).toEqual([]);
|
|
73
|
+
expect(surfaces.skills).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("prefers .js over .ts for the same basename and skips .d.ts declarations", () => {
|
|
77
|
+
// GIVEN a compiled hook shipping both .ts and .js plus a .d.ts declaration
|
|
78
|
+
touch("hooks/post-model-call.ts");
|
|
79
|
+
touch("hooks/post-model-call.js");
|
|
80
|
+
touch("hooks/post-model-call.d.ts");
|
|
81
|
+
|
|
82
|
+
// WHEN its surfaces are detected
|
|
83
|
+
const surfaces = detectPluginSurfaces(pluginDir);
|
|
84
|
+
|
|
85
|
+
// THEN the basename appears once and the declaration file is ignored
|
|
86
|
+
expect(surfaces.hooks).toEqual(["post-model-call"]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("ignores skill directories without a SKILL.md", () => {
|
|
90
|
+
// GIVEN a skills dir with one real skill and one stray subdirectory
|
|
91
|
+
touch("skills/real-skill/SKILL.md");
|
|
92
|
+
touch("skills/not-a-skill/README.md");
|
|
93
|
+
|
|
94
|
+
// WHEN its surfaces are detected
|
|
95
|
+
const surfaces = detectPluginSurfaces(pluginDir);
|
|
96
|
+
|
|
97
|
+
// THEN only the directory carrying a SKILL.md is reported
|
|
98
|
+
expect(surfaces.skills).toEqual(["real-skill"]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("returns empty surfaces for a plugin with no surface directories", () => {
|
|
102
|
+
// GIVEN a plugin tree with only a package.json
|
|
103
|
+
touch("package.json");
|
|
104
|
+
|
|
105
|
+
// WHEN its surfaces are detected
|
|
106
|
+
const surfaces = detectPluginSurfaces(pluginDir);
|
|
107
|
+
|
|
108
|
+
// THEN every surface type is empty
|
|
109
|
+
expect(surfaces).toEqual({ skills: [], hooks: [], tools: [] });
|
|
110
|
+
});
|
|
111
|
+
});
|