@vellumai/assistant 0.6.5 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline wrapper tests for `ToolExecutor.execute` (PR 16).
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - The public `execute` method routes through `runPipeline("toolExecute",
|
|
6
|
+
* ...)` so `getMiddlewaresFor("toolExecute")` middleware participates.
|
|
7
|
+
* - The default `toolExecute` plugin (passthrough) preserves the original
|
|
8
|
+
* execution path — result and side effects match the unwrapped executor.
|
|
9
|
+
* - A spy middleware observes the full tool invocation (name, input, ctx).
|
|
10
|
+
* - A short-circuit middleware intercepts the call and supplies a custom
|
|
11
|
+
* result without hitting the real tool.
|
|
12
|
+
*
|
|
13
|
+
* These tests reuse the same module mocks as `tool-executor.test.ts` so the
|
|
14
|
+
* permission check, risk classifier, and tool registry are stubbed; the
|
|
15
|
+
* focus here is the pipeline wrapper, not the internal execution body.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
19
|
+
|
|
20
|
+
import type { ToolExecutionResult } from "../tools/types.js";
|
|
21
|
+
|
|
22
|
+
// ── Config mock ───────────────────────────────────────────────────────
|
|
23
|
+
const mockConfig = {
|
|
24
|
+
provider: "anthropic",
|
|
25
|
+
model: "test",
|
|
26
|
+
maxTokens: 4096,
|
|
27
|
+
dataDir: "/tmp",
|
|
28
|
+
timeouts: {
|
|
29
|
+
shellDefaultTimeoutSec: 120,
|
|
30
|
+
shellMaxTimeoutSec: 600,
|
|
31
|
+
permissionTimeoutSec: 300,
|
|
32
|
+
toolExecutionTimeoutSec: 120,
|
|
33
|
+
},
|
|
34
|
+
sandbox: {
|
|
35
|
+
enabled: false,
|
|
36
|
+
backend: "native" as const,
|
|
37
|
+
docker: {
|
|
38
|
+
image: "vellum-sandbox:latest",
|
|
39
|
+
cpus: 1,
|
|
40
|
+
memoryMb: 512,
|
|
41
|
+
pidsLimit: 256,
|
|
42
|
+
network: "none" as const,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
46
|
+
secretDetection: {
|
|
47
|
+
enabled: false,
|
|
48
|
+
action: "warn" as const,
|
|
49
|
+
entropyThreshold: 4.0,
|
|
50
|
+
},
|
|
51
|
+
permissions: {
|
|
52
|
+
mode: "workspace" as const,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
mock.module("../config/loader.js", () => ({
|
|
57
|
+
getConfig: () => mockConfig,
|
|
58
|
+
loadConfig: () => mockConfig,
|
|
59
|
+
invalidateConfigCache: () => {},
|
|
60
|
+
saveConfig: () => {},
|
|
61
|
+
loadRawConfig: () => ({}),
|
|
62
|
+
saveRawConfig: () => {},
|
|
63
|
+
getNestedValue: () => undefined,
|
|
64
|
+
setNestedValue: () => {},
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
// ── Logger mock ──────────────────────────────────────────────────────
|
|
68
|
+
// Bun's `mock.module` persists across test files until explicitly
|
|
69
|
+
// restored, so this mock can leak into `plugin-bootstrap.test.ts`. That
|
|
70
|
+
// file inspects `ctx.logger` (populated via `log.child({ plugin })`), so
|
|
71
|
+
// we return a Proxy whose `.child(...)` yields another Proxy with the
|
|
72
|
+
// same shape — the bootstrap test's `expect(ctx.logger).toBeDefined()`
|
|
73
|
+
// then passes regardless of test-file ordering.
|
|
74
|
+
function makeFakeLoggerProxy(): object {
|
|
75
|
+
return new Proxy({} as Record<string, unknown>, {
|
|
76
|
+
get: (_target, prop) => {
|
|
77
|
+
if (prop === "child") return () => makeFakeLoggerProxy();
|
|
78
|
+
return () => {};
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
mock.module("../util/logger.js", () => ({
|
|
83
|
+
getLogger: (_name?: string) => makeFakeLoggerProxy(),
|
|
84
|
+
truncateForLog: (value: string) => value,
|
|
85
|
+
}));
|
|
86
|
+
|
|
87
|
+
// ── Permission checker — always allow so execution reaches the tool ──
|
|
88
|
+
mock.module("../permissions/checker.js", () => ({
|
|
89
|
+
classifyRisk: async () => ({ level: "low" }),
|
|
90
|
+
check: async () => ({ decision: "allow", reason: "allowed" }),
|
|
91
|
+
generateAllowlistOptions: () => [
|
|
92
|
+
{ label: "exact", description: "exact", pattern: "exact" },
|
|
93
|
+
],
|
|
94
|
+
generateScopeOptions: () => [{ label: "/tmp", scope: "/tmp" }],
|
|
95
|
+
}));
|
|
96
|
+
|
|
97
|
+
// ── Tool usage store stub ────────────────────────────────────────────
|
|
98
|
+
mock.module("../memory/tool-usage-store.js", () => ({
|
|
99
|
+
recordToolInvocation: () => {},
|
|
100
|
+
getRecentInvocations: () => [],
|
|
101
|
+
rotateToolInvocations: () => 0,
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
// ── Tool registry: return a stub tool whose execute records the call ─
|
|
105
|
+
let lastToolCall: { name: string; input: Record<string, unknown> } | undefined;
|
|
106
|
+
let fakeToolResult: ToolExecutionResult = {
|
|
107
|
+
content: "real tool output",
|
|
108
|
+
isError: false,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
mock.module("../tools/registry.js", () => ({
|
|
112
|
+
getTool: (name: string) => {
|
|
113
|
+
if (name === "unknown_tool") return undefined;
|
|
114
|
+
return {
|
|
115
|
+
name,
|
|
116
|
+
description: "test tool",
|
|
117
|
+
category: "test",
|
|
118
|
+
defaultRiskLevel: "low",
|
|
119
|
+
getDefinition: () => ({}),
|
|
120
|
+
execute: async (input: Record<string, unknown>) => {
|
|
121
|
+
lastToolCall = { name, input };
|
|
122
|
+
return fakeToolResult;
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
getAllTools: () => [],
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
mock.module("../tools/shared/filesystem/path-policy.js", () => ({
|
|
130
|
+
sandboxPolicy: () => ({ ok: false }),
|
|
131
|
+
hostPolicy: () => ({ ok: false }),
|
|
132
|
+
}));
|
|
133
|
+
|
|
134
|
+
mock.module("../tools/terminal/sandbox.js", () => ({
|
|
135
|
+
wrapCommand: () => ({ command: "", sandboxed: false }),
|
|
136
|
+
}));
|
|
137
|
+
|
|
138
|
+
// ── Redaction + token manager so the executor's imports resolve ──────
|
|
139
|
+
mock.module("../security/redaction.js", () => ({
|
|
140
|
+
redactSensitiveFields: (input: Record<string, unknown>) => input,
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
mock.module("../security/token-manager.js", () => ({
|
|
144
|
+
TokenExpiredError: class TokenExpiredError extends Error {},
|
|
145
|
+
}));
|
|
146
|
+
|
|
147
|
+
// ── Imports — after mock.module so the executor under test picks them up ──
|
|
148
|
+
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
149
|
+
import { defaultToolExecutePlugin } from "../plugins/defaults/tool-execute.js";
|
|
150
|
+
import {
|
|
151
|
+
getMiddlewaresFor,
|
|
152
|
+
registerPlugin,
|
|
153
|
+
resetPluginRegistryForTests,
|
|
154
|
+
} from "../plugins/registry.js";
|
|
155
|
+
import type {
|
|
156
|
+
Middleware,
|
|
157
|
+
Plugin,
|
|
158
|
+
ToolExecuteArgs,
|
|
159
|
+
ToolExecuteResult,
|
|
160
|
+
} from "../plugins/types.js";
|
|
161
|
+
import { ToolExecutor } from "../tools/executor.js";
|
|
162
|
+
import type { ToolContext } from "../tools/types.js";
|
|
163
|
+
|
|
164
|
+
function makeContext(overrides?: Partial<ToolContext>): ToolContext {
|
|
165
|
+
return {
|
|
166
|
+
workingDir: "/tmp/project",
|
|
167
|
+
conversationId: "conversation-pipeline",
|
|
168
|
+
trustClass: "guardian",
|
|
169
|
+
...overrides,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function makePrompter(): PermissionPrompter {
|
|
174
|
+
return {
|
|
175
|
+
prompt: async () => ({ decision: "allow" as const }),
|
|
176
|
+
resolveConfirmation: () => {},
|
|
177
|
+
updateSender: () => {},
|
|
178
|
+
dispose: () => {},
|
|
179
|
+
} as unknown as PermissionPrompter;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
afterAll(() => {
|
|
183
|
+
mock.restore();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
beforeEach(() => {
|
|
187
|
+
resetPluginRegistryForTests();
|
|
188
|
+
lastToolCall = undefined;
|
|
189
|
+
fakeToolResult = { content: "real tool output", isError: false };
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe("ToolExecutor.execute → toolExecute pipeline", () => {
|
|
193
|
+
test("default pipeline (no plugins) runs the same execution path", async () => {
|
|
194
|
+
// With no plugins registered, the pipeline has an empty middleware
|
|
195
|
+
// array and the terminal (executeInternal) runs directly. The
|
|
196
|
+
// observable result must match the unwrapped behavior.
|
|
197
|
+
const executor = new ToolExecutor(makePrompter());
|
|
198
|
+
const result = await executor.execute(
|
|
199
|
+
"file_read",
|
|
200
|
+
{ path: "README.md" },
|
|
201
|
+
makeContext(),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
expect(result.isError).toBe(false);
|
|
205
|
+
expect(result.content).toBe("real tool output");
|
|
206
|
+
expect(lastToolCall).toEqual({
|
|
207
|
+
name: "file_read",
|
|
208
|
+
input: { path: "README.md" },
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("default tool-execute plugin: registering the passthrough preserves behavior", async () => {
|
|
213
|
+
// The default plugin is a passthrough whose middleware forwards to
|
|
214
|
+
// `next`. Registering it should not change observable behavior —
|
|
215
|
+
// the terminal still runs and returns the real tool result.
|
|
216
|
+
registerPlugin(defaultToolExecutePlugin);
|
|
217
|
+
|
|
218
|
+
// Sanity: the registry now reports exactly one middleware for the
|
|
219
|
+
// `toolExecute` slot, named `defaultToolExecute`.
|
|
220
|
+
const middlewares = getMiddlewaresFor("toolExecute");
|
|
221
|
+
expect(middlewares).toHaveLength(1);
|
|
222
|
+
expect(middlewares[0]?.name).toBe("defaultToolExecute");
|
|
223
|
+
|
|
224
|
+
const executor = new ToolExecutor(makePrompter());
|
|
225
|
+
const result = await executor.execute(
|
|
226
|
+
"file_read",
|
|
227
|
+
{ path: "README.md" },
|
|
228
|
+
makeContext(),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
expect(result.isError).toBe(false);
|
|
232
|
+
expect(result.content).toBe("real tool output");
|
|
233
|
+
expect(lastToolCall).toEqual({
|
|
234
|
+
name: "file_read",
|
|
235
|
+
input: { path: "README.md" },
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("spy middleware observes the full tool invocation (name, input, ctx)", async () => {
|
|
240
|
+
let observedArgs: ToolExecuteArgs | undefined;
|
|
241
|
+
let observedTurnCtx:
|
|
242
|
+
| { conversationId: string; requestId: string }
|
|
243
|
+
| undefined;
|
|
244
|
+
|
|
245
|
+
const spyMiddleware: Middleware<ToolExecuteArgs, ToolExecuteResult> =
|
|
246
|
+
async function spy(args, next, ctx) {
|
|
247
|
+
observedArgs = args;
|
|
248
|
+
observedTurnCtx = {
|
|
249
|
+
conversationId: ctx.conversationId,
|
|
250
|
+
requestId: ctx.requestId,
|
|
251
|
+
};
|
|
252
|
+
return next(args);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const spyPlugin: Plugin = {
|
|
256
|
+
manifest: {
|
|
257
|
+
name: "spy-tool-execute",
|
|
258
|
+
version: "0.0.1",
|
|
259
|
+
requires: { pluginRuntime: "v1" },
|
|
260
|
+
},
|
|
261
|
+
middleware: { toolExecute: spyMiddleware },
|
|
262
|
+
};
|
|
263
|
+
registerPlugin(spyPlugin);
|
|
264
|
+
|
|
265
|
+
const executor = new ToolExecutor(makePrompter());
|
|
266
|
+
const ctx = makeContext({
|
|
267
|
+
conversationId: "conv-spy",
|
|
268
|
+
requestId: "req-spy",
|
|
269
|
+
});
|
|
270
|
+
const result = await executor.execute(
|
|
271
|
+
"bash",
|
|
272
|
+
{ command: "echo hi", timeout_seconds: 10 },
|
|
273
|
+
ctx,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// Spy observed the full args
|
|
277
|
+
expect(observedArgs).toBeDefined();
|
|
278
|
+
expect(observedArgs!.name).toBe("bash");
|
|
279
|
+
expect(observedArgs!.input).toEqual({
|
|
280
|
+
command: "echo hi",
|
|
281
|
+
timeout_seconds: 10,
|
|
282
|
+
});
|
|
283
|
+
expect(observedArgs!.context).toBe(ctx);
|
|
284
|
+
|
|
285
|
+
// Spy observed the synthesized TurnContext carrying conversation +
|
|
286
|
+
// request IDs from the ToolContext.
|
|
287
|
+
expect(observedTurnCtx).toEqual({
|
|
288
|
+
conversationId: "conv-spy",
|
|
289
|
+
requestId: "req-spy",
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Terminal still ran — result reflects the real tool output.
|
|
293
|
+
expect(result.isError).toBe(false);
|
|
294
|
+
expect(result.content).toBe("real tool output");
|
|
295
|
+
expect(lastToolCall).toEqual({
|
|
296
|
+
name: "bash",
|
|
297
|
+
input: { command: "echo hi", timeout_seconds: 10 },
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("short-circuit middleware intercepts and supplies a custom result", async () => {
|
|
302
|
+
const syntheticResult: ToolExecuteResult = {
|
|
303
|
+
content: "synthesized by middleware",
|
|
304
|
+
isError: false,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const shortCircuit: Middleware<ToolExecuteArgs, ToolExecuteResult> =
|
|
308
|
+
async function shortCircuitMw(_args, _next) {
|
|
309
|
+
// Intentionally omit `next` — the terminal (real tool execution)
|
|
310
|
+
// must not run.
|
|
311
|
+
return syntheticResult;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const interceptPlugin: Plugin = {
|
|
315
|
+
manifest: {
|
|
316
|
+
name: "short-circuit-tool-execute",
|
|
317
|
+
version: "0.0.1",
|
|
318
|
+
requires: { pluginRuntime: "v1" },
|
|
319
|
+
},
|
|
320
|
+
middleware: { toolExecute: shortCircuit },
|
|
321
|
+
};
|
|
322
|
+
registerPlugin(interceptPlugin);
|
|
323
|
+
|
|
324
|
+
const executor = new ToolExecutor(makePrompter());
|
|
325
|
+
const result = await executor.execute(
|
|
326
|
+
"file_write",
|
|
327
|
+
{ path: "dangerous.txt", content: "should not run" },
|
|
328
|
+
makeContext(),
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
expect(result).toEqual(syntheticResult);
|
|
332
|
+
// The real tool execute must NOT have been called.
|
|
333
|
+
expect(lastToolCall).toBeUndefined();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test("slow middleware does not trip a pipeline-level timeout", async () => {
|
|
337
|
+
// Regression: `ToolExecutor.execute` used to pass the per-tool timeout
|
|
338
|
+
// to `runPipeline`, which made the pipeline race everything upstream of
|
|
339
|
+
// the tool call (permission checks, approval waits, middleware) against
|
|
340
|
+
// the same budget. A slow human clicking "allow" produced a
|
|
341
|
+
// `PluginTimeoutError` thrown past `executeInternal`'s catch block,
|
|
342
|
+
// breaking the `execute()` never-throws contract. The pipeline is now
|
|
343
|
+
// untimed; `executeWithTimeout` inside `executeInternal` is the sole
|
|
344
|
+
// enforcer of the per-tool budget and only wraps the actual tool call.
|
|
345
|
+
const slow: Middleware<ToolExecuteArgs, ToolExecuteResult> =
|
|
346
|
+
async function slowMw(args, next) {
|
|
347
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
348
|
+
return next(args);
|
|
349
|
+
};
|
|
350
|
+
registerPlugin({
|
|
351
|
+
manifest: {
|
|
352
|
+
name: "slow-tool-execute",
|
|
353
|
+
version: "0.0.1",
|
|
354
|
+
requires: { pluginRuntime: "v1" },
|
|
355
|
+
},
|
|
356
|
+
middleware: { toolExecute: slow },
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const prev = mockConfig.timeouts.toolExecutionTimeoutSec;
|
|
360
|
+
mockConfig.timeouts.toolExecutionTimeoutSec = 0.01;
|
|
361
|
+
try {
|
|
362
|
+
const executor = new ToolExecutor(makePrompter());
|
|
363
|
+
const result = await executor.execute(
|
|
364
|
+
"file_read",
|
|
365
|
+
{ path: "README.md" },
|
|
366
|
+
makeContext(),
|
|
367
|
+
);
|
|
368
|
+
// Middleware phase (50ms) exceeds the per-tool budget (10ms), but
|
|
369
|
+
// that budget is only enforced inside `executeWithTimeout` around
|
|
370
|
+
// the tool invocation itself. The terminal runs and succeeds.
|
|
371
|
+
expect(result.isError).toBe(false);
|
|
372
|
+
expect(result.content).toBe("real tool output");
|
|
373
|
+
} finally {
|
|
374
|
+
mockConfig.timeouts.toolExecutionTimeoutSec = prev;
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("multiple middlewares compose in registration order (outer-first)", async () => {
|
|
379
|
+
const trace: string[] = [];
|
|
380
|
+
|
|
381
|
+
const outer: Middleware<ToolExecuteArgs, ToolExecuteResult> =
|
|
382
|
+
async function outerMw(args, next) {
|
|
383
|
+
trace.push("outer:before");
|
|
384
|
+
const result = await next(args);
|
|
385
|
+
trace.push("outer:after");
|
|
386
|
+
return result;
|
|
387
|
+
};
|
|
388
|
+
const inner: Middleware<ToolExecuteArgs, ToolExecuteResult> =
|
|
389
|
+
async function innerMw(args, next) {
|
|
390
|
+
trace.push("inner:before");
|
|
391
|
+
const result = await next(args);
|
|
392
|
+
trace.push("inner:after");
|
|
393
|
+
return result;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
registerPlugin({
|
|
397
|
+
manifest: {
|
|
398
|
+
name: "outer-tool-execute",
|
|
399
|
+
version: "0.0.1",
|
|
400
|
+
requires: { pluginRuntime: "v1" },
|
|
401
|
+
},
|
|
402
|
+
middleware: { toolExecute: outer },
|
|
403
|
+
});
|
|
404
|
+
registerPlugin({
|
|
405
|
+
manifest: {
|
|
406
|
+
name: "inner-tool-execute",
|
|
407
|
+
version: "0.0.1",
|
|
408
|
+
requires: { pluginRuntime: "v1" },
|
|
409
|
+
},
|
|
410
|
+
middleware: { toolExecute: inner },
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const executor = new ToolExecutor(makePrompter());
|
|
414
|
+
const result = await executor.execute(
|
|
415
|
+
"file_read",
|
|
416
|
+
{ path: "README.md" },
|
|
417
|
+
makeContext(),
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
expect(result.isError).toBe(false);
|
|
421
|
+
// Outer middleware wraps inner (registration order = onion order),
|
|
422
|
+
// so the trace is outer:before → inner:before → terminal →
|
|
423
|
+
// inner:after → outer:after.
|
|
424
|
+
expect(trace).toEqual([
|
|
425
|
+
"outer:before",
|
|
426
|
+
"inner:before",
|
|
427
|
+
"inner:after",
|
|
428
|
+
"outer:after",
|
|
429
|
+
]);
|
|
430
|
+
});
|
|
431
|
+
});
|
|
@@ -67,12 +67,6 @@ mock.module("../tools/registry.js", () => ({
|
|
|
67
67
|
},
|
|
68
68
|
}));
|
|
69
69
|
|
|
70
|
-
mock.module("../hooks/manager.js", () => ({
|
|
71
|
-
getHookManager: () => ({
|
|
72
|
-
trigger: () => Promise.resolve({ blocked: false }),
|
|
73
|
-
}),
|
|
74
|
-
}));
|
|
75
|
-
|
|
76
70
|
import { check, classifyRisk } from "../permissions/checker.js";
|
|
77
71
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
78
72
|
import { RiskLevel } from "../permissions/types.js";
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Unlike tool-executor.test.ts, this file does NOT mock ../permissions/checker.js,
|
|
5
5
|
* so generateAllowlistOptions and generateScopeOptions run through the actual
|
|
6
|
-
* implementation (
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* implementation. With permission-controls-v3 OFF (the default), bash tools use
|
|
7
|
+
* the legacy shellAllowlistStrategy (buildShellAllowlistOptions → action: key
|
|
8
|
+
* patterns). With the flag ON, they use classifier-produced scope ladder options.
|
|
9
9
|
*/
|
|
10
10
|
import { beforeAll, describe, expect, mock, test } from "bun:test";
|
|
11
11
|
|
|
@@ -111,13 +111,6 @@ mock.module("../tools/terminal/sandbox.js", () => ({
|
|
|
111
111
|
wrapCommand: () => ({ command: "", sandboxed: false }),
|
|
112
112
|
}));
|
|
113
113
|
|
|
114
|
-
// ── Hooks manager ──
|
|
115
|
-
mock.module("../hooks/manager.js", () => ({
|
|
116
|
-
getHookManager: () => ({
|
|
117
|
-
trigger: async () => ({ blocked: false }),
|
|
118
|
-
}),
|
|
119
|
-
}));
|
|
120
|
-
|
|
121
114
|
// ── Ephemeral permissions ──
|
|
122
115
|
mock.module("../tasks/ephemeral-permissions.js", () => ({
|
|
123
116
|
getTaskRunRules: () => [],
|
|
@@ -193,6 +186,10 @@ beforeAll(async () => {
|
|
|
193
186
|
});
|
|
194
187
|
|
|
195
188
|
describe("ToolExecutor → real shell allowlist integration", () => {
|
|
189
|
+
// These tests run with permission-controls-v3 OFF (default), so
|
|
190
|
+
// generateAllowlistOptions falls through to shellAllowlistStrategy
|
|
191
|
+
// which uses action: key patterns from buildShellAllowlistOptions.
|
|
192
|
+
|
|
196
193
|
test("simple command produces parser-derived action keys", async () => {
|
|
197
194
|
const { prompter, getAllowlist, getScopes } = makeCapturingPrompter();
|
|
198
195
|
const executor = new ToolExecutor(prompter);
|