@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,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for plugin bootstrap (PR 14).
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - A noop `init()` fires with a valid `PluginInitContext` that exposes every
|
|
6
|
+
* documented field.
|
|
7
|
+
* - `requiresCredential` entries are resolved through the credential store
|
|
8
|
+
* helper and arrive in `ctx.credentials`.
|
|
9
|
+
* - Version-mismatch registration fails with an error that names the plugin
|
|
10
|
+
* (the registry enforces this at `registerPlugin` time, so bootstrap never
|
|
11
|
+
* sees the malformed plugin).
|
|
12
|
+
* - Shutdown hook walks plugins in reverse registration order.
|
|
13
|
+
*
|
|
14
|
+
* Uses `mock.module` to stub `security/secure-keys.js` so credential
|
|
15
|
+
* resolution doesn't hit the real backend. `resetPluginRegistryForTests()`
|
|
16
|
+
* isolates registry state between cases.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync } from "node:fs";
|
|
20
|
+
import { rm } from "node:fs/promises";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
24
|
+
|
|
25
|
+
// Mock credential store before importing the bootstrap module so the
|
|
26
|
+
// module-under-test captures the stubbed binding.
|
|
27
|
+
const getSecureKeyAsyncMock = mock(
|
|
28
|
+
async (_account: string): Promise<string | undefined> => undefined,
|
|
29
|
+
);
|
|
30
|
+
mock.module("../security/secure-keys.js", () => ({
|
|
31
|
+
getSecureKeyAsync: getSecureKeyAsyncMock,
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
_setOverridesForTesting,
|
|
36
|
+
clearFeatureFlagOverridesCache,
|
|
37
|
+
} from "../config/assistant-feature-flags.js";
|
|
38
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
39
|
+
import {
|
|
40
|
+
bootstrapPlugins,
|
|
41
|
+
type DaemonContext,
|
|
42
|
+
} from "../daemon/external-plugins-bootstrap.js";
|
|
43
|
+
import { runShutdownHooks } from "../daemon/shutdown-registry.js";
|
|
44
|
+
import { RiskLevel } from "../permissions/types.js";
|
|
45
|
+
import {
|
|
46
|
+
ASSISTANT_API_VERSIONS,
|
|
47
|
+
registerPlugin,
|
|
48
|
+
resetPluginRegistryForTests,
|
|
49
|
+
} from "../plugins/registry.js";
|
|
50
|
+
import {
|
|
51
|
+
type Plugin,
|
|
52
|
+
PluginExecutionError,
|
|
53
|
+
type PluginInitContext,
|
|
54
|
+
} from "../plugins/types.js";
|
|
55
|
+
|
|
56
|
+
// Redirect plugin storage directory creation into a per-process temp tree so
|
|
57
|
+
// the test doesn't touch the developer's real ~/.vellum.
|
|
58
|
+
const TEST_INSTANCE_DIR = join(
|
|
59
|
+
tmpdir(),
|
|
60
|
+
`vellum-plugin-bootstrap-test-${process.pid}`,
|
|
61
|
+
);
|
|
62
|
+
process.env.BASE_DATA_DIR = TEST_INSTANCE_DIR;
|
|
63
|
+
|
|
64
|
+
const fakeConfig = {} as unknown as AssistantConfig;
|
|
65
|
+
const fakeCtx: DaemonContext = {
|
|
66
|
+
config: fakeConfig,
|
|
67
|
+
assistantVersion: "9.9.9-test",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function buildPlugin(
|
|
71
|
+
name: string,
|
|
72
|
+
extras: Partial<Omit<Plugin, "manifest">> = {},
|
|
73
|
+
options: {
|
|
74
|
+
requires?: Record<string, string>;
|
|
75
|
+
requiresCredential?: string[];
|
|
76
|
+
requiresFlag?: string[];
|
|
77
|
+
} = {},
|
|
78
|
+
): Plugin {
|
|
79
|
+
return {
|
|
80
|
+
manifest: {
|
|
81
|
+
name,
|
|
82
|
+
version: "0.0.1",
|
|
83
|
+
requires: options.requires ?? { pluginRuntime: "v1" },
|
|
84
|
+
...(options.requiresCredential
|
|
85
|
+
? { requiresCredential: options.requiresCredential }
|
|
86
|
+
: {}),
|
|
87
|
+
...(options.requiresFlag ? { requiresFlag: options.requiresFlag } : {}),
|
|
88
|
+
},
|
|
89
|
+
...extras,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
describe("plugin bootstrap", () => {
|
|
94
|
+
beforeEach(async () => {
|
|
95
|
+
resetPluginRegistryForTests();
|
|
96
|
+
getSecureKeyAsyncMock.mockReset();
|
|
97
|
+
getSecureKeyAsyncMock.mockImplementation(async () => undefined);
|
|
98
|
+
// Reset feature-flag cache so tests start from a known state. Individual
|
|
99
|
+
// tests that exercise `requiresFlag` use `_setOverridesForTesting(...)`
|
|
100
|
+
// to install their own overrides.
|
|
101
|
+
clearFeatureFlagOverridesCache();
|
|
102
|
+
// Clean storage directory between runs so nothing leaks across cases.
|
|
103
|
+
await rm(TEST_INSTANCE_DIR, { recursive: true, force: true });
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("noop plugin: init fires with a fully-populated PluginInitContext", async () => {
|
|
107
|
+
let received: PluginInitContext | undefined;
|
|
108
|
+
const plugin: Plugin = buildPlugin("alpha", {
|
|
109
|
+
async init(ctx) {
|
|
110
|
+
received = ctx;
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
registerPlugin(plugin);
|
|
114
|
+
|
|
115
|
+
await bootstrapPlugins(fakeCtx);
|
|
116
|
+
|
|
117
|
+
expect(received).toBeDefined();
|
|
118
|
+
const ctx = received!;
|
|
119
|
+
|
|
120
|
+
// Every documented field must be present on the context passed to init.
|
|
121
|
+
expect(ctx.config).toBeUndefined(); // no `plugins.alpha` block in fake config
|
|
122
|
+
expect(ctx.credentials).toEqual({});
|
|
123
|
+
expect(ctx.logger).toBeDefined();
|
|
124
|
+
expect(typeof (ctx.logger as { info: unknown }).info).toBe("function");
|
|
125
|
+
// Storage dir lives under vellumRoot()/plugins-data/<name> and must have
|
|
126
|
+
// been created on disk by bootstrap.
|
|
127
|
+
expect(ctx.pluginStorageDir).toBe(
|
|
128
|
+
join(TEST_INSTANCE_DIR, ".vellum", "plugins-data", "alpha"),
|
|
129
|
+
);
|
|
130
|
+
expect(existsSync(ctx.pluginStorageDir)).toBe(true);
|
|
131
|
+
expect(ctx.assistantVersion).toBe("9.9.9-test");
|
|
132
|
+
// apiVersions must surface the canonical capability table from the
|
|
133
|
+
// registry so plugins can negotiate at runtime.
|
|
134
|
+
expect(ctx.apiVersions).toBe(ASSISTANT_API_VERSIONS);
|
|
135
|
+
expect(ctx.apiVersions.pluginRuntime).toEqual(["v1"]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("credential resolution: init receives the resolved value under credentials[key]", async () => {
|
|
139
|
+
getSecureKeyAsyncMock.mockImplementation(async (account: string) => {
|
|
140
|
+
if (account === "some-key") return "super-secret-value";
|
|
141
|
+
return undefined;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
let received: PluginInitContext | undefined;
|
|
145
|
+
const plugin = buildPlugin(
|
|
146
|
+
"credentialed",
|
|
147
|
+
{
|
|
148
|
+
async init(ctx) {
|
|
149
|
+
received = ctx;
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{ requiresCredential: ["some-key"] },
|
|
153
|
+
);
|
|
154
|
+
registerPlugin(plugin);
|
|
155
|
+
|
|
156
|
+
await bootstrapPlugins(fakeCtx);
|
|
157
|
+
|
|
158
|
+
expect(getSecureKeyAsyncMock).toHaveBeenCalledTimes(1);
|
|
159
|
+
expect(getSecureKeyAsyncMock).toHaveBeenCalledWith("some-key");
|
|
160
|
+
expect(received?.credentials).toEqual({ "some-key": "super-secret-value" });
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("credential resolution: missing credential fails bootstrap with the plugin named", async () => {
|
|
164
|
+
getSecureKeyAsyncMock.mockImplementation(async () => undefined);
|
|
165
|
+
|
|
166
|
+
registerPlugin(
|
|
167
|
+
buildPlugin(
|
|
168
|
+
"missing-cred",
|
|
169
|
+
{ async init() {} },
|
|
170
|
+
{ requiresCredential: ["absent-key"] },
|
|
171
|
+
),
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
let caught: unknown;
|
|
175
|
+
try {
|
|
176
|
+
await bootstrapPlugins(fakeCtx);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
caught = err;
|
|
179
|
+
}
|
|
180
|
+
expect(caught).toBeInstanceOf(PluginExecutionError);
|
|
181
|
+
const msg = (caught as PluginExecutionError).message;
|
|
182
|
+
expect(msg).toContain("missing-cred");
|
|
183
|
+
expect(msg).toContain("absent-key");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("version mismatch: registration surfaces a clear error naming the plugin", () => {
|
|
187
|
+
// The assistant only exposes pluginRuntime@v1 — asking for v99 must fail
|
|
188
|
+
// registration with the plugin name in the message. The error is raised
|
|
189
|
+
// at registerPlugin() rather than bootstrap, because the registry is the
|
|
190
|
+
// single authoritative point of capability validation.
|
|
191
|
+
const plugin = buildPlugin(
|
|
192
|
+
"from-the-future",
|
|
193
|
+
{},
|
|
194
|
+
{ requires: { pluginRuntime: "v99" } },
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
let caught: unknown;
|
|
198
|
+
try {
|
|
199
|
+
registerPlugin(plugin);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
caught = err;
|
|
202
|
+
}
|
|
203
|
+
expect(caught).toBeInstanceOf(PluginExecutionError);
|
|
204
|
+
const msg = (caught as PluginExecutionError).message;
|
|
205
|
+
expect(msg).toContain("from-the-future");
|
|
206
|
+
expect(msg).toContain("pluginRuntime");
|
|
207
|
+
expect(msg).toContain("v99");
|
|
208
|
+
expect((caught as PluginExecutionError).pluginName).toBe("from-the-future");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("plugin init throw: bootstrap throws a PluginExecutionError naming the plugin", async () => {
|
|
212
|
+
registerPlugin(
|
|
213
|
+
buildPlugin("broken", {
|
|
214
|
+
async init() {
|
|
215
|
+
throw new Error("kaboom");
|
|
216
|
+
},
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
let caught: unknown;
|
|
221
|
+
try {
|
|
222
|
+
await bootstrapPlugins(fakeCtx);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
caught = err;
|
|
225
|
+
}
|
|
226
|
+
expect(caught).toBeInstanceOf(PluginExecutionError);
|
|
227
|
+
const msg = (caught as PluginExecutionError).message;
|
|
228
|
+
expect(msg).toContain("broken");
|
|
229
|
+
expect(msg).toContain("kaboom");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("partial-init failure: earlier plugins' onShutdown runs in reverse before the error propagates", async () => {
|
|
233
|
+
// If plugin N throws during init, every plugin 1..N-1 that already made
|
|
234
|
+
// it through its full init+contribution phase must have onShutdown()
|
|
235
|
+
// invoked in reverse registration order before bootstrap re-throws.
|
|
236
|
+
// Without this, earlier plugins leak live tools/routes/skills because
|
|
237
|
+
// the shutdown hook is only registered once the entire loop completes.
|
|
238
|
+
const callOrder: string[] = [];
|
|
239
|
+
registerPlugin(
|
|
240
|
+
buildPlugin("survivor-a", {
|
|
241
|
+
async init() {},
|
|
242
|
+
async onShutdown() {
|
|
243
|
+
callOrder.push("survivor-a");
|
|
244
|
+
},
|
|
245
|
+
}),
|
|
246
|
+
);
|
|
247
|
+
registerPlugin(
|
|
248
|
+
buildPlugin("survivor-b", {
|
|
249
|
+
async init() {},
|
|
250
|
+
async onShutdown() {
|
|
251
|
+
callOrder.push("survivor-b");
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
);
|
|
255
|
+
registerPlugin(
|
|
256
|
+
buildPlugin("failing", {
|
|
257
|
+
async init() {
|
|
258
|
+
throw new Error("mid-bootstrap failure");
|
|
259
|
+
},
|
|
260
|
+
async onShutdown() {
|
|
261
|
+
// Never called — this plugin never completes init, so it was never
|
|
262
|
+
// added to the active list that teardown walks.
|
|
263
|
+
callOrder.push("failing");
|
|
264
|
+
},
|
|
265
|
+
}),
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
let caught: unknown;
|
|
269
|
+
try {
|
|
270
|
+
await bootstrapPlugins(fakeCtx);
|
|
271
|
+
} catch (err) {
|
|
272
|
+
caught = err;
|
|
273
|
+
}
|
|
274
|
+
expect(caught).toBeInstanceOf(PluginExecutionError);
|
|
275
|
+
|
|
276
|
+
// Reverse order: survivor-b registered after survivor-a, so it tears
|
|
277
|
+
// down first; "failing" never entered the active list.
|
|
278
|
+
expect(callOrder).toEqual(["survivor-b", "survivor-a"]);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("shutdown order: onShutdown fires in reverse registration order", async () => {
|
|
282
|
+
const callOrder: string[] = [];
|
|
283
|
+
registerPlugin(
|
|
284
|
+
buildPlugin("first-registered", {
|
|
285
|
+
async onShutdown() {
|
|
286
|
+
callOrder.push("first-registered");
|
|
287
|
+
},
|
|
288
|
+
}),
|
|
289
|
+
);
|
|
290
|
+
registerPlugin(
|
|
291
|
+
buildPlugin("second-registered", {
|
|
292
|
+
async onShutdown() {
|
|
293
|
+
callOrder.push("second-registered");
|
|
294
|
+
},
|
|
295
|
+
}),
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
await bootstrapPlugins(fakeCtx);
|
|
299
|
+
await runShutdownHooks("test-shutdown");
|
|
300
|
+
|
|
301
|
+
// The last plugin to register must shut down first; the first to register
|
|
302
|
+
// shuts down last. Symmetric tear-down around registration order is the
|
|
303
|
+
// whole point of the reverse walk.
|
|
304
|
+
expect(callOrder).toEqual(["second-registered", "first-registered"]);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("empty registry: bootstrap seeds the first-party defaults without throwing", async () => {
|
|
308
|
+
// The bootstrap path calls `registerDefaultPlugins` at the top, so even
|
|
309
|
+
// when the test-reset registry starts empty the bootstrap emerges with
|
|
310
|
+
// the canonical defaults installed (compaction circuit breaker,
|
|
311
|
+
// tool-result truncate, etc.). Just assert bootstrap completes without
|
|
312
|
+
// throwing — the surface of defaults is verified in each pipeline's own
|
|
313
|
+
// dedicated test file.
|
|
314
|
+
await bootstrapPlugins(fakeCtx);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// ── requiresFlag gating (G2.2) ──────────────────────────────────────────
|
|
318
|
+
//
|
|
319
|
+
// Plugins that declare `manifest.requiresFlag: [key1, ...]` must only
|
|
320
|
+
// activate when ALL listed flag keys resolve to `true` at bootstrap.
|
|
321
|
+
// "Skipping" a plugin means:
|
|
322
|
+
// - init() is not invoked,
|
|
323
|
+
// - tools/routes/skills are not registered,
|
|
324
|
+
// - no shutdown hook entry is installed (nothing to tear down later).
|
|
325
|
+
// Plugins without `requiresFlag` are unaffected.
|
|
326
|
+
//
|
|
327
|
+
// Uses `_setOverridesForTesting` to control the resolver deterministically
|
|
328
|
+
// — no disk writes, no gateway IPC, no reliance on registry defaults.
|
|
329
|
+
|
|
330
|
+
test("requiresFlag enabled: plugin inits normally", async () => {
|
|
331
|
+
_setOverridesForTesting({ "plugin-gated-enabled": true });
|
|
332
|
+
|
|
333
|
+
let initFired = false;
|
|
334
|
+
const plugin = buildPlugin(
|
|
335
|
+
"gated-on",
|
|
336
|
+
{
|
|
337
|
+
async init() {
|
|
338
|
+
initFired = true;
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
{ requiresFlag: ["plugin-gated-enabled"] },
|
|
342
|
+
);
|
|
343
|
+
registerPlugin(plugin);
|
|
344
|
+
|
|
345
|
+
await bootstrapPlugins(fakeCtx);
|
|
346
|
+
|
|
347
|
+
expect(initFired).toBe(true);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("requiresFlag disabled: init does not fire and no tools/routes/skills are registered", async () => {
|
|
351
|
+
_setOverridesForTesting({ "plugin-gated-disabled": false });
|
|
352
|
+
|
|
353
|
+
let initFired = false;
|
|
354
|
+
// Attach tool/route/skill contributions alongside init. If gating works,
|
|
355
|
+
// none of them should land in their respective registries.
|
|
356
|
+
const plugin = buildPlugin(
|
|
357
|
+
"gated-off",
|
|
358
|
+
{
|
|
359
|
+
async init() {
|
|
360
|
+
initFired = true;
|
|
361
|
+
},
|
|
362
|
+
tools: [
|
|
363
|
+
{
|
|
364
|
+
name: "gated-off-tool",
|
|
365
|
+
description: "should not be registered",
|
|
366
|
+
category: "plugin-test",
|
|
367
|
+
defaultRiskLevel: RiskLevel.Low,
|
|
368
|
+
getDefinition: () => ({
|
|
369
|
+
name: "gated-off-tool",
|
|
370
|
+
description: "should not be registered",
|
|
371
|
+
input_schema: { type: "object", properties: {}, required: [] },
|
|
372
|
+
}),
|
|
373
|
+
execute: async () => ({ content: "nope", isError: false }),
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
routes: [
|
|
377
|
+
{
|
|
378
|
+
// Unique pattern so we don't collide with any other test's route.
|
|
379
|
+
pattern: /^\/_plugin\/gated-off\/status$/,
|
|
380
|
+
methods: ["GET"],
|
|
381
|
+
handler: async () => new Response("ok"),
|
|
382
|
+
},
|
|
383
|
+
],
|
|
384
|
+
skills: [
|
|
385
|
+
{
|
|
386
|
+
id: "gated-off/skill",
|
|
387
|
+
name: "gated-off-skill",
|
|
388
|
+
description: "should not be catalogued",
|
|
389
|
+
body: "# unused",
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
{ requiresFlag: ["plugin-gated-disabled"] },
|
|
394
|
+
);
|
|
395
|
+
registerPlugin(plugin);
|
|
396
|
+
|
|
397
|
+
// Grab tool / route / skill introspection helpers lazily so the import
|
|
398
|
+
// side effect happens after `mock.module` has taken effect.
|
|
399
|
+
const { getTool } = await import("../tools/registry.js");
|
|
400
|
+
const { getPluginSkillRefCount } =
|
|
401
|
+
await import("../plugins/plugin-skill-contributions.js");
|
|
402
|
+
const { matchSkillRoute } =
|
|
403
|
+
await import("../runtime/skill-route-registry.js");
|
|
404
|
+
|
|
405
|
+
await bootstrapPlugins(fakeCtx);
|
|
406
|
+
|
|
407
|
+
// init must not have fired.
|
|
408
|
+
expect(initFired).toBe(false);
|
|
409
|
+
// No tool contributed.
|
|
410
|
+
expect(getTool("gated-off-tool")).toBeUndefined();
|
|
411
|
+
// No route wired up — `matchSkillRoute` returns null when nothing matches.
|
|
412
|
+
expect(matchSkillRoute("/_plugin/gated-off/status", "GET")).toBeNull();
|
|
413
|
+
// No skill catalogued under this plugin's name — ref count stays 0.
|
|
414
|
+
expect(getPluginSkillRefCount("gated-off")).toBe(0);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
test("requiresFlag absent: plugin activates unconditionally", async () => {
|
|
418
|
+
// Deliberately do not set any overrides — the resolver defaults
|
|
419
|
+
// undeclared keys to `true`, but more importantly a plugin with no
|
|
420
|
+
// `requiresFlag` key must not consult the resolver at all.
|
|
421
|
+
let initFired = false;
|
|
422
|
+
const plugin = buildPlugin("no-flag", {
|
|
423
|
+
async init() {
|
|
424
|
+
initFired = true;
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
registerPlugin(plugin);
|
|
428
|
+
|
|
429
|
+
await bootstrapPlugins(fakeCtx);
|
|
430
|
+
|
|
431
|
+
expect(initFired).toBe(true);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("requiresFlag: one disabled flag out of several skips the plugin", async () => {
|
|
435
|
+
// When ANY listed flag is disabled, the plugin is skipped wholesale —
|
|
436
|
+
// this prevents sneaky partial activation on AND semantics.
|
|
437
|
+
_setOverridesForTesting({
|
|
438
|
+
"plugin-multi-a": true,
|
|
439
|
+
"plugin-multi-b": false,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
let initFired = false;
|
|
443
|
+
const plugin = buildPlugin(
|
|
444
|
+
"multi-flag",
|
|
445
|
+
{
|
|
446
|
+
async init() {
|
|
447
|
+
initFired = true;
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
{ requiresFlag: ["plugin-multi-a", "plugin-multi-b"] },
|
|
451
|
+
);
|
|
452
|
+
registerPlugin(plugin);
|
|
453
|
+
|
|
454
|
+
await bootstrapPlugins(fakeCtx);
|
|
455
|
+
|
|
456
|
+
expect(initFired).toBe(false);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test("requiresFlag disabled: no shutdown hook entry installed for the skipped plugin", async () => {
|
|
460
|
+
_setOverridesForTesting({ "plugin-shutdown-flag": false });
|
|
461
|
+
|
|
462
|
+
let shutdownFired = false;
|
|
463
|
+
const plugin = buildPlugin(
|
|
464
|
+
"shutdown-skipped",
|
|
465
|
+
{
|
|
466
|
+
async init() {},
|
|
467
|
+
async onShutdown() {
|
|
468
|
+
shutdownFired = true;
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
{ requiresFlag: ["plugin-shutdown-flag"] },
|
|
472
|
+
);
|
|
473
|
+
registerPlugin(plugin);
|
|
474
|
+
|
|
475
|
+
await bootstrapPlugins(fakeCtx);
|
|
476
|
+
await runShutdownHooks("test-shutdown");
|
|
477
|
+
|
|
478
|
+
// The shutdown hook is a single registered callback that walks a
|
|
479
|
+
// snapshot taken at bootstrap. A skipped plugin should never appear in
|
|
480
|
+
// that snapshot, so its `onShutdown` must never fire.
|
|
481
|
+
expect(shutdownFired).toBe(false);
|
|
482
|
+
});
|
|
483
|
+
});
|