@vellumai/assistant 0.10.2-dev.202606250318.5e7cfb0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +0 -20
- package/docs/workspace-tools.md +33 -42
- package/eslint-rules/cli-no-daemon-internals.js +0 -6
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -31
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -44
- package/node_modules/@vellumai/gateway-client/src/index.ts +0 -14
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -17
- package/node_modules/@vellumai/service-contracts/package.json +0 -1
- package/node_modules/@vellumai/service-contracts/src/index.ts +0 -1
- package/openapi.yaml +0 -155
- package/package.json +1 -4
- package/scripts/test.sh +15 -36
- package/src/__tests__/actor-token-service.test.ts +14 -36
- package/src/__tests__/agent-loop-override-profile.test.ts +0 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +0 -2
- package/src/__tests__/annotate-activity-metadata.test.ts +0 -2
- package/src/__tests__/annotate-risk-options.test.ts +0 -2
- package/src/__tests__/approval-cascade.test.ts +0 -2
- package/src/__tests__/assistant-attachments.test.ts +0 -42
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -2
- package/src/__tests__/build-persisted-content.test.ts +0 -2
- package/src/__tests__/call-controller.test.ts +0 -19
- package/src/__tests__/channel-guardian.test.ts +58 -94
- package/src/__tests__/channel-reply-delivery.test.ts +0 -2
- package/src/__tests__/compaction-events.test.ts +0 -2
- package/src/__tests__/compaction.benchmark.test.ts +0 -2
- package/src/__tests__/compactor-call-site-logging.test.ts +0 -2
- package/src/__tests__/compactor-low-watermark-cut.test.ts +0 -2
- package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -2
- package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -2
- package/src/__tests__/compactor-web-search-strip.test.ts +0 -2
- package/src/__tests__/config-loader-backfill.test.ts +10 -123
- package/src/__tests__/config-schema.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -31
- package/src/__tests__/contacts-relay-reads.test.ts +15 -13
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop.test.ts +0 -134
- package/src/__tests__/conversation-analysis-routes.test.ts +0 -2
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -2
- package/src/__tests__/conversation-history-web-search.test.ts +0 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +0 -2
- package/src/__tests__/conversation-pairing.test.ts +0 -2
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -2
- package/src/__tests__/conversation-process-callsite.test.ts +0 -2
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -2
- package/src/__tests__/conversation-queue.test.ts +0 -91
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -14
- package/src/__tests__/conversation-slash-queue.test.ts +0 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -2
- package/src/__tests__/conversation-speed-override.test.ts +0 -2
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -29
- package/src/__tests__/conversation-title-service.test.ts +0 -2
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -47
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -2
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-migration-rollback.test.ts +171 -205
- package/src/__tests__/db-test-helpers.ts +4 -5
- package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -4
- package/src/__tests__/disk-pressure-guard.test.ts +0 -41
- package/src/__tests__/dm-persistence.test.ts +0 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +5 -10
- package/src/__tests__/events-dev-bypass-actor.test.ts +1 -7
- package/src/__tests__/exploration-drift-hook.test.ts +2 -3
- package/src/__tests__/filing-service.test.ts +0 -2
- package/src/__tests__/guardian-binding-drift-heal.test.ts +10 -75
- package/src/__tests__/guardian-dispatch.test.ts +1 -95
- package/src/__tests__/guardian-outbound-http.test.ts +0 -13
- package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -2
- package/src/__tests__/heartbeat-service.test.ts +0 -2
- package/src/__tests__/helpers/channel-test-adapter.ts +7 -1
- package/src/__tests__/host-app-control-routes.test.ts +30 -24
- package/src/__tests__/host-bash-routes.test.ts +41 -31
- package/src/__tests__/host-browser-routes.test.ts +32 -26
- package/src/__tests__/host-cu-routes-targeted.test.ts +33 -25
- package/src/__tests__/host-file-routes-targeted.test.ts +52 -40
- package/src/__tests__/host-transfer-routes-targeted.test.ts +43 -31
- package/src/__tests__/http-user-message-parity.test.ts +8 -290
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -28
- package/src/__tests__/inbound-slack-persistence.test.ts +0 -2
- package/src/__tests__/invite-redemption-service.test.ts +0 -198
- package/src/__tests__/llm-context-normalization.test.ts +0 -105
- package/src/__tests__/llm-request-log-error-payload.test.ts +9 -71
- package/src/__tests__/llm-usage-store.test.ts +0 -25
- package/src/__tests__/mcp-health-check.test.ts +1 -2
- package/src/__tests__/media-stream-server-integration.test.ts +0 -127
- package/src/__tests__/memory-retrieval-hook.test.ts +0 -2
- package/src/__tests__/messaging-send-tool.test.ts +0 -2
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +5 -146
- package/src/__tests__/native-web-search.test.ts +0 -2
- package/src/__tests__/non-member-access-request.test.ts +17 -189
- package/src/__tests__/notification-broadcaster.test.ts +0 -4
- package/src/__tests__/notification-decision-recipient-context.test.ts +32 -33
- package/src/__tests__/notification-deep-link.test.ts +0 -6
- package/src/__tests__/notification-guardian-path.test.ts +0 -19
- package/src/__tests__/openai-provider.test.ts +12 -22
- package/src/__tests__/openai-responses-provider.test.ts +2 -12
- package/src/__tests__/outbound-slack-persistence.test.ts +0 -2
- package/src/__tests__/pending-interactions-resolved-event.test.ts +4 -7
- package/src/__tests__/persistence-secret-redaction.test.ts +0 -2
- package/src/__tests__/plugin-bootstrap.test.ts +73 -3
- package/src/__tests__/plugin-route-contribution.test.ts +17 -4
- package/src/__tests__/plugin-tool-contribution.test.ts +18 -3
- package/src/__tests__/plugin-types.test.ts +2 -0
- package/src/__tests__/process-message-background-slack.test.ts +0 -2
- package/src/__tests__/process-message-display-content.test.ts +0 -2
- package/src/__tests__/provider-error-scenarios.test.ts +4 -5
- package/src/__tests__/provider-usage-tracking.test.ts +0 -39
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -2
- package/src/__tests__/registry.test.ts +1 -4
- package/src/__tests__/relay-server.test.ts +25 -694
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -14
- package/src/__tests__/send-endpoint-busy.test.ts +8 -30
- package/src/__tests__/skills.test.ts +0 -44
- package/src/__tests__/slack-inbound-verification.test.ts +2 -47
- package/src/__tests__/stt-hints.test.ts +13 -44
- package/src/__tests__/subagent-detail.test.ts +0 -27
- package/src/__tests__/subagent-disposal.test.ts +0 -65
- package/src/__tests__/subagent-notify-parent.test.ts +0 -2
- package/src/__tests__/subagent-role-registry.test.ts +2 -7
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +0 -2
- package/src/__tests__/subagent-tools.test.ts +0 -2
- package/src/__tests__/suggestion-routes.test.ts +0 -2
- package/src/__tests__/title-generate-hook.test.ts +0 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -2
- package/src/__tests__/tool-executor.test.ts +11 -16
- package/src/__tests__/tool-preview-lifecycle.test.ts +0 -2
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -2
- package/src/__tests__/tool-start-timestamp.test.ts +0 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
- package/src/__tests__/twilio-routes.test.ts +0 -96
- package/src/__tests__/ui-file-upload-surface.test.ts +0 -86
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +0 -33
- package/src/__tests__/web-search-backend-failure.test.ts +0 -2
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +35 -14
- package/src/__tests__/workspace-tool-loader.test.ts +2 -195
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +0 -56
- package/src/api/index.ts +1 -19
- package/src/api/responses/llm-request-log-entry.ts +0 -29
- package/src/api/responses/subagent-detail.ts +0 -17
- package/src/api/surfaces.ts +3 -39
- package/src/approvals/guardian-request-resolvers.ts +11 -1
- package/src/calls/__tests__/relay-setup-router.test.ts +4 -262
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +8 -10
- package/src/calls/inbound-trust-reader.ts +1 -17
- package/src/calls/media-stream-server.ts +0 -21
- package/src/calls/relay-server.ts +50 -167
- package/src/calls/relay-setup-router.ts +7 -37
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +12 -9
- package/src/calls/twilio-routes.ts +4 -14
- package/src/channels/types.ts +20 -10
- package/src/cli/commands/__tests__/cache.test.ts +1 -8
- package/src/cli/commands/cache.ts +181 -194
- package/src/cli/commands/db/__tests__/repair.test.ts +5 -6
- package/src/cli/commands/db/status.ts +1 -37
- package/src/cli/commands/mcp.ts +218 -252
- package/src/cli/commands/memory/index.ts +0 -2
- package/src/cli/commands/plugins.ts +3 -75
- package/src/cli/lib/__tests__/install-from-github.test.ts +0 -102
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +1 -160
- package/src/cli/lib/list-installed-plugins.ts +1 -179
- package/src/config/__tests__/sync-gated-profiles.test.ts +3 -11
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +17 -27
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +3 -13
- package/src/config/bundled-skills/subagent/SKILL.md +1 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +5 -38
- package/src/config/schemas/__tests__/memory-v3.test.ts +0 -1
- package/src/config/schemas/memory-lifecycle.ts +0 -12
- package/src/config/schemas/memory-v3.ts +0 -7
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/timeouts.ts +0 -8
- package/src/config/seed-inference-profiles.ts +11 -21
- package/src/config/skills.ts +5 -27
- package/src/config/sync-gated-profiles.ts +13 -12
- package/src/contacts/contacts-write.ts +0 -3
- package/src/daemon/assistant-attachments.ts +4 -27
- package/src/daemon/conversation-agent-loop.ts +0 -28
- package/src/daemon/conversation-process.ts +16 -35
- package/src/daemon/conversation-surfaces.ts +38 -111
- package/src/daemon/conversation-tool-setup.ts +16 -50
- package/src/daemon/conversation.ts +1 -13
- package/src/daemon/disk-pressure-guard.ts +2 -12
- package/src/daemon/event-loop-watchdog.ts +1 -28
- package/src/daemon/external-plugins-bootstrap.ts +34 -4
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -25
- package/src/daemon/handlers/config-a2a.ts +14 -6
- package/src/daemon/handlers/config-channels.ts +22 -78
- package/src/daemon/handlers/conversations.ts +0 -77
- package/src/daemon/lifecycle.ts +0 -4
- package/src/daemon/mcp-reload-service.ts +0 -10
- package/src/daemon/memory-v2-startup.test.ts +0 -72
- package/src/daemon/memory-v2-startup.ts +19 -87
- package/src/daemon/message-types/conversations.ts +0 -2
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +4 -0
- package/src/daemon/shutdown-handlers.ts +0 -20
- package/src/daemon/tool-setup-types.ts +0 -9
- package/src/daemon/workspace-tools-watcher.ts +328 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/client.ts +1 -15
- package/src/mcp/mcp-auth-orchestrator.ts +1 -6
- package/src/mcp/mcp-oauth-provider.ts +8 -19
- package/src/memory/__tests__/memory-retrospective-job.test.ts +0 -8
- package/src/memory/conversation-crud.ts +0 -38
- package/src/memory/db-connection.ts +3 -22
- package/src/memory/db-init.ts +502 -36
- package/src/memory/db-singleton.ts +4 -6
- package/src/memory/jobs-worker.ts +0 -58
- package/src/memory/llm-request-log-store.ts +1 -26
- package/src/memory/llm-usage-store.ts +20 -48
- package/src/memory/memory-retrospective-job.ts +8 -9
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +56 -130
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/registry.ts +573 -0
- package/src/memory/migrations/run-migrations.ts +6 -90
- package/src/memory/migrations/validate-migration-state.ts +66 -101
- package/src/memory/schema/conversations.ts +0 -9
- package/src/memory/schema/infrastructure.ts +0 -20
- package/src/memory/v2/__tests__/cli-command-store.test.ts +0 -25
- package/src/memory/v2/__tests__/skill-store.test.ts +0 -80
- package/src/memory/v2/cli-command-store.ts +38 -75
- package/src/memory/v2/prompts/consolidation.ts +82 -13
- package/src/memory/v2/prompts/router.ts +93 -21
- package/src/memory/v2/skill-store.ts +31 -68
- package/src/notifications/__tests__/broadcaster.test.ts +8 -16
- package/src/notifications/__tests__/decision-engine.test.ts +9 -78
- package/src/notifications/broadcaster.ts +1 -8
- package/src/notifications/decision-engine.ts +7 -15
- package/src/notifications/destination-resolver.ts +24 -68
- package/src/notifications/emit-signal.ts +14 -39
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +4 -7
- package/src/plugin-api/index.ts +6 -6
- package/src/plugin-api/types.ts +5 -3
- package/src/plugin-api/vision-support.test.ts +4 -28
- package/src/plugin-api/vision-support.ts +31 -66
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -161
- package/src/plugins/defaults/advisor/consult.ts +6 -110
- package/src/plugins/defaults/advisor/steering.ts +2 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +5 -32
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +1 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +7 -47
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +11 -10
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +20 -12
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +11 -42
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +3 -33
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +4 -48
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +8 -4
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +15 -43
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +2 -11
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -77
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +11 -12
- package/src/plugins/mtime-cache.ts +291 -76
- package/src/plugins/pipeline.ts +13 -111
- package/src/plugins/types.ts +2 -0
- package/src/providers/anthropic/client.ts +0 -5
- package/src/providers/call-site-routing.ts +0 -4
- package/src/providers/model-catalog.ts +0 -16
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/chat-completions-provider.ts +83 -37
- package/src/providers/openai/responses-provider.ts +46 -50
- package/src/providers/openrouter/client.ts +0 -5
- package/src/providers/provider-send-message.ts +0 -4
- package/src/providers/ratelimit.ts +0 -4
- package/src/providers/retry.ts +0 -4
- package/src/providers/types.ts +0 -9
- package/src/providers/usage-tracking.ts +0 -4
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +3 -335
- package/src/runtime/access-request-helper.ts +39 -19
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +2 -9
- package/src/runtime/auth/require-bound-guardian.ts +11 -21
- package/src/runtime/channel-verification-service.ts +31 -56
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +7 -66
- package/src/runtime/invite-redemption-service.ts +187 -198
- package/src/runtime/local-actor-identity.ts +11 -76
- package/src/runtime/pending-interactions.ts +1 -11
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +5 -56
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +0 -187
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -13
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +32 -8
- package/src/runtime/routes/conversation-cli-routes.ts +5 -4
- package/src/runtime/routes/conversation-list-routes.ts +7 -4
- package/src/runtime/routes/conversation-query-routes.ts +0 -72
- package/src/runtime/routes/conversation-routes.ts +85 -84
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +1 -3
- package/src/runtime/routes/guardian-action-routes.ts +5 -4
- package/src/runtime/routes/host-app-control-routes.ts +4 -5
- package/src/runtime/routes/host-bash-routes.ts +4 -5
- package/src/runtime/routes/host-browser-routes.ts +11 -9
- package/src/runtime/routes/host-cu-routes.ts +4 -5
- package/src/runtime/routes/host-file-routes.ts +4 -5
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +2 -3
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +5 -97
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +49 -61
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -16
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +8 -21
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +3 -14
- package/src/runtime/routes/index.ts +0 -2
- package/src/runtime/routes/llm-context-normalization.ts +0 -83
- package/src/runtime/routes/mcp-auth-routes.ts +19 -171
- package/src/runtime/routes/migration-rollback-routes.ts +3 -4
- package/src/runtime/routes/migration-routes.ts +1 -4
- package/src/runtime/routes/subagents-routes.ts +0 -5
- package/src/runtime/routes/surface-action-routes.ts +56 -42
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
- package/src/runtime/services/conversation-serializer.ts +9 -7
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +9 -85
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +0 -16
- package/src/subagent/manager.ts +0 -9
- package/src/subagent/types.ts +3 -3
- package/src/telemetry/types.ts +1 -34
- package/src/telemetry/usage-telemetry-reporter.test.ts +2 -3
- package/src/telemetry/usage-telemetry-reporter.ts +3 -87
- package/src/tools/ask-question/ask-question-tool.test.ts +0 -29
- package/src/tools/ask-question/ask-question-tool.ts +0 -13
- package/src/tools/executor.ts +4 -4
- package/src/tools/registry.ts +0 -18
- package/src/tools/shared/filesystem/path-policy.ts +5 -12
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +2 -9
- package/src/tools/tool-manifest.ts +0 -3
- package/src/tools/types.ts +2 -17
- package/src/tools/workspace-tools/loader.ts +244 -348
- package/src/util/errors.ts +1 -26
- package/src/util/platform.ts +0 -5
- package/src/workflows/library.test.ts +0 -140
- package/src/workflows/library.ts +28 -82
- package/src/workspace/migrations/017-seed-persona-dirs.ts +34 -3
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +24 -3
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +66 -14
- package/src/workspace/migrations/registry.ts +0 -2
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +0 -91
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +0 -48
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +0 -28
- package/node_modules/@vellumai/service-contracts/src/channels.ts +0 -41
- package/src/__tests__/code-search-tool.test.ts +0 -585
- package/src/__tests__/guardian-expiry-notifier.test.ts +0 -282
- package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -390
- package/src/__tests__/plugin-pipeline.test.ts +0 -96
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +0 -102
- package/src/__tests__/steer-on-enqueue-question.test.ts +0 -181
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +0 -208
- package/src/agent/loop-exclusive-tool.test.ts +0 -150
- package/src/api/constants/sse-replay.ts +0 -41
- package/src/api/events/conversation-notice.ts +0 -26
- package/src/approvals/guardian-channel-delivery.ts +0 -30
- package/src/approvals/guardian-expiry-notifier.ts +0 -148
- package/src/cli/commands/memory/__tests__/worker.test.ts +0 -302
- package/src/cli/commands/memory/worker.ts +0 -175
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -143
- package/src/config/prune-seeded-callsite-defaults.ts +0 -110
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -129
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +0 -312
- package/src/contacts/__tests__/member-write-relay.test.ts +0 -202
- package/src/contacts/guardian-delivery-reader.ts +0 -223
- package/src/contacts/member-write-relay.ts +0 -189
- package/src/daemon/conversation-notices.ts +0 -60
- package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -225
- package/src/hooks/hook-loader.ts +0 -341
- package/src/mcp/mcp-header-store.ts +0 -134
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +0 -110
- package/src/memory/__tests__/prompt-override.test.ts +0 -192
- package/src/memory/__tests__/watchdog-events-store.test.ts +0 -161
- package/src/memory/migrations/300-add-processing-started-at.ts +0 -30
- package/src/memory/migrations/301-create-watchdog-events.ts +0 -45
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +0 -224
- package/src/memory/prompt-override.ts +0 -129
- package/src/memory/steps.ts +0 -573
- package/src/memory/watchdog-events-store.ts +0 -87
- package/src/memory/worker-control.ts +0 -118
- package/src/memory/worker-process.ts +0 -72
- package/src/notifications/__tests__/connected-channels.test.ts +0 -114
- package/src/notifications/__tests__/destination-resolver.test.ts +0 -256
- package/src/onboarding/checkin-event.test.ts +0 -222
- package/src/onboarding/checkin-event.ts +0 -321
- package/src/onboarding/schedule-checkin.ts +0 -190
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
- package/src/plugins/defaults/advisor/context-pack.ts +0 -288
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +0 -146
- package/src/plugins/surface-import.ts +0 -121
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -321
- package/src/providers/openai/api-error-normalization.ts +0 -270
- package/src/runtime/__tests__/channel-verification-service.test.ts +0 -133
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +0 -181
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +0 -66
- package/src/runtime/__tests__/local-principal-trust.test.ts +0 -164
- package/src/runtime/anchored-guardian.test.ts +0 -156
- package/src/runtime/anchored-guardian.ts +0 -135
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +0 -99
- package/src/runtime/local-principal-trust.ts +0 -52
- package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -212
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -93
- package/src/runtime/routes/onboarding-checkin-routes.ts +0 -86
- package/src/tools/filesystem/search.ts +0 -543
- package/src/util/telemetry-db-path.ts +0 -24
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +0 -134
package/src/hooks/hook-loader.ts
DELETED
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hook surface loader — the first-class home for user-land lifecycle/event
|
|
3
|
-
* hooks.
|
|
4
|
-
*
|
|
5
|
-
* A "hook" is a named lifecycle event (`init`, `shutdown`, `user-prompt-submit`,
|
|
6
|
-
* `post-tool-use`, `stop`, ...) handled by a default export. Hooks come from
|
|
7
|
-
* two surfaces, both cached here keyed by their source file's mtime:
|
|
8
|
-
*
|
|
9
|
-
* - **Plugin hooks** — `<workspace>/plugins/<name>/hooks/<event>.{ts,js}`,
|
|
10
|
-
* discovered alongside the owning plugin's tools.
|
|
11
|
-
* - **Workspace hooks** — `<workspace>/hooks/<event>.{ts,js}`, standalone
|
|
12
|
-
* files not tied to any plugin (no `package.json`, no tools — just hooks).
|
|
13
|
-
*
|
|
14
|
-
* This module owns the hook cache and every hook operation (collect, init,
|
|
15
|
-
* shutdown, eviction). Plugin *discovery* (which plugin directories exist, in
|
|
16
|
-
* what order) lives in `../plugins/mtime-cache.ts`; the orchestrator there
|
|
17
|
-
* passes the discovered directories into {@link collectUserHooks} and drives
|
|
18
|
-
* pre-import / init / shutdown at boot. Keeping discovery out of this module
|
|
19
|
-
* lets it sit below the plugin cache with no import cycle.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { mkdirSync } from "node:fs";
|
|
23
|
-
import { join } from "node:path";
|
|
24
|
-
|
|
25
|
-
import { getConfig } from "../config/loader.js";
|
|
26
|
-
import { HOOKS } from "../plugin-api/constants.js";
|
|
27
|
-
import type {
|
|
28
|
-
PluginHookFn,
|
|
29
|
-
PluginInitContext,
|
|
30
|
-
PluginShutdownContext,
|
|
31
|
-
} from "../plugin-api/types.js";
|
|
32
|
-
import { listSurfaceDir } from "../plugins/external-plugin-loader.js";
|
|
33
|
-
import { getMtime, importWithTimeout } from "../plugins/surface-import.js";
|
|
34
|
-
import { getLogger } from "../util/logger.js";
|
|
35
|
-
import { getWorkspaceDir, getWorkspaceHooksDir } from "../util/platform.js";
|
|
36
|
-
import { APP_VERSION } from "../version.js";
|
|
37
|
-
|
|
38
|
-
const log = getLogger("hook-loader");
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Synthetic owner name for standalone hooks that live directly under
|
|
42
|
-
* `<workspace>/hooks/` rather than inside a plugin's `hooks/` directory.
|
|
43
|
-
*
|
|
44
|
-
* Used as the cache-key prefix (`__workspace__/<hookName>`) so workspace
|
|
45
|
-
* hooks never collide with a plugin's hooks. The leading/trailing double
|
|
46
|
-
* underscores keep it disjoint from any scope-stripped npm package name a
|
|
47
|
-
* real plugin could carry.
|
|
48
|
-
*/
|
|
49
|
-
export const WORKSPACE_HOOKS_OWNER = "__workspace__";
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* A cached hook function plus the mtime of its source file. When the on-disk
|
|
53
|
-
* mtime changes, the hook is re-imported and the entry is replaced.
|
|
54
|
-
*/
|
|
55
|
-
interface CachedHook {
|
|
56
|
-
readonly hook: PluginHookFn;
|
|
57
|
-
/** mtimeMs of the source file this hook was imported from. */
|
|
58
|
-
readonly sourceMtime: number;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Cached hooks keyed by `${ownerName}/${hookName}`. The key includes the
|
|
63
|
-
* owner (plugin name, or {@link WORKSPACE_HOOKS_OWNER}) so hooks from
|
|
64
|
-
* different owners don't collide.
|
|
65
|
-
*/
|
|
66
|
-
const hookCache = new Map<string, CachedHook>();
|
|
67
|
-
|
|
68
|
-
/** Cache key for a hook: `${ownerName}/${hookName}`. */
|
|
69
|
-
function hookKey(ownerName: string, hookName: string): string {
|
|
70
|
-
return `${ownerName}/${hookName}`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Resolve a single hook file through the mtime cache: return the cached hook
|
|
75
|
-
* when its source mtime is unchanged, otherwise re-import and refresh the
|
|
76
|
-
* entry. Returns `undefined` when the file was deleted (evicting any stale
|
|
77
|
-
* entry) or the import failed / produced a non-function default export.
|
|
78
|
-
*
|
|
79
|
-
* `ownerName` is the cache-key prefix and the attribution label in logs (a
|
|
80
|
-
* plugin name, or {@link WORKSPACE_HOOKS_OWNER}).
|
|
81
|
-
*/
|
|
82
|
-
async function resolveCachedHook<TCtx>(
|
|
83
|
-
ownerName: string,
|
|
84
|
-
hookName: string,
|
|
85
|
-
filePath: string,
|
|
86
|
-
): Promise<PluginHookFn<TCtx> | undefined> {
|
|
87
|
-
const key = hookKey(ownerName, hookName);
|
|
88
|
-
const currentMtime = getMtime(filePath);
|
|
89
|
-
|
|
90
|
-
// Cache hit — same mtime.
|
|
91
|
-
const cached = hookCache.get(key);
|
|
92
|
-
if (
|
|
93
|
-
cached !== undefined &&
|
|
94
|
-
cached.sourceMtime === currentMtime &&
|
|
95
|
-
currentMtime > 0
|
|
96
|
-
) {
|
|
97
|
-
return cached.hook as PluginHookFn<TCtx>;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Cache miss — re-import.
|
|
101
|
-
if (currentMtime === 0) {
|
|
102
|
-
// File was deleted between listing and stat — evict the cache entry.
|
|
103
|
-
hookCache.delete(key);
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
const hook = await importWithTimeout<PluginHookFn>(filePath);
|
|
109
|
-
if (hook === undefined || typeof hook !== "function") {
|
|
110
|
-
log.error(
|
|
111
|
-
{ plugin: ownerName, hook: hookName, path: filePath },
|
|
112
|
-
`hook ${hookName} default export must be a function (got ${typeof hook}) — skipping`,
|
|
113
|
-
);
|
|
114
|
-
return undefined;
|
|
115
|
-
}
|
|
116
|
-
hookCache.set(key, { hook, sourceMtime: currentMtime });
|
|
117
|
-
return hook as PluginHookFn<TCtx>;
|
|
118
|
-
} catch (err) {
|
|
119
|
-
log.error(
|
|
120
|
-
{ err, plugin: ownerName, hook: hookName, path: filePath },
|
|
121
|
-
`Failed to import hook ${hookName} from ${filePath}`,
|
|
122
|
-
);
|
|
123
|
-
return undefined;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Collect every hook for a given event name across all surfaces, re-importing
|
|
129
|
-
* any whose source file's mtime changed since the cache was populated.
|
|
130
|
-
*
|
|
131
|
-
* `pluginDirs` is the orchestrator's discovered `[dir, ownerName]` set (in
|
|
132
|
-
* install-date order). Each plugin's hook runs first, then the standalone
|
|
133
|
-
* workspace hook under `<workspace>/hooks/<hookName>.{ts,js}` runs last — so
|
|
134
|
-
* a plugin can shape the threaded context before a workspace-wide hook
|
|
135
|
-
* observes or finalizes it.
|
|
136
|
-
*
|
|
137
|
-
* Added and removed hook files are picked up live (discovery is by directory
|
|
138
|
-
* listing). A content edit to an existing file is only reflected after a
|
|
139
|
-
* process restart, since Bun caches dynamic imports by resolved path.
|
|
140
|
-
*/
|
|
141
|
-
export async function collectUserHooks<TCtx = unknown>(
|
|
142
|
-
hookName: string,
|
|
143
|
-
pluginDirs: Iterable<readonly [string, string]>,
|
|
144
|
-
): Promise<PluginHookFn<TCtx>[]> {
|
|
145
|
-
const out: PluginHookFn<TCtx>[] = [];
|
|
146
|
-
|
|
147
|
-
for (const [pluginDir, pluginName] of pluginDirs) {
|
|
148
|
-
const hookFile = listSurfaceDir(join(pluginDir, "hooks")).find(
|
|
149
|
-
(f) => f.name === hookName,
|
|
150
|
-
);
|
|
151
|
-
if (hookFile === undefined) continue;
|
|
152
|
-
|
|
153
|
-
const hook = await resolveCachedHook<TCtx>(
|
|
154
|
-
pluginName,
|
|
155
|
-
hookName,
|
|
156
|
-
hookFile.path,
|
|
157
|
-
);
|
|
158
|
-
if (hook !== undefined) out.push(hook);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Standalone workspace hooks: files directly under `<workspace>/hooks/`
|
|
162
|
-
// that are not part of any plugin (no package.json, no tools — just hooks).
|
|
163
|
-
const wsHookFile = listSurfaceDir(getWorkspaceHooksDir()).find(
|
|
164
|
-
(f) => f.name === hookName,
|
|
165
|
-
);
|
|
166
|
-
if (wsHookFile !== undefined) {
|
|
167
|
-
const hook = await resolveCachedHook<TCtx>(
|
|
168
|
-
WORKSPACE_HOOKS_OWNER,
|
|
169
|
-
hookName,
|
|
170
|
-
wsHookFile.path,
|
|
171
|
-
);
|
|
172
|
-
if (hook !== undefined) out.push(hook);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return out;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Pre-import every hook file under `hooksDir` and cache it keyed by
|
|
180
|
-
* `${ownerName}/${hookName}`, so the first turn doesn't pay the import cost.
|
|
181
|
-
* Best-effort per file: a failing import is logged and skipped. A missing
|
|
182
|
-
* directory yields no files (handled by {@link listSurfaceDir}).
|
|
183
|
-
*/
|
|
184
|
-
export async function preImportHooksDir(
|
|
185
|
-
hooksDir: string,
|
|
186
|
-
ownerName: string,
|
|
187
|
-
): Promise<void> {
|
|
188
|
-
for (const file of listSurfaceDir(hooksDir)) {
|
|
189
|
-
const key = hookKey(ownerName, file.name);
|
|
190
|
-
const currentMtime = getMtime(file.path);
|
|
191
|
-
if (currentMtime === 0) continue;
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
const hook = await importWithTimeout<PluginHookFn>(file.path);
|
|
195
|
-
if (hook !== undefined && typeof hook === "function") {
|
|
196
|
-
hookCache.set(key, { hook, sourceMtime: currentMtime });
|
|
197
|
-
}
|
|
198
|
-
} catch (err) {
|
|
199
|
-
log.error(
|
|
200
|
-
{ err, plugin: ownerName, hook: file.name, path: file.path },
|
|
201
|
-
`Failed to pre-import hook ${file.name}`,
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Whether the standalone workspace hooks directory currently holds any hook
|
|
209
|
-
* files. Used by the boot orchestrator to skip activating (and registering
|
|
210
|
-
* teardown for) an empty/absent directory.
|
|
211
|
-
*/
|
|
212
|
-
export function hasWorkspaceHooks(): boolean {
|
|
213
|
-
return listSurfaceDir(getWorkspaceHooksDir()).length > 0;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Pre-import the standalone workspace hooks under {@link WORKSPACE_HOOKS_OWNER}.
|
|
218
|
-
* Convenience wrapper over {@link preImportHooksDir} that keeps the workspace
|
|
219
|
-
* hooks directory path inside this module.
|
|
220
|
-
*/
|
|
221
|
-
export async function preImportWorkspaceHooks(): Promise<void> {
|
|
222
|
-
await preImportHooksDir(getWorkspaceHooksDir(), WORKSPACE_HOOKS_OWNER);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Run the `init` hook for `ownerName` if one was pre-imported into the cache.
|
|
227
|
-
* Shared by user plugins and standalone workspace hooks so both get the same
|
|
228
|
-
* init-context shape and per-owner isolation (a thrown `init` is logged and
|
|
229
|
-
* swallowed, never blocking boot).
|
|
230
|
-
*/
|
|
231
|
-
export async function runInitHook(ownerName: string): Promise<void> {
|
|
232
|
-
const initHookEntry = hookCache.get(hookKey(ownerName, HOOKS.INIT));
|
|
233
|
-
if (initHookEntry === undefined) return;
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
const initContext: PluginInitContext = {
|
|
237
|
-
config: getConfig().plugins?.[ownerName],
|
|
238
|
-
logger: log.child({ plugin: ownerName }),
|
|
239
|
-
pluginStorageDir: ensureHookStorageDir(ownerName),
|
|
240
|
-
assistantVersion: APP_VERSION,
|
|
241
|
-
};
|
|
242
|
-
await initHookEntry.hook(initContext);
|
|
243
|
-
log.info({ plugin: ownerName }, "user hooks initialized");
|
|
244
|
-
} catch (err) {
|
|
245
|
-
log.error(
|
|
246
|
-
{ err, plugin: ownerName },
|
|
247
|
-
`User hooks for ${ownerName} init() failed — continuing`,
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Run the `shutdown` hook for `ownerName` if one is cached. Best-effort: a
|
|
254
|
-
* thrown shutdown is logged and swallowed. `reason` is threaded into the log
|
|
255
|
-
* for attribution only.
|
|
256
|
-
*/
|
|
257
|
-
export async function runShutdownHook(
|
|
258
|
-
ownerName: string,
|
|
259
|
-
context: PluginShutdownContext,
|
|
260
|
-
reason: string,
|
|
261
|
-
): Promise<void> {
|
|
262
|
-
const shutdownHookEntry = hookCache.get(hookKey(ownerName, HOOKS.SHUTDOWN));
|
|
263
|
-
if (shutdownHookEntry === undefined) return;
|
|
264
|
-
|
|
265
|
-
try {
|
|
266
|
-
await shutdownHookEntry.hook(context);
|
|
267
|
-
} catch (err) {
|
|
268
|
-
log.warn(
|
|
269
|
-
{ err, plugin: ownerName, reason },
|
|
270
|
-
"user hooks shutdown failed (continuing)",
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Evict every cached hook owned by `ownerName` (e.g. when a plugin directory
|
|
277
|
-
* is removed). No-op when the owner has no cached hooks.
|
|
278
|
-
*/
|
|
279
|
-
export function evictHooksForOwner(ownerName: string): void {
|
|
280
|
-
const prefix = `${ownerName}/`;
|
|
281
|
-
for (const key of hookCache.keys()) {
|
|
282
|
-
if (key.startsWith(prefix)) {
|
|
283
|
-
hookCache.delete(key);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Evict all plugin-owned hooks while preserving standalone workspace hooks.
|
|
290
|
-
* Called when the plugins directory is gone entirely: workspace hooks live
|
|
291
|
-
* outside it, so the absence of any plugin must not evict them.
|
|
292
|
-
*/
|
|
293
|
-
export function clearPluginHooks(): void {
|
|
294
|
-
for (const key of hookCache.keys()) {
|
|
295
|
-
if (!key.startsWith(`${WORKSPACE_HOOKS_OWNER}/`)) {
|
|
296
|
-
hookCache.delete(key);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Ensure `<workspaceDir>/plugins-data/<name>/` exists and return its path.
|
|
303
|
-
* Used as the per-owner storage directory in the hook init context.
|
|
304
|
-
*/
|
|
305
|
-
function ensureHookStorageDir(ownerName: string): string {
|
|
306
|
-
const dir = join(getWorkspaceDir(), "plugins-data", ownerName);
|
|
307
|
-
mkdirSync(dir, { recursive: true });
|
|
308
|
-
return dir;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// ─── Test hooks ──────────────────────────────────────────────────────────────
|
|
312
|
-
|
|
313
|
-
/** Clear the hook cache. Test-only. */
|
|
314
|
-
export function resetHookCacheForTests(): void {
|
|
315
|
-
const isTest =
|
|
316
|
-
process.env.BUN_TEST === "1" || process.env.NODE_ENV === "test";
|
|
317
|
-
if (!isTest) {
|
|
318
|
-
throw new Error(
|
|
319
|
-
"resetHookCacheForTests may only be called in test environments",
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
hookCache.clear();
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/** Test-only: inspect the hook cache. */
|
|
326
|
-
export function _inspectHookCacheForTests(): Array<{
|
|
327
|
-
key: string;
|
|
328
|
-
sourceMtime: number;
|
|
329
|
-
}> {
|
|
330
|
-
const isTest =
|
|
331
|
-
process.env.BUN_TEST === "1" || process.env.NODE_ENV === "test";
|
|
332
|
-
if (!isTest) {
|
|
333
|
-
throw new Error(
|
|
334
|
-
"_inspectHookCacheForTests may only be called in test environments",
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
return Array.from(hookCache.entries()).map(([key, c]) => ({
|
|
338
|
-
key,
|
|
339
|
-
sourceMtime: c.sourceMtime,
|
|
340
|
-
}));
|
|
341
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Credential-store-backed persistence for MCP server static auth headers.
|
|
3
|
-
*
|
|
4
|
-
* Follows the same pattern as mcp-oauth-provider.ts: headers are stored in
|
|
5
|
-
* the secure credential store (CES or encrypted file fallback) rather than
|
|
6
|
-
* in plaintext config.json, keeping secrets out of workspace config files.
|
|
7
|
-
*
|
|
8
|
-
* Key format: mcp:{serverId}:headers — stores JSON-serialized Record<string, string>.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { loadRawConfig, saveRawConfig } from "../config/loader.js";
|
|
12
|
-
import {
|
|
13
|
-
deleteSecureKeyAsync,
|
|
14
|
-
getSecureKeyAsync,
|
|
15
|
-
setSecureKeyAsync,
|
|
16
|
-
} from "../security/secure-keys.js";
|
|
17
|
-
import { getLogger } from "../util/logger.js";
|
|
18
|
-
|
|
19
|
-
const log = getLogger("mcp-header-store");
|
|
20
|
-
|
|
21
|
-
function headersKey(serverId: string): string {
|
|
22
|
-
return `mcp:${serverId}:headers`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Retrieve stored static auth headers for an MCP server.
|
|
27
|
-
* Returns undefined if none are stored or if the credential store is unreachable.
|
|
28
|
-
*/
|
|
29
|
-
export async function getMcpHeaders(
|
|
30
|
-
serverId: string,
|
|
31
|
-
): Promise<Record<string, string> | undefined> {
|
|
32
|
-
const raw = await getSecureKeyAsync(headersKey(serverId));
|
|
33
|
-
if (!raw) return undefined;
|
|
34
|
-
try {
|
|
35
|
-
return JSON.parse(raw) as Record<string, string>;
|
|
36
|
-
} catch {
|
|
37
|
-
log.warn({ serverId }, "Failed to parse stored MCP headers");
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Store static auth headers for an MCP server in the credential store.
|
|
44
|
-
*/
|
|
45
|
-
export async function setMcpHeaders(
|
|
46
|
-
serverId: string,
|
|
47
|
-
headers: Record<string, string>,
|
|
48
|
-
): Promise<boolean> {
|
|
49
|
-
const ok = await setSecureKeyAsync(
|
|
50
|
-
headersKey(serverId),
|
|
51
|
-
JSON.stringify(headers),
|
|
52
|
-
);
|
|
53
|
-
if (!ok) {
|
|
54
|
-
log.warn({ serverId }, "Failed to persist MCP headers to secure storage");
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
log.info({ serverId }, "MCP static auth headers saved to credential store");
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Delete stored static auth headers for an MCP server.
|
|
63
|
-
*/
|
|
64
|
-
export async function deleteMcpHeaders(serverId: string): Promise<boolean> {
|
|
65
|
-
const result = await deleteSecureKeyAsync(headersKey(serverId));
|
|
66
|
-
if (result === "error") {
|
|
67
|
-
log.warn({ serverId }, "Failed to delete MCP headers from secure storage");
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* One-time lazy migration: move any plaintext headers from config.json
|
|
75
|
-
* transport entries into the credential store and strip them from config.
|
|
76
|
-
* Safe to call on every MCP reload — no-ops when no legacy headers remain.
|
|
77
|
-
*/
|
|
78
|
-
export async function migrateLegacyMcpHeaders(): Promise<void> {
|
|
79
|
-
const raw = loadRawConfig();
|
|
80
|
-
const mcpConfig = raw.mcp as
|
|
81
|
-
| { servers?: Record<string, Record<string, unknown>> }
|
|
82
|
-
| undefined;
|
|
83
|
-
const servers = mcpConfig?.servers;
|
|
84
|
-
if (!servers) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
let configDirty = false;
|
|
89
|
-
for (const [id, server] of Object.entries(servers)) {
|
|
90
|
-
const transport = server?.transport as Record<string, unknown> | undefined;
|
|
91
|
-
if (
|
|
92
|
-
!transport ||
|
|
93
|
-
(transport.type !== "sse" && transport.type !== "streamable-http")
|
|
94
|
-
) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
const legacyHeaders = transport.headers as
|
|
98
|
-
| Record<string, string>
|
|
99
|
-
| undefined;
|
|
100
|
-
if (!legacyHeaders || Object.keys(legacyHeaders).length === 0) {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Only migrate if credential store doesn't already have headers for
|
|
105
|
-
// this server (idempotent — safe to re-run after partial failure).
|
|
106
|
-
const existing = await getMcpHeaders(id);
|
|
107
|
-
if (existing) {
|
|
108
|
-
// Credential store already has headers; just strip the config copy.
|
|
109
|
-
delete transport.headers;
|
|
110
|
-
configDirty = true;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const ok = await setMcpHeaders(id, legacyHeaders);
|
|
115
|
-
if (ok) {
|
|
116
|
-
delete transport.headers;
|
|
117
|
-
configDirty = true;
|
|
118
|
-
log.info(
|
|
119
|
-
{ serverId: id },
|
|
120
|
-
"Migrated legacy MCP headers to credential store",
|
|
121
|
-
);
|
|
122
|
-
} else {
|
|
123
|
-
log.warn(
|
|
124
|
-
{ serverId: id },
|
|
125
|
-
"Skipping legacy header migration — credential store write failed; will retry on next reload",
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (configDirty) {
|
|
131
|
-
saveRawConfig(raw);
|
|
132
|
-
log.info("Config updated: legacy MCP headers removed after migration");
|
|
133
|
-
}
|
|
134
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { Database } from "bun:sqlite";
|
|
5
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
6
|
-
|
|
7
|
-
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
8
|
-
|
|
9
|
-
// The migration opens the telemetry connection via getTelemetrySqlite(), which
|
|
10
|
-
// routes through the singleton. Mock the path helper so the connection opens a
|
|
11
|
-
// temp file we control, then mock the db-connection getter so it returns that
|
|
12
|
-
// same file's Database.
|
|
13
|
-
const tmpDir = mkdtempSync(join(tmpdir(), "wd-migration-"));
|
|
14
|
-
const telemetryPath = join(tmpDir, "assistant-telemetry.db");
|
|
15
|
-
let openedRaw: Database | null = null;
|
|
16
|
-
|
|
17
|
-
mock.module("../../util/telemetry-db-path.js", () => ({
|
|
18
|
-
getTelemetryDbPath: () => telemetryPath,
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
mock.module("../db-connection.js", () => ({
|
|
22
|
-
// The migration calls getTelemetrySqlite(); return our temp Database so the
|
|
23
|
-
// DDL runs against it. getSqliteFrom is used in other contexts but not by
|
|
24
|
-
// this migration when the mock is active.
|
|
25
|
-
getTelemetrySqlite: () => openedRaw,
|
|
26
|
-
getSqliteFrom: (db: unknown) =>
|
|
27
|
-
(db as unknown as { $client: Database }).$client,
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
import { createWatchdogEventsTable } from "../migrations/301-create-watchdog-events.js";
|
|
31
|
-
import * as schema from "../schema.js";
|
|
32
|
-
|
|
33
|
-
function createTestDb() {
|
|
34
|
-
const sqlite = new Database(":memory:");
|
|
35
|
-
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function openTelemetryRaw(): Database {
|
|
39
|
-
openedRaw = new Database(telemetryPath);
|
|
40
|
-
return openedRaw;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function columnNames(raw: Database): string[] {
|
|
44
|
-
return (
|
|
45
|
-
raw.query("PRAGMA table_info(watchdog_events)").all() as Array<{
|
|
46
|
-
name: string;
|
|
47
|
-
}>
|
|
48
|
-
).map((c) => c.name);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function indexNames(raw: Database): string[] {
|
|
52
|
-
return (
|
|
53
|
-
raw.query("PRAGMA index_list(watchdog_events)").all() as Array<{
|
|
54
|
-
name: string;
|
|
55
|
-
}>
|
|
56
|
-
).map((i) => i.name);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
describe("migration 300: watchdog_events table on telemetry DB", () => {
|
|
60
|
-
test("creates the table with the expected columns", () => {
|
|
61
|
-
const raw = openTelemetryRaw();
|
|
62
|
-
const { db } = createTestDb();
|
|
63
|
-
|
|
64
|
-
createWatchdogEventsTable(db);
|
|
65
|
-
|
|
66
|
-
expect(columnNames(raw)).toEqual([
|
|
67
|
-
"id",
|
|
68
|
-
"created_at",
|
|
69
|
-
"check_name",
|
|
70
|
-
"value",
|
|
71
|
-
"detail",
|
|
72
|
-
]);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("creates the (created_at, id) cursor index", () => {
|
|
76
|
-
const raw = openTelemetryRaw();
|
|
77
|
-
const { db } = createTestDb();
|
|
78
|
-
|
|
79
|
-
createWatchdogEventsTable(db);
|
|
80
|
-
|
|
81
|
-
expect(indexNames(raw)).toContain("idx_watchdog_events_created_at_id");
|
|
82
|
-
const columns = (
|
|
83
|
-
raw
|
|
84
|
-
.query("PRAGMA index_info(idx_watchdog_events_created_at_id)")
|
|
85
|
-
.all() as Array<{ name: string }>
|
|
86
|
-
).map((c) => c.name);
|
|
87
|
-
expect(columns).toEqual(["created_at", "id"]);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("is idempotent — re-run is a no-op and preserves existing rows", () => {
|
|
91
|
-
const raw = openTelemetryRaw();
|
|
92
|
-
const { db } = createTestDb();
|
|
93
|
-
|
|
94
|
-
createWatchdogEventsTable(db);
|
|
95
|
-
raw.exec(/*sql*/ `
|
|
96
|
-
INSERT INTO watchdog_events (id, created_at, check_name, value)
|
|
97
|
-
VALUES ('wd-1', 1000, 'event_loop_blocked', 60000)
|
|
98
|
-
`);
|
|
99
|
-
|
|
100
|
-
expect(() => createWatchdogEventsTable(db)).not.toThrow();
|
|
101
|
-
|
|
102
|
-
const rows = raw.query("SELECT id FROM watchdog_events").all();
|
|
103
|
-
expect(rows).toEqual([{ id: "wd-1" }]);
|
|
104
|
-
expect(
|
|
105
|
-
indexNames(raw).filter(
|
|
106
|
-
(name) => name === "idx_watchdog_events_created_at_id",
|
|
107
|
-
),
|
|
108
|
-
).toHaveLength(1);
|
|
109
|
-
});
|
|
110
|
-
});
|