@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 diffPlugin}.
|
|
3
|
+
*
|
|
4
|
+
* A diff re-materializes the recorded install commit through the same pipeline
|
|
5
|
+
* install uses, then compares that baseline tree against the on-disk install.
|
|
6
|
+
* The clone is replaced with a fake {@link GitRunner} that writes a known
|
|
7
|
+
* baseline tree, the adapter-stub lookup is answered 404 by an in-memory
|
|
8
|
+
* `fetch` (so the clone is treated as a raw external tree), and the install
|
|
9
|
+
* target is a real temp directory passed via `workspacePluginsDir` — no globals
|
|
10
|
+
* are patched. Fixtures vary the on-disk tree to exercise modified/added/
|
|
11
|
+
* removed drift, binary content, and the clean case.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { dirname, join } from "node:path";
|
|
18
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
19
|
+
|
|
20
|
+
import { diffPlugin, PluginDiffUnavailableError } from "../diff-plugin.js";
|
|
21
|
+
import {
|
|
22
|
+
type FetchLike,
|
|
23
|
+
type GitRunner,
|
|
24
|
+
PluginNotFoundError,
|
|
25
|
+
} from "../install-from-github.js";
|
|
26
|
+
import { PluginNotInstalledError } from "../uninstall-plugin.js";
|
|
27
|
+
|
|
28
|
+
const SHA_A = "a".repeat(40);
|
|
29
|
+
|
|
30
|
+
/** Bytes for each baseline / on-disk file, keyed by repo-relative POSIX path. */
|
|
31
|
+
type Tree = Record<string, string | Buffer>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Build a `fetch` that answers the GitHub Contents API listing (the adapter
|
|
35
|
+
* stub lookup) with a 404, so the clone is materialized as a raw external tree
|
|
36
|
+
* with no overlay. Anything else surfaces a 500 so test bugs are loud.
|
|
37
|
+
*/
|
|
38
|
+
function makeFetch(): FetchLike {
|
|
39
|
+
return (async (input: RequestInfo | URL) => {
|
|
40
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
41
|
+
if (url.includes("api.github.com")) {
|
|
42
|
+
return new Response("not found", { status: 404 });
|
|
43
|
+
}
|
|
44
|
+
return new Response(`unexpected url: ${url}`, { status: 500 });
|
|
45
|
+
}) as FetchLike;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A fake clone that materializes `files` into the scratch clone dir at `fetch`
|
|
50
|
+
* and reports `commit` at HEAD, mirroring how the real git pipeline stages a
|
|
51
|
+
* tree before it is copied into the destination.
|
|
52
|
+
*/
|
|
53
|
+
function fakeGitRunner(commit: string, files: Tree): GitRunner {
|
|
54
|
+
return async (args, { cwd }) => {
|
|
55
|
+
switch (args[0]) {
|
|
56
|
+
case "fetch": {
|
|
57
|
+
mkdirSync(join(cwd, ".git"), { recursive: true });
|
|
58
|
+
writeFileSync(join(cwd, ".git", "config"), "[core]\n");
|
|
59
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
60
|
+
const abs = join(cwd, rel);
|
|
61
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
62
|
+
writeFileSync(abs, content);
|
|
63
|
+
}
|
|
64
|
+
return { stdout: "" };
|
|
65
|
+
}
|
|
66
|
+
case "rev-parse":
|
|
67
|
+
return { stdout: `${commit}\n` };
|
|
68
|
+
default:
|
|
69
|
+
return { stdout: "" };
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** A git runner that fails the test if any git command runs. */
|
|
75
|
+
const unusedGitRunner: GitRunner = async (args) => {
|
|
76
|
+
throw new Error(`git should not run for this diff: ${args.join(" ")}`);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Per-file SHA-256 digest of a tree, in the {@link Fingerprint} shape install
|
|
81
|
+
* records in `install-meta.json`. Drift is classified against this recorded
|
|
82
|
+
* baseline (as `inspect` does), so each fixture records the digest of its
|
|
83
|
+
* install-time tree.
|
|
84
|
+
*/
|
|
85
|
+
function fingerprintOf(tree: Tree): {
|
|
86
|
+
algorithm: "sha256";
|
|
87
|
+
files: Record<string, string>;
|
|
88
|
+
} {
|
|
89
|
+
const files: Record<string, string> = {};
|
|
90
|
+
for (const [rel, content] of Object.entries(tree)) {
|
|
91
|
+
const buf = typeof content === "string" ? Buffer.from(content) : content;
|
|
92
|
+
files[rel] = createHash("sha256").update(buf).digest("hex");
|
|
93
|
+
}
|
|
94
|
+
return { algorithm: "sha256", files };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Materialize an installed plugin copy with `files` on disk and an optional
|
|
99
|
+
* provenance sidecar. `sidecar: null` writes no `install-meta.json`; a sidecar
|
|
100
|
+
* with `commit: null` records an install that captured no commit. `baseline`
|
|
101
|
+
* is the install-time tree whose fingerprint is recorded — drift is classified
|
|
102
|
+
* against it (as `inspect` does); omit it to record an install with no
|
|
103
|
+
* fingerprint (an older or manually-copied install).
|
|
104
|
+
*/
|
|
105
|
+
function installCopy(
|
|
106
|
+
pluginsDir: string,
|
|
107
|
+
name: string,
|
|
108
|
+
files: Tree,
|
|
109
|
+
sidecar: {
|
|
110
|
+
commit: string | null;
|
|
111
|
+
committedAt?: string;
|
|
112
|
+
baseline?: Tree;
|
|
113
|
+
} | null,
|
|
114
|
+
): void {
|
|
115
|
+
const dir = join(pluginsDir, name);
|
|
116
|
+
mkdirSync(dir, { recursive: true });
|
|
117
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
118
|
+
const abs = join(dir, rel);
|
|
119
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
120
|
+
writeFileSync(abs, content);
|
|
121
|
+
}
|
|
122
|
+
if (sidecar !== null) {
|
|
123
|
+
writeFileSync(
|
|
124
|
+
join(dir, "install-meta.json"),
|
|
125
|
+
JSON.stringify({
|
|
126
|
+
origin: "vellum",
|
|
127
|
+
name,
|
|
128
|
+
source: {
|
|
129
|
+
kind: "github",
|
|
130
|
+
owner: "example-org",
|
|
131
|
+
repo: name,
|
|
132
|
+
ref: SHA_A,
|
|
133
|
+
},
|
|
134
|
+
commit: sidecar.commit,
|
|
135
|
+
committedAt: sidecar.committedAt,
|
|
136
|
+
installedAt: "2026-06-10T12:00:00.000Z",
|
|
137
|
+
fingerprint:
|
|
138
|
+
sidecar.baseline !== undefined
|
|
139
|
+
? fingerprintOf(sidecar.baseline)
|
|
140
|
+
: null,
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let ws: string;
|
|
147
|
+
let pluginsDir: string;
|
|
148
|
+
|
|
149
|
+
beforeEach(() => {
|
|
150
|
+
ws = mkdtempSync(join(tmpdir(), "diff-plugin-"));
|
|
151
|
+
pluginsDir = join(ws, "plugins");
|
|
152
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
afterEach(() => {
|
|
156
|
+
rmSync(ws, { recursive: true, force: true });
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("diffPlugin", () => {
|
|
160
|
+
test("reports a unified diff for a file edited since install", async () => {
|
|
161
|
+
// GIVEN a plugin whose install baseline declares a two-line file
|
|
162
|
+
const baseline: Tree = {
|
|
163
|
+
"package.json": '{"name":"level-up"}',
|
|
164
|
+
"src/skill.ts": "export const a = 1;\nexport const b = 2;\n",
|
|
165
|
+
};
|
|
166
|
+
// AND the on-disk copy edited the second line of that file
|
|
167
|
+
installCopy(
|
|
168
|
+
pluginsDir,
|
|
169
|
+
"level-up",
|
|
170
|
+
{
|
|
171
|
+
"package.json": '{"name":"level-up"}',
|
|
172
|
+
"src/skill.ts": "export const a = 1;\nexport const b = 99;\n",
|
|
173
|
+
},
|
|
174
|
+
{ commit: SHA_A, baseline },
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// WHEN the plugin is diffed against its install commit
|
|
178
|
+
const result = await diffPlugin(
|
|
179
|
+
{ name: "level-up" },
|
|
180
|
+
{
|
|
181
|
+
fetch: makeFetch(),
|
|
182
|
+
runGit: fakeGitRunner(SHA_A, baseline),
|
|
183
|
+
workspacePluginsDir: pluginsDir,
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// THEN the recorded baseline commit is reported as drifted
|
|
188
|
+
expect(result.commit).toBe(SHA_A);
|
|
189
|
+
expect(result.clean).toBe(false);
|
|
190
|
+
// AND exactly the edited file is surfaced as modified
|
|
191
|
+
expect(result.files).toHaveLength(1);
|
|
192
|
+
const [file] = result.files;
|
|
193
|
+
expect(file.path).toBe("src/skill.ts");
|
|
194
|
+
expect(file.status).toBe("modified");
|
|
195
|
+
expect(file.binary).toBe(false);
|
|
196
|
+
expect(file.reconstructed).toBe(true);
|
|
197
|
+
// AND the unified diff shows the old and new line
|
|
198
|
+
expect(file.diff).toContain("-export const b = 2;");
|
|
199
|
+
expect(file.diff).toContain("+export const b = 99;");
|
|
200
|
+
expect(file.diff).toContain("a/src/skill.ts");
|
|
201
|
+
expect(file.diff).toContain("b/src/skill.ts");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("classifies added and removed files against the baseline", async () => {
|
|
205
|
+
// GIVEN a baseline with a single source file besides the manifest
|
|
206
|
+
const baseline: Tree = {
|
|
207
|
+
"package.json": '{"name":"level-up"}',
|
|
208
|
+
"src/old.ts": "export const gone = true;\n",
|
|
209
|
+
};
|
|
210
|
+
// AND an on-disk copy that dropped that file and added a new one
|
|
211
|
+
installCopy(
|
|
212
|
+
pluginsDir,
|
|
213
|
+
"level-up",
|
|
214
|
+
{
|
|
215
|
+
"package.json": '{"name":"level-up"}',
|
|
216
|
+
"src/new.ts": "export const added = true;\n",
|
|
217
|
+
},
|
|
218
|
+
{ commit: SHA_A, baseline },
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// WHEN the plugin is diffed
|
|
222
|
+
const result = await diffPlugin(
|
|
223
|
+
{ name: "level-up" },
|
|
224
|
+
{
|
|
225
|
+
fetch: makeFetch(),
|
|
226
|
+
runGit: fakeGitRunner(SHA_A, baseline),
|
|
227
|
+
workspacePluginsDir: pluginsDir,
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// THEN both the addition and the removal are reported, sorted by path
|
|
232
|
+
expect(result.clean).toBe(false);
|
|
233
|
+
expect(result.files.map((f) => [f.path, f.status])).toEqual([
|
|
234
|
+
["src/new.ts", "added"],
|
|
235
|
+
["src/old.ts", "removed"],
|
|
236
|
+
]);
|
|
237
|
+
// AND each side diffs against /dev/null like git
|
|
238
|
+
const added = result.files[0];
|
|
239
|
+
const removed = result.files[1];
|
|
240
|
+
expect(added.diff).toContain("+export const added = true;");
|
|
241
|
+
expect(added.diff).toContain("/dev/null");
|
|
242
|
+
expect(removed.diff).toContain("-export const gone = true;");
|
|
243
|
+
expect(removed.diff).toContain("/dev/null");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test("reports clean when the on-disk tree matches the baseline", async () => {
|
|
247
|
+
// GIVEN a baseline and an identical on-disk copy
|
|
248
|
+
const tree: Tree = {
|
|
249
|
+
"package.json": '{"name":"level-up"}',
|
|
250
|
+
"src/skill.ts": "export const a = 1;\n",
|
|
251
|
+
};
|
|
252
|
+
installCopy(pluginsDir, "level-up", tree, {
|
|
253
|
+
commit: SHA_A,
|
|
254
|
+
baseline: tree,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// WHEN the plugin is diffed
|
|
258
|
+
const result = await diffPlugin(
|
|
259
|
+
{ name: "level-up" },
|
|
260
|
+
{
|
|
261
|
+
fetch: makeFetch(),
|
|
262
|
+
runGit: fakeGitRunner(SHA_A, tree),
|
|
263
|
+
workspacePluginsDir: pluginsDir,
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// THEN no drift is reported and the file list is empty
|
|
268
|
+
expect(result.clean).toBe(true);
|
|
269
|
+
expect(result.files).toHaveLength(0);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("excludes the provenance sidecar from the diff", async () => {
|
|
273
|
+
// GIVEN a baseline (which never contains install-meta.json) and an on-disk
|
|
274
|
+
// copy identical to it apart from the sidecar install always writes
|
|
275
|
+
const tree: Tree = { "package.json": '{"name":"level-up"}' };
|
|
276
|
+
installCopy(pluginsDir, "level-up", tree, {
|
|
277
|
+
commit: SHA_A,
|
|
278
|
+
baseline: tree,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// WHEN the plugin is diffed
|
|
282
|
+
const result = await diffPlugin(
|
|
283
|
+
{ name: "level-up" },
|
|
284
|
+
{
|
|
285
|
+
fetch: makeFetch(),
|
|
286
|
+
runGit: fakeGitRunner(SHA_A, tree),
|
|
287
|
+
workspacePluginsDir: pluginsDir,
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// THEN the sidecar is not surfaced as a local addition
|
|
292
|
+
expect(result.clean).toBe(true);
|
|
293
|
+
expect(result.files.map((f) => f.path)).not.toContain("install-meta.json");
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("marks a drifted binary file instead of emitting a line diff", async () => {
|
|
297
|
+
// GIVEN a baseline binary blob and an on-disk copy with different bytes
|
|
298
|
+
const baseline: Tree = {
|
|
299
|
+
"package.json": '{"name":"level-up"}',
|
|
300
|
+
"assets/icon.bin": Buffer.from([0x00, 0x01, 0x02, 0x03]),
|
|
301
|
+
};
|
|
302
|
+
installCopy(
|
|
303
|
+
pluginsDir,
|
|
304
|
+
"level-up",
|
|
305
|
+
{
|
|
306
|
+
"package.json": '{"name":"level-up"}',
|
|
307
|
+
"assets/icon.bin": Buffer.from([0x00, 0xff, 0xfe, 0xfd]),
|
|
308
|
+
},
|
|
309
|
+
{ commit: SHA_A, baseline },
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// WHEN the plugin is diffed
|
|
313
|
+
const result = await diffPlugin(
|
|
314
|
+
{ name: "level-up" },
|
|
315
|
+
{
|
|
316
|
+
fetch: makeFetch(),
|
|
317
|
+
runGit: fakeGitRunner(SHA_A, baseline),
|
|
318
|
+
workspacePluginsDir: pluginsDir,
|
|
319
|
+
},
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// THEN the binary file is flagged rather than line-diffed
|
|
323
|
+
expect(result.files).toHaveLength(1);
|
|
324
|
+
const [file] = result.files;
|
|
325
|
+
expect(file.path).toBe("assets/icon.bin");
|
|
326
|
+
expect(file.binary).toBe(true);
|
|
327
|
+
expect(file.reconstructed).toBe(true);
|
|
328
|
+
expect(file.diff).toContain("Binary files differ");
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("flags a file whose install-time baseline cannot be reconstructed", async () => {
|
|
332
|
+
// GIVEN an install whose recorded fingerprint captured one version of a file
|
|
333
|
+
const recorded: Tree = {
|
|
334
|
+
"package.json": '{"name":"level-up"}',
|
|
335
|
+
"src/skill.ts": "export const v = 1;\n",
|
|
336
|
+
};
|
|
337
|
+
// AND an on-disk copy the user edited (drift vs the recorded baseline)
|
|
338
|
+
installCopy(
|
|
339
|
+
pluginsDir,
|
|
340
|
+
"level-up",
|
|
341
|
+
{
|
|
342
|
+
"package.json": '{"name":"level-up"}',
|
|
343
|
+
"src/skill.ts": "export const v = 2;\n",
|
|
344
|
+
},
|
|
345
|
+
{ commit: SHA_A, baseline: recorded },
|
|
346
|
+
);
|
|
347
|
+
// AND a re-materialization that yields DIFFERENT bytes than were recorded
|
|
348
|
+
// (e.g. the curated adapter overlay moved since install), so the baseline
|
|
349
|
+
// bytes cannot be faithfully reconstructed for this file
|
|
350
|
+
const driftedClone: Tree = {
|
|
351
|
+
"package.json": '{"name":"level-up"}',
|
|
352
|
+
"src/skill.ts": "export const v = 3;\n",
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// WHEN the plugin is diffed
|
|
356
|
+
const result = await diffPlugin(
|
|
357
|
+
{ name: "level-up" },
|
|
358
|
+
{
|
|
359
|
+
fetch: makeFetch(),
|
|
360
|
+
runGit: fakeGitRunner(SHA_A, driftedClone),
|
|
361
|
+
workspacePluginsDir: pluginsDir,
|
|
362
|
+
},
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
// THEN the file is still reported as drifted (classified vs the recorded
|
|
366
|
+
// fingerprint, like inspect), but flagged rather than diffed against the
|
|
367
|
+
// fabricated baseline bytes
|
|
368
|
+
expect(result.clean).toBe(false);
|
|
369
|
+
expect(result.files).toHaveLength(1);
|
|
370
|
+
const [file] = result.files;
|
|
371
|
+
expect(file.path).toBe("src/skill.ts");
|
|
372
|
+
expect(file.status).toBe("modified");
|
|
373
|
+
expect(file.reconstructed).toBe(false);
|
|
374
|
+
expect(file.diff).toContain("Baseline unavailable");
|
|
375
|
+
// AND the re-materialized (wrong) bytes are never presented as the baseline
|
|
376
|
+
expect(file.diff).not.toContain("export const v = 3;");
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("throws PluginNotInstalledError when no copy is installed", async () => {
|
|
380
|
+
// GIVEN an empty plugins directory
|
|
381
|
+
// WHEN a plugin that was never installed is diffed
|
|
382
|
+
// THEN it reports the install is missing (git is never reached)
|
|
383
|
+
await expect(
|
|
384
|
+
diffPlugin(
|
|
385
|
+
{ name: "level-up" },
|
|
386
|
+
{
|
|
387
|
+
fetch: makeFetch(),
|
|
388
|
+
runGit: unusedGitRunner,
|
|
389
|
+
workspacePluginsDir: pluginsDir,
|
|
390
|
+
},
|
|
391
|
+
),
|
|
392
|
+
).rejects.toBeInstanceOf(PluginNotInstalledError);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test("throws PluginDiffUnavailableError when the install recorded no commit", async () => {
|
|
396
|
+
// GIVEN an installed copy whose sidecar captured no commit
|
|
397
|
+
installCopy(
|
|
398
|
+
pluginsDir,
|
|
399
|
+
"level-up",
|
|
400
|
+
{ "package.json": '{"name":"level-up"}' },
|
|
401
|
+
{ commit: null },
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
// WHEN the plugin is diffed
|
|
405
|
+
// THEN there is no immutable baseline to re-materialize (git is never run)
|
|
406
|
+
await expect(
|
|
407
|
+
diffPlugin(
|
|
408
|
+
{ name: "level-up" },
|
|
409
|
+
{
|
|
410
|
+
fetch: makeFetch(),
|
|
411
|
+
runGit: unusedGitRunner,
|
|
412
|
+
workspacePluginsDir: pluginsDir,
|
|
413
|
+
},
|
|
414
|
+
),
|
|
415
|
+
).rejects.toBeInstanceOf(PluginDiffUnavailableError);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("throws PluginNotFoundError when the recorded commit yields no tree", async () => {
|
|
419
|
+
// GIVEN an installed copy with a recorded commit and fingerprint (so the
|
|
420
|
+
// baseline-classification guard passes and execution reaches the clone)
|
|
421
|
+
installCopy(
|
|
422
|
+
pluginsDir,
|
|
423
|
+
"level-up",
|
|
424
|
+
{ "package.json": '{"name":"level-up"}' },
|
|
425
|
+
{ commit: SHA_A, baseline: { "package.json": '{"name":"level-up"}' } },
|
|
426
|
+
);
|
|
427
|
+
// AND a clone that materializes no files (the commit/sub-path is gone)
|
|
428
|
+
const emptyClone = fakeGitRunner(SHA_A, {});
|
|
429
|
+
|
|
430
|
+
// WHEN the plugin is diffed
|
|
431
|
+
// THEN the missing baseline is surfaced as not-found
|
|
432
|
+
await expect(
|
|
433
|
+
diffPlugin(
|
|
434
|
+
{ name: "level-up" },
|
|
435
|
+
{
|
|
436
|
+
fetch: makeFetch(),
|
|
437
|
+
runGit: emptyClone,
|
|
438
|
+
workspacePluginsDir: pluginsDir,
|
|
439
|
+
},
|
|
440
|
+
),
|
|
441
|
+
).rejects.toBeInstanceOf(PluginNotFoundError);
|
|
442
|
+
});
|
|
443
|
+
});
|
|
@@ -90,6 +90,8 @@ function installPlugin(
|
|
|
90
90
|
} | null;
|
|
91
91
|
/** Embed a content fingerprint of the materialized tree in the sidecar. */
|
|
92
92
|
fingerprint?: boolean;
|
|
93
|
+
/** Materialize surface directories (`hooks/<name>.ts`, `skills/<id>/SKILL.md`). */
|
|
94
|
+
surfaces?: { hooks?: string[]; tools?: string[]; skills?: string[] };
|
|
93
95
|
} = {},
|
|
94
96
|
): void {
|
|
95
97
|
const dir = join(workspace, name);
|
|
@@ -102,6 +104,18 @@ function installPlugin(
|
|
|
102
104
|
description: "Installed copy.",
|
|
103
105
|
}),
|
|
104
106
|
);
|
|
107
|
+
for (const hook of opts.surfaces?.hooks ?? []) {
|
|
108
|
+
mkdirSync(join(dir, "hooks"), { recursive: true });
|
|
109
|
+
writeFileSync(join(dir, "hooks", `${hook}.ts`), "export default () => {};");
|
|
110
|
+
}
|
|
111
|
+
for (const tool of opts.surfaces?.tools ?? []) {
|
|
112
|
+
mkdirSync(join(dir, "tools"), { recursive: true });
|
|
113
|
+
writeFileSync(join(dir, "tools", `${tool}.ts`), "export default {};");
|
|
114
|
+
}
|
|
115
|
+
for (const skill of opts.surfaces?.skills ?? []) {
|
|
116
|
+
mkdirSync(join(dir, "skills", skill), { recursive: true });
|
|
117
|
+
writeFileSync(join(dir, "skills", skill, "SKILL.md"), `# ${skill}`);
|
|
118
|
+
}
|
|
105
119
|
if (opts.sidecar !== null) {
|
|
106
120
|
const sidecar = opts.sidecar ?? { commit: SHA_A };
|
|
107
121
|
// Mirror install: fingerprint the tree before the sidecar is written so it
|
|
@@ -367,6 +381,46 @@ describe("inspectPlugin", () => {
|
|
|
367
381
|
expect(result.local?.localChanges).toBeNull();
|
|
368
382
|
});
|
|
369
383
|
|
|
384
|
+
test("reports the surfaces an installed copy contributes", async () => {
|
|
385
|
+
// GIVEN an installed plugin shipping hooks and skills but no tools
|
|
386
|
+
installPlugin(workspace, "level-up", {
|
|
387
|
+
sidecar: { commit: SHA_A },
|
|
388
|
+
surfaces: {
|
|
389
|
+
hooks: ["post-model-call", "init"],
|
|
390
|
+
skills: ["second-skill", "first-skill"],
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
const fetch = makeFetch({ marketplace: manifestWith("level-up", SHA_A) });
|
|
394
|
+
|
|
395
|
+
// WHEN it is inspected
|
|
396
|
+
const result = await inspectPlugin(
|
|
397
|
+
{ name: "level-up" },
|
|
398
|
+
{ fetch, workspacePluginsDir: workspace },
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
// THEN the contributed surfaces are listed (sorted) and tools is empty
|
|
402
|
+
expect(result.surfaces).toEqual({
|
|
403
|
+
skills: ["first-skill", "second-skill"],
|
|
404
|
+
hooks: ["init", "post-model-call"],
|
|
405
|
+
tools: [],
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test("leaves surfaces null when the plugin is not installed", async () => {
|
|
410
|
+
// GIVEN a marketplace entry with no local copy to walk
|
|
411
|
+
const fetch = makeFetch({ marketplace: manifestWith("level-up", SHA_B) });
|
|
412
|
+
|
|
413
|
+
// WHEN it is inspected
|
|
414
|
+
const result = await inspectPlugin(
|
|
415
|
+
{ name: "level-up" },
|
|
416
|
+
{ fetch, workspacePluginsDir: workspace },
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// THEN there is no tree to read surfaces from
|
|
420
|
+
expect(result.installed).toBe(false);
|
|
421
|
+
expect(result.surfaces).toBeNull();
|
|
422
|
+
});
|
|
423
|
+
|
|
370
424
|
test("throws when the plugin is neither installed nor in the marketplace", async () => {
|
|
371
425
|
// GIVEN no local copy and an empty catalog
|
|
372
426
|
const fetch = makeFetch({ marketplace: undefined });
|