@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.
Files changed (667) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +5 -13
  4. package/docs/backup-troubleshooting.md +52 -0
  5. package/docs/browser-use-architecture-phase2.md +174 -0
  6. package/docs/stt-provider-onboarding.md +120 -0
  7. package/knip.json +12 -2
  8. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  9. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  10. package/openapi.yaml +982 -72
  11. package/package.json +4 -6
  12. package/scripts/generate-openapi.ts +0 -1
  13. package/scripts/test.sh +73 -18
  14. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  15. package/src/__tests__/agent-loop.test.ts +123 -0
  16. package/src/__tests__/anthropic-provider.test.ts +263 -10
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  18. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  19. package/src/__tests__/browser-fill-credential.test.ts +11 -0
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  22. package/src/__tests__/btw-routes.test.ts +7 -0
  23. package/src/__tests__/call-controller.test.ts +581 -20
  24. package/src/__tests__/catalog-files.test.ts +138 -0
  25. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  26. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  27. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  28. package/src/__tests__/checker.test.ts +157 -10
  29. package/src/__tests__/clawhub-files.test.ts +347 -0
  30. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  31. package/src/__tests__/config-analysis.test.ts +100 -0
  32. package/src/__tests__/config-schema.test.ts +1013 -66
  33. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  34. package/src/__tests__/config-watcher.test.ts +43 -8
  35. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  36. package/src/__tests__/contacts-write.test.ts +197 -0
  37. package/src/__tests__/context-window-manager.test.ts +88 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +98 -2
  41. package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
  42. package/src/__tests__/conversation-error.test.ts +70 -0
  43. package/src/__tests__/conversation-history-web-search.test.ts +11 -4
  44. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  45. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  46. package/src/__tests__/conversation-list-source.test.ts +145 -0
  47. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  49. package/src/__tests__/conversation-queue.test.ts +901 -60
  50. package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
  51. package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
  52. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  53. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  59. package/src/__tests__/credential-health-service.test.ts +352 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  61. package/src/__tests__/credential-vault-unit.test.ts +379 -3
  62. package/src/__tests__/credentials-cli.test.ts +40 -16
  63. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  65. package/src/__tests__/device-id.test.ts +112 -0
  66. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  67. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  68. package/src/__tests__/email-html-renderer.test.ts +71 -0
  69. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  70. package/src/__tests__/emit-event-signal.test.ts +71 -0
  71. package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
  72. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  73. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  74. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  75. package/src/__tests__/gemini-provider.test.ts +64 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  77. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  78. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  79. package/src/__tests__/gmail-preferences.test.ts +117 -0
  80. package/src/__tests__/headless-browser-interactions.test.ts +43 -0
  81. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  82. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  83. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  84. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  85. package/src/__tests__/heartbeat-service.test.ts +70 -17
  86. package/src/__tests__/home-state-routes.test.ts +162 -0
  87. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  88. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  89. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  90. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  91. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  92. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  93. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  94. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  95. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  96. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  97. package/src/__tests__/llm-usage-store.test.ts +363 -0
  98. package/src/__tests__/media-stream-output.test.ts +555 -0
  99. package/src/__tests__/media-stream-parser.test.ts +374 -0
  100. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  101. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  102. package/src/__tests__/media-turn-detector.test.ts +440 -0
  103. package/src/__tests__/message-queue.test.ts +125 -0
  104. package/src/__tests__/migration-export-http.test.ts +6 -6
  105. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  106. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  107. package/src/__tests__/migration-validate-http.test.ts +3 -3
  108. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  109. package/src/__tests__/model-intents.test.ts +2 -2
  110. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  111. package/src/__tests__/oauth-cli.test.ts +2 -0
  112. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  115. package/src/__tests__/oauth-store.test.ts +85 -0
  116. package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
  117. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  118. package/src/__tests__/openai-provider.test.ts +176 -0
  119. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  120. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  121. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  122. package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
  123. package/src/__tests__/persona-resolver.test.ts +251 -0
  124. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  125. package/src/__tests__/platform.test.ts +92 -1
  126. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  127. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  128. package/src/__tests__/pricing.test.ts +174 -0
  129. package/src/__tests__/qdrant-manager.test.ts +29 -8
  130. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  131. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  132. package/src/__tests__/relay-server.test.ts +423 -5
  133. package/src/__tests__/search-skills-unified.test.ts +118 -0
  134. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  135. package/src/__tests__/secure-keys.test.ts +107 -0
  136. package/src/__tests__/send-endpoint-busy.test.ts +5 -1
  137. package/src/__tests__/sequence-store.test.ts +1 -1
  138. package/src/__tests__/server-history-render.test.ts +49 -0
  139. package/src/__tests__/settings-routes.test.ts +201 -0
  140. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  141. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  142. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  143. package/src/__tests__/skills.test.ts +5 -2
  144. package/src/__tests__/skillssh-files.test.ts +446 -0
  145. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  146. package/src/__tests__/slack-channel-config.test.ts +564 -1
  147. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  148. package/src/__tests__/stt-stream-session.test.ts +535 -0
  149. package/src/__tests__/system-prompt.test.ts +112 -26
  150. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  151. package/src/__tests__/terminal-tools.test.ts +18 -7
  152. package/src/__tests__/test-preload.ts +18 -0
  153. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  155. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  156. package/src/__tests__/tool-executor.test.ts +33 -24
  157. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  158. package/src/__tests__/trust-store.test.ts +7 -1
  159. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  160. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  161. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  162. package/src/__tests__/twilio-routes.test.ts +376 -0
  163. package/src/__tests__/unicode.test.ts +293 -0
  164. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  165. package/src/__tests__/update-bulletin.test.ts +206 -5
  166. package/src/__tests__/usage-routes.test.ts +25 -4
  167. package/src/__tests__/user-reference.test.ts +46 -61
  168. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  169. package/src/__tests__/voice-config-update.test.ts +403 -0
  170. package/src/__tests__/voice-quality.test.ts +434 -19
  171. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  172. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  173. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  174. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  175. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  176. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  177. package/src/__tests__/workspace-policy.test.ts +2 -0
  178. package/src/agent/image-optimize.ts +24 -12
  179. package/src/agent/loop.ts +43 -3
  180. package/src/backup/__tests__/backup-key.test.ts +152 -0
  181. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  182. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  183. package/src/backup/__tests__/local-writer.test.ts +218 -0
  184. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  185. package/src/backup/__tests__/paths.test.ts +300 -0
  186. package/src/backup/__tests__/restore.test.ts +498 -0
  187. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  188. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  189. package/src/backup/backup-key.ts +137 -0
  190. package/src/backup/backup-worker.ts +459 -0
  191. package/src/backup/list-snapshots.ts +147 -0
  192. package/src/backup/local-writer.ts +133 -0
  193. package/src/backup/offsite-writer.ts +222 -0
  194. package/src/backup/paths.ts +226 -0
  195. package/src/backup/restore.ts +322 -0
  196. package/src/backup/snapshot-lock.ts +431 -0
  197. package/src/backup/stream-crypt.ts +263 -0
  198. package/src/bundler/package-resolver.ts +4 -0
  199. package/src/calls/audio-store.ts +11 -5
  200. package/src/calls/call-controller.ts +226 -71
  201. package/src/calls/call-domain.ts +9 -0
  202. package/src/calls/call-speech-output.ts +190 -0
  203. package/src/calls/call-transport.ts +77 -0
  204. package/src/calls/media-stream-audio-transcode.ts +173 -0
  205. package/src/calls/media-stream-output.ts +660 -0
  206. package/src/calls/media-stream-parser.ts +300 -0
  207. package/src/calls/media-stream-protocol.ts +166 -0
  208. package/src/calls/media-stream-server.ts +592 -0
  209. package/src/calls/media-stream-stt-session.ts +460 -0
  210. package/src/calls/media-turn-detector.ts +230 -0
  211. package/src/calls/relay-server.ts +90 -75
  212. package/src/calls/resolve-call-tts-provider.ts +136 -0
  213. package/src/calls/telephony-stt-routing.ts +145 -0
  214. package/src/calls/tts-call-strategy.ts +161 -0
  215. package/src/calls/tts-text-sanitizer.ts +32 -16
  216. package/src/calls/twilio-routes.ts +281 -17
  217. package/src/calls/voice-quality.ts +78 -35
  218. package/src/calls/voice-session-bridge.ts +8 -1
  219. package/src/channels/types.ts +16 -0
  220. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  221. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  222. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  223. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  224. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  225. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  226. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  227. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  228. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  229. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  230. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  231. package/src/cli/commands/backup.ts +993 -0
  232. package/src/cli/commands/conversations.ts +77 -0
  233. package/src/cli/commands/credentials.ts +0 -1
  234. package/src/cli/commands/domain.ts +210 -0
  235. package/src/cli/commands/email.ts +255 -3
  236. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  237. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  238. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  239. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  240. package/src/cli/commands/oauth/mode.ts +12 -3
  241. package/src/cli/commands/oauth/providers.ts +15 -0
  242. package/src/cli/commands/oauth/shared.ts +2 -1
  243. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
  244. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  245. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  246. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  247. package/src/cli/program.ts +30 -4
  248. package/src/config/__tests__/backup-schema.test.ts +134 -0
  249. package/src/config/assistant-feature-flags.ts +61 -62
  250. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  251. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  252. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  253. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  254. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  255. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  256. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  258. package/src/config/bundled-skills/gmail/SKILL.md +53 -7
  259. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  260. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  261. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  262. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  263. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  264. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  265. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  266. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  267. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  268. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  269. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  270. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  271. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  272. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  273. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  274. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  275. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  276. package/src/config/bundled-skills/outlook/SKILL.md +2 -2
  277. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  278. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  279. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  280. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  281. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  282. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  283. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  284. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  285. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  286. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  287. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  288. package/src/config/bundled-tool-registry.ts +8 -0
  289. package/src/config/env-registry.ts +24 -0
  290. package/src/config/env.ts +34 -10
  291. package/src/config/feature-flag-registry.json +46 -14
  292. package/src/config/loader.ts +26 -12
  293. package/src/config/schema.ts +35 -10
  294. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  295. package/src/config/schemas/analysis.ts +51 -0
  296. package/src/config/schemas/backup.ts +72 -0
  297. package/src/config/schemas/calls.ts +1 -26
  298. package/src/config/schemas/elevenlabs.ts +0 -59
  299. package/src/config/schemas/filing.ts +47 -7
  300. package/src/config/schemas/heartbeat.ts +27 -5
  301. package/src/config/schemas/host-browser.ts +47 -1
  302. package/src/config/schemas/inference.ts +1 -1
  303. package/src/config/schemas/memory-lifecycle.ts +14 -2
  304. package/src/config/schemas/services.ts +44 -0
  305. package/src/config/schemas/stt.ts +59 -0
  306. package/src/config/schemas/tts.ts +230 -0
  307. package/src/config/schemas/updates.ts +14 -0
  308. package/src/config/skills.ts +4 -0
  309. package/src/config/types.ts +4 -0
  310. package/src/contacts/contact-store.ts +56 -11
  311. package/src/contacts/contacts-write.ts +38 -1
  312. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  313. package/src/context/tool-result-truncation.ts +2 -1
  314. package/src/context/window-manager.ts +45 -12
  315. package/src/credential-execution/executable-discovery.ts +12 -2
  316. package/src/credential-execution/process-manager.ts +33 -2
  317. package/src/credential-health/credential-health-service.ts +366 -0
  318. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  319. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  320. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  321. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  322. package/src/daemon/config-watcher.ts +99 -5
  323. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  324. package/src/daemon/conversation-agent-loop.ts +101 -24
  325. package/src/daemon/conversation-error.ts +11 -0
  326. package/src/daemon/conversation-history.ts +40 -6
  327. package/src/daemon/conversation-launch.ts +220 -0
  328. package/src/daemon/conversation-lifecycle.ts +59 -9
  329. package/src/daemon/conversation-messaging.ts +37 -3
  330. package/src/daemon/conversation-notifiers.ts +5 -0
  331. package/src/daemon/conversation-process.ts +581 -19
  332. package/src/daemon/conversation-queue-manager.ts +24 -0
  333. package/src/daemon/conversation-runtime-assembly.ts +11 -1
  334. package/src/daemon/conversation-slash.ts +36 -0
  335. package/src/daemon/conversation-surfaces.ts +94 -4
  336. package/src/daemon/conversation-tool-setup.ts +25 -0
  337. package/src/daemon/conversation-usage.ts +7 -4
  338. package/src/daemon/conversation.ts +86 -28
  339. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  340. package/src/daemon/handlers/conversations.ts +4 -1
  341. package/src/daemon/handlers/shared.ts +22 -0
  342. package/src/daemon/handlers/skills.ts +321 -77
  343. package/src/daemon/host-browser-proxy.ts +2 -1
  344. package/src/daemon/lifecycle.ts +122 -25
  345. package/src/daemon/message-protocol.ts +6 -0
  346. package/src/daemon/message-types/conversations.ts +34 -1
  347. package/src/daemon/message-types/home.ts +40 -0
  348. package/src/daemon/message-types/meet.ts +143 -0
  349. package/src/daemon/message-types/messages.ts +14 -0
  350. package/src/daemon/message-types/schedules.ts +34 -2
  351. package/src/daemon/message-types/skills.ts +16 -0
  352. package/src/daemon/message-types/surfaces.ts +2 -0
  353. package/src/daemon/server.ts +347 -2
  354. package/src/daemon/shutdown-handlers.ts +32 -4
  355. package/src/daemon/shutdown-registry.ts +40 -0
  356. package/src/daemon/tool-side-effects.ts +9 -0
  357. package/src/email/html-renderer.ts +76 -0
  358. package/src/heartbeat/heartbeat-service.ts +93 -7
  359. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  360. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  361. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  362. package/src/home/__tests__/feed-types.test.ts +275 -0
  363. package/src/home/__tests__/feed-writer.test.ts +688 -0
  364. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  365. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  366. package/src/home/__tests__/progress-formula.test.ts +213 -0
  367. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  368. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  369. package/src/home/assistant-feed-authoring.ts +124 -0
  370. package/src/home/emit-feed-event.ts +158 -0
  371. package/src/home/feed-scheduler.ts +247 -0
  372. package/src/home/feed-types.ts +181 -0
  373. package/src/home/feed-writer.ts +469 -0
  374. package/src/home/platform-gmail-digest.ts +163 -0
  375. package/src/home/progress-formula.ts +86 -0
  376. package/src/home/relationship-state-writer.ts +824 -0
  377. package/src/home/relationship-state.ts +143 -0
  378. package/src/home/rollup-producer.ts +384 -0
  379. package/src/hooks/runner.ts +7 -0
  380. package/src/inbound/platform-callback-registration.ts +12 -3
  381. package/src/inbound/public-ingress-urls.ts +12 -0
  382. package/src/instrument.ts +1 -1
  383. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  384. package/src/ipc/cli-client.ts +151 -0
  385. package/src/ipc/cli-server.ts +234 -0
  386. package/src/ipc/gateway-client.ts +180 -0
  387. package/src/ipc/routes/index.ts +5 -0
  388. package/src/ipc/routes/wake-conversation.ts +19 -0
  389. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  390. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  391. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  392. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  393. package/src/memory/app-store.ts +1 -1
  394. package/src/memory/attachments-store.ts +70 -0
  395. package/src/memory/auto-analysis-enqueue.ts +127 -0
  396. package/src/memory/auto-analysis-guard.ts +27 -0
  397. package/src/memory/cleanup-schedule-state.ts +37 -0
  398. package/src/memory/conversation-analyze-job.ts +73 -0
  399. package/src/memory/conversation-crud.ts +99 -0
  400. package/src/memory/conversation-disk-view.ts +7 -0
  401. package/src/memory/conversation-group-migration.ts +34 -2
  402. package/src/memory/conversation-queries.ts +6 -5
  403. package/src/memory/db-init.ts +6 -0
  404. package/src/memory/db-maintenance.ts +108 -0
  405. package/src/memory/db.ts +1 -0
  406. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  407. package/src/memory/graph/extraction.test.ts +23 -0
  408. package/src/memory/graph/extraction.ts +8 -0
  409. package/src/memory/graph/retriever.ts +27 -18
  410. package/src/memory/graph/scoring.test.ts +186 -0
  411. package/src/memory/graph/scoring.ts +31 -1
  412. package/src/memory/graph/tools.ts +1 -1
  413. package/src/memory/group-crud.ts +6 -1
  414. package/src/memory/indexer.ts +95 -16
  415. package/src/memory/job-handlers/cleanup.ts +11 -8
  416. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  417. package/src/memory/jobs-store.ts +64 -4
  418. package/src/memory/jobs-worker.ts +22 -9
  419. package/src/memory/llm-usage-store.ts +92 -56
  420. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  421. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  422. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  423. package/src/memory/migrations/index.ts +6 -0
  424. package/src/memory/migrations/registry.ts +8 -0
  425. package/src/memory/qdrant-manager.ts +43 -16
  426. package/src/memory/schema/conversations.ts +2 -0
  427. package/src/memory/schema/oauth.ts +3 -0
  428. package/src/memory/usage-buckets.ts +396 -0
  429. package/src/messaging/providers/gmail/client.ts +57 -6
  430. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  431. package/src/messaging/providers/slack/adapter.ts +143 -38
  432. package/src/messaging/providers/slack/client.ts +16 -0
  433. package/src/messaging/providers/slack/types.ts +4 -0
  434. package/src/notifications/decision-engine.ts +3 -3
  435. package/src/notifications/signal.ts +5 -0
  436. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  437. package/src/oauth/byo-connection.test.ts +18 -1
  438. package/src/oauth/byo-connection.ts +3 -1
  439. package/src/oauth/connect-orchestrator.ts +2 -0
  440. package/src/oauth/connection-resolver.ts +6 -2
  441. package/src/oauth/connection.ts +2 -0
  442. package/src/oauth/oauth-store.ts +9 -0
  443. package/src/oauth/platform-connection.test.ts +98 -0
  444. package/src/oauth/platform-connection.ts +52 -31
  445. package/src/oauth/seed-providers.ts +7 -0
  446. package/src/permissions/checker.ts +16 -6
  447. package/src/permissions/defaults.ts +49 -1
  448. package/src/permissions/trust-store.ts +3 -3
  449. package/src/permissions/workspace-policy.ts +3 -0
  450. package/src/platform/client.test.ts +10 -0
  451. package/src/platform/sync-identity.ts +129 -0
  452. package/src/prompts/persona-resolver.ts +126 -2
  453. package/src/prompts/system-prompt.ts +59 -18
  454. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  455. package/src/prompts/templates/SOUL.md +3 -1
  456. package/src/prompts/templates/UPDATES.md +12 -0
  457. package/src/prompts/templates/channels/slack.md +20 -0
  458. package/src/prompts/update-bulletin-format.ts +26 -9
  459. package/src/prompts/update-bulletin.ts +34 -23
  460. package/src/prompts/user-reference.ts +20 -17
  461. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  462. package/src/providers/anthropic/client.ts +157 -61
  463. package/src/providers/fireworks/client.ts +2 -2
  464. package/src/providers/gemini/client.ts +9 -1
  465. package/src/providers/model-catalog.ts +6 -0
  466. package/src/providers/model-intents.ts +4 -4
  467. package/src/providers/ollama/client.ts +2 -2
  468. package/src/providers/openai/chat-completions-provider.ts +474 -0
  469. package/src/providers/openai/client.ts +25 -440
  470. package/src/providers/openai/responses-provider.ts +502 -0
  471. package/src/providers/openrouter/client.ts +101 -4
  472. package/src/providers/provider-secret-catalog.ts +139 -0
  473. package/src/providers/registry.ts +2 -2
  474. package/src/providers/retry.ts +14 -3
  475. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  476. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  477. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  478. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  479. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  480. package/src/providers/speech-to-text/deepgram.ts +115 -0
  481. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  482. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  483. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  484. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  485. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  486. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  487. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  488. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  489. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  490. package/src/providers/speech-to-text/resolve.ts +386 -6
  491. package/src/providers/types.ts +9 -0
  492. package/src/runtime/AGENTS.md +43 -1
  493. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  494. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  495. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  496. package/src/runtime/agent-wake.ts +512 -0
  497. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  498. package/src/runtime/auth/route-policy.ts +30 -5
  499. package/src/runtime/auth/token-service.ts +56 -1
  500. package/src/runtime/btw-sidechain.ts +2 -0
  501. package/src/runtime/capability-tokens.ts +10 -10
  502. package/src/runtime/channel-invite-transport.ts +1 -1
  503. package/src/runtime/channel-invite-transports/email.ts +14 -6
  504. package/src/runtime/channel-readiness-service.ts +12 -22
  505. package/src/runtime/chrome-extension-registry.ts +38 -2
  506. package/src/runtime/http-server.ts +395 -10
  507. package/src/runtime/http-types.ts +6 -2
  508. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  509. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  510. package/src/runtime/migrations/migration-transport.ts +1 -0
  511. package/src/runtime/migrations/migration-wizard.ts +1 -0
  512. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  513. package/src/runtime/migrations/vbundle-importer.ts +34 -0
  514. package/src/runtime/pending-interactions.ts +0 -11
  515. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  516. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  517. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  518. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  519. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  520. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  521. package/src/runtime/routes/app-management-routes.ts +12 -18
  522. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  523. package/src/runtime/routes/attachment-routes.ts +216 -17
  524. package/src/runtime/routes/backup-routes.ts +519 -0
  525. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  526. package/src/runtime/routes/btw-routes.ts +8 -6
  527. package/src/runtime/routes/contact-routes.test.ts +298 -0
  528. package/src/runtime/routes/contact-routes.ts +132 -5
  529. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  530. package/src/runtime/routes/conversation-management-routes.ts +115 -0
  531. package/src/runtime/routes/conversation-routes.ts +367 -146
  532. package/src/runtime/routes/filing-routes.ts +93 -0
  533. package/src/runtime/routes/home-feed-routes.ts +334 -0
  534. package/src/runtime/routes/home-state-routes.ts +138 -0
  535. package/src/runtime/routes/host-browser-routes.ts +3 -14
  536. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  537. package/src/runtime/routes/identity-routes.ts +3 -17
  538. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  539. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  540. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  541. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  542. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  543. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  544. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  545. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  546. package/src/runtime/routes/migration-routes.ts +40 -5
  547. package/src/runtime/routes/settings-routes.ts +22 -5
  548. package/src/runtime/routes/skills-routes.ts +76 -7
  549. package/src/runtime/routes/stt-routes.ts +233 -0
  550. package/src/runtime/routes/surface-action-routes.ts +41 -2
  551. package/src/runtime/routes/tts-routes.ts +108 -24
  552. package/src/runtime/routes/usage-routes.ts +30 -2
  553. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  554. package/src/runtime/routes/user-routes.ts +13 -1
  555. package/src/runtime/routes/work-items-routes.ts +8 -1
  556. package/src/runtime/runtime-mode.ts +33 -0
  557. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  558. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  559. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  560. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  561. package/src/runtime/services/analyze-conversation.ts +344 -0
  562. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  563. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  564. package/src/runtime/skill-route-registry.ts +49 -0
  565. package/src/runtime/slack-block-formatting.ts +437 -10
  566. package/src/schedule/scheduler.ts +50 -0
  567. package/src/security/oauth2.ts +26 -4
  568. package/src/security/secure-keys.ts +25 -2
  569. package/src/security/token-manager.ts +8 -0
  570. package/src/sequence/engine.ts +23 -0
  571. package/src/sequence/types.ts +1 -1
  572. package/src/skills/catalog-files.ts +64 -2
  573. package/src/skills/category-inference.ts +122 -0
  574. package/src/skills/clawhub-files.ts +213 -0
  575. package/src/skills/clawhub.ts +84 -23
  576. package/src/skills/skill-file-provider.ts +40 -0
  577. package/src/skills/skillssh-files.ts +395 -0
  578. package/src/skills/skillssh-registry.ts +4 -4
  579. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  580. package/src/stt/__tests__/types.test.ts +89 -0
  581. package/src/stt/daemon-batch-transcriber.ts +195 -0
  582. package/src/stt/stt-stream-session.ts +499 -0
  583. package/src/stt/types.ts +330 -0
  584. package/src/stt/wav-encoder.test.ts +373 -0
  585. package/src/stt/wav-encoder.ts +175 -0
  586. package/src/subagent/manager.ts +38 -14
  587. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  588. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  589. package/src/tools/browser/browser-execution.ts +1163 -23
  590. package/src/tools/browser/browser-manager.ts +45 -0
  591. package/src/tools/browser/browser-mode-constants.ts +12 -0
  592. package/src/tools/browser/browser-mode.ts +92 -0
  593. package/src/tools/browser/browser-status-constants.ts +33 -0
  594. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  595. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  596. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  597. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  598. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
  599. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  600. package/src/tools/browser/cdp-client/errors.ts +15 -0
  601. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  602. package/src/tools/browser/cdp-client/factory.ts +797 -87
  603. package/src/tools/browser/cdp-client/index.ts +16 -2
  604. package/src/tools/browser/cdp-client/types.ts +68 -0
  605. package/src/tools/credentials/vault.ts +35 -6
  606. package/src/tools/network/web-fetch.ts +5 -2
  607. package/src/tools/network/web-search.ts +5 -2
  608. package/src/tools/shared/shell-output.ts +3 -1
  609. package/src/tools/side-effects.ts +2 -0
  610. package/src/tools/skills/sandbox-runner.ts +3 -2
  611. package/src/tools/terminal/safe-env.ts +10 -2
  612. package/src/tools/terminal/shell.ts +15 -4
  613. package/src/tools/tool-manifest.ts +21 -0
  614. package/src/tools/types.ts +17 -0
  615. package/src/tools/ui-surface/definitions.ts +6 -1
  616. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  617. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  618. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  619. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  620. package/src/tts/provider-catalog.ts +201 -0
  621. package/src/tts/provider-registry.ts +73 -0
  622. package/src/tts/providers/deepgram-provider.ts +219 -0
  623. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  624. package/src/tts/providers/fish-audio-provider.ts +183 -0
  625. package/src/tts/providers/index.ts +42 -0
  626. package/src/tts/providers/register-builtins.ts +130 -0
  627. package/src/tts/synthesize-text.ts +110 -0
  628. package/src/tts/tts-config-resolver.ts +78 -0
  629. package/src/tts/types.ts +153 -0
  630. package/src/types/onboarding-context.ts +7 -0
  631. package/src/util/abort-reasons.ts +58 -0
  632. package/src/util/device-id.ts +32 -16
  633. package/src/util/errors.ts +9 -1
  634. package/src/util/platform.ts +54 -10
  635. package/src/util/pricing.ts +66 -3
  636. package/src/util/spawn.ts +1 -1
  637. package/src/util/truncate.ts +4 -2
  638. package/src/util/unicode.ts +201 -0
  639. package/src/version.ts +19 -24
  640. package/src/watcher/engine.ts +23 -0
  641. package/src/watcher/watcher-store.ts +31 -0
  642. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  643. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  644. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  645. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  646. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  647. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  648. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  649. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  650. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  651. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  652. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  653. package/src/workspace/migrations/registry.ts +16 -0
  654. package/src/workspace/top-level-renderer.ts +13 -1
  655. package/src/workspace/turn-commit.ts +31 -0
  656. package/src/__tests__/email-cli.test.ts +0 -297
  657. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  658. package/src/cli/commands/browser-relay.ts +0 -466
  659. package/src/email/guardrails.ts +0 -221
  660. package/src/email/provider.ts +0 -117
  661. package/src/email/providers/agentmail.ts +0 -361
  662. package/src/email/providers/index.ts +0 -65
  663. package/src/email/service.ts +0 -384
  664. package/src/email/types.ts +0 -126
  665. package/src/prompts/templates/USER.md +0 -13
  666. package/src/providers/speech-to-text/types.ts +0 -17
  667. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
@@ -23,7 +23,7 @@ function getClawhubProjectRoot(): string {
23
23
  }
24
24
 
25
25
  // Validate slug format (alphanumeric, hyphens, dots, underscores; optional namespace with single slash)
26
- function validateSlug(slug: string): boolean {
26
+ export function validateSlug(slug: string): boolean {
27
27
  return /^[a-zA-Z0-9]([a-zA-Z0-9._-]*(\/[a-zA-Z0-9][a-zA-Z0-9._-]*)?)?$/.test(
28
28
  slug,
29
29
  );
@@ -312,18 +312,37 @@ export async function clawhubSearch(
312
312
  };
313
313
  }
314
314
  } catch {
315
- // CLI outputs text: "slug vVersion DisplayName (score)"
315
+ // CLI outputs text fall through to line parser below.
316
316
  }
317
317
 
318
- // Parse text output lines: "slug vVersion Display Name (score)"
318
+ // Parse text output lines. The CLI format varies by version:
319
+ // With version: "slug v1.0.0 Display Name (3.459)"
320
+ // Without version: "slug Display Name (3.459)"
319
321
  const skills: ClawhubSearchResultItem[] = [];
320
322
  for (const line of result.stdout.split("\n")) {
321
- const match = line.match(/^(\S+)\s+v(\S+)\s+(.+?)\s+\([\d.]+\)\s*$/);
322
- if (match) {
323
+ // Try format with version first
324
+ const withVersion = line.match(/^(\S+)\s+v(\S+)\s+(.+?)\s+\([\d.]+\)\s*$/);
325
+ if (withVersion) {
323
326
  skills.push({
324
- slug: match[1],
325
- version: match[2],
326
- name: match[3].trim(),
327
+ slug: withVersion[1],
328
+ version: withVersion[2],
329
+ name: withVersion[3].trim(),
330
+ description: "",
331
+ author: "",
332
+ stars: 0,
333
+ installs: 0,
334
+ createdAt: 0,
335
+ source: "clawhub",
336
+ });
337
+ continue;
338
+ }
339
+ // Try format without version
340
+ const withoutVersion = line.match(/^(\S+)\s+(.+?)\s+\([\d.]+\)\s*$/);
341
+ if (withoutVersion) {
342
+ skills.push({
343
+ slug: withoutVersion[1],
344
+ version: "",
345
+ name: withoutVersion[2].trim(),
327
346
  description: "",
328
347
  author: "",
329
348
  stars: 0,
@@ -420,12 +439,18 @@ export async function clawhubInspect(
420
439
  }
421
440
  try {
422
441
  const parsed = JSON.parse(result.stdout);
423
- // Normalize the raw inspect response to our interface
442
+ // Normalize the raw inspect response to our interface.
443
+ // The CLI nests skill metadata under `parsed.skill` and files
444
+ // under `parsed.version.files`. Fall back to top-level fields
445
+ // for older CLI versions that used a flat structure.
446
+ const ps = parsed.skill ?? parsed;
447
+ const rawStats = ps.stats ?? parsed.stats;
448
+ const rawFiles = parsed.version?.files ?? parsed.files ?? null;
424
449
  const data: ClawhubInspectResult = {
425
450
  skill: {
426
- slug: parsed.slug ?? slug,
427
- displayName: parsed.displayName ?? parsed.name ?? slug,
428
- summary: parsed.summary ?? parsed.description ?? "",
451
+ slug: ps.slug ?? slug,
452
+ displayName: ps.displayName ?? ps.name ?? slug,
453
+ summary: ps.summary ?? ps.description ?? "",
429
454
  },
430
455
  owner: parsed.owner
431
456
  ? {
@@ -434,26 +459,24 @@ export async function clawhubInspect(
434
459
  image: parsed.owner.image ?? parsed.owner.avatar ?? undefined,
435
460
  }
436
461
  : null,
437
- stats: parsed.stats
462
+ stats: rawStats
438
463
  ? {
439
- stars: parsed.stats.stars ?? 0,
440
- installs:
441
- parsed.stats.installsAllTime ?? parsed.stats.installs ?? 0,
442
- downloads:
443
- parsed.stats.downloadsAllTime ?? parsed.stats.downloads ?? 0,
444
- versions: parsed.stats.versions ?? 0,
464
+ stars: rawStats.stars ?? 0,
465
+ installs: rawStats.installsAllTime ?? rawStats.installs ?? 0,
466
+ downloads: rawStats.downloadsAllTime ?? rawStats.downloads ?? 0,
467
+ versions: rawStats.versions ?? 0,
445
468
  }
446
469
  : null,
447
- createdAt: parsed.createdAt ?? null,
448
- updatedAt: parsed.updatedAt ?? null,
470
+ createdAt: ps.createdAt ?? parsed.createdAt ?? null,
471
+ updatedAt: ps.updatedAt ?? parsed.updatedAt ?? null,
449
472
  latestVersion: parsed.latestVersion
450
473
  ? {
451
474
  version: parsed.latestVersion.version ?? "",
452
475
  changelog: parsed.latestVersion.changelog ?? undefined,
453
476
  }
454
477
  : null,
455
- files: Array.isArray(parsed.files)
456
- ? parsed.files.map((f: Record<string, unknown>) => ({
478
+ files: Array.isArray(rawFiles)
479
+ ? rawFiles.map((f: Record<string, unknown>) => ({
457
480
  path: (f.path as string) ?? "",
458
481
  size: (f.size as number) ?? 0,
459
482
  contentType: (f.contentType as string) ?? undefined,
@@ -475,6 +498,44 @@ export async function clawhubInspect(
475
498
  }
476
499
  }
477
500
 
501
+ /**
502
+ * Fetch a single file's content from a published clawhub skill.
503
+ * Calls `npx clawhub inspect <slug> --json --file <filePath>` and returns
504
+ * the file content string, or `null` on failure.
505
+ */
506
+ export async function clawhubInspectFile(
507
+ slug: string,
508
+ filePath: string,
509
+ ): Promise<string | null> {
510
+ if (!validateSlug(slug)) return null;
511
+
512
+ try {
513
+ const result = await runClawhub([
514
+ "inspect",
515
+ slug,
516
+ "--json",
517
+ "--file",
518
+ filePath,
519
+ ]);
520
+ if (result.exitCode !== 0) return null;
521
+
522
+ try {
523
+ const parsed = JSON.parse(result.stdout);
524
+ // The CLI returns the file content in one of these fields
525
+ const content =
526
+ parsed.skillMdContent ??
527
+ parsed.fileContents?.[filePath] ??
528
+ parsed.file?.content ??
529
+ null;
530
+ return typeof content === "string" ? content : null;
531
+ } catch {
532
+ return null;
533
+ }
534
+ } catch {
535
+ return null;
536
+ }
537
+ }
538
+
478
539
  export async function clawhubCheckUpdates(): Promise<ClawhubUpdateCheckItem[]> {
479
540
  // This is a placeholder -- clawhub doesn't have a dedicated check-updates command
480
541
  // For now return empty; will be implemented when the CLI supports it
@@ -0,0 +1,40 @@
1
+ import type { SlimSkillResponse } from "../daemon/message-types/skills.js";
2
+ import type { SkillFileEntry } from "./catalog-files.js";
3
+
4
+ /**
5
+ * A file provider can resolve file listings and single-file content for
6
+ * skills that are NOT installed locally. Each origin (vellum catalog,
7
+ * skills.sh, clawhub) implements this interface.
8
+ */
9
+ export interface SkillFileProvider {
10
+ /**
11
+ * Return true if this provider can handle the given skill id.
12
+ * Called synchronously — must not perform I/O.
13
+ */
14
+ canHandle(skillId: string): boolean;
15
+
16
+ /**
17
+ * List all files in the skill directory. Returns entries with
18
+ * `content: null` (content is fetched on demand via `readFileContent`).
19
+ * Returns `null` if the skill doesn't exist in this provider.
20
+ */
21
+ listFiles(skillId: string): Promise<SkillFileEntry[] | null>;
22
+
23
+ /**
24
+ * Read a single file's content. `relativePath` has already been
25
+ * sanitized by the caller (sanitizeRelativePath + hasHiddenOrSkippedSegment).
26
+ * Returns `null` if the file doesn't exist.
27
+ */
28
+ readFileContent(
29
+ skillId: string,
30
+ sanitizedPath: string,
31
+ ): Promise<SkillFileEntry | null>;
32
+
33
+ /**
34
+ * Synthesize a SlimSkillResponse for an uninstalled skill in this
35
+ * provider. Used by getSkill/getSkillFiles when the skill isn't in
36
+ * the local catalog. Returns `null` if the provider can't produce
37
+ * metadata for this skill.
38
+ */
39
+ toSlimSkill(skillId: string): Promise<SlimSkillResponse | null>;
40
+ }
@@ -0,0 +1,395 @@
1
+ /**
2
+ * skillssh-files — SkillFileProvider for skills.sh (GitHub-hosted) skills.
3
+ *
4
+ * Lists files and reads individual file content via the GitHub Contents API
5
+ * and Tree API, without downloading or installing the skill locally.
6
+ *
7
+ * Path resolution (conventional `skills/<slug>/` with tree-search fallback)
8
+ * mirrors the logic in `fetchSkillFromGitHub` from `skillssh-registry.ts`,
9
+ * but only collects metadata (no file content on listing) and fetches
10
+ * content on demand for individual files.
11
+ */
12
+
13
+ import { basename } from "node:path";
14
+
15
+ import type { SlimSkillResponse } from "../daemon/message-types/skills.js";
16
+ import {
17
+ isTextMimeType as isTextMime,
18
+ MAX_INLINE_TEXT_SIZE,
19
+ } from "../runtime/routes/workspace-utils.js";
20
+ import { getLogger } from "../util/logger.js";
21
+ import type { SkillFileEntry } from "./catalog-files.js";
22
+ import {
23
+ hasHiddenOrSkippedSegment,
24
+ sanitizeRelativePath,
25
+ SKIP_DIRS,
26
+ } from "./catalog-files.js";
27
+ import type { SkillFileProvider } from "./skill-file-provider.js";
28
+ import type { GitHubContentsEntry } from "./skillssh-registry.js";
29
+ import {
30
+ findSkillDirInTree,
31
+ githubHeaders,
32
+ resolveSkillSource,
33
+ } from "./skillssh-registry.js";
34
+
35
+ const log = getLogger("skillssh-files");
36
+
37
+ // ─── Path resolution cache ──────────────────────────────────────────────────
38
+
39
+ interface CacheEntry {
40
+ dirPath: string;
41
+ expiresAt: number;
42
+ }
43
+
44
+ const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
45
+
46
+ /**
47
+ * In-memory cache for resolved skill directory paths. Keyed by
48
+ * `${owner}/${repo}/${skillSlug}` so repeated requests don't re-probe the
49
+ * GitHub Contents/Tree APIs.
50
+ */
51
+ const dirPathCache = new Map<string, CacheEntry>();
52
+
53
+ function cacheKey(owner: string, repo: string, skillSlug: string): string {
54
+ return `${owner}/${repo}/${skillSlug}`;
55
+ }
56
+
57
+ function getCachedDirPath(
58
+ owner: string,
59
+ repo: string,
60
+ skillSlug: string,
61
+ ): string | null {
62
+ const key = cacheKey(owner, repo, skillSlug);
63
+ const entry = dirPathCache.get(key);
64
+ if (!entry) return null;
65
+ if (Date.now() > entry.expiresAt) {
66
+ dirPathCache.delete(key);
67
+ return null;
68
+ }
69
+ return entry.dirPath;
70
+ }
71
+
72
+ function setCachedDirPath(
73
+ owner: string,
74
+ repo: string,
75
+ skillSlug: string,
76
+ dirPath: string,
77
+ ): void {
78
+ const key = cacheKey(owner, repo, skillSlug);
79
+ dirPathCache.set(key, { dirPath, expiresAt: Date.now() + CACHE_TTL_MS });
80
+ }
81
+
82
+ // Exported for testing only
83
+ export function clearDirPathCache(): void {
84
+ dirPathCache.clear();
85
+ }
86
+
87
+ // ─── Binary classification ──────────────────────────────────────────────────
88
+
89
+ /**
90
+ * Classify a file as binary from its name alone. Mirrors the
91
+ * `classifyByName` pattern in `catalog-files.ts`.
92
+ */
93
+ function classifyByName(name: string): boolean {
94
+ const mime = Bun.file(name).type;
95
+ return !isTextMime(mime, name);
96
+ }
97
+
98
+ // ─── Skill directory resolution ─────────────────────────────────────────────
99
+
100
+ /**
101
+ * Resolve the directory path for a skill in a GitHub repo. Tries the
102
+ * conventional `skills/<slug>/` path first, falls back to tree search.
103
+ * Returns null if the skill cannot be located.
104
+ */
105
+ async function resolveSkillDir(
106
+ owner: string,
107
+ repo: string,
108
+ skillSlug: string,
109
+ ref?: string,
110
+ ): Promise<string | null> {
111
+ // Check cache first
112
+ const cached = getCachedDirPath(owner, repo, skillSlug);
113
+ if (cached !== null) return cached;
114
+
115
+ const headers = githubHeaders();
116
+ const conventionalPath = `skills/${encodeURIComponent(skillSlug)}`;
117
+
118
+ const probeUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${conventionalPath}${ref ? `?ref=${encodeURIComponent(ref)}` : ""}`;
119
+
120
+ const probeResponse = await fetch(probeUrl, {
121
+ headers,
122
+ signal: AbortSignal.timeout(15_000),
123
+ });
124
+
125
+ if (probeResponse.ok) {
126
+ setCachedDirPath(owner, repo, skillSlug, conventionalPath);
127
+ return conventionalPath;
128
+ }
129
+
130
+ if (probeResponse.status === 404) {
131
+ // Fall back to tree search
132
+ const treeRef = ref ?? "HEAD";
133
+ const foundPath = await findSkillDirInTree(
134
+ owner,
135
+ repo,
136
+ skillSlug,
137
+ treeRef,
138
+ headers,
139
+ );
140
+ if (foundPath) {
141
+ setCachedDirPath(owner, repo, skillSlug, foundPath);
142
+ return foundPath;
143
+ }
144
+ return null;
145
+ }
146
+
147
+ // Non-404 error — log and return null
148
+ log.warn(
149
+ { status: probeResponse.status, owner, repo, skillSlug },
150
+ "GitHub Contents API returned non-2xx during skill dir probe",
151
+ );
152
+ return null;
153
+ }
154
+
155
+ // ─── Recursive directory listing ────────────────────────────────────────────
156
+
157
+ /**
158
+ * Recursively list files in a GitHub directory via the Contents API.
159
+ * Collects `SkillFileEntry` objects with `content: null`. Skips hidden
160
+ * files, `node_modules`, `__pycache__`, `.git` (same filtering as
161
+ * `walkSkillDir` in `catalog-files.ts`).
162
+ */
163
+ async function listGitHubDir(
164
+ owner: string,
165
+ repo: string,
166
+ dirPath: string,
167
+ prefix: string,
168
+ ref: string | undefined,
169
+ headers: Record<string, string>,
170
+ ): Promise<SkillFileEntry[]> {
171
+ let apiUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${dirPath}`;
172
+ if (ref) {
173
+ apiUrl += `?ref=${encodeURIComponent(ref)}`;
174
+ }
175
+
176
+ const response = await fetch(apiUrl, {
177
+ headers,
178
+ signal: AbortSignal.timeout(15_000),
179
+ });
180
+
181
+ if (!response.ok) {
182
+ throw new Error(
183
+ `GitHub Contents API error: HTTP ${response.status} ${response.statusText}`,
184
+ );
185
+ }
186
+
187
+ const entries = (await response.json()) as GitHubContentsEntry[];
188
+ if (!Array.isArray(entries)) return [];
189
+
190
+ const result: SkillFileEntry[] = [];
191
+
192
+ for (const entry of entries) {
193
+ // Skip hidden files/dirs (same as walkSkillDir)
194
+ if (entry.name.startsWith(".")) continue;
195
+
196
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
197
+
198
+ if (entry.type === "dir") {
199
+ // Skip well-known heavyweight directories
200
+ if (SKIP_DIRS.has(entry.name)) continue;
201
+
202
+ const subEntries = await listGitHubDir(
203
+ owner,
204
+ repo,
205
+ `${dirPath}/${entry.name}`,
206
+ relativePath,
207
+ ref,
208
+ headers,
209
+ );
210
+ result.push(...subEntries);
211
+ continue;
212
+ }
213
+
214
+ if (entry.type !== "file") continue;
215
+
216
+ result.push({
217
+ path: relativePath,
218
+ name: basename(relativePath),
219
+ size: 0, // GitHub Contents API directory listings don't include size
220
+ mimeType: "",
221
+ isBinary: classifyByName(entry.name),
222
+ content: null,
223
+ });
224
+ }
225
+
226
+ return result;
227
+ }
228
+
229
+ // ─── Provider implementation ────────────────────────────────────────────────
230
+
231
+ export function createSkillsShProvider(): SkillFileProvider {
232
+ return {
233
+ canHandle(skillId: string): boolean {
234
+ return skillId.split("/").length >= 3;
235
+ },
236
+
237
+ async listFiles(skillId: string): Promise<SkillFileEntry[] | null> {
238
+ let source;
239
+ try {
240
+ source = resolveSkillSource(skillId);
241
+ } catch {
242
+ return null;
243
+ }
244
+
245
+ try {
246
+ const dirPath = await resolveSkillDir(
247
+ source.owner,
248
+ source.repo,
249
+ source.skillSlug,
250
+ source.ref,
251
+ );
252
+ if (!dirPath) return null;
253
+
254
+ const headers = githubHeaders();
255
+ const entries = await listGitHubDir(
256
+ source.owner,
257
+ source.repo,
258
+ dirPath,
259
+ "",
260
+ source.ref,
261
+ headers,
262
+ );
263
+ entries.sort((a, b) => a.path.localeCompare(b.path));
264
+ return entries;
265
+ } catch (err) {
266
+ log.warn({ err, skillId }, "Failed to list files for skills.sh skill");
267
+ return null;
268
+ }
269
+ },
270
+
271
+ async readFileContent(
272
+ skillId: string,
273
+ sanitizedPath: string,
274
+ ): Promise<SkillFileEntry | null> {
275
+ // Re-validate the path even though the caller should have sanitized
276
+ const safe = sanitizeRelativePath(sanitizedPath);
277
+ if (!safe) return null;
278
+ if (hasHiddenOrSkippedSegment(safe)) return null;
279
+
280
+ let source;
281
+ try {
282
+ source = resolveSkillSource(skillId);
283
+ } catch {
284
+ return null;
285
+ }
286
+
287
+ try {
288
+ const dirPath = await resolveSkillDir(
289
+ source.owner,
290
+ source.repo,
291
+ source.skillSlug,
292
+ source.ref,
293
+ );
294
+ if (!dirPath) return null;
295
+
296
+ const headers = githubHeaders();
297
+ const filePath = `${dirPath}/${safe}`;
298
+
299
+ // Fetch the file entry via GitHub Contents API
300
+ let apiUrl = `https://api.github.com/repos/${encodeURIComponent(source.owner)}/${encodeURIComponent(source.repo)}/contents/${filePath}`;
301
+ if (source.ref) {
302
+ apiUrl += `?ref=${encodeURIComponent(source.ref)}`;
303
+ }
304
+
305
+ const response = await fetch(apiUrl, {
306
+ headers,
307
+ signal: AbortSignal.timeout(15_000),
308
+ });
309
+
310
+ if (!response.ok) return null;
311
+
312
+ const entry = (await response.json()) as GitHubContentsEntry & {
313
+ size?: number;
314
+ };
315
+
316
+ // Ensure it's a file, not a directory
317
+ if (entry.type !== "file") return null;
318
+
319
+ const name = basename(safe);
320
+ const isBinary = classifyByName(name);
321
+
322
+ // Return null content for binary files
323
+ if (isBinary) {
324
+ return {
325
+ path: safe,
326
+ name,
327
+ size: entry.size ?? 0,
328
+ mimeType: "",
329
+ isBinary: true,
330
+ content: null,
331
+ };
332
+ }
333
+
334
+ // For text files, check size and fetch content
335
+ const size = entry.size ?? 0;
336
+ if (size > MAX_INLINE_TEXT_SIZE) {
337
+ return {
338
+ path: safe,
339
+ name,
340
+ size,
341
+ mimeType: "",
342
+ isBinary: false,
343
+ content: null,
344
+ };
345
+ }
346
+
347
+ // Fetch the actual file content via download_url
348
+ if (!entry.download_url) return null;
349
+
350
+ const contentResponse = await fetch(entry.download_url, {
351
+ headers,
352
+ signal: AbortSignal.timeout(10_000),
353
+ });
354
+
355
+ if (!contentResponse.ok) return null;
356
+
357
+ const content = await contentResponse.text();
358
+
359
+ return {
360
+ path: safe,
361
+ name,
362
+ size: content.length,
363
+ mimeType: "",
364
+ isBinary: false,
365
+ content,
366
+ };
367
+ } catch (err) {
368
+ log.warn(
369
+ { err, skillId, path: sanitizedPath },
370
+ "Failed to read file content for skills.sh skill",
371
+ );
372
+ return null;
373
+ }
374
+ },
375
+
376
+ async toSlimSkill(skillId: string): Promise<SlimSkillResponse | null> {
377
+ try {
378
+ const source = resolveSkillSource(skillId);
379
+ return {
380
+ id: skillId,
381
+ name: source.skillSlug,
382
+ description: "",
383
+ kind: "catalog",
384
+ status: "available",
385
+ origin: "skillssh",
386
+ slug: skillId,
387
+ sourceRepo: `${source.owner}/${source.repo}`,
388
+ installs: 0,
389
+ };
390
+ } catch {
391
+ return null;
392
+ }
393
+ },
394
+ };
395
+ }
@@ -189,14 +189,14 @@ export function resolveSkillSource(source: string): ResolvedSkillSource {
189
189
 
190
190
  // ─── GitHub fetch ───────────────────────────────────────────────────────────
191
191
 
192
- interface GitHubContentsEntry {
192
+ export interface GitHubContentsEntry {
193
193
  name: string;
194
194
  type: "file" | "dir";
195
195
  download_url: string | null;
196
196
  }
197
197
 
198
198
  /** Build common headers for GitHub API requests (User-Agent + optional auth). */
199
- function githubHeaders(): Record<string, string> {
199
+ export function githubHeaders(): Record<string, string> {
200
200
  const headers: Record<string, string> = {
201
201
  Accept: "application/vnd.github.v3+json",
202
202
  "User-Agent": "vellum-assistant",
@@ -208,7 +208,7 @@ function githubHeaders(): Record<string, string> {
208
208
  return headers;
209
209
  }
210
210
 
211
- interface GitHubTreeEntry {
211
+ export interface GitHubTreeEntry {
212
212
  path: string;
213
213
  type: "blob" | "tree";
214
214
  }
@@ -217,7 +217,7 @@ interface GitHubTreeEntry {
217
217
  * Search the repo tree for a directory containing `<slug>/SKILL.md`.
218
218
  * Returns the directory path (e.g. "examples/skills-tool/skills/csv") or null.
219
219
  */
220
- async function findSkillDirInTree(
220
+ export async function findSkillDirInTree(
221
221
  owner: string,
222
222
  repo: string,
223
223
  skillSlug: string,