@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62
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 +6 -6
- package/bun.lock +2 -2
- package/docs/activation-funnel-telemetry.md +310 -0
- package/examples/plugins/echo/README.md +61 -60
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +273 -121
- package/package.json +2 -2
- package/scripts/sync-llm-catalog.ts +2 -0
- package/src/__tests__/activation-early-marking.test.ts +120 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +23 -8
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +6 -5
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +146 -0
- package/src/__tests__/compaction-events.test.ts +7 -7
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +265 -125
- package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
- package/src/__tests__/conversation-agent-loop.test.ts +342 -623
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-media-retry.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
- package/src/__tests__/conversation-process-callsite.test.ts +15 -15
- package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
- package/src/__tests__/conversation-queue.test.ts +10 -10
- package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +7 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
- package/src/__tests__/conversation-speed-override.test.ts +11 -10
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
- package/src/__tests__/conversation-undo.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
- package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +36 -0
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +1 -1
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +17 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/history-repair-hook.test.ts +5 -1
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/inference-profile-reaper.test.ts +62 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
- package/src/__tests__/injector-background-turn.test.ts +13 -23
- package/src/__tests__/injector-chain.test.ts +268 -44
- package/src/__tests__/injector-disk-pressure.test.ts +210 -52
- package/src/__tests__/injector-document-comments.test.ts +97 -114
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-v3-suppression.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver.test.ts +73 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
- package/src/__tests__/persist-unsendable-image.test.ts +97 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +91 -81
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/published-app-updater.test.ts +138 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
- package/src/__tests__/steer-tool-repair.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-detail.test.ts +25 -7
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +5 -1
- package/src/__tests__/tool-error-hook.test.ts +1 -1
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/web-fetch.test.ts +45 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +188 -28
- package/src/acp/resolve-agent.ts +119 -42
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +403 -251
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/responses/conversation-message.ts +72 -13
- package/src/api/responses/memory-v3-selection-log.ts +19 -10
- package/src/approvals/guardian-request-resolvers.ts +27 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/memory-v3.ts +57 -199
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
- package/src/cli/lib/install-from-github.ts +797 -60
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +416 -0
- package/src/cli/lib/plugin-marketplace.ts +215 -0
- package/src/cli/lib/search-plugins.ts +201 -29
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +9 -3
- package/src/config/acp-schema.ts +1 -7
- package/src/config/bundled-skills/acp/SKILL.md +80 -41
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -3
- package/src/config/feature-flag-registry.json +10 -56
- package/src/config/llm-resolver.ts +39 -7
- package/src/config/loader.ts +5 -22
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
- package/src/config/schemas/call-site-catalog.ts +0 -21
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +17 -3
- package/src/config/schemas/memory-v3.ts +58 -8
- package/src/config/seed-inference-profiles.ts +18 -0
- package/src/context/compactor.ts +22 -4
- package/src/context/post-turn-tool-result-truncation.ts +39 -1
- package/src/context/strip-injections.ts +8 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
- package/src/daemon/conversation-agent-loop.ts +308 -1297
- package/src/daemon/conversation-error.ts +31 -4
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-media-retry.ts +19 -6
- package/src/daemon/conversation-messaging.ts +17 -0
- package/src/daemon/conversation-process.ts +22 -141
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +361 -393
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +165 -11
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +444 -62
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +36 -46
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/persist-unsendable-image.ts +62 -25
- package/src/daemon/process-message.ts +1 -1
- package/src/daemon/server.ts +2 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/wake-conversation-ops.ts +269 -0
- package/src/embedded/plugin-api.ts +2 -2
- package/src/export/__tests__/transcript-formatter.test.ts +5 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/memory/__tests__/activation-session-store.test.ts +41 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
- package/src/memory/activation-session-store.ts +43 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +6 -0
- package/src/memory/embedding-backend.ts +5 -22
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
- package/src/memory/jobs-worker.ts +25 -1
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
- package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/onboarding-events-store.ts +66 -18
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
- package/src/memory/v2/__tests__/page-store.test.ts +22 -0
- package/src/memory/v2/consolidation-job.ts +2 -63
- package/src/memory/v2/types.ts +5 -0
- package/src/messaging/providers/telegram-bot/api.ts +14 -5
- 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 +9 -2
- 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/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +40 -11
- package/src/plugin-api/types.ts +108 -0
- package/src/plugins/defaults/compaction/compact.ts +106 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
- package/src/plugins/defaults/empty-response/package.json +0 -1
- package/src/plugins/defaults/history-repair/package.json +0 -1
- package/src/plugins/defaults/index.ts +156 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
- package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
- package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
- package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
- package/src/plugins/defaults/title-generate/package.json +0 -1
- package/src/plugins/defaults/tool-error/package.json +0 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
- package/src/plugins/external-api.ts +12 -2
- package/src/plugins/pipeline.ts +6 -293
- package/src/plugins/registry.ts +9 -37
- package/src/plugins/types.ts +76 -381
- package/src/plugins/user-loader.ts +30 -127
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/system-prompt.ts +61 -10
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
- package/src/providers/anthropic/client.ts +9 -10
- package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
- package/src/providers/inference/logit-bias.ts +55 -0
- package/src/providers/model-catalog.ts +57 -0
- package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -1
- package/src/providers/retry.ts +57 -7
- package/src/providers/types.ts +6 -0
- package/src/runtime/__tests__/agent-wake.test.ts +555 -691
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +108 -209
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/acp-routes.test.ts +185 -32
- package/src/runtime/routes/acp-routes.ts +328 -30
- package/src/runtime/routes/app-management-routes.ts +3 -0
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +30 -2
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +18 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
- package/src/runtime/routes/memory-v3-routes.ts +65 -305
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/stt-routes.ts +45 -12
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +50 -15
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/services/published-app-updater.ts +30 -8
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
- package/src/telemetry/activation-funnel.ts +167 -0
- package/src/telemetry/types.ts +13 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
- package/src/telemetry/usage-telemetry-reporter.ts +26 -1
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +9 -19
- package/src/tools/acp/list-agents.ts +3 -15
- package/src/tools/acp/spawn.test.ts +173 -202
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/browser/browser-execution.ts +12 -2
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/web-fetch.ts +65 -24
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +32 -5
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
- package/docs/plugins.md +0 -832
- package/examples/plugins/echo/register.ts +0 -143
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
- package/src/__tests__/pipeline-runner.test.ts +0 -554
- package/src/daemon/wake-target-adapter.ts +0 -253
- package/src/memory/v3/__tests__/assign.test.ts +0 -242
- package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
- package/src/memory/v3/__tests__/core.test.ts +0 -39
- package/src/memory/v3/__tests__/health.test.ts +0 -219
- package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
- package/src/memory/v3/__tests__/needle.test.ts +0 -107
- package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
- package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
- package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
- package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
- package/src/memory/v3/__tests__/router.test.ts +0 -332
- package/src/memory/v3/__tests__/selector.test.ts +0 -470
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
- package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
- package/src/memory/v3/__tests__/tree.test.ts +0 -192
- package/src/memory/v3/assign.ts +0 -268
- package/src/memory/v3/capabilities.ts +0 -124
- package/src/memory/v3/core.ts +0 -26
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +0 -314
- package/src/memory/v3/needle.ts +0 -115
- package/src/memory/v3/orchestrate.ts +0 -126
- package/src/memory/v3/page-content.ts +0 -34
- package/src/memory/v3/provider-blocks.ts +0 -26
- package/src/memory/v3/reconcile.ts +0 -523
- package/src/memory/v3/router.ts +0 -190
- package/src/memory/v3/selection-log-store.ts +0 -84
- package/src/memory/v3/selector.ts +0 -226
- package/src/memory/v3/shadow-plugin.ts +0 -411
- package/src/memory/v3/snapshot.ts +0 -209
- package/src/memory/v3/tree.ts +0 -174
- package/src/memory/v3/types.ts +0 -59
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/register.ts +0 -35
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/register.ts +0 -23
- package/src/plugins/defaults/history-repair/register.ts +0 -24
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/title-generate/register.ts +0 -35
- package/src/plugins/defaults/tool-error/register.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
- package/src/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- package/src/util/map-limit.ts +0 -27
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the curated plugin marketplace manifest from the canonical repo.
|
|
3
|
+
*
|
|
4
|
+
* The manifest at `experimental/plugins/marketplace.json` whitelists external
|
|
5
|
+
* ecosystem plugins so they appear in `assistant plugins search` / the web
|
|
6
|
+
* catalog and become installable by name. Its shape is a subset of the
|
|
7
|
+
* Claude Code marketplace manifest
|
|
8
|
+
* (https://code.claude.com/docs/en/plugin-marketplaces) — `name` + `owner` +
|
|
9
|
+
* a `plugins` array where each entry carries a `name` and a `source`. Only
|
|
10
|
+
* `github` sources are resolved today.
|
|
11
|
+
*
|
|
12
|
+
* Like the first-party plugin listing, the manifest is fetched from the repo
|
|
13
|
+
* at a git ref (via the GitHub Contents API) rather than bundled into the
|
|
14
|
+
* assistant build — so the whitelist can grow without shipping a new release.
|
|
15
|
+
* Every external source pins an explicit `ref` that MUST be a full commit SHA:
|
|
16
|
+
* the fetched code is locked to an immutable revision. Tags and branches are
|
|
17
|
+
* rejected because they are mutable — an upstream owner could retag/repoint
|
|
18
|
+
* them to attacker code that the daemon would then `import()` (the install
|
|
19
|
+
* tree is dynamically loaded). A SHA cannot be repointed, so the reviewed
|
|
20
|
+
* manifest fully determines what gets executed.
|
|
21
|
+
*
|
|
22
|
+
* Designed for direct programmatic use with an injected `fetch`, mirroring
|
|
23
|
+
* {@link ./search-plugins} and {@link ./install-from-github}.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
|
|
28
|
+
// Type-only import: keeps the dependency direction one-way at runtime
|
|
29
|
+
// (install-from-github resolves sources *through* this module) while still
|
|
30
|
+
// sharing the injected-`fetch` shape.
|
|
31
|
+
import type { FetchLike } from "./install-from-github.js";
|
|
32
|
+
|
|
33
|
+
/** Canonical location of the marketplace manifest. */
|
|
34
|
+
const MARKETPLACE_SOURCE_OWNER = "vellum-ai";
|
|
35
|
+
const MARKETPLACE_SOURCE_REPO = "vellum-assistant";
|
|
36
|
+
const MARKETPLACE_FILE_PATH = "experimental/plugins/marketplace.json";
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Manifest schema (subset of the Claude Code marketplace schema)
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
/** `owner/repo`, kebab/underscore/dot segments — matches GitHub's slug rules. */
|
|
43
|
+
const REPO_SLUG_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
44
|
+
/** Install name: a single kebab-case path segment (same rule as the CLI). */
|
|
45
|
+
const PLUGIN_NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
|
|
46
|
+
/**
|
|
47
|
+
* Full Git commit SHA — 40 hex chars (SHA-1) or 64 (SHA-256). External
|
|
48
|
+
* marketplace refs must be a complete object name so the install is pinned to
|
|
49
|
+
* an immutable revision; abbreviated SHAs, tags, and branches are all mutable
|
|
50
|
+
* or ambiguous and are rejected.
|
|
51
|
+
*/
|
|
52
|
+
const COMMIT_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
|
|
53
|
+
|
|
54
|
+
const githubSourceSchema = z.object({
|
|
55
|
+
/** Discriminator. Only GitHub sources are resolved today. */
|
|
56
|
+
source: z.literal("github"),
|
|
57
|
+
/** `owner/repo` of the external plugin repository. */
|
|
58
|
+
repo: z.string().regex(REPO_SLUG_RE, "expected an `owner/repo` slug"),
|
|
59
|
+
/**
|
|
60
|
+
* Directory within the repo that holds the plugin root. Omitted / empty
|
|
61
|
+
* means the repository root. Must not escape the repo.
|
|
62
|
+
*/
|
|
63
|
+
path: z
|
|
64
|
+
.string()
|
|
65
|
+
.refine(
|
|
66
|
+
(p) => !p.split(/[/\\]/).some((seg) => seg === ".." || seg === ""),
|
|
67
|
+
"path must be a clean repo-relative directory",
|
|
68
|
+
)
|
|
69
|
+
.optional(),
|
|
70
|
+
/**
|
|
71
|
+
* Immutable revision to fetch the plugin from. Must be a full commit SHA:
|
|
72
|
+
* tags and branches are mutable, so allowing them would let an upstream
|
|
73
|
+
* owner retarget a curated entry at code the daemon later `import()`s (RCE).
|
|
74
|
+
* A full SHA pins the install to exactly the reviewed bytes.
|
|
75
|
+
*/
|
|
76
|
+
ref: z
|
|
77
|
+
.string()
|
|
78
|
+
.regex(
|
|
79
|
+
COMMIT_SHA_RE,
|
|
80
|
+
"expected a full commit SHA (40 or 64 hex chars); tags and branches are mutable and not allowed",
|
|
81
|
+
),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const marketplaceEntrySchema = z.object({
|
|
85
|
+
/** Install name. `assistant plugins install <name>` resolves to this entry. */
|
|
86
|
+
name: z.string().regex(PLUGIN_NAME_RE, "expected a kebab-case install name"),
|
|
87
|
+
source: githubSourceSchema,
|
|
88
|
+
description: z.string().optional(),
|
|
89
|
+
/** Free-form grouping hint (e.g. `productivity`). Informational. */
|
|
90
|
+
category: z.string().optional(),
|
|
91
|
+
homepage: z.string().optional(),
|
|
92
|
+
license: z.string().optional(),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const marketplaceManifestSchema = z.object({
|
|
96
|
+
name: z.string(),
|
|
97
|
+
owner: z
|
|
98
|
+
.object({
|
|
99
|
+
name: z.string(),
|
|
100
|
+
url: z.string().optional(),
|
|
101
|
+
email: z.string().optional(),
|
|
102
|
+
})
|
|
103
|
+
.optional(),
|
|
104
|
+
plugins: z.array(marketplaceEntrySchema),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/** A single whitelisted external plugin entry. */
|
|
108
|
+
export type MarketplaceEntry = z.infer<typeof marketplaceEntrySchema>;
|
|
109
|
+
|
|
110
|
+
/** Concrete GitHub coordinates an entry resolves to for install. */
|
|
111
|
+
export interface ResolvedPluginSource {
|
|
112
|
+
readonly owner: string;
|
|
113
|
+
readonly repo: string;
|
|
114
|
+
/** Directory within the repo holding the plugin root; `""` = repo root. */
|
|
115
|
+
readonly path: string;
|
|
116
|
+
/** Git ref to fetch from. */
|
|
117
|
+
readonly ref: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Options controlling which marketplace revision to read. */
|
|
121
|
+
export interface FetchMarketplaceOptions {
|
|
122
|
+
/** Ref of the canonical repo to read the manifest from. */
|
|
123
|
+
readonly ref: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Dependencies injected by the caller. */
|
|
127
|
+
export interface FetchMarketplaceDeps {
|
|
128
|
+
/** HTTP client. Production callers pass `globalThis.fetch.bind(globalThis)`. */
|
|
129
|
+
readonly fetch: FetchLike;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The manifest could not be read or did not validate. Distinct from "no
|
|
134
|
+
* manifest at this ref" (a missing file is a normal, empty result) — this
|
|
135
|
+
* signals an upstream/parse problem the caller may choose to surface or
|
|
136
|
+
* degrade past.
|
|
137
|
+
*/
|
|
138
|
+
export class MarketplaceFetchError extends Error {
|
|
139
|
+
constructor(message: string) {
|
|
140
|
+
super(message);
|
|
141
|
+
this.name = "MarketplaceFetchError";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Fetch and validate the marketplace manifest, returning its plugin entries.
|
|
147
|
+
*
|
|
148
|
+
* A missing manifest (HTTP 404) is treated as an empty whitelist — the
|
|
149
|
+
* marketplace is supplementary to the first-party plugin listing, so its
|
|
150
|
+
* absence is not an error. Any other HTTP failure, a non-JSON body, or a
|
|
151
|
+
* schema violation throws {@link MarketplaceFetchError}.
|
|
152
|
+
*/
|
|
153
|
+
export async function fetchMarketplaceEntries(
|
|
154
|
+
deps: FetchMarketplaceDeps,
|
|
155
|
+
opts: FetchMarketplaceOptions,
|
|
156
|
+
): Promise<readonly MarketplaceEntry[]> {
|
|
157
|
+
const { ref } = opts;
|
|
158
|
+
const url =
|
|
159
|
+
`https://api.github.com/repos/${MARKETPLACE_SOURCE_OWNER}/${MARKETPLACE_SOURCE_REPO}` +
|
|
160
|
+
`/contents/${MARKETPLACE_FILE_PATH}?ref=${encodeURIComponent(ref)}`;
|
|
161
|
+
|
|
162
|
+
const res = await deps.fetch(url, {
|
|
163
|
+
headers: {
|
|
164
|
+
// Ask for the raw file body rather than the base64-wrapped Contents
|
|
165
|
+
// envelope, so the response text is the manifest JSON directly.
|
|
166
|
+
Accept: "application/vnd.github.raw",
|
|
167
|
+
"User-Agent": "vellum-assistant-cli",
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (res.status === 404) return [];
|
|
172
|
+
if (!res.ok) {
|
|
173
|
+
throw new MarketplaceFetchError(
|
|
174
|
+
`Marketplace manifest fetch failed for ${MARKETPLACE_FILE_PATH} @ ${ref}: HTTP ${res.status}`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let json: unknown;
|
|
179
|
+
try {
|
|
180
|
+
json = JSON.parse(await res.text());
|
|
181
|
+
} catch (err) {
|
|
182
|
+
throw new MarketplaceFetchError(
|
|
183
|
+
`Marketplace manifest is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const parsed = marketplaceManifestSchema.safeParse(json);
|
|
188
|
+
if (!parsed.success) {
|
|
189
|
+
throw new MarketplaceFetchError(
|
|
190
|
+
`Marketplace manifest failed validation: ${parsed.error.message}`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return parsed.data.plugins;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolve a plugin name to concrete GitHub coordinates using the supplied
|
|
199
|
+
* marketplace entries. Returns `null` when no entry claims the name (the
|
|
200
|
+
* caller then falls back to the first-party source convention).
|
|
201
|
+
*/
|
|
202
|
+
export function resolveMarketplaceSource(
|
|
203
|
+
name: string,
|
|
204
|
+
entries: readonly MarketplaceEntry[],
|
|
205
|
+
): ResolvedPluginSource | null {
|
|
206
|
+
const entry = entries.find((e) => e.name === name);
|
|
207
|
+
if (!entry) return null;
|
|
208
|
+
const [owner, repo] = entry.source.repo.split("/", 2) as [string, string];
|
|
209
|
+
return {
|
|
210
|
+
owner,
|
|
211
|
+
repo,
|
|
212
|
+
path: entry.source.path ?? "",
|
|
213
|
+
ref: entry.source.ref,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Search
|
|
2
|
+
* Search the installable plugin catalog in the canonical GitHub source.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* git ref
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The catalog is the union of two sources, both fetched from the repo at the
|
|
5
|
+
* configured git ref:
|
|
6
|
+
* 1. First-party plugins — directories under
|
|
7
|
+
* `vellum-ai/vellum-assistant/experimental/plugins/`.
|
|
8
|
+
* 2. Whitelisted external ecosystem plugins — entries in the curated
|
|
9
|
+
* `experimental/plugins/marketplace.json` manifest (see
|
|
10
|
+
* {@link ./plugin-marketplace}).
|
|
11
|
+
*
|
|
12
|
+
* Entries are filtered by case-insensitive ECMAScript regex against the
|
|
13
|
+
* plugin name. A plain query like `"memory"` matches anywhere in the name;
|
|
14
|
+
* anchors like `"^simple"` work without escaping.
|
|
8
15
|
*
|
|
9
16
|
* Designed for direct programmatic use. The CLI command
|
|
10
17
|
* `assistant plugins search <query>` is a thin wrapper that supplies
|
|
@@ -15,6 +22,10 @@
|
|
|
15
22
|
|
|
16
23
|
import type { FetchLike } from "./install-from-github.js";
|
|
17
24
|
import { DEFAULT_PLUGIN_REF } from "./install-from-github.js";
|
|
25
|
+
import {
|
|
26
|
+
fetchMarketplaceEntries,
|
|
27
|
+
type MarketplaceEntry,
|
|
28
|
+
} from "./plugin-marketplace.js";
|
|
18
29
|
|
|
19
30
|
// Re-export the dep-injection type so callers can grab everything they need
|
|
20
31
|
// from one module rather than reaching into `install-from-github.js`.
|
|
@@ -50,12 +61,33 @@ export interface SearchPluginsDeps {
|
|
|
50
61
|
readonly fetch: FetchLike;
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
/**
|
|
64
|
+
/** Where a catalog match comes from. */
|
|
65
|
+
export type PluginMatchSource =
|
|
66
|
+
| { readonly kind: "first-party" }
|
|
67
|
+
| {
|
|
68
|
+
readonly kind: "github";
|
|
69
|
+
/** `owner/repo` of the external plugin repository. */
|
|
70
|
+
readonly repo: string;
|
|
71
|
+
/** Directory within the repo, when the plugin is not at the root. */
|
|
72
|
+
readonly path?: string;
|
|
73
|
+
/** Pinned git ref the plugin is fetched from. */
|
|
74
|
+
readonly ref: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** One matching catalog entry. */
|
|
54
78
|
export interface PluginSearchMatch {
|
|
55
|
-
/**
|
|
79
|
+
/** Install name — `assistant plugins install <name>` resolves to it. */
|
|
56
80
|
readonly name: string;
|
|
57
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Human-readable origin of the entry: the repo-relative path for
|
|
83
|
+
* first-party plugins (e.g. `experimental/plugins/<name>`) or a
|
|
84
|
+
* `github:owner/repo@ref` locator for external ones.
|
|
85
|
+
*/
|
|
58
86
|
readonly path: string;
|
|
87
|
+
/** Short description, when known (external entries only today). */
|
|
88
|
+
readonly description?: string;
|
|
89
|
+
/** Discriminated origin, so callers can render/install accordingly. */
|
|
90
|
+
readonly source: PluginMatchSource;
|
|
59
91
|
}
|
|
60
92
|
|
|
61
93
|
/** Search result envelope. */
|
|
@@ -75,12 +107,28 @@ export class InvalidSearchPatternError extends Error {
|
|
|
75
107
|
}
|
|
76
108
|
|
|
77
109
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
110
|
+
* The catalog source (GitHub) was reachable but refused or could not serve
|
|
111
|
+
* the request right now — rate limiting (HTTP 403 with the rate-limit budget
|
|
112
|
+
* exhausted, or 429) or an upstream 5xx. Distinct from a hard 404 on the
|
|
113
|
+
* plugins prefix (a real "source gone" misconfiguration): a transient
|
|
114
|
+
* upstream failure should surface as a retryable "temporarily unavailable"
|
|
115
|
+
* rather than a generic internal error, and is a candidate for serving a
|
|
116
|
+
* stale cached catalog.
|
|
117
|
+
*/
|
|
118
|
+
export class PluginCatalogUnavailableError extends Error {
|
|
119
|
+
/** Upstream HTTP status that triggered the failure. */
|
|
120
|
+
readonly status: number;
|
|
121
|
+
constructor(message: string, status: number) {
|
|
122
|
+
super(message);
|
|
123
|
+
this.name = "PluginCatalogUnavailableError";
|
|
124
|
+
this.status = status;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Build the catalog at {@link opts.ref} and return the entries whose name
|
|
130
|
+
* matches {@link opts.query} (case-insensitive ECMAScript regex; an empty
|
|
131
|
+
* query matches everything).
|
|
84
132
|
*/
|
|
85
133
|
export async function searchPlugins(
|
|
86
134
|
opts: SearchPluginsOptions,
|
|
@@ -92,17 +140,125 @@ export async function searchPlugins(
|
|
|
92
140
|
// the network — keeps "user typo" cheap to recover from.
|
|
93
141
|
const matcher = buildMatcher(opts.query);
|
|
94
142
|
|
|
95
|
-
const
|
|
143
|
+
const { matches: catalog } = await loadPluginCatalog({ ref }, deps);
|
|
144
|
+
const matches = catalog.filter((m) => matcher(m.name));
|
|
145
|
+
|
|
146
|
+
return { query: opts.query, ref, matches };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Validate that {@link query} compiles as a case-insensitive ECMAScript regex,
|
|
151
|
+
* throwing {@link InvalidSearchPatternError} if not. Lets a caching caller
|
|
152
|
+
* (the daemon) reject a malformed query before loading the catalog, so a typo
|
|
153
|
+
* is a cheap deterministic 400 rather than a wasted GitHub request.
|
|
154
|
+
*/
|
|
155
|
+
export function assertValidSearchPattern(query: string): void {
|
|
156
|
+
buildMatcher(query);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Filter a pre-loaded {@link PluginCatalog} by {@link query}, compiling it as
|
|
161
|
+
* a case-insensitive ECMAScript regex (an empty query matches everything).
|
|
162
|
+
* Lets a caching caller (the daemon) reuse one catalog load across many
|
|
163
|
+
* searches. Throws {@link InvalidSearchPatternError} on a malformed pattern.
|
|
164
|
+
*/
|
|
165
|
+
export function filterPluginCatalog(
|
|
166
|
+
catalog: PluginCatalog,
|
|
167
|
+
query: string,
|
|
168
|
+
): PluginSearchMatch[] {
|
|
169
|
+
const matcher = buildMatcher(query);
|
|
170
|
+
return catalog.matches.filter((m) => matcher(m.name));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The full, unfiltered catalog at a given ref. */
|
|
174
|
+
export interface PluginCatalog {
|
|
175
|
+
readonly ref: string;
|
|
176
|
+
/** Every catalog entry, deduped and sorted alphabetically by name. */
|
|
177
|
+
readonly matches: readonly PluginSearchMatch[];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Build the full catalog at {@link opts.ref}: every first-party plugin
|
|
182
|
+
* directory under `experimental/plugins/` merged with every whitelisted
|
|
183
|
+
* external entry in the marketplace manifest.
|
|
184
|
+
*
|
|
185
|
+
* The result is **query-independent** — `searchPlugins` applies the regex
|
|
186
|
+
* filter in memory afterwards. That separation is what lets a long-lived
|
|
187
|
+
* caller (the daemon) cache one catalog load and serve any number of
|
|
188
|
+
* searches from it without re-hitting GitHub (see {@link ./plugin-catalog-cache}).
|
|
189
|
+
*/
|
|
190
|
+
export async function loadPluginCatalog(
|
|
191
|
+
opts: { readonly ref?: string },
|
|
192
|
+
deps: SearchPluginsDeps,
|
|
193
|
+
): Promise<PluginCatalog> {
|
|
194
|
+
const ref = opts.ref ?? DEFAULT_PLUGIN_REF;
|
|
195
|
+
|
|
196
|
+
const [entries, marketplace] = await Promise.all([
|
|
197
|
+
listDir(PLUGIN_SOURCE_PATH_PREFIX, ref, deps.fetch),
|
|
198
|
+
fetchMarketplaceSafe(deps.fetch, ref),
|
|
199
|
+
]);
|
|
96
200
|
|
|
97
201
|
const matches: PluginSearchMatch[] = [];
|
|
202
|
+
const seen = new Set<string>();
|
|
203
|
+
|
|
204
|
+
// Whitelisted external entries own their name. A same-named
|
|
205
|
+
// `experimental/plugins/<name>/` directory is that plugin's curated adapter
|
|
206
|
+
// stub (overlaid onto the clone at install time), not a standalone
|
|
207
|
+
// first-party plugin — so it must surface once, as the external entry.
|
|
208
|
+
for (const entry of marketplace) {
|
|
209
|
+
if (seen.has(entry.name)) continue;
|
|
210
|
+
matches.push(marketplaceMatch(entry));
|
|
211
|
+
seen.add(entry.name);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// First-party plugins are the in-repo directories whose name the marketplace
|
|
215
|
+
// does not claim.
|
|
98
216
|
for (const entry of entries) {
|
|
99
217
|
if (entry.type !== "dir") continue;
|
|
100
|
-
if (
|
|
101
|
-
matches.push({
|
|
218
|
+
if (seen.has(entry.name)) continue;
|
|
219
|
+
matches.push({
|
|
220
|
+
name: entry.name,
|
|
221
|
+
path: entry.path,
|
|
222
|
+
source: { kind: "first-party" },
|
|
223
|
+
});
|
|
224
|
+
seen.add(entry.name);
|
|
102
225
|
}
|
|
226
|
+
|
|
103
227
|
matches.sort((a, b) => a.name.localeCompare(b.name));
|
|
104
228
|
|
|
105
|
-
return {
|
|
229
|
+
return { ref, matches };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Project a marketplace entry onto the catalog match shape, building a
|
|
234
|
+
* `github:owner/repo[/path]@ref` locator for display.
|
|
235
|
+
*/
|
|
236
|
+
function marketplaceMatch(entry: MarketplaceEntry): PluginSearchMatch {
|
|
237
|
+
const { repo, path, ref } = entry.source;
|
|
238
|
+
const locator = `github:${repo}${path ? `/${path}` : ""}@${ref}`;
|
|
239
|
+
return {
|
|
240
|
+
name: entry.name,
|
|
241
|
+
path: locator,
|
|
242
|
+
description: entry.description,
|
|
243
|
+
source: { kind: "github", repo, path, ref },
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Fetch the marketplace manifest, degrading to an empty whitelist on any
|
|
249
|
+
* failure. The manifest is supplementary to the first-party listing, so a
|
|
250
|
+
* missing or malformed manifest must never break the core catalog — mirroring
|
|
251
|
+
* the daemon's "never block over a subsystem failure" philosophy.
|
|
252
|
+
*/
|
|
253
|
+
async function fetchMarketplaceSafe(
|
|
254
|
+
fetchFn: FetchLike,
|
|
255
|
+
ref: string,
|
|
256
|
+
): Promise<readonly MarketplaceEntry[]> {
|
|
257
|
+
try {
|
|
258
|
+
return await fetchMarketplaceEntries({ fetch: fetchFn }, { ref });
|
|
259
|
+
} catch {
|
|
260
|
+
return [];
|
|
261
|
+
}
|
|
106
262
|
}
|
|
107
263
|
|
|
108
264
|
function buildMatcher(query: string): (name: string) => boolean {
|
|
@@ -127,13 +283,17 @@ async function listDir(
|
|
|
127
283
|
|
|
128
284
|
const res = await githubFetch(url, fetchFn);
|
|
129
285
|
if (!res.ok) {
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
)
|
|
286
|
+
const detail = `GitHub contents listing failed for ${apiPath} @ ${ref}: HTTP ${res.status}`;
|
|
287
|
+
// Rate limiting (403 with the budget exhausted, or 429) and upstream
|
|
288
|
+
// 5xx are transient — surface them as a retryable "temporarily
|
|
289
|
+
// unavailable" so the caller can serve a stale cache and the route can
|
|
290
|
+
// map to 503 instead of a misleading 500. A 404 on the plugins prefix
|
|
291
|
+
// itself means the canonical source path is gone (a real
|
|
292
|
+
// misconfiguration), so it stays a hard error.
|
|
293
|
+
if (isTransientUpstreamStatus(res)) {
|
|
294
|
+
throw new PluginCatalogUnavailableError(detail, res.status);
|
|
295
|
+
}
|
|
296
|
+
throw new Error(detail);
|
|
137
297
|
}
|
|
138
298
|
|
|
139
299
|
const body = (await res.json()) as unknown;
|
|
@@ -150,10 +310,22 @@ async function listDir(
|
|
|
150
310
|
* request. Unauthenticated — the canonical source is a public repo, mirroring
|
|
151
311
|
* `installPlugin` which uses the same envelope.
|
|
152
312
|
*/
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
)
|
|
313
|
+
/**
|
|
314
|
+
* Whether a non-OK GitHub response should be treated as a transient
|
|
315
|
+
* "temporarily unavailable" failure rather than a hard error. Covers
|
|
316
|
+
* rate limiting (429, or 403 once the rate-limit budget is exhausted) and
|
|
317
|
+
* upstream server errors (5xx). A bare 403 without the rate-limit signal
|
|
318
|
+
* (e.g. a genuine permissions problem) is not treated as transient.
|
|
319
|
+
*/
|
|
320
|
+
function isTransientUpstreamStatus(res: Response): boolean {
|
|
321
|
+
if (res.status === 429 || res.status >= 500) return true;
|
|
322
|
+
if (res.status === 403) {
|
|
323
|
+
return res.headers.get("x-ratelimit-remaining") === "0";
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function githubFetch(url: string, fetchFn: FetchLike): Promise<Response> {
|
|
157
329
|
return fetchFn(url, {
|
|
158
330
|
headers: {
|
|
159
331
|
Accept: "application/vnd.github+json",
|
|
@@ -85,8 +85,8 @@ describe("unified feature flag registry guard", () => {
|
|
|
85
85
|
) {
|
|
86
86
|
violations.push(`${prefix}: missing or non-string 'description'`);
|
|
87
87
|
}
|
|
88
|
-
if (typeof flag.defaultEnabled !== "boolean") {
|
|
89
|
-
violations.push(`${prefix}: missing or
|
|
88
|
+
if (typeof flag.defaultEnabled !== "boolean" && typeof flag.defaultEnabled !== "string") {
|
|
89
|
+
violations.push(`${prefix}: missing or invalid 'defaultEnabled' (expected boolean or string)`);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -10,6 +10,7 @@ describe("DEFAULT_ACP_AGENT_PROFILES", () => {
|
|
|
10
10
|
expect(Object.keys(DEFAULT_ACP_AGENT_PROFILES).sort()).toEqual([
|
|
11
11
|
"claude",
|
|
12
12
|
"codex",
|
|
13
|
+
"gemini",
|
|
13
14
|
]);
|
|
14
15
|
});
|
|
15
16
|
|
|
@@ -29,6 +30,14 @@ describe("DEFAULT_ACP_AGENT_PROFILES", () => {
|
|
|
29
30
|
});
|
|
30
31
|
});
|
|
31
32
|
|
|
33
|
+
test("gemini profile speaks native ACP via gemini --acp (no adapter binary)", () => {
|
|
34
|
+
expect(DEFAULT_ACP_AGENT_PROFILES.gemini).toEqual({
|
|
35
|
+
command: "gemini",
|
|
36
|
+
args: ["--acp"],
|
|
37
|
+
description: "Google Gemini CLI (native ACP via gemini --acp)",
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
32
41
|
test("is deeply frozen so mutation throws in strict mode", () => {
|
|
33
42
|
expect(Object.isFrozen(DEFAULT_ACP_AGENT_PROFILES)).toBe(true);
|
|
34
43
|
for (const profile of Object.values(DEFAULT_ACP_AGENT_PROFILES)) {
|
|
@@ -46,6 +55,7 @@ describe("DEFAULT_AGENT_NPM_PACKAGES", () => {
|
|
|
46
55
|
expect(DEFAULT_AGENT_NPM_PACKAGES).toEqual({
|
|
47
56
|
"claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
|
|
48
57
|
"codex-acp": "@zed-industries/codex-acp",
|
|
58
|
+
gemini: "@google/gemini-cli",
|
|
49
59
|
});
|
|
50
60
|
});
|
|
51
61
|
|
|
@@ -9,9 +9,9 @@ const FROZEN_EMPTY_ARGS = Object.freeze([] as string[]) as unknown as string[];
|
|
|
9
9
|
/**
|
|
10
10
|
* Default ACP agent profiles that ship with the assistant.
|
|
11
11
|
*
|
|
12
|
-
* When
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* When the user has not provided a config entry for an agent id, the resolver
|
|
13
|
+
* falls back to this map so common agents like `claude` and `codex` Just Work
|
|
14
|
+
* without requiring per-user config.
|
|
15
15
|
*
|
|
16
16
|
* Keyed by agent id. Deeply frozen — the outer object, each profile, and the
|
|
17
17
|
* `args` arrays — so mutation throws in strict mode rather than silently
|
|
@@ -30,6 +30,11 @@ export const DEFAULT_ACP_AGENT_PROFILES: Readonly<
|
|
|
30
30
|
args: FROZEN_EMPTY_ARGS,
|
|
31
31
|
description: "OpenAI Codex CLI (via @zed-industries/codex-acp)",
|
|
32
32
|
}),
|
|
33
|
+
gemini: Object.freeze({
|
|
34
|
+
command: "gemini",
|
|
35
|
+
args: Object.freeze(["--acp"]) as unknown as string[],
|
|
36
|
+
description: "Google Gemini CLI (native ACP via gemini --acp)",
|
|
37
|
+
}),
|
|
33
38
|
});
|
|
34
39
|
|
|
35
40
|
/**
|
|
@@ -44,4 +49,5 @@ export const DEFAULT_AGENT_NPM_PACKAGES: Readonly<Record<string, string>> =
|
|
|
44
49
|
Object.freeze({
|
|
45
50
|
"claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
|
|
46
51
|
"codex-acp": "@zed-industries/codex-acp",
|
|
52
|
+
gemini: "@google/gemini-cli",
|
|
47
53
|
});
|
package/src/config/acp-schema.ts
CHANGED
|
@@ -20,12 +20,6 @@ const AcpAgentConfigSchema = z
|
|
|
20
20
|
|
|
21
21
|
export const AcpConfigSchema = z
|
|
22
22
|
.object({
|
|
23
|
-
enabled: z
|
|
24
|
-
.boolean()
|
|
25
|
-
.default(false)
|
|
26
|
-
.describe(
|
|
27
|
-
"Whether the Agent Communication Protocol (ACP) system is enabled",
|
|
28
|
-
),
|
|
29
23
|
maxConcurrentSessions: z
|
|
30
24
|
.number()
|
|
31
25
|
.int()
|
|
@@ -40,7 +34,7 @@ export const AcpConfigSchema = z
|
|
|
40
34
|
.describe("Map of agent names to their configurations"),
|
|
41
35
|
})
|
|
42
36
|
.describe(
|
|
43
|
-
"Agent Communication Protocol (ACP) —
|
|
37
|
+
"Agent Communication Protocol (ACP) — inter-agent communication and delegation",
|
|
44
38
|
);
|
|
45
39
|
|
|
46
40
|
export type AcpConfig = z.infer<typeof AcpConfigSchema>;
|