@vellumai/assistant 0.6.3 → 0.6.4
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 +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +5 -13
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/openapi.yaml +982 -72
- package/package.json +4 -6
- package/scripts/generate-openapi.ts +0 -1
- package/scripts/test.sh +73 -18
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +11 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +138 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -0
- package/src/__tests__/config-schema.test.ts +1013 -66
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +98 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -4
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +901 -60
- package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/credential-vault-unit.test.ts +379 -3
- package/src/__tests__/credentials-cli.test.ts +40 -16
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
- package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/headless-browser-interactions.test.ts +43 -0
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +142 -5
- package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +0 -5
- package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
- package/src/__tests__/host-cu-proxy.test.ts +0 -5
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-export-http.test.ts +6 -6
- package/src/__tests__/migration-import-commit-http.test.ts +8 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +2 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
- package/src/__tests__/oauth-providers-routes.test.ts +2 -0
- package/src/__tests__/oauth-store.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
- package/src/__tests__/onboarding-template-contract.test.ts +6 -13
- package/src/__tests__/openai-provider.test.ts +176 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +5 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
- package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +564 -1
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +112 -26
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +18 -7
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -0
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +43 -3
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- package/src/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/types.ts +16 -0
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +0 -1
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +255 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +15 -0
- package/src/cli/commands/oauth/shared.ts +2 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
- package/src/cli/program.ts +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
- package/src/config/bundled-skills/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +2 -2
- package/src/config/bundled-skills/gmail/SKILL.md +53 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +2 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +24 -0
- package/src/config/env.ts +34 -10
- package/src/config/feature-flag-registry.json +46 -14
- package/src/config/loader.ts +26 -12
- package/src/config/schema.ts +35 -10
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +47 -1
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/services.ts +44 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +3 -2
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +45 -12
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +101 -24
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +581 -19
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +11 -1
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +94 -4
- package/src/daemon/conversation-tool-setup.ts +25 -0
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation.ts +86 -28
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +4 -1
- package/src/daemon/handlers/shared.ts +22 -0
- package/src/daemon/handlers/skills.ts +321 -77
- package/src/daemon/host-browser-proxy.ts +2 -1
- package/src/daemon/lifecycle.ts +122 -25
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/conversations.ts +34 -1
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +14 -0
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +16 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +347 -2
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +9 -0
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +12 -3
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +1 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +99 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -5
- package/src/memory/db-init.ts +6 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +27 -18
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +92 -56
- package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
- package/src/oauth/byo-connection.test.ts +18 -1
- package/src/oauth/byo-connection.ts +3 -1
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -2
- package/src/oauth/connection.ts +2 -0
- package/src/oauth/oauth-store.ts +9 -0
- package/src/oauth/platform-connection.test.ts +98 -0
- package/src/oauth/platform-connection.ts +52 -31
- package/src/oauth/seed-providers.ts +7 -0
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +59 -18
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -61
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +9 -0
- package/src/runtime/AGENTS.md +43 -1
- package/src/runtime/__tests__/agent-wake.test.ts +831 -0
- package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -0
- package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
- package/src/runtime/auth/route-policy.ts +30 -5
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +10 -10
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +38 -2
- package/src/runtime/http-server.ts +395 -10
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +1 -0
- package/src/runtime/migrations/migration-wizard.ts +1 -0
- package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
- package/src/runtime/migrations/vbundle-importer.ts +34 -0
- package/src/runtime/pending-interactions.ts +0 -11
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
- package/src/runtime/routes/conversation-management-routes.ts +115 -0
- package/src/runtime/routes/conversation-routes.ts +367 -146
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +3 -14
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +3 -17
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/migration-routes.ts +40 -5
- package/src/runtime/routes/settings-routes.ts +22 -5
- package/src/runtime/routes/skills-routes.ts +76 -7
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +30 -2
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- package/src/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +50 -0
- package/src/security/oauth2.ts +26 -4
- package/src/security/secure-keys.ts +25 -2
- package/src/security/token-manager.ts +8 -0
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +64 -2
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +38 -14
- package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/browser-execution.ts +1163 -23
- package/src/tools/browser/browser-manager.ts +45 -0
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
- package/src/tools/browser/cdp-client/errors.ts +15 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
- package/src/tools/browser/cdp-client/factory.ts +797 -87
- package/src/tools/browser/cdp-client/index.ts +16 -2
- package/src/tools/browser/cdp-client/types.ts +68 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- package/src/tools/terminal/safe-env.ts +10 -2
- package/src/tools/terminal/shell.ts +15 -4
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +17 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +54 -10
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +13 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/cli/commands/browser-relay.ts +0 -466
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/runtime/routes/browser-cdp-routes.ts +0 -229
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
* - Installed skill, traversal path → 400 "Invalid path".
|
|
7
7
|
* - Installed skill, missing file → 404 "File not found".
|
|
8
8
|
* - Installed skill with missing on-disk directory → 404 "Skill directory
|
|
9
|
-
* missing" without consulting the
|
|
10
|
-
* - Uninstalled catalog skill →
|
|
11
|
-
*
|
|
9
|
+
* missing" without consulting the provider chain fallback.
|
|
10
|
+
* - Uninstalled vellum catalog skill → vellum provider returns content.
|
|
11
|
+
* - Uninstalled skills.sh skill → skills.sh provider returns content.
|
|
12
|
+
* - Uninstalled clawhub skill → clawhub provider returns content.
|
|
12
13
|
* - Skill not found anywhere → 404.
|
|
13
14
|
* - Hidden / SKIP_DIRS path segments → rejected with 400 before touching
|
|
14
|
-
* either the installed-skill disk read or the
|
|
15
|
+
* either the installed-skill disk read or the provider chain fallback.
|
|
15
16
|
*
|
|
16
17
|
* The test exercises the daemon handler directly — route wiring is a thin
|
|
17
|
-
* pass-through to this function. The
|
|
18
|
-
* handler's wiring is exercised in isolation
|
|
19
|
-
* platform-mode behavior is covered in `catalog-files.test.ts`.
|
|
18
|
+
* pass-through to this function. The provider modules are mocked so the
|
|
19
|
+
* handler's wiring is exercised in isolation.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -25,8 +25,9 @@ import { join } from "node:path";
|
|
|
25
25
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
26
26
|
|
|
27
27
|
import type { SkillSummary } from "../config/skills.js";
|
|
28
|
+
import type { SlimSkillResponse } from "../daemon/message-types/skills.js";
|
|
28
29
|
import type { SkillFileEntry } from "../skills/catalog-files.js";
|
|
29
|
-
import type {
|
|
30
|
+
import type { SkillFileProvider } from "../skills/skill-file-provider.js";
|
|
30
31
|
|
|
31
32
|
// ---------------------------------------------------------------------------
|
|
32
33
|
// Mocks — must be declared before importing the module under test
|
|
@@ -56,19 +57,39 @@ let mockResolvedSkills: Array<{
|
|
|
56
57
|
summary: SkillSummary;
|
|
57
58
|
state: "enabled" | "disabled";
|
|
58
59
|
}> = [];
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
|
|
61
|
+
// Per-provider mock state
|
|
62
|
+
let mockVellumProvider: SkillFileProvider;
|
|
63
|
+
let mockSkillsshProvider: SkillFileProvider;
|
|
64
|
+
let mockClawhubProvider: SkillFileProvider;
|
|
65
|
+
|
|
66
|
+
// Track provider calls
|
|
67
|
+
const providerReadCalls: Array<{
|
|
68
|
+
provider: string;
|
|
69
|
+
skillId: string;
|
|
70
|
+
path: string;
|
|
71
|
+
}> = [];
|
|
72
|
+
|
|
73
|
+
function makeNoopProvider(name: string): SkillFileProvider {
|
|
74
|
+
return {
|
|
75
|
+
canHandle(): boolean {
|
|
76
|
+
return false;
|
|
77
|
+
},
|
|
78
|
+
async listFiles(): Promise<SkillFileEntry[] | null> {
|
|
79
|
+
return null;
|
|
80
|
+
},
|
|
81
|
+
async readFileContent(
|
|
82
|
+
skillId: string,
|
|
83
|
+
path: string,
|
|
84
|
+
): Promise<SkillFileEntry | null> {
|
|
85
|
+
providerReadCalls.push({ provider: name, skillId, path });
|
|
86
|
+
return null;
|
|
87
|
+
},
|
|
88
|
+
async toSlimSkill(): Promise<SlimSkillResponse | null> {
|
|
89
|
+
return null;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
72
93
|
|
|
73
94
|
mock.module("../config/skills.js", () => ({
|
|
74
95
|
loadSkillCatalog: () => mockResolvedSkills.map((r) => r.summary),
|
|
@@ -91,22 +112,11 @@ mock.module("../config/assistant-feature-flags.js", () => ({
|
|
|
91
112
|
}));
|
|
92
113
|
|
|
93
114
|
mock.module("../skills/catalog-cache.js", () => ({
|
|
94
|
-
getCatalog: async () =>
|
|
115
|
+
getCatalog: async () => [],
|
|
95
116
|
}));
|
|
96
117
|
|
|
97
|
-
// The `catalog-files.js` mock
|
|
98
|
-
//
|
|
99
|
-
// responder. Other exports (`sanitizeRelativePath`,
|
|
100
|
-
// `hasHiddenOrSkippedSegment`, `SKIP_DIRS`, `readCatalogSkillFiles`) are
|
|
101
|
-
// re-implemented inline from the production module so the handler's
|
|
102
|
-
// path-validation code still runs against equivalent logic without
|
|
103
|
-
// recursing back through the mocked module.
|
|
104
|
-
//
|
|
105
|
-
// The spy gives the new "installed skill directory missing" regression
|
|
106
|
-
// test a way to assert that the catalog fallback helper is NOT consulted
|
|
107
|
-
// when `findSkillById` resolves a ghost install — mirroring the
|
|
108
|
-
// `catalogFilesCalls.length === 0` assertion in
|
|
109
|
-
// `skills-files-catalog-fallback.test.ts`.
|
|
118
|
+
// The `catalog-files.js` mock — provides path validation functions inline
|
|
119
|
+
// and delegates provider creation to mock state.
|
|
110
120
|
const INLINE_SKIP_DIRS = new Set(["node_modules", "__pycache__", ".git"]);
|
|
111
121
|
|
|
112
122
|
function inlineSanitizeRelativePath(rawPath: string): string | null {
|
|
@@ -119,7 +129,6 @@ function inlineSanitizeRelativePath(rawPath: string): string | null {
|
|
|
119
129
|
candidate = candidate.slice(2);
|
|
120
130
|
}
|
|
121
131
|
if (candidate.length === 0) return null;
|
|
122
|
-
// posix.normalize without the node import: collapse segments manually.
|
|
123
132
|
const segments: string[] = [];
|
|
124
133
|
for (const seg of candidate.split("/")) {
|
|
125
134
|
if (seg === "" || seg === ".") continue;
|
|
@@ -146,20 +155,48 @@ function inlineHasHiddenOrSkippedSegment(sanitized: string): boolean {
|
|
|
146
155
|
return false;
|
|
147
156
|
}
|
|
148
157
|
|
|
158
|
+
// The provider factory functions are called once at module init and the
|
|
159
|
+
// returned objects are captured in the `fileProviders` array. To allow
|
|
160
|
+
// per-test mock reassignment, return proxy objects that delegate every
|
|
161
|
+
// method call to the CURRENT value of the mutable mock variable.
|
|
149
162
|
mock.module("../skills/catalog-files.js", () => ({
|
|
150
163
|
SKIP_DIRS: INLINE_SKIP_DIRS,
|
|
151
164
|
sanitizeRelativePath: inlineSanitizeRelativePath,
|
|
152
165
|
hasHiddenOrSkippedSegment: inlineHasHiddenOrSkippedSegment,
|
|
166
|
+
catalogSkillToSlim: () => ({}),
|
|
167
|
+
createVellumCatalogProvider: () => ({
|
|
168
|
+
canHandle: (id: string) => mockVellumProvider.canHandle(id),
|
|
169
|
+
listFiles: (id: string) => mockVellumProvider.listFiles(id),
|
|
170
|
+
readFileContent: (id: string, p: string) =>
|
|
171
|
+
mockVellumProvider.readFileContent(id, p),
|
|
172
|
+
toSlimSkill: (id: string) => mockVellumProvider.toSlimSkill(id),
|
|
173
|
+
}),
|
|
153
174
|
readCatalogSkillFiles: async () => null,
|
|
154
|
-
readCatalogSkillFileContent: async (
|
|
155
|
-
catalogFileContentCalls.push({ skillId, path });
|
|
156
|
-
if (mockCatalogFileContentResponder) {
|
|
157
|
-
return mockCatalogFileContentResponder(skillId, path);
|
|
158
|
-
}
|
|
159
|
-
return null;
|
|
160
|
-
},
|
|
175
|
+
readCatalogSkillFileContent: async () => null,
|
|
161
176
|
}));
|
|
162
177
|
|
|
178
|
+
mock.module("../skills/skillssh-files.js", () => ({
|
|
179
|
+
createSkillsShProvider: () => ({
|
|
180
|
+
canHandle: (id: string) => mockSkillsshProvider.canHandle(id),
|
|
181
|
+
listFiles: (id: string) => mockSkillsshProvider.listFiles(id),
|
|
182
|
+
readFileContent: (id: string, p: string) =>
|
|
183
|
+
mockSkillsshProvider.readFileContent(id, p),
|
|
184
|
+
toSlimSkill: (id: string) => mockSkillsshProvider.toSlimSkill(id),
|
|
185
|
+
}),
|
|
186
|
+
}));
|
|
187
|
+
|
|
188
|
+
mock.module("../skills/clawhub-files.js", () => ({
|
|
189
|
+
createClawhubProvider: () => ({
|
|
190
|
+
canHandle: (id: string) => mockClawhubProvider.canHandle(id),
|
|
191
|
+
listFiles: (id: string) => mockClawhubProvider.listFiles(id),
|
|
192
|
+
readFileContent: (id: string, p: string) =>
|
|
193
|
+
mockClawhubProvider.readFileContent(id, p),
|
|
194
|
+
toSlimSkill: (id: string) => mockClawhubProvider.toSlimSkill(id),
|
|
195
|
+
}),
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
mock.module("../skills/skill-file-provider.js", () => ({}));
|
|
199
|
+
|
|
163
200
|
mock.module("../skills/catalog-install.js", () => ({
|
|
164
201
|
installSkillLocally: async () => {},
|
|
165
202
|
upsertSkillsIndex: () => {},
|
|
@@ -244,7 +281,10 @@ mock.module("../daemon/handlers/shared.js", () => ({
|
|
|
244
281
|
// ---------------------------------------------------------------------------
|
|
245
282
|
|
|
246
283
|
import type { SkillOperationContext } from "../daemon/handlers/skills.js";
|
|
247
|
-
import {
|
|
284
|
+
import {
|
|
285
|
+
_resetFileProvidersForTest,
|
|
286
|
+
getSkillFileContent,
|
|
287
|
+
} from "../daemon/handlers/skills.js";
|
|
248
288
|
|
|
249
289
|
// ---------------------------------------------------------------------------
|
|
250
290
|
// Helpers
|
|
@@ -298,10 +338,6 @@ function installedSkill(id: string, directoryPath: string) {
|
|
|
298
338
|
};
|
|
299
339
|
}
|
|
300
340
|
|
|
301
|
-
function catalogSkill(id: string): CatalogSkill {
|
|
302
|
-
return { id, name: id, description: id };
|
|
303
|
-
}
|
|
304
|
-
|
|
305
341
|
// ---------------------------------------------------------------------------
|
|
306
342
|
// Setup / teardown
|
|
307
343
|
// ---------------------------------------------------------------------------
|
|
@@ -309,10 +345,13 @@ function catalogSkill(id: string): CatalogSkill {
|
|
|
309
345
|
beforeEach(() => {
|
|
310
346
|
originalFetch = globalThis.fetch;
|
|
311
347
|
mockResolvedSkills = [];
|
|
312
|
-
mockCatalog = [];
|
|
313
348
|
mockPlatformBaseUrl = "https://platform.test";
|
|
314
|
-
|
|
315
|
-
|
|
349
|
+
providerReadCalls.length = 0;
|
|
350
|
+
mockVellumProvider = makeNoopProvider("vellum");
|
|
351
|
+
mockSkillsshProvider = makeNoopProvider("skillssh");
|
|
352
|
+
mockClawhubProvider = makeNoopProvider("clawhub");
|
|
353
|
+
// Force provider chain re-creation from the (mocked) factory functions
|
|
354
|
+
_resetFileProvidersForTest();
|
|
316
355
|
});
|
|
317
356
|
|
|
318
357
|
afterEach(() => {
|
|
@@ -412,31 +451,27 @@ describe("getSkillFileContent — installed skill", () => {
|
|
|
412
451
|
});
|
|
413
452
|
|
|
414
453
|
// ---------------------------------------------------------------------------
|
|
415
|
-
//
|
|
454
|
+
// Provider chain fallback — uninstalled skill content
|
|
416
455
|
// ---------------------------------------------------------------------------
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// catalog. The helper's own dev-mode / platform-mode behavior is covered
|
|
421
|
-
// in detail in `catalog-files.test.ts`; these tests assert the handler
|
|
422
|
-
// wiring: the helper is invoked with the sanitized path, and the
|
|
423
|
-
// helper's result is returned on the response shape.
|
|
424
|
-
|
|
425
|
-
describe("getSkillFileContent — uninstalled catalog skill", () => {
|
|
426
|
-
test("delegates to readCatalogSkillFileContent and returns the helper payload", async () => {
|
|
427
|
-
// Skill is NOT in the installed catalog, but IS in the platform catalog.
|
|
456
|
+
|
|
457
|
+
describe("getSkillFileContent — uninstalled skill (provider chain)", () => {
|
|
458
|
+
test("delegates to vellum provider and returns the payload", async () => {
|
|
428
459
|
mockResolvedSkills = [];
|
|
429
|
-
mockCatalog = [catalogSkill("remote-skill")];
|
|
430
460
|
installFetchForbidden();
|
|
431
461
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
462
|
+
mockVellumProvider = {
|
|
463
|
+
canHandle: () => true,
|
|
464
|
+
listFiles: async () => null,
|
|
465
|
+
readFileContent: async (_skillId, path) => ({
|
|
466
|
+
path,
|
|
467
|
+
name: "SKILL.md",
|
|
468
|
+
size: 14,
|
|
469
|
+
mimeType: "text/markdown",
|
|
470
|
+
isBinary: false,
|
|
471
|
+
content: "# hello world\n",
|
|
472
|
+
}),
|
|
473
|
+
toSlimSkill: async () => null,
|
|
474
|
+
};
|
|
440
475
|
|
|
441
476
|
const result = await getSkillFileContent(
|
|
442
477
|
"remote-skill",
|
|
@@ -451,33 +486,154 @@ describe("getSkillFileContent — uninstalled catalog skill", () => {
|
|
|
451
486
|
expect(result.mimeType).toBe("text/markdown");
|
|
452
487
|
expect(result.isBinary).toBe(false);
|
|
453
488
|
expect(result.content).toBe("# hello world\n");
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
test("returns 404 when all providers return null", async () => {
|
|
492
|
+
mockResolvedSkills = [];
|
|
493
|
+
installFetchForbidden();
|
|
454
494
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
495
|
+
// All providers return canHandle=false (default noop)
|
|
496
|
+
|
|
497
|
+
const result = await getSkillFileContent(
|
|
498
|
+
"ghost-skill",
|
|
499
|
+
"SKILL.md",
|
|
500
|
+
dummyCtx,
|
|
501
|
+
);
|
|
502
|
+
expect("error" in result).toBe(true);
|
|
503
|
+
if (!("error" in result)) return;
|
|
504
|
+
expect(result.status).toBe(404);
|
|
505
|
+
expect(result.error).toBe("Skill not found");
|
|
458
506
|
});
|
|
459
507
|
|
|
460
|
-
test("
|
|
461
|
-
// Catalog helper returns null for a missing or unreadable file —
|
|
462
|
-
// daemon handler translates that to 404 "File not found".
|
|
508
|
+
test("skills.sh content fallback", async () => {
|
|
463
509
|
mockResolvedSkills = [];
|
|
464
|
-
mockCatalog = [catalogSkill("remote-skill")];
|
|
465
510
|
installFetchForbidden();
|
|
466
511
|
|
|
467
|
-
|
|
512
|
+
// Vellum doesn't handle
|
|
513
|
+
mockVellumProvider = makeNoopProvider("vellum");
|
|
514
|
+
|
|
515
|
+
// skills.sh handles and returns content
|
|
516
|
+
mockSkillsshProvider = {
|
|
517
|
+
canHandle: (id: string) => id.split("/").length >= 3,
|
|
518
|
+
listFiles: async () => null,
|
|
519
|
+
readFileContent: async (_skillId, path) => ({
|
|
520
|
+
path,
|
|
521
|
+
name: "SKILL.md",
|
|
522
|
+
size: 20,
|
|
523
|
+
mimeType: "text/markdown",
|
|
524
|
+
isBinary: false,
|
|
525
|
+
content: "# skillssh content\n",
|
|
526
|
+
}),
|
|
527
|
+
toSlimSkill: async () => null,
|
|
528
|
+
};
|
|
468
529
|
|
|
469
530
|
const result = await getSkillFileContent(
|
|
470
|
-
"
|
|
471
|
-
"
|
|
531
|
+
"owner/repo/my-skill",
|
|
532
|
+
"SKILL.md",
|
|
533
|
+
dummyCtx,
|
|
534
|
+
);
|
|
535
|
+
expect("error" in result).toBe(false);
|
|
536
|
+
if ("error" in result) return;
|
|
537
|
+
expect(result.content).toBe("# skillssh content\n");
|
|
538
|
+
expect(result.path).toBe("SKILL.md");
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test("clawhub content fallback", async () => {
|
|
542
|
+
mockResolvedSkills = [];
|
|
543
|
+
installFetchForbidden();
|
|
544
|
+
|
|
545
|
+
// Vellum and skills.sh don't handle
|
|
546
|
+
mockVellumProvider = makeNoopProvider("vellum");
|
|
547
|
+
mockSkillsshProvider = makeNoopProvider("skillssh");
|
|
548
|
+
|
|
549
|
+
// Clawhub handles and returns content
|
|
550
|
+
mockClawhubProvider = {
|
|
551
|
+
canHandle: () => true,
|
|
552
|
+
listFiles: async () => null,
|
|
553
|
+
readFileContent: async (_skillId, path) => ({
|
|
554
|
+
path,
|
|
555
|
+
name: "SKILL.md",
|
|
556
|
+
size: 22,
|
|
557
|
+
mimeType: "text/markdown",
|
|
558
|
+
isBinary: false,
|
|
559
|
+
content: "# clawhub content yo\n",
|
|
560
|
+
}),
|
|
561
|
+
toSlimSkill: async () => null,
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
const result = await getSkillFileContent("cool-tool", "SKILL.md", dummyCtx);
|
|
565
|
+
expect("error" in result).toBe(false);
|
|
566
|
+
if ("error" in result) return;
|
|
567
|
+
expect(result.content).toBe("# clawhub content yo\n");
|
|
568
|
+
expect(result.path).toBe("SKILL.md");
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
test("returns 'File not found' when provider canHandle returns true but readFileContent returns null", async () => {
|
|
572
|
+
mockResolvedSkills = [];
|
|
573
|
+
installFetchForbidden();
|
|
574
|
+
|
|
575
|
+
// Vellum provider claims this skill but returns null for the specific file
|
|
576
|
+
mockVellumProvider = {
|
|
577
|
+
canHandle: () => true,
|
|
578
|
+
listFiles: async () => null,
|
|
579
|
+
readFileContent: async () => null,
|
|
580
|
+
toSlimSkill: async () => null,
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const result = await getSkillFileContent(
|
|
584
|
+
"known-skill",
|
|
585
|
+
"nonexistent.txt",
|
|
472
586
|
dummyCtx,
|
|
473
587
|
);
|
|
474
588
|
expect("error" in result).toBe(true);
|
|
475
589
|
if (!("error" in result)) return;
|
|
476
590
|
expect(result.status).toBe(404);
|
|
477
591
|
expect(result.error).toBe("File not found");
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test("stop-on-first-match: does not try clawhub when vellum canHandle returns true", async () => {
|
|
595
|
+
mockResolvedSkills = [];
|
|
596
|
+
installFetchForbidden();
|
|
597
|
+
|
|
598
|
+
const clawhubReadCalls: string[] = [];
|
|
599
|
+
|
|
600
|
+
// Vellum provider claims the skill but returns null for file content
|
|
601
|
+
mockVellumProvider = {
|
|
602
|
+
canHandle: () => true,
|
|
603
|
+
listFiles: async () => null,
|
|
604
|
+
readFileContent: async () => null,
|
|
605
|
+
toSlimSkill: async () => null,
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
// Clawhub would return content if asked, but should NOT be consulted
|
|
609
|
+
mockClawhubProvider = {
|
|
610
|
+
canHandle: () => true,
|
|
611
|
+
listFiles: async () => null,
|
|
612
|
+
readFileContent: async (_skillId, path) => {
|
|
613
|
+
clawhubReadCalls.push(path);
|
|
614
|
+
return {
|
|
615
|
+
path,
|
|
616
|
+
name: "SKILL.md",
|
|
617
|
+
size: 10,
|
|
618
|
+
mimeType: "text/markdown",
|
|
619
|
+
isBinary: false,
|
|
620
|
+
content: "# from clawhub\n",
|
|
621
|
+
};
|
|
622
|
+
},
|
|
623
|
+
toSlimSkill: async () => null,
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
const result = await getSkillFileContent(
|
|
627
|
+
"simple-slug",
|
|
628
|
+
"SKILL.md",
|
|
629
|
+
dummyCtx,
|
|
630
|
+
);
|
|
631
|
+
// Should be "File not found" (vellum handled but returned null)
|
|
632
|
+
expect("error" in result).toBe(true);
|
|
633
|
+
if (!("error" in result)) return;
|
|
634
|
+
expect(result.error).toBe("File not found");
|
|
635
|
+
// Clawhub should NOT have been called
|
|
636
|
+
expect(clawhubReadCalls).toEqual([]);
|
|
481
637
|
});
|
|
482
638
|
});
|
|
483
639
|
|
|
@@ -486,9 +642,8 @@ describe("getSkillFileContent — uninstalled catalog skill", () => {
|
|
|
486
642
|
// ---------------------------------------------------------------------------
|
|
487
643
|
|
|
488
644
|
describe("getSkillFileContent — skill not found", () => {
|
|
489
|
-
test("returns 404 when the skill is neither installed nor
|
|
645
|
+
test("returns 404 when the skill is neither installed nor handled by any provider", async () => {
|
|
490
646
|
mockResolvedSkills = [];
|
|
491
|
-
mockCatalog = [];
|
|
492
647
|
installFetchForbidden();
|
|
493
648
|
|
|
494
649
|
const result = await getSkillFileContent(
|
|
@@ -506,38 +661,30 @@ describe("getSkillFileContent — skill not found", () => {
|
|
|
506
661
|
// ---------------------------------------------------------------------------
|
|
507
662
|
// Installed skill with missing directory (ghost install)
|
|
508
663
|
// ---------------------------------------------------------------------------
|
|
509
|
-
//
|
|
510
|
-
// When `findSkillById` resolves a skill as installed but the on-disk
|
|
511
|
-
// directory has disappeared (corrupted install, mid-delete race,
|
|
512
|
-
// external unmount), the handler must return a distinct 404 "Skill
|
|
513
|
-
// directory missing" instead of falling through to the catalog path.
|
|
514
|
-
// Falling through would flip the content response to a catalog payload
|
|
515
|
-
// even though `listSkillsWithCatalog` still classifies the same id as
|
|
516
|
-
// `kind: "installed"`, breaking the `isInstalled` contract between the
|
|
517
|
-
// listing and content responses. Mirrors the same fix on
|
|
518
|
-
// `getSkillFiles` verified by
|
|
519
|
-
// `skills-files-catalog-fallback.test.ts`.
|
|
520
664
|
|
|
521
665
|
describe("getSkillFileContent — installed skill with missing directory", () => {
|
|
522
|
-
test("returns 404 without consulting
|
|
666
|
+
test("returns 404 without consulting providers when the installed dir is gone", async () => {
|
|
523
667
|
mockResolvedSkills = [
|
|
524
668
|
installedSkill(
|
|
525
669
|
"ghost-installed",
|
|
526
670
|
"/tmp/definitely-does-not-exist-" + Date.now(),
|
|
527
671
|
),
|
|
528
672
|
];
|
|
529
|
-
// Even if
|
|
530
|
-
// fall through.
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
673
|
+
// Even if a provider would return content, the handler must NOT
|
|
674
|
+
// fall through.
|
|
675
|
+
mockVellumProvider = {
|
|
676
|
+
canHandle: () => true,
|
|
677
|
+
listFiles: async () => null,
|
|
678
|
+
readFileContent: async () => ({
|
|
679
|
+
path: "SKILL.md",
|
|
680
|
+
name: "SKILL.md",
|
|
681
|
+
size: 10,
|
|
682
|
+
mimeType: "text/markdown",
|
|
683
|
+
isBinary: false,
|
|
684
|
+
content: "# from provider\n",
|
|
685
|
+
}),
|
|
686
|
+
toSlimSkill: async () => null,
|
|
687
|
+
};
|
|
541
688
|
installFetchForbidden();
|
|
542
689
|
|
|
543
690
|
const result = await getSkillFileContent(
|
|
@@ -551,27 +698,17 @@ describe("getSkillFileContent — installed skill with missing directory", () =>
|
|
|
551
698
|
expect(result.status).toBe(404);
|
|
552
699
|
expect(result.error).toContain("ghost-installed");
|
|
553
700
|
expect(result.error).toContain("directory missing");
|
|
554
|
-
//
|
|
555
|
-
expect(
|
|
701
|
+
// Provider chain must not have been consulted.
|
|
702
|
+
expect(providerReadCalls).toEqual([]);
|
|
556
703
|
});
|
|
557
704
|
});
|
|
558
705
|
|
|
559
706
|
// ---------------------------------------------------------------------------
|
|
560
707
|
// Hidden / SKIP_DIRS path rejection
|
|
561
708
|
// ---------------------------------------------------------------------------
|
|
562
|
-
//
|
|
563
|
-
// The daemon handler rejects paths containing dotfile segments (`.env`,
|
|
564
|
-
// `.git/`) or SKIP_DIRS segments (`node_modules`, `__pycache__`) with a
|
|
565
|
-
// 400 "Invalid path" BEFORE any disk read or network round-trip, matching
|
|
566
|
-
// the file-listing endpoint that hides these entries. This applies
|
|
567
|
-
// regardless of whether the skill is installed locally or only available
|
|
568
|
-
// via the catalog.
|
|
569
709
|
|
|
570
710
|
describe("getSkillFileContent — hidden / SKIP_DIRS rejection", () => {
|
|
571
711
|
test("rejects dotfile reads from an installed skill with 400 Invalid path", async () => {
|
|
572
|
-
// Set up an installed skill where a real `.env` file exists on disk.
|
|
573
|
-
// Without the hidden-segment rejection, the handler would happily
|
|
574
|
-
// read its content because sanitizeRelativePath accepts `.env`.
|
|
575
712
|
const skillDir = makeTempSkillDir("leaky-skill");
|
|
576
713
|
writeFile(skillDir, "SKILL.md", "# ok\n");
|
|
577
714
|
writeFile(skillDir, ".env", "SECRET=abc\n");
|
|
@@ -585,37 +722,33 @@ describe("getSkillFileContent — hidden / SKIP_DIRS rejection", () => {
|
|
|
585
722
|
expect(result.error).toBe("Invalid path");
|
|
586
723
|
});
|
|
587
724
|
|
|
588
|
-
test("rejects dotfile reads for an uninstalled
|
|
589
|
-
// Same dotfile attack, but the skill id is NOT installed — only
|
|
590
|
-
// present in the Vellum catalog. The rejection must run in the daemon
|
|
591
|
-
// handler BEFORE the catalog fallback, so the catalog helper should
|
|
592
|
-
// never be consulted. We prime the responder with content that WOULD
|
|
593
|
-
// succeed to prove that even though the fallback path would return a
|
|
594
|
-
// payload, the daemon short-circuits first.
|
|
725
|
+
test("rejects dotfile reads for an uninstalled skill before any provider read", async () => {
|
|
595
726
|
mockResolvedSkills = []; // not installed
|
|
596
|
-
mockCatalog = [catalogSkill("catalog-leaky")];
|
|
597
727
|
installFetchForbidden();
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
728
|
+
mockVellumProvider = {
|
|
729
|
+
canHandle: () => true,
|
|
730
|
+
listFiles: async () => null,
|
|
731
|
+
readFileContent: async () => ({
|
|
732
|
+
path: ".env",
|
|
733
|
+
name: ".env",
|
|
734
|
+
size: 12,
|
|
735
|
+
mimeType: "text/plain",
|
|
736
|
+
isBinary: false,
|
|
737
|
+
content: "SECRET=xyz\n",
|
|
738
|
+
}),
|
|
739
|
+
toSlimSkill: async () => null,
|
|
740
|
+
};
|
|
606
741
|
|
|
607
742
|
const result = await getSkillFileContent("catalog-leaky", ".env", dummyCtx);
|
|
608
743
|
expect("error" in result).toBe(true);
|
|
609
744
|
if (!("error" in result)) return;
|
|
610
745
|
expect(result.status).toBe(400);
|
|
611
746
|
expect(result.error).toBe("Invalid path");
|
|
612
|
-
// The hidden-segment check must run before the
|
|
613
|
-
expect(
|
|
747
|
+
// The hidden-segment check must run before the provider chain.
|
|
748
|
+
expect(providerReadCalls).toEqual([]);
|
|
614
749
|
});
|
|
615
750
|
|
|
616
751
|
test("rejects paths whose parent directory is a dotfile segment", async () => {
|
|
617
|
-
// `.git/config` and `docs/.hidden/file.md` both contain hidden
|
|
618
|
-
// segments even though the leaf isn't a dotfile.
|
|
619
752
|
const skillDir = makeTempSkillDir("my-skill");
|
|
620
753
|
writeFile(skillDir, "SKILL.md", "# ok\n");
|
|
621
754
|
mockResolvedSkills = [installedSkill("my-skill", skillDir)];
|
|
@@ -650,8 +783,6 @@ describe("getSkillFileContent — hidden / SKIP_DIRS rejection", () => {
|
|
|
650
783
|
});
|
|
651
784
|
|
|
652
785
|
test("regular SKILL.md still reads successfully (sanity)", async () => {
|
|
653
|
-
// Guards against collateral damage from the hidden/SKIP_DIRS filter:
|
|
654
|
-
// a normal, non-hidden path must continue to work unchanged.
|
|
655
786
|
const skillDir = makeTempSkillDir("healthy-skill");
|
|
656
787
|
writeFile(skillDir, "SKILL.md", "# hello\n");
|
|
657
788
|
mockResolvedSkills = [installedSkill("healthy-skill", skillDir)];
|