@vellumai/assistant 0.11.4 → 0.11.5-staging.1
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 +2 -2
- package/ARCHITECTURE.md +29 -4
- package/README.md +1 -1
- package/docs/architecture/memory.md +17 -5
- package/docs/architecture/security.md +96 -152
- package/docs/guardian-request-flow.md +13 -2
- 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/index.ts +1 -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/index.ts +1 -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/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +7 -14
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +29 -31
- 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/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +193 -3
- package/package.json +1 -1
- package/src/__tests__/anthropic-provider.test.ts +10 -3
- package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +4 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -6
- package/src/__tests__/channel-reply-delivery.test.ts +26 -13
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/client-os-metadata-persistence.test.ts +16 -0
- package/src/__tests__/compactor-retained-image-validation.test.ts +142 -0
- package/src/__tests__/container-cpu-sampler.test.ts +153 -0
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +17 -0
- package/src/__tests__/conversation-lifecycle.test.ts +20 -26
- package/src/__tests__/conversation-pairing.test.ts +186 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +21 -2
- package/src/__tests__/credential-broker-server-use.test.ts +24 -5
- package/src/__tests__/credential-routes.test.ts +22 -3
- package/src/__tests__/credential-security-invariants.test.ts +2 -1
- package/src/__tests__/daemon-credential-client.test.ts +88 -0
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/dm-backfill.test.ts +63 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -4
- package/src/__tests__/events-client-registration.test.ts +28 -0
- package/src/__tests__/file-write-tool.test.ts +4 -2
- package/src/__tests__/guardian-prompt-notice-privacy.test.ts +133 -0
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +143 -97
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/host-proxy-interface.test.ts +11 -1
- package/src/__tests__/host-shell-tool.test.ts +23 -4
- package/src/__tests__/image-conversion.test.ts +143 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/input-repairs.test.ts +207 -0
- package/src/__tests__/live-workspace-guard.test.ts +75 -0
- package/src/__tests__/mcp-abort-signal.test.ts +1 -1
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +1 -1
- package/src/__tests__/media-resolve-image-validation.test.ts +310 -0
- package/src/__tests__/mtime-cache.test.ts +2 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +124 -1
- package/src/__tests__/openai-provider.test.ts +22 -0
- package/src/__tests__/personal-memory-auth-bypass.test.ts +22 -6
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/platform.test.ts +16 -1
- package/src/__tests__/plugin-api-resolve-credential.test.ts +22 -13
- package/src/__tests__/plugin-api-shim.test.ts +5 -0
- package/src/__tests__/plugin-api-store-credential.test.ts +268 -0
- package/src/__tests__/plugin-config-data-migration.test.ts +8 -1
- 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 +0 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/resource-pressure-guard.test.ts +428 -0
- package/src/__tests__/resource-pressure-routes.test.ts +113 -0
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +27 -1
- package/src/__tests__/secret-routes-platform-proxy.test.ts +7 -0
- package/src/__tests__/skill-tool-factory.test.ts +161 -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 +63 -38
- package/src/__tests__/tool-policy.test.ts +97 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +7 -4
- package/src/__tests__/user-plugin-loader.test.ts +2 -0
- package/src/__tests__/validate-input.test.ts +243 -7
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/acp/__tests__/acp-claude-oauth.test.ts +97 -29
- package/src/acp/__tests__/prepare-agent-env.test.ts +376 -153
- package/src/acp/acp-claude-oauth.ts +38 -21
- package/src/acp/prepare-agent-env.ts +146 -55
- package/src/agent/loop-tool-dedup.test.ts +161 -0
- package/src/agent/loop.ts +68 -22
- package/src/api/constants/app-tools.ts +34 -0
- package/src/api/events/resource-pressure-status-changed.ts +53 -0
- package/src/api/index.ts +19 -0
- package/src/api/responses/resource-pressure-status.ts +25 -0
- package/src/approvals/guardian-channel-delivery.ts +70 -6
- package/src/approvals/guardian-request-resolvers.ts +32 -66
- package/src/channels/__tests__/message-audience.test.ts +49 -0
- package/src/channels/__tests__/types.test.ts +17 -3
- package/src/channels/message-audience.ts +40 -0
- package/src/channels/types.ts +16 -12
- package/src/cli/__tests__/catalog-search-help.test.ts +50 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +31 -0
- package/src/cli/commands/__tests__/conversations-search.test.ts +289 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/__tests__/inference-providers.test.ts +68 -0
- package/src/cli/commands/__tests__/keys.test.ts +89 -8
- package/src/cli/commands/__tests__/plugins.test.ts +57 -1
- package/src/cli/commands/channel-verification-sessions.help.ts +11 -9
- package/src/cli/commands/channel-verification-sessions.ts +11 -21
- package/src/cli/commands/conversations.help.ts +34 -0
- package/src/cli/commands/conversations.ts +125 -0
- package/src/cli/commands/inference-providers.ts +9 -4
- package/src/cli/commands/inference.help.ts +9 -4
- package/src/cli/commands/keys.help.ts +13 -1
- package/src/cli/commands/keys.ts +26 -15
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/index.help.ts +41 -27
- 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-validate.ts +18 -0
- package/src/cli/commands/monitoring.ts +1 -0
- package/src/cli/commands/plugins.help.ts +28 -31
- package/src/cli/commands/plugins.ts +10 -0
- package/src/cli/commands/skills.help.ts +13 -16
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/install-from-github.test.ts +38 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +34 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +32 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +2 -1
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +64 -0
- package/src/cli/lib/bundled-marketplace.json +13 -0
- package/src/cli/lib/daemon-credential-client.ts +25 -3
- package/src/cli/lib/install-from-github.ts +35 -24
- package/src/cli/lib/merge-plugin-tree.ts +22 -4
- package/src/cli/lib/plugin-surfaces.ts +27 -0
- package/src/config/feature-flag-registry.json +35 -19
- package/src/config/webhook-routing.ts +8 -0
- package/src/context/compactor.ts +31 -2
- package/src/daemon/__tests__/provider-rejection-log-fields.test.ts +272 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +4 -1
- package/src/daemon/conversation-error.ts +13 -0
- package/src/daemon/conversation-messaging.ts +22 -2
- package/src/daemon/conversation-process.ts +11 -0
- package/src/daemon/conversation-store.ts +10 -0
- package/src/daemon/conversation-surfaces.ts +21 -8
- package/src/daemon/conversation.ts +12 -10
- package/src/daemon/lifecycle.ts +6 -2
- package/src/daemon/message-types/conversations.ts +5 -7
- package/src/daemon/provider-rejection-log-fields.ts +124 -0
- package/src/daemon/resource-pressure-guard-lifecycle.ts +66 -0
- package/src/daemon/resource-pressure-guard.ts +387 -0
- package/src/daemon/shutdown-handlers.ts +2 -0
- package/src/daemon/startup-error.ts +16 -0
- package/src/daemon/trust-context.ts +14 -10
- package/src/daemon/unsendable-image-notice.ts +76 -0
- package/src/hooks/registry.ts +65 -51
- package/src/ipc/__tests__/socket-path.test.ts +15 -7
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/socket-cleanup.ts +2 -11
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -1
- package/src/messaging/provider-message-metadata.ts +128 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +116 -68
- package/src/messaging/providers/channel-transport.ts +90 -13
- package/src/messaging/providers/discord/send.ts +16 -0
- package/src/messaging/providers/discord/transport.ts +11 -1
- package/src/messaging/providers/index.ts +109 -16
- package/src/messaging/providers/slack/message-metadata.ts +45 -0
- package/src/messaging/providers/slack/render-transcript.ts +1 -4
- package/src/messaging/providers/slack/send.test.ts +6 -9
- package/src/messaging/providers/slack/send.ts +40 -28
- package/src/messaging/providers/slack/transport.ts +22 -26
- package/src/messaging/providers/telegram-bot/send.test.ts +2 -8
- package/src/messaging/providers/telegram-bot/transport.ts +3 -6
- package/src/messaging/read-provider-metadata.test.ts +157 -0
- package/src/messaging/read-provider-metadata.ts +51 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +94 -0
- package/src/notifications/__tests__/edit-notification.test.ts +174 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +536 -4
- package/src/notifications/adapters/macos.ts +55 -0
- package/src/notifications/adapters/slack.ts +10 -5
- package/src/notifications/assistant-reply-producer.ts +37 -4
- package/src/notifications/conversation-pairing.ts +111 -21
- package/src/notifications/edit-notification.ts +38 -8
- package/src/notifications/emit-signal.ts +12 -14
- package/src/notifications/home-feed-side-effect.ts +218 -15
- package/src/notifications/types.ts +5 -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 +28 -3
- package/src/permissions/confirmation-guardian-request.ts +5 -1
- package/src/persistence/conversation-crud.ts +23 -20
- package/src/persistence/conversation-queries.ts +14 -1
- package/src/persistence/conversation-types.ts +22 -0
- package/src/persistence/delivery-crud.ts +122 -15
- package/src/plugin-api/__tests__/import-graph-partial-mock.test.ts +67 -0
- package/src/plugin-api/conversation-turn.ts +27 -0
- package/src/plugin-api/credential-scope.test.ts +15 -0
- package/src/plugin-api/credential-scope.ts +14 -0
- package/src/plugin-api/index.ts +19 -1
- package/src/plugin-api/resolve-credential.ts +15 -10
- package/src/plugin-api/store-credential.ts +148 -0
- package/src/plugin-api/system-card.ts +36 -0
- package/src/plugin-api/vision-support.test.ts +82 -0
- package/src/plugin-api/vision-support.ts +14 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +361 -110
- package/src/plugins/defaults/image-fallback/__tests__/vision-recovery.test.ts +4 -0
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +58 -0
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +64 -23
- package/src/plugins/defaults/image-recovery/detect.ts +24 -1
- package/src/plugins/defaults/main.ts +15 -0
- package/src/plugins/defaults/memory/AGENTS.md +11 -3
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +9 -12
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +114 -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 +1 -61
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +68 -6
- 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/static-context.ts +0 -29
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +3 -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/mtime-cache.ts +1 -1
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/plugin-tree-walk.ts +63 -4
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/connection-resolution.ts +2 -1
- package/src/providers/content-blocks.ts +22 -0
- package/src/providers/gemini/client.ts +5 -2
- package/src/providers/inference/__tests__/adapter-factory-litellm.test.ts +47 -1
- package/src/providers/inference/__tests__/adapter-factory-ollama.test.ts +83 -0
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +98 -1
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +38 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +10 -0
- package/src/providers/inference/__tests__/connections-ollama.test.ts +90 -0
- package/src/providers/inference/__tests__/missing-credential-guard.test.ts +117 -0
- package/src/providers/inference/adapter-factory.ts +33 -2
- package/src/providers/inference/auth.ts +13 -2
- package/src/providers/inference/credential-usage.ts +37 -0
- package/src/providers/inference/missing-credential-guard.ts +110 -0
- package/src/providers/inference/resolve-auth.ts +7 -7
- package/src/providers/media-resolve.ts +176 -15
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +576 -46
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +322 -42
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +31 -10
- package/src/runtime/AGENTS.md +35 -0
- package/src/runtime/__tests__/runtime-http-port-collision.test.ts +131 -0
- package/src/runtime/__tests__/web-presence.test.ts +234 -0
- package/src/runtime/assistant-event-hub.ts +38 -0
- package/src/runtime/channel-reply-delivery.ts +54 -31
- package/src/runtime/channel-retry-sweep.ts +6 -4
- package/src/runtime/effective-capabilities.test.ts +19 -0
- package/src/runtime/effective-capabilities.ts +25 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/http-errors.ts +1 -0
- package/src/runtime/http-server.ts +20 -5
- package/src/runtime/routes/__tests__/client-routes.test.ts +150 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +133 -0
- package/src/runtime/routes/__tests__/credential-delete-in-use.test.ts +155 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +52 -1
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +175 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +88 -35
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/system-card-turn-termination.test.ts +107 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +75 -4
- package/src/runtime/routes/approval-routes.ts +37 -4
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +16 -22
- package/src/runtime/routes/canned-message-complete.ts +68 -29
- package/src/runtime/routes/client-routes.ts +113 -0
- package/src/runtime/routes/conversation-list-routes.ts +22 -7
- package/src/runtime/routes/conversation-management-routes.ts +15 -9
- package/src/runtime/routes/conversation-routes.ts +36 -22
- package/src/runtime/routes/credential-in-use.ts +85 -0
- package/src/runtime/routes/credential-routes.ts +55 -85
- package/src/runtime/routes/guardian-approval-interception.ts +22 -22
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +14 -12
- package/src/runtime/routes/identity-routes.ts +5 -121
- package/src/runtime/routes/inbound-message-handler.ts +71 -27
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -17
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +77 -50
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -97
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -77
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +17 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -70
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +9 -7
- package/src/runtime/routes/monitoring-routes.ts +9 -1
- package/src/runtime/routes/plugins-routes.ts +17 -5
- package/src/runtime/routes/resource-pressure-routes.ts +22 -0
- package/src/runtime/routes/secret-routes.ts +39 -8
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/user-route-dispatcher.ts +31 -2
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/slack-reply-session.test.ts +23 -13
- package/src/runtime/slack-reply-session.ts +38 -55
- package/src/runtime/web-presence.ts +90 -0
- package/src/skills/validate-input.ts +177 -40
- package/src/subagent/__tests__/consult-context-gating.test.ts +6 -10
- package/src/subagent/consult-context.ts +7 -23
- package/src/tools/credentials/broker.ts +24 -79
- 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/credentials/tool-policy.ts +67 -0
- package/src/tools/executor.ts +53 -15
- package/src/tools/network/__tests__/web-search.test.ts +41 -1
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/network/web-search.ts +21 -3
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/schema-transforms.ts +40 -5
- package/src/tools/shared/input-repairs.ts +160 -0
- package/src/tools/skills/skill-tool-factory.ts +18 -4
- package/src/tools/subagent/spawn.ts +0 -1
- package/src/tools/tool-approval-handler.ts +16 -10
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/util/__tests__/cgroup-memory.test.ts +3 -0
- package/src/util/cgroup-memory.ts +3 -0
- package/src/util/container-cpu-sampler.ts +250 -0
- package/src/util/image-conversion.ts +178 -14
- package/src/util/platform.ts +93 -10
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
afterAll,
|
|
7
|
+
afterEach,
|
|
8
|
+
beforeEach,
|
|
9
|
+
describe,
|
|
10
|
+
expect,
|
|
11
|
+
spyOn,
|
|
12
|
+
test,
|
|
13
|
+
} from "bun:test";
|
|
14
|
+
|
|
15
|
+
import { credentialKey } from "@vellumai/credential-storage";
|
|
16
|
+
|
|
17
|
+
import * as scrub from "../daemon/credential-transcript-scrub.js";
|
|
18
|
+
import * as manualToken from "../oauth/manual-token-connection.js";
|
|
19
|
+
import { resolveCredential } from "../plugin-api/resolve-credential.js";
|
|
20
|
+
import {
|
|
21
|
+
CredentialStoreError,
|
|
22
|
+
storeCredential,
|
|
23
|
+
} from "../plugin-api/store-credential.js";
|
|
24
|
+
import { runInPluginContext } from "../plugins/plugin-execution-context.js";
|
|
25
|
+
import * as secureKeys from "../security/secure-keys.js";
|
|
26
|
+
import {
|
|
27
|
+
_setMetadataPath,
|
|
28
|
+
getCredentialMetadata,
|
|
29
|
+
} from "../tools/credentials/metadata-store.js";
|
|
30
|
+
import { storeCredentialValue } from "../tools/credentials/store.js";
|
|
31
|
+
|
|
32
|
+
// Real metadata store backed by a temp file. A mock.module on metadata-store
|
|
33
|
+
// or secure-keys would replace the whole module namespace, so only the secure
|
|
34
|
+
// backend access, the transcript scrub, and the manual-token connection sync
|
|
35
|
+
// are intercepted, via restorable spies.
|
|
36
|
+
|
|
37
|
+
const TEST_DIR = join(
|
|
38
|
+
tmpdir(),
|
|
39
|
+
`vellum-plugin-storecred-${randomBytes(4).toString("hex")}`,
|
|
40
|
+
);
|
|
41
|
+
const META_PATH = join(TEST_DIR, "metadata.json");
|
|
42
|
+
|
|
43
|
+
/** The plugin every scoped case runs as: it owns the `acme` service. */
|
|
44
|
+
const PLUGIN = "acme";
|
|
45
|
+
|
|
46
|
+
/** Run `fn` as the plugin would: inside its execution context. */
|
|
47
|
+
function asPlugin<T>(fn: () => T): T {
|
|
48
|
+
return runInPluginContext(PLUGIN, fn);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let secureStore: Map<string, string>;
|
|
52
|
+
let writeSucceeds: boolean;
|
|
53
|
+
let setSpy: ReturnType<typeof spyOn>;
|
|
54
|
+
let getSpy: ReturnType<typeof spyOn>;
|
|
55
|
+
let scrubSpy: ReturnType<typeof spyOn>;
|
|
56
|
+
let syncSpy: ReturnType<typeof spyOn>;
|
|
57
|
+
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
if (existsSync(TEST_DIR)) {
|
|
60
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
63
|
+
_setMetadataPath(META_PATH);
|
|
64
|
+
|
|
65
|
+
secureStore = new Map();
|
|
66
|
+
writeSucceeds = true;
|
|
67
|
+
setSpy = spyOn(secureKeys, "setSecureKeyAsync").mockImplementation(
|
|
68
|
+
async (key: string, value: string) => {
|
|
69
|
+
if (!writeSucceeds) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
secureStore.set(key, value);
|
|
73
|
+
return true;
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
getSpy = spyOn(secureKeys, "getSecureKeyResultAsync").mockImplementation(
|
|
77
|
+
async (key: string) => ({
|
|
78
|
+
value: secureStore.get(key),
|
|
79
|
+
unreachable: false,
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
scrubSpy = spyOn(
|
|
83
|
+
scrub,
|
|
84
|
+
"scrubStoredCredentialFromTranscripts",
|
|
85
|
+
).mockImplementation(async () => ({
|
|
86
|
+
dbMessagesScrubbed: 0,
|
|
87
|
+
residentMessagesScrubbed: 0,
|
|
88
|
+
}));
|
|
89
|
+
syncSpy = spyOn(manualToken, "syncManualTokenConnection").mockImplementation(
|
|
90
|
+
async () => {},
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
afterEach(() => {
|
|
95
|
+
setSpy.mockRestore();
|
|
96
|
+
getSpy.mockRestore();
|
|
97
|
+
scrubSpy.mockRestore();
|
|
98
|
+
syncSpy.mockRestore();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
afterAll(() => {
|
|
102
|
+
_setMetadataPath(null);
|
|
103
|
+
if (existsSync(TEST_DIR)) {
|
|
104
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("storeCredential", () => {
|
|
109
|
+
test("creates a credential from a service/field ref", async () => {
|
|
110
|
+
const stored = await asPlugin(() =>
|
|
111
|
+
storeCredential("acme/api_key", "sk-secret"),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
expect(stored.service).toBe("acme");
|
|
115
|
+
expect(stored.field).toBe("api_key");
|
|
116
|
+
expect(stored.credentialId).toBeTruthy();
|
|
117
|
+
expect(secureStore.get(credentialKey("acme", "api_key"))).toBe("sk-secret");
|
|
118
|
+
expect(getCredentialMetadata("acme", "api_key")?.credentialId).toBe(
|
|
119
|
+
stored.credentialId,
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("stores a value that resolveCredential reads back", async () => {
|
|
124
|
+
await asPlugin(() => storeCredential("acme/api_key", "sk-secret"));
|
|
125
|
+
await expect(
|
|
126
|
+
asPlugin(() => resolveCredential("acme/api_key")),
|
|
127
|
+
).resolves.toBe("sk-secret");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("trims edge whitespace from the value", async () => {
|
|
131
|
+
await asPlugin(() => storeCredential("acme/api_key", " sk-secret\n"));
|
|
132
|
+
expect(secureStore.get(credentialKey("acme", "api_key"))).toBe("sk-secret");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("records label and description on the credential metadata", async () => {
|
|
136
|
+
await asPlugin(() =>
|
|
137
|
+
storeCredential("acme/api_key", "sk-secret", {
|
|
138
|
+
label: "Acme key",
|
|
139
|
+
description: "Used by the acme plugin",
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const metadata = getCredentialMetadata("acme", "api_key");
|
|
144
|
+
expect(metadata?.alias).toBe("Acme key");
|
|
145
|
+
expect(metadata?.usageDescription).toBe("Used by the acme plugin");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("replaces the value of an existing credential named by UUID", async () => {
|
|
149
|
+
const first = await asPlugin(() =>
|
|
150
|
+
storeCredential("acme/api_key", "sk-old"),
|
|
151
|
+
);
|
|
152
|
+
const second = await asPlugin(() =>
|
|
153
|
+
storeCredential(first.credentialId, "sk-new"),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
expect(second).toEqual(first);
|
|
157
|
+
expect(secureStore.get(credentialKey("acme", "api_key"))).toBe("sk-new");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("rejects a ref that names no credential and is not service/field", async () => {
|
|
161
|
+
await expect(
|
|
162
|
+
asPlugin(() => storeCredential("openai", "sk-secret")),
|
|
163
|
+
).rejects.toBeInstanceOf(CredentialStoreError);
|
|
164
|
+
expect(setSpy).not.toHaveBeenCalled();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("rejects an empty value", async () => {
|
|
168
|
+
await expect(
|
|
169
|
+
asPlugin(() => storeCredential("acme/api_key", " ")),
|
|
170
|
+
).rejects.toThrow(/value is required/);
|
|
171
|
+
expect(setSpy).not.toHaveBeenCalled();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("surfaces a failed secure-backend write", async () => {
|
|
175
|
+
writeSucceeds = false;
|
|
176
|
+
await expect(
|
|
177
|
+
asPlugin(() => storeCredential("acme/api_key", "sk-secret")),
|
|
178
|
+
).rejects.toThrow(CredentialStoreError);
|
|
179
|
+
expect(getCredentialMetadata("acme", "api_key")).toBeUndefined();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("scrubs the stored value from recent transcripts", async () => {
|
|
183
|
+
await asPlugin(() => storeCredential("acme/api_key", "sk-secret"));
|
|
184
|
+
expect(scrubSpy).toHaveBeenCalledWith("sk-secret");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("skips the transcript scrub when asked", async () => {
|
|
188
|
+
await asPlugin(() =>
|
|
189
|
+
storeCredential("acme/api_key", "sk-secret", {
|
|
190
|
+
skipTranscriptScrub: true,
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
193
|
+
expect(scrubSpy).not.toHaveBeenCalled();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("reconciles the manual-token connection for the service", async () => {
|
|
197
|
+
await asPlugin(() => storeCredential("acme/api_key", "sk-secret"));
|
|
198
|
+
expect(syncSpy).toHaveBeenCalledWith("acme");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("plugin scoping", () => {
|
|
202
|
+
test("fails closed with no plugin in context", async () => {
|
|
203
|
+
// A plugin's module body is evaluated outside any context. An unscoped
|
|
204
|
+
// branch there would let top-level code overwrite the user's own
|
|
205
|
+
// credentials, so the write is refused instead.
|
|
206
|
+
await expect(storeCredential("acme/api_key", "sk-secret")).rejects.toThrow(
|
|
207
|
+
/requires an active plugin execution context/,
|
|
208
|
+
);
|
|
209
|
+
expect(setSpy).not.toHaveBeenCalled();
|
|
210
|
+
expect(getCredentialMetadata("acme", "api_key")).toBeUndefined();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("blocks a plugin from storing a credential it does not own", async () => {
|
|
214
|
+
await expect(
|
|
215
|
+
asPlugin(() => storeCredential("openai/api_key", "sk-secret")),
|
|
216
|
+
).rejects.toThrow(/out of scope/);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("does not write the secure backend when out of scope", async () => {
|
|
220
|
+
await expect(
|
|
221
|
+
asPlugin(() => storeCredential("openai/api_key", "sk-secret")),
|
|
222
|
+
).rejects.toThrow(CredentialStoreError);
|
|
223
|
+
expect(setSpy).not.toHaveBeenCalled();
|
|
224
|
+
expect(getCredentialMetadata("openai", "api_key")).toBeUndefined();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("blocks a plugin from overwriting another credential by UUID", async () => {
|
|
228
|
+
// Seeded through the host write path, the way the user's own credential
|
|
229
|
+
// is created (`credentials set`), not through the plugin API.
|
|
230
|
+
const existing = await storeCredentialValue({
|
|
231
|
+
service: "openai",
|
|
232
|
+
field: "api_key",
|
|
233
|
+
value: "sk-user",
|
|
234
|
+
skipTranscriptScrub: true,
|
|
235
|
+
});
|
|
236
|
+
setSpy.mockClear();
|
|
237
|
+
|
|
238
|
+
await expect(
|
|
239
|
+
asPlugin(() => storeCredential(existing.credentialId, "sk-hijacked")),
|
|
240
|
+
).rejects.toThrow(/out of scope/);
|
|
241
|
+
expect(setSpy).not.toHaveBeenCalled();
|
|
242
|
+
expect(secureStore.get(credentialKey("openai", "api_key"))).toBe(
|
|
243
|
+
"sk-user",
|
|
244
|
+
);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("blocks a plugin from storing another service even when the field matches its name", async () => {
|
|
248
|
+
await expect(
|
|
249
|
+
asPlugin(() => storeCredential("openai/acme", "sk-secret")),
|
|
250
|
+
).rejects.toThrow(/out of scope/);
|
|
251
|
+
expect(setSpy).not.toHaveBeenCalled();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("allows a plugin to store multiple fields under its own service", async () => {
|
|
255
|
+
await asPlugin(async () => {
|
|
256
|
+
await storeCredential("acme/api_key", "key-secret");
|
|
257
|
+
await storeCredential("acme/token", "token-secret");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
expect(secureStore.get(credentialKey("acme", "api_key"))).toBe(
|
|
261
|
+
"key-secret",
|
|
262
|
+
);
|
|
263
|
+
expect(secureStore.get(credentialKey("acme", "token"))).toBe(
|
|
264
|
+
"token-secret",
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
});
|
|
@@ -32,7 +32,10 @@ import {
|
|
|
32
32
|
populateCacheAtBoot,
|
|
33
33
|
resetPluginCacheForTests,
|
|
34
34
|
} from "../plugins/mtime-cache.js";
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
PRESERVED_ENTRIES,
|
|
37
|
+
USER_STATE_ENTRIES,
|
|
38
|
+
} from "../plugins/plugin-tree-walk.js";
|
|
36
39
|
|
|
37
40
|
const ROOT = join(
|
|
38
41
|
tmpdir(),
|
|
@@ -87,6 +90,10 @@ describe("PRESERVED_ENTRIES", () => {
|
|
|
87
90
|
expect(PRESERVED_ENTRIES).toContain("data");
|
|
88
91
|
expect(PRESERVED_ENTRIES).toContain(".disabled");
|
|
89
92
|
});
|
|
93
|
+
|
|
94
|
+
test("user-state entries are the preserved set minus the provenance sidecar", () => {
|
|
95
|
+
expect([...USER_STATE_ENTRIES]).toEqual(["config.json", "data", ".disabled"]);
|
|
96
|
+
});
|
|
90
97
|
});
|
|
91
98
|
|
|
92
99
|
describe("fingerprint exclusion of preserved entries", () => {
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
getRegisteredInjectors,
|
|
23
23
|
registerPluginInjectors,
|
|
24
24
|
} from "../plugins/injector-registry.js";
|
|
25
|
+
import { resetPluginCacheForTests } from "../plugins/mtime-cache.js";
|
|
25
26
|
import {
|
|
26
27
|
registerPlugin,
|
|
27
28
|
resetPluginRegistryForTests,
|
|
@@ -85,6 +86,7 @@ function makeFakeTool(name: string): Tool {
|
|
|
85
86
|
|
|
86
87
|
beforeEach(() => {
|
|
87
88
|
resetPluginRegistryForTests();
|
|
89
|
+
resetPluginCacheForTests();
|
|
88
90
|
clearInjectorRegistry();
|
|
89
91
|
});
|
|
90
92
|
|
|
@@ -8,14 +8,18 @@
|
|
|
8
8
|
* non-null Set is an allowlist, `null` means no restriction. This suite locks
|
|
9
9
|
* the contract that the hook gather sites honor it:
|
|
10
10
|
* - `getHooksFor(name, { conversationId })` resolves the conversation's scope
|
|
11
|
-
* and excludes
|
|
11
|
+
* and excludes user-plugin hooks outside it. First-party default plugins
|
|
12
|
+
* always remain (they are core runtime, not per-chat toggles). Omitting the
|
|
12
13
|
* conversationId imposes no restriction.
|
|
14
|
+
* - `getUserHooksFor` returns those same in-process default-plugin hooks
|
|
15
|
+
* (plus user-land hooks), not only filesystem plugins.
|
|
13
16
|
* - `collectUserHookEntries(name, dirs, set)` excludes user-land plugin hooks
|
|
14
17
|
* outside the set, while standalone workspace hooks (not owned by a plugin)
|
|
15
18
|
* still run.
|
|
16
19
|
*
|
|
17
20
|
* The per-chat scope layers on top of (does not replace) the global
|
|
18
|
-
* `.disabled` sentinel check
|
|
21
|
+
* `.disabled` sentinel check. A user plugin excluded by either is excluded.
|
|
22
|
+
* A first-party default plugin is excluded only by `.disabled`.
|
|
19
23
|
* Injectors are intentionally NOT per-chat scoped (see
|
|
20
24
|
* `getRegisteredInjectors`): they run inside already-scoped plugin hooks, and
|
|
21
25
|
* every injector-contributing plugin is a first-party default.
|
|
@@ -39,7 +43,8 @@ import {
|
|
|
39
43
|
collectUserHookEntries,
|
|
40
44
|
resetHookCacheForTests,
|
|
41
45
|
} from "../hooks/hook-loader.js";
|
|
42
|
-
import { getHooksFor } from "../hooks/registry.js";
|
|
46
|
+
import { getHooksFor, getUserHooksFor } from "../hooks/registry.js";
|
|
47
|
+
import { resetPluginCacheForTests } from "../plugins/mtime-cache.js";
|
|
43
48
|
import {
|
|
44
49
|
registerPlugin,
|
|
45
50
|
resetPluginRegistryForTests,
|
|
@@ -66,6 +71,7 @@ function buildPlugin(
|
|
|
66
71
|
describe("getHooksFor per-chat plugin scope (in-process default hooks)", () => {
|
|
67
72
|
beforeEach(() => {
|
|
68
73
|
resetPluginRegistryForTests();
|
|
74
|
+
resetPluginCacheForTests();
|
|
69
75
|
resolveScopeMock.mockReset();
|
|
70
76
|
resolveScopeMock.mockImplementation(() => null);
|
|
71
77
|
});
|
|
@@ -136,6 +142,52 @@ describe("getHooksFor per-chat plugin scope (in-process default hooks)", () => {
|
|
|
136
142
|
await getHooksFor("user-prompt-submit", { conversationId: "c1" }),
|
|
137
143
|
).toHaveLength(0);
|
|
138
144
|
});
|
|
145
|
+
|
|
146
|
+
test("getUserHooksFor returns in-process default plugin hooks", async () => {
|
|
147
|
+
registerPlugin(
|
|
148
|
+
buildPlugin("default-memory", {
|
|
149
|
+
"user-prompt-submit": () => Promise.resolve(),
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
expect(await getUserHooksFor("user-prompt-submit")).toHaveLength(1);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("a first-party default plugin's hooks run even when the chat scope omits it", async () => {
|
|
156
|
+
let ran = 0;
|
|
157
|
+
registerPlugin(
|
|
158
|
+
buildPlugin("default-memory", {
|
|
159
|
+
"user-prompt-submit": () => {
|
|
160
|
+
ran++;
|
|
161
|
+
return Promise.resolve();
|
|
162
|
+
},
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
resolveScopeMock.mockImplementation(() => new Set(["user-a"]));
|
|
166
|
+
|
|
167
|
+
const hooks = await getHooksFor("user-prompt-submit", {
|
|
168
|
+
conversationId: "c1",
|
|
169
|
+
});
|
|
170
|
+
expect(hooks).toHaveLength(1);
|
|
171
|
+
await hooks[0]!({});
|
|
172
|
+
expect(ran).toBe(1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("a workspace-disabled first-party default is still excluded", async () => {
|
|
176
|
+
registerPlugin(
|
|
177
|
+
buildPlugin("default-memory", {
|
|
178
|
+
"user-prompt-submit": () => Promise.resolve(),
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
181
|
+
const dir = join(getWorkspacePluginsDir(), "default-memory");
|
|
182
|
+
mkdirSync(dir, { recursive: true });
|
|
183
|
+
writeFileSync(join(dir, ".disabled"), "");
|
|
184
|
+
try {
|
|
185
|
+
expect(await getHooksFor("user-prompt-submit")).toHaveLength(0);
|
|
186
|
+
expect(await getUserHooksFor("user-prompt-submit")).toHaveLength(0);
|
|
187
|
+
} finally {
|
|
188
|
+
rmSync(dir, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
});
|
|
139
191
|
});
|
|
140
192
|
|
|
141
193
|
describe("collectUserHookEntries per-chat plugin scope (user-land hooks)", () => {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getCurrentPluginName,
|
|
5
|
+
runInPluginContext,
|
|
6
|
+
runOutsidePluginContext,
|
|
7
|
+
} from "../plugins/plugin-execution-context.js";
|
|
8
|
+
|
|
9
|
+
describe("plugin execution context", () => {
|
|
10
|
+
test("reports no plugin outside any context", () => {
|
|
11
|
+
expect(getCurrentPluginName()).toBeUndefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("marks the plugin for the duration of the call", () => {
|
|
15
|
+
const seen = runInPluginContext("acme", () => getCurrentPluginName());
|
|
16
|
+
expect(seen).toBe("acme");
|
|
17
|
+
expect(getCurrentPluginName()).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("carries across await boundaries", async () => {
|
|
21
|
+
const seen = await runInPluginContext("acme", async () => {
|
|
22
|
+
await Promise.resolve();
|
|
23
|
+
return getCurrentPluginName();
|
|
24
|
+
});
|
|
25
|
+
expect(seen).toBe("acme");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("the innermost plugin wins when contexts nest", () => {
|
|
29
|
+
const seen = runInPluginContext("outer", () =>
|
|
30
|
+
runInPluginContext("inner", () => getCurrentPluginName()),
|
|
31
|
+
);
|
|
32
|
+
expect(seen).toBe("inner");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("runOutsidePluginContext", () => {
|
|
36
|
+
// Host code reached from a plugin (a default tool run during a turn a
|
|
37
|
+
// plugin route started) is not the plugin's code and must not borrow its
|
|
38
|
+
// identity, or a scoped API would attribute that work to the plugin.
|
|
39
|
+
test("clears a surrounding plugin context", () => {
|
|
40
|
+
const seen = runInPluginContext("acme", () =>
|
|
41
|
+
runOutsidePluginContext(() => getCurrentPluginName()),
|
|
42
|
+
);
|
|
43
|
+
expect(seen).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("keeps the context cleared across await boundaries", async () => {
|
|
47
|
+
const seen = await runInPluginContext("acme", () =>
|
|
48
|
+
runOutsidePluginContext(async () => {
|
|
49
|
+
await Promise.resolve();
|
|
50
|
+
return getCurrentPluginName();
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
expect(seen).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("restores the plugin context for work after it", () => {
|
|
57
|
+
const seen = runInPluginContext("acme", () => {
|
|
58
|
+
runOutsidePluginContext(() => getCurrentPluginName());
|
|
59
|
+
return getCurrentPluginName();
|
|
60
|
+
});
|
|
61
|
+
expect(seen).toBe("acme");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("a plugin context inside it scopes to the inner plugin", () => {
|
|
65
|
+
const seen = runInPluginContext("acme", () =>
|
|
66
|
+
runOutsidePluginContext(() =>
|
|
67
|
+
runInPluginContext("other", () => getCurrentPluginName()),
|
|
68
|
+
),
|
|
69
|
+
);
|
|
70
|
+
expect(seen).toBe("other");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -99,7 +99,6 @@ const BASELINE: Record<string, readonly string[]> = {
|
|
|
99
99
|
"../../../../../security/untrusted-content.js",
|
|
100
100
|
"../../../../api/events/memory-recalled.js",
|
|
101
101
|
"../../../../api/responses/memory-v3-selection-log.js",
|
|
102
|
-
"../../../../channels/types.js",
|
|
103
102
|
"../../../../config/assistant-feature-flags.js",
|
|
104
103
|
"../../../../config/default-profile-catalog.js",
|
|
105
104
|
"../../../../config/loader.js",
|
|
@@ -99,7 +99,6 @@ const BASELINE: Record<string, readonly string[]> = {
|
|
|
99
99
|
"src/daemon/handlers/skills.ts",
|
|
100
100
|
"src/daemon/skill-memory-refresh.ts",
|
|
101
101
|
"src/daemon/tool-side-effects.ts",
|
|
102
|
-
"src/daemon/trust-context.ts",
|
|
103
102
|
"src/home/feed-source-enrichment.ts",
|
|
104
103
|
"src/permissions/checker.ts",
|
|
105
104
|
"src/persistence/conversation-crud.ts",
|