@vellumai/assistant 0.11.4 → 0.11.5-staging.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/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__/config-loader-backfill.test.ts +19 -10
- 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__/managed-profile-guard.test.ts +4 -2
- 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/__tests__/voice-scoped-grant-consumer.test.ts +2 -0
- package/src/__tests__/voice-session-bridge.test.ts +42 -0
- 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/calls/__tests__/voice-session-bridge.test.ts +114 -0
- package/src/calls/voice-session-bridge.ts +60 -6
- 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/__tests__/balanced-model-experiment.test.ts +7 -7
- package/src/config/__tests__/default-profile-catalog.test.ts +3 -3
- package/src/config/default-profile-catalog.ts +5 -6
- package/src/config/feature-flag-registry.json +32 -32
- 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
|
@@ -110,9 +110,12 @@ function holdTheTurnOpen(): void {
|
|
|
110
110
|
// gate it never opened.
|
|
111
111
|
afterEach(() => releaseGate?.());
|
|
112
112
|
|
|
113
|
+
const providerCalls: { tools?: { name: string }[] }[] = [];
|
|
114
|
+
|
|
113
115
|
const provider: Provider = {
|
|
114
116
|
name: "scripted",
|
|
115
117
|
async sendMessage(messages, options): Promise<ProviderResponse> {
|
|
118
|
+
providerCalls.push({ tools: options?.tools });
|
|
116
119
|
await gate;
|
|
117
120
|
return scriptedProvider.sendMessage(messages, options);
|
|
118
121
|
},
|
|
@@ -169,6 +172,7 @@ function resetDb(): void {
|
|
|
169
172
|
db.run("DELETE FROM conversation_keys");
|
|
170
173
|
db.run("DELETE FROM conversations");
|
|
171
174
|
broadcasts.length = 0;
|
|
175
|
+
providerCalls.length = 0;
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
describe("runConversationTurn persistence", () => {
|
|
@@ -399,12 +403,32 @@ describe("runConversationTurn channel binding", () => {
|
|
|
399
403
|
expect(metadataOf(rows[0])).toMatchObject({
|
|
400
404
|
userMessageChannel: "plugin",
|
|
401
405
|
assistantMessageChannel: "plugin",
|
|
406
|
+
userMessageInterface: "plugin",
|
|
407
|
+
assistantMessageInterface: "plugin",
|
|
402
408
|
});
|
|
403
409
|
expect(rows[1].role).toBe("assistant");
|
|
404
410
|
expect(metadataOf(rows[1])).toMatchObject({
|
|
405
411
|
userMessageChannel: "plugin",
|
|
406
412
|
assistantMessageChannel: "plugin",
|
|
413
|
+
userMessageInterface: "plugin",
|
|
414
|
+
assistantMessageInterface: "plugin",
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("puts core tools on the wire for a plugin-channel turn", async () => {
|
|
419
|
+
// Plugin-driven iMessage turns are non-interactive, so host tools stay
|
|
420
|
+
// off, but sandbox tools (bash, web_search, ...) have to remain on the
|
|
421
|
+
// request. Missing them is what makes the model dump `to=bash code:`
|
|
422
|
+
// into the SMS as prose. The create path initializes the registry
|
|
423
|
+
// before constructing the Conversation that snapshots it.
|
|
424
|
+
await runConversationTurn({
|
|
425
|
+
channel: CHANNEL,
|
|
426
|
+
content: [{ type: "text", text: "hello" }],
|
|
407
427
|
});
|
|
428
|
+
|
|
429
|
+
const names = providerCalls[0]?.tools?.map((tool) => tool.name) ?? [];
|
|
430
|
+
expect(names.length).toBeGreaterThan(0);
|
|
431
|
+
expect(names).toContain("bash");
|
|
408
432
|
});
|
|
409
433
|
|
|
410
434
|
test("a queued turn carries its own channel rather than inheriting one", async () => {
|
|
@@ -441,6 +465,8 @@ describe("runConversationTurn channel binding", () => {
|
|
|
441
465
|
expect(metadataOf(userRows(conversationId)[1])).toMatchObject({
|
|
442
466
|
userMessageChannel: "plugin",
|
|
443
467
|
assistantMessageChannel: "plugin",
|
|
468
|
+
userMessageInterface: "plugin",
|
|
469
|
+
assistantMessageInterface: "plugin",
|
|
444
470
|
});
|
|
445
471
|
}, 20_000);
|
|
446
472
|
|
|
@@ -466,7 +492,7 @@ describe("runConversationTurn channel binding", () => {
|
|
|
466
492
|
userMessageChannel: "vellum",
|
|
467
493
|
assistantMessageChannel: "vellum",
|
|
468
494
|
});
|
|
469
|
-
});
|
|
495
|
+
}, 20_000);
|
|
470
496
|
|
|
471
497
|
test("keeps a known sender when a later turn omits it", async () => {
|
|
472
498
|
// A plugin that knows only the chat coordinates this time is silent about
|
|
@@ -103,6 +103,7 @@ mock.module("../daemon/conversation-store.js", () => ({
|
|
|
103
103
|
},
|
|
104
104
|
}));
|
|
105
105
|
|
|
106
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
106
107
|
import {
|
|
107
108
|
getProviderRoutingSource,
|
|
108
109
|
initializeProviders,
|
|
@@ -113,6 +114,12 @@ import {
|
|
|
113
114
|
ROUTES,
|
|
114
115
|
} from "../runtime/routes/secret-routes.js";
|
|
115
116
|
|
|
117
|
+
// A delete looks up the provider connections that resolve their auth through
|
|
118
|
+
// the credential, so the inference schema has to exist. No connection is
|
|
119
|
+
// created here: the refusal is covered by
|
|
120
|
+
// runtime/routes/__tests__/credential-delete-in-use.test.ts.
|
|
121
|
+
await initializeDb();
|
|
122
|
+
|
|
116
123
|
const addRoute = ROUTES.find(
|
|
117
124
|
(r) => r.method === "POST" && r.endpoint === "secrets",
|
|
118
125
|
)!;
|
|
@@ -470,6 +470,75 @@ describe("createSkillTool — required/type/enum validation", () => {
|
|
|
470
470
|
);
|
|
471
471
|
});
|
|
472
472
|
|
|
473
|
+
test("repairs array shapes before validation and passes the repaired value to the executor", async () => {
|
|
474
|
+
const hash = computeSkillVersionHash(tempDir);
|
|
475
|
+
const tool = createSkillTool(
|
|
476
|
+
makeEntry({
|
|
477
|
+
executor: "echo.ts",
|
|
478
|
+
input_schema: {
|
|
479
|
+
type: "object",
|
|
480
|
+
properties: {
|
|
481
|
+
activation_hints: { type: "array", items: { type: "string" } },
|
|
482
|
+
avoid_when: { type: "array", items: { type: "string" } },
|
|
483
|
+
files: { type: "array", items: { type: "object" } },
|
|
484
|
+
name: { type: "string" },
|
|
485
|
+
},
|
|
486
|
+
required: ["activation_hints"],
|
|
487
|
+
},
|
|
488
|
+
}),
|
|
489
|
+
tempDir,
|
|
490
|
+
hash,
|
|
491
|
+
BUNDLED,
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
const result = await tool.execute(
|
|
495
|
+
{
|
|
496
|
+
activation_hints: '["user asks to deploy staging","needs a rollback"]',
|
|
497
|
+
avoid_when: "the repo is dirty",
|
|
498
|
+
files: '[{"path":"references/notes.md","content":"hi"}]',
|
|
499
|
+
name: "x",
|
|
500
|
+
},
|
|
501
|
+
makeContext(),
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
expect(result.isError).toBe(false);
|
|
505
|
+
const parsed = JSON.parse(result.content);
|
|
506
|
+
expect(parsed.input).toEqual({
|
|
507
|
+
activation_hints: ["user asks to deploy staging", "needs a rollback"],
|
|
508
|
+
avoid_when: ["the repo is dirty"],
|
|
509
|
+
files: [{ path: "references/notes.md", content: "hi" }],
|
|
510
|
+
name: "x",
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test("rejects a truncated array with a self-correcting message", async () => {
|
|
515
|
+
const hash = computeSkillVersionHash(tempDir);
|
|
516
|
+
const tool = createSkillTool(
|
|
517
|
+
makeEntry({
|
|
518
|
+
executor: "echo.ts",
|
|
519
|
+
input_schema: {
|
|
520
|
+
type: "object",
|
|
521
|
+
properties: {
|
|
522
|
+
activation_hints: { type: "array", items: { type: "string" } },
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
}),
|
|
526
|
+
tempDir,
|
|
527
|
+
hash,
|
|
528
|
+
BUNDLED,
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
const result = await tool.execute(
|
|
532
|
+
{ activation_hints: "[user asks to deploy staging" },
|
|
533
|
+
makeContext(),
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
expect(result.isError).toBe(true);
|
|
537
|
+
expect(result.content).toContain(
|
|
538
|
+
"activation_hints must be an array: pass a JSON array, not a string",
|
|
539
|
+
);
|
|
540
|
+
});
|
|
541
|
+
|
|
473
542
|
test("passes valid input through to the executor unchanged", async () => {
|
|
474
543
|
const hash = computeSkillVersionHash(tempDir);
|
|
475
544
|
const tool = createSkillTool(
|
|
@@ -680,6 +749,98 @@ function subagentSpawnTool() {
|
|
|
680
749
|
* than the resolver silently deletes the resolver's behavior, and every test
|
|
681
750
|
* that calls `executeSubagentSpawn` directly still passes.
|
|
682
751
|
*/
|
|
752
|
+
describe("createSkillTool: bundled input repairs", () => {
|
|
753
|
+
const SCAFFOLD_SCHEMA = {
|
|
754
|
+
type: "object",
|
|
755
|
+
properties: {
|
|
756
|
+
skill_id: { type: "string" },
|
|
757
|
+
body_markdown: { type: "string" },
|
|
758
|
+
files: {
|
|
759
|
+
type: "array",
|
|
760
|
+
items: {
|
|
761
|
+
type: "object",
|
|
762
|
+
properties: {
|
|
763
|
+
path: { type: "string" },
|
|
764
|
+
content: { type: "string" },
|
|
765
|
+
copy_from: { type: "string" },
|
|
766
|
+
},
|
|
767
|
+
required: ["path"],
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
required: ["skill_id", "body_markdown"],
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
function makeScaffoldTool(bundled: boolean) {
|
|
775
|
+
return createSkillTool(
|
|
776
|
+
makeEntry({
|
|
777
|
+
name: "scaffold_managed_skill",
|
|
778
|
+
executor: "echo.ts",
|
|
779
|
+
input_schema: SCAFFOLD_SCHEMA,
|
|
780
|
+
}),
|
|
781
|
+
tempDir,
|
|
782
|
+
computeSkillVersionHash(tempDir),
|
|
783
|
+
bundled,
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
test("reads `body` as `body_markdown` and reaches the executor", async () => {
|
|
788
|
+
const result = await makeScaffoldTool(true).execute(
|
|
789
|
+
{ skill_id: "deploy", body: "# Deploy\n" },
|
|
790
|
+
makeContext(),
|
|
791
|
+
);
|
|
792
|
+
|
|
793
|
+
expect(result.isError).toBe(false);
|
|
794
|
+
expect(JSON.parse(result.content).input).toEqual({
|
|
795
|
+
skill_id: "deploy",
|
|
796
|
+
body_markdown: "# Deploy\n",
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
test("reads a path-keyed files map as the declared array", async () => {
|
|
801
|
+
const result = await makeScaffoldTool(true).execute(
|
|
802
|
+
{
|
|
803
|
+
skill_id: "deploy",
|
|
804
|
+
body_markdown: "# Deploy\n",
|
|
805
|
+
files: { "references/notes.md": "hi" },
|
|
806
|
+
},
|
|
807
|
+
makeContext(),
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
expect(result.isError).toBe(false);
|
|
811
|
+
expect(JSON.parse(result.content).input.files).toEqual([
|
|
812
|
+
{ path: "references/notes.md", content: "hi" },
|
|
813
|
+
]);
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
test("accepts the injected activity field the model is shown", async () => {
|
|
817
|
+
const result = await makeScaffoldTool(true).execute(
|
|
818
|
+
{
|
|
819
|
+
skill_id: "deploy",
|
|
820
|
+
body_markdown: "# Deploy\n",
|
|
821
|
+
activity: "Writing the deploy skill",
|
|
822
|
+
},
|
|
823
|
+
makeContext(),
|
|
824
|
+
);
|
|
825
|
+
|
|
826
|
+
expect(result.isError).toBe(false);
|
|
827
|
+
expect(JSON.parse(result.content).input.activity).toBe(
|
|
828
|
+
"Writing the deploy skill",
|
|
829
|
+
);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
test("a non-bundled skill reusing the name keeps its own contract", async () => {
|
|
833
|
+
const result = await makeScaffoldTool(false).execute(
|
|
834
|
+
{ skill_id: "deploy", body: "# Deploy\n" },
|
|
835
|
+
makeContext(),
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
expect(result.isError).toBe(true);
|
|
839
|
+
expect(result.content).toContain("body_markdown is required");
|
|
840
|
+
expect(result.content).toContain('Unknown parameter "body"');
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
|
|
683
844
|
describe("createSkillTool: subagent_spawn role validation", () => {
|
|
684
845
|
const ROLE_CASES: ReadonlyArray<readonly [string, SubagentRole]> = [
|
|
685
846
|
["researcher", "researcher"],
|
|
@@ -93,7 +93,7 @@ mock.module("../skills/version-hash.js", () => ({
|
|
|
93
93
|
// In CI there's no gateway, so we return a sensible default.
|
|
94
94
|
mock.module("../ipc/gateway-client.js", () => ({
|
|
95
95
|
ipcClassifyRisk: async (): Promise<
|
|
96
|
-
import("
|
|
96
|
+
import("@vellumai/gateway-client").ClassifyRiskIpcResponse
|
|
97
97
|
> => ({
|
|
98
98
|
risk: "low",
|
|
99
99
|
reason: "mock",
|
|
@@ -6,9 +6,11 @@ import type { UsageAttributionSnapshot } from "../usage/attribution.js";
|
|
|
6
6
|
let checkerDecision: "allow" | "prompt" | "deny" = "allow";
|
|
7
7
|
let checkerReason = "allowed";
|
|
8
8
|
let checkerRisk = "low";
|
|
9
|
+
let classifyThrow: Error | null = null;
|
|
9
10
|
let promptDecision: "allow" | "deny" = "allow";
|
|
10
11
|
let fakeToolResult: ToolExecutionResult = { content: "ok", isError: false };
|
|
11
12
|
let toolThrow: Error | null = null;
|
|
13
|
+
let grantConsumeOk = false;
|
|
12
14
|
|
|
13
15
|
// ── audit-terminal captures ───────────────────────────────
|
|
14
16
|
// The executor and its permission/approval collaborators no longer emit
|
|
@@ -86,13 +88,38 @@ mock.module("../platform/consent-cache.js", () => ({
|
|
|
86
88
|
|
|
87
89
|
mock.module("../permissions/checker.js", () => ({
|
|
88
90
|
isDynamicSkillLoadInvocation: () => false,
|
|
89
|
-
|
|
91
|
+
// Mirrors the real classifier's two failure modes: it throws on an aborted
|
|
92
|
+
// signal before doing anything, and it throws when the gateway is
|
|
93
|
+
// unreachable.
|
|
94
|
+
classifyRisk: async (
|
|
95
|
+
_name: string,
|
|
96
|
+
_input: Record<string, unknown>,
|
|
97
|
+
_workingDir?: string,
|
|
98
|
+
_preParsed?: unknown,
|
|
99
|
+
_manifestOverride?: unknown,
|
|
100
|
+
signal?: AbortSignal,
|
|
101
|
+
) => {
|
|
102
|
+
signal?.throwIfAborted();
|
|
103
|
+
if (classifyThrow) {
|
|
104
|
+
throw classifyThrow;
|
|
105
|
+
}
|
|
106
|
+
return { level: checkerRisk };
|
|
107
|
+
},
|
|
90
108
|
check: async () => ({ decision: checkerDecision, reason: checkerReason }),
|
|
91
|
-
generateAllowlistOptions: () => [
|
|
92
|
-
{ label: "exact", description: "exact", pattern: "exact" },
|
|
93
|
-
],
|
|
94
109
|
generateScopeOptions: () => [{ label: "/tmp", scope: "/tmp" }],
|
|
95
|
-
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
// The scoped-grant consume the gate performs for a sensitive tool invoked by
|
|
113
|
+
// an unknown actor. `mintGrantFromDecision` is stubbed only so the module's
|
|
114
|
+
// other importer links; nothing here mints.
|
|
115
|
+
mock.module("../approvals/approval-primitive.js", () => ({
|
|
116
|
+
consumeGrantForInvocation: async () =>
|
|
117
|
+
grantConsumeOk
|
|
118
|
+
? { ok: true, grant: { id: "grant-1" } }
|
|
119
|
+
: { ok: false, reason: "no_match" },
|
|
120
|
+
mintGrantFromDecision: async () => {
|
|
121
|
+
throw new Error("mintGrantFromDecision should not be called");
|
|
122
|
+
},
|
|
96
123
|
}));
|
|
97
124
|
|
|
98
125
|
mock.module("../persistence/conversation-crud.js", () => ({
|
|
@@ -255,9 +282,11 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
255
282
|
checkerDecision = "allow";
|
|
256
283
|
checkerReason = "allowed";
|
|
257
284
|
checkerRisk = "low";
|
|
285
|
+
classifyThrow = null;
|
|
258
286
|
promptDecision = "allow";
|
|
259
287
|
fakeToolResult = { content: "ok", isError: false };
|
|
260
288
|
toolThrow = null;
|
|
289
|
+
grantConsumeOk = false;
|
|
261
290
|
executedCaptures.length = 0;
|
|
262
291
|
errorCaptures.length = 0;
|
|
263
292
|
deniedCaptures.length = 0;
|
|
@@ -786,4 +815,102 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
786
815
|
expect(executedCaptures).toHaveLength(1);
|
|
787
816
|
expect(executedCaptures[0].toolName).toBe("file_edit");
|
|
788
817
|
});
|
|
818
|
+
|
|
819
|
+
// ── audit riskLevel on rows the permission check never produces ─────────
|
|
820
|
+
// The classifier runs before the pre-execution gates, so a gate denial, a
|
|
821
|
+
// gate error, and a grant-consumed execution all record the call's real
|
|
822
|
+
// risk rather than a placeholder (LUM-3159).
|
|
823
|
+
|
|
824
|
+
test("a gate-denied call records the classified risk, not a placeholder", async () => {
|
|
825
|
+
checkerRisk = "high";
|
|
826
|
+
const executor = new ToolExecutor(makePrompter());
|
|
827
|
+
|
|
828
|
+
// Verification control-plane paths are guardian-only; an unknown actor is
|
|
829
|
+
// denied by the gate before any permission check.
|
|
830
|
+
const result = await executor.execute(
|
|
831
|
+
"bash",
|
|
832
|
+
{ command: "curl http://host/v1/channel-verification-sessions" },
|
|
833
|
+
makeContext({ trustClass: "unknown" }),
|
|
834
|
+
);
|
|
835
|
+
|
|
836
|
+
expect(result.isError).toBe(true);
|
|
837
|
+
expect(deniedCaptures).toHaveLength(1);
|
|
838
|
+
expect(deniedCaptures[0].riskLevel).toBe("high");
|
|
839
|
+
expect(deniedCaptures[0].wasPrompted).toBe(false);
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
test("a gate-errored call records the classified risk", async () => {
|
|
843
|
+
checkerRisk = "medium";
|
|
844
|
+
const executor = new ToolExecutor(makePrompter());
|
|
845
|
+
|
|
846
|
+
await executor.execute("unknown_tool", { test: true }, makeContext());
|
|
847
|
+
|
|
848
|
+
expect(errorCaptures).toHaveLength(1);
|
|
849
|
+
expect(errorCaptures[0].errorMessage).toContain("Unknown tool");
|
|
850
|
+
expect(errorCaptures[0].riskLevel).toBe("medium");
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
test("a grant-consumed execution records the classified risk", async () => {
|
|
854
|
+
checkerRisk = "high";
|
|
855
|
+
grantConsumeOk = true;
|
|
856
|
+
const executor = new ToolExecutor(makePrompter());
|
|
857
|
+
|
|
858
|
+
// A sensitive tool from an unknown actor routes through the scoped-grant
|
|
859
|
+
// consume; a consumed grant skips the permission check entirely, so this
|
|
860
|
+
// is the one executed row whose risk the permission check never supplies.
|
|
861
|
+
const result = await executor.execute(
|
|
862
|
+
"file_write",
|
|
863
|
+
{ path: "/tmp/project/out.txt", content: "x" },
|
|
864
|
+
makeContext({ trustClass: "unknown" }),
|
|
865
|
+
);
|
|
866
|
+
|
|
867
|
+
expect(result).toMatchObject({
|
|
868
|
+
isError: false,
|
|
869
|
+
approvalReason: "grant_scoped_consumed",
|
|
870
|
+
});
|
|
871
|
+
expect(executedCaptures).toHaveLength(1);
|
|
872
|
+
expect(executedCaptures[0].decision).toBe("allow");
|
|
873
|
+
expect(executedCaptures[0].riskLevel).toBe("high");
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
test("a call whose classification never completes records 'unclassified', not 'low'", async () => {
|
|
877
|
+
classifyThrow = new Error("gateway unreachable");
|
|
878
|
+
const executor = new ToolExecutor(makePrompter());
|
|
879
|
+
|
|
880
|
+
// Gate error path: the row says the risk was never assessed.
|
|
881
|
+
await executor.execute("unknown_tool", { test: true }, makeContext());
|
|
882
|
+
expect(errorCaptures).toHaveLength(1);
|
|
883
|
+
expect(errorCaptures[0].riskLevel).toBe("unclassified");
|
|
884
|
+
|
|
885
|
+
// Past the gates, the permission check still requires a classification
|
|
886
|
+
// and fails closed; that error row is likewise honest about the risk.
|
|
887
|
+
errorCaptures.length = 0;
|
|
888
|
+
const result = await executor.execute(
|
|
889
|
+
"file_read",
|
|
890
|
+
{ path: "README.md" },
|
|
891
|
+
makeContext(),
|
|
892
|
+
);
|
|
893
|
+
expect(result.isError).toBe(true);
|
|
894
|
+
expect(executedCaptures).toHaveLength(0);
|
|
895
|
+
expect(errorCaptures).toHaveLength(1);
|
|
896
|
+
expect(errorCaptures[0].errorMessage).toContain("gateway unreachable");
|
|
897
|
+
expect(errorCaptures[0].riskLevel).toBe("unclassified");
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
test("a call aborted before it starts records 'unclassified' on its cancelled row", async () => {
|
|
901
|
+
checkerRisk = "high";
|
|
902
|
+
const executor = new ToolExecutor(makePrompter());
|
|
903
|
+
const controller = new AbortController();
|
|
904
|
+
controller.abort();
|
|
905
|
+
|
|
906
|
+
const result = await executor.execute(
|
|
907
|
+
"bash",
|
|
908
|
+
{ command: "rm -rf /tmp" },
|
|
909
|
+
makeContext({ signal: controller.signal }),
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
expect(result).toEqual({ content: "Cancelled", isError: true });
|
|
913
|
+
expect(errorCaptures).toHaveLength(1);
|
|
914
|
+
expect(errorCaptures[0].riskLevel).toBe("unclassified");
|
|
915
|
+
});
|
|
789
916
|
});
|