@vellumai/assistant 0.6.2 → 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 (895) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +41 -49
  4. package/bunfig.toml +3 -0
  5. package/docs/architecture/memory.md +1 -1
  6. package/docs/backup-troubleshooting.md +52 -0
  7. package/docs/browser-use-architecture-phase2.md +174 -0
  8. package/docs/stt-provider-onboarding.md +120 -0
  9. package/knip.json +12 -2
  10. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  11. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  12. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  13. package/openapi.yaml +1111 -86
  14. package/package.json +40 -42
  15. package/scripts/generate-openapi.ts +0 -2
  16. package/scripts/test.sh +73 -18
  17. package/src/__tests__/acp-session.test.ts +43 -0
  18. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  19. package/src/__tests__/agent-loop.test.ts +123 -0
  20. package/src/__tests__/anthropic-provider.test.ts +263 -10
  21. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  22. package/src/__tests__/app-executors.test.ts +1 -0
  23. package/src/__tests__/app-source-watcher.test.ts +37 -11
  24. package/src/__tests__/approval-routes-http.test.ts +178 -1
  25. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  26. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  27. package/src/__tests__/browser-fill-credential.test.ts +240 -94
  28. package/src/__tests__/browser-manager.test.ts +40 -27
  29. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  30. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  31. package/src/__tests__/btw-routes.test.ts +7 -0
  32. package/src/__tests__/call-controller.test.ts +581 -20
  33. package/src/__tests__/catalog-files.test.ts +1000 -0
  34. package/src/__tests__/channel-approvals.test.ts +53 -0
  35. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  36. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  37. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  38. package/src/__tests__/checker.test.ts +157 -10
  39. package/src/__tests__/clawhub-files.test.ts +347 -0
  40. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  41. package/src/__tests__/config-analysis.test.ts +100 -0
  42. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  43. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  44. package/src/__tests__/config-schema.test.ts +1248 -224
  45. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  46. package/src/__tests__/config-watcher.test.ts +43 -8
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  48. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  49. package/src/__tests__/contacts-write.test.ts +197 -0
  50. package/src/__tests__/context-overflow-approval.test.ts +16 -1
  51. package/src/__tests__/context-window-manager.test.ts +88 -0
  52. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  54. package/src/__tests__/conversation-agent-loop.test.ts +99 -3
  55. package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
  56. package/src/__tests__/conversation-attachments.test.ts +80 -4
  57. package/src/__tests__/conversation-confirmation-signals.test.ts +290 -0
  58. package/src/__tests__/conversation-error.test.ts +70 -0
  59. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  60. package/src/__tests__/conversation-history-web-search.test.ts +12 -4
  61. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  62. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  63. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  64. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  65. package/src/__tests__/conversation-list-source.test.ts +145 -0
  66. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  67. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  68. package/src/__tests__/conversation-queue.test.ts +946 -62
  69. package/src/__tests__/conversation-routes-disk-view.test.ts +275 -0
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +324 -46
  73. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  74. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  76. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  77. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  78. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  79. package/src/__tests__/conversation-store.test.ts +195 -0
  80. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  81. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  82. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  83. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  84. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
  85. package/src/__tests__/credential-health-service.test.ts +352 -0
  86. package/src/__tests__/credential-security-invariants.test.ts +6 -3
  87. package/src/__tests__/credential-vault-unit.test.ts +383 -7
  88. package/src/__tests__/credential-vault.test.ts +152 -13
  89. package/src/__tests__/credentials-cli.test.ts +42 -18
  90. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  91. package/src/__tests__/date-context.test.ts +4 -4
  92. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  93. package/src/__tests__/device-id.test.ts +112 -0
  94. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  95. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  96. package/src/__tests__/email-html-renderer.test.ts +71 -0
  97. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  98. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  99. package/src/__tests__/emit-event-signal.test.ts +71 -0
  100. package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
  101. package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
  102. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -0
  104. package/src/__tests__/gemini-provider.test.ts +66 -2
  105. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  106. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  107. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  108. package/src/__tests__/gmail-preferences.test.ts +117 -0
  109. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  110. package/src/__tests__/headless-browser-interactions.test.ts +738 -359
  111. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  112. package/src/__tests__/headless-browser-navigate.test.ts +528 -49
  113. package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
  114. package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
  115. package/src/__tests__/heartbeat-service.test.ts +70 -17
  116. package/src/__tests__/home-state-routes.test.ts +162 -0
  117. package/src/__tests__/host-bash-proxy.test.ts +145 -1
  118. package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -0
  119. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  120. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  121. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  122. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  123. package/src/__tests__/host-browser-routes.test.ts +198 -0
  124. package/src/__tests__/host-browser-ws-events-e2e.test.ts +423 -0
  125. package/src/__tests__/host-cu-proxy.test.ts +166 -1
  126. package/src/__tests__/host-file-proxy.test.ts +185 -1
  127. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  128. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  129. package/src/__tests__/host-shell-tool.test.ts +1 -11
  130. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  131. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  132. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  133. package/src/__tests__/integration-status.test.ts +6 -7
  134. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  135. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  136. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  137. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  138. package/src/__tests__/llm-usage-store.test.ts +363 -0
  139. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  140. package/src/__tests__/mcp-health-check.test.ts +10 -3
  141. package/src/__tests__/media-stream-output.test.ts +555 -0
  142. package/src/__tests__/media-stream-parser.test.ts +374 -0
  143. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  144. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  145. package/src/__tests__/media-turn-detector.test.ts +440 -0
  146. package/src/__tests__/message-queue.test.ts +125 -0
  147. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  148. package/src/__tests__/migration-export-http.test.ts +67 -8
  149. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  150. package/src/__tests__/migration-import-commit-http.test.ts +109 -7
  151. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  152. package/src/__tests__/migration-validate-http.test.ts +3 -3
  153. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  154. package/src/__tests__/model-intents.test.ts +2 -2
  155. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  156. package/src/__tests__/oauth-apps-routes.test.ts +18 -12
  157. package/src/__tests__/oauth-cli.test.ts +709 -60
  158. package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
  159. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  160. package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
  161. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  162. package/src/__tests__/oauth-providers-routes.test.ts +52 -14
  163. package/src/__tests__/oauth-store.test.ts +1465 -176
  164. package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
  165. package/src/__tests__/onboarding-template-contract.test.ts +81 -70
  166. package/src/__tests__/openai-provider.test.ts +178 -2
  167. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  168. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  169. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  170. package/src/__tests__/outlook-categories.test.ts +1 -1
  171. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  172. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  173. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  174. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  175. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  176. package/src/__tests__/outlook-trash.test.ts +1 -1
  177. package/src/__tests__/outlook-unsubscribe.test.ts +32 -3
  178. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  179. package/src/__tests__/permission-mode.test.ts +28 -56
  180. package/src/__tests__/persona-resolver.test.ts +251 -0
  181. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  182. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  183. package/src/__tests__/platform.test.ts +92 -1
  184. package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
  185. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  186. package/src/__tests__/pricing.test.ts +174 -0
  187. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  188. package/src/__tests__/qdrant-manager.test.ts +29 -8
  189. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  190. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  191. package/src/__tests__/relay-server.test.ts +423 -5
  192. package/src/__tests__/require-fresh-approval.test.ts +40 -1
  193. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  194. package/src/__tests__/schedule-routes.test.ts +162 -0
  195. package/src/__tests__/search-skills-unified.test.ts +118 -0
  196. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  197. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  198. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  199. package/src/__tests__/secure-keys.test.ts +107 -0
  200. package/src/__tests__/send-endpoint-busy.test.ts +8 -1
  201. package/src/__tests__/sequence-store.test.ts +1 -1
  202. package/src/__tests__/server-history-render.test.ts +49 -0
  203. package/src/__tests__/set-permission-mode.test.ts +13 -250
  204. package/src/__tests__/settings-routes.test.ts +201 -0
  205. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  206. package/src/__tests__/skills-file-content-endpoint.test.ts +801 -0
  207. package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
  208. package/src/__tests__/skills.test.ts +5 -2
  209. package/src/__tests__/skillssh-files.test.ts +446 -0
  210. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  211. package/src/__tests__/slack-channel-config.test.ts +576 -16
  212. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  213. package/src/__tests__/stt-stream-session.test.ts +535 -0
  214. package/src/__tests__/subagent-detail.test.ts +44 -2
  215. package/src/__tests__/subagent-disposal.test.ts +1 -0
  216. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  217. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  218. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  219. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  220. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  221. package/src/__tests__/subagent-tools.test.ts +1 -0
  222. package/src/__tests__/subagent-types.test.ts +1 -0
  223. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  224. package/src/__tests__/system-prompt.test.ts +184 -27
  225. package/src/__tests__/task-scheduler.test.ts +32 -6
  226. package/src/__tests__/telegram-config.test.ts +10 -13
  227. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  228. package/src/__tests__/terminal-tools.test.ts +25 -5
  229. package/src/__tests__/test-preload.ts +18 -0
  230. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  231. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  232. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  233. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  234. package/src/__tests__/tool-executor.test.ts +33 -24
  235. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  236. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  237. package/src/__tests__/top-level-renderer.test.ts +73 -1
  238. package/src/__tests__/transport-hints-queue.test.ts +14 -29
  239. package/src/__tests__/trust-store.test.ts +7 -1
  240. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  241. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  242. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  243. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  244. package/src/__tests__/twilio-routes.test.ts +376 -0
  245. package/src/__tests__/unicode.test.ts +293 -0
  246. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  247. package/src/__tests__/update-bulletin.test.ts +206 -5
  248. package/src/__tests__/usage-routes.test.ts +25 -4
  249. package/src/__tests__/user-reference.test.ts +46 -61
  250. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  251. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  252. package/src/__tests__/voice-config-update.test.ts +403 -0
  253. package/src/__tests__/voice-quality.test.ts +434 -19
  254. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  255. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  256. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  257. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  258. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  259. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  260. package/src/__tests__/workspace-policy.test.ts +2 -0
  261. package/src/acp/client-handler.ts +30 -4
  262. package/src/agent/image-optimize.ts +24 -12
  263. package/src/agent/loop.ts +55 -9
  264. package/src/approvals/guardian-request-resolvers.ts +21 -15
  265. package/src/backup/__tests__/backup-key.test.ts +152 -0
  266. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  267. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  268. package/src/backup/__tests__/local-writer.test.ts +218 -0
  269. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  270. package/src/backup/__tests__/paths.test.ts +300 -0
  271. package/src/backup/__tests__/restore.test.ts +498 -0
  272. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  273. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  274. package/src/backup/backup-key.ts +137 -0
  275. package/src/backup/backup-worker.ts +459 -0
  276. package/src/backup/list-snapshots.ts +147 -0
  277. package/src/backup/local-writer.ts +133 -0
  278. package/src/backup/offsite-writer.ts +222 -0
  279. package/src/backup/paths.ts +226 -0
  280. package/src/backup/restore.ts +322 -0
  281. package/src/backup/snapshot-lock.ts +431 -0
  282. package/src/backup/stream-crypt.ts +263 -0
  283. package/src/browser-session/__tests__/manager.test.ts +297 -0
  284. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  285. package/src/browser-session/backends/extension.ts +26 -0
  286. package/src/browser-session/backends/local.ts +24 -0
  287. package/src/browser-session/events.ts +164 -0
  288. package/src/browser-session/index.ts +27 -0
  289. package/src/browser-session/manager.ts +159 -0
  290. package/src/browser-session/types.ts +28 -0
  291. package/src/bundler/package-resolver.ts +4 -0
  292. package/src/calls/audio-store.ts +11 -5
  293. package/src/calls/call-controller.ts +226 -71
  294. package/src/calls/call-domain.ts +9 -0
  295. package/src/calls/call-speech-output.ts +190 -0
  296. package/src/calls/call-transport.ts +77 -0
  297. package/src/calls/media-stream-audio-transcode.ts +173 -0
  298. package/src/calls/media-stream-output.ts +660 -0
  299. package/src/calls/media-stream-parser.ts +300 -0
  300. package/src/calls/media-stream-protocol.ts +166 -0
  301. package/src/calls/media-stream-server.ts +592 -0
  302. package/src/calls/media-stream-stt-session.ts +460 -0
  303. package/src/calls/media-turn-detector.ts +230 -0
  304. package/src/calls/relay-server.ts +90 -75
  305. package/src/calls/resolve-call-tts-provider.ts +136 -0
  306. package/src/calls/telephony-stt-routing.ts +145 -0
  307. package/src/calls/tts-call-strategy.ts +161 -0
  308. package/src/calls/tts-text-sanitizer.ts +32 -16
  309. package/src/calls/twilio-routes.ts +281 -17
  310. package/src/calls/voice-quality.ts +78 -35
  311. package/src/calls/voice-session-bridge.ts +8 -1
  312. package/src/channels/__tests__/types.test.ts +134 -0
  313. package/src/channels/types.ts +69 -3
  314. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  315. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  316. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  317. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  318. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  319. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  320. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  321. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  322. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  323. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  324. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  325. package/src/cli/commands/backup.ts +993 -0
  326. package/src/cli/commands/conversations.ts +77 -0
  327. package/src/cli/commands/credentials.ts +3 -4
  328. package/src/cli/commands/domain.ts +210 -0
  329. package/src/cli/commands/email.ts +273 -16
  330. package/src/cli/commands/mcp.ts +16 -4
  331. package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -44
  332. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  333. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  334. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  335. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +32 -33
  336. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
  337. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -12
  338. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  339. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  340. package/src/cli/commands/oauth/apps.ts +7 -4
  341. package/src/cli/commands/oauth/connect.ts +6 -3
  342. package/src/cli/commands/oauth/disconnect.ts +1 -1
  343. package/src/cli/commands/oauth/mode.ts +12 -3
  344. package/src/cli/commands/oauth/providers.ts +215 -36
  345. package/src/cli/commands/oauth/shared.ts +7 -6
  346. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
  347. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  348. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  349. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  350. package/src/cli/commands/platform/index.ts +107 -10
  351. package/src/cli/commands/usage.ts +10 -9
  352. package/src/cli/lib/daemon-credential-client.ts +4 -0
  353. package/src/cli/program.ts +30 -4
  354. package/src/config/__tests__/backup-schema.test.ts +134 -0
  355. package/src/config/assistant-feature-flags.ts +61 -62
  356. package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
  357. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -0
  358. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  359. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  360. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  361. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  362. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  363. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  364. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  365. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  366. package/src/config/bundled-skills/contacts/SKILL.md +5 -2
  367. package/src/config/bundled-skills/document/SKILL.md +4 -0
  368. package/src/config/bundled-skills/gmail/SKILL.md +54 -8
  369. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  370. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  371. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  372. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  373. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  374. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  375. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  376. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  377. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  378. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  379. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  380. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  381. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  382. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  383. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  384. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  385. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  386. package/src/config/bundled-skills/outlook/SKILL.md +9 -2
  387. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  388. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  389. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  390. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  391. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  392. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  393. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  394. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  395. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  396. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  397. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  398. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  399. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  400. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  401. package/src/config/bundled-tool-registry.ts +8 -0
  402. package/src/config/env-registry.ts +38 -0
  403. package/src/config/env.ts +49 -4
  404. package/src/config/feature-flag-registry.json +85 -14
  405. package/src/config/loader.ts +82 -13
  406. package/src/config/sanitize-for-transfer.ts +47 -0
  407. package/src/config/schema.ts +81 -15
  408. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  409. package/src/config/schemas/analysis.ts +51 -0
  410. package/src/config/schemas/backup.ts +72 -0
  411. package/src/config/schemas/calls.ts +1 -26
  412. package/src/config/schemas/elevenlabs.ts +0 -59
  413. package/src/config/schemas/filing.ts +47 -7
  414. package/src/config/schemas/heartbeat.ts +27 -5
  415. package/src/config/schemas/host-browser.ts +112 -0
  416. package/src/config/schemas/inference.ts +1 -1
  417. package/src/config/schemas/memory-lifecycle.ts +14 -2
  418. package/src/config/schemas/memory-retrieval.ts +103 -0
  419. package/src/config/schemas/security.ts +0 -6
  420. package/src/config/schemas/services.ts +52 -0
  421. package/src/config/schemas/stt.ts +59 -0
  422. package/src/config/schemas/tts.ts +230 -0
  423. package/src/config/schemas/updates.ts +14 -0
  424. package/src/config/skills.ts +4 -0
  425. package/src/config/types.ts +4 -1
  426. package/src/contacts/contact-store.ts +56 -11
  427. package/src/contacts/contacts-write.ts +38 -1
  428. package/src/context/post-turn-tool-result-truncation.ts +177 -0
  429. package/src/context/tool-result-truncation.ts +2 -1
  430. package/src/context/window-manager.ts +61 -10
  431. package/src/credential-execution/approval-bridge.ts +49 -15
  432. package/src/credential-execution/executable-discovery.ts +12 -2
  433. package/src/credential-execution/process-manager.ts +33 -2
  434. package/src/credential-health/credential-health-service.ts +366 -0
  435. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  436. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  437. package/src/daemon/__tests__/conversation-tool-setup.test.ts +195 -0
  438. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  439. package/src/daemon/app-source-watcher.ts +35 -0
  440. package/src/daemon/config-watcher.ts +99 -5
  441. package/src/daemon/context-overflow-approval.ts +5 -0
  442. package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
  443. package/src/daemon/conversation-agent-loop.ts +153 -42
  444. package/src/daemon/conversation-attachments.ts +40 -0
  445. package/src/daemon/conversation-error.ts +11 -0
  446. package/src/daemon/conversation-history.ts +40 -6
  447. package/src/daemon/conversation-launch.ts +220 -0
  448. package/src/daemon/conversation-lifecycle.ts +59 -9
  449. package/src/daemon/conversation-messaging.ts +37 -3
  450. package/src/daemon/conversation-notifiers.ts +5 -0
  451. package/src/daemon/conversation-process.ts +622 -13
  452. package/src/daemon/conversation-queue-manager.ts +24 -0
  453. package/src/daemon/conversation-runtime-assembly.ts +128 -36
  454. package/src/daemon/conversation-slash.ts +36 -0
  455. package/src/daemon/conversation-surfaces.ts +131 -40
  456. package/src/daemon/conversation-tool-setup.ts +99 -8
  457. package/src/daemon/conversation-usage.ts +7 -4
  458. package/src/daemon/conversation-workspace.ts +12 -0
  459. package/src/daemon/conversation.ts +292 -16
  460. package/src/daemon/date-context.ts +10 -10
  461. package/src/daemon/first-greeting.ts +3 -2
  462. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  463. package/src/daemon/handlers/conversations.ts +13 -141
  464. package/src/daemon/handlers/shared.ts +80 -0
  465. package/src/daemon/handlers/skills.ts +483 -44
  466. package/src/daemon/host-bash-proxy.ts +48 -13
  467. package/src/daemon/host-browser-proxy.ts +192 -0
  468. package/src/daemon/host-cu-proxy.ts +36 -11
  469. package/src/daemon/host-file-proxy.ts +57 -9
  470. package/src/daemon/lifecycle.ts +179 -28
  471. package/src/daemon/message-protocol.ts +13 -0
  472. package/src/daemon/message-types/conversations.ts +89 -14
  473. package/src/daemon/message-types/home.ts +40 -0
  474. package/src/daemon/message-types/host-browser.ts +100 -0
  475. package/src/daemon/message-types/meet.ts +143 -0
  476. package/src/daemon/message-types/messages.ts +19 -5
  477. package/src/daemon/message-types/schedules.ts +34 -2
  478. package/src/daemon/message-types/skills.ts +26 -0
  479. package/src/daemon/message-types/subagents.ts +2 -0
  480. package/src/daemon/message-types/surfaces.ts +2 -0
  481. package/src/daemon/server.ts +439 -14
  482. package/src/daemon/shutdown-handlers.ts +32 -4
  483. package/src/daemon/shutdown-registry.ts +40 -0
  484. package/src/daemon/tool-side-effects.ts +15 -0
  485. package/src/daemon/transport-hints.ts +5 -24
  486. package/src/email/html-renderer.ts +76 -0
  487. package/src/heartbeat/heartbeat-service.ts +93 -7
  488. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  489. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  490. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  491. package/src/home/__tests__/feed-types.test.ts +275 -0
  492. package/src/home/__tests__/feed-writer.test.ts +688 -0
  493. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  494. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  495. package/src/home/__tests__/progress-formula.test.ts +213 -0
  496. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  497. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  498. package/src/home/assistant-feed-authoring.ts +124 -0
  499. package/src/home/emit-feed-event.ts +158 -0
  500. package/src/home/feed-scheduler.ts +247 -0
  501. package/src/home/feed-types.ts +181 -0
  502. package/src/home/feed-writer.ts +469 -0
  503. package/src/home/platform-gmail-digest.ts +163 -0
  504. package/src/home/progress-formula.ts +86 -0
  505. package/src/home/relationship-state-writer.ts +824 -0
  506. package/src/home/relationship-state.ts +143 -0
  507. package/src/home/rollup-producer.ts +384 -0
  508. package/src/hooks/runner.ts +7 -0
  509. package/src/inbound/platform-callback-registration.ts +30 -20
  510. package/src/inbound/public-ingress-urls.ts +12 -0
  511. package/src/instrument.ts +1 -1
  512. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  513. package/src/ipc/cli-client.ts +151 -0
  514. package/src/ipc/cli-server.ts +234 -0
  515. package/src/ipc/gateway-client.ts +180 -0
  516. package/src/ipc/routes/index.ts +5 -0
  517. package/src/ipc/routes/wake-conversation.ts +19 -0
  518. package/src/mcp/client.ts +59 -24
  519. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  520. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  521. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  522. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  523. package/src/memory/app-store.ts +31 -1
  524. package/src/memory/attachments-store.ts +70 -0
  525. package/src/memory/auto-analysis-enqueue.ts +127 -0
  526. package/src/memory/auto-analysis-guard.ts +27 -0
  527. package/src/memory/cleanup-schedule-state.ts +37 -0
  528. package/src/memory/conversation-analyze-job.ts +73 -0
  529. package/src/memory/conversation-crud.ts +122 -0
  530. package/src/memory/conversation-disk-view.ts +7 -0
  531. package/src/memory/conversation-group-migration.ts +34 -2
  532. package/src/memory/conversation-queries.ts +6 -5
  533. package/src/memory/conversation-starters-cadence.ts +76 -0
  534. package/src/memory/conversation-title-service.ts +5 -2
  535. package/src/memory/db-init.ts +18 -0
  536. package/src/memory/db-maintenance.ts +108 -0
  537. package/src/memory/db.ts +1 -0
  538. package/src/memory/embedding-backend.test.ts +75 -0
  539. package/src/memory/embedding-backend.ts +131 -5
  540. package/src/memory/embedding-gemini.test.ts +54 -0
  541. package/src/memory/embedding-gemini.ts +20 -9
  542. package/src/memory/embedding-local.ts +176 -17
  543. package/src/memory/graph/consolidation.ts +10 -23
  544. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  545. package/src/memory/graph/extraction-job.ts +15 -0
  546. package/src/memory/graph/extraction.test.ts +23 -0
  547. package/src/memory/graph/extraction.ts +8 -0
  548. package/src/memory/graph/retriever.ts +67 -40
  549. package/src/memory/graph/scoring.test.ts +186 -0
  550. package/src/memory/graph/scoring.ts +31 -1
  551. package/src/memory/graph/store.test.ts +7 -3
  552. package/src/memory/graph/store.ts +47 -12
  553. package/src/memory/graph/tools.ts +1 -1
  554. package/src/memory/group-crud.ts +6 -1
  555. package/src/memory/indexer.ts +95 -16
  556. package/src/memory/job-handlers/cleanup.ts +11 -8
  557. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  558. package/src/memory/jobs-store.ts +64 -4
  559. package/src/memory/jobs-worker.ts +22 -9
  560. package/src/memory/llm-usage-store.ts +137 -60
  561. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  562. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  563. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  564. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  565. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  566. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  567. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  568. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  569. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  570. package/src/memory/migrations/index.ts +12 -0
  571. package/src/memory/migrations/registry.ts +16 -0
  572. package/src/memory/qdrant-manager.ts +43 -16
  573. package/src/memory/schema/conversations.ts +3 -0
  574. package/src/memory/schema/oauth.ts +21 -13
  575. package/src/memory/usage-buckets.ts +396 -0
  576. package/src/messaging/providers/gmail/client.ts +57 -6
  577. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  578. package/src/messaging/providers/slack/adapter.ts +143 -38
  579. package/src/messaging/providers/slack/client.ts +16 -0
  580. package/src/messaging/providers/slack/types.ts +4 -0
  581. package/src/notifications/decision-engine.ts +3 -3
  582. package/src/notifications/signal.ts +5 -0
  583. package/src/oauth/AGENTS.md +76 -0
  584. package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
  585. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  586. package/src/oauth/byo-connection.test.ts +26 -9
  587. package/src/oauth/byo-connection.ts +10 -8
  588. package/src/oauth/connect-orchestrator.ts +25 -21
  589. package/src/oauth/connect-types.ts +3 -3
  590. package/src/oauth/connection-resolver.test.ts +17 -4
  591. package/src/oauth/connection-resolver.ts +22 -18
  592. package/src/oauth/connection.ts +3 -1
  593. package/src/oauth/manual-token-connection.ts +13 -13
  594. package/src/oauth/oauth-store.ts +223 -100
  595. package/src/oauth/platform-connection.test.ts +101 -3
  596. package/src/oauth/platform-connection.ts +56 -35
  597. package/src/oauth/provider-serializer.ts +31 -5
  598. package/src/oauth/revoke.ts +76 -0
  599. package/src/oauth/seed-providers.ts +133 -87
  600. package/src/oauth/token-persistence.ts +1 -1
  601. package/src/permissions/checker.ts +16 -6
  602. package/src/permissions/defaults.ts +49 -1
  603. package/src/permissions/permission-mode.ts +4 -11
  604. package/src/permissions/prompter.ts +13 -1
  605. package/src/permissions/trust-store.ts +3 -3
  606. package/src/permissions/v2-consent-policy.ts +87 -0
  607. package/src/permissions/workspace-policy.ts +3 -0
  608. package/src/platform/client.test.ts +10 -0
  609. package/src/platform/sync-identity.ts +129 -0
  610. package/src/prompts/persona-resolver.ts +126 -2
  611. package/src/prompts/system-prompt.ts +76 -38
  612. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  613. package/src/prompts/templates/BOOTSTRAP.md +59 -105
  614. package/src/prompts/templates/SOUL.md +3 -1
  615. package/src/prompts/templates/UPDATES.md +12 -0
  616. package/src/prompts/templates/channels/slack.md +20 -0
  617. package/src/prompts/update-bulletin-format.ts +26 -9
  618. package/src/prompts/update-bulletin.ts +34 -23
  619. package/src/prompts/user-reference.ts +20 -17
  620. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  621. package/src/providers/anthropic/client.ts +157 -60
  622. package/src/providers/fireworks/client.ts +2 -2
  623. package/src/providers/gemini/client.ts +9 -1
  624. package/src/providers/model-catalog.ts +6 -0
  625. package/src/providers/model-intents.ts +4 -4
  626. package/src/providers/ollama/client.ts +2 -2
  627. package/src/providers/openai/chat-completions-provider.ts +474 -0
  628. package/src/providers/openai/client.ts +25 -440
  629. package/src/providers/openai/responses-provider.ts +502 -0
  630. package/src/providers/openrouter/client.ts +101 -4
  631. package/src/providers/provider-secret-catalog.ts +139 -0
  632. package/src/providers/registry.ts +2 -2
  633. package/src/providers/retry.ts +14 -3
  634. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  635. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  636. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  637. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  638. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  639. package/src/providers/speech-to-text/deepgram.ts +115 -0
  640. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  641. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  642. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  643. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  644. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  645. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  646. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  647. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  648. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  649. package/src/providers/speech-to-text/resolve.ts +386 -6
  650. package/src/providers/types.ts +10 -1
  651. package/src/runtime/AGENTS.md +65 -0
  652. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  653. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  654. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  655. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  656. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  657. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  658. package/src/runtime/agent-wake.ts +512 -0
  659. package/src/runtime/assistant-event-hub.ts +2 -2
  660. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  661. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  662. package/src/runtime/auth/__tests__/route-policy.test.ts +48 -0
  663. package/src/runtime/auth/middleware.ts +98 -0
  664. package/src/runtime/auth/route-policy.ts +33 -9
  665. package/src/runtime/auth/token-service.ts +56 -1
  666. package/src/runtime/btw-sidechain.ts +2 -0
  667. package/src/runtime/capability-tokens.ts +414 -0
  668. package/src/runtime/channel-approvals.ts +18 -5
  669. package/src/runtime/channel-invite-transport.ts +1 -1
  670. package/src/runtime/channel-invite-transports/email.ts +14 -6
  671. package/src/runtime/channel-readiness-service.ts +12 -22
  672. package/src/runtime/chrome-extension-registry.ts +368 -0
  673. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  674. package/src/runtime/guardian-decision-types.ts +7 -0
  675. package/src/runtime/http-server.ts +815 -75
  676. package/src/runtime/http-types.ts +6 -2
  677. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  678. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  679. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +198 -0
  680. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  681. package/src/runtime/migrations/migration-transport.ts +7 -0
  682. package/src/runtime/migrations/migration-wizard.ts +23 -2
  683. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  684. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  685. package/src/runtime/migrations/vbundle-import-analyzer.ts +96 -1
  686. package/src/runtime/migrations/vbundle-importer.ts +89 -5
  687. package/src/runtime/pending-interactions.ts +18 -13
  688. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  689. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  690. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  691. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  692. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  693. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  694. package/src/runtime/routes/app-management-routes.ts +12 -18
  695. package/src/runtime/routes/approval-routes.ts +90 -16
  696. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  697. package/src/runtime/routes/attachment-routes.ts +216 -17
  698. package/src/runtime/routes/backup-routes.ts +519 -0
  699. package/src/runtime/routes/browser-extension-pair-routes.ts +556 -0
  700. package/src/runtime/routes/btw-routes.ts +8 -6
  701. package/src/runtime/routes/contact-routes.test.ts +298 -0
  702. package/src/runtime/routes/contact-routes.ts +132 -5
  703. package/src/runtime/routes/conversation-analysis-routes.ts +22 -141
  704. package/src/runtime/routes/conversation-management-routes.ts +223 -0
  705. package/src/runtime/routes/conversation-routes.ts +598 -103
  706. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  707. package/src/runtime/routes/filing-routes.ts +93 -0
  708. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  709. package/src/runtime/routes/home-feed-routes.ts +334 -0
  710. package/src/runtime/routes/home-state-routes.ts +138 -0
  711. package/src/runtime/routes/host-browser-routes.ts +268 -0
  712. package/src/runtime/routes/host-file-routes.ts +9 -1
  713. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  714. package/src/runtime/routes/identity-routes.ts +262 -33
  715. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  716. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  717. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  718. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  719. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  720. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  721. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  722. package/src/runtime/routes/log-export-routes.ts +42 -22
  723. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  724. package/src/runtime/routes/memory-item-routes.ts +1 -7
  725. package/src/runtime/routes/migration-routes.ts +122 -2
  726. package/src/runtime/routes/oauth-apps.ts +15 -17
  727. package/src/runtime/routes/oauth-providers.ts +4 -0
  728. package/src/runtime/routes/schedule-routes.ts +24 -11
  729. package/src/runtime/routes/settings-routes.ts +31 -102
  730. package/src/runtime/routes/skills-routes.ts +128 -9
  731. package/src/runtime/routes/stt-routes.ts +233 -0
  732. package/src/runtime/routes/subagents-routes.ts +14 -10
  733. package/src/runtime/routes/surface-action-routes.ts +41 -2
  734. package/src/runtime/routes/tts-routes.ts +108 -24
  735. package/src/runtime/routes/usage-routes.ts +38 -9
  736. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  737. package/src/runtime/routes/user-routes.ts +13 -1
  738. package/src/runtime/routes/work-items-routes.ts +8 -1
  739. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  740. package/src/runtime/routes/workspace-routes.ts +8 -1
  741. package/src/runtime/routes/workspace-utils.ts +2 -0
  742. package/src/runtime/runtime-mode.ts +33 -0
  743. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  744. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  745. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  746. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  747. package/src/runtime/services/analyze-conversation.ts +344 -0
  748. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  749. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  750. package/src/runtime/skill-route-registry.ts +49 -0
  751. package/src/runtime/slack-block-formatting.ts +437 -10
  752. package/src/schedule/scheduler.ts +57 -5
  753. package/src/security/ces-credential-client.ts +20 -0
  754. package/src/security/ces-rpc-credential-backend.ts +17 -0
  755. package/src/security/credential-backend.ts +5 -0
  756. package/src/security/oauth2.ts +68 -29
  757. package/src/security/secure-keys.ts +143 -27
  758. package/src/security/token-manager.ts +31 -10
  759. package/src/sequence/engine.ts +23 -0
  760. package/src/sequence/types.ts +1 -1
  761. package/src/skills/catalog-files.ts +554 -0
  762. package/src/skills/category-inference.ts +122 -0
  763. package/src/skills/clawhub-files.ts +213 -0
  764. package/src/skills/clawhub.ts +84 -23
  765. package/src/skills/skill-file-provider.ts +40 -0
  766. package/src/skills/skillssh-files.ts +395 -0
  767. package/src/skills/skillssh-registry.ts +4 -4
  768. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  769. package/src/stt/__tests__/types.test.ts +89 -0
  770. package/src/stt/daemon-batch-transcriber.ts +195 -0
  771. package/src/stt/stt-stream-session.ts +499 -0
  772. package/src/stt/types.ts +330 -0
  773. package/src/stt/wav-encoder.test.ts +373 -0
  774. package/src/stt/wav-encoder.ts +175 -0
  775. package/src/subagent/manager.ts +169 -40
  776. package/src/subagent/types.ts +19 -0
  777. package/src/tools/apps/executors.ts +11 -2
  778. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  779. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  780. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  781. package/src/tools/browser/auth-detector.ts +43 -12
  782. package/src/tools/browser/browser-execution.ts +1787 -342
  783. package/src/tools/browser/browser-manager.ts +81 -12
  784. package/src/tools/browser/browser-mode-constants.ts +12 -0
  785. package/src/tools/browser/browser-mode.ts +92 -0
  786. package/src/tools/browser/browser-status-constants.ts +33 -0
  787. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  788. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  789. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +1263 -0
  790. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
  791. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -0
  792. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  793. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  794. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  795. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  796. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  797. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  798. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +1007 -0
  799. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  800. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +744 -0
  801. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  802. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
  803. package/src/tools/browser/cdp-client/errors.ts +49 -0
  804. package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
  805. package/src/tools/browser/cdp-client/factory.ts +914 -0
  806. package/src/tools/browser/cdp-client/index.ts +28 -0
  807. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  808. package/src/tools/browser/cdp-client/types.ts +120 -0
  809. package/src/tools/credentials/vault.ts +35 -6
  810. package/src/tools/filesystem/edit.ts +1 -1
  811. package/src/tools/filesystem/list.ts +1 -1
  812. package/src/tools/filesystem/read.ts +1 -1
  813. package/src/tools/filesystem/write.ts +2 -1
  814. package/src/tools/host-filesystem/edit.ts +1 -1
  815. package/src/tools/host-filesystem/read.ts +12 -15
  816. package/src/tools/host-filesystem/write.ts +1 -1
  817. package/src/tools/host-terminal/host-shell.ts +21 -16
  818. package/src/tools/network/web-fetch.ts +5 -2
  819. package/src/tools/network/web-search.ts +5 -2
  820. package/src/tools/permission-checker.ts +77 -82
  821. package/src/tools/registry.ts +0 -2
  822. package/src/tools/secret-detection-handler.ts +34 -0
  823. package/src/tools/shared/filesystem/image-read.ts +61 -40
  824. package/src/tools/shared/shell-output.ts +3 -1
  825. package/src/tools/side-effects.ts +2 -0
  826. package/src/tools/skills/sandbox-runner.ts +3 -2
  827. package/src/tools/subagent/spawn.ts +47 -3
  828. package/src/tools/subagent/status.ts +2 -0
  829. package/src/tools/system/register.ts +2 -16
  830. package/src/tools/terminal/safe-env.ts +15 -0
  831. package/src/tools/terminal/shell.ts +36 -20
  832. package/src/tools/tool-approval-handler.ts +48 -2
  833. package/src/tools/tool-manifest.ts +21 -0
  834. package/src/tools/types.ts +19 -0
  835. package/src/tools/ui-surface/definitions.ts +6 -1
  836. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  837. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  838. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  839. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  840. package/src/tts/provider-catalog.ts +201 -0
  841. package/src/tts/provider-registry.ts +73 -0
  842. package/src/tts/providers/deepgram-provider.ts +219 -0
  843. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  844. package/src/tts/providers/fish-audio-provider.ts +183 -0
  845. package/src/tts/providers/index.ts +42 -0
  846. package/src/tts/providers/register-builtins.ts +130 -0
  847. package/src/tts/synthesize-text.ts +110 -0
  848. package/src/tts/tts-config-resolver.ts +78 -0
  849. package/src/tts/types.ts +153 -0
  850. package/src/types/onboarding-context.ts +7 -0
  851. package/src/util/abort-reasons.ts +58 -0
  852. package/src/util/device-id.ts +32 -16
  853. package/src/util/errors.ts +9 -1
  854. package/src/util/platform.ts +63 -24
  855. package/src/util/pricing.ts +66 -3
  856. package/src/util/spawn.ts +1 -1
  857. package/src/util/truncate.ts +4 -2
  858. package/src/util/unicode.ts +201 -0
  859. package/src/version.ts +19 -24
  860. package/src/watcher/engine.ts +23 -0
  861. package/src/watcher/watcher-store.ts +31 -0
  862. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  863. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  864. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  865. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  866. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  867. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  868. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  869. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  870. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  871. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  872. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  873. package/src/workspace/migrations/registry.ts +16 -0
  874. package/src/workspace/top-level-renderer.ts +31 -1
  875. package/src/workspace/turn-commit.ts +31 -0
  876. package/src/__tests__/chrome-cdp.test.ts +0 -419
  877. package/src/__tests__/email-cli.test.ts +0 -297
  878. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  879. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  880. package/src/__tests__/permission-mode-store.test.ts +0 -277
  881. package/src/browser-extension-relay/protocol.ts +0 -63
  882. package/src/browser-extension-relay/server.ts +0 -203
  883. package/src/cli/commands/browser-relay.ts +0 -536
  884. package/src/config/schemas/sandbox.ts +0 -14
  885. package/src/email/guardrails.ts +0 -221
  886. package/src/email/provider.ts +0 -117
  887. package/src/email/providers/agentmail.ts +0 -361
  888. package/src/email/providers/index.ts +0 -65
  889. package/src/email/service.ts +0 -384
  890. package/src/email/types.ts +0 -126
  891. package/src/permissions/permission-mode-store.ts +0 -180
  892. package/src/prompts/templates/USER.md +0 -13
  893. package/src/providers/speech-to-text/types.ts +0 -17
  894. package/src/tools/browser/chrome-cdp.ts +0 -239
  895. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -9,7 +9,7 @@ import {
9
9
  statSync,
10
10
  } from "node:fs";
11
11
  import { homedir } from "node:os";
12
- import { join, relative } from "node:path";
12
+ import { basename, join, relative, sep } from "node:path";
13
13
 
14
14
  import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
15
15
  import {
@@ -31,13 +31,27 @@ import {
31
31
  getConfiguredProvider,
32
32
  userMessage,
33
33
  } from "../../providers/provider-send-message.js";
34
- import { isTextMimeType as isTextMime } from "../../runtime/routes/workspace-utils.js";
34
+ import {
35
+ isTextMimeType as isTextMime,
36
+ MAX_INLINE_TEXT_SIZE,
37
+ } from "../../runtime/routes/workspace-utils.js";
35
38
  import { getCatalog } from "../../skills/catalog-cache.js";
36
39
  import {
40
+ catalogSkillToSlim,
41
+ createVellumCatalogProvider,
42
+ hasHiddenOrSkippedSegment,
43
+ sanitizeRelativePath,
44
+ type SkillFileEntry,
45
+ SKIP_DIRS,
46
+ } from "../../skills/catalog-files.js";
47
+ import {
48
+ type CatalogSkill,
37
49
  installSkillLocally,
38
50
  upsertSkillsIndex,
39
51
  } from "../../skills/catalog-install.js";
40
52
  import { filterByQuery } from "../../skills/catalog-search.js";
53
+ import type { SkillCategory } from "../../skills/category-inference.js";
54
+ import { inferCategory } from "../../skills/category-inference.js";
41
55
  import {
42
56
  clawhubCheckUpdates,
43
57
  clawhubInspect,
@@ -46,6 +60,7 @@ import {
46
60
  clawhubSearch,
47
61
  clawhubUpdate,
48
62
  } from "../../skills/clawhub.js";
63
+ import { createClawhubProvider } from "../../skills/clawhub-files.js";
49
64
  import {
50
65
  readInstallMeta,
51
66
  type SkillInstallMeta,
@@ -56,16 +71,25 @@ import {
56
71
  removeSkillsIndexEntry,
57
72
  validateManagedSkillId,
58
73
  } from "../../skills/managed-store.js";
74
+ import type { SkillFileProvider } from "../../skills/skill-file-provider.js";
75
+ import { createSkillsShProvider } from "../../skills/skillssh-files.js";
59
76
  import {
77
+ fetchSkillAudits,
60
78
  installExternalSkill,
61
79
  resolveSkillSource,
62
80
  searchSkillsRegistry,
81
+ type SkillAuditData,
63
82
  } from "../../skills/skillssh-registry.js";
64
83
  import { getWorkspaceSkillsDir } from "../../util/platform.js";
65
84
  import type {
66
85
  SkillDetailResponse,
86
+ SkillFileContentResponse,
67
87
  SlimSkillResponse,
68
88
  } from "../message-types/skills.js";
89
+
90
+ // Re-export for use by route layer and future consumers.
91
+ export type { SkillCategory };
92
+ export { inferCategory };
69
93
  import {
70
94
  CONFIG_RELOAD_DEBOUNCE_MS,
71
95
  ensureSkillEntry,
@@ -73,10 +97,6 @@ import {
73
97
  log,
74
98
  } from "./shared.js";
75
99
 
76
- // ─── MIME detection helpers ───────────────────────────────────────────────────
77
-
78
- const MAX_INLINE_SIZE = 2 * 1024 * 1024; // 2 MB
79
-
80
100
  // ─── Shared context for standalone functions ─────────────────────────────────
81
101
 
82
102
  /**
@@ -91,6 +111,62 @@ export interface SkillOperationContext {
91
111
  broadcast: HandlerContext["broadcast"];
92
112
  }
93
113
 
114
+ // ─── Provider chain for uninstalled skill file preview ───────────────────────
115
+ // Ordered by priority: vellum first (most common and cheapest to check),
116
+ // then skills.sh, then clawhub.
117
+ //
118
+ // Lazy-initialized on first access so that mock modules (in tests) can
119
+ // replace the factory functions before providers are constructed.
120
+
121
+ let _fileProviders: SkillFileProvider[] | null = null;
122
+
123
+ function getFileProviders(): SkillFileProvider[] {
124
+ if (!_fileProviders) {
125
+ _fileProviders = [
126
+ createVellumCatalogProvider(),
127
+ createSkillsShProvider(),
128
+ createClawhubProvider(),
129
+ ];
130
+ }
131
+ return _fileProviders;
132
+ }
133
+
134
+ /** @internal Exported for test use only — forces re-creation of providers. */
135
+ export function _resetFileProvidersForTest(): void {
136
+ _fileProviders = null;
137
+ }
138
+
139
+ async function resolveSkillFiles(skillId: string): Promise<{
140
+ handled: boolean;
141
+ skill: SlimSkillResponse | null;
142
+ files: SkillFileEntry[] | null;
143
+ }> {
144
+ for (const provider of getFileProviders()) {
145
+ if (!provider.canHandle(skillId)) continue;
146
+ // Commit to this provider — don't fall through to subsequent providers.
147
+ const files = await provider.listFiles(skillId);
148
+ if (files === null) return { handled: true, skill: null, files: null };
149
+ const skill = await provider.toSlimSkill(skillId);
150
+ if (skill === null) return { handled: true, skill: null, files: null };
151
+ files.sort((a, b) => a.path.localeCompare(b.path));
152
+ return { handled: true, skill, files };
153
+ }
154
+ return { handled: false, skill: null, files: null };
155
+ }
156
+
157
+ async function resolveSkillFileContent(
158
+ skillId: string,
159
+ sanitizedPath: string,
160
+ ): Promise<{ handled: boolean; result: SkillFileEntry | null }> {
161
+ for (const provider of getFileProviders()) {
162
+ if (!provider.canHandle(skillId)) continue;
163
+ // Commit to this provider — don't fall through to subsequent providers.
164
+ const result = await provider.readFileContent(skillId, sanitizedPath);
165
+ return { handled: true, result };
166
+ }
167
+ return { handled: false, result: null };
168
+ }
169
+
94
170
  // ─── Frontmatter parsing ─────────────────────────────────────────────────────
95
171
 
96
172
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -317,6 +393,7 @@ function toSlimSkillResponse(
317
393
  stars: 0,
318
394
  installs: 0,
319
395
  reports: 0,
396
+ version: "",
320
397
  };
321
398
  }
322
399
  case "skillssh": {
@@ -364,7 +441,7 @@ export async function listSkillsWithCatalog(
364
441
  const installed = listSkills(ctx);
365
442
  const installedIds = new Set(installed.map((s) => s.id));
366
443
 
367
- let catalogSkills: import("../../skills/catalog-install.js").CatalogSkill[];
444
+ let catalogSkills: CatalogSkill[];
368
445
  try {
369
446
  catalogSkills = await getCatalog();
370
447
  } catch {
@@ -376,15 +453,7 @@ export async function listSkillsWithCatalog(
376
453
  // Create SlimSkillResponses for catalog skills not already installed.
377
454
  const available: SlimSkillResponse[] = catalogSkills
378
455
  .filter((cs) => !installedIds.has(cs.id))
379
- .map((cs) => ({
380
- id: cs.id,
381
- name: cs.metadata?.vellum?.["display-name"] ?? cs.name,
382
- description: cs.description,
383
- emoji: cs.emoji,
384
- kind: "catalog" as const,
385
- origin: "vellum" as const,
386
- status: "available" as const,
387
- }));
456
+ .map((cs) => catalogSkillToSlim(cs));
388
457
 
389
458
  const merged = [...installed, ...available];
390
459
 
@@ -398,6 +467,138 @@ export async function listSkillsWithCatalog(
398
467
  return merged;
399
468
  }
400
469
 
470
+ // ─── Filtered skill listing ──────────────────────────────────────────────────
471
+
472
+ export interface SkillListFilter {
473
+ origin?: string;
474
+ kind?: string;
475
+ q?: string;
476
+ category?: string;
477
+ includeCatalog?: boolean;
478
+ }
479
+
480
+ /** Human-readable labels matching Swift's `sourceLabel`. */
481
+ export function originDisplayLabel(origin: string): string {
482
+ switch (origin) {
483
+ case "vellum":
484
+ return "Vellum";
485
+ case "clawhub":
486
+ return "Clawhub";
487
+ case "skillssh":
488
+ return "skills.sh";
489
+ case "custom":
490
+ return "Custom";
491
+ default:
492
+ return origin;
493
+ }
494
+ }
495
+
496
+ /** Check if a skill's origin matches a text query (matching Swift logic). */
497
+ export function originMatchesQuery(origin: string, query: string): boolean {
498
+ const label = originDisplayLabel(origin).toLowerCase();
499
+ if (label.includes(query)) return true;
500
+ // "community" umbrella matches clawhub and skillssh
501
+ if (
502
+ (origin === "clawhub" || origin === "skillssh") &&
503
+ "community".includes(query)
504
+ ) {
505
+ return true;
506
+ }
507
+ return false;
508
+ }
509
+
510
+ /**
511
+ * List skills with filtering, category counts, and sorting.
512
+ * Calls listSkillsWithCatalog for the full merged list, then applies filters.
513
+ */
514
+ export async function listSkillsFiltered(
515
+ filter: SkillListFilter,
516
+ ctx: SkillOperationContext,
517
+ ): Promise<{
518
+ skills: SlimSkillResponse[];
519
+ categoryCounts: Record<string, number>;
520
+ totalCount: number;
521
+ }> {
522
+ let skills =
523
+ filter.includeCatalog !== false
524
+ ? await listSkillsWithCatalog(ctx)
525
+ : listSkills(ctx);
526
+
527
+ // Apply origin filter
528
+ if (filter.origin) {
529
+ skills = skills.filter((s) => s.origin === filter.origin);
530
+ }
531
+
532
+ // Apply kind/status filter
533
+ if (filter.kind) {
534
+ switch (filter.kind) {
535
+ case "installed":
536
+ skills = skills.filter(
537
+ (s) => s.kind === "installed" || s.kind === "bundled",
538
+ );
539
+ break;
540
+ case "available":
541
+ skills = skills.filter((s) => s.status === "available");
542
+ break;
543
+ default:
544
+ skills = skills.filter((s) => s.kind === filter.kind);
545
+ break;
546
+ }
547
+ }
548
+
549
+ // Apply text search
550
+ if (filter.q) {
551
+ const query = filter.q.trim().toLowerCase();
552
+ if (query) {
553
+ skills = skills.filter((s) => {
554
+ if (s.name.toLowerCase().includes(query)) return true;
555
+ if (s.description.toLowerCase().includes(query)) return true;
556
+ if (s.id.toLowerCase().includes(query)) return true;
557
+ if (originMatchesQuery(s.origin, query)) return true;
558
+ return false;
559
+ });
560
+ }
561
+ }
562
+
563
+ // Compute category counts BEFORE applying the category filter
564
+ const categoryCounts: Record<string, number> = {};
565
+ for (const s of skills) {
566
+ const cat = inferCategory(s.name, s.description);
567
+ categoryCounts[cat] = (categoryCounts[cat] ?? 0) + 1;
568
+ }
569
+ const totalCount = skills.length;
570
+
571
+ // Apply category filter
572
+ if (filter.category) {
573
+ skills = skills.filter(
574
+ (s) => inferCategory(s.name, s.description) === filter.category,
575
+ );
576
+ }
577
+
578
+ // Sort: installed first, community origins before core within installed,
579
+ // then alphabetical by name (matching Swift sorting logic)
580
+ skills.sort((a, b) => {
581
+ // Installed (bundled + installed) before catalog (available)
582
+ const aInstalled = a.kind === "installed" || a.kind === "bundled" ? 0 : 1;
583
+ const bInstalled = b.kind === "installed" || b.kind === "bundled" ? 0 : 1;
584
+ if (aInstalled !== bInstalled) return aInstalled - bInstalled;
585
+
586
+ // Within installed, community origins (clawhub, skillssh) before core (vellum)
587
+ if (aInstalled === 0 && bInstalled === 0) {
588
+ const aCommunity =
589
+ a.origin === "clawhub" || a.origin === "skillssh" ? 0 : 1;
590
+ const bCommunity =
591
+ b.origin === "clawhub" || b.origin === "skillssh" ? 0 : 1;
592
+ if (aCommunity !== bCommunity) return aCommunity - bCommunity;
593
+ }
594
+
595
+ // Alphabetical by name
596
+ return a.name.localeCompare(b.name);
597
+ });
598
+
599
+ return { skills, categoryCounts, totalCount };
600
+ }
601
+
401
602
  /** Look up a single skill by ID from the resolved catalog, returning its SlimSkillResponse. */
402
603
  function findSkillById(
403
604
  skillId: string,
@@ -419,6 +620,32 @@ export async function getSkill(
419
620
  ): Promise<{ skill: SkillDetailResponse } | { error: string; status: number }> {
420
621
  const found = findSkillById(skillId);
421
622
  if (!found) {
623
+ // Fallback: skill is not installed. Try all file providers.
624
+ for (const provider of getFileProviders()) {
625
+ if (!provider.canHandle(skillId)) continue;
626
+ // Commit to this provider — don't fall through to subsequent providers.
627
+ const slim = await provider.toSlimSkill(skillId);
628
+ if (slim) {
629
+ // Enrich uninstalled skills.sh skills with audit data (non-fatal)
630
+ if (slim.origin === "skillssh") {
631
+ try {
632
+ const sourceRepo = slim.sourceRepo;
633
+ const skillSlug = slim.slug.split("/").pop() ?? slim.slug;
634
+ const audits = await fetchSkillAudits(sourceRepo, [skillSlug]);
635
+ if (audits[skillSlug]) {
636
+ (slim as { audit?: SkillAuditData }).audit = audits[skillSlug];
637
+ }
638
+ } catch (err) {
639
+ log.warn(
640
+ { err, skillId },
641
+ "Failed to enrich uninstalled skillssh skill with audit data",
642
+ );
643
+ }
644
+ }
645
+ return { skill: slim as SkillDetailResponse };
646
+ }
647
+ return { error: `Skill "${skillId}" not found`, status: 404 };
648
+ }
422
649
  return { error: `Skill "${skillId}" not found`, status: 404 };
423
650
  }
424
651
 
@@ -442,6 +669,7 @@ export async function getSkill(
442
669
  installs: slim.installs,
443
670
  reports: slim.reports,
444
671
  publishedAt: slim.publishedAt,
672
+ version: slim.version,
445
673
  };
446
674
  try {
447
675
  const inspectResult = await clawhubInspect(slim.slug);
@@ -476,6 +704,20 @@ export async function getSkill(
476
704
  sourceRepo: slim.sourceRepo,
477
705
  installs: slim.installs,
478
706
  };
707
+ // Enrich with audit data (non-fatal on failure)
708
+ try {
709
+ const sourceRepo = slim.sourceRepo;
710
+ const skillSlug = slim.slug.split("/").pop() ?? slim.slug;
711
+ const audits = await fetchSkillAudits(sourceRepo, [skillSlug]);
712
+ if (audits[skillSlug]) {
713
+ (detail as { audit?: SkillAuditData }).audit = audits[skillSlug];
714
+ }
715
+ } catch (err) {
716
+ log.warn(
717
+ { err, skillId },
718
+ "Failed to enrich skillssh skill detail with audit data",
719
+ );
720
+ }
479
721
  return { skill: detail };
480
722
  }
481
723
 
@@ -494,16 +736,12 @@ export async function getSkill(
494
736
 
495
737
  // ─── Skill file listing ──────────────────────────────────────────────────────
496
738
 
497
- export interface SkillFileEntry {
498
- path: string; // relative to skill directory root (e.g. "SKILL.md", "tools/foo.ts")
499
- name: string; // basename
500
- size: number;
501
- mimeType: string;
502
- isBinary: boolean;
503
- content: string | null; // inline text if ≤ 2 MB and text MIME, else null
504
- }
505
-
506
- const SKIP_DIRS = new Set(["node_modules", "__pycache__", ".git"]);
739
+ // `SkillFileEntry` lives in `../../skills/catalog-files.ts` to keep a single
740
+ // source of truth for the shape and avoid a circular import (catalog-files
741
+ // depends on `catalog-cache.ts`, which would otherwise be reachable via this
742
+ // handler module). Re-exported here so handlers can import it alongside
743
+ // the other skill handler exports.
744
+ export type { SkillFileEntry } from "../../skills/catalog-files.js";
507
745
 
508
746
  /**
509
747
  * Returns true if `filePath` is a symlink whose resolved real path escapes
@@ -550,7 +788,7 @@ function readDirRecursive(dir: string, rootDir: string): SkillFileEntry[] {
550
788
  const mimeType = Bun.file(fullPath).type;
551
789
  const isText = isTextMime(mimeType, dirent.name);
552
790
  let content: string | null = null;
553
- if (isText && stat.size <= MAX_INLINE_SIZE) {
791
+ if (isText && stat.size <= MAX_INLINE_TEXT_SIZE) {
554
792
  content = readFileSync(fullPath, "utf-8");
555
793
  }
556
794
  entries.push({
@@ -568,26 +806,180 @@ function readDirRecursive(dir: string, rootDir: string): SkillFileEntry[] {
568
806
  return entries;
569
807
  }
570
808
 
571
- export function getSkillFiles(
809
+ /**
810
+ * Read a single file's content from an installed or uninstalled skill.
811
+ *
812
+ * Installed-skill path (eager): reads the file directly from the skill's
813
+ * on-disk directory. Applies lexical containment, symlink rejection, and
814
+ * realpath containment checks for defense in depth.
815
+ *
816
+ * Provider chain fallback: when the skill id is not backed by a local
817
+ * directory, iterates the file-provider chain (vellum catalog,
818
+ * skills.sh, clawhub) until one returns content.
819
+ */
820
+ export async function getSkillFileContent(
572
821
  skillId: string,
822
+ relativePath: string,
573
823
  _ctx: SkillOperationContext,
574
- ):
575
- | { skill: SlimSkillResponse; files: SkillFileEntry[] }
576
- | { error: string; status: number } {
824
+ ): Promise<SkillFileContentResponse | { error: string; status: number }> {
825
+ const sanitized = sanitizeRelativePath(relativePath);
826
+ if (!sanitized) {
827
+ return { error: "Invalid path", status: 400 };
828
+ }
829
+
830
+ // Reject any sanitized path that references a hidden segment (dotfiles
831
+ // like `.env`, dot-dirs like `.git`) or a SKIP_DIRS segment (e.g.
832
+ // `node_modules`, `__pycache__`). Both file-listing endpoints (installed
833
+ // and catalog) intentionally omit these entries, so allowing the content
834
+ // endpoint to read them would create a data-exposure path and break
835
+ // parity with the visible file list. This check runs BEFORE both the
836
+ // installed-skill disk read and the catalog fallback so the rejection
837
+ // is uniform regardless of source.
838
+ if (hasHiddenOrSkippedSegment(sanitized)) {
839
+ return { error: "Invalid path", status: 400 };
840
+ }
841
+
577
842
  const found = findSkillById(skillId);
578
- if (!found) {
579
- return { error: `Skill "${skillId}" not found`, status: 404 };
843
+ if (found) {
844
+ if (!existsSync(found.summary.directoryPath)) {
845
+ // Resolver lists the skill as installed but the directory is missing
846
+ // on disk (corrupted install, mid-delete race, external unmount, etc.).
847
+ // Return a distinct 404 instead of falling through to the catalog path
848
+ // so the content response stays consistent with `listSkillsWithCatalog`
849
+ // and `getSkillFiles`, which classify the same id as `kind: "installed"`.
850
+ return {
851
+ error: `Skill directory missing for "${skillId}"`,
852
+ status: 404,
853
+ };
854
+ }
855
+ const dir = found.summary.directoryPath;
856
+ const abs = join(dir, sanitized);
857
+
858
+ // Lexical containment: the resolved absolute path must stay inside the
859
+ // skill directory even after `join` normalization. Cheap short-circuit
860
+ // before any fs calls.
861
+ if (!(abs === dir || abs.startsWith(dir + sep))) {
862
+ return { error: "Invalid path", status: 400 };
863
+ }
864
+
865
+ // Defense-in-depth symlink rejection: refuse to follow a symlinked file
866
+ // inside the skill dir that could point outside the root. Also catches
867
+ // symlinked parent directories via a realpath containment check.
868
+ let lstat;
869
+ try {
870
+ lstat = lstatSync(abs);
871
+ } catch {
872
+ return { error: "File not found", status: 404 };
873
+ }
874
+ if (lstat.isSymbolicLink()) {
875
+ return { error: "File not found", status: 404 };
876
+ }
877
+ if (!lstat.isFile()) {
878
+ return { error: "File not found", status: 404 };
879
+ }
880
+
881
+ let realAbs: string;
882
+ let realDir: string;
883
+ try {
884
+ realAbs = realpathSync(abs);
885
+ realDir = realpathSync(dir);
886
+ } catch {
887
+ return { error: "File not found", status: 404 };
888
+ }
889
+ if (!(realAbs === realDir || realAbs.startsWith(realDir + sep))) {
890
+ return { error: "File not found", status: 404 };
891
+ }
892
+
893
+ let stat;
894
+ try {
895
+ stat = statSync(abs);
896
+ } catch {
897
+ return { error: "File not found", status: 404 };
898
+ }
899
+ if (!stat.isFile()) {
900
+ return { error: "File not found", status: 404 };
901
+ }
902
+
903
+ const name = basename(sanitized);
904
+ const mimeType = Bun.file(abs).type;
905
+ const isText = isTextMime(mimeType, name);
906
+ const isBinary = !isText;
907
+ let content: string | null = null;
908
+ if (isText && stat.size <= MAX_INLINE_TEXT_SIZE) {
909
+ try {
910
+ content = readFileSync(abs, "utf-8");
911
+ } catch {
912
+ content = null;
913
+ }
914
+ }
915
+ return {
916
+ path: sanitized,
917
+ name,
918
+ size: stat.size,
919
+ mimeType,
920
+ isBinary,
921
+ content,
922
+ };
580
923
  }
581
924
 
582
- const dirPath = found.summary.directoryPath;
583
- if (!existsSync(dirPath)) {
584
- return { error: `Skill directory not found for "${skillId}"`, status: 404 };
925
+ // Fallback: skill is not installed. Try all file providers.
926
+ const { handled, result } = await resolveSkillFileContent(skillId, sanitized);
927
+ if (handled && result) {
928
+ return {
929
+ path: result.path,
930
+ name: result.name,
931
+ size: result.size,
932
+ mimeType: result.mimeType,
933
+ isBinary: result.isBinary,
934
+ content: result.content,
935
+ };
585
936
  }
937
+ if (handled) {
938
+ // A provider claimed this skill but the specific file wasn't found.
939
+ return { error: "File not found", status: 404 };
940
+ }
941
+ return { error: "Skill not found", status: 404 };
942
+ }
586
943
 
587
- const files = readDirRecursive(dirPath, dirPath);
588
- files.sort((a, b) => a.path.localeCompare(b.path));
944
+ export async function getSkillFiles(
945
+ skillId: string,
946
+ _ctx: SkillOperationContext,
947
+ ): Promise<
948
+ | { skill: SlimSkillResponse; files: SkillFileEntry[] }
949
+ | { error: string; status: number }
950
+ > {
951
+ // Preferred path: the skill is resolved locally (bundled, managed,
952
+ // workspace, or extra) AND its directory exists on disk. Read files
953
+ // eagerly with inline content.
954
+ const found = findSkillById(skillId);
955
+ if (found) {
956
+ if (existsSync(found.summary.directoryPath)) {
957
+ const dirPath = found.summary.directoryPath;
958
+ const files = readDirRecursive(dirPath, dirPath);
959
+ files.sort((a, b) => a.path.localeCompare(b.path));
960
+ return { skill: found.item, files };
961
+ }
962
+ // Resolver lists the skill as installed but the directory is missing
963
+ // on disk (corrupted install, mid-delete race, external unmount, etc.).
964
+ // Return a distinct 404 instead of falling through to the catalog path
965
+ // so the detail response stays consistent with `listSkillsWithCatalog`,
966
+ // which classifies the same id as `kind: "installed"`.
967
+ return {
968
+ error: `Skill directory missing for "${skillId}"`,
969
+ status: 404,
970
+ };
971
+ }
589
972
 
590
- return { skill: found.item, files };
973
+ // Fallback: skill is not installed. Try all file providers.
974
+ const resolved = await resolveSkillFiles(skillId);
975
+ if (resolved.handled && resolved.skill && resolved.files) {
976
+ return { skill: resolved.skill, files: resolved.files };
977
+ }
978
+ if (resolved.handled) {
979
+ // A provider claimed this skill but couldn't produce files/metadata.
980
+ return { error: `Skill files unavailable for "${skillId}"`, status: 404 };
981
+ }
982
+ return { error: `Skill "${skillId}" not found`, status: 404 };
591
983
  }
592
984
 
593
985
  export function enableSkill(
@@ -676,7 +1068,9 @@ export async function installSkill(
676
1068
  contactId?: string;
677
1069
  },
678
1070
  ctx: SkillOperationContext,
679
- ): Promise<{ success: true } | { success: false; error: string }> {
1071
+ ): Promise<
1072
+ { success: true; skillId: string } | { success: false; error: string }
1073
+ > {
680
1074
  try {
681
1075
  // Bundled skills are already available — no install needed
682
1076
  const catalog = loadSkillCatalog();
@@ -721,7 +1115,7 @@ export async function installSkill(
721
1115
  }
722
1116
  seedSkillGraphNodes();
723
1117
  void seedUninstalledCatalogSkillMemories().catch(() => {});
724
- return { success: true };
1118
+ return { success: true, skillId: spec.slug };
725
1119
  }
726
1120
 
727
1121
  // Check the Vellum catalog (first-party skills hosted on the platform).
@@ -742,7 +1136,7 @@ export async function installSkill(
742
1136
 
743
1137
  const skillDir = join(getWorkspaceSkillsDir(), spec.slug);
744
1138
  postInstallSkill(spec.slug, skillDir, ctx);
745
- return { success: true };
1139
+ return { success: true, skillId: spec.slug };
746
1140
  }
747
1141
  } catch (err) {
748
1142
  // If catalog lookup/install fails, fall through to community registries
@@ -770,7 +1164,7 @@ export async function installSkill(
770
1164
 
771
1165
  const skillDir = join(getWorkspaceSkillsDir(), resolved.skillSlug);
772
1166
  postInstallSkill(resolved.skillSlug, skillDir, ctx);
773
- return { success: true };
1167
+ return { success: true, skillId: resolved.skillSlug };
774
1168
  }
775
1169
 
776
1170
  // Install from clawhub (community)
@@ -798,7 +1192,7 @@ export async function installSkill(
798
1192
  upsertSkillsIndex(skillId);
799
1193
 
800
1194
  postInstallSkill(skillId, skillDir, ctx);
801
- return { success: true };
1195
+ return { success: true, skillId };
802
1196
  } catch (err) {
803
1197
  const message = err instanceof Error ? err.message : String(err);
804
1198
  log.error({ err }, "Failed to install skill");
@@ -971,6 +1365,7 @@ export async function searchSkills(
971
1365
  publishedAt: s.createdAt
972
1366
  ? new Date(s.createdAt * 1000).toISOString()
973
1367
  : undefined,
1368
+ version: s.version,
974
1369
  }));
975
1370
  } else {
976
1371
  log.warn(
@@ -992,6 +1387,50 @@ export async function searchSkills(
992
1387
  sourceRepo: r.source,
993
1388
  installs: r.installs,
994
1389
  }));
1390
+
1391
+ // Batch-fetch audit data for skills.sh results, grouped by source repo.
1392
+ try {
1393
+ if (skillsshResult.value.length > 0) {
1394
+ const sourceToSlugs = new Map<string, string[]>();
1395
+ for (const r of skillsshResult.value) {
1396
+ const slugs = sourceToSlugs.get(r.source) ?? [];
1397
+ slugs.push(r.skillId);
1398
+ sourceToSlugs.set(r.source, slugs);
1399
+ }
1400
+
1401
+ const auditResults = await Promise.allSettled(
1402
+ [...sourceToSlugs.entries()].map(([source, slugs]) =>
1403
+ fetchSkillAudits(source, slugs).then((audits) => ({
1404
+ source,
1405
+ audits,
1406
+ })),
1407
+ ),
1408
+ );
1409
+
1410
+ // Build a lookup map keyed by full skill ID (e.g. "owner/repo/skill-name")
1411
+ const auditMap = new Map<string, SkillAuditData>();
1412
+ for (const result of auditResults) {
1413
+ if (result.status !== "fulfilled") continue;
1414
+ const { source, audits } = result.value;
1415
+ for (const [skillSlug, auditData] of Object.entries(audits)) {
1416
+ auditMap.set(`${source}/${skillSlug}`, auditData);
1417
+ }
1418
+ }
1419
+
1420
+ // Enrich each skills.sh skill with audit data
1421
+ skillsshSkills = skillsshSkills.map((skill) => {
1422
+ if (skill.origin !== "skillssh") return skill;
1423
+ const audit = auditMap.get(skill.id);
1424
+ if (!audit) return skill;
1425
+ return { ...skill, audit };
1426
+ });
1427
+ }
1428
+ } catch (err) {
1429
+ log.warn(
1430
+ { err },
1431
+ "Audit fetch failed for skills.sh results, continuing without audit data",
1432
+ );
1433
+ }
995
1434
  } else {
996
1435
  log.warn(
997
1436
  { err: skillsshResult.reason },