@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
|
@@ -1223,7 +1223,14 @@ describe("getUsageGroupedSeries", () => {
|
|
|
1223
1223
|
describe("queryUnreportedUsageEvents", () => {
|
|
1224
1224
|
beforeEach(() => {
|
|
1225
1225
|
const db = getDb();
|
|
1226
|
+
// Order matters: clear `llm_usage_events` (no FK), then `messages`
|
|
1227
|
+
// (FK to conversations cascades, but be explicit), then
|
|
1228
|
+
// `conversations`. The conversation-level metadata tests below
|
|
1229
|
+
// depend on a clean conversations + messages slate so JOINs are
|
|
1230
|
+
// deterministic.
|
|
1226
1231
|
db.run(`DELETE FROM llm_usage_events`);
|
|
1232
|
+
db.run(`DELETE FROM messages`);
|
|
1233
|
+
db.run(`DELETE FROM conversations`);
|
|
1227
1234
|
});
|
|
1228
1235
|
|
|
1229
1236
|
test("returns events with createdAt strictly greater than afterCreatedAt in ascending order", () => {
|
|
@@ -1277,4 +1284,111 @@ describe("queryUnreportedUsageEvents", () => {
|
|
|
1277
1284
|
const events = queryUnreportedUsageEvents(0, undefined, 100);
|
|
1278
1285
|
expect(events).toHaveLength(0);
|
|
1279
1286
|
});
|
|
1287
|
+
|
|
1288
|
+
// -------------------------------------------------------------------------
|
|
1289
|
+
// Conversation-level metadata (conversationType + turnIndex). These are
|
|
1290
|
+
// JOIN-computed at telemetry-query time so the reporter can emit them on
|
|
1291
|
+
// the wire without persisting extra columns on `llm_usage_events`.
|
|
1292
|
+
// -------------------------------------------------------------------------
|
|
1293
|
+
|
|
1294
|
+
test("conversationType is JOINed from the conversations table", () => {
|
|
1295
|
+
const db = getDb();
|
|
1296
|
+
const now = Date.now();
|
|
1297
|
+
db.run(
|
|
1298
|
+
`INSERT INTO conversations (id, conversation_type, created_at, updated_at) VALUES ('conv-std', 'standard', ${now}, ${now})`,
|
|
1299
|
+
);
|
|
1300
|
+
db.run(
|
|
1301
|
+
`INSERT INTO conversations (id, conversation_type, created_at, updated_at) VALUES ('conv-bg', 'background', ${now}, ${now})`,
|
|
1302
|
+
);
|
|
1303
|
+
|
|
1304
|
+
insertEventAt(1000, { conversationId: "conv-std" });
|
|
1305
|
+
insertEventAt(2000, { conversationId: "conv-bg" });
|
|
1306
|
+
insertEventAt(3000, { conversationId: null });
|
|
1307
|
+
|
|
1308
|
+
const events = queryUnreportedUsageEvents(0, undefined, 100);
|
|
1309
|
+
expect(events).toHaveLength(3);
|
|
1310
|
+
expect(events[0].conversationType).toBe("standard");
|
|
1311
|
+
expect(events[1].conversationType).toBe("background");
|
|
1312
|
+
// LLM calls without a parent conversation get null — LEFT JOIN, not INNER.
|
|
1313
|
+
expect(events[2].conversationType).toBeNull();
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
test("turnIndex counts real user turns up to the LLM call's createdAt", () => {
|
|
1317
|
+
const db = getDb();
|
|
1318
|
+
const now = Date.now();
|
|
1319
|
+
db.run(
|
|
1320
|
+
`INSERT INTO conversations (id, conversation_type, created_at, updated_at) VALUES ('conv-1', 'standard', ${now}, ${now})`,
|
|
1321
|
+
);
|
|
1322
|
+
// Two user messages in the conversation.
|
|
1323
|
+
db.run(
|
|
1324
|
+
`INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES ('m1', 'conv-1', 'user', 'hello', 1000)`,
|
|
1325
|
+
);
|
|
1326
|
+
db.run(
|
|
1327
|
+
`INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES ('m2', 'conv-1', 'user', 'follow up', 3000)`,
|
|
1328
|
+
);
|
|
1329
|
+
|
|
1330
|
+
// Three LLM calls across the timeline:
|
|
1331
|
+
// - mid-turn-1 (between m1 and m2)
|
|
1332
|
+
// - exactly at m2's createdAt (still counts m2: `created_at <= e.created_at`)
|
|
1333
|
+
// - after m2
|
|
1334
|
+
insertEventAt(2000, { conversationId: "conv-1" });
|
|
1335
|
+
insertEventAt(3000, { conversationId: "conv-1" });
|
|
1336
|
+
insertEventAt(4000, { conversationId: "conv-1" });
|
|
1337
|
+
|
|
1338
|
+
const events = queryUnreportedUsageEvents(0, undefined, 100);
|
|
1339
|
+
expect(events).toHaveLength(3);
|
|
1340
|
+
expect(events[0].turnIndex).toBe(1);
|
|
1341
|
+
expect(events[1].turnIndex).toBe(2);
|
|
1342
|
+
expect(events[2].turnIndex).toBe(2);
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
test("turnIndex is null when the LLM call has no conversationId", () => {
|
|
1346
|
+
insertEventAt(1000, { conversationId: null });
|
|
1347
|
+
|
|
1348
|
+
const events = queryUnreportedUsageEvents(0, undefined, 100);
|
|
1349
|
+
expect(events).toHaveLength(1);
|
|
1350
|
+
expect(events[0].conversationId).toBeNull();
|
|
1351
|
+
expect(events[0].turnIndex).toBeNull();
|
|
1352
|
+
});
|
|
1353
|
+
|
|
1354
|
+
test("turnIndex skips tool_result rows when counting", () => {
|
|
1355
|
+
const db = getDb();
|
|
1356
|
+
const now = Date.now();
|
|
1357
|
+
db.run(
|
|
1358
|
+
`INSERT INTO conversations (id, conversation_type, created_at, updated_at) VALUES ('conv-tr', 'standard', ${now}, ${now})`,
|
|
1359
|
+
);
|
|
1360
|
+
// One real user turn, then a tool_result row (which should be
|
|
1361
|
+
// ignored), then the LLM call. Expected turn_index = 1.
|
|
1362
|
+
db.run(
|
|
1363
|
+
`INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES ('real-1', 'conv-tr', 'user', 'real text', 1000)`,
|
|
1364
|
+
);
|
|
1365
|
+
db.run(
|
|
1366
|
+
`INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES ('tool-1', 'conv-tr', 'user', '[{"type":"tool_result","tool_use_id":"x","content":""}]', 1500)`,
|
|
1367
|
+
);
|
|
1368
|
+
insertEventAt(2000, { conversationId: "conv-tr" });
|
|
1369
|
+
|
|
1370
|
+
const events = queryUnreportedUsageEvents(0, undefined, 100);
|
|
1371
|
+
expect(events).toHaveLength(1);
|
|
1372
|
+
expect(events[0].turnIndex).toBe(1);
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
test("turnIndex is 0 when the LLM call fires before any user message", () => {
|
|
1376
|
+
const db = getDb();
|
|
1377
|
+
const now = Date.now();
|
|
1378
|
+
db.run(
|
|
1379
|
+
`INSERT INTO conversations (id, conversation_type, created_at, updated_at) VALUES ('conv-early', 'standard', ${now}, ${now})`,
|
|
1380
|
+
);
|
|
1381
|
+
// LLM call fires at t=1000; first user message is at t=2000.
|
|
1382
|
+
insertEventAt(1000, { conversationId: "conv-early" });
|
|
1383
|
+
db.run(
|
|
1384
|
+
`INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES ('later', 'conv-early', 'user', 'hi', 2000)`,
|
|
1385
|
+
);
|
|
1386
|
+
|
|
1387
|
+
const events = queryUnreportedUsageEvents(0, undefined, 100);
|
|
1388
|
+
expect(events).toHaveLength(1);
|
|
1389
|
+
// Conversation exists but no user turn has fired yet. The CASE
|
|
1390
|
+
// short-circuits only on null conversationId, so we get a real 0.
|
|
1391
|
+
// Analytics can treat 0 as "pre-first-turn" if needed.
|
|
1392
|
+
expect(events[0].turnIndex).toBe(0);
|
|
1393
|
+
});
|
|
1280
1394
|
});
|
|
@@ -67,6 +67,8 @@ mock.module("../config/loader.js", () => ({
|
|
|
67
67
|
},
|
|
68
68
|
getConfig: () => rawConfig,
|
|
69
69
|
invalidateConfigCache: () => {},
|
|
70
|
+
withSuppressedConfigDiskWrites: async (fn: () => unknown) => fn(),
|
|
71
|
+
withSuppressedConfigDiskWritesSync: (fn: () => unknown) => fn(),
|
|
70
72
|
}));
|
|
71
73
|
|
|
72
74
|
mock.module("../providers/registry.js", () => ({
|
|
@@ -96,39 +98,39 @@ beforeEach(() => {
|
|
|
96
98
|
// ---------------------------------------------------------------------------
|
|
97
99
|
|
|
98
100
|
describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
99
|
-
test("rejects edits to quality-optimized that touch non-label/status fields", () => {
|
|
100
|
-
expect(
|
|
101
|
+
test("rejects edits to quality-optimized that touch non-label/status fields", async () => {
|
|
102
|
+
await expect(
|
|
101
103
|
replaceRoute.handler({
|
|
102
104
|
pathParams: { name: "quality-optimized" },
|
|
103
105
|
body: { provider: "openai", model: "gpt-4o" },
|
|
104
106
|
}),
|
|
105
|
-
).toThrow(
|
|
107
|
+
).rejects.toThrow(
|
|
106
108
|
'Cannot edit managed profile "quality-optimized" fields [provider, model]. ' +
|
|
107
109
|
"Only label and status may be edited; duplicate to a custom profile to change other fields.",
|
|
108
110
|
);
|
|
109
111
|
});
|
|
110
112
|
|
|
111
|
-
test("rejects edits to balanced", () => {
|
|
112
|
-
expect(
|
|
113
|
+
test("rejects edits to balanced", async () => {
|
|
114
|
+
await expect(
|
|
113
115
|
replaceRoute.handler({
|
|
114
116
|
pathParams: { name: "balanced" },
|
|
115
117
|
body: { provider: "openai", model: "gpt-4o" },
|
|
116
118
|
}),
|
|
117
|
-
).toThrow(BadRequestError);
|
|
119
|
+
).rejects.toThrow(BadRequestError);
|
|
118
120
|
});
|
|
119
121
|
|
|
120
|
-
test("rejects edits to cost-optimized", () => {
|
|
121
|
-
expect(
|
|
122
|
+
test("rejects edits to cost-optimized", async () => {
|
|
123
|
+
await expect(
|
|
122
124
|
replaceRoute.handler({
|
|
123
125
|
pathParams: { name: "cost-optimized" },
|
|
124
126
|
body: { provider: "openai", model: "gpt-4o" },
|
|
125
127
|
}),
|
|
126
|
-
).toThrow(BadRequestError);
|
|
128
|
+
).rejects.toThrow(BadRequestError);
|
|
127
129
|
});
|
|
128
130
|
|
|
129
|
-
test("allows edits to custom-balanced (user-owned)", () => {
|
|
131
|
+
test("allows edits to custom-balanced (user-owned)", async () => {
|
|
130
132
|
savedRaw = null;
|
|
131
|
-
const result = replaceRoute.handler({
|
|
133
|
+
const result = await replaceRoute.handler({
|
|
132
134
|
pathParams: { name: "custom-balanced" },
|
|
133
135
|
body: { provider: "openai", model: "gpt-4o" },
|
|
134
136
|
});
|
|
@@ -136,9 +138,9 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
136
138
|
expect(savedRaw).not.toBeNull();
|
|
137
139
|
});
|
|
138
140
|
|
|
139
|
-
test("allows edits to a user-defined profile", () => {
|
|
141
|
+
test("allows edits to a user-defined profile", async () => {
|
|
140
142
|
savedRaw = null;
|
|
141
|
-
const result = replaceRoute.handler({
|
|
143
|
+
const result = await replaceRoute.handler({
|
|
142
144
|
pathParams: { name: "my-custom" },
|
|
143
145
|
body: { provider: "openai", model: "gpt-4o" },
|
|
144
146
|
});
|
|
@@ -147,14 +149,14 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
147
149
|
});
|
|
148
150
|
|
|
149
151
|
// -------------------------------------------------------------------------
|
|
150
|
-
// Null-as-clear sentinel: `
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
// These tests lock the round-trip
|
|
152
|
+
// Null-as-clear sentinel: clients send `{ label: null }` or
|
|
153
|
+
// `{ status: null }` to clear a managed profile's overrides back to the
|
|
154
|
+
// seed defaults. The Zod `ProfileEntry` schema accepts null for both
|
|
155
|
+
// fields, and the managed-profile guard / `patchManagedProfileFields`
|
|
156
|
+
// propagate the clear through to disk. These tests lock the round-trip.
|
|
155
157
|
// -------------------------------------------------------------------------
|
|
156
158
|
|
|
157
|
-
test("PUT { label: null } on managed profile clears the label on disk", () => {
|
|
159
|
+
test("PUT { label: null } on managed profile clears the label on disk", async () => {
|
|
158
160
|
savedRaw = null;
|
|
159
161
|
rawConfig = {
|
|
160
162
|
llm: {
|
|
@@ -168,7 +170,7 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
168
170
|
},
|
|
169
171
|
},
|
|
170
172
|
};
|
|
171
|
-
const result = replaceRoute.handler({
|
|
173
|
+
const result = await replaceRoute.handler({
|
|
172
174
|
pathParams: { name: "balanced" },
|
|
173
175
|
body: { label: null },
|
|
174
176
|
});
|
|
@@ -182,7 +184,7 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
182
184
|
expect(profile.source).toBe("managed");
|
|
183
185
|
});
|
|
184
186
|
|
|
185
|
-
test("PUT { status: null } on managed profile clears status (back to active-by-absence)", () => {
|
|
187
|
+
test("PUT { status: null } on managed profile clears status (back to active-by-absence)", async () => {
|
|
186
188
|
savedRaw = null;
|
|
187
189
|
rawConfig = {
|
|
188
190
|
llm: {
|
|
@@ -196,7 +198,7 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
196
198
|
},
|
|
197
199
|
},
|
|
198
200
|
};
|
|
199
|
-
const result = replaceRoute.handler({
|
|
201
|
+
const result = await replaceRoute.handler({
|
|
200
202
|
pathParams: { name: "quality-optimized" },
|
|
201
203
|
body: { status: null },
|
|
202
204
|
});
|
|
@@ -208,7 +210,7 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
208
210
|
expect(profile.model).toBe("claude-opus");
|
|
209
211
|
});
|
|
210
212
|
|
|
211
|
-
test("PUT { label: null, status: null } clears both in a single request", () => {
|
|
213
|
+
test("PUT { label: null, status: null } clears both in a single request", async () => {
|
|
212
214
|
savedRaw = null;
|
|
213
215
|
rawConfig = {
|
|
214
216
|
llm: {
|
|
@@ -223,7 +225,7 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
223
225
|
},
|
|
224
226
|
},
|
|
225
227
|
};
|
|
226
|
-
const result = replaceRoute.handler({
|
|
228
|
+
const result = await replaceRoute.handler({
|
|
227
229
|
pathParams: { name: "cost-optimized" },
|
|
228
230
|
body: { label: null, status: null },
|
|
229
231
|
});
|
|
@@ -236,7 +238,7 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
236
238
|
expect(profile.model).toBe("claude-haiku");
|
|
237
239
|
});
|
|
238
240
|
|
|
239
|
-
test("PUT { label: null, status: 'disabled' } mixes clear + set in one call", () => {
|
|
241
|
+
test("PUT { label: null, status: 'disabled' } mixes clear + set in one call", async () => {
|
|
240
242
|
savedRaw = null;
|
|
241
243
|
rawConfig = {
|
|
242
244
|
llm: {
|
|
@@ -250,7 +252,7 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
250
252
|
},
|
|
251
253
|
},
|
|
252
254
|
};
|
|
253
|
-
const result = replaceRoute.handler({
|
|
255
|
+
const result = await replaceRoute.handler({
|
|
254
256
|
pathParams: { name: "balanced" },
|
|
255
257
|
body: { label: null, status: "disabled" },
|
|
256
258
|
});
|
|
@@ -261,17 +263,17 @@ describe("PUT /v1/config/llm/profiles/:name — managed profile guard", () => {
|
|
|
261
263
|
expect(profile.status).toBe("disabled");
|
|
262
264
|
});
|
|
263
265
|
|
|
264
|
-
test("PUT { label: '' } on managed profile still rejected by `.min(1)`", () => {
|
|
266
|
+
test("PUT { label: '' } on managed profile still rejected by `.min(1)`", async () => {
|
|
265
267
|
// `.nullable()` only widens the type to accept null — empty strings
|
|
266
268
|
// still fail the min-length check, which is correct: an empty string
|
|
267
269
|
// would persist as a literal "" override, not the clear-to-seed
|
|
268
270
|
// intent. Clients must send `null` to clear.
|
|
269
|
-
expect(
|
|
271
|
+
await expect(
|
|
270
272
|
replaceRoute.handler({
|
|
271
273
|
pathParams: { name: "balanced" },
|
|
272
274
|
body: { label: "" },
|
|
273
275
|
}),
|
|
274
|
-
).toThrow(BadRequestError);
|
|
276
|
+
).rejects.toThrow(BadRequestError);
|
|
275
277
|
});
|
|
276
278
|
});
|
|
277
279
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
4
4
|
|
|
5
5
|
let TEST_DIR = "";
|
|
6
|
+
const seedUpsertSlugs: string[] = [];
|
|
6
7
|
|
|
7
8
|
const mockConfig = {
|
|
8
9
|
provider: "anthropic",
|
|
@@ -32,6 +33,10 @@ const mockConfig = {
|
|
|
32
33
|
},
|
|
33
34
|
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
34
35
|
},
|
|
36
|
+
skills: {
|
|
37
|
+
entries: {},
|
|
38
|
+
allowBundled: [],
|
|
39
|
+
},
|
|
35
40
|
};
|
|
36
41
|
|
|
37
42
|
mock.module("../util/logger.js", () => ({
|
|
@@ -51,7 +56,36 @@ mock.module("../config/loader.js", () => ({
|
|
|
51
56
|
setNestedValue: () => {},
|
|
52
57
|
}));
|
|
53
58
|
|
|
59
|
+
mock.module("../skills/catalog-cache.js", () => ({
|
|
60
|
+
getCatalog: async () => [],
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
mock.module("../memory/embedding-backend.js", () => ({
|
|
64
|
+
embedWithBackend: async (_config: unknown, inputs: unknown[]) => ({
|
|
65
|
+
provider: "local",
|
|
66
|
+
model: "test-model",
|
|
67
|
+
vectors: inputs.map(() => [0.1, 0.2, 0.3]),
|
|
68
|
+
}),
|
|
69
|
+
generateSparseEmbedding: () => ({ indices: [1], values: [1] }),
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
mock.module("../memory/v2/qdrant.js", () => ({
|
|
73
|
+
upsertConceptPageEmbedding: async (params: { slug: string }) => {
|
|
74
|
+
seedUpsertSlugs.push(params.slug);
|
|
75
|
+
},
|
|
76
|
+
pruneSlugsWithPrefixExcept: async () => {},
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
mock.module("../daemon/skill-memory-refresh.js", () => ({
|
|
80
|
+
refreshSkillCapabilityMemories: mock(() => {}),
|
|
81
|
+
}));
|
|
82
|
+
|
|
54
83
|
import { loadSkillCatalog } from "../config/skills.js";
|
|
84
|
+
import {
|
|
85
|
+
_resetSkillStoreForTests,
|
|
86
|
+
getSkillCapability,
|
|
87
|
+
seedV2SkillEntries,
|
|
88
|
+
} from "../memory/v2/skill-store.js";
|
|
55
89
|
import { executeDeleteManagedSkill } from "../tools/skills/delete-managed.js";
|
|
56
90
|
import { SkillLoadTool } from "../tools/skills/load.js";
|
|
57
91
|
import { executeScaffoldManagedSkill } from "../tools/skills/scaffold-managed.js";
|
|
@@ -68,9 +102,69 @@ function makeContext(): ToolContext {
|
|
|
68
102
|
beforeEach(() => {
|
|
69
103
|
TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
70
104
|
mkdirSync(join(TEST_DIR, "skills"), { recursive: true });
|
|
105
|
+
seedUpsertSlugs.length = 0;
|
|
106
|
+
_resetSkillStoreForTests();
|
|
71
107
|
});
|
|
72
108
|
|
|
73
109
|
describe("managed skill lifecycle: scaffold → catalog → prompt → delete", () => {
|
|
110
|
+
test("valid managed skill without SKILLS.md works across catalog, skill_load, and Memory V2 seeding", async () => {
|
|
111
|
+
const skillId = "e2e-custom-skill";
|
|
112
|
+
const skillSlug = `skills/${skillId}`;
|
|
113
|
+
const skillDir = join(TEST_DIR, "skills", skillId);
|
|
114
|
+
mkdirSync(skillDir, { recursive: true });
|
|
115
|
+
writeFileSync(
|
|
116
|
+
join(skillDir, "SKILL.md"),
|
|
117
|
+
`---
|
|
118
|
+
name: "E2E Custom Skill"
|
|
119
|
+
description: "Exercises custom managed skill loading."
|
|
120
|
+
metadata:
|
|
121
|
+
vellum:
|
|
122
|
+
activation-hints:
|
|
123
|
+
- user asks for custom lifecycle verification
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
Run the custom lifecycle verification procedure.
|
|
127
|
+
`,
|
|
128
|
+
"utf-8",
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
|
|
132
|
+
|
|
133
|
+
const catalog = loadSkillCatalog();
|
|
134
|
+
const catalogSkill = catalog.find((s) => s.id === skillId);
|
|
135
|
+
expect(catalogSkill).toBeDefined();
|
|
136
|
+
expect(catalogSkill!.source).toBe("managed");
|
|
137
|
+
expect(catalogSkill!.displayName).toBe("E2E Custom Skill");
|
|
138
|
+
|
|
139
|
+
const skillLoadTool = new (SkillLoadTool as any)() as InstanceType<
|
|
140
|
+
typeof SkillLoadTool
|
|
141
|
+
>;
|
|
142
|
+
const loadResult = await skillLoadTool.execute(
|
|
143
|
+
{ skill: skillId },
|
|
144
|
+
makeContext(),
|
|
145
|
+
);
|
|
146
|
+
expect(loadResult.isError).not.toBe(true);
|
|
147
|
+
expect(loadResult.content as string).toContain("Skill: E2E Custom Skill");
|
|
148
|
+
expect(loadResult.content as string).toContain("ID: e2e-custom-skill");
|
|
149
|
+
expect(loadResult.content as string).toContain(
|
|
150
|
+
"Run the custom lifecycle verification procedure.",
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
await seedV2SkillEntries();
|
|
154
|
+
|
|
155
|
+
expect(seedUpsertSlugs).toContain(skillSlug);
|
|
156
|
+
const capability = getSkillCapability(skillSlug);
|
|
157
|
+
expect(capability).not.toBeNull();
|
|
158
|
+
expect(capability!.id).toBe("e2e-custom-skill");
|
|
159
|
+
expect(capability!.content).toContain('The "E2E Custom Skill" skill');
|
|
160
|
+
expect(capability!.content).toContain(
|
|
161
|
+
"Exercises custom managed skill loading.",
|
|
162
|
+
);
|
|
163
|
+
expect(capability!.content).toContain(
|
|
164
|
+
"Use when: user asks for custom lifecycle verification.",
|
|
165
|
+
);
|
|
166
|
+
}, 15_000);
|
|
167
|
+
|
|
74
168
|
test("full lifecycle: create skill, verify in catalog and prompt, then delete", async () => {
|
|
75
169
|
// Step 1: Scaffold a managed skill
|
|
76
170
|
const scaffoldResult = await executeScaffoldManagedSkill(
|
|
@@ -87,9 +181,11 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
|
|
|
87
181
|
expect(scaffoldResult.isError).not.toBe(true);
|
|
88
182
|
const scaffoldData = JSON.parse(scaffoldResult.content as string);
|
|
89
183
|
expect(scaffoldData.created).toBe(true);
|
|
184
|
+
expect(scaffoldData).not.toHaveProperty("index_updated");
|
|
90
185
|
|
|
91
186
|
// Step 2: Verify SKILL.md was written
|
|
92
|
-
const
|
|
187
|
+
const skillDir = join(TEST_DIR, "skills", "lifecycle-test");
|
|
188
|
+
const skillMdPath = join(skillDir, "SKILL.md");
|
|
93
189
|
expect(existsSync(skillMdPath)).toBe(true);
|
|
94
190
|
const skillContent = readFileSync(skillMdPath, "utf-8");
|
|
95
191
|
expect(skillContent).toContain('name: "Lifecycle Test"');
|
|
@@ -102,6 +198,7 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
|
|
|
102
198
|
expect(found).toBeDefined();
|
|
103
199
|
expect(found!.name).toBe("Lifecycle Test");
|
|
104
200
|
expect(found!.description).toBe("Integration test skill.");
|
|
201
|
+
expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
|
|
105
202
|
|
|
106
203
|
// Step 4: Delete the skill
|
|
107
204
|
const deleteResult = await executeDeleteManagedSkill(
|
|
@@ -114,20 +211,15 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
|
|
|
114
211
|
expect(deleteResult.isError).not.toBe(true);
|
|
115
212
|
const deleteData = JSON.parse(deleteResult.content as string);
|
|
116
213
|
expect(deleteData.deleted).toBe(true);
|
|
214
|
+
expect(deleteData).not.toHaveProperty("index_updated");
|
|
117
215
|
|
|
118
|
-
// Step 5: Verify skill is gone from filesystem
|
|
119
|
-
expect(existsSync(
|
|
216
|
+
// Step 5: Verify skill directory is gone from filesystem
|
|
217
|
+
expect(existsSync(skillDir)).toBe(false);
|
|
120
218
|
|
|
121
219
|
// Step 6: Verify skill no longer in catalog
|
|
122
220
|
const catalogAfter = loadSkillCatalog();
|
|
123
221
|
expect(catalogAfter.find((s) => s.id === "lifecycle-test")).toBeUndefined();
|
|
124
|
-
|
|
125
|
-
// Step 7: Verify SKILLS.md index no longer has the entry
|
|
126
|
-
const indexPath = join(TEST_DIR, "skills", "SKILLS.md");
|
|
127
|
-
if (existsSync(indexPath)) {
|
|
128
|
-
const indexContent = readFileSync(indexPath, "utf-8");
|
|
129
|
-
expect(indexContent).not.toContain("lifecycle-test");
|
|
130
|
-
}
|
|
222
|
+
expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
|
|
131
223
|
});
|
|
132
224
|
|
|
133
225
|
test("scaffold with overwrite replaces existing skill", async () => {
|
|
@@ -166,13 +258,12 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
|
|
|
166
258
|
expect(skillContent).toContain("Updated body.");
|
|
167
259
|
expect(skillContent).not.toContain("Original body.");
|
|
168
260
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
expect(matches?.length).toBe(1);
|
|
261
|
+
expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
|
|
262
|
+
|
|
263
|
+
const catalog = loadSkillCatalog();
|
|
264
|
+
const skill = catalog.find((s) => s.id === "overwrite-test");
|
|
265
|
+
expect(skill).toBeDefined();
|
|
266
|
+
expect(skill!.name).toBe("V2");
|
|
176
267
|
});
|
|
177
268
|
|
|
178
269
|
test("delete non-existent skill returns error", async () => {
|