@vellumai/assistant 0.8.1 → 0.8.3
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 +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +642 -5
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +31 -65
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +59 -1
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +670 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +25 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +288 -11
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +74 -22
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +187 -42
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +40 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +98 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +17 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +107 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External plugin runtime — `globalThis.__vellumPluginRuntime` bridge.
|
|
3
|
+
*
|
|
4
|
+
* Workspace-local plugins (`<workspaceDir>/plugins/*`) get dynamic-imported by
|
|
5
|
+
* {@link loadUserPlugins}. They need to call `registerPlugin()` from their
|
|
6
|
+
* module body and they typically also want to read secrets, subscribe to
|
|
7
|
+
* runtime events, etc.
|
|
8
|
+
*
|
|
9
|
+
* Importing those symbols by absolute path
|
|
10
|
+
* (`/abs/path/to/assistant/src/plugins/registry.js`) works when the daemon is
|
|
11
|
+
* running from source — both the daemon and the dynamic-imported plugin
|
|
12
|
+
* resolve the same on-disk file and share module identity. It DOES NOT work
|
|
13
|
+
* when the daemon is a `bun --compile` binary: the daemon's modules are baked
|
|
14
|
+
* into the executable, and any absolute-path import re-loads a fresh disk
|
|
15
|
+
* copy. `registerPlugin()` then writes into a disjoint registry instance and
|
|
16
|
+
* the daemon never sees the plugin.
|
|
17
|
+
*
|
|
18
|
+
* The fix is to expose a single, stable handle on `globalThis` that plugins
|
|
19
|
+
* read at module-load time. The daemon's bundled modules attach themselves
|
|
20
|
+
* here once at startup; plugins consume the same instance regardless of how
|
|
21
|
+
* the daemon was built.
|
|
22
|
+
*
|
|
23
|
+
* Plugins use the bridge like this:
|
|
24
|
+
*
|
|
25
|
+
* const runtime = (globalThis as { __vellumPluginRuntime?: ... })
|
|
26
|
+
* .__vellumPluginRuntime;
|
|
27
|
+
* if (!runtime || runtime.version !== 1) throw new Error("...");
|
|
28
|
+
* const { registerPlugin, assistantEventHub, getSecureKeyAsync } = runtime;
|
|
29
|
+
*
|
|
30
|
+
* Type-only imports (`import type { Plugin } from "..."`) remain free to use
|
|
31
|
+
* absolute paths or workspace-local copies — the TypeScript compiler erases
|
|
32
|
+
* them and they have no module-identity effect at runtime.
|
|
33
|
+
*
|
|
34
|
+
* See `assistant/docs/plugins.md` for the full authoring contract and
|
|
35
|
+
* `assistant/examples/plugins/echo/register.ts` for a worked example.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
39
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
40
|
+
import { registerPlugin } from "./registry.js";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The handle plugins read from `globalThis.__vellumPluginRuntime`.
|
|
44
|
+
*
|
|
45
|
+
* `version` is the contract version. Plugins should assert against the
|
|
46
|
+
* version they were authored against and refuse to register if they see a
|
|
47
|
+
* different one — bumping it is a breaking change to the plugin surface.
|
|
48
|
+
*
|
|
49
|
+
* The field set is intentionally small: the most-commonly-needed symbols
|
|
50
|
+
* across both static (module-load-time) and dynamic (init-time) plugin
|
|
51
|
+
* lifecycle. Plugins that need richer runtime state can still receive it
|
|
52
|
+
* through {@link PluginInitContext} during `init()`.
|
|
53
|
+
*/
|
|
54
|
+
export interface VellumPluginRuntime {
|
|
55
|
+
readonly version: 1;
|
|
56
|
+
readonly registerPlugin: typeof registerPlugin;
|
|
57
|
+
readonly assistantEventHub: typeof assistantEventHub;
|
|
58
|
+
readonly getSecureKeyAsync: typeof getSecureKeyAsync;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Stable globalThis key. Don't rename — plugins reference it by string. */
|
|
62
|
+
const RUNTIME_GLOBAL_KEY = "__vellumPluginRuntime" as const;
|
|
63
|
+
|
|
64
|
+
interface GlobalWithRuntime {
|
|
65
|
+
[RUNTIME_GLOBAL_KEY]?: VellumPluginRuntime;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Install the plugin runtime bridge on `globalThis`. Idempotent — repeat
|
|
70
|
+
* calls are no-ops, so it's safe to invoke from tests that also touch the
|
|
71
|
+
* lifecycle path.
|
|
72
|
+
*
|
|
73
|
+
* Must be called BEFORE {@link loadUserPlugins} runs, otherwise plugins that
|
|
74
|
+
* touch `globalThis.__vellumPluginRuntime` in their module body will throw.
|
|
75
|
+
*/
|
|
76
|
+
export function installPluginRuntime(): void {
|
|
77
|
+
const g = globalThis as GlobalWithRuntime;
|
|
78
|
+
if (g[RUNTIME_GLOBAL_KEY]) return;
|
|
79
|
+
g[RUNTIME_GLOBAL_KEY] = {
|
|
80
|
+
version: 1,
|
|
81
|
+
registerPlugin,
|
|
82
|
+
assistantEventHub,
|
|
83
|
+
getSecureKeyAsync,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Read the installed runtime. Returns `undefined` if {@link installPluginRuntime}
|
|
89
|
+
* hasn't been called yet — plugins should treat this as a fatal error.
|
|
90
|
+
*
|
|
91
|
+
* Exposed mainly for tests and for the rare in-process consumer that wants
|
|
92
|
+
* to read the bridge from the same module graph that installed it.
|
|
93
|
+
*/
|
|
94
|
+
export function getPluginRuntime(): VellumPluginRuntime | undefined {
|
|
95
|
+
return (globalThis as GlobalWithRuntime)[RUNTIME_GLOBAL_KEY];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Tear down the runtime handle. Test-only — production code never uninstalls
|
|
100
|
+
* because the runtime lifetime is the daemon lifetime.
|
|
101
|
+
*/
|
|
102
|
+
export function uninstallPluginRuntimeForTests(): void {
|
|
103
|
+
delete (globalThis as GlobalWithRuntime)[RUNTIME_GLOBAL_KEY];
|
|
104
|
+
}
|
|
@@ -11,8 +11,11 @@
|
|
|
11
11
|
*
|
|
12
12
|
* <pluginDir>/
|
|
13
13
|
* package.json ← manifest.name comes from `name`
|
|
14
|
-
* (npm scope stripped);
|
|
15
|
-
*
|
|
14
|
+
* (npm scope stripped);
|
|
15
|
+
* peerDependencies["@vellumai/plugin-api"]
|
|
16
|
+
* semver range is checked against the
|
|
17
|
+
* running assistant version and rejects
|
|
18
|
+
* the plugin if unsatisfied
|
|
16
19
|
* hooks/
|
|
17
20
|
* <name>.ts ← default export → plugin.hooks[<name>]
|
|
18
21
|
* (today the runtime invokes "init" at
|
|
@@ -20,7 +23,9 @@
|
|
|
20
23
|
* other filenames sit in the map for
|
|
21
24
|
* forward compatibility)
|
|
22
25
|
* tools/
|
|
23
|
-
* *.ts ← each
|
|
26
|
+
* *.ts ← each default export → plugin.tools[];
|
|
27
|
+
* runtime name derives from the filename
|
|
28
|
+
* basename
|
|
24
29
|
* src/ ← internal helpers, ignored by the loader
|
|
25
30
|
*
|
|
26
31
|
* Per-surface, `.js` is preferred over `.ts` (compiled-binary semantics).
|
|
@@ -40,8 +45,16 @@ import { readFile } from "node:fs/promises";
|
|
|
40
45
|
import { join } from "node:path";
|
|
41
46
|
import { pathToFileURL } from "node:url";
|
|
42
47
|
|
|
48
|
+
import semver from "semver";
|
|
43
49
|
import { z } from "zod";
|
|
44
50
|
|
|
51
|
+
import assistantPkg from "../../package.json" with { type: "json" };
|
|
52
|
+
import type {
|
|
53
|
+
LoadedPluginTool,
|
|
54
|
+
PluginTool,
|
|
55
|
+
RiskLevel,
|
|
56
|
+
ToolExecutionResult,
|
|
57
|
+
} from "../tools/types.js";
|
|
45
58
|
import { getLogger } from "../util/logger.js";
|
|
46
59
|
import { registerPlugin } from "./registry.js";
|
|
47
60
|
import type {
|
|
@@ -52,6 +65,8 @@ import type {
|
|
|
52
65
|
PluginToolRegistration,
|
|
53
66
|
} from "./types.js";
|
|
54
67
|
|
|
68
|
+
const PLUGIN_API_PEER_DEP = "@vellumai/plugin-api";
|
|
69
|
+
|
|
55
70
|
const log = getLogger("external-plugin-loader");
|
|
56
71
|
|
|
57
72
|
/** Default upper bound on how long a single plugin load may take. */
|
|
@@ -61,9 +76,11 @@ const DEFAULT_IMPORT_TIMEOUT_MS = 10_000;
|
|
|
61
76
|
* Zod schema for the subset of `package.json` the external loader reads.
|
|
62
77
|
*
|
|
63
78
|
* - `name` is the only required field; everything else is best-effort.
|
|
64
|
-
* - `
|
|
65
|
-
*
|
|
66
|
-
*
|
|
79
|
+
* - `peerDependencies["@vellumai/plugin-api"]` is the canonical host-compat
|
|
80
|
+
* declaration. If present, the loader checks `semver.satisfies(host, range)`
|
|
81
|
+
* against the running assistant version and rejects the plugin on
|
|
82
|
+
* mismatch. If absent, the plugin loads without a host-compat claim
|
|
83
|
+
* (with a warning).
|
|
67
84
|
* - Unknown fields pass through (`passthrough`) so the loader does not
|
|
68
85
|
* destructively reshape the file when the rest of the npm ecosystem
|
|
69
86
|
* writes to it.
|
|
@@ -72,12 +89,7 @@ const PluginPackageJsonSchema = z
|
|
|
72
89
|
.object({
|
|
73
90
|
name: z.string().min(1, "package.json `name` must be a non-empty string"),
|
|
74
91
|
version: z.string().optional(),
|
|
75
|
-
|
|
76
|
-
.object({
|
|
77
|
-
requires: z.record(z.string(), z.string()).optional(),
|
|
78
|
-
})
|
|
79
|
-
.passthrough()
|
|
80
|
-
.optional(),
|
|
92
|
+
peerDependencies: z.record(z.string(), z.string()).optional(),
|
|
81
93
|
})
|
|
82
94
|
.passthrough();
|
|
83
95
|
|
|
@@ -102,6 +114,72 @@ function stripScope(name: string): string {
|
|
|
102
114
|
return match ? match[1]! : name;
|
|
103
115
|
}
|
|
104
116
|
|
|
117
|
+
function toToolNameSegment(value: string): string {
|
|
118
|
+
return value.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "tool";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function deriveToolName(toolFileBaseName: string): string {
|
|
122
|
+
return toToolNameSegment(toolFileBaseName);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Defaults applied by {@link applyPluginToolDefaults} when a plugin tool
|
|
127
|
+
* omits one of the normally-required fields. Exported as a constant so
|
|
128
|
+
* tests and callers can reference the same source of truth.
|
|
129
|
+
*
|
|
130
|
+
* The default `execute` returns an error result so the model sees a clear
|
|
131
|
+
* "this tool isn't wired up" signal at call time. The plugin still loads
|
|
132
|
+
* cleanly — broken individual tools must never block daemon boot.
|
|
133
|
+
*/
|
|
134
|
+
export const PLUGIN_TOOL_DEFAULTS = Object.freeze({
|
|
135
|
+
description: "",
|
|
136
|
+
defaultRiskLevel: "medium" as RiskLevel,
|
|
137
|
+
input_schema: Object.freeze({
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: {},
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
}) as object,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Fill the four normally-required {@link PluginTool} fields with documented
|
|
146
|
+
* defaults when the author omitted them. Returns a {@link LoadedPluginTool}
|
|
147
|
+
* that is safe to register.
|
|
148
|
+
*/
|
|
149
|
+
function applyPluginToolDefaults(
|
|
150
|
+
tool: PluginTool,
|
|
151
|
+
name: string,
|
|
152
|
+
): LoadedPluginTool {
|
|
153
|
+
const description =
|
|
154
|
+
typeof tool.description === "string"
|
|
155
|
+
? tool.description
|
|
156
|
+
: PLUGIN_TOOL_DEFAULTS.description;
|
|
157
|
+
const defaultRiskLevel =
|
|
158
|
+
typeof tool.defaultRiskLevel === "string"
|
|
159
|
+
? tool.defaultRiskLevel
|
|
160
|
+
: PLUGIN_TOOL_DEFAULTS.defaultRiskLevel;
|
|
161
|
+
const input_schema =
|
|
162
|
+
tool.input_schema !== null &&
|
|
163
|
+
typeof tool.input_schema === "object"
|
|
164
|
+
? tool.input_schema
|
|
165
|
+
: PLUGIN_TOOL_DEFAULTS.input_schema;
|
|
166
|
+
const execute =
|
|
167
|
+
typeof tool.execute === "function"
|
|
168
|
+
? tool.execute
|
|
169
|
+
: async (): Promise<ToolExecutionResult> => ({
|
|
170
|
+
content: `plugin tool ${name} has no execute implementation`,
|
|
171
|
+
isError: true,
|
|
172
|
+
});
|
|
173
|
+
return {
|
|
174
|
+
...tool,
|
|
175
|
+
name,
|
|
176
|
+
description,
|
|
177
|
+
defaultRiskLevel,
|
|
178
|
+
input_schema,
|
|
179
|
+
execute,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
105
183
|
/**
|
|
106
184
|
* Dynamic-import `absolutePath` and return its default export. Throws when
|
|
107
185
|
* the module has no default export — callers attribute the error.
|
|
@@ -213,30 +291,65 @@ async function buildPluginFromDir(pluginDir: string): Promise<Plugin> {
|
|
|
213
291
|
const name = stripScope(pkg.name);
|
|
214
292
|
const version = pkg.version && pkg.version.length > 0 ? pkg.version : "0.0.0";
|
|
215
293
|
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
|
|
294
|
+
// Host-compat negotiation: plugins declare their plugin-api version
|
|
295
|
+
// range via standard `peerDependencies["@vellumai/plugin-api"]`. We
|
|
296
|
+
// inspect the range and report unparseable / unsatisfied cases via
|
|
297
|
+
// `log.error` but still load the plugin — the plugin-installation
|
|
298
|
+
// flow is in flux and a strict gate here would block experimentation
|
|
299
|
+
// for the customers driving the install UX. Once the install path
|
|
300
|
+
// settles, the two `log.error` branches below should harden into
|
|
301
|
+
// throws so a stale plugin can't silently run against a mismatched
|
|
302
|
+
// host.
|
|
303
|
+
//
|
|
304
|
+
// If the peerDep is absent, the plugin loads without a host-compat
|
|
305
|
+
// claim; we log a warning so the omission is visible at boot.
|
|
306
|
+
const range = pkg.peerDependencies?.[PLUGIN_API_PEER_DEP];
|
|
307
|
+
if (range !== undefined) {
|
|
308
|
+
if (!semver.validRange(range)) {
|
|
309
|
+
log.error(
|
|
310
|
+
{ pluginDir, plugin: name, peerDep: PLUGIN_API_PEER_DEP, range },
|
|
311
|
+
`external plugin ${name}: peerDependencies["${PLUGIN_API_PEER_DEP}"] is not a valid semver range — loading anyway`,
|
|
312
|
+
);
|
|
313
|
+
} else if (
|
|
314
|
+
!semver.satisfies(assistantPkg.version, range, {
|
|
315
|
+
includePrerelease: true,
|
|
316
|
+
})
|
|
317
|
+
) {
|
|
318
|
+
log.error(
|
|
319
|
+
{
|
|
320
|
+
pluginDir,
|
|
321
|
+
plugin: name,
|
|
322
|
+
peerDep: PLUGIN_API_PEER_DEP,
|
|
323
|
+
range,
|
|
324
|
+
assistantVersion: assistantPkg.version,
|
|
325
|
+
},
|
|
326
|
+
`external plugin ${name}: peerDependencies["${PLUGIN_API_PEER_DEP}"] requires "${range}" but assistant is ${assistantPkg.version} — loading anyway`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
log.warn(
|
|
331
|
+
{ pluginDir, plugin: name, peerDep: PLUGIN_API_PEER_DEP },
|
|
332
|
+
"external plugin missing plugin-api peerDependency — loading without host-compat claim",
|
|
333
|
+
);
|
|
334
|
+
}
|
|
220
335
|
|
|
221
|
-
const manifest: PluginManifest = { name, version
|
|
336
|
+
const manifest: PluginManifest = { name, version };
|
|
222
337
|
const plugin: Plugin = { manifest };
|
|
223
338
|
|
|
224
339
|
const hooks = await loadHooks(pluginDir, name);
|
|
225
340
|
if (hooks !== undefined) plugin.hooks = hooks;
|
|
226
341
|
|
|
227
342
|
const tools: PluginToolRegistration[] = [];
|
|
228
|
-
for (const { path: toolPath } of listSurfaceDir(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
typeof (tool as { name?: unknown }).name !== "string"
|
|
234
|
-
) {
|
|
343
|
+
for (const { name: toolName, path: toolPath } of listSurfaceDir(
|
|
344
|
+
join(pluginDir, "tools"),
|
|
345
|
+
)) {
|
|
346
|
+
const tool = await importDefault<PluginTool>(toolPath);
|
|
347
|
+
if (tool === null || typeof tool !== "object") {
|
|
235
348
|
throw new Error(
|
|
236
|
-
`external plugin ${name}: ${toolPath} default export must be
|
|
349
|
+
`external plugin ${name}: ${toolPath} default export must be an object`,
|
|
237
350
|
);
|
|
238
351
|
}
|
|
239
|
-
tools.push(tool);
|
|
352
|
+
tools.push(applyPluginToolDefaults(tool, deriveToolName(toolName)));
|
|
240
353
|
}
|
|
241
354
|
if (tools.length > 0) plugin.tools = tools;
|
|
242
355
|
|
|
@@ -244,16 +357,23 @@ async function buildPluginFromDir(pluginDir: string): Promise<Plugin> {
|
|
|
244
357
|
}
|
|
245
358
|
|
|
246
359
|
/**
|
|
247
|
-
*
|
|
360
|
+
* Build a {@link Plugin} from `pluginDir` with the same timeout +
|
|
361
|
+
* per-plugin isolation contract as {@link loadExternalPlugin}, but
|
|
362
|
+
* without registering it. The plugin-source watcher consumes this so it
|
|
363
|
+
* can decide between first-time registration (init once, then publish) and
|
|
364
|
+
* hot-reload (replace + skip init) based on what's already in the registry.
|
|
365
|
+
*
|
|
366
|
+
* Returns `undefined` on timeout, build failure, or abandoned surface
|
|
367
|
+
* import. Never throws — failures are logged with directory attribution.
|
|
248
368
|
*/
|
|
249
|
-
export async function
|
|
369
|
+
export async function buildExternalPlugin(
|
|
250
370
|
pluginDir: string,
|
|
251
371
|
opts: LoadExternalPluginOptions = {},
|
|
252
|
-
): Promise<
|
|
372
|
+
): Promise<Plugin | undefined> {
|
|
253
373
|
const timeoutMs = opts.importTimeoutMs ?? DEFAULT_IMPORT_TIMEOUT_MS;
|
|
254
374
|
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
255
375
|
try {
|
|
256
|
-
const timeoutSentinel = Symbol("external-plugin-
|
|
376
|
+
const timeoutSentinel = Symbol("external-plugin-build-timeout");
|
|
257
377
|
const buildPromise = buildPluginFromDir(pluginDir);
|
|
258
378
|
const timeoutPromise = new Promise<typeof timeoutSentinel>((resolve) => {
|
|
259
379
|
timeoutHandle = setTimeout(() => resolve(timeoutSentinel), timeoutMs);
|
|
@@ -262,33 +382,58 @@ export async function loadExternalPlugin(
|
|
|
262
382
|
if (result === timeoutSentinel) {
|
|
263
383
|
// Abandoned build — surface imports may still be running. Attach a
|
|
264
384
|
// terminal `.catch` so a late rejection does not surface as an
|
|
265
|
-
// unhandled-rejection crash.
|
|
266
|
-
// `
|
|
267
|
-
//
|
|
268
|
-
// moved on.
|
|
385
|
+
// unhandled-rejection crash. Callers who feed the returned plugin
|
|
386
|
+
// into `registerPlugin` rely on the closed-registration latch
|
|
387
|
+
// (registry.ts) to reject any stale late-arriving registration.
|
|
269
388
|
buildPromise.catch(() => {
|
|
270
389
|
/* swallow — see comment above */
|
|
271
390
|
});
|
|
272
391
|
log.warn(
|
|
273
392
|
{ pluginDir, timeoutMs },
|
|
274
|
-
`Timed out
|
|
393
|
+
`Timed out building external plugin ${pluginDir} after ${timeoutMs}ms — skipping`,
|
|
275
394
|
);
|
|
276
|
-
return;
|
|
395
|
+
return undefined;
|
|
277
396
|
}
|
|
278
|
-
|
|
279
|
-
log.info(
|
|
280
|
-
{ pluginDir, name: result.manifest.name },
|
|
281
|
-
"loaded external plugin",
|
|
282
|
-
);
|
|
397
|
+
return result;
|
|
283
398
|
} catch (err) {
|
|
284
399
|
// Per-plugin isolation: one bad external plugin must not crash the
|
|
285
400
|
// daemon. Surface the failure with attribution and move on.
|
|
286
401
|
const message = err instanceof Error ? err.message : String(err);
|
|
287
402
|
log.error(
|
|
288
403
|
{ err, pluginDir },
|
|
289
|
-
`Failed to
|
|
404
|
+
`Failed to build external plugin ${pluginDir}: ${message}`,
|
|
290
405
|
);
|
|
406
|
+
return undefined;
|
|
291
407
|
} finally {
|
|
292
408
|
if (timeoutHandle !== undefined) clearTimeout(timeoutHandle);
|
|
293
409
|
}
|
|
294
410
|
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Load the external plugin at `pluginDir` and register it. Thin wrapper
|
|
414
|
+
* over {@link buildExternalPlugin} that calls `registerPlugin` on the
|
|
415
|
+
* built plugin, preserving the existing `loadUserPlugins` call shape.
|
|
416
|
+
*/
|
|
417
|
+
export async function loadExternalPlugin(
|
|
418
|
+
pluginDir: string,
|
|
419
|
+
opts: LoadExternalPluginOptions = {},
|
|
420
|
+
): Promise<void> {
|
|
421
|
+
const plugin = await buildExternalPlugin(pluginDir, opts);
|
|
422
|
+
if (plugin === undefined) {
|
|
423
|
+
// buildExternalPlugin already logged the failure with attribution.
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
registerPlugin(plugin);
|
|
428
|
+
log.info(
|
|
429
|
+
{ pluginDir, name: plugin.manifest.name },
|
|
430
|
+
"loaded external plugin",
|
|
431
|
+
);
|
|
432
|
+
} catch (err) {
|
|
433
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
434
|
+
log.error(
|
|
435
|
+
{ err, pluginDir, plugin: plugin.manifest.name },
|
|
436
|
+
`Failed to register external plugin ${pluginDir}: ${message}`,
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External-plugin feature gate.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for whether the experimental external-plugin
|
|
5
|
+
* surface is enabled. The flag gates both the `assistant plugins` CLI
|
|
6
|
+
* command tree and (in future) the declarative external-plugin loader
|
|
7
|
+
* pathway in the daemon.
|
|
8
|
+
*
|
|
9
|
+
* The flag key uses the simple kebab-case format and is declared in
|
|
10
|
+
* `meta/feature-flags/feature-flag-registry.json`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
14
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
15
|
+
|
|
16
|
+
/** Gate key for the external-plugin surface. */
|
|
17
|
+
const EXTERNAL_PLUGINS_FLAG_KEY = "external-plugins" as const;
|
|
18
|
+
|
|
19
|
+
/** Whether the external-plugin surface is enabled. */
|
|
20
|
+
export function isExternalPluginsEnabled(config: AssistantConfig): boolean {
|
|
21
|
+
return isAssistantFeatureFlagEnabled(EXTERNAL_PLUGINS_FLAG_KEY, config);
|
|
22
|
+
}
|
package/src/plugins/pipeline.ts
CHANGED
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
|
|
27
27
|
import type { Logger } from "pino";
|
|
28
28
|
|
|
29
|
+
import type { HookName } from "../plugin-api/constants.js";
|
|
29
30
|
import { getLogger } from "../util/logger.js";
|
|
31
|
+
import { getHooksFor } from "./registry.js";
|
|
30
32
|
import {
|
|
31
33
|
type Middleware,
|
|
32
34
|
type PipelineName,
|
|
@@ -314,3 +316,38 @@ export async function runPipeline<A, R>(
|
|
|
314
316
|
logger.info(record, "plugin.pipeline");
|
|
315
317
|
}
|
|
316
318
|
}
|
|
319
|
+
|
|
320
|
+
// ─── Hook runner ────────────────────────────────────────────────────────────
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Execute a hook chain: walk every registered plugin's hook for `name` in
|
|
324
|
+
* registration order, threading `initialCtx` through each. Hooks may either
|
|
325
|
+
* mutate the context in place (returning `void`) or return a new context
|
|
326
|
+
* that replaces the threaded value for subsequent hooks. The final context
|
|
327
|
+
* after the chain settles is returned.
|
|
328
|
+
*
|
|
329
|
+
* `runHook` is the hook-side counterpart to {@link runPipeline}:
|
|
330
|
+
* `runPipeline` composes middleware around a terminal handler for stateful
|
|
331
|
+
* request/response pipelines (memory retrieval, history repair, etc.);
|
|
332
|
+
* `runHook` walks ordered hook functions for declarative chain-style
|
|
333
|
+
* context transformations (`user-prompt-submit` today).
|
|
334
|
+
*
|
|
335
|
+
* @param name The hook identifier — pick one from {@link HOOKS}.
|
|
336
|
+
* @param initialCtx Context the first hook receives.
|
|
337
|
+
* @returns The final context after the chain settles. Same reference as
|
|
338
|
+
* `initialCtx` when no plugin registers `name`, and when every
|
|
339
|
+
* chained hook returns `void` (mutation-in-place style).
|
|
340
|
+
*/
|
|
341
|
+
export async function runHook<TCtx>(
|
|
342
|
+
name: HookName,
|
|
343
|
+
initialCtx: TCtx,
|
|
344
|
+
): Promise<TCtx> {
|
|
345
|
+
let active = initialCtx;
|
|
346
|
+
for (const hook of getHooksFor<TCtx>(name)) {
|
|
347
|
+
const result = await hook(active);
|
|
348
|
+
if (result !== undefined) {
|
|
349
|
+
active = result;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return active;
|
|
353
|
+
}
|
package/src/plugins/registry.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Plugin registry with manifest validation
|
|
2
|
+
* Plugin registry with manifest validation.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Host-compat negotiation lives in the plugin's `package.json`
|
|
5
|
+
* `peerDependencies["@vellumai/plugin-api"]` semver range — the
|
|
6
|
+
* external-plugin loader checks it against the assistant version at
|
|
7
|
+
* load time. This module owns the rest of the manifest validation
|
|
8
|
+
* contract: name/version presence, duplicate-name detection, and the
|
|
9
|
+
* closed-registration latch that protects `bootstrapPlugins()` from
|
|
10
|
+
* late-arriving registrations.
|
|
10
11
|
*
|
|
11
12
|
* Registration is order-preserving: {@link getRegisteredPlugins},
|
|
12
13
|
* {@link getMiddlewaresFor}, and (secondarily) {@link getInjectors} all reflect
|
|
@@ -26,49 +27,9 @@ import {
|
|
|
26
27
|
type PipelineName,
|
|
27
28
|
type Plugin,
|
|
28
29
|
PluginExecutionError,
|
|
30
|
+
type PluginHookFn,
|
|
29
31
|
} from "./types.js";
|
|
30
32
|
|
|
31
|
-
/**
|
|
32
|
-
* Capability table declaring which plugin-facing API versions the assistant
|
|
33
|
-
* runtime exposes. Each capability maps to the list of supported semver-lite
|
|
34
|
-
* tags (currently a single `"v1"` per capability).
|
|
35
|
-
*
|
|
36
|
-
* New capabilities must be added here AND in their corresponding pipeline /
|
|
37
|
-
* runtime module so plugins can negotiate against them. Removing a version
|
|
38
|
-
* tag is a breaking change — all consumers in the plugin ecosystem relying on
|
|
39
|
-
* it will fail to register until they update their `requires` map.
|
|
40
|
-
*
|
|
41
|
-
* The `pluginRuntime` capability is the base runtime API every plugin must
|
|
42
|
-
* negotiate for; the remaining entries mirror {@link PipelineName} and the
|
|
43
|
-
* top-level context APIs plugins most commonly consume.
|
|
44
|
-
*/
|
|
45
|
-
export const ASSISTANT_API_VERSIONS: Record<string, string[]> = {
|
|
46
|
-
// Runtime APIs every plugin interacts with at some level. `memoryApi` is the
|
|
47
|
-
// broader memory-subsystem capability (distinct from the `memoryRetrieval`
|
|
48
|
-
// pipeline, which gets its own `memoryRetrievalApi` entry below).
|
|
49
|
-
pluginRuntime: ["v1"],
|
|
50
|
-
memoryApi: ["v1"],
|
|
51
|
-
compactionApi: ["v1"],
|
|
52
|
-
persistenceApi: ["v1"],
|
|
53
|
-
|
|
54
|
-
// Per-pipeline APIs. One entry for every slot in {@link PipelineName} that
|
|
55
|
-
// isn't already covered by the runtime-APIs block above (`compaction` and
|
|
56
|
-
// `persistence` live there because plugins commonly interact with them
|
|
57
|
-
// outside a pipeline middleware context).
|
|
58
|
-
turnApi: ["v1"],
|
|
59
|
-
llmCallApi: ["v1"],
|
|
60
|
-
toolExecuteApi: ["v1"],
|
|
61
|
-
memoryRetrievalApi: ["v1"],
|
|
62
|
-
historyRepairApi: ["v1"],
|
|
63
|
-
tokenEstimateApi: ["v1"],
|
|
64
|
-
overflowReduceApi: ["v1"],
|
|
65
|
-
titleGenerateApi: ["v1"],
|
|
66
|
-
toolResultTruncateApi: ["v1"],
|
|
67
|
-
emptyResponseApi: ["v1"],
|
|
68
|
-
toolErrorApi: ["v1"],
|
|
69
|
-
circuitBreakerApi: ["v1"],
|
|
70
|
-
};
|
|
71
|
-
|
|
72
33
|
// ─── Internal state ──────────────────────────────────────────────────────────
|
|
73
34
|
|
|
74
35
|
/**
|
|
@@ -96,11 +57,13 @@ let registrationClosed = false;
|
|
|
96
57
|
/**
|
|
97
58
|
* Validate and register a plugin. Throws {@link PluginExecutionError} if:
|
|
98
59
|
*
|
|
99
|
-
* - `manifest`, `manifest.name`, `manifest.version
|
|
100
|
-
* are missing.
|
|
60
|
+
* - `manifest`, `manifest.name`, or `manifest.version` are missing.
|
|
101
61
|
* - a plugin with the same name is already registered.
|
|
102
|
-
* -
|
|
103
|
-
*
|
|
62
|
+
* - registration has been closed by {@link closeRegistration}.
|
|
63
|
+
*
|
|
64
|
+
* Host-compat is checked upstream by the external-plugin loader against
|
|
65
|
+
* the plugin's `peerDependencies["@vellumai/plugin-api"]` semver range —
|
|
66
|
+
* the registry does not re-validate it here.
|
|
104
67
|
*
|
|
105
68
|
* On success the plugin is appended to the registry in the order this
|
|
106
69
|
* function is called. This function does NOT invoke `plugin.init()` — that
|
|
@@ -144,13 +107,6 @@ export function registerPlugin(plugin: Plugin): void {
|
|
|
144
107
|
name,
|
|
145
108
|
);
|
|
146
109
|
}
|
|
147
|
-
if (!manifest.requires || typeof manifest.requires !== "object") {
|
|
148
|
-
throw new PluginExecutionError(
|
|
149
|
-
`plugin ${name} manifest.requires is required`,
|
|
150
|
-
name,
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
110
|
// Duplicate-name check — plugins must be uniquely addressable in logs,
|
|
155
111
|
// storage paths, and error messages. Runs BEFORE the closed-registration
|
|
156
112
|
// check so `registerDefaultPlugins()` (which replays every default even
|
|
@@ -174,27 +130,6 @@ export function registerPlugin(plugin: Plugin): void {
|
|
|
174
130
|
);
|
|
175
131
|
}
|
|
176
132
|
|
|
177
|
-
// Capability negotiation. Every plugin must negotiate against
|
|
178
|
-
// `pluginRuntime`; we enforce that by requiring an entry to exist rather
|
|
179
|
-
// than special-casing it here, so the per-entry mismatch error is uniform.
|
|
180
|
-
if (!("pluginRuntime" in manifest.requires)) {
|
|
181
|
-
throw new PluginExecutionError(
|
|
182
|
-
`plugin ${name} must declare requires.pluginRuntime (e.g. "v1")`,
|
|
183
|
-
name,
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
for (const [api, requiredVersion] of Object.entries(manifest.requires)) {
|
|
188
|
-
const supported = ASSISTANT_API_VERSIONS[api];
|
|
189
|
-
if (!supported || !supported.includes(requiredVersion)) {
|
|
190
|
-
const exposed = supported ? supported.join(", ") : "(none)";
|
|
191
|
-
throw new PluginExecutionError(
|
|
192
|
-
`plugin ${name} requires ${api}@${requiredVersion}, assistant exposes ${exposed}`,
|
|
193
|
-
name,
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
133
|
registeredPlugins.set(name, plugin);
|
|
199
134
|
}
|
|
200
135
|
|
|
@@ -230,6 +165,31 @@ export function getMiddlewaresFor<P extends PipelineName>(
|
|
|
230
165
|
return out;
|
|
231
166
|
}
|
|
232
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Collect every registered plugin's hook for the given name, in
|
|
170
|
+
* registration order. Plugins that don't declare a hook for `name` are
|
|
171
|
+
* skipped. Used by the daemon to invoke chain-style hooks like
|
|
172
|
+
* `user-prompt-submit` where each plugin's hook may transform a shared
|
|
173
|
+
* context.
|
|
174
|
+
*
|
|
175
|
+
* The `TCtx` generic mirrors {@link PluginHookFn}'s — callers parameterize
|
|
176
|
+
* over the concrete context type their hook receives. Hooks that mutate
|
|
177
|
+
* the context in place return `void`; hooks that return a new context
|
|
178
|
+
* replace the threaded value for the next hook in the chain.
|
|
179
|
+
*/
|
|
180
|
+
export function getHooksFor<TCtx = unknown>(
|
|
181
|
+
name: string,
|
|
182
|
+
): PluginHookFn<TCtx>[] {
|
|
183
|
+
const out: PluginHookFn<TCtx>[] = [];
|
|
184
|
+
for (const plugin of registeredPlugins.values()) {
|
|
185
|
+
const hook = plugin.hooks?.[name];
|
|
186
|
+
if (hook) {
|
|
187
|
+
out.push(hook as PluginHookFn<TCtx>);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
|
|
233
193
|
/**
|
|
234
194
|
* Flatten every registered plugin's `injectors` array and sort the result by
|
|
235
195
|
* `order` ascending. Two injectors with the same `order` retain their relative
|
|
@@ -275,6 +235,14 @@ export function unregisterPlugin(name: string): void {
|
|
|
275
235
|
registeredPlugins.delete(name);
|
|
276
236
|
}
|
|
277
237
|
|
|
238
|
+
export function getRegisteredPlugin(name: string): Plugin | undefined {
|
|
239
|
+
return registeredPlugins.get(name);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function setRegisteredPlugin(plugin: Plugin): void {
|
|
243
|
+
registeredPlugins.set(plugin.manifest.name, plugin);
|
|
244
|
+
}
|
|
245
|
+
|
|
278
246
|
// ─── Test hooks ──────────────────────────────────────────────────────────────
|
|
279
247
|
|
|
280
248
|
/**
|