@vellumai/assistant 0.8.10 → 0.8.11-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- package/src/plugins/plugin-skill-contributions.ts +0 -292
package/src/util/device-id.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* extensible for future per-device metadata.
|
|
7
7
|
*
|
|
8
8
|
* Path resolution:
|
|
9
|
+
* - All modes: the `VELLUM_DEVICE_ID` env var takes precedence when set
|
|
10
|
+
* and is never written to device.json (see getDeviceIdOverride in
|
|
11
|
+
* config/env-registry.ts).
|
|
9
12
|
* - Containerized (IS_CONTAINERIZED=true): `/home/assistant/.vellum/device.json`
|
|
10
13
|
* — the assistant user's persistent home dir, kept off the shared data
|
|
11
14
|
* volume. Not affected by VELLUM_ENVIRONMENT because the container fs
|
|
@@ -24,7 +27,10 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
24
27
|
import { homedir } from "node:os";
|
|
25
28
|
import { join } from "node:path";
|
|
26
29
|
|
|
27
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
getDeviceIdOverride,
|
|
32
|
+
getIsContainerized,
|
|
33
|
+
} from "../config/env-registry.js";
|
|
28
34
|
import { getLogger } from "./logger.js";
|
|
29
35
|
import { getXdgVellumConfigDirName } from "./platform.js";
|
|
30
36
|
|
|
@@ -66,8 +72,9 @@ function resolveDeviceIdPaths(): { dir: string; file: string } {
|
|
|
66
72
|
*
|
|
67
73
|
* Resolution order:
|
|
68
74
|
* 1. Cached in-memory value (populated on first call)
|
|
69
|
-
* 2. `
|
|
70
|
-
* 3.
|
|
75
|
+
* 2. `VELLUM_DEVICE_ID` env var (CLI-injected; see env-registry)
|
|
76
|
+
* 3. `deviceId` field from device.json
|
|
77
|
+
* 4. Generate a new UUID, persist it to device.json, and return it
|
|
71
78
|
*
|
|
72
79
|
* On any read/write error the generated UUID is still cached so the
|
|
73
80
|
* process uses a consistent ID for the remainder of its lifetime.
|
|
@@ -77,6 +84,13 @@ export function getDeviceId(): string {
|
|
|
77
84
|
return cached;
|
|
78
85
|
}
|
|
79
86
|
|
|
87
|
+
const fromEnv = getDeviceIdOverride();
|
|
88
|
+
if (fromEnv) {
|
|
89
|
+
cached = fromEnv;
|
|
90
|
+
log.info({ deviceId: cached }, "Resolved device ID from VELLUM_DEVICE_ID");
|
|
91
|
+
return cached;
|
|
92
|
+
}
|
|
93
|
+
|
|
80
94
|
const { dir: vellumDir, file: filePath } = resolveDeviceIdPaths();
|
|
81
95
|
const generated = randomUUID();
|
|
82
96
|
|
package/src/util/platform.ts
CHANGED
|
@@ -282,6 +282,22 @@ export function getWorkspacePluginsDir(): string {
|
|
|
282
282
|
return join(getWorkspaceDir(), "plugins");
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Returns $VELLUM_WORKSPACE_DIR/tools — user-defined tool overrides.
|
|
287
|
+
*
|
|
288
|
+
* Each subdirectory `<name>/` provides either an override of a core tool of
|
|
289
|
+
* the same name or a net-new tool. The single canonical location removes
|
|
290
|
+
* the "which plugin wins" ambiguity that would arise if multiple plugins
|
|
291
|
+
* could register competing overrides for the same tool.
|
|
292
|
+
*
|
|
293
|
+
* Files under this directory are dynamic-imported by the workspace-tool
|
|
294
|
+
* loader on daemon start; the file risk classifier escalates writes under
|
|
295
|
+
* this path to High for the same reason `plugins/` is escalated.
|
|
296
|
+
*/
|
|
297
|
+
export function getWorkspaceToolsDir(): string {
|
|
298
|
+
return join(getWorkspaceDir(), "tools");
|
|
299
|
+
}
|
|
300
|
+
|
|
285
301
|
/** Returns $VELLUM_WORKSPACE_DIR/routes — user-defined HTTP route handlers. */
|
|
286
302
|
export function getWorkspaceRoutesDir(): string {
|
|
287
303
|
return join(getWorkspaceDir(), "routes");
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": false,
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": false,
|
|
7
|
+
"emitDeclarationOnly": true,
|
|
8
|
+
"sourceMap": false,
|
|
9
|
+
"outDir": "./.plugin-api-build/dts",
|
|
10
|
+
"rootDir": "./src"
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/plugin-api/index.ts"]
|
|
13
|
+
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the `globalThis.__vellumPluginRuntime` bridge.
|
|
3
|
-
*
|
|
4
|
-
* The bridge exists so workspace-local plugins (`<workspaceDir>/plugins/*`)
|
|
5
|
-
* can register with the daemon's bundled module instances even when the
|
|
6
|
-
* daemon is a `bun --compile` binary. Absolute-path imports against a
|
|
7
|
-
* compiled binary load fresh disk copies into a disjoint module graph; the
|
|
8
|
-
* bridge sidesteps that by attaching a stable handle to globalThis.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
getPluginRuntime,
|
|
15
|
-
installPluginRuntime,
|
|
16
|
-
uninstallPluginRuntimeForTests,
|
|
17
|
-
} from "../plugins/external-api.js";
|
|
18
|
-
import { registerPlugin } from "../plugins/registry.js";
|
|
19
|
-
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
20
|
-
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
21
|
-
|
|
22
|
-
describe("plugin external-api bridge", () => {
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
uninstallPluginRuntimeForTests();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
afterEach(() => {
|
|
28
|
-
uninstallPluginRuntimeForTests();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("installs a runtime handle on globalThis", () => {
|
|
32
|
-
expect(getPluginRuntime()).toBeUndefined();
|
|
33
|
-
installPluginRuntime();
|
|
34
|
-
const runtime = getPluginRuntime();
|
|
35
|
-
expect(runtime).toBeDefined();
|
|
36
|
-
expect(runtime?.version).toBe(1);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("exposes the canonical registerPlugin / hub / secrets references", () => {
|
|
40
|
-
installPluginRuntime();
|
|
41
|
-
const runtime = getPluginRuntime();
|
|
42
|
-
expect(runtime?.registerPlugin).toBe(registerPlugin);
|
|
43
|
-
expect(runtime?.assistantEventHub).toBe(assistantEventHub);
|
|
44
|
-
expect(runtime?.getSecureKeyAsync).toBe(getSecureKeyAsync);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("plugins can read the runtime via the documented globalThis key", () => {
|
|
48
|
-
installPluginRuntime();
|
|
49
|
-
// Mirror the access pattern documented for plugin authors.
|
|
50
|
-
const runtime = (
|
|
51
|
-
globalThis as { __vellumPluginRuntime?: { version: number } }
|
|
52
|
-
).__vellumPluginRuntime;
|
|
53
|
-
expect(runtime).toBeDefined();
|
|
54
|
-
expect(runtime?.version).toBe(1);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("install is idempotent — repeat calls preserve the same handle", () => {
|
|
58
|
-
installPluginRuntime();
|
|
59
|
-
const first = getPluginRuntime();
|
|
60
|
-
installPluginRuntime();
|
|
61
|
-
installPluginRuntime();
|
|
62
|
-
expect(getPluginRuntime()).toBe(first);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("getPluginRuntime returns undefined when the bridge is not installed", () => {
|
|
66
|
-
expect(getPluginRuntime()).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for plugin-contributed skills (PR 33).
|
|
3
|
-
*
|
|
4
|
-
* Covers:
|
|
5
|
-
* - A plugin declaring `skills: [...]` has its entries registered after
|
|
6
|
-
* bootstrap's `init()` succeeds.
|
|
7
|
-
* - The registered skill is discoverable via `loadSkillCatalog` and
|
|
8
|
-
* resolvable via `loadSkillBySelector` (the exact entry points the model's
|
|
9
|
-
* `skill_load` / `skill_execute` flow use).
|
|
10
|
-
* - Shutdown (runShutdownHooks) unregisters the plugin's skills so repeated
|
|
11
|
-
* bootstraps don't leak catalog entries.
|
|
12
|
-
* - Ref-counted register/unregister semantics match the tool registry's
|
|
13
|
-
* per-skill-id semantics (PR 13 precedent).
|
|
14
|
-
*
|
|
15
|
-
* Strategy mirrors `plugin-bootstrap.test.ts`: stub the credential store so
|
|
16
|
-
* bootstrap doesn't hit real backends, and `resetPluginRegistryForTests` /
|
|
17
|
-
* `resetPluginSkillContributionsForTests` between cases for isolation.
|
|
18
|
-
*/
|
|
19
|
-
|
|
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
|
-
// Stub credential store before importing bootstrap so the module captures
|
|
26
|
-
// the fake 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 { loadSkillBySelector, loadSkillCatalog } from "../config/skills.js";
|
|
35
|
-
import { bootstrapPlugins } from "../daemon/external-plugins-bootstrap.js";
|
|
36
|
-
import { runShutdownHooks } from "../daemon/shutdown-registry.js";
|
|
37
|
-
import {
|
|
38
|
-
getPluginContributedSkillDefinition,
|
|
39
|
-
getPluginContributedSkillSummaries,
|
|
40
|
-
getPluginSkillRefCount,
|
|
41
|
-
registerPluginSkills,
|
|
42
|
-
resetPluginSkillContributionsForTests,
|
|
43
|
-
unregisterPluginSkills,
|
|
44
|
-
} from "../plugins/plugin-skill-contributions.js";
|
|
45
|
-
import {
|
|
46
|
-
registerPlugin,
|
|
47
|
-
resetPluginRegistryForTests,
|
|
48
|
-
} from "../plugins/registry.js";
|
|
49
|
-
import {
|
|
50
|
-
type Plugin,
|
|
51
|
-
PluginExecutionError,
|
|
52
|
-
type PluginSkillRegistration,
|
|
53
|
-
} from "../plugins/types.js";
|
|
54
|
-
|
|
55
|
-
// Per-process temp tree so bootstrap's plugin-storage directory creation
|
|
56
|
-
// doesn't touch the developer's real ~/.vellum.
|
|
57
|
-
const TEST_WORKSPACE_DIR = join(
|
|
58
|
-
tmpdir(),
|
|
59
|
-
`vellum-plugin-skill-test-${process.pid}`,
|
|
60
|
-
);
|
|
61
|
-
process.env.VELLUM_WORKSPACE_DIR = TEST_WORKSPACE_DIR;
|
|
62
|
-
|
|
63
|
-
/** Build a plugin that contributes one or more skills. */
|
|
64
|
-
function buildSkillPlugin(
|
|
65
|
-
name: string,
|
|
66
|
-
skills: PluginSkillRegistration[],
|
|
67
|
-
extras: Partial<Omit<Plugin, "manifest" | "skills">> = {},
|
|
68
|
-
): Plugin {
|
|
69
|
-
return {
|
|
70
|
-
manifest: {
|
|
71
|
-
name,
|
|
72
|
-
version: "0.0.1",
|
|
73
|
-
},
|
|
74
|
-
skills,
|
|
75
|
-
...extras,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
describe("plugin skill contributions", () => {
|
|
80
|
-
beforeEach(async () => {
|
|
81
|
-
resetPluginRegistryForTests();
|
|
82
|
-
resetPluginSkillContributionsForTests();
|
|
83
|
-
getSecureKeyAsyncMock.mockReset();
|
|
84
|
-
getSecureKeyAsyncMock.mockImplementation(async () => undefined);
|
|
85
|
-
await rm(TEST_WORKSPACE_DIR, { recursive: true, force: true });
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("plugin skills are registered after bootstrap and exposed by the catalog", async () => {
|
|
89
|
-
const skill: PluginSkillRegistration = {
|
|
90
|
-
id: "plugin-demo-skill",
|
|
91
|
-
name: "plugin-demo",
|
|
92
|
-
displayName: "Plugin Demo",
|
|
93
|
-
description: "A skill contributed by a plugin",
|
|
94
|
-
body: "# Plugin Demo\n\nThis is the plugin-provided body.",
|
|
95
|
-
activationHints: ["demo", "plugin"],
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
registerPlugin(buildSkillPlugin("demo-plugin", [skill]));
|
|
99
|
-
await bootstrapPlugins();
|
|
100
|
-
|
|
101
|
-
// Ref count bumped to exactly 1 so we can tell register and unregister
|
|
102
|
-
// are balanced downstream.
|
|
103
|
-
expect(getPluginSkillRefCount("demo-plugin")).toBe(1);
|
|
104
|
-
|
|
105
|
-
// In-memory registry query surfaces the summary form.
|
|
106
|
-
const summaries = getPluginContributedSkillSummaries();
|
|
107
|
-
const registered = summaries.find((s) => s.id === "plugin-demo-skill");
|
|
108
|
-
expect(registered).toBeDefined();
|
|
109
|
-
expect(registered?.source).toBe("plugin");
|
|
110
|
-
expect(registered?.name).toBe("plugin-demo");
|
|
111
|
-
expect(registered?.displayName).toBe("Plugin Demo");
|
|
112
|
-
expect(registered?.activationHints).toEqual(["demo", "plugin"]);
|
|
113
|
-
// Summary must not carry the body — bodies are definition-only.
|
|
114
|
-
expect((registered as unknown as { body?: unknown }).body).toBeUndefined();
|
|
115
|
-
|
|
116
|
-
// The full definition is retrievable by id.
|
|
117
|
-
const def = getPluginContributedSkillDefinition("plugin-demo-skill");
|
|
118
|
-
expect(def).toBeDefined();
|
|
119
|
-
expect(def?.body).toContain("This is the plugin-provided body");
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("catalog and loadSkillBySelector discover plugin-contributed skills (skill_load pathway)", async () => {
|
|
123
|
-
const skill: PluginSkillRegistration = {
|
|
124
|
-
id: "catalog-visible-skill",
|
|
125
|
-
name: "catalog-visible",
|
|
126
|
-
description: "A plugin skill expected to surface in loadSkillCatalog",
|
|
127
|
-
body: "# Catalog-Visible Skill\n\nBody content for skill_load.",
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
registerPlugin(buildSkillPlugin("catalog-plugin", [skill]));
|
|
131
|
-
await bootstrapPlugins();
|
|
132
|
-
|
|
133
|
-
// loadSkillCatalog is the exact entry point `skill_load` consults via
|
|
134
|
-
// `loadSkillBySelector` -> `resolveSkillSelector`.
|
|
135
|
-
const catalog = loadSkillCatalog();
|
|
136
|
-
const found = catalog.find((s) => s.id === "catalog-visible-skill");
|
|
137
|
-
expect(found).toBeDefined();
|
|
138
|
-
expect(found?.source).toBe("plugin");
|
|
139
|
-
expect(found?.description).toBe(skill.description);
|
|
140
|
-
|
|
141
|
-
// loadSkillBySelector is what SkillLoadTool.execute calls — it must
|
|
142
|
-
// return a fully-populated SkillDefinition, including the body.
|
|
143
|
-
const lookup = loadSkillBySelector("catalog-visible-skill");
|
|
144
|
-
expect(lookup.error).toBeUndefined();
|
|
145
|
-
expect(lookup.skill).toBeDefined();
|
|
146
|
-
expect(lookup.skill?.id).toBe("catalog-visible-skill");
|
|
147
|
-
expect(lookup.skill?.body).toContain("Body content for skill_load");
|
|
148
|
-
|
|
149
|
-
// And name-based resolution (what users type when the model picks a
|
|
150
|
-
// skill by name) works the same way.
|
|
151
|
-
const byName = loadSkillBySelector("catalog-visible");
|
|
152
|
-
expect(byName.skill?.id).toBe("catalog-visible-skill");
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("shutdown unregisters plugin skills so the catalog no longer lists them", async () => {
|
|
156
|
-
const skill: PluginSkillRegistration = {
|
|
157
|
-
id: "ephemeral-skill",
|
|
158
|
-
name: "ephemeral",
|
|
159
|
-
description: "Disappears after shutdown",
|
|
160
|
-
body: "Only visible while the plugin is alive.",
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
registerPlugin(buildSkillPlugin("ephemeral-plugin", [skill]));
|
|
164
|
-
await bootstrapPlugins();
|
|
165
|
-
|
|
166
|
-
// Sanity: present before shutdown.
|
|
167
|
-
expect(loadSkillCatalog().some((s) => s.id === "ephemeral-skill")).toBe(
|
|
168
|
-
true,
|
|
169
|
-
);
|
|
170
|
-
expect(getPluginSkillRefCount("ephemeral-plugin")).toBe(1);
|
|
171
|
-
|
|
172
|
-
await runShutdownHooks("test-shutdown");
|
|
173
|
-
|
|
174
|
-
// After shutdown, the plugin's skill is gone from both the in-memory
|
|
175
|
-
// registry and any catalog view that consults it.
|
|
176
|
-
expect(getPluginSkillRefCount("ephemeral-plugin")).toBe(0);
|
|
177
|
-
expect(
|
|
178
|
-
getPluginContributedSkillDefinition("ephemeral-skill"),
|
|
179
|
-
).toBeUndefined();
|
|
180
|
-
|
|
181
|
-
const catalogAfter = loadSkillCatalog();
|
|
182
|
-
expect(catalogAfter.some((s) => s.id === "ephemeral-skill")).toBe(false);
|
|
183
|
-
|
|
184
|
-
// And the selector lookup the model would use fails closed.
|
|
185
|
-
const lookup = loadSkillBySelector("ephemeral-skill");
|
|
186
|
-
expect(lookup.skill).toBeUndefined();
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("bootstrap is a no-op for plugins without a skills list", async () => {
|
|
190
|
-
// A plugin with no `skills` field must not bump ref counts or
|
|
191
|
-
// populate the catalog at all.
|
|
192
|
-
registerPlugin({
|
|
193
|
-
manifest: {
|
|
194
|
-
name: "no-skills-plugin",
|
|
195
|
-
version: "0.0.1",
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
await bootstrapPlugins();
|
|
200
|
-
|
|
201
|
-
expect(getPluginSkillRefCount("no-skills-plugin")).toBe(0);
|
|
202
|
-
expect(getPluginContributedSkillSummaries()).toEqual([]);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
test("duplicate skill id across plugins fails bootstrap with the plugin named", async () => {
|
|
206
|
-
const shared: PluginSkillRegistration = {
|
|
207
|
-
id: "contested-id",
|
|
208
|
-
name: "contested",
|
|
209
|
-
description: "First",
|
|
210
|
-
body: "from first plugin",
|
|
211
|
-
};
|
|
212
|
-
const duplicate: PluginSkillRegistration = {
|
|
213
|
-
id: "contested-id",
|
|
214
|
-
name: "contested",
|
|
215
|
-
description: "Second",
|
|
216
|
-
body: "from second plugin",
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
registerPlugin(buildSkillPlugin("first-plugin", [shared]));
|
|
220
|
-
registerPlugin(buildSkillPlugin("second-plugin", [duplicate]));
|
|
221
|
-
|
|
222
|
-
let caught: unknown;
|
|
223
|
-
try {
|
|
224
|
-
await bootstrapPlugins();
|
|
225
|
-
} catch (err) {
|
|
226
|
-
caught = err;
|
|
227
|
-
}
|
|
228
|
-
expect(caught).toBeInstanceOf(PluginExecutionError);
|
|
229
|
-
// The error must identify which plugin tripped the collision so
|
|
230
|
-
// operators can deploy a fix.
|
|
231
|
-
const msg = (caught as PluginExecutionError).message;
|
|
232
|
-
expect(msg).toContain("second-plugin");
|
|
233
|
-
expect(msg).toContain("contested-id");
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
test("intra-batch duplicate skill id in one plugin is rejected at registration", () => {
|
|
237
|
-
// Directly exercise the registry (no bootstrap) — a single plugin
|
|
238
|
-
// declaring the same id twice is a configuration bug and must fail
|
|
239
|
-
// loudly rather than silently overwrite.
|
|
240
|
-
expect(() =>
|
|
241
|
-
registerPluginSkills("dup-plugin", [
|
|
242
|
-
{
|
|
243
|
-
id: "dup-id",
|
|
244
|
-
name: "one",
|
|
245
|
-
description: "first",
|
|
246
|
-
body: "A",
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
id: "dup-id",
|
|
250
|
-
name: "two",
|
|
251
|
-
description: "second",
|
|
252
|
-
body: "B",
|
|
253
|
-
},
|
|
254
|
-
]),
|
|
255
|
-
).toThrow(PluginExecutionError);
|
|
256
|
-
expect(() =>
|
|
257
|
-
registerPluginSkills("dup-plugin", [
|
|
258
|
-
{ id: "x", name: "one", description: "a", body: "A" },
|
|
259
|
-
{ id: "x", name: "two", description: "b", body: "B" },
|
|
260
|
-
]),
|
|
261
|
-
).toThrow(/declared skill "x" more than once/);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
test("ref-counted unregister: second unregister call after repeated registers drops skills", () => {
|
|
265
|
-
const registrations: PluginSkillRegistration[] = [
|
|
266
|
-
{
|
|
267
|
-
id: "refcount-skill",
|
|
268
|
-
name: "refcount",
|
|
269
|
-
description: "Ref-count demo",
|
|
270
|
-
body: "stays until the counter hits zero",
|
|
271
|
-
},
|
|
272
|
-
];
|
|
273
|
-
|
|
274
|
-
registerPluginSkills("refcount-plugin", registrations);
|
|
275
|
-
// Second call is the "same plugin registered again" pattern (hot
|
|
276
|
-
// reload). It must bump the counter without re-inserting.
|
|
277
|
-
registerPluginSkills("refcount-plugin", registrations);
|
|
278
|
-
expect(getPluginSkillRefCount("refcount-plugin")).toBe(2);
|
|
279
|
-
expect(getPluginContributedSkillDefinition("refcount-skill")).toBeDefined();
|
|
280
|
-
|
|
281
|
-
// One unregister decrements only — skill must still be registered.
|
|
282
|
-
unregisterPluginSkills("refcount-plugin");
|
|
283
|
-
expect(getPluginSkillRefCount("refcount-plugin")).toBe(1);
|
|
284
|
-
expect(getPluginContributedSkillDefinition("refcount-skill")).toBeDefined();
|
|
285
|
-
|
|
286
|
-
// Second unregister drops the entry for real.
|
|
287
|
-
unregisterPluginSkills("refcount-plugin");
|
|
288
|
-
expect(getPluginSkillRefCount("refcount-plugin")).toBe(0);
|
|
289
|
-
expect(
|
|
290
|
-
getPluginContributedSkillDefinition("refcount-skill"),
|
|
291
|
-
).toBeUndefined();
|
|
292
|
-
|
|
293
|
-
// Third unregister (over-decrement) is a no-op, not a throw — mirrors
|
|
294
|
-
// the tool-registry behavior so shutdown races don't crash the
|
|
295
|
-
// daemon.
|
|
296
|
-
expect(() => unregisterPluginSkills("refcount-plugin")).not.toThrow();
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
test("managed (filesystem) skill with the same id overrides a plugin-contributed skill", async () => {
|
|
300
|
-
// Plugin skills sit below managed/workspace in the catalog precedence
|
|
301
|
-
// chain so a user can shadow a plugin-provided skill by dropping a
|
|
302
|
-
// SKILL.md with the same id under ~/.vellum/workspace/skills. The test
|
|
303
|
-
// harness configures VELLUM_WORKSPACE_DIR via the bun test preload, so
|
|
304
|
-
// we can synthesize a filesystem skill for the same id and assert it
|
|
305
|
-
// wins.
|
|
306
|
-
const { mkdirSync, writeFileSync, rmSync, existsSync } =
|
|
307
|
-
await import("node:fs");
|
|
308
|
-
|
|
309
|
-
const workspaceDir = process.env.VELLUM_WORKSPACE_DIR;
|
|
310
|
-
// Skip the filesystem override check when the harness did not provide a
|
|
311
|
-
// workspace dir — the other tests in this file still cover catalog
|
|
312
|
-
// visibility, which is the core of PR 33.
|
|
313
|
-
if (!workspaceDir) {
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const skillsDir = join(workspaceDir, "skills", "shared-id");
|
|
318
|
-
mkdirSync(skillsDir, { recursive: true });
|
|
319
|
-
writeFileSync(
|
|
320
|
-
join(skillsDir, "SKILL.md"),
|
|
321
|
-
[
|
|
322
|
-
"---",
|
|
323
|
-
'name: "filesystem-version"',
|
|
324
|
-
'description: "User-authored override"',
|
|
325
|
-
"---",
|
|
326
|
-
"",
|
|
327
|
-
"# Filesystem Override",
|
|
328
|
-
].join("\n"),
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
const pluginSkill: PluginSkillRegistration = {
|
|
332
|
-
id: "shared-id",
|
|
333
|
-
name: "plugin-version",
|
|
334
|
-
description: "Plugin-provided skill that should be shadowed",
|
|
335
|
-
body: "plugin body",
|
|
336
|
-
};
|
|
337
|
-
registerPlugin(buildSkillPlugin("shadow-plugin", [pluginSkill]));
|
|
338
|
-
|
|
339
|
-
try {
|
|
340
|
-
await bootstrapPlugins();
|
|
341
|
-
|
|
342
|
-
const catalog = loadSkillCatalog();
|
|
343
|
-
const entry = catalog.find((s) => s.id === "shared-id");
|
|
344
|
-
// The filesystem SKILL.md wins — source flips to "managed" and the
|
|
345
|
-
// plugin's metadata is not what we see.
|
|
346
|
-
expect(entry).toBeDefined();
|
|
347
|
-
expect(entry?.source).toBe("managed");
|
|
348
|
-
expect(entry?.name).toBe("filesystem-version");
|
|
349
|
-
} finally {
|
|
350
|
-
if (existsSync(skillsDir)) {
|
|
351
|
-
rmSync(skillsDir, { recursive: true, force: true });
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Browser interaction types.
|
|
2
|
-
// CDP request/response messaging was removed — Playwright's connectOverCDP is broken
|
|
3
|
-
// under Bun's runtime. Browser is now launched directly via Playwright.
|
|
4
|
-
|
|
5
|
-
// --- Domain-level union aliases (consumed by the barrel file) ---
|
|
6
|
-
// These must exist (even as `never`) so the barrel union compiles.
|
|
7
|
-
|
|
8
|
-
export type _BrowserClientMessages = never;
|
|
9
|
-
|
|
10
|
-
export type _BrowserServerMessages = never;
|