@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2
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/AGENTS.md +9 -3
- package/ARCHITECTURE.md +32 -10
- package/docs/architecture/memory.md +43 -5
- package/docs/architecture/security.md +96 -152
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/browser-use-architecture-phase2.md +128 -56
- package/docs/flux-turn-detection-spike.md +248 -0
- package/docs/guardian-request-flow.md +35 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/knip.json +3 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +289 -113
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/scripts/write-plugin-api-shim.ts +10 -0
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/app-control-flow.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -2
- package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
- package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/compaction-events.test.ts +8 -10
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +32 -21
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +154 -4
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/conversation-slash-queue.test.ts +3 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/document-sync-tags.test.ts +0 -75
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -11
- package/src/__tests__/file-ops-service.test.ts +163 -30
- package/src/__tests__/filesystem-tools.test.ts +23 -24
- package/src/__tests__/gateway-only-guard.test.ts +2 -5
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/host-file-read-tool.test.ts +16 -19
- package/src/__tests__/http-user-message-parity.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/media-generate-image.test.ts +131 -21
- package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +3 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/proxy-approval-callback.test.ts +1 -0
- package/src/__tests__/qdrant-manager.test.ts +14 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
- package/src/__tests__/run-due-schedules.test.ts +21 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
- package/src/__tests__/schedule-routes.test.ts +23 -0
- package/src/__tests__/schedule-store.test.ts +17 -0
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +3 -3
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
- package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
- package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +81 -101
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +68 -39
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-visual-surface.test.ts +1 -1
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/ui-work-result-surface.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
- package/src/__tests__/voice-session-bridge.test.ts +85 -29
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +76 -3
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/events/host-file.ts +2 -2
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +12 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
- package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +115 -41
- package/src/calls/voice-triage-escalate.ts +105 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/bundled-modules.ts +29 -0
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/db/repair.ts +4 -8
- package/src/cli/commands/domain.ts +6 -3
- package/src/cli/commands/email.ts +6 -3
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +84 -28
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/plugins.ts +85 -36
- package/src/cli/commands/schedules.ts +35 -1
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +14 -1
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/assistant-feature-flags.ts +36 -15
- package/src/config/balanced-model-experiment.ts +35 -0
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -12
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +11 -5
- package/src/config/default-profile-catalog.ts +179 -16
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/feature-flag-registry.json +12 -12
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/skills.ts +9 -2
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +31 -19
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +20 -10
- package/src/daemon/conversation-process.ts +20 -6
- package/src/daemon/conversation-runtime-assembly.ts +12 -6
- package/src/daemon/conversation-store.ts +14 -4
- package/src/daemon/conversation-surfaces.ts +75 -26
- package/src/daemon/conversation-tool-setup.ts +3 -6
- package/src/daemon/conversation.ts +154 -51
- package/src/daemon/doordash-steps.ts +2 -2
- package/src/daemon/lifecycle.ts +14 -1
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -26
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/trust-context.ts +12 -7
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/daemon/windows-compiled-entry.ts +4 -0
- package/src/documents/document-store.ts +143 -240
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +103 -45
- package/src/hooks/types.ts +5 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/gateway-flag-listener.ts +17 -3
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-manager.ts +16 -3
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +598 -323
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/live-voice/windows-compiled-live-voice.ts +4 -0
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/control.ts +1 -0
- package/src/monitoring/db-integrity-sample.ts +4 -5
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/approval-card-data.ts +33 -0
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -5
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +182 -52
- package/src/persistence/delivery-crud.ts +150 -36
- package/src/persistence/embeddings/qdrant-manager.ts +84 -49
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/schema/documents.ts +4 -4
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/constants.ts +8 -0
- package/src/plugin-api/conversation-turn.ts +195 -5
- package/src/plugin-api/index.ts +38 -6
- package/src/plugin-api/resolve-credential.ts +3 -2
- package/src/plugin-api/store-credential.ts +146 -0
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugin-api/webhook-url.ts +13 -11
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/main.ts +21 -7
- package/src/plugins/defaults/memory/AGENTS.md +15 -3
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/defaults/memory/v3/injector.ts +8 -0
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
- package/src/plugins/defaults/memory/worker-control.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/defaults/worker-entrypoints.ts +3 -0
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +37 -46
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/prompts/templates/system-sections.ts +0 -7
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -2
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +133 -36
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/control.ts +1 -0
- package/src/routes/route-host-client.ts +1 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +48 -15
- package/src/runtime/AGENTS.md +16 -17
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +18 -15
- package/src/runtime/effective-capabilities.test.ts +43 -0
- package/src/runtime/effective-capabilities.ts +27 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +181 -23
- package/src/runtime/routes/conversation-management-routes.ts +17 -12
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/conversation-routes.ts +43 -29
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/credential-routes.ts +26 -82
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/documents-routes.ts +3 -222
- package/src/runtime/routes/inbound-message-handler.ts +21 -45
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -2
- package/src/runtime/routes/playground/reset-circuit.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +38 -8
- package/src/runtime/routes/schedule-routes.ts +94 -7
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +69 -15
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/workspace-routes.ts +0 -9
- package/src/runtime/routes/workspace-utils.ts +3 -13
- package/src/runtime/services/conversation-serializer.ts +7 -2
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
- package/src/schedule/plugin-schedule-availability.ts +58 -0
- package/src/schedule/plugin-schedule-declarations.ts +23 -27
- package/src/schedule/plugin-schedule-reconciler.ts +12 -3
- package/src/schedule/schedule-store.ts +5 -1
- package/src/schedule/scheduler.ts +9 -4
- package/src/schedule/worker-control.ts +1 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
- package/src/subagent/consult-context.ts +11 -11
- package/src/subagent/consult-prompt.ts +26 -35
- package/src/subagent/manager.ts +21 -40
- package/src/subagent/notify.ts +7 -1
- package/src/subagent/types.ts +15 -12
- package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +38 -4
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/executor.ts +53 -15
- package/src/tools/filesystem/read.ts +27 -10
- package/src/tools/host-filesystem/read.ts +27 -15
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/registry.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
- package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
- package/src/tools/shared/filesystem/types.ts +5 -5
- package/src/tools/skills/scaffold-managed.ts +25 -7
- package/src/tools/subagent/spawn.ts +28 -88
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/__tests__/worker-process-command.test.ts +37 -0
- package/src/util/logger.ts +16 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/util/worker-process.ts +37 -4
- package/src/windows-compiled-cli.ts +32 -0
- package/src/windows-compiled-entry.ts +4 -0
- package/src/windows-compiled-logger.ts +6 -0
- package/src/windows-compiled-worker-entry.ts +29 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/__tests__/document-workspace-file.test.ts +0 -467
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
- package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
- package/src/subagent/consult-transcript.ts +0 -90
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin-contributed MCP servers.
|
|
3
|
+
*
|
|
4
|
+
* A plugin declares MCP servers in a root `mcp.json`, per the Agent
|
|
5
|
+
* Plugins 1.0.0 specification:
|
|
6
|
+
*
|
|
7
|
+
* {
|
|
8
|
+
* "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
|
|
9
|
+
* "mcpServers": {
|
|
10
|
+
* "unabyss": { "type": "streamable-http", "url": "https://mcp.unabyss.com" }
|
|
11
|
+
* }
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* This module reads those files and projects each entry onto the
|
|
15
|
+
* assistant's own `McpServerConfig` shape, so a plugin-declared server can
|
|
16
|
+
* flow through the same surfaces as one configured in the workspace
|
|
17
|
+
* `config.json`.
|
|
18
|
+
*
|
|
19
|
+
* Failure isolation follows the spec: an invalid top-level `mcp.json`
|
|
20
|
+
* disables MCP for that plugin only, and an invalid individual server
|
|
21
|
+
* disables only that entry. Neither ever throws, because a malformed file
|
|
22
|
+
* in one plugin must not remove another plugin's servers from a listing.
|
|
23
|
+
*
|
|
24
|
+
* Authentication is deliberately absent. Agent Plugins 1.0.0 defines no
|
|
25
|
+
* portable OAuth or credential-reference fields; authentication is
|
|
26
|
+
* client-managed, and any `headers` in the file are literal package data.
|
|
27
|
+
* A plugin therefore cannot ship a credential, and the assistant's own
|
|
28
|
+
* credential store stays the only place secrets live.
|
|
29
|
+
*
|
|
30
|
+
* Consumers must not resolve a plugin server's id against the
|
|
31
|
+
* `mcp:<serverId>:*` credential namespace. Those keys belong to
|
|
32
|
+
* workspace-configured servers, and a plugin controls both its server key
|
|
33
|
+
* and its URL, so honoring them for a plugin-declared server would send a
|
|
34
|
+
* workspace credential to an endpoint the plugin chose. Every config built
|
|
35
|
+
* here carries `source: "plugin"`, which is what `McpClient` reads to
|
|
36
|
+
* decide, so the rule travels with the server rather than with the caller.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
40
|
+
import { join } from "node:path";
|
|
41
|
+
|
|
42
|
+
import { z } from "zod";
|
|
43
|
+
|
|
44
|
+
import {
|
|
45
|
+
type AllPluginInfo,
|
|
46
|
+
listAllPlugins,
|
|
47
|
+
type ListInstalledPluginsOptions,
|
|
48
|
+
} from "../cli/lib/list-installed-plugins.js";
|
|
49
|
+
import type {
|
|
50
|
+
McpTransport,
|
|
51
|
+
ResolvedMcpServerConfig,
|
|
52
|
+
} from "../config/schemas/mcp.js";
|
|
53
|
+
import { getLogger } from "../util/logger.js";
|
|
54
|
+
|
|
55
|
+
const log = getLogger("plugin-mcp-servers");
|
|
56
|
+
|
|
57
|
+
/** Filename a plugin declares its MCP servers in, at the plugin root. */
|
|
58
|
+
export const PLUGIN_MCP_MANIFEST = "mcp.json";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Risk level assigned to a plugin-declared server. `mcp.json` has no risk
|
|
62
|
+
* field, since the spec defines none, so a host default applies.
|
|
63
|
+
*
|
|
64
|
+
* `low` — so a plugin's tools run without prompting under the default
|
|
65
|
+
* auto-approve threshold — because the review happens earlier: the
|
|
66
|
+
* marketplace catalog (`plugins/marketplace.json`) is a curated whitelist
|
|
67
|
+
* of SHA-pinned entries, and installing a plugin is itself the user's
|
|
68
|
+
* decision to run the code it ships. Gating every call afterwards prompts
|
|
69
|
+
* on the tools the user installed the plugin to get.
|
|
70
|
+
*
|
|
71
|
+
* The gap this leaves is deliberate and known: a plugin installed
|
|
72
|
+
* off-marketplace straight from a GitHub URL gets the same default, and
|
|
73
|
+
* nothing recorded at install time distinguishes the two afterwards. A
|
|
74
|
+
* provenance signal is what would let this default be curation-gated
|
|
75
|
+
* rather than blanket. The user can still override per server — see the
|
|
76
|
+
* `defaultRiskLevel` field on a workspace `config.json` entry, which
|
|
77
|
+
* outranks a plugin's declaration of the same id.
|
|
78
|
+
*/
|
|
79
|
+
const PLUGIN_SERVER_DEFAULT_RISK = "low" as const;
|
|
80
|
+
|
|
81
|
+
/** Matches the `maxTools` default in `McpServerConfigSchema`. */
|
|
82
|
+
const PLUGIN_SERVER_DEFAULT_MAX_TOOLS = 20;
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Wire schema (Agent Plugins 1.0.0)
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
const PluginStdioServerSchema = z.object({
|
|
89
|
+
type: z.literal("stdio"),
|
|
90
|
+
command: z.string().min(1),
|
|
91
|
+
args: z.array(z.string()).optional(),
|
|
92
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
93
|
+
cwd: z.string().optional(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const PluginHttpServerSchema = z.object({
|
|
97
|
+
type: z.enum(["streamable-http", "sse"]),
|
|
98
|
+
url: z.string().min(1),
|
|
99
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const PluginMcpServerSchema = z.discriminatedUnion("type", [
|
|
103
|
+
PluginStdioServerSchema,
|
|
104
|
+
PluginHttpServerSchema,
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
const PluginMcpManifestSchema = z.object({
|
|
108
|
+
mcpServers: z.record(z.string(), z.unknown()),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Public shape
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
/** One MCP server declared by one plugin. */
|
|
116
|
+
export interface PluginMcpServer {
|
|
117
|
+
/** Server id in the assistant's namespace. See {@link buildServerId}. */
|
|
118
|
+
readonly id: string;
|
|
119
|
+
/** Directory name of the plugin that declared it. */
|
|
120
|
+
readonly pluginName: string;
|
|
121
|
+
/** Key this server appears under in the plugin's `mcp.json`. */
|
|
122
|
+
readonly serverKey: string;
|
|
123
|
+
/** Projected onto the assistant's own server-config shape. */
|
|
124
|
+
readonly config: ResolvedMcpServerConfig;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** A plugin's `mcp.json` that could not be used, in whole or in part. */
|
|
128
|
+
export interface PluginMcpIssue {
|
|
129
|
+
readonly pluginName: string;
|
|
130
|
+
/** Absent when the whole manifest is unusable rather than one entry. */
|
|
131
|
+
readonly serverKey?: string;
|
|
132
|
+
readonly message: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface PluginMcpServersResult {
|
|
136
|
+
readonly servers: readonly PluginMcpServer[];
|
|
137
|
+
readonly issues: readonly PluginMcpIssue[];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Server id for a plugin-declared server.
|
|
142
|
+
*
|
|
143
|
+
* Plugin servers share one namespace with workspace servers and with each
|
|
144
|
+
* other, so the plugin name is the qualifier. The redundant case is
|
|
145
|
+
* collapsed: a plugin named `unabyss` whose server key is also `unabyss`
|
|
146
|
+
* yields `unabyss`, not `unabyss__unabyss`, which matters because the id
|
|
147
|
+
* is embedded in every tool name the server contributes
|
|
148
|
+
* (`mcp__<id>__<tool>`).
|
|
149
|
+
*/
|
|
150
|
+
export function buildServerId(pluginName: string, serverKey: string): string {
|
|
151
|
+
return pluginName === serverKey ? pluginName : `${pluginName}__${serverKey}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Expand the two path variables the Agent Plugins spec defines for stdio
|
|
156
|
+
* servers. They interpolate textually in `args`, `env` values, and `cwd`
|
|
157
|
+
* only, never in `command`, a URL, or a header, so a manifest cannot use
|
|
158
|
+
* them to build the executable path itself.
|
|
159
|
+
*/
|
|
160
|
+
export function interpolatePluginPaths(
|
|
161
|
+
value: string,
|
|
162
|
+
pluginRoot: string,
|
|
163
|
+
pluginData: string,
|
|
164
|
+
): string {
|
|
165
|
+
return value
|
|
166
|
+
.replaceAll("${PLUGIN_ROOT}", pluginRoot)
|
|
167
|
+
.replaceAll("${PLUGIN_DATA}", pluginData);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Whether a directory is a plugin the runtime would actually load.
|
|
172
|
+
*
|
|
173
|
+
* `listAllPlugins` is an inventory of directories and reports a malformed
|
|
174
|
+
* entry rather than dropping it, so it happily returns a directory with no
|
|
175
|
+
* usable `package.json`. The runtime loader rejects those in
|
|
176
|
+
* `parsePluginManifest`: the manifest must parse and carry a non-empty
|
|
177
|
+
* `name`. Applying the same gate here keeps `mcp.json` from being honored
|
|
178
|
+
* for a directory that will never load as a plugin.
|
|
179
|
+
*/
|
|
180
|
+
export function hasLoadableManifest(plugin: AllPluginInfo): boolean {
|
|
181
|
+
const name = plugin.packageJson?.name;
|
|
182
|
+
return typeof name === "string" && name.length > 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Reading
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Read every installed plugin's `mcp.json` and return the servers they
|
|
191
|
+
* declare, plus anything that could not be read.
|
|
192
|
+
*
|
|
193
|
+
* A disabled plugin is skipped entirely: `.disabled` means the plugin
|
|
194
|
+
* contributes no hooks and no tools, and an MCP server it declares is no
|
|
195
|
+
* different.
|
|
196
|
+
*/
|
|
197
|
+
export function readPluginMcpServers(
|
|
198
|
+
opts: ListInstalledPluginsOptions = {},
|
|
199
|
+
): PluginMcpServersResult {
|
|
200
|
+
const servers: PluginMcpServer[] = [];
|
|
201
|
+
const issues: PluginMcpIssue[] = [];
|
|
202
|
+
const seen = new Map<string, string>();
|
|
203
|
+
|
|
204
|
+
let plugins: readonly AllPluginInfo[];
|
|
205
|
+
try {
|
|
206
|
+
plugins = listAllPlugins(opts);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
// The plugins directory being unreadable is not a reason to fail a
|
|
209
|
+
// listing that also has workspace-configured servers in it.
|
|
210
|
+
log.warn({ err }, "Could not enumerate plugins for MCP declarations");
|
|
211
|
+
return { servers: [], issues: [] };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const plugin of plugins) {
|
|
215
|
+
if (plugin.disabled) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const manifestPath = join(plugin.target, PLUGIN_MCP_MANIFEST);
|
|
219
|
+
if (!existsSync(manifestPath)) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (!hasLoadableManifest(plugin)) {
|
|
224
|
+
issues.push({
|
|
225
|
+
pluginName: plugin.name,
|
|
226
|
+
message: `${PLUGIN_MCP_MANIFEST} ignored: package.json is missing, unparseable, or has no name, so the runtime will not load this directory as a plugin`,
|
|
227
|
+
});
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const parsed = parseManifest(manifestPath);
|
|
232
|
+
if ("error" in parsed) {
|
|
233
|
+
issues.push({ pluginName: plugin.name, message: parsed.error });
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for (const [serverKey, raw] of Object.entries(parsed.mcpServers)) {
|
|
238
|
+
if (serverKey.trim().length === 0) {
|
|
239
|
+
issues.push({
|
|
240
|
+
pluginName: plugin.name,
|
|
241
|
+
message: "server key must be a non-empty string; skipping",
|
|
242
|
+
});
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const entry = PluginMcpServerSchema.safeParse(raw);
|
|
247
|
+
if (!entry.success) {
|
|
248
|
+
issues.push({
|
|
249
|
+
pluginName: plugin.name,
|
|
250
|
+
serverKey,
|
|
251
|
+
message: `invalid server entry: ${entry.error.issues
|
|
252
|
+
.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
253
|
+
.join("; ")}`,
|
|
254
|
+
});
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const id = buildServerId(plugin.name, serverKey);
|
|
259
|
+
const previous = seen.get(id);
|
|
260
|
+
if (previous) {
|
|
261
|
+
// Two plugins claiming one id would silently shadow each other, and
|
|
262
|
+
// the loser's tools would just be missing. Report both instead.
|
|
263
|
+
issues.push({
|
|
264
|
+
pluginName: plugin.name,
|
|
265
|
+
serverKey,
|
|
266
|
+
message: `server id "${id}" is already declared by plugin "${previous}"; skipping`,
|
|
267
|
+
});
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
seen.set(id, plugin.name);
|
|
271
|
+
|
|
272
|
+
// The spec allows `cwd` on a stdio server; the assistant's transport
|
|
273
|
+
// has no such field. Dropping it silently would leave a server that
|
|
274
|
+
// resolves its relative paths against the wrong directory and fails
|
|
275
|
+
// for no visible reason, so say so.
|
|
276
|
+
if (entry.data.type === "stdio" && entry.data.cwd) {
|
|
277
|
+
issues.push({
|
|
278
|
+
pluginName: plugin.name,
|
|
279
|
+
serverKey,
|
|
280
|
+
message: `"cwd" is not supported by this host and was ignored; the server runs in the daemon's working directory`,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
servers.push({
|
|
285
|
+
id,
|
|
286
|
+
pluginName: plugin.name,
|
|
287
|
+
serverKey,
|
|
288
|
+
config: {
|
|
289
|
+
transport: projectTransport(entry.data, plugin.target),
|
|
290
|
+
enabled: true,
|
|
291
|
+
defaultRiskLevel: PLUGIN_SERVER_DEFAULT_RISK,
|
|
292
|
+
maxTools: PLUGIN_SERVER_DEFAULT_MAX_TOOLS,
|
|
293
|
+
source: "plugin",
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return { servers, issues };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function parseManifest(
|
|
303
|
+
path: string,
|
|
304
|
+
): { mcpServers: Record<string, unknown> } | { error: string } {
|
|
305
|
+
let raw: string;
|
|
306
|
+
try {
|
|
307
|
+
raw = readFileSync(path, "utf8");
|
|
308
|
+
} catch (err) {
|
|
309
|
+
return {
|
|
310
|
+
error: `${PLUGIN_MCP_MANIFEST} unreadable: ${err instanceof Error ? err.message : String(err)}`,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
let json: unknown;
|
|
315
|
+
try {
|
|
316
|
+
json = JSON.parse(raw);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
return {
|
|
319
|
+
error: `${PLUGIN_MCP_MANIFEST} invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const manifest = PluginMcpManifestSchema.safeParse(json);
|
|
324
|
+
if (!manifest.success) {
|
|
325
|
+
return {
|
|
326
|
+
error: `${PLUGIN_MCP_MANIFEST} is missing a valid "mcpServers" object`,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
return { mcpServers: manifest.data.mcpServers };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Project one spec-shaped entry onto the assistant's transport union. The
|
|
334
|
+
* two vocabularies already agree on type names and required fields, so this
|
|
335
|
+
* is a field copy plus path interpolation for the stdio case.
|
|
336
|
+
*/
|
|
337
|
+
function projectTransport(
|
|
338
|
+
entry: z.infer<typeof PluginMcpServerSchema>,
|
|
339
|
+
pluginRoot: string,
|
|
340
|
+
): McpTransport {
|
|
341
|
+
if (entry.type === "stdio") {
|
|
342
|
+
const pluginData = join(pluginRoot, "data");
|
|
343
|
+
const expand = (v: string): string =>
|
|
344
|
+
interpolatePluginPaths(v, pluginRoot, pluginData);
|
|
345
|
+
return {
|
|
346
|
+
type: "stdio",
|
|
347
|
+
command: entry.command,
|
|
348
|
+
args: (entry.args ?? []).map(expand),
|
|
349
|
+
...(entry.env && {
|
|
350
|
+
env: Object.fromEntries(
|
|
351
|
+
Object.entries(entry.env).map(([k, v]) => [k, expand(v)]),
|
|
352
|
+
),
|
|
353
|
+
}),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
type: entry.type,
|
|
358
|
+
url: entry.url,
|
|
359
|
+
...(entry.headers && { headers: entry.headers }),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
* - Plugins are never "registered" as a unit — we register their tools into
|
|
27
27
|
* the global tool registry.
|
|
28
28
|
*
|
|
29
|
-
* Tools are populated at boot by `loadUserPlugins()`;
|
|
30
|
-
*
|
|
31
|
-
* each hook through the hook loader on demand.
|
|
29
|
+
* Tools are populated at boot by `loadUserPlugins()`; hook dispatch reads
|
|
30
|
+
* discovered plugin names from this cache via {@link getDiscoveredUserPluginNames}
|
|
31
|
+
* and resolves each hook through the hook loader on demand.
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
@@ -38,7 +38,6 @@ import type { Logger } from "pino";
|
|
|
38
38
|
|
|
39
39
|
import {
|
|
40
40
|
clearPluginHooks,
|
|
41
|
-
collectUserHookEntries,
|
|
42
41
|
evictHooksForOwner,
|
|
43
42
|
hasWorkspaceHooks,
|
|
44
43
|
type HookOwnerKind,
|
|
@@ -47,7 +46,7 @@ import {
|
|
|
47
46
|
runShutdownHook,
|
|
48
47
|
WORKSPACE_HOOKS_OWNER,
|
|
49
48
|
} from "../hooks/hook-loader.js";
|
|
50
|
-
import type {
|
|
49
|
+
import type { ShutdownReason } from "../plugin-api/types.js";
|
|
51
50
|
import {
|
|
52
51
|
registerPluginSecretPatterns,
|
|
53
52
|
resetPluginSecretPatternsForTests,
|
|
@@ -76,11 +75,7 @@ import {
|
|
|
76
75
|
importWithTimeout,
|
|
77
76
|
setSurfaceImportTimeout,
|
|
78
77
|
} from "./surface-import.js";
|
|
79
|
-
import type {
|
|
80
|
-
|
|
81
|
-
// Re-export for type compat — consumers that import HookFunction from
|
|
82
|
-
// the mtime cache module still resolve.
|
|
83
|
-
export type { HookFunction } from "./types.js";
|
|
78
|
+
import type { PluginCredentialKeyPattern } from "./types.js";
|
|
84
79
|
|
|
85
80
|
const log = getLogger("plugin-mtime-cache");
|
|
86
81
|
|
|
@@ -206,48 +201,32 @@ let lastVersions: Record<string, PluginSourceVersion> = {};
|
|
|
206
201
|
/** In-flight reconcile — concurrent imperative pokes await it rather than racing. */
|
|
207
202
|
let reconcileInFlight: Promise<void> | null = null;
|
|
208
203
|
|
|
209
|
-
// ───
|
|
204
|
+
// ─── Discovery reads ─────────────────────────────────────────────────────────
|
|
210
205
|
|
|
211
206
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* This is a pure cache read: it never scans disk, activates a plugin, or
|
|
217
|
-
* runs `init`. Activation happens only at boot ({@link populateCacheAtBoot})
|
|
218
|
-
* and through the imperative install/uninstall poke
|
|
219
|
-
* ({@link reconcilePluginSourcesNow}) — both main-daemon paths — so a
|
|
220
|
-
* sidecar process that dispatches hooks (a worker running conversation
|
|
221
|
-
* turns) can never bring a plugin up in its own process.
|
|
222
|
-
*
|
|
223
|
-
* `effectiveEnabledPlugins` carries the per-chat plugin scope: when non-null,
|
|
224
|
-
* user plugins outside the set are skipped (standalone workspace hooks always
|
|
225
|
-
* run). `null`/omitted means no per-chat restriction.
|
|
207
|
+
* Plugin names currently in the discovery cache, in install-date order.
|
|
208
|
+
* Hook lookup in `hooks/registry.ts` walks this set; the cache itself does
|
|
209
|
+
* not resolve hooks.
|
|
226
210
|
*/
|
|
227
|
-
export
|
|
228
|
-
|
|
229
|
-
effectiveEnabledPlugins?: Set<string> | null,
|
|
230
|
-
): Promise<HookEntry<TCtx>[]> {
|
|
231
|
-
return collectUserHookEntries<TCtx>(
|
|
232
|
-
hookName,
|
|
233
|
-
discoveredPluginDirs.values(),
|
|
234
|
-
effectiveEnabledPlugins,
|
|
235
|
-
);
|
|
211
|
+
export function getDiscoveredUserPluginNames(): Iterable<string> {
|
|
212
|
+
return discoveredPluginDirs.values();
|
|
236
213
|
}
|
|
237
214
|
|
|
238
215
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
216
|
+
* True when this daemon process brought the plugin directory `dir` up:
|
|
217
|
+
* {@link bringUpPlugin} ran for it and its `init` was attempted. An `init` that
|
|
218
|
+
* threw still counts, because activation never aborts on init failure (see
|
|
219
|
+
* {@link activatePlugin}), and that is deliberate: an init-failed plugin's
|
|
220
|
+
* hooks and tools stay live, so its schedules must too. What membership
|
|
221
|
+
* excludes is a directory dropped in out-of-band that no boot scan and no
|
|
222
|
+
* reconcile pass has activated yet.
|
|
223
|
+
*
|
|
224
|
+
* The backing map is per-process, so a process that runs no plugin loader (the
|
|
225
|
+
* schedule worker, sidecar turn workers) reads `false` for every directory.
|
|
226
|
+
* Only code that provably runs in the main daemon may treat this as an answer.
|
|
241
227
|
*/
|
|
242
|
-
export
|
|
243
|
-
|
|
244
|
-
effectiveEnabledPlugins?: Set<string> | null,
|
|
245
|
-
): Promise<HookFunction<TCtx>[]> {
|
|
246
|
-
const entries = await getUserHookEntriesFor<TCtx>(
|
|
247
|
-
hookName,
|
|
248
|
-
effectiveEnabledPlugins,
|
|
249
|
-
);
|
|
250
|
-
return entries.map((e) => e.fn);
|
|
228
|
+
export function isPluginDirActivated(dir: string): boolean {
|
|
229
|
+
return discoveredPluginDirs.has(dir);
|
|
251
230
|
}
|
|
252
231
|
|
|
253
232
|
// ─── Source-versions reconcile ───────────────────────────────────────────────
|
|
@@ -300,6 +279,18 @@ export async function reconcilePluginSourcesNow(): Promise<void> {
|
|
|
300
279
|
} catch (err) {
|
|
301
280
|
log.error({ err }, "plugin schedule reconcile failed");
|
|
302
281
|
}
|
|
282
|
+
// Converge plugin-declared MCP servers the same way, so an install
|
|
283
|
+
// brings its `mcp.json` servers up and an uninstall/disable takes their
|
|
284
|
+
// connected clients and registered tools back down. Reloads only when
|
|
285
|
+
// the declared set actually moved. Lazily imported for the same reason
|
|
286
|
+
// as the schedule reconciler, and equally self-contained.
|
|
287
|
+
try {
|
|
288
|
+
const { reconcilePluginMcpServers } =
|
|
289
|
+
await import("../daemon/mcp-reload-service.js");
|
|
290
|
+
await reconcilePluginMcpServers();
|
|
291
|
+
} catch (err) {
|
|
292
|
+
log.error({ err }, "plugin MCP reconcile failed");
|
|
293
|
+
}
|
|
303
294
|
})().finally(() => {
|
|
304
295
|
reconcileInFlight = null;
|
|
305
296
|
});
|
|
@@ -1080,7 +1071,7 @@ async function deactivatePlugin(
|
|
|
1080
1071
|
* Called by `loadUserPlugins()` during daemon startup. After boot, the same
|
|
1081
1072
|
* `activatePlugin`/`deactivatePlugin` reconciliation runs only through the
|
|
1082
1073
|
* imperative poke ({@link reconcilePluginSourcesNow}) the install/uninstall/
|
|
1083
|
-
* enable
|
|
1074
|
+
* upgrade/enable routes call, so plugin lifecycle stays confined to the main
|
|
1084
1075
|
* daemon — dispatch-time hook and tool reads never activate anything.
|
|
1085
1076
|
*/
|
|
1086
1077
|
export async function populateCacheAtBoot(
|
package/src/plugins/pipeline.ts
CHANGED
|
@@ -25,7 +25,10 @@ import { getHookEntriesFor } from "../hooks/registry.js";
|
|
|
25
25
|
import type { BaseHookContext } from "../hooks/types.js";
|
|
26
26
|
import { type HookName, HOOKS } from "../plugin-api/constants.js";
|
|
27
27
|
import { getLogger } from "../util/logger.js";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
runInPluginContext,
|
|
30
|
+
runOutsidePluginContext,
|
|
31
|
+
} from "./plugin-execution-context.js";
|
|
29
32
|
import type { HookEntry } from "./types.js";
|
|
30
33
|
|
|
31
34
|
// ─── Hook runner ────────────────────────────────────────────────────────────
|
|
@@ -303,9 +306,10 @@ export async function callWithTimeout<T>(
|
|
|
303
306
|
*
|
|
304
307
|
* When `initialCtx` carries a `conversationId`, it is passed to
|
|
305
308
|
* {@link getHookEntriesFor}, which resolves the conversation's per-chat plugin
|
|
306
|
-
* scope (memory, then DB) and skips a hook whose
|
|
307
|
-
* the effective set.
|
|
308
|
-
*
|
|
309
|
+
* scope (memory, then DB) and skips a user-plugin hook whose owner is
|
|
310
|
+
* outside the effective set. First-party default plugins always run (unless
|
|
311
|
+
* workspace-disabled). Contexts without a `conversationId` impose no
|
|
312
|
+
* restriction: every globally-enabled plugin's hook runs.
|
|
309
313
|
*
|
|
310
314
|
* Before each hook runs, the pipeline stamps the {@link BaseHookContext}
|
|
311
315
|
* capabilities onto its (freshly-cloned) draft, both bound to that hook's
|
|
@@ -354,12 +358,15 @@ export async function runHook<TInput extends object>(
|
|
|
354
358
|
};
|
|
355
359
|
try {
|
|
356
360
|
// Mark the contributing plugin as in context so host APIs the hook
|
|
357
|
-
// reaches (e.g. resolveCredential) can scope to it.
|
|
358
|
-
//
|
|
361
|
+
// reaches (e.g. resolveCredential) can scope to it. A standalone
|
|
362
|
+
// workspace hook is not a plugin, and runs with the context explicitly
|
|
363
|
+
// cleared rather than merely unset: the turn may have been started by a
|
|
364
|
+
// plugin (a route handler calling `runConversationTurn`), and this hook
|
|
365
|
+
// must not inherit that plugin's identity.
|
|
359
366
|
const invokeHook =
|
|
360
367
|
owner.kind === "plugin"
|
|
361
368
|
? () => runInPluginContext(owner.id, () => fn(draft))
|
|
362
|
-
: () => fn(draft);
|
|
369
|
+
: () => runOutsidePluginContext(() => fn(draft));
|
|
363
370
|
const result = await callWithTimeout(
|
|
364
371
|
invokeHook,
|
|
365
372
|
HOOK_TIMEOUT_MS,
|
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Plugin execution context
|
|
2
|
+
* Plugin execution context. Tracks which plugin's code is currently running.
|
|
3
3
|
*
|
|
4
4
|
* Host APIs exposed to plugins through `@vellumai/plugin-api` sometimes need to
|
|
5
5
|
* know *which* plugin is calling them so they can scope their behavior to that
|
|
6
6
|
* plugin (e.g. {@link ../plugin-api/resolve-credential.resolveCredential} limits
|
|
7
7
|
* a plugin to its own credentials). A plugin's manifest name is not threaded
|
|
8
|
-
* through every host call, so
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* through every host call, so every seam that enters a plugin's code marks the
|
|
9
|
+
* plugin as "in context" for the duration of that invocation via an
|
|
10
|
+
* {@link AsyncLocalStorage}: the pipeline that invokes its hook, the executor
|
|
11
|
+
* that runs its tool, and the dispatcher that serves its
|
|
12
|
+
* `/x/plugins/<name>/` routes. Host APIs read {@link getCurrentPluginName} to
|
|
13
|
+
* recover it.
|
|
12
14
|
*
|
|
13
15
|
* The store propagates across `await` boundaries, so a plugin that awaits a
|
|
14
|
-
* host API deep inside its hook/tool body is still seen as in context.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
16
|
+
* host API deep inside its hook/tool/route body is still seen as in context.
|
|
17
|
+
* That propagation is why the same seams clear the context on the way back out
|
|
18
|
+
* to host code ({@link runOutsidePluginContext}): a plugin route can start a
|
|
19
|
+
* whole conversation turn, and the host tools and workspace hooks that turn
|
|
20
|
+
* runs are not the plugin's code, so they must not inherit its identity.
|
|
21
|
+
*
|
|
22
|
+
* When no plugin is in context (host-internal callers, the CLI, tests), the
|
|
23
|
+
* store is empty and scoped APIs fall back to their unscoped behavior.
|
|
17
24
|
*/
|
|
18
25
|
|
|
19
26
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20
27
|
|
|
21
28
|
export interface PluginExecutionContext {
|
|
22
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Manifest name of the plugin whose hook, tool, or route is currently
|
|
31
|
+
* executing.
|
|
32
|
+
*/
|
|
23
33
|
pluginName: string;
|
|
24
34
|
}
|
|
25
35
|
|
|
@@ -36,8 +46,22 @@ export function runInPluginContext<T>(pluginName: string, fn: () => T): T {
|
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
49
|
+
* Run `fn` with no plugin in context, whatever the caller is running inside.
|
|
50
|
+
*
|
|
51
|
+
* Host code reached *from* a plugin (a default tool executed during a turn a
|
|
52
|
+
* plugin route started, a workspace hook fired inside a plugin's hook) is not
|
|
53
|
+
* the plugin's code and must not borrow its identity: scoped APIs would
|
|
54
|
+
* attribute the host's work to that plugin, or refuse it for being out of the
|
|
55
|
+
* plugin's scope. The seams that dispatch non-plugin code call this so the
|
|
56
|
+
* store reflects who is really running rather than who started the chain.
|
|
57
|
+
*/
|
|
58
|
+
export function runOutsidePluginContext<T>(fn: () => T): T {
|
|
59
|
+
return storage.exit(fn);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Name of the plugin whose hook, tool, or route is currently executing, or
|
|
64
|
+
* `undefined` when no plugin is in context.
|
|
41
65
|
*/
|
|
42
66
|
export function getCurrentPluginName(): string | undefined {
|
|
43
67
|
return storage.getStore()?.pluginName;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default-plugin surface for sidecar worker processes.
|
|
3
|
+
*
|
|
4
|
+
* Worker processes that run real agent conversations (the memory jobs worker,
|
|
5
|
+
* the schedule worker) register the first-party default plugins' hooks and
|
|
6
|
+
* runtime injectors so those conversations get the same hook-driven behavior as
|
|
7
|
+
* conversations in the daemon: image captioning and vision-rejection recovery,
|
|
8
|
+
* tool-result truncation, history repair, and the per-turn runtime injections.
|
|
9
|
+
* Hook and injector dispatch is process-global, so one call at worker startup
|
|
10
|
+
* covers every conversation that process runs.
|
|
11
|
+
*
|
|
12
|
+
* Plugin `init` hooks are deliberately not run here. Registration is pure
|
|
13
|
+
* hook-table and injector-table population, and the default plugins degrade
|
|
14
|
+
* gracefully without their init-owned state (the image-fallback caption cache
|
|
15
|
+
* falls back to in-memory caching when its SQLite handle is absent). The memory
|
|
16
|
+
* plugin's init must never run in a worker: it starts the memory jobs worker
|
|
17
|
+
* process itself.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
registerDefaultPluginInjectors,
|
|
22
|
+
registerDefaultPlugins,
|
|
23
|
+
} from "./defaults/index.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register the first-party default plugins' hooks and runtime injectors in the
|
|
27
|
+
* current process. Both registration functions are idempotent, so repeat calls
|
|
28
|
+
* are safe.
|
|
29
|
+
*/
|
|
30
|
+
export function registerWorkerPluginSurface(): void {
|
|
31
|
+
registerDefaultPlugins();
|
|
32
|
+
registerDefaultPluginInjectors();
|
|
33
|
+
}
|
|
@@ -267,13 +267,6 @@ export const BUNDLED_SYSTEM_SECTIONS: readonly BundledSection[] = [
|
|
|
267
267
|
body: "",
|
|
268
268
|
enabled: "!excludeCustomPrefix",
|
|
269
269
|
},
|
|
270
|
-
{
|
|
271
|
-
id: "01-delegate-subagents",
|
|
272
|
-
body: `## Delegate independent work
|
|
273
|
-
|
|
274
|
-
When part of a task can run on its own — a research sweep, a multi-file investigation, a build-and-test loop — hand it off instead of grinding through it inline: load the \`subagent\` skill, then \`subagent_spawn\` early and in parallel. Make delegating that kind of work your default, not a last resort; an unnecessary subagent is cheaper than serialized work, and a long inline dig floods your own context.
|
|
275
|
-
`,
|
|
276
|
-
},
|
|
277
270
|
{
|
|
278
271
|
id: "01-parallel-tool-calls",
|
|
279
272
|
body: `<use_parallel_tool_calls>
|