@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- 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/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -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__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -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__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- 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__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -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-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- 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 +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- 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/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -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-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- 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/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.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/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -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 +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- 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/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- 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 +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- 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-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- 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__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- 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/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- 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/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /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__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +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}/core.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}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
package/docs/plugins.md
DELETED
|
@@ -1,836 +0,0 @@
|
|
|
1
|
-
# Plugins
|
|
2
|
-
|
|
3
|
-
> **Note:** This guide documents the **legacy** plugin architecture — the
|
|
4
|
-
> in-tree `register.ts` + middleware-pipeline system. We are converging on
|
|
5
|
-
> the schema at
|
|
6
|
-
> [`experimental/plugins/README.md`](../../experimental/plugins/README.md)
|
|
7
|
-
> (file-based discovery, a `package.json` manifest, the
|
|
8
|
-
> `@vellumai/plugin-api` public contract). The plan is to reshape this guide
|
|
9
|
-
> section by section until it matches that one; wherever the two still
|
|
10
|
-
> differ is the next piece of consolidation work. New plugins should target
|
|
11
|
-
> the modern schema.
|
|
12
|
-
|
|
13
|
-
Plugins extend the assistant's default capabilities using hooks, tools,
|
|
14
|
-
skills, and more.
|
|
15
|
-
|
|
16
|
-
If you're authoring a plugin against the current convention, this file is
|
|
17
|
-
your map. Read [`examples/plugins/echo/`](../examples/plugins/echo/README.md)
|
|
18
|
-
alongside, it's the canonical reference implementation and exercises every
|
|
19
|
-
wired surface.
|
|
20
|
-
|
|
21
|
-
## Table of contents
|
|
22
|
-
|
|
23
|
-
- [TL;DR](#tldr)
|
|
24
|
-
- [What a plugin can contribute today](#what-a-plugin-can-contribute-today)
|
|
25
|
-
- [Anatomy of a plugin](#anatomy-of-a-plugin)
|
|
26
|
-
- [Where plugins live](#where-plugins-live)
|
|
27
|
-
- [Manifest](#manifest)
|
|
28
|
-
- [Registration](#registration)
|
|
29
|
-
- [Middleware patterns](#middleware-patterns)
|
|
30
|
-
- [Pipeline reference](#pipeline-reference)
|
|
31
|
-
- [Timeouts](#timeouts)
|
|
32
|
-
- [Strict-fail semantics](#strict-fail-semantics)
|
|
33
|
-
- [Credentials and config](#credentials-and-config)
|
|
34
|
-
- [Tool, route, and skill contributions](#tool-route-and-skill-contributions)
|
|
35
|
-
- [Cross-plugin communication](#cross-plugin-communication)
|
|
36
|
-
- [Hot reload](#hot-reload)
|
|
37
|
-
- [Troubleshooting](#troubleshooting)
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## TL;DR
|
|
42
|
-
|
|
43
|
-
1. Create a directory `<workspaceDir>/plugins/my-plugin/`.
|
|
44
|
-
2. Drop a `package.json` with a `name` and a `peerDependencies["@vellumai/plugin-api"]` semver range.
|
|
45
|
-
3. Add `middlewares/<name>.ts` files (default export = middleware function).
|
|
46
|
-
4. Add `tools/<name>.ts` files (default export = tool definition).
|
|
47
|
-
5. Restart the assistant — the loader scans `<workspaceDir>/plugins/*` and
|
|
48
|
-
registers the plugin on startup.
|
|
49
|
-
|
|
50
|
-
## What a plugin can contribute today
|
|
51
|
-
|
|
52
|
-
| Surface | Where | Discovery |
|
|
53
|
-
| ----------------------- | ------------------------- | ------------------------------------------------- |
|
|
54
|
-
| Pipeline middleware | `plugin.middleware` | keyed by pipeline name in `PipelineMiddlewareMap` |
|
|
55
|
-
| Model-visible tools | `plugin.tools` | each `PluginToolRegistration` |
|
|
56
|
-
| HTTP routes | `plugin.routes` | each `PluginRouteRegistration` |
|
|
57
|
-
| Skills | `plugin.skills` | each `PluginSkillRegistration` |
|
|
58
|
-
| System-prompt injectors | `plugin.injectors` | each `Injector` |
|
|
59
|
-
| Lifecycle | `init()` / `onShutdown()` | methods on the `Plugin` object |
|
|
60
|
-
|
|
61
|
-
The modern schema wires only **hooks** and **tools**; the middleware
|
|
62
|
-
pipelines, routes, skills, and injectors above are the surfaces that still
|
|
63
|
-
have no modern equivalent.
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## Anatomy of a plugin
|
|
68
|
-
|
|
69
|
-
A plugin is a directory that exports a single `register.ts` (or compiled
|
|
70
|
-
`register.js`) entry point. That file builds a `Plugin` object and passes
|
|
71
|
-
it to `registerPlugin()` as an import-time side effect. Everything else —
|
|
72
|
-
pipeline middleware, lifecycle hooks, model-visible capabilities — hangs
|
|
73
|
-
off that one `Plugin` object.
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
my-plugin/
|
|
77
|
-
├── package.json # Node/Bun package metadata
|
|
78
|
-
├── README.md # optional — human docs
|
|
79
|
-
└── register.ts # the entry point the assistant imports
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
The `Plugin` shape is declared in
|
|
83
|
-
[`assistant/src/plugins/types.ts`](../src/plugins/types.ts):
|
|
84
|
-
|
|
85
|
-
```typescript
|
|
86
|
-
export interface Plugin {
|
|
87
|
-
manifest: PluginManifest;
|
|
88
|
-
init?(ctx: PluginInitContext): Promise<void>;
|
|
89
|
-
onShutdown?(): Promise<void>;
|
|
90
|
-
tools?: PluginToolRegistration[];
|
|
91
|
-
routes?: PluginRouteRegistration[];
|
|
92
|
-
skills?: PluginSkillRegistration[];
|
|
93
|
-
injectors?: Injector[];
|
|
94
|
-
middleware?: Partial<PipelineMiddlewareMap>;
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Every field except `manifest` is optional. A plugin that only contributes
|
|
99
|
-
middleware doesn't need tools or routes; a plugin that only contributes a
|
|
100
|
-
skill can omit middleware entirely.
|
|
101
|
-
|
|
102
|
-
## Where plugins live
|
|
103
|
-
|
|
104
|
-
The assistant scans `<workspaceDir>/plugins/*` at startup. Any subdirectory
|
|
105
|
-
containing `register.js` or `register.ts` is dynamic-imported once. The
|
|
106
|
-
loader lives in
|
|
107
|
-
[`assistant/src/plugins/user-loader.ts`](../src/plugins/user-loader.ts) and
|
|
108
|
-
has three key properties:
|
|
109
|
-
|
|
110
|
-
- **Compiled wins.** If both `register.js` and `register.ts` are present,
|
|
111
|
-
the compiled `.js` file is loaded. This matches how the compiled
|
|
112
|
-
assistant binary resolves modules in production.
|
|
113
|
-
- **Per-plugin isolation.** If one plugin throws at import time, the error
|
|
114
|
-
is logged with the plugin directory and the loader moves on. Other
|
|
115
|
-
plugins still load. One broken plugin cannot brick the assistant.
|
|
116
|
-
- **Per-instance.** The scan runs under `vellumRoot()`. Each assistant
|
|
117
|
-
instance loads its own plugin set.
|
|
118
|
-
|
|
119
|
-
The loader runs after first-party plugin registrations and before
|
|
120
|
-
`bootstrapPlugins()` invokes every plugin's `init()`.
|
|
121
|
-
|
|
122
|
-
## Manifest
|
|
123
|
-
|
|
124
|
-
The manifest is static metadata validated by the registry at registration
|
|
125
|
-
time. Its shape (see
|
|
126
|
-
[`types.ts`](../src/plugins/types.ts)):
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
export interface PluginManifest {
|
|
130
|
-
name: string; // kebab-case, unique
|
|
131
|
-
version: string; // semver, informational
|
|
132
|
-
requiresCredential?: string[]; // credential keys resolved before init()
|
|
133
|
-
requiresFlag?: string[]; // feature flag keys that must all be enabled
|
|
134
|
-
config?: unknown; // Zod-like parser for plugins.<name>
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
| Field | Required | Purpose |
|
|
139
|
-
| -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
140
|
-
| `name` | yes | Unique plugin identifier. Duplicate names fail registration. Used as the directory under `<workspaceDir>/plugins-data/<name>/` and the attribution tag in logs. |
|
|
141
|
-
| `version` | yes | Plugin's own semver. Informational — the registry does not compare it. |
|
|
142
|
-
| `requiresCredential` | no | Credential keys the plugin needs. The bootstrap resolves them via the credential store before `init()` runs and hands the values to the plugin in `ctx.credentials`. A missing credential fails startup with a clear error. |
|
|
143
|
-
| `requiresFlag` | no | Assistant feature-flag keys that must all be ON for the plugin to activate. If any listed flag is disabled at bootstrap, the plugin is skipped entirely: `init()` is not invoked and no tools, routes, skills, or shutdown hooks are registered for it. See [Feature-flag gating](#feature-flag-gating) below. |
|
|
144
|
-
| `config` | no | A parser-like validator (Zod schema, or any object with a `.parse(input)` method). If supplied, the bootstrap validates `config.plugins.<name>` through it before passing the result into `init()`. |
|
|
145
|
-
|
|
146
|
-
### Host-compat: `peerDependencies["@vellumai/plugin-api"]`
|
|
147
|
-
|
|
148
|
-
Plugins declare which assistant versions they support via standard
|
|
149
|
-
`peerDependencies` in their `package.json`:
|
|
150
|
-
|
|
151
|
-
```json
|
|
152
|
-
{
|
|
153
|
-
"name": "@me/my-logger",
|
|
154
|
-
"version": "1.2.3",
|
|
155
|
-
"peerDependencies": {
|
|
156
|
-
"@vellumai/plugin-api": "^0.8.0"
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
At load time, the external-plugin loader resolves the assistant's running
|
|
162
|
-
version and runs `semver.satisfies(assistantVersion, range)` against the
|
|
163
|
-
declared range. The contract is currently soft while the plugin-installation
|
|
164
|
-
flow is in flux:
|
|
165
|
-
|
|
166
|
-
- **Range satisfied** — plugin loads.
|
|
167
|
-
- **Range not satisfied** — loader logs an error (`log.error`) and loads
|
|
168
|
-
the plugin anyway.
|
|
169
|
-
- **Range unparseable** — loader logs an error and loads the plugin anyway.
|
|
170
|
-
- **`@vellumai/plugin-api` peerDep absent** — loader logs a warning and
|
|
171
|
-
loads the plugin without a host-compat claim.
|
|
172
|
-
|
|
173
|
-
Once the install flow settles, the two error-logging branches above will
|
|
174
|
-
harden into hard rejections (with per-plugin isolation catching the
|
|
175
|
-
throw so one bad plugin can't brick the rest of the registry).
|
|
176
|
-
|
|
177
|
-
In-tree default plugins do not declare a peerDep — they ship with the
|
|
178
|
-
assistant binary and are version-locked by construction.
|
|
179
|
-
|
|
180
|
-
### Example manifest
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
const manifest: PluginManifest = {
|
|
184
|
-
name: "my-logger",
|
|
185
|
-
version: "1.2.3",
|
|
186
|
-
requiresCredential: ["LOGGER_API_KEY"],
|
|
187
|
-
requiresFlag: ["my-logger-enabled"],
|
|
188
|
-
config: z.object({
|
|
189
|
-
endpoint: z.string().url(),
|
|
190
|
-
sampleRate: z.number().min(0).max(1).default(0.1),
|
|
191
|
-
}),
|
|
192
|
-
};
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Feature-flag gating
|
|
196
|
-
|
|
197
|
-
`manifest.requiresFlag` lists one or more **assistant-scope** feature-flag
|
|
198
|
-
keys (the same keys declared in
|
|
199
|
-
`meta/feature-flags/feature-flag-registry.json`). The bootstrap checks each
|
|
200
|
-
key against `isAssistantFeatureFlagEnabled` before touching the plugin. If
|
|
201
|
-
**any** listed flag is disabled, the plugin is skipped entirely for the
|
|
202
|
-
duration of this assistant boot:
|
|
203
|
-
|
|
204
|
-
- `init()` is **not** invoked.
|
|
205
|
-
- `tools`, `routes`, and `skills` are **not** registered.
|
|
206
|
-
- No shutdown hook entry is installed, so a plugin skipped at boot has
|
|
207
|
-
nothing to tear down on shutdown.
|
|
208
|
-
|
|
209
|
-
Flag state is resolved once at bootstrap time. Flipping a `requiresFlag`
|
|
210
|
-
key at runtime does not hot-reload the plugin — restart the assistant
|
|
211
|
-
after changing the flag to pick up the new state. An empty `requiresFlag` (or
|
|
212
|
-
the field being absent) means the plugin activates unconditionally.
|
|
213
|
-
|
|
214
|
-
The skip path emits a single `info`-level log line naming both the plugin
|
|
215
|
-
and the disabled flag, so operators can diagnose "why isn't my plugin
|
|
216
|
-
loading?" at a glance:
|
|
217
|
-
|
|
218
|
-
```
|
|
219
|
-
plugins-bootstrap skipping plugin my-logger: feature flag my-logger-enabled is disabled
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
**Cross-repo note:** new flag keys used here must be declared in the
|
|
223
|
-
assistant-scope section of
|
|
224
|
-
`meta/feature-flags/feature-flag-registry.json` (and provisioned in the
|
|
225
|
-
platform's Terraform configuration). See the root `CLAUDE.md`'s "Assistant
|
|
226
|
-
Feature Flags" section for the full procedure.
|
|
227
|
-
|
|
228
|
-
## Registration
|
|
229
|
-
|
|
230
|
-
A plugin's `register.ts` calls `registerPlugin()` at module load time. The
|
|
231
|
-
function is exposed via the `globalThis.__vellumPluginRuntime` bridge so the
|
|
232
|
-
plugin file does not need to import from the daemon's source tree:
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
import type { Plugin } from "<path-to-assistant>/src/plugins/types.js";
|
|
236
|
-
|
|
237
|
-
interface VellumPluginRuntime {
|
|
238
|
-
readonly version: 1;
|
|
239
|
-
readonly registerPlugin: (plugin: Plugin) => void;
|
|
240
|
-
readonly assistantEventHub: import("<path-to-assistant>/src/runtime/assistant-event-hub.js").AssistantEventHub;
|
|
241
|
-
readonly getSecureKeyAsync: (account: string) => Promise<string | undefined>;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const runtime = (globalThis as { __vellumPluginRuntime?: VellumPluginRuntime })
|
|
245
|
-
.__vellumPluginRuntime;
|
|
246
|
-
if (!runtime || runtime.version !== 1) {
|
|
247
|
-
throw new Error(
|
|
248
|
-
"vellum plugin runtime not available — install a recent assistant build",
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
const { registerPlugin } = runtime;
|
|
252
|
-
|
|
253
|
-
const myPlugin: Plugin = {
|
|
254
|
-
manifest: {
|
|
255
|
-
name: "my-plugin",
|
|
256
|
-
version: "0.1.0",
|
|
257
|
-
},
|
|
258
|
-
middleware: {
|
|
259
|
-
/* ... */
|
|
260
|
-
},
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
registerPlugin(myPlugin);
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
**Why the bridge?** When the daemon is a `bun --compile` binary, its modules
|
|
267
|
-
are bundled into the executable. Plugins that import the daemon's modules by
|
|
268
|
-
absolute path (`/abs/path/to/assistant/src/plugins/registry.js`) reload fresh
|
|
269
|
-
disk copies into a separate module graph, and any `registerPlugin()` call in
|
|
270
|
-
the plugin lands in a registry the daemon never reads. The
|
|
271
|
-
`globalThis.__vellumPluginRuntime` handle is the same instance the daemon's
|
|
272
|
-
bundled code holds onto, so plugin registrations always reach the right
|
|
273
|
-
place — whether the daemon was built with `bun --compile` or is running from
|
|
274
|
-
source.
|
|
275
|
-
|
|
276
|
-
Type-only imports (`import type { Plugin } from "..."`) remain free to use
|
|
277
|
-
absolute paths to the assistant source — the TypeScript compiler erases them
|
|
278
|
-
and they have no module-identity effect at runtime.
|
|
279
|
-
|
|
280
|
-
**Rules:**
|
|
281
|
-
|
|
282
|
-
- Exactly one `registerPlugin()` call per plugin. The registry rejects
|
|
283
|
-
duplicate names.
|
|
284
|
-
- `register.ts` must not export named symbols consumed from outside. The
|
|
285
|
-
loader treats the import as side-effect-only.
|
|
286
|
-
- Throwing inside `register.ts` is caught by the loader and logged, then
|
|
287
|
-
the loader moves on. Do not rely on throws to signal "please don't load
|
|
288
|
-
this plugin" — use `requiresFlag` or a guard inside `init()` instead.
|
|
289
|
-
- The file runs before any lifecycle hooks. Keep it fast — heavy work
|
|
290
|
-
belongs in `init()`.
|
|
291
|
-
- The bridge is installed by the daemon before `loadUserPlugins()` runs, so
|
|
292
|
-
the global is always present when a plugin's module body executes.
|
|
293
|
-
|
|
294
|
-
## Middleware patterns
|
|
295
|
-
|
|
296
|
-
Middleware is the heart of the plugin system. Every pipeline slot uses the
|
|
297
|
-
same onion-style signature:
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
export type Middleware<A, R> = (
|
|
301
|
-
args: A,
|
|
302
|
-
next: (args: A) => Promise<R>,
|
|
303
|
-
ctx: TurnContext,
|
|
304
|
-
) => Promise<R>;
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
The runner composes an array of middleware around a terminal handler. The
|
|
308
|
-
first middleware sees the request first and the response last; the
|
|
309
|
-
terminal runs at the innermost layer. See
|
|
310
|
-
[`assistant/src/plugins/pipeline.ts`](../src/plugins/pipeline.ts) for the
|
|
311
|
-
composition algorithm.
|
|
312
|
-
|
|
313
|
-
Four common patterns emerge from that signature:
|
|
314
|
-
|
|
315
|
-
### Observe-only
|
|
316
|
-
|
|
317
|
-
Record something without changing the call. Call `next(args)` unchanged,
|
|
318
|
-
return the result unchanged. Wrap the call in `try`/`finally` so your
|
|
319
|
-
observer runs on both success and failure paths.
|
|
320
|
-
|
|
321
|
-
```typescript
|
|
322
|
-
const observer: Middleware<ToolExecuteArgs, ToolExecuteResult> =
|
|
323
|
-
async function observeToolExecute(args, next, ctx) {
|
|
324
|
-
const start = performance.now();
|
|
325
|
-
let outcome: "success" | "error" = "success";
|
|
326
|
-
try {
|
|
327
|
-
return await next(args);
|
|
328
|
-
} catch (err) {
|
|
329
|
-
outcome = "error";
|
|
330
|
-
throw err;
|
|
331
|
-
} finally {
|
|
332
|
-
const ms = Math.round(performance.now() - start);
|
|
333
|
-
console.error(JSON.stringify({ tool: args.name, ms, outcome }));
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### Transform input
|
|
339
|
-
|
|
340
|
-
Rewrite `args` before calling downstream. Useful for request shimming
|
|
341
|
-
(adding headers, redacting inputs, picking a different provider).
|
|
342
|
-
|
|
343
|
-
```typescript
|
|
344
|
-
const addHeader: Middleware<LLMCallArgs, LLMCallResult> =
|
|
345
|
-
async function addHeader(args, next, ctx) {
|
|
346
|
-
const tagged = {
|
|
347
|
-
...args,
|
|
348
|
-
options: {
|
|
349
|
-
...args.options,
|
|
350
|
-
config: { ...args.options?.config, requestId: ctx.requestId },
|
|
351
|
-
},
|
|
352
|
-
};
|
|
353
|
-
return next(tagged);
|
|
354
|
-
};
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### Transform output
|
|
358
|
-
|
|
359
|
-
Call `next(args)` first, then modify the result before returning.
|
|
360
|
-
|
|
361
|
-
```typescript
|
|
362
|
-
const redactPII: Middleware<LLMCallArgs, LLMCallResult> =
|
|
363
|
-
async function redactPII(args, next, ctx) {
|
|
364
|
-
const response = await next(args);
|
|
365
|
-
return {
|
|
366
|
-
...response,
|
|
367
|
-
content: response.content.map(redactBlock),
|
|
368
|
-
};
|
|
369
|
-
};
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Short-circuit
|
|
373
|
-
|
|
374
|
-
Do not call `next(args)` — return a synthetic result directly. The
|
|
375
|
-
terminal and any inner middleware are skipped. Use this to stub, cache,
|
|
376
|
-
or mock a pipeline.
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
const cacheHit: Middleware<LLMCallArgs, LLMCallResult> =
|
|
380
|
-
async function cacheHit(args, next, ctx) {
|
|
381
|
-
const cached = await lookupCache(args);
|
|
382
|
-
if (cached) return cached;
|
|
383
|
-
return next(args);
|
|
384
|
-
};
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### Veto (throw)
|
|
388
|
-
|
|
389
|
-
Throwing from middleware aborts the pipeline. The error propagates out
|
|
390
|
-
through any outer middleware unchanged — there is no internal
|
|
391
|
-
`try`/`catch` around user middleware.
|
|
392
|
-
|
|
393
|
-
```typescript
|
|
394
|
-
const denyIfUnauthorized: Middleware<ToolExecuteArgs, ToolExecuteResult> =
|
|
395
|
-
async function denyIfUnauthorized(args, next, ctx) {
|
|
396
|
-
if (!isAuthorizedFor(args.name, ctx.trust)) {
|
|
397
|
-
throw new Error(`tool ${args.name} denied by policy`);
|
|
398
|
-
}
|
|
399
|
-
return next(args);
|
|
400
|
-
};
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
### Naming middleware
|
|
404
|
-
|
|
405
|
-
Give middleware a stable `name` (via `async function <name>(…)`). The
|
|
406
|
-
pipeline runner pulls `Function.name` into its `chain` log field so
|
|
407
|
-
operators can see the registered chain at a glance:
|
|
408
|
-
|
|
409
|
-
```
|
|
410
|
-
plugin.pipeline pipeline=llmCall chain=["observeLlm","addHeader","defaultLlmCall"] durationMs=1840 outcome=success
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
## Pipeline reference
|
|
414
|
-
|
|
415
|
-
Every pipeline slot and its purpose. Type details live in
|
|
416
|
-
[`types.ts`](../src/plugins/types.ts).
|
|
417
|
-
|
|
418
|
-
| Pipeline | Purpose |
|
|
419
|
-
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
420
|
-
| `turn` | The outermost wrapper around a single assistant turn. Middleware here sees everything a turn does end-to-end. |
|
|
421
|
-
| `llmCall` | Every call to `Provider.sendMessage`. Input carries `messages` and `options` (with `tools`, `systemPrompt`, `config`, `onEvent`, `signal` inside). |
|
|
422
|
-
| `toolExecute` | Every `ToolExecutor.execute` call. Input carries `name`, `input`, and the full `ToolContext`. |
|
|
423
|
-
| `memoryRetrieval` | PKB, NOW.md, and memory-graph retrieval for a turn. Output is a merged `MemoryResult`. |
|
|
424
|
-
| `historyRepair` | The pre-run repair pass on the message history. Wraps `repairHistory`. |
|
|
425
|
-
| `tokenEstimate` | The token-count estimate used for budgeting. Wraps `estimatePromptTokensRaw`. |
|
|
426
|
-
| `compaction` | The conversation-compaction step. Wraps `ContextWindowManager.maybeCompact`. |
|
|
427
|
-
| `overflowReduce` | The reducer tier loop invoked when a turn blows the context budget. |
|
|
428
|
-
| `persistence` | Every message CRUD op (`add` / `update` / `delete`). Discriminated by `args.op`. |
|
|
429
|
-
| `titleGenerate` | Conversation title generation. Fire-and-forget by default. |
|
|
430
|
-
| `toolResultTruncate` | The per-tool-result truncation step that fits a tool's output into the context window. |
|
|
431
|
-
| `emptyResponse` | The decision about what to do when the model returns an empty turn (nudge / accept / error). |
|
|
432
|
-
| `toolError` | The decision about what to do when one or more tool calls errored on a turn. |
|
|
433
|
-
| `circuitBreaker` | The compaction circuit breaker. Tracks consecutive-failure state, decides whether to open the circuit. |
|
|
434
|
-
|
|
435
|
-
## Timeouts
|
|
436
|
-
|
|
437
|
-
Each pipeline has a default timeout budget in milliseconds. When the
|
|
438
|
-
budget is exceeded the runner throws `PluginTimeoutError` carrying the
|
|
439
|
-
pipeline name, the offending plugin's name (if known), and the elapsed
|
|
440
|
-
duration. See
|
|
441
|
-
[`assistant/src/plugins/pipeline.ts`](../src/plugins/pipeline.ts) for the
|
|
442
|
-
current values.
|
|
443
|
-
|
|
444
|
-
| Pipeline | Timeout | Rationale |
|
|
445
|
-
| -------------------- | -------- | -------------------------------------------------------------------------------------------------------------- |
|
|
446
|
-
| `turn` | none | Turn duration is bounded by the downstream `llmCall` / `toolExecute` timeouts, not a pipeline-level timer. |
|
|
447
|
-
| `llmCall` | none | Deferred to the provider's HTTP timeout so network hiccups surface as provider errors, not pipeline timeouts. |
|
|
448
|
-
| `toolExecute` | none | Deferred to the per-tool timeout already enforced by `ToolExecutor`. |
|
|
449
|
-
| `memoryRetrieval` | 5000 ms | Memory reads may hit Qdrant and disk; 5 s leaves slack for cold caches without blocking the turn indefinitely. |
|
|
450
|
-
| `historyRepair` | 1000 ms | CPU-bound list walk — should finish in a few ms. |
|
|
451
|
-
| `tokenEstimate` | 1000 ms | Same — CPU-bound, should return instantly. |
|
|
452
|
-
| `compaction` | 30000 ms | Summarization involves a provider call; mirrors the pipeline-level budget for LLM-backed operations. |
|
|
453
|
-
| `overflowReduce` | 30000 ms | Iterative compaction; matches the `compaction` budget since each tier step may invoke it. |
|
|
454
|
-
| `persistence` | 10000 ms | SQLite writes, Qdrant deletes, and disk syncs. 10 s is generous for the slowest op (batched segment inserts). |
|
|
455
|
-
| `titleGenerate` | 30000 ms | Provider-backed. Fire-and-forget, but the budget exists so a stuck call doesn't leak forever. |
|
|
456
|
-
| `toolResultTruncate` | 1000 ms | Pure string op. |
|
|
457
|
-
| `emptyResponse` | 500 ms | Decision logic only — must be near-instant. |
|
|
458
|
-
| `toolError` | 500 ms | Decision logic only — must be near-instant. |
|
|
459
|
-
| `circuitBreaker` | 500 ms | Numeric state update — must be near-instant. |
|
|
460
|
-
|
|
461
|
-
`null` timeouts skip the timer entirely. Finite timeouts arm a
|
|
462
|
-
`setTimeout` that races the pipeline via `Promise.race`.
|
|
463
|
-
|
|
464
|
-
## Strict-fail semantics
|
|
465
|
-
|
|
466
|
-
**Plugin errors and timeouts fail the turn loudly. There is no silent
|
|
467
|
-
fallback to the default behavior.**
|
|
468
|
-
|
|
469
|
-
This is a deliberate design decision. The old inline behavior silently
|
|
470
|
-
absorbed many edge cases (a memory retrieval failure became an empty
|
|
471
|
-
memory block, a compaction error became no compaction, etc.). That made
|
|
472
|
-
debugging production issues miserable because failures disappeared into
|
|
473
|
-
logs nobody checked.
|
|
474
|
-
|
|
475
|
-
With strict-fail:
|
|
476
|
-
|
|
477
|
-
- Any error thrown from middleware propagates up to the caller. The
|
|
478
|
-
pipeline runner does not catch it.
|
|
479
|
-
- Any `PluginTimeoutError` from a budget breach propagates identically.
|
|
480
|
-
- The caller (agent loop, memory subsystem, whoever) decides how to
|
|
481
|
-
degrade. The pipeline itself does not paper over the failure.
|
|
482
|
-
- Exactly one structured log line is emitted per pipeline invocation, in
|
|
483
|
-
a `finally` block, regardless of outcome. It carries `outcome`
|
|
484
|
-
(`"success" | "error" | "timeout"`), `durationMs`, `chain`, plugin
|
|
485
|
-
attribution, and error details when applicable.
|
|
486
|
-
|
|
487
|
-
If you're writing middleware that wants to "try, fall back to default on
|
|
488
|
-
failure," express that at the call site instead — wrap the pipeline
|
|
489
|
-
invocation in your own try/catch. Do not swallow the error inside your
|
|
490
|
-
middleware's `try`/`catch` and silently return a degraded result.
|
|
491
|
-
|
|
492
|
-
## Credentials and config
|
|
493
|
-
|
|
494
|
-
### Credentials
|
|
495
|
-
|
|
496
|
-
Declare required credential keys in `manifest.requiresCredential`:
|
|
497
|
-
|
|
498
|
-
```typescript
|
|
499
|
-
const manifest: PluginManifest = {
|
|
500
|
-
name: "my-plugin",
|
|
501
|
-
version: "1.0.0",
|
|
502
|
-
requiresCredential: ["MY_PLUGIN_API_KEY"],
|
|
503
|
-
};
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
During bootstrap, the assistant resolves each key through the credential
|
|
507
|
-
store (via `getSecureKeyAsync`). In Docker mode that call goes through
|
|
508
|
-
the CES HTTP API; in local mode it hits the encrypted file store / CES
|
|
509
|
-
RPC backend. The resolved values are handed to your `init()`:
|
|
510
|
-
|
|
511
|
-
```typescript
|
|
512
|
-
async init(ctx: PluginInitContext) {
|
|
513
|
-
const apiKey = ctx.credentials["MY_PLUGIN_API_KEY"];
|
|
514
|
-
// use it
|
|
515
|
-
}
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
**Rules:**
|
|
519
|
-
|
|
520
|
-
- Never import the credential store directly. Always go through the
|
|
521
|
-
manifest.
|
|
522
|
-
- Missing credentials fail startup with a clear error naming the plugin
|
|
523
|
-
and the key. There is no silent fallback.
|
|
524
|
-
- Credentials are resolved once at bootstrap. Long-running plugins that
|
|
525
|
-
need rotation must re-resolve through their own mechanism.
|
|
526
|
-
|
|
527
|
-
### Config
|
|
528
|
-
|
|
529
|
-
Declare a parser-like validator in `manifest.config`:
|
|
530
|
-
|
|
531
|
-
```typescript
|
|
532
|
-
const configSchema = z.object({
|
|
533
|
-
endpoint: z.string().url(),
|
|
534
|
-
sampleRate: z.number().min(0).max(1).default(0.1),
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
const manifest: PluginManifest = {
|
|
538
|
-
name: "my-plugin",
|
|
539
|
-
version: "1.0.0",
|
|
540
|
-
config: configSchema,
|
|
541
|
-
};
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
The bootstrap reads `config.plugins.<name>` from the assistant's config
|
|
545
|
-
and calls `manifest.config.parse(raw)`. The parsed result is handed to
|
|
546
|
-
your `init()`:
|
|
547
|
-
|
|
548
|
-
```typescript
|
|
549
|
-
async init(ctx: PluginInitContext) {
|
|
550
|
-
const cfg = ctx.config as z.infer<typeof configSchema>;
|
|
551
|
-
// use cfg
|
|
552
|
-
}
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
If you don't supply a validator, the raw config is passed through
|
|
556
|
-
untouched as `unknown` and your plugin must narrow it itself.
|
|
557
|
-
|
|
558
|
-
### Other init context fields
|
|
559
|
-
|
|
560
|
-
The full `PluginInitContext`:
|
|
561
|
-
|
|
562
|
-
```typescript
|
|
563
|
-
export interface PluginInitContext {
|
|
564
|
-
config: unknown; // parsed config (or raw if no validator)
|
|
565
|
-
credentials: Record<string, string>; // resolved credentials from requiresCredential
|
|
566
|
-
logger: unknown; // pino child logger, tagged { plugin: <name> }
|
|
567
|
-
pluginStorageDir: string; // <workspaceDir>/plugins-data/<name>/ (created by bootstrap)
|
|
568
|
-
assistantVersion: string; // assistant semver — same value used by the loader
|
|
569
|
-
// against your peerDependencies range
|
|
570
|
-
}
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
`pluginStorageDir` is a per-plugin writable directory. Use it for
|
|
574
|
-
persistent state — cache files, counters, anything that must survive an
|
|
575
|
-
assistant restart. The bootstrap creates it on demand.
|
|
576
|
-
|
|
577
|
-
## Tool, route, and skill contributions
|
|
578
|
-
|
|
579
|
-
Plugins can contribute model-visible capabilities alongside their
|
|
580
|
-
middleware. Each is optional.
|
|
581
|
-
|
|
582
|
-
### Tools (`plugin.tools`)
|
|
583
|
-
|
|
584
|
-
An array of `Tool` objects. The bootstrap registers them with the global
|
|
585
|
-
tool registry after `init()` succeeds, stamping `origin: "plugin"` and
|
|
586
|
-
`owner: { kind: "plugin", id: <plugin.name> }` so they live in a ref-count
|
|
587
|
-
namespace disjoint from real skills (a plugin whose `manifest.name`
|
|
588
|
-
happens to match a skill id cannot collide with that skill's
|
|
589
|
-
registrations).
|
|
590
|
-
|
|
591
|
-
```typescript
|
|
592
|
-
const myPlugin: Plugin = {
|
|
593
|
-
manifest: {
|
|
594
|
-
/* ... */
|
|
595
|
-
},
|
|
596
|
-
tools: [
|
|
597
|
-
{
|
|
598
|
-
name: "my_tool",
|
|
599
|
-
description: "Does the thing.",
|
|
600
|
-
category: "plugin",
|
|
601
|
-
defaultRiskLevel: "low",
|
|
602
|
-
getDefinition: () => ({
|
|
603
|
-
name: "my_tool",
|
|
604
|
-
description: "Does the thing.",
|
|
605
|
-
input_schema: { type: "object", properties: {}, required: [] },
|
|
606
|
-
}),
|
|
607
|
-
execute: async (input, ctx) => ({ content: "result", isError: false }),
|
|
608
|
-
},
|
|
609
|
-
],
|
|
610
|
-
};
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
Tools are unregistered automatically on shutdown. See
|
|
614
|
-
[`assistant/src/tools/types.ts`](../src/tools/types.ts) for the full
|
|
615
|
-
`Tool` interface including optional fields like `executionMode` and
|
|
616
|
-
`executionTarget`.
|
|
617
|
-
|
|
618
|
-
### Routes (`plugin.routes`)
|
|
619
|
-
|
|
620
|
-
An array of `SkillRoute` objects — the same shape the skill-route
|
|
621
|
-
registry consumes. Registered via `registerSkillRoute` after `init()`
|
|
622
|
-
succeeds; the runtime retains the opaque handle returned by each call
|
|
623
|
-
and uses those handles to unregister the plugin's routes on shutdown.
|
|
624
|
-
Handle-keyed unregistration is deliberate: two owners (plugin vs.
|
|
625
|
-
skill, or plugin vs. plugin) can legitimately declare the same regex,
|
|
626
|
-
and identity matching ensures one owner's teardown cannot evict
|
|
627
|
-
another owner's live routes.
|
|
628
|
-
|
|
629
|
-
```typescript
|
|
630
|
-
const myPlugin: Plugin = {
|
|
631
|
-
manifest: {
|
|
632
|
-
/* ... */
|
|
633
|
-
},
|
|
634
|
-
routes: [
|
|
635
|
-
{
|
|
636
|
-
pattern: /^\/_plugin\/my-plugin\/status$/,
|
|
637
|
-
methods: ["GET"],
|
|
638
|
-
handler: async (req, match) => new Response("ok"),
|
|
639
|
-
},
|
|
640
|
-
],
|
|
641
|
-
};
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
### Skills (`plugin.skills`)
|
|
645
|
-
|
|
646
|
-
An array of `PluginSkillRegistration` objects. Each becomes a discoverable
|
|
647
|
-
skill under `source: "plugin"` in the model's `skill_load` /
|
|
648
|
-
`skill_execute` flow.
|
|
649
|
-
|
|
650
|
-
```typescript
|
|
651
|
-
const myPlugin: Plugin = {
|
|
652
|
-
manifest: {
|
|
653
|
-
/* ... */
|
|
654
|
-
},
|
|
655
|
-
skills: [
|
|
656
|
-
{
|
|
657
|
-
id: "my-plugin/do-thing",
|
|
658
|
-
name: "do-thing",
|
|
659
|
-
description: "Does the thing via plugin-contributed skill.",
|
|
660
|
-
body: "# SKILL.md body returned when loaded\n...",
|
|
661
|
-
},
|
|
662
|
-
],
|
|
663
|
-
};
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
See
|
|
667
|
-
[`plugin-skill-contributions.ts`](../src/plugins/plugin-skill-contributions.ts)
|
|
668
|
-
for the in-memory registry details and ref-counted lifecycle.
|
|
669
|
-
|
|
670
|
-
### Injectors (`plugin.injectors`)
|
|
671
|
-
|
|
672
|
-
An array of `Injector` objects that emit system-prompt-time content.
|
|
673
|
-
Each has a stable `name`, an ascending `order` used to position it in the
|
|
674
|
-
injection chain, and a `produce(ctx)` method that returns an
|
|
675
|
-
`InjectionBlock` or `null`.
|
|
676
|
-
|
|
677
|
-
The default injectors use `order` 10 through 70 with gaps of 10, so
|
|
678
|
-
plugin-contributed injectors can slot at `25`, `35`, etc. without
|
|
679
|
-
renumbering.
|
|
680
|
-
|
|
681
|
-
```typescript
|
|
682
|
-
const myPlugin: Plugin = {
|
|
683
|
-
manifest: {
|
|
684
|
-
/* ... */
|
|
685
|
-
},
|
|
686
|
-
injectors: [
|
|
687
|
-
{
|
|
688
|
-
name: "my-plugin/status",
|
|
689
|
-
order: 25,
|
|
690
|
-
async produce(ctx) {
|
|
691
|
-
return {
|
|
692
|
-
id: "my-plugin/status",
|
|
693
|
-
text: `<my_plugin_status>ok</my_plugin_status>`,
|
|
694
|
-
};
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
],
|
|
698
|
-
};
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
## Cross-plugin communication
|
|
702
|
-
|
|
703
|
-
Plugins should not call each other directly. There is no cross-plugin
|
|
704
|
-
import API — a plugin's export surface is intentionally limited to the
|
|
705
|
-
`Plugin` object it registers.
|
|
706
|
-
|
|
707
|
-
For cross-cutting concerns (broadcasting events, reacting to
|
|
708
|
-
system-level changes), use the `assistantEventHub` pub/sub in
|
|
709
|
-
[`runtime/assistant-event-hub.ts`](../src/runtime/assistant-event-hub.ts).
|
|
710
|
-
The hub is the canonical place to publish events from inside the
|
|
711
|
-
assistant process and to subscribe from anywhere that has access to the
|
|
712
|
-
assistant's module graph.
|
|
713
|
-
|
|
714
|
-
Do not add new HTTP endpoints to implement plugin-to-plugin messaging
|
|
715
|
-
inside a single assistant process.
|
|
716
|
-
|
|
717
|
-
## Hot reload
|
|
718
|
-
|
|
719
|
-
**Not supported in v1.** Registering a plugin takes effect at assistant
|
|
720
|
-
startup only. To pick up a new or modified plugin:
|
|
721
|
-
|
|
722
|
-
```bash
|
|
723
|
-
vellum restart
|
|
724
|
-
```
|
|
725
|
-
|
|
726
|
-
The registry's internal state is not mutable at runtime. `init()` and
|
|
727
|
-
`onShutdown()` hooks are fired exactly once per assistant boot.
|
|
728
|
-
|
|
729
|
-
If you need hot reload for development, symlink your plugin directory
|
|
730
|
-
into `<workspaceDir>/plugins/` so edits propagate, and automate the restart
|
|
731
|
-
loop externally.
|
|
732
|
-
|
|
733
|
-
## Troubleshooting
|
|
734
|
-
|
|
735
|
-
### `external plugin X: peerDependencies["@vellumai/plugin-api"] requires "<range>" but assistant is <version> — loading anyway`
|
|
736
|
-
|
|
737
|
-
Logged at `error` level. Your plugin's declared
|
|
738
|
-
`peerDependencies["@vellumai/plugin-api"]` range does not include the
|
|
739
|
-
running assistant's version. The plugin still loads while the install
|
|
740
|
-
flow is being shaped, but a future release will turn this into a hard
|
|
741
|
-
rejection. Either widen the range in your `package.json` (typically by
|
|
742
|
-
bumping the major in `^X.Y.Z`) or upgrade the assistant.
|
|
743
|
-
|
|
744
|
-
### `external plugin X: peerDependencies["@vellumai/plugin-api"] is not a valid semver range — loading anyway`
|
|
745
|
-
|
|
746
|
-
Logged at `error` level, same lenient policy as above. The value declared
|
|
747
|
-
under `peerDependencies["@vellumai/plugin-api"]` is not parseable as a
|
|
748
|
-
semver range. Use a standard range expression such as `^0.8.0`,
|
|
749
|
-
`>=0.8.0 <0.10`, or an exact version.
|
|
750
|
-
|
|
751
|
-
### `external plugin X missing plugin-api peerDependency — loading without host-compat claim`
|
|
752
|
-
|
|
753
|
-
Warning, not an error. Your plugin's `package.json` does not declare a
|
|
754
|
-
`peerDependencies["@vellumai/plugin-api"]` entry, so the loader has no
|
|
755
|
-
host-compat range to check and loads the plugin without that guard. Add
|
|
756
|
-
the peerDep so future assistant upgrades surface incompatibility before
|
|
757
|
-
the plugin runs.
|
|
758
|
-
|
|
759
|
-
### "plugin X is already registered"
|
|
760
|
-
|
|
761
|
-
Two plugins tried to register under the same `manifest.name`. Names must
|
|
762
|
-
be globally unique. Rename one, or if this is a dev-reload issue,
|
|
763
|
-
restart the assistant.
|
|
764
|
-
|
|
765
|
-
### "plugin X requires credential Y but the credential store returned no value"
|
|
766
|
-
|
|
767
|
-
The credential named in `requiresCredential` is not set. Run:
|
|
768
|
-
|
|
769
|
-
```bash
|
|
770
|
-
vellum credentials set Y
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
…and restart the assistant.
|
|
774
|
-
|
|
775
|
-
### "plugin X config validation failed: …"
|
|
776
|
-
|
|
777
|
-
The config block under `config.plugins.<name>` failed the manifest's
|
|
778
|
-
parser. Check your config against the plugin's schema — the error
|
|
779
|
-
message carries the validator's diagnostic.
|
|
780
|
-
|
|
781
|
-
### `PluginTimeoutError: Plugin pipeline '<name>' timed out after N ms`
|
|
782
|
-
|
|
783
|
-
A plugin's middleware exceeded the pipeline's budget. The offending
|
|
784
|
-
plugin is named in `ctx.pluginName` when available. Tighten the
|
|
785
|
-
middleware (it's probably blocking on I/O it shouldn't) or, if the
|
|
786
|
-
work is genuinely heavy, move it out of the critical path into a
|
|
787
|
-
background job that publishes results through `assistantEventHub`.
|
|
788
|
-
|
|
789
|
-
### Reading pipeline log records
|
|
790
|
-
|
|
791
|
-
Every pipeline invocation emits one structured line tagged
|
|
792
|
-
`event=plugin.pipeline`. The fields:
|
|
793
|
-
|
|
794
|
-
| Field | Meaning |
|
|
795
|
-
| ------------------------------------------ | ----------------------------------------------------------------------- |
|
|
796
|
-
| `pipeline` | Pipeline name (`llmCall`, `toolExecute`, …). |
|
|
797
|
-
| `chain` | Ordered list of middleware function names, outermost first. |
|
|
798
|
-
| `durationMs` | Total time spent in the composed chain. |
|
|
799
|
-
| `outcome` | `"success"`, `"error"`, or `"timeout"`. |
|
|
800
|
-
| `pluginName` | The specific plugin's name when the runner could attribute the frame. |
|
|
801
|
-
| `timeoutMs` | The configured budget (only when one was set). |
|
|
802
|
-
| `errorName`, `errorMessage`, `errorStack` | Present on failure outcomes. |
|
|
803
|
-
| `requestId`, `conversationId`, `turnIndex` | Per-turn context for correlating with the rest of the assistant's logs. |
|
|
804
|
-
|
|
805
|
-
Pipe the assistant's stderr through `jq` to filter and inspect:
|
|
806
|
-
|
|
807
|
-
```bash
|
|
808
|
-
tail -f ~/.vellum/daemon.log | jq 'select(.event == "plugin.pipeline")'
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
To isolate slow pipelines:
|
|
812
|
-
|
|
813
|
-
```bash
|
|
814
|
-
tail -f ~/.vellum/daemon.log \
|
|
815
|
-
| jq 'select(.event == "plugin.pipeline" and .durationMs > 1000)'
|
|
816
|
-
```
|
|
817
|
-
|
|
818
|
-
To isolate errors and timeouts:
|
|
819
|
-
|
|
820
|
-
```bash
|
|
821
|
-
tail -f ~/.vellum/daemon.log \
|
|
822
|
-
| jq 'select(.event == "plugin.pipeline" and .outcome != "success")'
|
|
823
|
-
```
|
|
824
|
-
|
|
825
|
-
### Plugin not loading at all
|
|
826
|
-
|
|
827
|
-
- Confirm the directory is under `<workspaceDir>/plugins/`.
|
|
828
|
-
- Confirm it has a `register.ts` or `register.js` at the top level.
|
|
829
|
-
- Check the assistant's stderr for a line like
|
|
830
|
-
`loaded user plugin (side-effect import completed)` or
|
|
831
|
-
`Failed to load user plugin <dir>: <err>`. Import-time throws are
|
|
832
|
-
logged but do not crash the assistant — the plugin is silently skipped
|
|
833
|
-
otherwise.
|
|
834
|
-
- Verify `register.ts` calls `registerPlugin()` exactly once at module
|
|
835
|
-
level. If the call is inside an unrelated conditional or wrapped in
|
|
836
|
-
an async function that is never awaited, the registry won't see it.
|