@vellumai/assistant 0.8.10 → 0.8.11-staging.1
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/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- package/src/plugins/plugin-skill-contributions.ts +0 -292
|
@@ -55,8 +55,12 @@ function catalog(ref: string, names: string[]): PluginCatalog {
|
|
|
55
55
|
ref,
|
|
56
56
|
matches: names.map((name) => ({
|
|
57
57
|
name,
|
|
58
|
-
path: `
|
|
59
|
-
source: {
|
|
58
|
+
path: `github:acme/${name}@${"0".repeat(40)}`,
|
|
59
|
+
source: {
|
|
60
|
+
kind: "github" as const,
|
|
61
|
+
repo: `acme/${name}`,
|
|
62
|
+
ref: "0".repeat(40),
|
|
63
|
+
},
|
|
60
64
|
})),
|
|
61
65
|
};
|
|
62
66
|
}
|
|
@@ -33,7 +33,7 @@ function fileEntry(name: string, downloadUrl: string): ContentEntry {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
interface FixtureConfig {
|
|
36
|
-
/** Manifest object served at `
|
|
36
|
+
/** Manifest object served at `plugins/marketplace.json`. Omit for 404. */
|
|
37
37
|
marketplace?: unknown;
|
|
38
38
|
/** Directory listings keyed by `<owner>/<repo>[/<path>]`. Missing key → 404. */
|
|
39
39
|
listings?: Record<string, ContentEntry[]>;
|
|
@@ -124,8 +124,7 @@ describe("getPluginDetails", () => {
|
|
|
124
124
|
},
|
|
125
125
|
],
|
|
126
126
|
},
|
|
127
|
-
// AND
|
|
128
|
-
// root lists a README and package.json
|
|
127
|
+
// AND the external repo root lists a README and package.json
|
|
129
128
|
listings: {
|
|
130
129
|
"example-org/caveman": [
|
|
131
130
|
fileEntry("README.md", "raw://caveman/readme"),
|
|
@@ -190,9 +189,7 @@ describe("getPluginDetails", () => {
|
|
|
190
189
|
}
|
|
191
190
|
if (url.includes("/contents")) {
|
|
192
191
|
const ref = new URL(url).searchParams.get("ref") ?? "";
|
|
193
|
-
const key = url.includes("example-org/caveman")
|
|
194
|
-
? "external"
|
|
195
|
-
: "first-party";
|
|
192
|
+
const key = url.includes("example-org/caveman") ? "external" : "other";
|
|
196
193
|
contentsRefs.set(key, ref);
|
|
197
194
|
if (key === "external") {
|
|
198
195
|
return new Response(
|
|
@@ -218,17 +215,15 @@ describe("getPluginDetails", () => {
|
|
|
218
215
|
expect(contentsRefs.get("external")).toBe(
|
|
219
216
|
"2222222222222222222222222222222222222222",
|
|
220
217
|
);
|
|
221
|
-
// AND the
|
|
222
|
-
//
|
|
223
|
-
expect(contentsRefs.has("
|
|
218
|
+
// AND the pinned external repo is the only contents request — no other
|
|
219
|
+
// GitHub lookups are made for the name
|
|
220
|
+
expect(contentsRefs.has("other")).toBe(false);
|
|
224
221
|
// AND the README from the pinned ref is surfaced
|
|
225
222
|
expect(details.readme).toContain("Caveman");
|
|
226
223
|
});
|
|
227
224
|
|
|
228
|
-
test("
|
|
229
|
-
// GIVEN a marketplace entry for "simple-memory"
|
|
230
|
-
// AND a same-named in-repo directory — caveman-style, this is the plugin's
|
|
231
|
-
// adapter stub, not a standalone first-party plugin
|
|
225
|
+
test("resolves the external marketplace source for an uninstalled plugin", async () => {
|
|
226
|
+
// GIVEN a marketplace entry for "simple-memory" and no installed copy
|
|
232
227
|
const fetch = makeFetch({
|
|
233
228
|
marketplace: {
|
|
234
229
|
name: "vellum",
|
|
@@ -257,8 +252,7 @@ describe("getPluginDetails", () => {
|
|
|
257
252
|
{ fetch, workspacePluginsDir: workspace },
|
|
258
253
|
);
|
|
259
254
|
|
|
260
|
-
// THEN the external marketplace source is used
|
|
261
|
-
// adapter overlay for the clone, so it does not override the claim
|
|
255
|
+
// THEN the external marketplace source is used
|
|
262
256
|
expect(details.source).toEqual({
|
|
263
257
|
kind: "github",
|
|
264
258
|
repo: "example-org/simple-memory",
|
|
@@ -314,7 +308,7 @@ describe("getPluginDetails", () => {
|
|
|
314
308
|
});
|
|
315
309
|
|
|
316
310
|
test("throws PluginDetailsNotFoundError when nothing claims the name", async () => {
|
|
317
|
-
// GIVEN no
|
|
311
|
+
// GIVEN no installed copy and an empty marketplace
|
|
318
312
|
const fetch = makeFetch({
|
|
319
313
|
marketplace: { name: "vellum", plugins: [] },
|
|
320
314
|
});
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "../plugin-marketplace.js";
|
|
17
17
|
|
|
18
18
|
const MANIFEST_URL_PREFIX =
|
|
19
|
-
"https://api.github.com/repos/vellum-ai/vellum-assistant/contents/
|
|
19
|
+
"https://api.github.com/repos/vellum-ai/vellum-assistant/contents/plugins/marketplace.json";
|
|
20
20
|
|
|
21
21
|
// External marketplace refs must be full commit SHAs (immutable). Fixtures use
|
|
22
22
|
// realistic 40-char hex object names rather than tags/branches.
|
|
@@ -264,7 +264,7 @@ describe("resolveMarketplaceSource", () => {
|
|
|
264
264
|
test("returns null for a name not in the whitelist", () => {
|
|
265
265
|
// GIVEN whitelisted entries
|
|
266
266
|
// WHEN resolving an unknown name
|
|
267
|
-
// THEN no source is returned (
|
|
267
|
+
// THEN no source is returned (the name is not installable)
|
|
268
268
|
expect(resolveMarketplaceSource("unknown", entries)).toBeNull();
|
|
269
269
|
});
|
|
270
270
|
});
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for {@link searchPlugins}.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The catalog is the whitelisted external plugins in the curated
|
|
5
|
+
* `plugins/marketplace.json` manifest. Network is replaced with an in-memory
|
|
6
|
+
* fixture passed via the `fetch` dependency — no globals are monkey-patched and
|
|
7
|
+
* no `--test-hook` exports leak into production code.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import { describe, expect, test } from "bun:test";
|
|
10
11
|
|
|
12
|
+
import { MarketplaceFetchError } from "../plugin-marketplace.js";
|
|
11
13
|
import {
|
|
12
14
|
type FetchLike,
|
|
13
15
|
InvalidSearchPatternError,
|
|
@@ -15,52 +17,64 @@ import {
|
|
|
15
17
|
searchPlugins,
|
|
16
18
|
} from "../search-plugins.js";
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
const MANIFEST_URL_PREFIX =
|
|
21
|
+
"https://api.github.com/repos/vellum-ai/vellum-assistant/contents/plugins/marketplace.json";
|
|
22
|
+
|
|
23
|
+
// External marketplace refs must be full commit SHAs (immutable). Fixtures use
|
|
24
|
+
// realistic 40-char hex object names rather than tags/branches.
|
|
25
|
+
const SHA_A = "63a91ecadbf4c4719a4602a5abb00883f9966034";
|
|
26
|
+
const SHA_B = "0123456789abcdef0123456789abcdef01234567";
|
|
27
|
+
const SHA_C = "89abcdef0123456789abcdef0123456789abcdef";
|
|
28
|
+
|
|
29
|
+
interface ManifestPlugin {
|
|
30
|
+
name: string;
|
|
31
|
+
source: { source: "github"; repo: string; path?: string; ref: string };
|
|
32
|
+
description?: string;
|
|
33
|
+
}
|
|
31
34
|
|
|
35
|
+
/** Serve `plugins` as the raw marketplace manifest at the manifest URL. */
|
|
36
|
+
function manifestFetch(plugins: ManifestPlugin[]): FetchLike {
|
|
32
37
|
return (async (input: RequestInfo | URL) => {
|
|
33
38
|
const url = typeof input === "string" ? input : input.toString();
|
|
34
|
-
if (!url.startsWith(
|
|
39
|
+
if (!url.startsWith(MANIFEST_URL_PREFIX)) {
|
|
35
40
|
return new Response("unexpected url: " + url, { status: 500 });
|
|
36
41
|
}
|
|
37
|
-
|
|
38
|
-
name,
|
|
39
|
-
path: `experimental/plugins/${name}`,
|
|
40
|
-
type,
|
|
41
|
-
size: type === "file" ? 1 : 0,
|
|
42
|
-
download_url:
|
|
43
|
-
type === "file"
|
|
44
|
-
? `https://raw.githubusercontent.com/vellum-ai/vellum-assistant/main/experimental/plugins/${name}`
|
|
45
|
-
: null,
|
|
46
|
-
}));
|
|
47
|
-
return new Response(JSON.stringify(body), {
|
|
42
|
+
return new Response(JSON.stringify({ name: "vellum-assistant", plugins }), {
|
|
48
43
|
status: 200,
|
|
49
44
|
headers: { "content-type": "application/json" },
|
|
50
45
|
});
|
|
51
46
|
}) as FetchLike;
|
|
52
47
|
}
|
|
53
48
|
|
|
49
|
+
/** A github-sourced manifest entry, with optional path/description. */
|
|
50
|
+
function entry(
|
|
51
|
+
name: string,
|
|
52
|
+
repo: string,
|
|
53
|
+
ref: string,
|
|
54
|
+
extra?: { path?: string; description?: string },
|
|
55
|
+
): ManifestPlugin {
|
|
56
|
+
return {
|
|
57
|
+
name,
|
|
58
|
+
source: {
|
|
59
|
+
source: "github",
|
|
60
|
+
repo,
|
|
61
|
+
...(extra?.path ? { path: extra.path } : {}),
|
|
62
|
+
ref,
|
|
63
|
+
},
|
|
64
|
+
...(extra?.description ? { description: extra.description } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
54
68
|
describe("searchPlugins", () => {
|
|
55
|
-
test("matches the query as a case-insensitive regex against
|
|
69
|
+
test("matches the query as a case-insensitive regex against plugin names", async () => {
|
|
56
70
|
const result = await searchPlugins(
|
|
57
71
|
{ query: "memory" },
|
|
58
72
|
{
|
|
59
|
-
fetch:
|
|
60
|
-
"simple-memory"
|
|
61
|
-
"memory-graph"
|
|
62
|
-
"git-tools"
|
|
63
|
-
|
|
73
|
+
fetch: manifestFetch([
|
|
74
|
+
entry("simple-memory", "vellum-ai/simple-memory", SHA_A),
|
|
75
|
+
entry("memory-graph", "acme/memory-graph", SHA_B),
|
|
76
|
+
entry("git-tools", "acme/git-tools", SHA_C),
|
|
77
|
+
]),
|
|
64
78
|
},
|
|
65
79
|
);
|
|
66
80
|
|
|
@@ -68,7 +82,7 @@ describe("searchPlugins", () => {
|
|
|
68
82
|
"memory-graph",
|
|
69
83
|
"simple-memory",
|
|
70
84
|
]);
|
|
71
|
-
expect(result.matches[0]!.path).toBe(
|
|
85
|
+
expect(result.matches[0]!.path).toBe(`github:acme/memory-graph@${SHA_B}`);
|
|
72
86
|
expect(result.query).toBe("memory");
|
|
73
87
|
expect(result.ref).toBe("main");
|
|
74
88
|
});
|
|
@@ -76,7 +90,11 @@ describe("searchPlugins", () => {
|
|
|
76
90
|
test("matches regardless of query casing (case-insensitive)", async () => {
|
|
77
91
|
const result = await searchPlugins(
|
|
78
92
|
{ query: "MEMORY" },
|
|
79
|
-
{
|
|
93
|
+
{
|
|
94
|
+
fetch: manifestFetch([
|
|
95
|
+
entry("simple-memory", "vellum-ai/simple-memory", SHA_A),
|
|
96
|
+
]),
|
|
97
|
+
},
|
|
80
98
|
);
|
|
81
99
|
expect(result.matches.map((m) => m.name)).toEqual(["simple-memory"]);
|
|
82
100
|
});
|
|
@@ -85,24 +103,24 @@ describe("searchPlugins", () => {
|
|
|
85
103
|
const result = await searchPlugins(
|
|
86
104
|
{ query: "^memory-" },
|
|
87
105
|
{
|
|
88
|
-
fetch:
|
|
89
|
-
"memory-graph"
|
|
90
|
-
"simple-memory"
|
|
91
|
-
|
|
106
|
+
fetch: manifestFetch([
|
|
107
|
+
entry("memory-graph", "acme/memory-graph", SHA_B),
|
|
108
|
+
entry("simple-memory", "vellum-ai/simple-memory", SHA_A),
|
|
109
|
+
]),
|
|
92
110
|
},
|
|
93
111
|
);
|
|
94
112
|
expect(result.matches.map((m) => m.name)).toEqual(["memory-graph"]);
|
|
95
113
|
});
|
|
96
114
|
|
|
97
|
-
test("empty query matches
|
|
115
|
+
test("empty query matches every whitelisted entry", async () => {
|
|
98
116
|
const result = await searchPlugins(
|
|
99
117
|
{ query: "" },
|
|
100
118
|
{
|
|
101
|
-
fetch:
|
|
102
|
-
"simple-memory"
|
|
103
|
-
"memory-graph"
|
|
104
|
-
"git-tools"
|
|
105
|
-
|
|
119
|
+
fetch: manifestFetch([
|
|
120
|
+
entry("simple-memory", "vellum-ai/simple-memory", SHA_A),
|
|
121
|
+
entry("memory-graph", "acme/memory-graph", SHA_B),
|
|
122
|
+
entry("git-tools", "acme/git-tools", SHA_C),
|
|
123
|
+
]),
|
|
106
124
|
},
|
|
107
125
|
);
|
|
108
126
|
expect(result.matches.map((m) => m.name)).toEqual([
|
|
@@ -112,19 +130,35 @@ describe("searchPlugins", () => {
|
|
|
112
130
|
]);
|
|
113
131
|
});
|
|
114
132
|
|
|
115
|
-
test("
|
|
133
|
+
test("projects each entry onto a github source with a display locator", async () => {
|
|
134
|
+
// GIVEN a whitelisted external plugin nested in a subdirectory
|
|
135
|
+
// WHEN we search
|
|
116
136
|
const result = await searchPlugins(
|
|
117
|
-
{ query: "" },
|
|
137
|
+
{ query: "nested" },
|
|
118
138
|
{
|
|
119
|
-
fetch:
|
|
120
|
-
"
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
139
|
+
fetch: manifestFetch([
|
|
140
|
+
entry("nested", "acme/monorepo", SHA_B, {
|
|
141
|
+
path: "packages/nested",
|
|
142
|
+
description: "A nested plugin.",
|
|
143
|
+
}),
|
|
144
|
+
]),
|
|
125
145
|
},
|
|
126
146
|
);
|
|
127
|
-
|
|
147
|
+
|
|
148
|
+
// THEN the match carries the github coordinates and a human locator
|
|
149
|
+
expect(result.matches).toEqual([
|
|
150
|
+
{
|
|
151
|
+
name: "nested",
|
|
152
|
+
path: `github:acme/monorepo/packages/nested@${SHA_B}`,
|
|
153
|
+
description: "A nested plugin.",
|
|
154
|
+
source: {
|
|
155
|
+
kind: "github",
|
|
156
|
+
repo: "acme/monorepo",
|
|
157
|
+
path: "packages/nested",
|
|
158
|
+
ref: SHA_B,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
]);
|
|
128
162
|
});
|
|
129
163
|
|
|
130
164
|
test("rejects invalid regex patterns up front (no network call)", async () => {
|
|
@@ -143,7 +177,11 @@ describe("searchPlugins", () => {
|
|
|
143
177
|
test("empty result set on no matches", async () => {
|
|
144
178
|
const result = await searchPlugins(
|
|
145
179
|
{ query: "nothing-matches" },
|
|
146
|
-
{
|
|
180
|
+
{
|
|
181
|
+
fetch: manifestFetch([
|
|
182
|
+
entry("simple-memory", "vellum-ai/simple-memory", SHA_A),
|
|
183
|
+
]),
|
|
184
|
+
},
|
|
147
185
|
);
|
|
148
186
|
expect(result.matches).toEqual([]);
|
|
149
187
|
});
|
|
@@ -161,15 +199,12 @@ describe("searchPlugins", () => {
|
|
|
161
199
|
const m = /[?&]ref=([^&]+)/.exec(url);
|
|
162
200
|
seenRef = m ? decodeURIComponent(m[1]!) : undefined;
|
|
163
201
|
return new Response(
|
|
164
|
-
JSON.stringify(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
download_url: null,
|
|
171
|
-
},
|
|
172
|
-
]),
|
|
202
|
+
JSON.stringify({
|
|
203
|
+
name: "vellum-assistant",
|
|
204
|
+
plugins: [
|
|
205
|
+
entry("simple-memory", "vellum-ai/simple-memory", SHA_A),
|
|
206
|
+
],
|
|
207
|
+
}),
|
|
173
208
|
{ status: 200, headers: { "content-type": "application/json" } },
|
|
174
209
|
);
|
|
175
210
|
}) as FetchLike,
|
|
@@ -233,32 +268,46 @@ describe("searchPlugins", () => {
|
|
|
233
268
|
expect((err as Error).message).toMatch(/HTTP 403/);
|
|
234
269
|
});
|
|
235
270
|
|
|
236
|
-
test("
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
const err = await searchPlugins(
|
|
271
|
+
test("a missing manifest (404) is a clean empty catalog", async () => {
|
|
272
|
+
// Unlike a 5xx or a bare 403, a 404 on `marketplace.json` means no
|
|
273
|
+
// whitelist has been published at this ref yet — a normal empty catalog,
|
|
274
|
+
// not a misconfiguration. The search returns no matches without error.
|
|
275
|
+
const result = await searchPlugins(
|
|
242
276
|
{ query: "memory" },
|
|
243
277
|
{
|
|
244
278
|
fetch: (async () =>
|
|
245
279
|
new Response("not found", { status: 404 })) as FetchLike,
|
|
246
280
|
},
|
|
281
|
+
);
|
|
282
|
+
expect(result.matches).toEqual([]);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("a malformed manifest is a hard error (not silently empty)", async () => {
|
|
286
|
+
// GIVEN the manifest body is not valid JSON
|
|
287
|
+
// WHEN we search
|
|
288
|
+
// THEN it surfaces as a hard MarketplaceFetchError — the catalog is the
|
|
289
|
+
// source of truth, so a broken whitelist must not masquerade as empty,
|
|
290
|
+
// and it is NOT transient (retrying won't fix malformed bytes).
|
|
291
|
+
const err = await searchPlugins(
|
|
292
|
+
{ query: "" },
|
|
293
|
+
{
|
|
294
|
+
fetch: (async () =>
|
|
295
|
+
new Response("{ not json", { status: 200 })) as FetchLike,
|
|
296
|
+
},
|
|
247
297
|
).catch((e: unknown) => e);
|
|
248
|
-
expect(err).toBeInstanceOf(
|
|
298
|
+
expect(err).toBeInstanceOf(MarketplaceFetchError);
|
|
249
299
|
expect(err).not.toBeInstanceOf(PluginCatalogUnavailableError);
|
|
250
|
-
expect((err as Error).message).toMatch(/HTTP 404/);
|
|
251
300
|
});
|
|
252
301
|
|
|
253
302
|
test("returns matches sorted by name", async () => {
|
|
254
303
|
const result = await searchPlugins(
|
|
255
304
|
{ query: "" },
|
|
256
305
|
{
|
|
257
|
-
fetch:
|
|
258
|
-
"zeta-plugin"
|
|
259
|
-
"alpha-plugin"
|
|
260
|
-
"mu-plugin"
|
|
261
|
-
|
|
306
|
+
fetch: manifestFetch([
|
|
307
|
+
entry("zeta-plugin", "acme/zeta", SHA_A),
|
|
308
|
+
entry("alpha-plugin", "acme/alpha", SHA_B),
|
|
309
|
+
entry("mu-plugin", "acme/mu", SHA_C),
|
|
310
|
+
]),
|
|
262
311
|
},
|
|
263
312
|
);
|
|
264
313
|
expect(result.matches.map((m) => m.name)).toEqual([
|
|
@@ -268,177 +317,33 @@ describe("searchPlugins", () => {
|
|
|
268
317
|
]);
|
|
269
318
|
});
|
|
270
319
|
|
|
271
|
-
test("
|
|
272
|
-
// GIVEN
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
name: "caveman",
|
|
279
|
-
source: {
|
|
280
|
-
source: "github",
|
|
281
|
-
repo: "JuliusBrussee/caveman",
|
|
282
|
-
ref: "63a91ecadbf4c4719a4602a5abb00883f9966034",
|
|
283
|
-
},
|
|
284
|
-
description: "Ultra-compressed communication mode.",
|
|
285
|
-
},
|
|
286
|
-
],
|
|
287
|
-
};
|
|
288
|
-
const fetch: FetchLike = (async (input: RequestInfo | URL) => {
|
|
289
|
-
const url = typeof input === "string" ? input : input.toString();
|
|
290
|
-
if (url.includes("marketplace.json")) {
|
|
291
|
-
return new Response(JSON.stringify(manifest), { status: 200 });
|
|
292
|
-
}
|
|
293
|
-
return new Response(
|
|
294
|
-
JSON.stringify([
|
|
295
|
-
{
|
|
296
|
-
name: "simple-memory",
|
|
297
|
-
path: "experimental/plugins/simple-memory",
|
|
298
|
-
type: "dir",
|
|
299
|
-
size: 0,
|
|
300
|
-
download_url: null,
|
|
301
|
-
},
|
|
302
|
-
]),
|
|
303
|
-
{ status: 200, headers: { "content-type": "application/json" } },
|
|
304
|
-
);
|
|
305
|
-
}) as FetchLike;
|
|
306
|
-
|
|
307
|
-
// WHEN we search the catalog with a match-all query
|
|
308
|
-
const result = await searchPlugins({ query: "" }, { fetch });
|
|
309
|
-
|
|
310
|
-
// THEN both sources appear, sorted by name, each tagged with its origin
|
|
311
|
-
expect(result.matches).toEqual([
|
|
312
|
-
{
|
|
313
|
-
name: "caveman",
|
|
314
|
-
path: "github:JuliusBrussee/caveman@63a91ecadbf4c4719a4602a5abb00883f9966034",
|
|
315
|
-
description: "Ultra-compressed communication mode.",
|
|
316
|
-
source: {
|
|
317
|
-
kind: "github",
|
|
318
|
-
repo: "JuliusBrussee/caveman",
|
|
319
|
-
ref: "63a91ecadbf4c4719a4602a5abb00883f9966034",
|
|
320
|
-
},
|
|
321
|
-
},
|
|
320
|
+
test("dedupes entries that repeat a name, keeping the first", async () => {
|
|
321
|
+
// GIVEN a manifest that lists the same name twice (e.g. a bad edit)
|
|
322
|
+
// WHEN we search
|
|
323
|
+
// THEN the name surfaces once, from the first entry, so a duplicate can't
|
|
324
|
+
// inflate the catalog or shadow the reviewed source
|
|
325
|
+
const result = await searchPlugins(
|
|
326
|
+
{ query: "caveman" },
|
|
322
327
|
{
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
},
|
|
327
|
-
]);
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
test("filters marketplace entries by the query too", async () => {
|
|
331
|
-
// GIVEN a marketplace whitelisting an external plugin and no first-party dirs
|
|
332
|
-
const manifest = {
|
|
333
|
-
name: "vellum-assistant",
|
|
334
|
-
plugins: [
|
|
335
|
-
{
|
|
336
|
-
name: "caveman",
|
|
337
|
-
source: {
|
|
338
|
-
source: "github",
|
|
339
|
-
repo: "JuliusBrussee/caveman",
|
|
340
|
-
ref: "63a91ecadbf4c4719a4602a5abb00883f9966034",
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
|
-
],
|
|
344
|
-
};
|
|
345
|
-
const fetch: FetchLike = (async (input: RequestInfo | URL) => {
|
|
346
|
-
const url = typeof input === "string" ? input : input.toString();
|
|
347
|
-
if (url.includes("marketplace.json")) {
|
|
348
|
-
return new Response(JSON.stringify(manifest), { status: 200 });
|
|
349
|
-
}
|
|
350
|
-
return new Response(JSON.stringify([]), { status: 200 });
|
|
351
|
-
}) as FetchLike;
|
|
352
|
-
|
|
353
|
-
// WHEN the query matches no plugin name
|
|
354
|
-
const result = await searchPlugins({ query: "memory" }, { fetch });
|
|
355
|
-
|
|
356
|
-
// THEN the non-matching marketplace entry is excluded
|
|
357
|
-
expect(result.matches).toEqual([]);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
test("a marketplace entry owns a name shared by an in-repo stub dir", async () => {
|
|
361
|
-
// GIVEN a marketplace entry named "caveman"
|
|
362
|
-
const manifest = {
|
|
363
|
-
name: "vellum-assistant",
|
|
364
|
-
plugins: [
|
|
365
|
-
{
|
|
366
|
-
name: "caveman",
|
|
367
|
-
source: {
|
|
368
|
-
source: "github",
|
|
369
|
-
repo: "JuliusBrussee/caveman",
|
|
370
|
-
ref: "63a91ecadbf4c4719a4602a5abb00883f9966034",
|
|
371
|
-
},
|
|
372
|
-
},
|
|
373
|
-
],
|
|
374
|
-
};
|
|
375
|
-
// AND a same-named in-repo directory, which is caveman's adapter stub
|
|
376
|
-
// rather than a standalone first-party plugin
|
|
377
|
-
const fetch: FetchLike = (async (input: RequestInfo | URL) => {
|
|
378
|
-
const url = typeof input === "string" ? input : input.toString();
|
|
379
|
-
if (url.includes("marketplace.json")) {
|
|
380
|
-
return new Response(JSON.stringify(manifest), { status: 200 });
|
|
381
|
-
}
|
|
382
|
-
return new Response(
|
|
383
|
-
JSON.stringify([
|
|
384
|
-
{
|
|
385
|
-
name: "caveman",
|
|
386
|
-
path: "experimental/plugins/caveman",
|
|
387
|
-
type: "dir",
|
|
388
|
-
size: 0,
|
|
389
|
-
download_url: null,
|
|
390
|
-
},
|
|
328
|
+
fetch: manifestFetch([
|
|
329
|
+
entry("caveman", "JuliusBrussee/caveman", SHA_A),
|
|
330
|
+
entry("caveman", "impostor/caveman", SHA_B),
|
|
391
331
|
]),
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}) as FetchLike;
|
|
395
|
-
|
|
396
|
-
// WHEN we search
|
|
397
|
-
const result = await searchPlugins({ query: "caveman" }, { fetch });
|
|
398
|
-
|
|
399
|
-
// THEN the name surfaces once, as the external marketplace entry — the
|
|
400
|
-
// stub dir is its adapter overlay, not a competing first-party listing
|
|
332
|
+
},
|
|
333
|
+
);
|
|
401
334
|
expect(result.matches).toEqual([
|
|
402
335
|
{
|
|
403
336
|
name: "caveman",
|
|
404
|
-
path:
|
|
337
|
+
path: `github:JuliusBrussee/caveman@${SHA_A}`,
|
|
405
338
|
source: {
|
|
406
339
|
kind: "github",
|
|
407
340
|
repo: "JuliusBrussee/caveman",
|
|
408
|
-
ref:
|
|
341
|
+
ref: SHA_A,
|
|
409
342
|
},
|
|
410
343
|
},
|
|
411
344
|
]);
|
|
412
345
|
});
|
|
413
346
|
|
|
414
|
-
test("a broken marketplace manifest degrades to the first-party listing", async () => {
|
|
415
|
-
// GIVEN the manifest is malformed but the first-party listing is healthy
|
|
416
|
-
const fetch: FetchLike = (async (input: RequestInfo | URL) => {
|
|
417
|
-
const url = typeof input === "string" ? input : input.toString();
|
|
418
|
-
if (url.includes("marketplace.json")) {
|
|
419
|
-
return new Response("{ not json", { status: 200 });
|
|
420
|
-
}
|
|
421
|
-
return new Response(
|
|
422
|
-
JSON.stringify([
|
|
423
|
-
{
|
|
424
|
-
name: "simple-memory",
|
|
425
|
-
path: "experimental/plugins/simple-memory",
|
|
426
|
-
type: "dir",
|
|
427
|
-
size: 0,
|
|
428
|
-
download_url: null,
|
|
429
|
-
},
|
|
430
|
-
]),
|
|
431
|
-
{ status: 200 },
|
|
432
|
-
);
|
|
433
|
-
}) as FetchLike;
|
|
434
|
-
|
|
435
|
-
// WHEN we search
|
|
436
|
-
const result = await searchPlugins({ query: "" }, { fetch });
|
|
437
|
-
|
|
438
|
-
// THEN the core catalog is unaffected by the broken whitelist
|
|
439
|
-
expect(result.matches.map((m) => m.name)).toEqual(["simple-memory"]);
|
|
440
|
-
});
|
|
441
|
-
|
|
442
347
|
test("sends no Authorization header (canonical source is a public repo)", async () => {
|
|
443
348
|
let seenAuth: string | undefined;
|
|
444
349
|
let seenUserAgent: string | undefined;
|
|
@@ -449,10 +354,10 @@ describe("searchPlugins", () => {
|
|
|
449
354
|
const headers = init?.headers as Record<string, string> | undefined;
|
|
450
355
|
seenAuth = headers?.Authorization;
|
|
451
356
|
seenUserAgent = headers?.["User-Agent"];
|
|
452
|
-
return new Response(
|
|
453
|
-
|
|
454
|
-
headers: { "content-type": "application/json" },
|
|
455
|
-
|
|
357
|
+
return new Response(
|
|
358
|
+
JSON.stringify({ name: "vellum-assistant", plugins: [] }),
|
|
359
|
+
{ status: 200, headers: { "content-type": "application/json" } },
|
|
360
|
+
);
|
|
456
361
|
}) as FetchLike,
|
|
457
362
|
},
|
|
458
363
|
);
|