@vellumai/assistant 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +325 -3
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +1 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +2 -0
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +266 -10
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +737 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop.ts +169 -9
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +16 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +110 -10
- package/src/memory/db-init.ts +6 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +36 -3
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +105 -32
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +31 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +121 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +57 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +15 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +72 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
mkdtempSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { gzipSync } from "node:zlib";
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
13
|
+
|
|
14
|
+
const mockExecSync = mock(() => {});
|
|
15
|
+
|
|
16
|
+
mock.module("node:child_process", () => ({
|
|
17
|
+
execSync: mockExecSync,
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
import { loadSkillCatalog } from "../config/skills.js";
|
|
21
|
+
import { installSkillLocally } from "../skills/catalog-install.js";
|
|
22
|
+
import { installExternalSkill } from "../skills/skillssh-registry.js";
|
|
23
|
+
import { makeTar } from "./helpers/tar-fixtures.js";
|
|
24
|
+
|
|
25
|
+
const originalWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
|
|
26
|
+
const originalFetch = globalThis.fetch;
|
|
27
|
+
|
|
28
|
+
let workspaceDir: string;
|
|
29
|
+
|
|
30
|
+
function skillMarkdown(name: string, body: string): string {
|
|
31
|
+
return `---
|
|
32
|
+
name: "${name}"
|
|
33
|
+
description: "A test skill."
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
${body}
|
|
37
|
+
`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function writeInstalledSkill(skillId: string, name: string): void {
|
|
41
|
+
const skillDir = join(workspaceDir, "skills", skillId);
|
|
42
|
+
mkdirSync(skillDir, { recursive: true });
|
|
43
|
+
writeFileSync(join(skillDir, "SKILL.md"), skillMarkdown(name, "Old body."));
|
|
44
|
+
writeFileSync(join(skillDir, "old.txt"), "keep me\n");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
workspaceDir = mkdtempSync(join(tmpdir(), "skills-install-staging-"));
|
|
49
|
+
process.env.VELLUM_WORKSPACE_DIR = workspaceDir;
|
|
50
|
+
mkdirSync(join(workspaceDir, "skills"), { recursive: true });
|
|
51
|
+
mockExecSync.mockReset();
|
|
52
|
+
mockExecSync.mockImplementation(() => {
|
|
53
|
+
throw new Error("dependency install failed");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
globalThis.fetch = originalFetch;
|
|
59
|
+
if (originalWorkspaceDir === undefined) {
|
|
60
|
+
delete process.env.VELLUM_WORKSPACE_DIR;
|
|
61
|
+
} else {
|
|
62
|
+
process.env.VELLUM_WORKSPACE_DIR = originalWorkspaceDir;
|
|
63
|
+
}
|
|
64
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("staged skill installs", () => {
|
|
68
|
+
test("catalog dependency failure does not leave a discoverable fresh install", async () => {
|
|
69
|
+
const archive = gzipSync(
|
|
70
|
+
makeTar([
|
|
71
|
+
{
|
|
72
|
+
name: "SKILL.md",
|
|
73
|
+
content: skillMarkdown("Demo Skill", "New body."),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "package.json",
|
|
77
|
+
content: JSON.stringify({ dependencies: { example: "1.0.0" } }),
|
|
78
|
+
},
|
|
79
|
+
]),
|
|
80
|
+
);
|
|
81
|
+
globalThis.fetch = mock(
|
|
82
|
+
async () => new Response(archive),
|
|
83
|
+
) as unknown as typeof fetch;
|
|
84
|
+
|
|
85
|
+
await expect(
|
|
86
|
+
installSkillLocally(
|
|
87
|
+
"demo-skill",
|
|
88
|
+
{
|
|
89
|
+
id: "demo-skill",
|
|
90
|
+
name: "Demo Skill",
|
|
91
|
+
description: "A test skill.",
|
|
92
|
+
},
|
|
93
|
+
false,
|
|
94
|
+
),
|
|
95
|
+
).rejects.toThrow("dependency install failed");
|
|
96
|
+
|
|
97
|
+
expect(
|
|
98
|
+
existsSync(join(workspaceDir, "skills", "demo-skill", "SKILL.md")),
|
|
99
|
+
).toBe(false);
|
|
100
|
+
expect(loadSkillCatalog().some((skill) => skill.id === "demo-skill")).toBe(
|
|
101
|
+
false,
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("skills.sh overwrite dependency failure preserves the previous skill", async () => {
|
|
106
|
+
writeInstalledSkill("demo-skill", "Old Demo Skill");
|
|
107
|
+
|
|
108
|
+
globalThis.fetch = mock(async (input: string | URL | Request) => {
|
|
109
|
+
const url = String(input);
|
|
110
|
+
if (url.includes("/contents/skills/demo-skill")) {
|
|
111
|
+
return new Response(
|
|
112
|
+
JSON.stringify([
|
|
113
|
+
{
|
|
114
|
+
name: "SKILL.md",
|
|
115
|
+
type: "file",
|
|
116
|
+
download_url: "https://example.com/SKILL.md",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "package.json",
|
|
120
|
+
type: "file",
|
|
121
|
+
download_url: "https://example.com/package.json",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "new.txt",
|
|
125
|
+
type: "file",
|
|
126
|
+
download_url: "https://example.com/new.txt",
|
|
127
|
+
},
|
|
128
|
+
]),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (url.endsWith("/SKILL.md")) {
|
|
132
|
+
return new Response(skillMarkdown("New Demo Skill", "New body."));
|
|
133
|
+
}
|
|
134
|
+
if (url.endsWith("/package.json")) {
|
|
135
|
+
return new Response(
|
|
136
|
+
JSON.stringify({ dependencies: { example: "1.0.0" } }),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (url.endsWith("/new.txt")) {
|
|
140
|
+
return new Response("new file\n");
|
|
141
|
+
}
|
|
142
|
+
return new Response("not found", { status: 404 });
|
|
143
|
+
}) as unknown as typeof fetch;
|
|
144
|
+
|
|
145
|
+
await expect(
|
|
146
|
+
installExternalSkill("owner", "repo", "demo-skill", true),
|
|
147
|
+
).rejects.toThrow("dependency install failed");
|
|
148
|
+
|
|
149
|
+
const skillDir = join(workspaceDir, "skills", "demo-skill");
|
|
150
|
+
expect(readFileSync(join(skillDir, "SKILL.md"), "utf-8")).toContain(
|
|
151
|
+
"Old Demo Skill",
|
|
152
|
+
);
|
|
153
|
+
expect(readFileSync(join(skillDir, "old.txt"), "utf-8")).toBe("keep me\n");
|
|
154
|
+
expect(existsSync(join(skillDir, "new.txt"))).toBe(false);
|
|
155
|
+
expect(
|
|
156
|
+
loadSkillCatalog().find((skill) => skill.id === "demo-skill")?.name,
|
|
157
|
+
).toBe("Old Demo Skill");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -53,81 +53,49 @@ afterEach(() => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
describe("assistant skills uninstall", () => {
|
|
56
|
-
test("removes skill directory
|
|
57
|
-
/**
|
|
58
|
-
* Tests the happy path for uninstalling a skill.
|
|
59
|
-
*/
|
|
60
|
-
|
|
61
|
-
// GIVEN a skill is installed locally
|
|
56
|
+
test("removes skill directory while leaving a stale SKILLS.md index unchanged", () => {
|
|
62
57
|
installFakeSkill("weather");
|
|
63
|
-
|
|
58
|
+
const originalIndex = "- weather\n- vellum-self-knowledge\n";
|
|
59
|
+
writeSkillsIndex(originalIndex);
|
|
64
60
|
|
|
65
|
-
// WHEN we uninstall the skill
|
|
66
61
|
uninstallSkillLocally("weather");
|
|
67
62
|
|
|
68
|
-
// THEN the skill directory should be removed
|
|
69
63
|
expect(existsSync(join(getSkillsDir(), "weather"))).toBe(false);
|
|
70
64
|
|
|
71
|
-
// AND the SKILLS.md entry should be removed
|
|
72
65
|
const index = readFileSync(getSkillsIndexPath(), "utf-8");
|
|
73
|
-
expect(index).
|
|
74
|
-
|
|
75
|
-
// AND other skills should remain in the index
|
|
76
|
-
expect(index).toContain("vellum-self-knowledge");
|
|
66
|
+
expect(index).toBe(originalIndex);
|
|
77
67
|
});
|
|
78
68
|
|
|
79
69
|
test("errors when skill is not installed", () => {
|
|
80
|
-
/**
|
|
81
|
-
* Tests that uninstalling a non-existent skill throws an error.
|
|
82
|
-
*/
|
|
83
|
-
|
|
84
|
-
// GIVEN no skills are installed
|
|
85
|
-
// WHEN we try to uninstall a nonexistent skill
|
|
86
|
-
// THEN it should throw an error
|
|
87
70
|
expect(() => uninstallSkillLocally("nonexistent")).toThrow(
|
|
88
71
|
'Skill "nonexistent" is not installed.',
|
|
89
72
|
);
|
|
90
73
|
});
|
|
91
74
|
|
|
92
|
-
test("works when SKILLS.md
|
|
93
|
-
/**
|
|
94
|
-
* Tests that uninstall works even if the SKILLS.md index file is missing.
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
// GIVEN a skill directory exists but no SKILLS.md
|
|
75
|
+
test("works when no stale SKILLS.md index exists", () => {
|
|
98
76
|
installFakeSkill("weather");
|
|
99
77
|
|
|
100
|
-
// WHEN we uninstall the skill
|
|
101
78
|
uninstallSkillLocally("weather");
|
|
102
79
|
|
|
103
|
-
// THEN the skill directory should be removed
|
|
104
80
|
expect(existsSync(join(getSkillsDir(), "weather"))).toBe(false);
|
|
105
81
|
|
|
106
|
-
// AND no SKILLS.md should have been created
|
|
107
82
|
expect(existsSync(getSkillsIndexPath())).toBe(false);
|
|
108
83
|
});
|
|
109
84
|
|
|
110
|
-
test("removes skill with nested files", () => {
|
|
111
|
-
/**
|
|
112
|
-
* Tests that uninstall recursively removes skills with nested directories.
|
|
113
|
-
*/
|
|
114
|
-
|
|
115
|
-
// GIVEN a skill with nested files is installed
|
|
85
|
+
test("removes skill with nested files while leaving a stale SKILLS.md index unchanged", () => {
|
|
116
86
|
const skillDir = join(getSkillsDir(), "weather");
|
|
117
87
|
mkdirSync(join(skillDir, "scripts", "lib"), { recursive: true });
|
|
118
88
|
writeFileSync(join(skillDir, "SKILL.md"), "# weather\n");
|
|
119
89
|
writeFileSync(join(skillDir, "scripts", "fetch.sh"), "#!/bin/bash\n");
|
|
120
90
|
writeFileSync(join(skillDir, "scripts", "lib", "utils.sh"), "# utils\n");
|
|
121
|
-
|
|
91
|
+
const originalIndex = "- weather\n";
|
|
92
|
+
writeSkillsIndex(originalIndex);
|
|
122
93
|
|
|
123
|
-
// WHEN we uninstall the skill
|
|
124
94
|
uninstallSkillLocally("weather");
|
|
125
95
|
|
|
126
|
-
// THEN the entire skill directory tree should be removed
|
|
127
96
|
expect(existsSync(skillDir)).toBe(false);
|
|
128
97
|
|
|
129
|
-
// AND the SKILLS.md entry should be removed
|
|
130
98
|
const index = readFileSync(getSkillsIndexPath(), "utf-8");
|
|
131
|
-
expect(index).
|
|
99
|
+
expect(index).toBe(originalIndex);
|
|
132
100
|
});
|
|
133
101
|
});
|
|
@@ -67,42 +67,62 @@ describe("skills catalog loading", () => {
|
|
|
67
67
|
rmSync(skillsDir, { recursive: true, force: true });
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
test("
|
|
71
|
-
writeSkill("
|
|
72
|
-
writeSkill("
|
|
73
|
-
writeFileSync(
|
|
74
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
75
|
-
"- alpha\n- beta/SKILL.md\n",
|
|
76
|
-
);
|
|
70
|
+
test("discovers valid skill directories alphabetically", () => {
|
|
71
|
+
writeSkill("zeta", "Zeta Skill", "Zeta");
|
|
72
|
+
writeSkill("alpha", "Alpha Skill", "Alpha");
|
|
77
73
|
|
|
78
74
|
const catalog = loadUserSkillCatalog();
|
|
79
|
-
expect(catalog.map((skill) => skill.id)).toEqual(["alpha", "
|
|
75
|
+
expect(catalog.map((skill) => skill.id)).toEqual(["alpha", "zeta"]);
|
|
80
76
|
});
|
|
81
77
|
|
|
82
|
-
test("
|
|
83
|
-
writeSkill("
|
|
84
|
-
writeSkill("
|
|
78
|
+
test("ignores stale SKILLS.md while discovering valid skill directories", () => {
|
|
79
|
+
writeSkill("first", "First Skill", "First");
|
|
80
|
+
writeSkill("second", "Second Skill", "Second");
|
|
85
81
|
writeFileSync(
|
|
86
82
|
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
87
|
-
"-
|
|
83
|
+
"- second\n- missing\n",
|
|
88
84
|
);
|
|
89
85
|
|
|
90
86
|
const catalog = loadUserSkillCatalog();
|
|
91
|
-
expect(catalog.map((skill) => skill.id)).toEqual(["
|
|
87
|
+
expect(catalog.map((skill) => skill.id)).toEqual(["first", "second"]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("managed skill overrides bundled skill with the same id", () => {
|
|
91
|
+
writeSkill(
|
|
92
|
+
"skill-management",
|
|
93
|
+
"Custom Skill Management",
|
|
94
|
+
"Managed override",
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const skill = loadSkillCatalog().find((s) => s.id === "skill-management");
|
|
98
|
+
expect(skill).toBeDefined();
|
|
99
|
+
expect(skill!.source).toBe("managed");
|
|
100
|
+
expect(skill!.name).toBe("Custom Skill Management");
|
|
101
|
+
expect(skill!.bundled).toBeUndefined();
|
|
92
102
|
});
|
|
93
103
|
|
|
94
|
-
test("
|
|
95
|
-
|
|
104
|
+
test("discovers symlinked skill directories that point inside ~/.vellum/workspace/skills", () => {
|
|
105
|
+
const internalSkillDir = join(
|
|
106
|
+
TEST_DIR,
|
|
107
|
+
"skills",
|
|
108
|
+
".linked-targets",
|
|
109
|
+
"internal-skill",
|
|
110
|
+
);
|
|
111
|
+
mkdirSync(internalSkillDir, { recursive: true });
|
|
96
112
|
writeFileSync(
|
|
97
|
-
join(
|
|
98
|
-
"
|
|
113
|
+
join(internalSkillDir, "SKILL.md"),
|
|
114
|
+
'---\nname: "Internal Linked Skill"\ndescription: "Inside skills root."\n---\n\nLoad me.\n',
|
|
99
115
|
);
|
|
100
116
|
|
|
117
|
+
symlinkSync(internalSkillDir, join(TEST_DIR, "skills", "linked-skill"));
|
|
118
|
+
|
|
101
119
|
const catalog = loadUserSkillCatalog();
|
|
102
|
-
expect(catalog
|
|
120
|
+
expect(catalog).toHaveLength(1);
|
|
121
|
+
expect(catalog[0].id).toBe("linked-skill");
|
|
122
|
+
expect(catalog[0].name).toBe("Internal Linked Skill");
|
|
103
123
|
});
|
|
104
124
|
|
|
105
|
-
test("
|
|
125
|
+
test("does not discover symlinked skill directories that point outside ~/.vellum/workspace/skills", () => {
|
|
106
126
|
const externalSkillDir = join(TEST_DIR, "outside", "external-skill");
|
|
107
127
|
mkdirSync(externalSkillDir, { recursive: true });
|
|
108
128
|
writeFileSync(
|
|
@@ -111,7 +131,6 @@ describe("skills catalog loading", () => {
|
|
|
111
131
|
);
|
|
112
132
|
|
|
113
133
|
symlinkSync(externalSkillDir, join(TEST_DIR, "skills", "linked-skill"));
|
|
114
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- linked-skill\n");
|
|
115
134
|
|
|
116
135
|
const catalog = loadUserSkillCatalog();
|
|
117
136
|
expect(catalog).toHaveLength(0);
|
|
@@ -130,35 +149,6 @@ describe("skills catalog loading", () => {
|
|
|
130
149
|
);
|
|
131
150
|
|
|
132
151
|
symlinkSync(externalSkillFile, join(linkedSkillDir, "SKILL.md"));
|
|
133
|
-
writeFileSync(
|
|
134
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
135
|
-
"- linked-file-skill\n",
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const catalog = loadUserSkillCatalog();
|
|
139
|
-
expect(catalog).toHaveLength(0);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test("uses SKILLS.md ordering when index exists", () => {
|
|
143
|
-
writeSkill("first", "First Skill", "First");
|
|
144
|
-
writeSkill("second", "Second Skill", "Second");
|
|
145
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- second\n- first\n");
|
|
146
|
-
|
|
147
|
-
const catalog = loadUserSkillCatalog();
|
|
148
|
-
expect(catalog.map((skill) => skill.id)).toEqual(["second", "first"]);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test("falls back to auto-discovery when SKILLS.md is missing", () => {
|
|
152
|
-
writeSkill("zeta", "Zeta Skill", "Zeta");
|
|
153
|
-
writeSkill("alpha", "Alpha Skill", "Alpha");
|
|
154
|
-
|
|
155
|
-
const catalog = loadUserSkillCatalog();
|
|
156
|
-
expect(catalog.map((skill) => skill.id)).toEqual(["alpha", "zeta"]);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("treats SKILLS.md as authoritative when present", () => {
|
|
160
|
-
writeSkill("available", "Available Skill", "Present on disk");
|
|
161
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- ../invalid-only\n");
|
|
162
152
|
|
|
163
153
|
const catalog = loadUserSkillCatalog();
|
|
164
154
|
expect(catalog).toHaveLength(0);
|
|
@@ -248,6 +238,18 @@ describe("workspace skills", () => {
|
|
|
248
238
|
expect(result.skill!.body).toBe("Full workspace body here");
|
|
249
239
|
expect(result.skill!.source).toBe("workspace");
|
|
250
240
|
});
|
|
241
|
+
|
|
242
|
+
test("workspace skill overrides managed skill with the same id", () => {
|
|
243
|
+
writeSkill("shared-id", "Managed Shared", "Managed version");
|
|
244
|
+
writeWorkspaceSkill("shared-id", "Workspace Shared", "Workspace version");
|
|
245
|
+
|
|
246
|
+
const skill = loadSkillCatalog(workspaceSkillsDir).find(
|
|
247
|
+
(s) => s.id === "shared-id",
|
|
248
|
+
);
|
|
249
|
+
expect(skill).toBeDefined();
|
|
250
|
+
expect(skill!.source).toBe("workspace");
|
|
251
|
+
expect(skill!.name).toBe("Workspace Shared");
|
|
252
|
+
});
|
|
251
253
|
});
|
|
252
254
|
|
|
253
255
|
describe("tool manifest detection", () => {
|
|
@@ -449,7 +451,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
449
451
|
|
|
450
452
|
test("parses valid includes array", () => {
|
|
451
453
|
writeSkillWithIncludes("parent", '["child-a", "child-b"]');
|
|
452
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
453
454
|
const catalog = loadUserSkillCatalog();
|
|
454
455
|
const skill = catalog.find((s) => s.id === "parent");
|
|
455
456
|
expect(skill).toBeDefined();
|
|
@@ -458,7 +459,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
458
459
|
|
|
459
460
|
test("trims whitespace in includes entries", () => {
|
|
460
461
|
writeSkillWithIncludes("parent", '[" child-a ", " child-b "]');
|
|
461
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
462
462
|
const catalog = loadUserSkillCatalog();
|
|
463
463
|
const skill = catalog.find((s) => s.id === "parent");
|
|
464
464
|
expect(skill!.includes).toEqual(["child-a", "child-b"]);
|
|
@@ -466,7 +466,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
466
466
|
|
|
467
467
|
test("removes empty strings from includes", () => {
|
|
468
468
|
writeSkillWithIncludes("parent", '["child-a", "", " ", "child-b"]');
|
|
469
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
470
469
|
const catalog = loadUserSkillCatalog();
|
|
471
470
|
const skill = catalog.find((s) => s.id === "parent");
|
|
472
471
|
expect(skill!.includes).toEqual(["child-a", "child-b"]);
|
|
@@ -474,7 +473,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
474
473
|
|
|
475
474
|
test("deduplicates includes preserving first-seen order", () => {
|
|
476
475
|
writeSkillWithIncludes("parent", '["child-a", "child-b", "child-a"]');
|
|
477
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
478
476
|
const catalog = loadUserSkillCatalog();
|
|
479
477
|
const skill = catalog.find((s) => s.id === "parent");
|
|
480
478
|
expect(skill!.includes).toEqual(["child-a", "child-b"]);
|
|
@@ -482,7 +480,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
482
480
|
|
|
483
481
|
test("returns undefined for invalid JSON", () => {
|
|
484
482
|
writeSkillWithIncludes("parent", "not-json");
|
|
485
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
486
483
|
const catalog = loadUserSkillCatalog();
|
|
487
484
|
const skill = catalog.find((s) => s.id === "parent");
|
|
488
485
|
expect(skill!.includes).toBeUndefined();
|
|
@@ -490,7 +487,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
490
487
|
|
|
491
488
|
test("returns undefined for non-array JSON", () => {
|
|
492
489
|
writeSkillWithIncludes("parent", '"just-a-string"');
|
|
493
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
494
490
|
const catalog = loadUserSkillCatalog();
|
|
495
491
|
const skill = catalog.find((s) => s.id === "parent");
|
|
496
492
|
expect(skill!.includes).toBeUndefined();
|
|
@@ -498,7 +494,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
498
494
|
|
|
499
495
|
test("returns undefined for array with non-string elements", () => {
|
|
500
496
|
writeSkillWithIncludes("parent", "[123, true]");
|
|
501
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
502
497
|
const catalog = loadUserSkillCatalog();
|
|
503
498
|
const skill = catalog.find((s) => s.id === "parent");
|
|
504
499
|
expect(skill!.includes).toBeUndefined();
|
|
@@ -506,7 +501,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
506
501
|
|
|
507
502
|
test("returns undefined for empty array", () => {
|
|
508
503
|
writeSkillWithIncludes("parent", "[]");
|
|
509
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
510
504
|
const catalog = loadUserSkillCatalog();
|
|
511
505
|
const skill = catalog.find((s) => s.id === "parent");
|
|
512
506
|
expect(skill!.includes).toBeUndefined();
|
|
@@ -514,7 +508,6 @@ describe("includes frontmatter parsing", () => {
|
|
|
514
508
|
|
|
515
509
|
test("skill without includes has undefined includes", () => {
|
|
516
510
|
writeSkill("no-includes", "No Includes", "Test");
|
|
517
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- no-includes\n");
|
|
518
511
|
const catalog = loadUserSkillCatalog();
|
|
519
512
|
const skill = catalog.find((s) => s.id === "no-includes");
|
|
520
513
|
expect(skill!.includes).toBeUndefined();
|
|
@@ -48,6 +48,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
48
48
|
teamName:
|
|
49
49
|
((configStore.slack as Record<string, unknown>)?.teamName as string) ??
|
|
50
50
|
"",
|
|
51
|
+
teamUrl:
|
|
52
|
+
((configStore.slack as Record<string, unknown>)?.teamUrl as string) ??
|
|
53
|
+
"",
|
|
51
54
|
botUserId:
|
|
52
55
|
((configStore.slack as Record<string, unknown>)?.botUserId as string) ??
|
|
53
56
|
"",
|
|
@@ -277,6 +280,7 @@ describe("Slack channel config handler", () => {
|
|
|
277
280
|
slack: {
|
|
278
281
|
teamId: "T123",
|
|
279
282
|
teamName: "TestTeam",
|
|
283
|
+
teamUrl: "https://example.slack.com/",
|
|
280
284
|
botUserId: "U_BOT",
|
|
281
285
|
botUsername: "testbot",
|
|
282
286
|
},
|
|
@@ -285,6 +289,7 @@ describe("Slack channel config handler", () => {
|
|
|
285
289
|
const result = await getSlackChannelConfig();
|
|
286
290
|
expect(result.teamId).toBe("T123");
|
|
287
291
|
expect(result.teamName).toBe("TestTeam");
|
|
292
|
+
expect(result.teamUrl).toBe("https://example.slack.com/");
|
|
288
293
|
expect(result.botUserId).toBe("U_BOT");
|
|
289
294
|
expect(result.botUsername).toBe("testbot");
|
|
290
295
|
});
|
|
@@ -314,6 +319,7 @@ describe("Slack channel config handler", () => {
|
|
|
314
319
|
ok: true,
|
|
315
320
|
team_id: "T_TEAM",
|
|
316
321
|
team: "MyTeam",
|
|
322
|
+
url: "https://myteam.slack.com/",
|
|
317
323
|
user_id: "U_BOT",
|
|
318
324
|
user: "mybot",
|
|
319
325
|
}),
|
|
@@ -329,11 +335,13 @@ describe("Slack channel config handler", () => {
|
|
|
329
335
|
expect(result.hasBotToken).toBe(true);
|
|
330
336
|
expect(result.teamId).toBe("T_TEAM");
|
|
331
337
|
expect(result.teamName).toBe("MyTeam");
|
|
338
|
+
expect(result.teamUrl).toBe("https://myteam.slack.com/");
|
|
332
339
|
|
|
333
340
|
// Assert metadata was written to config (not credential metadata)
|
|
334
341
|
const slack = configStore.slack as Record<string, unknown>;
|
|
335
342
|
expect(slack.teamId).toBe("T_TEAM");
|
|
336
343
|
expect(slack.teamName).toBe("MyTeam");
|
|
344
|
+
expect(slack.teamUrl).toBe("https://myteam.slack.com/");
|
|
337
345
|
expect(slack.botUserId).toBe("U_BOT");
|
|
338
346
|
expect(slack.botUsername).toBe("mybot");
|
|
339
347
|
});
|
|
@@ -397,6 +405,7 @@ describe("Slack channel config handler", () => {
|
|
|
397
405
|
const slack = configStore.slack as Record<string, unknown>;
|
|
398
406
|
expect(slack.teamId).toBe("");
|
|
399
407
|
expect(slack.teamName).toBe("");
|
|
408
|
+
expect(slack.teamUrl).toBe("");
|
|
400
409
|
expect(slack.botUserId).toBe("");
|
|
401
410
|
expect(slack.botUsername).toBe("");
|
|
402
411
|
});
|
|
@@ -68,4 +68,54 @@ describe("subagent-only tool filtering", () => {
|
|
|
68
68
|
// A regular tool not in SUBAGENT_ONLY_TOOL_NAMES should still be active
|
|
69
69
|
expect(isToolActiveForContext("bash", ctx)).toBe(true);
|
|
70
70
|
});
|
|
71
|
+
|
|
72
|
+
test("respects subagentAllowedTools — tools outside the allowlist are inactive", () => {
|
|
73
|
+
// Mirrors `createResolveToolsCallback`'s post-filter so callers see the
|
|
74
|
+
// same final tool set the LLM receives.
|
|
75
|
+
const ctx: SkillProjectionContext = {
|
|
76
|
+
skillProjectionState: new Map(),
|
|
77
|
+
skillProjectionCache: {},
|
|
78
|
+
coreToolNames: new Set(),
|
|
79
|
+
toolsDisabledDepth: 0,
|
|
80
|
+
hasNoClient: false,
|
|
81
|
+
isSubagent: true,
|
|
82
|
+
subagentAllowedTools: new Set(["bash"]),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
expect(isToolActiveForContext("bash", ctx)).toBe(true);
|
|
86
|
+
expect(isToolActiveForContext("ask_question", ctx)).toBe(false);
|
|
87
|
+
expect(isToolActiveForContext(TEST_TOOL_NAME, ctx)).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("returns false for every tool when toolsDisabledDepth > 0", () => {
|
|
91
|
+
// `createResolveToolsCallback` returns an empty tool list when tools are
|
|
92
|
+
// disabled; mirror it here so this helper reports the same final tool set.
|
|
93
|
+
const ctx: SkillProjectionContext = {
|
|
94
|
+
skillProjectionState: new Map(),
|
|
95
|
+
skillProjectionCache: {},
|
|
96
|
+
coreToolNames: new Set(),
|
|
97
|
+
toolsDisabledDepth: 1,
|
|
98
|
+
hasNoClient: false,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
expect(isToolActiveForContext("bash", ctx)).toBe(false);
|
|
102
|
+
expect(isToolActiveForContext("ask_question", ctx)).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("under disk-pressure cleanup mode, only cleanup-safe tools are active", () => {
|
|
106
|
+
// `createResolveToolsCallback` restricts the turn to cleanup-safe tools
|
|
107
|
+
// (`file_remove`, `bash`, etc.); ensure the helper agrees.
|
|
108
|
+
const ctx: SkillProjectionContext = {
|
|
109
|
+
skillProjectionState: new Map(),
|
|
110
|
+
skillProjectionCache: {},
|
|
111
|
+
coreToolNames: new Set(),
|
|
112
|
+
toolsDisabledDepth: 0,
|
|
113
|
+
hasNoClient: false,
|
|
114
|
+
diskPressureCleanupModeActive: true,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// `bash` is in DISK_PRESSURE_CLEANUP_TOOL_NAMES; `ask_question` is not.
|
|
118
|
+
expect(isToolActiveForContext("bash", ctx)).toBe(true);
|
|
119
|
+
expect(isToolActiveForContext("ask_question", ctx)).toBe(false);
|
|
120
|
+
});
|
|
71
121
|
});
|