@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
|
@@ -19,6 +19,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
19
19
|
const mockAutoInstall = mock((_skillId: string) => Promise.resolve(false));
|
|
20
20
|
mock.module("../skills/catalog-install.js", () => ({
|
|
21
21
|
autoInstallFromCatalog: (skillId: string) => mockAutoInstall(skillId),
|
|
22
|
+
getRepoSkillsDir: () => undefined,
|
|
22
23
|
resolveCatalog: (_skillId?: string) => Promise.resolve([]),
|
|
23
24
|
}));
|
|
24
25
|
|
|
@@ -115,10 +116,6 @@ describe("skill_load tool", () => {
|
|
|
115
116
|
"Runs release checks",
|
|
116
117
|
"1. Run tests",
|
|
117
118
|
);
|
|
118
|
-
writeFileSync(
|
|
119
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
120
|
-
"- release-checklist\n",
|
|
121
|
-
);
|
|
122
119
|
|
|
123
120
|
const result = await executeSkillLoad({ skill: "release-checklist" });
|
|
124
121
|
expect(result.isError).toBe(false);
|
|
@@ -140,7 +137,6 @@ describe("skill_load tool", () => {
|
|
|
140
137
|
"Handles incidents",
|
|
141
138
|
"Page primary responder",
|
|
142
139
|
);
|
|
143
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- oncall\n");
|
|
144
140
|
|
|
145
141
|
const result = await executeSkillLoad({ skill: "oncall runbook" });
|
|
146
142
|
expect(result.isError).toBe(false);
|
|
@@ -165,10 +161,6 @@ describe("skill_load tool", () => {
|
|
|
165
161
|
"Release flow",
|
|
166
162
|
"Run release checklist",
|
|
167
163
|
);
|
|
168
|
-
writeFileSync(
|
|
169
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
170
|
-
"- incident-response\n- release-checklist\n",
|
|
171
|
-
);
|
|
172
164
|
|
|
173
165
|
const result = await executeSkillLoad({ skill: "incident" });
|
|
174
166
|
expect(result.isError).toBe(false);
|
|
@@ -182,10 +174,6 @@ describe("skill_load tool", () => {
|
|
|
182
174
|
test("returns an error when name resolution is ambiguous", async () => {
|
|
183
175
|
writeSkill("skill-a", "Shared Name", "First", "Body A");
|
|
184
176
|
writeSkill("skill-b", "Shared Name", "Second", "Body B");
|
|
185
|
-
writeFileSync(
|
|
186
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
187
|
-
"- skill-a\n- skill-b\n",
|
|
188
|
-
);
|
|
189
177
|
|
|
190
178
|
const result = await executeSkillLoad({ skill: "Shared Name" });
|
|
191
179
|
expect(result.isError).toBe(true);
|
|
@@ -200,7 +188,6 @@ describe("skill_load tool", () => {
|
|
|
200
188
|
"Test versioning",
|
|
201
189
|
"Original body",
|
|
202
190
|
);
|
|
203
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- versioned\n");
|
|
204
191
|
|
|
205
192
|
const result1 = await executeSkillLoad({ skill: "versioned" });
|
|
206
193
|
const match1 = result1.content.match(
|
|
@@ -229,7 +216,6 @@ describe("skill_load tool", () => {
|
|
|
229
216
|
|
|
230
217
|
test("returns an error when skill is missing", async () => {
|
|
231
218
|
writeSkill("existing", "Existing Skill", "Exists", "Body");
|
|
232
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- existing\n");
|
|
233
219
|
|
|
234
220
|
const result = await executeSkillLoad({ skill: "does-not-exist" });
|
|
235
221
|
expect(result.isError).toBe(true);
|
|
@@ -237,6 +223,32 @@ describe("skill_load tool", () => {
|
|
|
237
223
|
expect(result.content).not.toContain("<loaded_skill");
|
|
238
224
|
});
|
|
239
225
|
|
|
226
|
+
test("loads a valid disk-discovered skill omitted from stale SKILLS.md", async () => {
|
|
227
|
+
writeSkill("existing", "Existing Skill", "Exists", "Existing body");
|
|
228
|
+
writeSkill(
|
|
229
|
+
"geo-article-writer",
|
|
230
|
+
"Geo Article Writer",
|
|
231
|
+
"Writes local geo articles",
|
|
232
|
+
"Draft the article.",
|
|
233
|
+
);
|
|
234
|
+
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- existing\n");
|
|
235
|
+
|
|
236
|
+
const result = await executeSkillLoad({ skill: "geo-article-writer" });
|
|
237
|
+
|
|
238
|
+
expect(result.isError).toBe(false);
|
|
239
|
+
expect(result.content).toContain("ID: geo-article-writer");
|
|
240
|
+
const markers = result.content.match(/<loaded_skill/g) || [];
|
|
241
|
+
expect(markers.length).toBe(1);
|
|
242
|
+
expect(result.content).toMatch(
|
|
243
|
+
/<loaded_skill id="geo-article-writer" version="v1:[a-f0-9]{64}" \/>/,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const missing = await executeSkillLoad({ skill: "does-not-exist" });
|
|
247
|
+
expect(missing.isError).toBe(true);
|
|
248
|
+
expect(missing.content).toContain("No skill matched");
|
|
249
|
+
expect(missing.content).not.toContain("<loaded_skill");
|
|
250
|
+
});
|
|
251
|
+
|
|
240
252
|
test('successful skill_load output shows "none" for skills without includes', async () => {
|
|
241
253
|
writeSkill(
|
|
242
254
|
"standalone",
|
|
@@ -244,7 +256,6 @@ describe("skill_load tool", () => {
|
|
|
244
256
|
"A skill with no children",
|
|
245
257
|
"Do the thing",
|
|
246
258
|
);
|
|
247
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- standalone\n");
|
|
248
259
|
|
|
249
260
|
const result = await executeSkillLoad({ skill: "standalone" });
|
|
250
261
|
expect(result.isError).toBe(false);
|
|
@@ -258,7 +269,6 @@ describe("skill_load tool", () => {
|
|
|
258
269
|
"Should have one marker",
|
|
259
270
|
"Step 1",
|
|
260
271
|
);
|
|
261
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- single-marker\n");
|
|
262
272
|
|
|
263
273
|
const result = await executeSkillLoad({ skill: "single-marker" });
|
|
264
274
|
expect(result.isError).toBe(false);
|
|
@@ -270,7 +280,6 @@ describe("skill_load tool", () => {
|
|
|
270
280
|
writeSkillWithIncludes("parent", "Parent", "Has missing child", "Body", [
|
|
271
281
|
"missing-child",
|
|
272
282
|
]);
|
|
273
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- parent\n");
|
|
274
283
|
|
|
275
284
|
const result = await executeSkillLoad({ skill: "parent" });
|
|
276
285
|
expect(result.isError).toBe(false);
|
|
@@ -288,10 +297,6 @@ describe("skill_load tool", () => {
|
|
|
288
297
|
writeSkillWithIncludes("skill-b", "Skill B", "Cycles", "Body B", [
|
|
289
298
|
"skill-a",
|
|
290
299
|
]);
|
|
291
|
-
writeFileSync(
|
|
292
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
293
|
-
"- skill-a\n- skill-b\n",
|
|
294
|
-
);
|
|
295
300
|
|
|
296
301
|
const result = await executeSkillLoad({ skill: "skill-a" });
|
|
297
302
|
expect(result.isError).toBe(true);
|
|
@@ -308,10 +313,6 @@ describe("skill_load tool", () => {
|
|
|
308
313
|
["valid-child"],
|
|
309
314
|
);
|
|
310
315
|
writeSkill("valid-child", "Valid Child", "A child", "Child body");
|
|
311
|
-
writeFileSync(
|
|
312
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
313
|
-
"- valid-parent\n- valid-child\n",
|
|
314
|
-
);
|
|
315
316
|
|
|
316
317
|
const result = await executeSkillLoad({ skill: "valid-parent" });
|
|
317
318
|
expect(result.isError).toBe(false);
|
|
@@ -326,7 +327,6 @@ describe("skill_load tool", () => {
|
|
|
326
327
|
join(skillDir, "SKILL.md"),
|
|
327
328
|
'---\nname: "Marker Missing"\ndescription: "test"\nmetadata: {"vellum":{"includes":["nonexistent"]}}\n---\n\nBody.\n',
|
|
328
329
|
);
|
|
329
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- marker-missing\n");
|
|
330
330
|
|
|
331
331
|
const result = await executeSkillLoad({ skill: "marker-missing" });
|
|
332
332
|
expect(result.isError).toBe(false);
|
|
@@ -351,10 +351,6 @@ describe("skill_load tool", () => {
|
|
|
351
351
|
join(dirB, "SKILL.md"),
|
|
352
352
|
'---\nname: "Cycle B"\ndescription: "test"\nmetadata: {"vellum":{"includes":["cycle-a"]}}\n---\n\nBody B.\n',
|
|
353
353
|
);
|
|
354
|
-
writeFileSync(
|
|
355
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
356
|
-
"- cycle-a\n- cycle-b\n",
|
|
357
|
-
);
|
|
358
354
|
|
|
359
355
|
const result = await executeSkillLoad({ skill: "cycle-a" });
|
|
360
356
|
expect(result.isError).toBe(true);
|
|
@@ -364,7 +360,6 @@ describe("skill_load tool", () => {
|
|
|
364
360
|
|
|
365
361
|
test("succeeds when skill has no includes", async () => {
|
|
366
362
|
writeSkill("no-includes", "No Includes", "Plain skill", "Body");
|
|
367
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- no-includes\n");
|
|
368
363
|
|
|
369
364
|
const result = await executeSkillLoad({ skill: "no-includes" });
|
|
370
365
|
expect(result.isError).toBe(false);
|
|
@@ -399,10 +394,6 @@ describe("skill_load tool", () => {
|
|
|
399
394
|
join(parentDir, "SKILL.md"),
|
|
400
395
|
'---\nname: "Parent"\ndescription: "Has children"\nmetadata: {"vellum":{"includes":["child-skill"]}}\n---\n\nParent body.\n',
|
|
401
396
|
);
|
|
402
|
-
writeFileSync(
|
|
403
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
404
|
-
"- parent-with-children\n- child-skill\n",
|
|
405
|
-
);
|
|
406
397
|
|
|
407
398
|
const result = await executeSkillLoad({ skill: "parent-with-children" });
|
|
408
399
|
expect(result.isError).toBe(false);
|
|
@@ -413,7 +404,6 @@ describe("skill_load tool", () => {
|
|
|
413
404
|
|
|
414
405
|
test('skill_load output shows "none" when no includes', async () => {
|
|
415
406
|
writeSkill("solo-skill", "Solo", "No children", "Body");
|
|
416
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- solo-skill\n");
|
|
417
407
|
|
|
418
408
|
const result = await executeSkillLoad({ skill: "solo-skill" });
|
|
419
409
|
expect(result.isError).toBe(false);
|
|
@@ -435,10 +425,6 @@ describe("skill_load tool", () => {
|
|
|
435
425
|
"Parent instructions.",
|
|
436
426
|
["e2e-child"],
|
|
437
427
|
);
|
|
438
|
-
writeFileSync(
|
|
439
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
440
|
-
"- e2e-parent\n- e2e-child\n",
|
|
441
|
-
);
|
|
442
428
|
|
|
443
429
|
// Load the parent
|
|
444
430
|
const result = await executeSkillLoad({ skill: "e2e-parent" });
|
|
@@ -479,10 +465,6 @@ describe("skill_load tool", () => {
|
|
|
479
465
|
"Grandparent body",
|
|
480
466
|
["child"],
|
|
481
467
|
);
|
|
482
|
-
writeFileSync(
|
|
483
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
484
|
-
"- grandparent\n- child\n- grandchild\n",
|
|
485
|
-
);
|
|
486
468
|
|
|
487
469
|
const result = await executeSkillLoad({ skill: "grandparent" });
|
|
488
470
|
expect(result.isError).toBe(false);
|
|
@@ -534,10 +516,6 @@ describe("skill_load tool", () => {
|
|
|
534
516
|
"Root body",
|
|
535
517
|
["branch-a", "branch-b"],
|
|
536
518
|
);
|
|
537
|
-
writeFileSync(
|
|
538
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
539
|
-
"- diamond-root\n- branch-a\n- branch-b\n- shared-leaf\n",
|
|
540
|
-
);
|
|
541
519
|
|
|
542
520
|
const result = await executeSkillLoad({ skill: "diamond-root" });
|
|
543
521
|
expect(result.isError).toBe(false);
|
|
@@ -576,7 +554,6 @@ describe("skill_load tool", () => {
|
|
|
576
554
|
"Body",
|
|
577
555
|
["self-ref"],
|
|
578
556
|
);
|
|
579
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- self-ref\n");
|
|
580
557
|
|
|
581
558
|
const result = await executeSkillLoad({ skill: "self-ref" });
|
|
582
559
|
expect(result.isError).toBe(true);
|
|
@@ -601,7 +578,6 @@ describe("skill_load tool", () => {
|
|
|
601
578
|
join(skillDir, "references", "TROUBLESHOOTING.md"),
|
|
602
579
|
"# Troubleshooting\n\nFix things here.",
|
|
603
580
|
);
|
|
604
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- with-refs\n");
|
|
605
581
|
|
|
606
582
|
const result = await executeSkillLoad({ skill: "with-refs" });
|
|
607
583
|
expect(result.isError).toBe(false);
|
|
@@ -624,7 +600,6 @@ describe("skill_load tool", () => {
|
|
|
624
600
|
|
|
625
601
|
test("skill without references/ directory loads normally", async () => {
|
|
626
602
|
writeSkill("no-refs", "No Refs", "No references dir", "Just body.");
|
|
627
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- no-refs\n");
|
|
628
603
|
|
|
629
604
|
const result = await executeSkillLoad({ skill: "no-refs" });
|
|
630
605
|
expect(result.isError).toBe(false);
|
|
@@ -650,8 +625,6 @@ describe("skill_load tool", () => {
|
|
|
650
625
|
writeFileSync(outsideSecretPath, "TOP_SECRET_DO_NOT_LOAD");
|
|
651
626
|
symlinkSync(outsideSecretPath, join(skillDir, "references", "secret.md"));
|
|
652
627
|
|
|
653
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- refs-symlink\n");
|
|
654
|
-
|
|
655
628
|
const result = await executeSkillLoad({ skill: "refs-symlink" });
|
|
656
629
|
expect(result.isError).toBe(false);
|
|
657
630
|
expect(result.content).toContain("Body.");
|
|
@@ -675,7 +648,6 @@ describe("skill_load tool", () => {
|
|
|
675
648
|
join(skillDir, "references", "data.json"),
|
|
676
649
|
'{"key": "value"}',
|
|
677
650
|
);
|
|
678
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- refs-filter\n");
|
|
679
651
|
|
|
680
652
|
const result = await executeSkillLoad({ skill: "refs-filter" });
|
|
681
653
|
expect(result.isError).toBe(false);
|
|
@@ -695,7 +667,6 @@ describe("skill_load tool", () => {
|
|
|
695
667
|
join(skillDir, "SKILL.md"),
|
|
696
668
|
'---\nname: "Empty Includes"\ndescription: "Has empty array"\nmetadata: {"vellum":{"includes":[]}}\n---\n\nBody.\n',
|
|
697
669
|
);
|
|
698
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- empty-includes\n");
|
|
699
670
|
|
|
700
671
|
const result = await executeSkillLoad({ skill: "empty-includes" });
|
|
701
672
|
expect(result.isError).toBe(false);
|
|
@@ -746,11 +717,6 @@ describe("skill_load tool", () => {
|
|
|
746
717
|
},
|
|
747
718
|
},
|
|
748
719
|
]);
|
|
749
|
-
writeFileSync(
|
|
750
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
751
|
-
"- skill-with-tools\n",
|
|
752
|
-
);
|
|
753
|
-
|
|
754
720
|
const result = await executeSkillLoad({ skill: "skill-with-tools" });
|
|
755
721
|
expect(result.isError).toBe(false);
|
|
756
722
|
|
|
@@ -784,7 +750,6 @@ describe("skill_load tool", () => {
|
|
|
784
750
|
|
|
785
751
|
test("skill without TOOLS.json does not include tool schemas section", async () => {
|
|
786
752
|
writeSkill("no-tools", "No Tools", "No tools manifest", "Body.");
|
|
787
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- no-tools\n");
|
|
788
753
|
|
|
789
754
|
const result = await executeSkillLoad({ skill: "no-tools" });
|
|
790
755
|
expect(result.isError).toBe(false);
|
|
@@ -817,10 +782,6 @@ describe("skill_load tool", () => {
|
|
|
817
782
|
},
|
|
818
783
|
},
|
|
819
784
|
]);
|
|
820
|
-
writeFileSync(
|
|
821
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
822
|
-
"- parent-tools\n- child-tools\n",
|
|
823
|
-
);
|
|
824
785
|
|
|
825
786
|
const result = await executeSkillLoad({ skill: "parent-tools" });
|
|
826
787
|
expect(result.isError).toBe(false);
|
|
@@ -845,17 +806,11 @@ describe("skill_load tool", () => {
|
|
|
845
806
|
"Parent body",
|
|
846
807
|
["dep-a"],
|
|
847
808
|
);
|
|
848
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- auto-parent\n");
|
|
849
809
|
|
|
850
810
|
// Mock autoInstallFromCatalog to succeed and write the skill to disk
|
|
851
811
|
mockAutoInstall.mockImplementation((skillId: string) => {
|
|
852
812
|
if (skillId === "dep-a") {
|
|
853
813
|
writeSkill("dep-a", "Dep A", "A dependency", "Dep A body");
|
|
854
|
-
// Add to SKILLS.md so catalog reload finds it
|
|
855
|
-
writeFileSync(
|
|
856
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
857
|
-
"- auto-parent\n- dep-a\n",
|
|
858
|
-
);
|
|
859
814
|
return Promise.resolve(true);
|
|
860
815
|
}
|
|
861
816
|
return Promise.resolve(false);
|
|
@@ -873,7 +828,6 @@ describe("skill_load tool", () => {
|
|
|
873
828
|
writeSkillWithIncludes("trans-a", "Trans A", "Top level", "Body A", [
|
|
874
829
|
"trans-b",
|
|
875
830
|
]);
|
|
876
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- trans-a\n");
|
|
877
831
|
|
|
878
832
|
let round = 0;
|
|
879
833
|
mockAutoInstall.mockImplementation((skillId: string) => {
|
|
@@ -882,20 +836,12 @@ describe("skill_load tool", () => {
|
|
|
882
836
|
writeSkillWithIncludes("trans-b", "Trans B", "Mid level", "Body B", [
|
|
883
837
|
"trans-c",
|
|
884
838
|
]);
|
|
885
|
-
writeFileSync(
|
|
886
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
887
|
-
"- trans-a\n- trans-b\n",
|
|
888
|
-
);
|
|
889
839
|
round++;
|
|
890
840
|
return Promise.resolve(true);
|
|
891
841
|
}
|
|
892
842
|
if (skillId === "trans-c") {
|
|
893
843
|
// Second round: install C
|
|
894
844
|
writeSkill("trans-c", "Trans C", "Leaf", "Body C");
|
|
895
|
-
writeFileSync(
|
|
896
|
-
join(TEST_DIR, "skills", "SKILLS.md"),
|
|
897
|
-
"- trans-a\n- trans-b\n- trans-c\n",
|
|
898
|
-
);
|
|
899
845
|
return Promise.resolve(true);
|
|
900
846
|
}
|
|
901
847
|
return Promise.resolve(false);
|
|
@@ -917,7 +863,6 @@ describe("skill_load tool", () => {
|
|
|
917
863
|
"Body",
|
|
918
864
|
["dep-x"],
|
|
919
865
|
);
|
|
920
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- fail-parent\n");
|
|
921
866
|
|
|
922
867
|
// autoInstallFromCatalog throws an error
|
|
923
868
|
mockAutoInstall.mockImplementation((skillId: string) => {
|
|
@@ -941,7 +886,6 @@ describe("skill_load tool", () => {
|
|
|
941
886
|
writeSkillWithIncludes("loop-root", "Loop Root", "Infinite deps", "Body", [
|
|
942
887
|
"loop-dep-0",
|
|
943
888
|
]);
|
|
944
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- loop-root\n");
|
|
945
889
|
|
|
946
890
|
let installCount = 0;
|
|
947
891
|
mockAutoInstall.mockImplementation((skillId: string) => {
|
|
@@ -957,12 +901,6 @@ describe("skill_load tool", () => {
|
|
|
957
901
|
"Body",
|
|
958
902
|
[nextDepId],
|
|
959
903
|
);
|
|
960
|
-
// Update SKILLS.md to include all installed deps so far
|
|
961
|
-
const entries = ["- loop-root\n"];
|
|
962
|
-
for (let i = 0; i < installCount; i++) {
|
|
963
|
-
entries.push(`- loop-dep-${i}\n`);
|
|
964
|
-
}
|
|
965
|
-
writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), entries.join(""));
|
|
966
904
|
return Promise.resolve(true);
|
|
967
905
|
}
|
|
968
906
|
return Promise.resolve(false);
|
|
@@ -3,9 +3,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
3
3
|
import type { SkillSummary } from "../config/skills.js";
|
|
4
4
|
import { fromSkillSummary } from "../skills/skill-memory.js";
|
|
5
5
|
|
|
6
|
-
function makeSkillSummary(
|
|
7
|
-
overrides: Partial<SkillSummary> = {},
|
|
8
|
-
): SkillSummary {
|
|
6
|
+
function makeSkillSummary(overrides: Partial<SkillSummary> = {}): SkillSummary {
|
|
9
7
|
return {
|
|
10
8
|
id: "test-skill",
|
|
11
9
|
name: "test-skill",
|
|
@@ -62,4 +60,26 @@ describe("fromSkillSummary", () => {
|
|
|
62
60
|
expect(input.id).toBe("my-id");
|
|
63
61
|
expect(input.description).toBe("Does amazing things");
|
|
64
62
|
});
|
|
63
|
+
|
|
64
|
+
test("maps custom managed skill metadata required by Memory V2 rendering", () => {
|
|
65
|
+
const entry = makeSkillSummary({
|
|
66
|
+
id: "geo-article-writer",
|
|
67
|
+
name: "geo-article-writer",
|
|
68
|
+
displayName: "Geo Article Writer",
|
|
69
|
+
description: "Writes local geo articles",
|
|
70
|
+
source: "managed",
|
|
71
|
+
activationHints: ["user asks for local article drafts"],
|
|
72
|
+
avoidWhen: ["user only wants citation extraction"],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const input = fromSkillSummary(entry);
|
|
76
|
+
|
|
77
|
+
expect(input).toEqual({
|
|
78
|
+
id: "geo-article-writer",
|
|
79
|
+
displayName: "Geo Article Writer",
|
|
80
|
+
description: "Writes local geo articles",
|
|
81
|
+
activationHints: ["user asks for local article drafts"],
|
|
82
|
+
avoidWhen: ["user only wants citation extraction"],
|
|
83
|
+
});
|
|
84
|
+
});
|
|
65
85
|
});
|
|
@@ -199,7 +199,6 @@ mock.module("../skills/skill-file-provider.js", () => ({}));
|
|
|
199
199
|
|
|
200
200
|
mock.module("../skills/catalog-install.js", () => ({
|
|
201
201
|
installSkillLocally: async () => {},
|
|
202
|
-
upsertSkillsIndex: () => {},
|
|
203
202
|
getRepoSkillsDir: () => undefined,
|
|
204
203
|
}));
|
|
205
204
|
|
|
@@ -230,7 +229,6 @@ mock.module("../skills/install-meta.js", () => ({
|
|
|
230
229
|
mock.module("../skills/managed-store.js", () => ({
|
|
231
230
|
createManagedSkill: () => ({ created: true }),
|
|
232
231
|
deleteManagedSkill: () => ({ deleted: true }),
|
|
233
|
-
removeSkillsIndexEntry: () => {},
|
|
234
232
|
validateManagedSkillId: () => null,
|
|
235
233
|
}));
|
|
236
234
|
|
|
@@ -412,10 +410,7 @@ describe("getSkillFileContent — installed skill", () => {
|
|
|
412
410
|
mockResolvedSkills = [installedSkill("my-skill", skillDir)];
|
|
413
411
|
installFetchForbidden();
|
|
414
412
|
|
|
415
|
-
const result = await getSkillFileContent(
|
|
416
|
-
"my-skill",
|
|
417
|
-
"SKILL.md\0.png",
|
|
418
|
-
);
|
|
413
|
+
const result = await getSkillFileContent("my-skill", "SKILL.md\0.png");
|
|
419
414
|
expect("error" in result).toBe(true);
|
|
420
415
|
if (!("error" in result)) return;
|
|
421
416
|
expect(result.status).toBe(400);
|
|
@@ -458,10 +453,7 @@ describe("getSkillFileContent — uninstalled skill (provider chain)", () => {
|
|
|
458
453
|
toSlimSkill: async () => null,
|
|
459
454
|
};
|
|
460
455
|
|
|
461
|
-
const result = await getSkillFileContent(
|
|
462
|
-
"remote-skill",
|
|
463
|
-
"SKILL.md",
|
|
464
|
-
);
|
|
456
|
+
const result = await getSkillFileContent("remote-skill", "SKILL.md");
|
|
465
457
|
expect("error" in result).toBe(false);
|
|
466
458
|
if ("error" in result) return;
|
|
467
459
|
expect(result.path).toBe("SKILL.md");
|
|
@@ -478,10 +470,7 @@ describe("getSkillFileContent — uninstalled skill (provider chain)", () => {
|
|
|
478
470
|
|
|
479
471
|
// All providers return canHandle=false (default noop)
|
|
480
472
|
|
|
481
|
-
const result = await getSkillFileContent(
|
|
482
|
-
"ghost-skill",
|
|
483
|
-
"SKILL.md",
|
|
484
|
-
);
|
|
473
|
+
const result = await getSkillFileContent("ghost-skill", "SKILL.md");
|
|
485
474
|
expect("error" in result).toBe(true);
|
|
486
475
|
if (!("error" in result)) return;
|
|
487
476
|
expect(result.status).toBe(404);
|
|
@@ -510,10 +499,7 @@ describe("getSkillFileContent — uninstalled skill (provider chain)", () => {
|
|
|
510
499
|
toSlimSkill: async () => null,
|
|
511
500
|
};
|
|
512
501
|
|
|
513
|
-
const result = await getSkillFileContent(
|
|
514
|
-
"owner/repo/my-skill",
|
|
515
|
-
"SKILL.md",
|
|
516
|
-
);
|
|
502
|
+
const result = await getSkillFileContent("owner/repo/my-skill", "SKILL.md");
|
|
517
503
|
expect("error" in result).toBe(false);
|
|
518
504
|
if ("error" in result) return;
|
|
519
505
|
expect(result.content).toBe("# skillssh content\n");
|
|
@@ -562,10 +548,7 @@ describe("getSkillFileContent — uninstalled skill (provider chain)", () => {
|
|
|
562
548
|
toSlimSkill: async () => null,
|
|
563
549
|
};
|
|
564
550
|
|
|
565
|
-
const result = await getSkillFileContent(
|
|
566
|
-
"known-skill",
|
|
567
|
-
"nonexistent.txt",
|
|
568
|
-
);
|
|
551
|
+
const result = await getSkillFileContent("known-skill", "nonexistent.txt");
|
|
569
552
|
expect("error" in result).toBe(true);
|
|
570
553
|
if (!("error" in result)) return;
|
|
571
554
|
expect(result.status).toBe(404);
|
|
@@ -604,10 +587,7 @@ describe("getSkillFileContent — uninstalled skill (provider chain)", () => {
|
|
|
604
587
|
toSlimSkill: async () => null,
|
|
605
588
|
};
|
|
606
589
|
|
|
607
|
-
const result = await getSkillFileContent(
|
|
608
|
-
"simple-slug",
|
|
609
|
-
"SKILL.md",
|
|
610
|
-
);
|
|
590
|
+
const result = await getSkillFileContent("simple-slug", "SKILL.md");
|
|
611
591
|
// Should be "File not found" (vellum handled but returned null)
|
|
612
592
|
expect("error" in result).toBe(true);
|
|
613
593
|
if (!("error" in result)) return;
|
|
@@ -626,10 +606,7 @@ describe("getSkillFileContent — skill not found", () => {
|
|
|
626
606
|
mockResolvedSkills = [];
|
|
627
607
|
installFetchForbidden();
|
|
628
608
|
|
|
629
|
-
const result = await getSkillFileContent(
|
|
630
|
-
"ghost-skill",
|
|
631
|
-
"SKILL.md",
|
|
632
|
-
);
|
|
609
|
+
const result = await getSkillFileContent("ghost-skill", "SKILL.md");
|
|
633
610
|
expect("error" in result).toBe(true);
|
|
634
611
|
if (!("error" in result)) return;
|
|
635
612
|
expect(result.status).toBe(404);
|
|
@@ -666,10 +643,7 @@ describe("getSkillFileContent — installed skill with missing directory", () =>
|
|
|
666
643
|
};
|
|
667
644
|
installFetchForbidden();
|
|
668
645
|
|
|
669
|
-
const result = await getSkillFileContent(
|
|
670
|
-
"ghost-installed",
|
|
671
|
-
"SKILL.md",
|
|
672
|
-
);
|
|
646
|
+
const result = await getSkillFileContent("ghost-installed", "SKILL.md");
|
|
673
647
|
|
|
674
648
|
expect("error" in result).toBe(true);
|
|
675
649
|
if (!("error" in result)) return;
|
|
@@ -766,10 +740,7 @@ describe("getSkillFileContent — hidden / SKIP_DIRS rejection", () => {
|
|
|
766
740
|
mockResolvedSkills = [installedSkill("healthy-skill", skillDir)];
|
|
767
741
|
installFetchForbidden();
|
|
768
742
|
|
|
769
|
-
const result = await getSkillFileContent(
|
|
770
|
-
"healthy-skill",
|
|
771
|
-
"SKILL.md",
|
|
772
|
-
);
|
|
743
|
+
const result = await getSkillFileContent("healthy-skill", "SKILL.md");
|
|
773
744
|
expect("error" in result).toBe(false);
|
|
774
745
|
if ("error" in result) return;
|
|
775
746
|
expect(result.content).toBe("# hello\n");
|
|
@@ -164,7 +164,6 @@ mock.module("../skills/skill-file-provider.js", () => ({}));
|
|
|
164
164
|
|
|
165
165
|
mock.module("../skills/catalog-install.js", () => ({
|
|
166
166
|
installSkillLocally: async () => {},
|
|
167
|
-
upsertSkillsIndex: () => {},
|
|
168
167
|
}));
|
|
169
168
|
|
|
170
169
|
mock.module("../skills/catalog-search.js", () => ({
|
|
@@ -188,7 +187,6 @@ mock.module("../skills/skillssh-registry.js", () => ({
|
|
|
188
187
|
mock.module("../skills/managed-store.js", () => ({
|
|
189
188
|
createManagedSkill: () => ({ created: true }),
|
|
190
189
|
deleteManagedSkill: () => ({ deleted: true }),
|
|
191
|
-
removeSkillsIndexEntry: () => {},
|
|
192
190
|
validateManagedSkillId: () => null,
|
|
193
191
|
}));
|
|
194
192
|
|
|
@@ -238,7 +236,6 @@ import {
|
|
|
238
236
|
// Helpers
|
|
239
237
|
// ---------------------------------------------------------------------------
|
|
240
238
|
|
|
241
|
-
|
|
242
239
|
function makeSummary(overrides: Partial<SkillSummary>): SkillSummary {
|
|
243
240
|
return {
|
|
244
241
|
id: overrides.id ?? "summary-id",
|
|
@@ -3,47 +3,14 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
extractTarToDir,
|
|
8
|
+
writeSkillFilesToDir,
|
|
9
|
+
} from "../skills/catalog-install.js";
|
|
10
|
+
import { makeTar } from "./helpers/tar-fixtures.js";
|
|
7
11
|
|
|
8
12
|
let tempDir: string;
|
|
9
13
|
|
|
10
|
-
function makeTarEntry(name: string, content: string): Buffer {
|
|
11
|
-
const header = Buffer.alloc(512, 0);
|
|
12
|
-
const nameBuffer = Buffer.from(name, "utf-8");
|
|
13
|
-
nameBuffer.copy(header, 0, 0, Math.min(nameBuffer.length, 100));
|
|
14
|
-
|
|
15
|
-
const mode = Buffer.from("0000644\0", "ascii");
|
|
16
|
-
mode.copy(header, 100);
|
|
17
|
-
Buffer.from("0000000\0", "ascii").copy(header, 108); // uid
|
|
18
|
-
Buffer.from("0000000\0", "ascii").copy(header, 116); // gid
|
|
19
|
-
|
|
20
|
-
const sizeOct = content.length.toString(8).padStart(11, "0") + "\0";
|
|
21
|
-
Buffer.from(sizeOct, "ascii").copy(header, 124);
|
|
22
|
-
|
|
23
|
-
Buffer.from("00000000000\0", "ascii").copy(header, 136); // mtime
|
|
24
|
-
Buffer.from(" ", "ascii").copy(header, 148); // checksum placeholder
|
|
25
|
-
header[156] = "0".charCodeAt(0);
|
|
26
|
-
Buffer.from("ustar\0", "ascii").copy(header, 257);
|
|
27
|
-
Buffer.from("00", "ascii").copy(header, 263);
|
|
28
|
-
|
|
29
|
-
let sum = 0;
|
|
30
|
-
for (let i = 0; i < 512; i += 1) sum += header[i] ?? 0;
|
|
31
|
-
const checksum = sum.toString(8).padStart(6, "0");
|
|
32
|
-
Buffer.from(`${checksum}\0 `, "ascii").copy(header, 148);
|
|
33
|
-
|
|
34
|
-
const data = Buffer.from(content, "utf-8");
|
|
35
|
-
const paddedSize = Math.ceil(data.length / 512) * 512;
|
|
36
|
-
const padded = Buffer.alloc(paddedSize, 0);
|
|
37
|
-
data.copy(padded);
|
|
38
|
-
|
|
39
|
-
return Buffer.concat([header, padded]);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function makeTar(entries: Array<{ name: string; content: string }>): Buffer {
|
|
43
|
-
const body = entries.map((entry) => makeTarEntry(entry.name, entry.content));
|
|
44
|
-
return Buffer.concat([...body, Buffer.alloc(1024, 0)]);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
14
|
beforeEach(() => {
|
|
48
15
|
tempDir = join(
|
|
49
16
|
tmpdir(),
|
|
@@ -90,4 +57,48 @@ describe("extractTarToDir", () => {
|
|
|
90
57
|
expect(existsSync(join(tempDir, "windows.txt"))).toBe(false);
|
|
91
58
|
expect(readFileSync(join(tempDir, "SKILL.md"), "utf-8")).toBe("# demo\n");
|
|
92
59
|
});
|
|
60
|
+
|
|
61
|
+
test("does not count nested SKILL.md as a valid skill root", () => {
|
|
62
|
+
const tar = makeTar([
|
|
63
|
+
{ name: "nested/SKILL.md", content: "# nested\n" },
|
|
64
|
+
{ name: "README.md", content: "# wrapper\n" },
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
const foundSkillMd = extractTarToDir(tar, tempDir);
|
|
68
|
+
|
|
69
|
+
expect(foundSkillMd).toBe(false);
|
|
70
|
+
expect(existsSync(join(tempDir, "SKILL.md"))).toBe(false);
|
|
71
|
+
expect(readFileSync(join(tempDir, "nested", "SKILL.md"), "utf-8")).toBe(
|
|
72
|
+
"# nested\n",
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("normalizes safe relative segments before top-level SKILL.md detection", () => {
|
|
77
|
+
const tar = makeTar([{ name: "wrapper/../SKILL.md", content: "# demo\n" }]);
|
|
78
|
+
|
|
79
|
+
const foundSkillMd = extractTarToDir(tar, tempDir);
|
|
80
|
+
|
|
81
|
+
expect(foundSkillMd).toBe(true);
|
|
82
|
+
expect(readFileSync(join(tempDir, "SKILL.md"), "utf-8")).toBe("# demo\n");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("writeSkillFilesToDir", () => {
|
|
87
|
+
test("uses the same traversal rules as tar extraction", () => {
|
|
88
|
+
const foundSkillMd = writeSkillFilesToDir(
|
|
89
|
+
{
|
|
90
|
+
"./SKILL.md": "# demo\n",
|
|
91
|
+
"scripts/../notes.md": "ok\n",
|
|
92
|
+
"../../escape.txt": "nope\n",
|
|
93
|
+
"/absolute.txt": "nope\n",
|
|
94
|
+
},
|
|
95
|
+
tempDir,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect(foundSkillMd).toBe(true);
|
|
99
|
+
expect(readFileSync(join(tempDir, "SKILL.md"), "utf-8")).toBe("# demo\n");
|
|
100
|
+
expect(readFileSync(join(tempDir, "notes.md"), "utf-8")).toBe("ok\n");
|
|
101
|
+
expect(existsSync(join(tempDir, "escape.txt"))).toBe(false);
|
|
102
|
+
expect(existsSync(join(tempDir, "absolute.txt"))).toBe(false);
|
|
103
|
+
});
|
|
93
104
|
});
|