@vellumai/assistant 0.5.10 → 0.5.12

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 (395) hide show
  1. package/AGENTS.md +8 -0
  2. package/ARCHITECTURE.md +43 -43
  3. package/Dockerfile +3 -0
  4. package/docs/architecture/integrations.md +37 -42
  5. package/docs/architecture/memory.md +7 -12
  6. package/docs/credential-execution-service.md +9 -9
  7. package/docs/skills.md +1 -1
  8. package/node_modules/@vellumai/ces-contracts/src/__tests__/grants.test.ts +7 -7
  9. package/node_modules/@vellumai/ces-contracts/src/handles.ts +5 -4
  10. package/node_modules/@vellumai/credential-storage/src/index.ts +3 -3
  11. package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
  12. package/openapi.yaml +7208 -0
  13. package/package.json +2 -1
  14. package/scripts/generate-openapi.ts +562 -0
  15. package/src/__tests__/acp-session.test.ts +239 -44
  16. package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
  17. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
  19. package/src/__tests__/browser-skill-endstate.test.ts +1 -1
  20. package/src/__tests__/btw-routes.test.ts +8 -0
  21. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
  22. package/src/__tests__/catalog-cache.test.ts +164 -0
  23. package/src/__tests__/catalog-search.test.ts +61 -0
  24. package/src/__tests__/channel-approvals.test.ts +7 -7
  25. package/src/__tests__/channel-readiness-service.test.ts +41 -0
  26. package/src/__tests__/cli-command-risk-guard.test.ts +181 -6
  27. package/src/__tests__/config-schema.test.ts +10 -2
  28. package/src/__tests__/context-memory-e2e.test.ts +2 -6
  29. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +396 -0
  30. package/src/__tests__/conversation-error.test.ts +3 -2
  31. package/src/__tests__/conversation-skill-tools.test.ts +1 -3
  32. package/src/__tests__/conversation-title-service.test.ts +2 -15
  33. package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
  34. package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
  35. package/src/__tests__/credential-security-e2e.test.ts +4 -4
  36. package/src/__tests__/credential-security-invariants.test.ts +12 -18
  37. package/src/__tests__/credential-vault-unit.test.ts +32 -34
  38. package/src/__tests__/credential-vault.test.ts +25 -33
  39. package/src/__tests__/credentials-cli.test.ts +3 -3
  40. package/src/__tests__/daemon-credential-client.test.ts +2 -2
  41. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
  42. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  43. package/src/__tests__/heartbeat-service.test.ts +35 -0
  44. package/src/__tests__/host-bash-proxy.test.ts +79 -0
  45. package/src/__tests__/host-cu-proxy.test.ts +90 -0
  46. package/src/__tests__/host-file-proxy.test.ts +89 -0
  47. package/src/__tests__/host-shell-tool.test.ts +1 -1
  48. package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
  49. package/src/__tests__/integration-status.test.ts +5 -5
  50. package/src/__tests__/list-messages-attachments.test.ts +171 -0
  51. package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
  52. package/src/__tests__/log-export-workspace.test.ts +1 -1
  53. package/src/__tests__/mcp-abort-signal.test.ts +205 -0
  54. package/src/__tests__/mcp-client-auth.test.ts +1 -1
  55. package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
  56. package/src/__tests__/memory-recall-log-store.test.ts +182 -0
  57. package/src/__tests__/memory-recall-quality.test.ts +6 -8
  58. package/src/__tests__/memory-regressions.test.ts +53 -42
  59. package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
  60. package/src/__tests__/messaging-send-tool.test.ts +5 -5
  61. package/src/__tests__/messaging-skill-split.test.ts +2 -17
  62. package/src/__tests__/notification-telegram-adapter.test.ts +125 -0
  63. package/src/__tests__/oauth-cli.test.ts +203 -649
  64. package/src/__tests__/oauth-provider-profiles.test.ts +55 -20
  65. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  66. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  67. package/src/__tests__/platform-callback-registration.test.ts +119 -0
  68. package/src/__tests__/secret-ingress-channel.test.ts +261 -0
  69. package/src/__tests__/secret-ingress-cli.test.ts +201 -0
  70. package/src/__tests__/secret-ingress-http.test.ts +312 -0
  71. package/src/__tests__/secret-ingress.test.ts +283 -0
  72. package/src/__tests__/secret-onetime-send.test.ts +4 -4
  73. package/src/__tests__/secret-routes-managed-proxy.test.ts +78 -0
  74. package/src/__tests__/secure-keys-managed-failover.test.ts +73 -0
  75. package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
  76. package/src/__tests__/skill-feature-flags.test.ts +11 -19
  77. package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
  78. package/src/__tests__/skill-load-inline-command.test.ts +3 -3
  79. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  80. package/src/__tests__/skill-memory.test.ts +2 -4
  81. package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
  82. package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
  83. package/src/__tests__/skills-uninstall.test.ts +2 -2
  84. package/src/__tests__/skills.test.ts +16 -2
  85. package/src/__tests__/slack-channel-config.test.ts +1 -1
  86. package/src/__tests__/slack-messaging-token-resolution.test.ts +22 -24
  87. package/src/__tests__/slack-share-routes.test.ts +5 -5
  88. package/src/__tests__/slack-skill.test.ts +5 -69
  89. package/src/__tests__/system-prompt.test.ts +39 -0
  90. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
  91. package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
  92. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +5 -4
  93. package/src/acp/client-handler.ts +113 -31
  94. package/src/acp/session-manager.ts +29 -27
  95. package/src/approvals/guardian-request-resolvers.ts +1 -1
  96. package/src/cli/AGENTS.md +113 -0
  97. package/src/cli/commands/autonomy.ts +3 -5
  98. package/src/cli/commands/browser-relay.ts +2 -17
  99. package/src/cli/commands/contacts.ts +6 -4
  100. package/src/cli/commands/conversations.ts +13 -1
  101. package/src/cli/commands/credential-execution.ts +17 -3
  102. package/src/cli/commands/credentials.ts +2 -8
  103. package/src/cli/commands/memory.ts +2 -3
  104. package/src/cli/commands/oauth/__tests__/connect.test.ts +706 -0
  105. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +686 -0
  106. package/src/cli/commands/oauth/__tests__/mode.test.ts +625 -0
  107. package/src/cli/commands/oauth/__tests__/ping.test.ts +631 -0
  108. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +574 -0
  109. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +416 -0
  110. package/src/cli/commands/oauth/__tests__/status.test.ts +551 -0
  111. package/src/cli/commands/oauth/__tests__/token.test.ts +420 -0
  112. package/src/cli/commands/oauth/apps.ts +87 -50
  113. package/src/cli/commands/oauth/connect.ts +405 -0
  114. package/src/cli/commands/oauth/disconnect.ts +285 -0
  115. package/src/cli/commands/oauth/index.ts +62 -20
  116. package/src/cli/commands/oauth/mode.ts +251 -0
  117. package/src/cli/commands/oauth/ping.ts +196 -0
  118. package/src/cli/commands/oauth/providers.ts +589 -55
  119. package/src/cli/commands/oauth/request.ts +564 -0
  120. package/src/cli/commands/oauth/shared.ts +114 -0
  121. package/src/cli/commands/oauth/status.ts +191 -0
  122. package/src/cli/commands/oauth/token.ts +150 -0
  123. package/src/cli/commands/platform/connect.ts +104 -0
  124. package/src/cli/commands/platform/disconnect.ts +118 -0
  125. package/src/cli/commands/platform/index.ts +252 -0
  126. package/src/cli/commands/sequence.ts +5 -4
  127. package/src/cli/commands/shotgun.ts +16 -0
  128. package/src/cli/commands/skills.ts +173 -41
  129. package/src/cli/commands/usage.ts +5 -11
  130. package/src/cli/lib/daemon-credential-client.ts +22 -38
  131. package/src/cli/program.ts +1 -1
  132. package/src/cli.ts +82 -17
  133. package/src/config/assistant-feature-flags.ts +77 -18
  134. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  135. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
  136. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  137. package/src/config/bundled-skills/conversations/SKILL.md +20 -0
  138. package/src/config/bundled-skills/conversations/TOOLS.json +23 -0
  139. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +66 -0
  140. package/src/config/bundled-skills/gmail/SKILL.md +13 -13
  141. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +3 -3
  142. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +2 -2
  143. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +1 -1
  144. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +1 -1
  145. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +1 -1
  146. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +1 -1
  147. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +2 -2
  148. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +1 -1
  149. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +1 -1
  150. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  151. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +1 -1
  152. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +1 -1
  153. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +1 -1
  154. package/src/config/bundled-skills/google-calendar/SKILL.md +10 -4
  155. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  156. package/src/config/bundled-skills/messaging/SKILL.md +19 -42
  157. package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
  158. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  159. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -2
  160. package/src/config/bundled-skills/messaging/tools/shared.ts +5 -6
  161. package/src/config/bundled-skills/notifications/SKILL.md +1 -1
  162. package/src/config/bundled-skills/schedule/SKILL.md +2 -2
  163. package/src/config/bundled-skills/settings/SKILL.md +5 -3
  164. package/src/config/bundled-skills/settings/TOOLS.json +17 -0
  165. package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
  166. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
  167. package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
  168. package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
  169. package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
  170. package/src/config/bundled-skills/slack/SKILL.md +58 -44
  171. package/src/config/bundled-tool-registry.ts +7 -19
  172. package/src/config/env.ts +5 -1
  173. package/src/config/feature-flag-registry.json +58 -42
  174. package/src/config/loader.ts +4 -0
  175. package/src/config/schemas/platform.ts +0 -8
  176. package/src/config/schemas/security.ts +9 -1
  177. package/src/config/schemas/services.ts +1 -1
  178. package/src/config/skill-state.ts +1 -3
  179. package/src/config/skills.ts +2 -4
  180. package/src/credential-execution/client.ts +1 -1
  181. package/src/credential-execution/feature-gates.ts +9 -16
  182. package/src/credential-execution/process-manager.ts +12 -0
  183. package/src/daemon/config-watcher.ts +4 -0
  184. package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
  185. package/src/daemon/conversation-agent-loop.ts +51 -2
  186. package/src/daemon/conversation-error.ts +36 -6
  187. package/src/daemon/conversation-memory.ts +0 -1
  188. package/src/daemon/conversation-messaging.ts +9 -0
  189. package/src/daemon/conversation-runtime-assembly.ts +33 -0
  190. package/src/daemon/conversation-surfaces.ts +120 -14
  191. package/src/daemon/conversation.ts +5 -0
  192. package/src/daemon/handlers/config-slack-channel.ts +43 -1
  193. package/src/daemon/handlers/conversations.ts +41 -33
  194. package/src/daemon/handlers/skills.ts +148 -3
  195. package/src/daemon/host-bash-proxy.ts +16 -0
  196. package/src/daemon/host-cu-proxy.ts +16 -0
  197. package/src/daemon/host-file-proxy.ts +16 -0
  198. package/src/daemon/lifecycle.ts +73 -3
  199. package/src/daemon/message-types/acp.ts +0 -15
  200. package/src/daemon/message-types/conversations.ts +1 -0
  201. package/src/daemon/message-types/guardian-actions.ts +2 -0
  202. package/src/daemon/message-types/host-bash.ts +6 -1
  203. package/src/daemon/message-types/host-cu.ts +6 -1
  204. package/src/daemon/message-types/host-file.ts +6 -1
  205. package/src/daemon/message-types/integrations.ts +0 -1
  206. package/src/daemon/message-types/memory.ts +0 -1
  207. package/src/daemon/message-types/messages.ts +9 -1
  208. package/src/daemon/message-types/schedules.ts +9 -0
  209. package/src/daemon/server.ts +48 -9
  210. package/src/email/feature-gate.ts +3 -3
  211. package/src/heartbeat/heartbeat-service.ts +48 -0
  212. package/src/hooks/cli.ts +74 -0
  213. package/src/inbound/platform-callback-registration.ts +68 -19
  214. package/src/mcp/client.ts +6 -1
  215. package/src/mcp/manager.ts +2 -1
  216. package/src/mcp/mcp-oauth-provider.ts +3 -3
  217. package/src/memory/app-store.ts +3 -3
  218. package/src/memory/conversation-crud.ts +213 -0
  219. package/src/memory/conversation-key-store.ts +26 -0
  220. package/src/memory/conversation-title-service.ts +7 -17
  221. package/src/memory/db-init.ts +24 -0
  222. package/src/memory/embedding-local.ts +47 -2
  223. package/src/memory/indexer.ts +13 -10
  224. package/src/memory/items-extractor.ts +12 -4
  225. package/src/memory/job-utils.ts +5 -0
  226. package/src/memory/jobs-store.ts +10 -2
  227. package/src/memory/journal-memory.ts +6 -2
  228. package/src/memory/llm-request-log-store.ts +88 -21
  229. package/src/memory/memory-recall-log-store.ts +128 -0
  230. package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
  231. package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
  232. package/src/memory/migrations/196-messages-conversation-created-at-index.ts +9 -0
  233. package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +186 -0
  234. package/src/memory/migrations/197-oauth-providers-behavior-columns.ts +29 -0
  235. package/src/memory/migrations/198-drop-setup-skill-id-column.ts +11 -0
  236. package/src/memory/migrations/index.ts +6 -0
  237. package/src/memory/migrations/registry.ts +8 -0
  238. package/src/memory/retriever.test.ts +4 -5
  239. package/src/memory/schema/infrastructure.ts +31 -0
  240. package/src/memory/schema/oauth.ts +14 -0
  241. package/src/messaging/provider.ts +13 -12
  242. package/src/messaging/providers/gmail/adapter.ts +44 -35
  243. package/src/messaging/providers/slack/adapter.ts +63 -33
  244. package/src/messaging/providers/telegram-bot/adapter.ts +7 -9
  245. package/src/messaging/providers/whatsapp/adapter.ts +6 -8
  246. package/src/notifications/adapters/telegram.ts +78 -2
  247. package/src/oauth/__tests__/identity-verifier.test.ts +464 -0
  248. package/src/oauth/byo-connection.test.ts +22 -24
  249. package/src/oauth/connect-orchestrator.ts +79 -64
  250. package/src/oauth/connect-types.ts +7 -65
  251. package/src/oauth/connection-resolver.test.ts +13 -13
  252. package/src/oauth/connection-resolver.ts +3 -4
  253. package/src/oauth/identity-verifier.ts +177 -0
  254. package/src/oauth/manual-token-connection.ts +5 -5
  255. package/src/oauth/oauth-store.ts +251 -5
  256. package/src/oauth/platform-connection.test.ts +56 -6
  257. package/src/oauth/platform-connection.ts +8 -1
  258. package/src/oauth/seed-providers.ts +256 -34
  259. package/src/permissions/checker.ts +129 -3
  260. package/src/permissions/trust-client.ts +2 -2
  261. package/src/platform/client.ts +2 -2
  262. package/src/prompts/journal-context.ts +6 -1
  263. package/src/prompts/system-prompt.ts +43 -9
  264. package/src/prompts/templates/BOOTSTRAP.md +16 -5
  265. package/src/providers/anthropic/client.ts +139 -28
  266. package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
  267. package/src/runtime/auth/route-policy.ts +0 -1
  268. package/src/runtime/btw-sidechain.ts +7 -1
  269. package/src/runtime/channel-approvals.ts +2 -2
  270. package/src/runtime/channel-readiness-service.ts +30 -7
  271. package/src/runtime/guardian-action-service.ts +7 -2
  272. package/src/runtime/http-router.ts +31 -0
  273. package/src/runtime/http-server.ts +26 -7
  274. package/src/runtime/http-types.ts +9 -0
  275. package/src/runtime/pending-interactions.ts +21 -3
  276. package/src/runtime/routes/acp-routes.ts +46 -28
  277. package/src/runtime/routes/app-management-routes.ts +123 -0
  278. package/src/runtime/routes/app-routes.ts +31 -0
  279. package/src/runtime/routes/approval-routes.ts +108 -3
  280. package/src/runtime/routes/attachment-routes.ts +45 -0
  281. package/src/runtime/routes/avatar-routes.ts +16 -0
  282. package/src/runtime/routes/brain-graph-routes.ts +18 -0
  283. package/src/runtime/routes/btw-routes.ts +20 -0
  284. package/src/runtime/routes/call-routes.ts +81 -0
  285. package/src/runtime/routes/channel-readiness-routes.ts +48 -7
  286. package/src/runtime/routes/channel-routes.ts +18 -0
  287. package/src/runtime/routes/channel-verification-routes.ts +49 -1
  288. package/src/runtime/routes/contact-routes.ts +77 -0
  289. package/src/runtime/routes/conversation-attention-routes.ts +37 -0
  290. package/src/runtime/routes/conversation-management-routes.ts +125 -0
  291. package/src/runtime/routes/conversation-query-routes.ts +78 -0
  292. package/src/runtime/routes/conversation-routes.ts +191 -39
  293. package/src/runtime/routes/conversation-starter-routes.ts +29 -0
  294. package/src/runtime/routes/debug-routes.ts +23 -0
  295. package/src/runtime/routes/diagnostics-routes.ts +30 -0
  296. package/src/runtime/routes/documents-routes.ts +42 -0
  297. package/src/runtime/routes/events-routes.ts +10 -0
  298. package/src/runtime/routes/global-search-routes.ts +35 -0
  299. package/src/runtime/routes/guardian-action-routes.ts +61 -3
  300. package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
  301. package/src/runtime/routes/heartbeat-routes.ts +278 -0
  302. package/src/runtime/routes/host-bash-routes.ts +16 -1
  303. package/src/runtime/routes/host-cu-routes.ts +23 -1
  304. package/src/runtime/routes/host-file-routes.ts +18 -1
  305. package/src/runtime/routes/identity-routes.ts +35 -0
  306. package/src/runtime/routes/inbound-message-handler.ts +46 -25
  307. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -8
  308. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
  309. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
  310. package/src/runtime/routes/integrations/slack/share.ts +1 -1
  311. package/src/runtime/routes/integrations/twilio.ts +32 -22
  312. package/src/runtime/routes/invite-routes.ts +83 -0
  313. package/src/runtime/routes/log-export-routes.ts +14 -0
  314. package/src/runtime/routes/memory-item-routes.ts +99 -1
  315. package/src/runtime/routes/migration-rollback-routes.ts +25 -0
  316. package/src/runtime/routes/migration-routes.ts +40 -0
  317. package/src/runtime/routes/notification-routes.ts +20 -0
  318. package/src/runtime/routes/oauth-apps.ts +13 -4
  319. package/src/runtime/routes/pairing-routes.ts +15 -0
  320. package/src/runtime/routes/recording-routes.ts +72 -0
  321. package/src/runtime/routes/schedule-routes.ts +77 -5
  322. package/src/runtime/routes/secret-routes.ts +99 -14
  323. package/src/runtime/routes/settings-routes.ts +102 -19
  324. package/src/runtime/routes/skills-routes.ts +141 -18
  325. package/src/runtime/routes/subagents-routes.ts +38 -3
  326. package/src/runtime/routes/surface-action-routes.ts +66 -24
  327. package/src/runtime/routes/surface-content-routes.ts +20 -0
  328. package/src/runtime/routes/telemetry-routes.ts +12 -0
  329. package/src/runtime/routes/trace-event-routes.ts +25 -0
  330. package/src/runtime/routes/trust-rules-routes.ts +46 -0
  331. package/src/runtime/routes/tts-routes.ts +15 -4
  332. package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
  333. package/src/runtime/routes/usage-routes.ts +59 -0
  334. package/src/runtime/routes/watch-routes.ts +28 -0
  335. package/src/runtime/routes/work-items-routes.ts +59 -0
  336. package/src/runtime/routes/workspace-commit-routes.ts +12 -0
  337. package/src/runtime/routes/workspace-routes.ts +102 -0
  338. package/src/schedule/integration-status.ts +2 -2
  339. package/src/schedule/scheduler.ts +7 -1
  340. package/src/security/AGENTS.md +7 -0
  341. package/src/security/ces-rpc-credential-backend.ts +19 -16
  342. package/src/security/credential-backend.ts +1 -1
  343. package/src/security/encrypted-store.ts +3 -3
  344. package/src/security/oauth-completion-page.ts +153 -0
  345. package/src/security/oauth2.ts +58 -17
  346. package/src/security/secret-ingress.ts +174 -0
  347. package/src/security/secret-patterns.ts +133 -0
  348. package/src/security/secret-scanner.ts +28 -117
  349. package/src/security/secure-keys.ts +207 -7
  350. package/src/security/token-manager.ts +3 -6
  351. package/src/signals/bash.ts +6 -1
  352. package/src/signals/confirm.ts +12 -8
  353. package/src/signals/user-message.ts +18 -3
  354. package/src/skills/catalog-cache.ts +44 -0
  355. package/src/skills/catalog-search.ts +18 -0
  356. package/src/skills/skill-memory.ts +1 -2
  357. package/src/tasks/task-runner.ts +7 -1
  358. package/src/tools/credentials/broker.ts +1 -1
  359. package/src/tools/credentials/metadata-store.ts +1 -1
  360. package/src/tools/credentials/post-connect-hooks.ts +1 -1
  361. package/src/tools/credentials/vault.ts +36 -48
  362. package/src/tools/host-terminal/host-shell.ts +16 -3
  363. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  364. package/src/tools/memory/definitions.ts +1 -1
  365. package/src/tools/memory/handlers.test.ts +2 -4
  366. package/src/tools/skills/load.ts +1 -1
  367. package/src/tools/skills/sandbox-runner.ts +16 -3
  368. package/src/tools/terminal/safe-env.ts +7 -0
  369. package/src/tools/terminal/shell.ts +16 -3
  370. package/src/tools/tool-manifest.ts +1 -1
  371. package/src/util/log-redact.ts +9 -34
  372. package/src/util/logger.ts +11 -1
  373. package/src/util/sentry-log-stream.ts +51 -0
  374. package/src/watcher/providers/github.ts +2 -2
  375. package/src/watcher/providers/gmail.ts +1 -1
  376. package/src/watcher/providers/google-calendar.ts +1 -1
  377. package/src/watcher/providers/linear.ts +2 -2
  378. package/src/workspace/migrations/011-backfill-installation-id.ts +5 -3
  379. package/src/workspace/migrations/020-rename-oauth-skill-dirs.ts +119 -0
  380. package/src/workspace/migrations/registry.ts +2 -0
  381. package/docs/architecture/keychain-broker.md +0 -68
  382. package/src/cli/commands/oauth/connections.ts +0 -734
  383. package/src/cli/commands/oauth/platform.ts +0 -525
  384. package/src/cli/commands/platform.ts +0 -176
  385. package/src/config/bundled-skills/slack/TOOLS.json +0 -272
  386. package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
  387. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
  388. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
  389. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
  390. package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
  391. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
  392. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
  393. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
  394. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
  395. package/src/oauth/provider-behaviors.ts +0 -634
@@ -0,0 +1,252 @@
1
+ import type { Command } from "commander";
2
+
3
+ import {
4
+ registerCallbackRoute,
5
+ resolvePlatformCallbackRegistrationContext,
6
+ } from "../../../inbound/platform-callback-registration.js";
7
+ import { credentialKey } from "../../../security/credential-key.js";
8
+ import { getSecureKeyViaDaemon } from "../../lib/daemon-credential-client.js";
9
+ import { log } from "../../logger.js";
10
+ import { shouldOutputJson, writeOutput } from "../../output.js";
11
+ import { CREDENTIAL_KEYS, registerPlatformConnectCommand } from "./connect.js";
12
+ import { registerPlatformDisconnectCommand } from "./disconnect.js";
13
+
14
+ export function registerPlatformCommand(program: Command): void {
15
+ const platform = program
16
+ .command("platform")
17
+ .description("Manage platform integration for containerized deployments")
18
+ .option("--json", "Machine-readable compact JSON output");
19
+
20
+ platform.addHelpText(
21
+ "after",
22
+ `
23
+ The platform subsystem manages the connection to Vellum Platform, callback
24
+ routing, containerized deployment context, and webhook forwarding for
25
+ assistants. Use 'connect', 'status', and 'disconnect' to manage platform
26
+ credentials. When IS_CONTAINERIZED=true with a configured VELLUM_PLATFORM_URL
27
+ and PLATFORM_ASSISTANT_ID, external service callbacks (Telegram webhooks,
28
+ Twilio webhooks, OAuth redirects) route through the platform's gateway proxy
29
+ instead of hitting the assistant directly.
30
+
31
+ Examples:
32
+ $ assistant platform status --json
33
+ $ assistant platform connect
34
+ $ assistant platform disconnect
35
+ $ assistant platform callback-routes register --path webhooks/telegram --type telegram --json`,
36
+ );
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // connect — store platform credentials and validate the connection
40
+ // ---------------------------------------------------------------------------
41
+
42
+ registerPlatformConnectCommand(platform);
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // status — deployment context and connection status combined
46
+ // ---------------------------------------------------------------------------
47
+
48
+ platform
49
+ .command("status")
50
+ .description(
51
+ "Show current platform deployment context and connection status",
52
+ )
53
+ .addHelpText(
54
+ "after",
55
+ `
56
+ Reads platform-related environment variables and stored credentials to report
57
+ the current containerized deployment context and connection state. Does not
58
+ require the assistant to be running.
59
+
60
+ Fields:
61
+ containerized Whether IS_CONTAINERIZED is set (boolean)
62
+ baseUrl VELLUM_PLATFORM_URL — the platform gateway base URL
63
+ assistantId PLATFORM_ASSISTANT_ID — this assistant's platform UUID
64
+ hasInternalApiKey Whether PLATFORM_INTERNAL_API_KEY is set (boolean,
65
+ value not disclosed)
66
+ hasAssistantApiKey Whether a stored assistant API key is available
67
+ available Whether callback registration prerequisites are satisfied
68
+ connected Whether platform credentials are stored (boolean)
69
+ organizationId The platform organization ID (from stored credentials)
70
+ userId The platform user ID (from stored credentials)
71
+
72
+ Examples:
73
+ $ assistant platform status
74
+ $ assistant platform status --json`,
75
+ )
76
+ .action(async (_opts: Record<string, unknown>, cmd: Command) => {
77
+ try {
78
+ const context = await resolvePlatformCallbackRegistrationContext();
79
+
80
+ const storedBaseUrl =
81
+ (await getSecureKeyViaDaemon(
82
+ credentialKey(
83
+ CREDENTIAL_KEYS.baseUrl.service,
84
+ CREDENTIAL_KEYS.baseUrl.field,
85
+ ),
86
+ )) ?? "";
87
+ const hasStoredApiKey = !!(await getSecureKeyViaDaemon(
88
+ credentialKey(
89
+ CREDENTIAL_KEYS.apiKey.service,
90
+ CREDENTIAL_KEYS.apiKey.field,
91
+ ),
92
+ ));
93
+ const organizationId =
94
+ (
95
+ await getSecureKeyViaDaemon(
96
+ credentialKey(
97
+ CREDENTIAL_KEYS.organizationId.service,
98
+ CREDENTIAL_KEYS.organizationId.field,
99
+ ),
100
+ )
101
+ )?.trim() ?? "";
102
+ const userId =
103
+ (
104
+ await getSecureKeyViaDaemon(
105
+ credentialKey(
106
+ CREDENTIAL_KEYS.userId.service,
107
+ CREDENTIAL_KEYS.userId.field,
108
+ ),
109
+ )
110
+ )?.trim() ?? "";
111
+
112
+ const connected = !!storedBaseUrl && hasStoredApiKey;
113
+
114
+ const result = {
115
+ containerized: context.containerized,
116
+ baseUrl: context.platformBaseUrl,
117
+ assistantId: context.assistantId,
118
+ hasInternalApiKey: context.hasInternalApiKey,
119
+ hasAssistantApiKey: context.hasAssistantApiKey,
120
+ available: context.enabled,
121
+ connected,
122
+ organizationId: organizationId || null,
123
+ userId: userId || null,
124
+ };
125
+
126
+ writeOutput(cmd, result);
127
+
128
+ if (!shouldOutputJson(cmd)) {
129
+ log.info(`Containerized: ${result.containerized}`);
130
+ log.info(`Base URL: ${result.baseUrl || "(not set)"}`);
131
+ log.info(`Assistant ID: ${result.assistantId || "(not set)"}`);
132
+ log.info(
133
+ `Internal API key: ${result.hasInternalApiKey ? "set" : "not set"}`,
134
+ );
135
+ log.info(
136
+ `Assistant API key: ${result.hasAssistantApiKey ? "set" : "not set"}`,
137
+ );
138
+ log.info(
139
+ `Callback registration available: ${result.available ? "yes" : "no"}`,
140
+ );
141
+ log.info(`Connected: ${connected}`);
142
+ log.info(`Organization ID: ${organizationId || "(not set)"}`);
143
+ log.info(`User ID: ${userId || "(not set)"}`);
144
+ }
145
+ } catch (err) {
146
+ const message = err instanceof Error ? err.message : String(err);
147
+ writeOutput(cmd, { ok: false, error: message });
148
+ process.exitCode = 1;
149
+ }
150
+ });
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // disconnect — remove stored platform credentials
154
+ // ---------------------------------------------------------------------------
155
+
156
+ registerPlatformDisconnectCommand(platform);
157
+
158
+ // ---------------------------------------------------------------------------
159
+ // callback-routes
160
+ // ---------------------------------------------------------------------------
161
+
162
+ const callbackRoutes = platform
163
+ .command("callback-routes")
164
+ .description("Manage platform callback route registrations");
165
+
166
+ callbackRoutes.addHelpText(
167
+ "after",
168
+ `
169
+ Callback routes tell the platform gateway how to forward inbound provider
170
+ webhooks to the correct containerized assistant instance. Each route maps a
171
+ callback path and type to a stable external URL that external services
172
+ (Telegram, Twilio, OAuth providers) should use.
173
+
174
+ Examples:
175
+ $ assistant platform callback-routes register --path webhooks/telegram --type telegram --json
176
+ $ assistant platform callback-routes register --path webhooks/twilio/voice --type twilio_voice --json`,
177
+ );
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // callback-routes register
181
+ // ---------------------------------------------------------------------------
182
+
183
+ callbackRoutes
184
+ .command("register")
185
+ .description("Register a callback route with the platform gateway")
186
+ .requiredOption(
187
+ "--path <path>",
188
+ "Callback path (e.g. webhooks/telegram, webhooks/twilio/voice)",
189
+ )
190
+ .requiredOption(
191
+ "--type <type>",
192
+ "Route type identifier (e.g. telegram, twilio_voice, twilio_status, oauth)",
193
+ )
194
+ .addHelpText(
195
+ "after",
196
+ `
197
+ Registers a callback route with the platform's internal gateway endpoint so
198
+ the platform knows how to forward inbound provider webhooks to this
199
+ containerized assistant instance.
200
+
201
+ Arguments:
202
+ --path The path portion after the ingress base URL. Leading/trailing
203
+ slashes are stripped by the platform.
204
+ --type The route type identifier used by the platform to classify and
205
+ route the callback.
206
+
207
+ Known callback path/type combinations:
208
+ --path webhooks/telegram --type telegram
209
+ --path webhooks/twilio/voice --type twilio_voice
210
+ --path webhooks/twilio/status --type twilio_status
211
+ --path oauth/callback --type oauth
212
+
213
+ Requires a containerized environment (IS_CONTAINERIZED=true) with
214
+ VELLUM_PLATFORM_URL and PLATFORM_ASSISTANT_ID configured. Returns the
215
+ platform-provided stable callback URL that external services should use.
216
+
217
+ Examples:
218
+ $ assistant platform callback-routes register --path webhooks/telegram --type telegram --json
219
+ $ assistant platform callback-routes register --path webhooks/twilio/voice --type twilio_voice --json`,
220
+ )
221
+ .action(async (opts: { path: string; type: string }, cmd: Command) => {
222
+ try {
223
+ const context = await resolvePlatformCallbackRegistrationContext();
224
+ if (!context.enabled) {
225
+ writeOutput(cmd, {
226
+ ok: false,
227
+ error:
228
+ "Platform callbacks not available — missing containerized platform registration context",
229
+ });
230
+ process.exitCode = 1;
231
+ return;
232
+ }
233
+
234
+ const callbackUrl = await registerCallbackRoute(opts.path, opts.type);
235
+
236
+ writeOutput(cmd, {
237
+ ok: true,
238
+ callbackUrl,
239
+ callbackPath: opts.path,
240
+ type: opts.type,
241
+ });
242
+
243
+ if (!shouldOutputJson(cmd)) {
244
+ log.info(`Callback route registered: ${callbackUrl}`);
245
+ }
246
+ } catch (err) {
247
+ const message = err instanceof Error ? err.message : String(err);
248
+ writeOutput(cmd, { ok: false, error: message });
249
+ process.exitCode = 1;
250
+ }
251
+ });
252
+ }
@@ -142,7 +142,7 @@ Examples:
142
142
  "after",
143
143
  `
144
144
  Arguments:
145
- id The sequence ID (e.g. seq_abc123)
145
+ id The sequence ID (e.g. seq_abc123). Run 'assistant sequence list' to find IDs.
146
146
 
147
147
  Returns full sequence details: name, status, channel, description, exit-on-reply
148
148
  setting, all steps with delay and approval configuration, and enrollment
@@ -215,7 +215,7 @@ Examples:
215
215
  "after",
216
216
  `
217
217
  Arguments:
218
- id The sequence ID to pause (e.g. seq_abc123)
218
+ id The sequence ID to pause (e.g. seq_abc123). Run 'assistant sequence list' to find IDs.
219
219
 
220
220
  Pauses a sequence, halting all scheduled step deliveries. Existing active
221
221
  enrollments remain in their current state but no new steps will be sent
@@ -246,7 +246,7 @@ Examples:
246
246
  "after",
247
247
  `
248
248
  Arguments:
249
- id The sequence ID to resume (e.g. seq_abc123)
249
+ id The sequence ID to resume (e.g. seq_abc123). Run 'assistant sequence list' to find IDs.
250
250
 
251
251
  Resumes a paused sequence, re-enabling scheduled step deliveries for all
252
252
  active enrollments. No-op if the sequence is already active.
@@ -276,7 +276,8 @@ Examples:
276
276
  "after",
277
277
  `
278
278
  Arguments:
279
- enrollmentId The enrollment ID to cancel (e.g. enr_xyz789)
279
+ enrollmentId The enrollment ID to cancel (e.g. enr_xyz789). Run 'assistant sequence get <id>'
280
+ to see enrollment IDs for a sequence.
280
281
 
281
282
  Immediately cancels a specific enrollment, stopping all future step
282
283
  deliveries for that contact in this sequence. The enrollment status
@@ -101,6 +101,19 @@ export function registerShotgunCommand(program: Command): void {
101
101
  .command("shotgun")
102
102
  .description("Start and monitor screen-watch (shotgun) sessions via IPC");
103
103
 
104
+ shotgun.addHelpText(
105
+ "after",
106
+ `
107
+ Screen-watch sessions capture periodic screenshots and feed them to the
108
+ assistant for observation. The CLI communicates with the running assistant
109
+ via IPC signal files — the assistant must be running for these commands
110
+ to work.
111
+
112
+ Examples:
113
+ $ assistant shotgun start --duration 600 --focus "browsing workflow"
114
+ $ assistant shotgun status <watchId>`,
115
+ );
116
+
104
117
  shotgun
105
118
  .command("start")
106
119
  .description("Start a new screen-watch session")
@@ -206,6 +219,9 @@ Examples:
206
219
  .addHelpText(
207
220
  "after",
208
221
  `
222
+ Arguments:
223
+ watchId The watch session ID returned by 'assistant shotgun start'.
224
+
209
225
  Queries the status of an existing screen-watch session by watchId.
210
226
 
211
227
  Output (JSON): { ok, watchId, conversationId, status }
@@ -1,3 +1,6 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
1
4
  import type { Command } from "commander";
2
5
 
3
6
  import type { CatalogSkill } from "../../skills/catalog-install.js";
@@ -8,7 +11,11 @@ import {
8
11
  readLocalCatalog,
9
12
  uninstallSkillLocally,
10
13
  } from "../../skills/catalog-install.js";
11
- import type { AuditResponse } from "../../skills/skillssh-registry.js";
14
+ import { filterByQuery } from "../../skills/catalog-search.js";
15
+ import type {
16
+ AuditResponse,
17
+ SkillsShSearchResult,
18
+ } from "../../skills/skillssh-registry.js";
12
19
  import {
13
20
  fetchSkillAudits,
14
21
  formatAuditBadges,
@@ -16,6 +23,7 @@ import {
16
23
  resolveSkillSource,
17
24
  searchSkillsRegistry,
18
25
  } from "../../skills/skillssh-registry.js";
26
+ import { getWorkspaceSkillsDir } from "../../util/platform.js";
19
27
  import { log } from "../logger.js";
20
28
 
21
29
  // ---------------------------------------------------------------------------
@@ -49,6 +57,16 @@ Examples:
49
57
  .command("list")
50
58
  .description("List available catalog skills")
51
59
  .option("--json", "Machine-readable JSON output")
60
+ .addHelpText(
61
+ "after",
62
+ `
63
+ Lists all skills available in the Vellum catalog with their ID, name,
64
+ description, and dependency information.
65
+
66
+ Examples:
67
+ $ assistant skills list
68
+ $ assistant skills list --json`,
69
+ )
52
70
  .action(async (opts: { json?: boolean }) => {
53
71
  try {
54
72
  // In dev mode, use the local catalog as the source of truth
@@ -93,20 +111,20 @@ Examples:
93
111
 
94
112
  skills
95
113
  .command("search <query>")
96
- .description("Search the skills.sh community registry")
97
- .option("--limit <n>", "Maximum number of results", "10")
114
+ .description("Search the Vellum catalog and skills.sh community registry")
115
+ .option("--limit <n>", "Maximum number of community results", "10")
98
116
  .option("--json", "Machine-readable JSON output")
99
117
  .addHelpText(
100
118
  "after",
101
119
  `
102
120
  Arguments:
103
121
  query Free-text search term matched against skill names, descriptions,
104
- and tags in the skills.sh registry.
122
+ and tags. Searches the Vellum catalog first, then the skills.sh
123
+ community registry.
105
124
 
106
- Searches the skills.sh community registry and displays matching skills
107
- with install counts and security audit badges (ATH, Socket, Snyk).
108
- Audit fetch failures are non-fatal results are still shown without
109
- security data.
125
+ Displays results from both sources with clear labels. When a skill ID
126
+ exists in both the Vellum catalog and the community registry, a conflict
127
+ note is shown with guidance on which install command to use.
110
128
 
111
129
  Examples:
112
130
  $ assistant skills search react
@@ -118,36 +136,80 @@ Examples:
118
136
  const limit = parseInt(opts.limit, 10) || 10;
119
137
 
120
138
  try {
121
- const results = await searchSkillsRegistry(query, limit);
139
+ // ── Vellum catalog search ────────────────────────────────────
140
+ const repoSkillsDir = getRepoSkillsDir();
141
+ let catalog: CatalogSkill[];
142
+ if (repoSkillsDir) {
143
+ catalog = readLocalCatalog(repoSkillsDir);
144
+ } else {
145
+ try {
146
+ catalog = await fetchCatalog();
147
+ } catch {
148
+ catalog = [];
149
+ }
150
+ }
151
+
152
+ const catalogMatches = filterByQuery(catalog, query, [
153
+ (s) => s.id,
154
+ (s) => s.name,
155
+ (s) => s.description,
156
+ ]);
157
+
158
+ // ── Community registry search (non-fatal on failure) ─────────
159
+ let registryResults: SkillsShSearchResult[] = [];
160
+ let registryError: string | undefined;
161
+ try {
162
+ registryResults = await searchSkillsRegistry(query, limit);
163
+ } catch (err) {
164
+ registryError = err instanceof Error ? err.message : String(err);
165
+ }
166
+
167
+ // ── Conflict detection ───────────────────────────────────────
168
+ const catalogIds = new Set(catalogMatches.map((s) => s.id));
169
+ const conflictIds = new Set(
170
+ registryResults
171
+ .filter((r) => catalogIds.has(r.skillId))
172
+ .map((r) => r.skillId),
173
+ );
122
174
 
123
- if (results.length === 0) {
175
+ if (catalogMatches.length === 0 && registryResults.length === 0) {
124
176
  if (json) {
125
- console.log(JSON.stringify({ ok: true, results: [], audits: {} }));
177
+ console.log(
178
+ JSON.stringify({
179
+ ok: true,
180
+ catalog: [],
181
+ community: [],
182
+ audits: {},
183
+ ...(registryError ? { registryError } : {}),
184
+ }),
185
+ );
126
186
  } else {
127
187
  log.info(`No skills found for "${query}".`);
188
+ if (registryError) {
189
+ log.warn(`(skills.sh registry unavailable: ${registryError})`);
190
+ }
128
191
  }
129
192
  return;
130
193
  }
131
194
 
132
- // Group skill slugs by source for batch audit lookups
133
- const sourceToSlugs = new Map<string, string[]>();
134
- for (const r of results) {
135
- const slugs = sourceToSlugs.get(r.source) ?? [];
136
- slugs.push(r.skillId);
137
- sourceToSlugs.set(r.source, slugs);
138
- }
139
-
140
- // Fetch audits for each unique source, keyed by source/skillId
141
- // to avoid collisions when different sources share the same slug.
195
+ // ── Fetch audits for community results ───────────────────────
142
196
  const allAudits: AuditResponse = {};
143
- for (const [source, slugs] of sourceToSlugs) {
144
- try {
145
- const audits = await fetchSkillAudits(source, slugs);
146
- for (const [skillId, auditData] of Object.entries(audits)) {
147
- allAudits[`${source}/${skillId}`] = auditData;
197
+ if (registryResults.length > 0) {
198
+ const sourceToSlugs = new Map<string, string[]>();
199
+ for (const r of registryResults) {
200
+ const slugs = sourceToSlugs.get(r.source) ?? [];
201
+ slugs.push(r.skillId);
202
+ sourceToSlugs.set(r.source, slugs);
203
+ }
204
+ for (const [source, slugs] of sourceToSlugs) {
205
+ try {
206
+ const audits = await fetchSkillAudits(source, slugs);
207
+ for (const [skillId, auditData] of Object.entries(audits)) {
208
+ allAudits[`${source}/${skillId}`] = auditData;
209
+ }
210
+ } catch {
211
+ // Audit fetch failures are non-fatal
148
212
  }
149
- } catch {
150
- // Audit fetch failures are non-fatal; display results without audits
151
213
  }
152
214
  }
153
215
 
@@ -155,26 +217,68 @@ Examples:
155
217
  console.log(
156
218
  JSON.stringify({
157
219
  ok: true,
158
- results,
220
+ catalog: catalogMatches,
221
+ community: registryResults,
159
222
  audits: allAudits,
223
+ ...(registryError ? { registryError } : {}),
160
224
  }),
161
225
  );
162
226
  return;
163
227
  }
164
228
 
165
- log.info(`Search results for "${query}" (${results.length}):\n`);
166
- for (const r of results) {
167
- log.info(` ${r.name}`);
168
- log.info(` ID: ${r.skillId}`);
169
- log.info(` Source: ${r.source}`);
170
- log.info(` Installs: ${r.installs}`);
171
- const auditData = allAudits[`${r.source}/${r.skillId}`];
172
- if (auditData) {
173
- log.info(` ${formatAuditBadges(auditData)}`);
174
- } else {
175
- log.info(" Security: no audit data");
229
+ // ── Installed-state detection ─────────────────────────────────
230
+ const skillsDir = getWorkspaceSkillsDir();
231
+ const isInstalled = (id: string) =>
232
+ existsSync(join(skillsDir, id, "SKILL.md"));
233
+
234
+ // ── Display catalog results ──────────────────────────────────
235
+ if (catalogMatches.length > 0) {
236
+ log.info(`Vellum catalog (${catalogMatches.length}):\n`);
237
+ for (const s of catalogMatches) {
238
+ const emoji = s.emoji ? `${s.emoji} ` : "";
239
+ const installed = isInstalled(s.id);
240
+ const badge = installed ? " [installed]" : "";
241
+ log.info(` ${emoji}${s.name}${badge}`);
242
+ if (s.name !== s.id) {
243
+ log.info(` ID: ${s.id}`);
244
+ }
245
+ log.info(` Description: ${s.description}`);
246
+ log.info(` Install: assistant skills install ${s.id}`);
247
+ if (conflictIds.has(s.id)) {
248
+ log.info(` NOTE: Also found in community registry`);
249
+ }
250
+ log.info("");
251
+ }
252
+ }
253
+
254
+ // ── Display community results ────────────────────────────────
255
+ if (registryResults.length > 0) {
256
+ log.info(`Community registry (${registryResults.length}):\n`);
257
+ for (const r of registryResults) {
258
+ const installed = isInstalled(r.skillId);
259
+ const badge = installed ? " [installed]" : "";
260
+ log.info(` ${r.name}${badge}`);
261
+ if (r.name !== r.skillId) {
262
+ log.info(` ID: ${r.skillId}`);
263
+ }
264
+ log.info(` Source: ${r.source}`);
265
+ log.info(` Installs: ${r.installs}`);
266
+ const auditData = allAudits[`${r.source}/${r.skillId}`];
267
+ if (auditData) {
268
+ log.info(` ${formatAuditBadges(auditData)}`);
269
+ } else {
270
+ log.info(" Security: no audit data");
271
+ }
272
+ log.info(
273
+ ` Install: assistant skills add ${r.source}@${r.skillId}`,
274
+ );
275
+ if (conflictIds.has(r.skillId)) {
276
+ log.info(` NOTE: Conflicts with Vellum catalog skill`);
277
+ }
278
+ log.info("");
176
279
  }
177
- log.info("");
280
+ } else if (registryError) {
281
+ log.warn(`\n(skills.sh registry unavailable: ${registryError})`);
178
282
  }
179
283
  } catch (err) {
180
284
  const msg = err instanceof Error ? err.message : String(err);
@@ -192,6 +296,21 @@ Examples:
192
296
  .description("Install a skill from the catalog")
193
297
  .option("--overwrite", "Replace an already installed skill")
194
298
  .option("--json", "Machine-readable JSON output")
299
+ .addHelpText(
300
+ "after",
301
+ `
302
+ Arguments:
303
+ skill-id Skill identifier from the Vellum catalog. Run 'assistant skills list'
304
+ to see available IDs. For community skills, use 'assistant skills add'.
305
+
306
+ Downloads and installs the skill into the workspace skills directory. If the
307
+ skill is already installed, use --overwrite to replace it.
308
+
309
+ Examples:
310
+ $ assistant skills install weather
311
+ $ assistant skills install weather --overwrite
312
+ $ assistant skills install weather --json`,
313
+ )
195
314
  .action(
196
315
  async (
197
316
  skillId: string,
@@ -244,6 +363,19 @@ Examples:
244
363
  .command("uninstall <skill-id>")
245
364
  .description("Uninstall a previously installed skill")
246
365
  .option("--json", "Machine-readable JSON output")
366
+ .addHelpText(
367
+ "after",
368
+ `
369
+ Arguments:
370
+ skill-id Skill identifier to remove. Run 'assistant skills list' to see
371
+ installed skills.
372
+
373
+ Removes the skill directory from the workspace. This action cannot be undone.
374
+
375
+ Examples:
376
+ $ assistant skills uninstall weather
377
+ $ assistant skills uninstall weather --json`,
378
+ )
247
379
  .action(async (skillId: string, opts: { json?: boolean }) => {
248
380
  const json = opts.json ?? false;
249
381
 
@@ -178,11 +178,6 @@ Reads from the local LLM usage event ledger (llm_usage_events table) to
178
178
  display token consumption and cost data. Operates on the local SQLite
179
179
  database directly — does not require the assistant to be running.
180
180
 
181
- Subcommands:
182
- totals Aggregate totals for a time range (default when no subcommand given)
183
- daily Per-day token and cost breakdown
184
- breakdown Grouped breakdown by actor, provider, or model
185
-
186
181
  Time range can be specified with --range presets (today, week, month, all)
187
182
  or explicit --from / --to epoch-millisecond timestamps.
188
183
 
@@ -285,12 +280,11 @@ Examples:
285
280
  .addHelpText(
286
281
  "after",
287
282
  `
288
- Arguments:
289
- --group-by <dimension> One of: actor, provider, model (default: model)
290
- actor Groups by the subsystem that made the call (main_agent,
291
- title_generator, etc.)
292
- provider Groups by LLM provider (anthropic, openai, etc.)
293
- model Groups by model name (claude-sonnet-4-20250514, etc.)
283
+ Grouping dimensions:
284
+ actor Groups by the subsystem that made the call (main_agent,
285
+ title_generator, etc.)
286
+ provider Groups by LLM provider (anthropic, openai, etc.)
287
+ model Groups by model name (claude-sonnet-4-20250514, etc.)
294
288
 
295
289
  Shows one row per group with input/output tokens, estimated cost, and
296
290
  call count. Rows are sorted by cost descending.