@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
@@ -10,6 +10,8 @@
10
10
  import { readFileSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
 
13
+ import { z } from "zod";
14
+
13
15
  import {
14
16
  getPlatformBaseUrl,
15
17
  setIngressPublicBaseUrl,
@@ -28,10 +30,6 @@ import {
28
30
  getMostRecentAppByProvider,
29
31
  getProvider,
30
32
  } from "../../oauth/oauth-store.js";
31
- import {
32
- getProviderBehavior,
33
- resolveService,
34
- } from "../../oauth/provider-behaviors.js";
35
33
  import {
36
34
  check,
37
35
  classifyRisk,
@@ -168,14 +166,14 @@ async function handleOAuthConnectStart(body: {
168
166
  return httpError("BAD_REQUEST", "Missing required field: service", 400);
169
167
  }
170
168
 
171
- const resolvedService = resolveService(body.service);
169
+ const service = body.service;
172
170
 
173
171
  // Resolve client_id and client_secret from oauth-store.
174
172
  let clientId: string | undefined;
175
173
  let clientSecret: string | undefined;
176
174
 
177
175
  // Try existing connection first (re-auth flow)
178
- const conn = getConnectionByProvider(resolvedService);
176
+ const conn = getConnectionByProvider(service);
179
177
  if (conn) {
180
178
  const app = getApp(conn.oauthAppId);
181
179
  if (app) {
@@ -186,7 +184,7 @@ async function handleOAuthConnectStart(body: {
186
184
 
187
185
  // Fall back to most recent app for this provider (first-time connect with stored app)
188
186
  if (!clientId) {
189
- const dbApp = getMostRecentAppByProvider(resolvedService);
187
+ const dbApp = getMostRecentAppByProvider(service);
190
188
  if (dbApp) {
191
189
  clientId = dbApp.clientId;
192
190
  if (!clientSecret) {
@@ -200,20 +198,17 @@ async function handleOAuthConnectStart(body: {
200
198
  if (!clientId) {
201
199
  return httpError(
202
200
  "BAD_REQUEST",
203
- `No client_id found for "${body.service}". Store it first via the credential vault.`,
201
+ `No client_id found for "${service}". Store it first via the credential vault.`,
204
202
  400,
205
203
  );
206
204
  }
207
205
 
208
- const behavior = getProviderBehavior(resolvedService);
209
- const providerRow = getProvider(resolvedService);
210
- const requiresSecret =
211
- behavior?.setup?.requiresClientSecret ??
212
- !!(providerRow?.tokenEndpointAuthMethod || providerRow?.extraParams);
206
+ const providerRow = getProvider(service);
207
+ const requiresSecret = !!providerRow?.requiresClientSecret;
213
208
  if (requiresSecret && !clientSecret) {
214
209
  return httpError(
215
210
  "BAD_REQUEST",
216
- `client_secret is required for "${body.service}" but not found in the credential store. Store it first via the credential vault.`,
211
+ `client_secret is required for "${service}" but not found in the credential store. Store it first via the credential vault.`,
217
212
  400,
218
213
  );
219
214
  }
@@ -224,7 +219,7 @@ async function handleOAuthConnectStart(body: {
224
219
  let authUrl: string | undefined;
225
220
 
226
221
  const result = await orchestrateOAuthConnect({
227
- service: body.service,
222
+ service,
228
223
  requestedScopes: body.requestedScopes,
229
224
  clientId,
230
225
  clientSecret,
@@ -236,7 +231,7 @@ async function handleOAuthConnectStart(body: {
236
231
  // Prefer accountInfo from oauth-store when available.
237
232
  let accountInfo = deferredResult.accountInfo;
238
233
  try {
239
- const conn = getConnectionByProvider(resolvedService);
234
+ const conn = getConnectionByProvider(service);
240
235
  if (conn?.accountInfo) accountInfo = conn.accountInfo;
241
236
  } catch {
242
237
  // DB not ready — use orchestrator value
@@ -275,7 +270,7 @@ async function handleOAuthConnectStart(body: {
275
270
 
276
271
  if (!result.success) {
277
272
  log.error(
278
- { err: result.error, service: body.service },
273
+ { err: result.error, service },
279
274
  "OAuth connect orchestrator returned error",
280
275
  );
281
276
  return httpError(
@@ -296,7 +291,7 @@ async function handleOAuthConnectStart(body: {
296
291
  // Prefer accountInfo from oauth-store when available.
297
292
  let responseAccountInfo = result.accountInfo;
298
293
  try {
299
- const conn = getConnectionByProvider(resolvedService);
294
+ const conn = getConnectionByProvider(service);
300
295
  if (conn?.accountInfo) responseAccountInfo = conn.accountInfo;
301
296
  } catch {
302
297
  // DB not ready — use orchestrator value
@@ -310,7 +305,7 @@ async function handleOAuthConnectStart(body: {
310
305
  });
311
306
  } catch (err) {
312
307
  const message = err instanceof Error ? err.message : String(err);
313
- log.error({ err, service: body.service }, "OAuth connect flow failed");
308
+ log.error({ err, service }, "OAuth connect flow failed");
314
309
  return httpError("INTERNAL_ERROR", sanitizeOAuthError(message), 500);
315
310
  }
316
311
  }
@@ -612,6 +607,12 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
612
607
  endpoint: "settings/voice",
613
608
  method: "PUT",
614
609
  policyKey: "settings/voice",
610
+ summary: "Update voice activation key",
611
+ description: "Validate and normalize a voice activation key.",
612
+ tags: ["settings"],
613
+ requestBody: z.object({
614
+ activationKey: z.string(),
615
+ }),
615
616
  handler: async ({ req }) => {
616
617
  const body = (await req.json()) as { activationKey?: string };
617
618
  if (!body.activationKey) {
@@ -626,6 +627,12 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
626
627
  endpoint: "settings/avatar/generate",
627
628
  method: "POST",
628
629
  policyKey: "settings/avatar/generate",
630
+ summary: "Generate avatar",
631
+ description: "Generate an AI avatar image from a text description.",
632
+ tags: ["settings"],
633
+ requestBody: z.object({
634
+ description: z.string(),
635
+ }),
629
636
  handler: async ({ req }) => {
630
637
  const body = (await req.json()) as { description?: string };
631
638
  return handleGenerateAvatar(body.description ?? "");
@@ -637,6 +644,13 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
637
644
  endpoint: "settings/client",
638
645
  method: "PUT",
639
646
  policyKey: "settings/client",
647
+ summary: "Update client setting",
648
+ description: "Set a single client-side setting key/value pair.",
649
+ tags: ["settings"],
650
+ requestBody: z.object({
651
+ key: z.string(),
652
+ value: z.string(),
653
+ }),
640
654
  handler: async ({ req }) => {
641
655
  const body = (await req.json()) as { key?: string; value?: string };
642
656
  if (!body.key || body.value === undefined) {
@@ -651,6 +665,14 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
651
665
  endpoint: "oauth/start",
652
666
  method: "POST",
653
667
  policyKey: "oauth/start",
668
+ summary: "Start OAuth flow",
669
+ description:
670
+ "Initiate an OAuth authorization flow for a third-party service.",
671
+ tags: ["oauth"],
672
+ requestBody: z.object({
673
+ service: z.string(),
674
+ requestedScopes: z.array(z.unknown()),
675
+ }),
654
676
  handler: async ({ req }) => {
655
677
  const body = (await req.json()) as {
656
678
  service?: string;
@@ -665,6 +687,13 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
665
687
  endpoint: "integrations/oauth/start",
666
688
  method: "POST",
667
689
  policyKey: "integrations/oauth/start",
690
+ summary: "Start OAuth flow (legacy)",
691
+ description: "Legacy alias for oauth/start.",
692
+ tags: ["oauth"],
693
+ requestBody: z.object({
694
+ service: z.string(),
695
+ requestedScopes: z.array(z.unknown()),
696
+ }),
668
697
  handler: async ({ req }) => {
669
698
  const body = (await req.json()) as {
670
699
  service?: string;
@@ -679,12 +708,18 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
679
708
  endpoint: "workspace-files",
680
709
  method: "GET",
681
710
  policyKey: "workspace-files",
711
+ summary: "List workspace files",
712
+ description: "Return an array of files in the workspace directory.",
713
+ tags: ["workspace"],
682
714
  handler: () => handleWorkspaceFilesList(),
683
715
  },
684
716
  {
685
717
  endpoint: "workspace-files/read",
686
718
  method: "GET",
687
719
  policyKey: "workspace-files/read",
720
+ summary: "Read a workspace file",
721
+ description: "Return the contents of a single file by path.",
722
+ tags: ["workspace"],
688
723
  handler: ({ url }) => {
689
724
  const filePath = url.searchParams.get("path") ?? "";
690
725
  if (!filePath) {
@@ -703,6 +738,10 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
703
738
  endpoint: "tools",
704
739
  method: "GET",
705
740
  policyKey: "tools",
741
+ summary: "List tools",
742
+ description:
743
+ "Return available tool names with their descriptions, risk levels, and categories.",
744
+ tags: ["tools"],
706
745
  handler: () => handleToolNamesList(),
707
746
  },
708
747
 
@@ -711,6 +750,17 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
711
750
  endpoint: "tools/simulate-permission",
712
751
  method: "POST",
713
752
  policyKey: "tools/simulate-permission",
753
+ summary: "Simulate tool permission check",
754
+ description:
755
+ "Dry-run a permission check for a tool invocation without executing it.",
756
+ tags: ["tools"],
757
+ requestBody: z.object({
758
+ toolName: z.string(),
759
+ input: z.object({}).passthrough(),
760
+ workingDir: z.string(),
761
+ forcePromptSideEffects: z.boolean(),
762
+ isInteractive: z.boolean(),
763
+ }),
714
764
  handler: async ({ req }) => {
715
765
  const body = (await req.json()) as {
716
766
  toolName?: string;
@@ -728,6 +778,10 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
728
778
  endpoint: "diagnostics/env-vars",
729
779
  method: "GET",
730
780
  policyKey: "diagnostics/env-vars",
781
+ summary: "List safe environment variables",
782
+ description:
783
+ "Return environment variable names and values that are safe to expose (no secrets).",
784
+ tags: ["diagnostics"],
731
785
  handler: () => handleEnvVars(),
732
786
  },
733
787
 
@@ -736,6 +790,13 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
736
790
  endpoint: "config/platform",
737
791
  method: "GET",
738
792
  policyKey: "config/platform:GET",
793
+ summary: "Get platform config",
794
+ description: "Return the platform base URL configuration.",
795
+ tags: ["config"],
796
+ responseBody: z.object({
797
+ baseUrl: z.string(),
798
+ success: z.boolean(),
799
+ }),
739
800
  handler: () => {
740
801
  const raw = loadRawConfig();
741
802
  const platform = (raw?.platform ?? {}) as Record<string, unknown>;
@@ -748,6 +809,12 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
748
809
  endpoint: "config/platform",
749
810
  method: "PUT",
750
811
  policyKey: "config/platform",
812
+ summary: "Update platform config",
813
+ description: "Set the platform base URL.",
814
+ tags: ["config"],
815
+ requestBody: z.object({
816
+ baseUrl: z.string(),
817
+ }),
751
818
  handler: async ({ req }) => {
752
819
  try {
753
820
  const body = (await req.json()) as { baseUrl?: string };
@@ -773,12 +840,28 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
773
840
  endpoint: "integrations/ingress/config",
774
841
  method: "GET",
775
842
  policyKey: "integrations/ingress/config:GET",
843
+ summary: "Get ingress config",
844
+ description: "Return the current ingress tunnel configuration.",
845
+ tags: ["config"],
776
846
  handler: () => Response.json(getIngressConfigResult()),
777
847
  },
778
848
  {
779
849
  endpoint: "integrations/ingress/config",
780
850
  method: "PUT",
781
851
  policyKey: "integrations/ingress/config",
852
+ summary: "Update ingress config",
853
+ description: "Set the ingress public base URL and enabled state.",
854
+ tags: ["config"],
855
+ requestBody: z.object({
856
+ publicBaseUrl: z.string(),
857
+ enabled: z.boolean(),
858
+ }),
859
+ responseBody: z.object({
860
+ enabled: z.boolean(),
861
+ publicBaseUrl: z.string(),
862
+ localGatewayTarget: z.string(),
863
+ success: z.boolean(),
864
+ }),
782
865
  handler: async ({ req }) => {
783
866
  try {
784
867
  const body = (await req.json()) as {
@@ -5,6 +5,8 @@
5
5
  * using the standalone functions extracted in `../../daemon/handlers/skills.ts`.
6
6
  */
7
7
 
8
+ import { z } from "zod";
9
+
8
10
  import type {
9
11
  CreateSkillParams,
10
12
  SkillOperationContext,
@@ -21,6 +23,7 @@ import {
21
23
  inspectSkill,
22
24
  installSkill,
23
25
  listSkills,
26
+ listSkillsWithCatalog,
24
27
  searchSkills,
25
28
  uninstallSkill,
26
29
  updateSkill,
@@ -40,22 +43,69 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
40
43
  const ctx = () => deps.getSkillContext();
41
44
 
42
45
  return [
43
- // GET /v1/skills — list all skills
44
46
  {
45
47
  endpoint: "skills",
46
48
  method: "GET",
47
49
  policyKey: "skills",
48
- handler: () => {
49
- const skills = listSkills(ctx());
50
+ summary: "List all skills",
51
+ description:
52
+ "Return all installed skills. Pass ?include=catalog to also include available catalog skills.",
53
+ tags: ["skills"],
54
+ queryParams: [
55
+ {
56
+ name: "include",
57
+ schema: { type: "string", enum: ["catalog"] },
58
+ description:
59
+ "Optional inclusion flag. Use 'catalog' to merge available Vellum catalog skills into the response.",
60
+ },
61
+ ],
62
+ responseBody: z.object({
63
+ skills: z
64
+ .array(
65
+ z.object({
66
+ id: z.string(),
67
+ name: z.string(),
68
+ description: z.string(),
69
+ emoji: z.string().optional(),
70
+ homepage: z.string().optional(),
71
+ source: z.enum([
72
+ "bundled",
73
+ "managed",
74
+ "workspace",
75
+ "clawhub",
76
+ "extra",
77
+ "catalog",
78
+ ]),
79
+ state: z.enum(["enabled", "disabled"]),
80
+ installStatus: z.enum(["bundled", "installed", "available"]),
81
+ updateAvailable: z.boolean(),
82
+ provenance: z.object({
83
+ kind: z.enum(["first-party", "third-party", "local"]),
84
+ provider: z.string().optional(),
85
+ originId: z.string().optional(),
86
+ sourceUrl: z.string().optional(),
87
+ }),
88
+ }),
89
+ )
90
+ .describe("Skill objects"),
91
+ }),
92
+ handler: async ({ url }) => {
93
+ const include = url.searchParams.get("include");
94
+ const skills =
95
+ include === "catalog"
96
+ ? await listSkillsWithCatalog(ctx())
97
+ : listSkills(ctx());
50
98
  return Response.json({ skills });
51
99
  },
52
100
  },
53
101
 
54
- // GET /v1/skills/:id/files — skill metadata + directory contents
55
102
  {
56
103
  endpoint: "skills/:id/files",
57
104
  method: "GET",
58
105
  policyKey: "skills",
106
+ summary: "Get skill files",
107
+ description: "Return skill metadata and directory contents.",
108
+ tags: ["skills"],
59
109
  handler: ({ params }) => {
60
110
  const result = getSkillFiles(params.id, ctx());
61
111
  if ("error" in result) {
@@ -68,11 +118,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
68
118
  },
69
119
  },
70
120
 
71
- // POST /v1/skills/:id/enable — enable skill
72
121
  {
73
122
  endpoint: "skills/:id/enable",
74
123
  method: "POST",
75
124
  policyKey: "skills",
125
+ summary: "Enable skill",
126
+ description: "Enable an installed skill.",
127
+ tags: ["skills"],
128
+ responseBody: z.object({
129
+ ok: z.boolean(),
130
+ }),
76
131
  handler: ({ params }) => {
77
132
  const result = enableSkill(params.id, ctx());
78
133
  if (!result.success) {
@@ -82,11 +137,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
82
137
  },
83
138
  },
84
139
 
85
- // POST /v1/skills/:id/disable — disable skill
86
140
  {
87
141
  endpoint: "skills/:id/disable",
88
142
  method: "POST",
89
143
  policyKey: "skills",
144
+ summary: "Disable skill",
145
+ description: "Disable an installed skill.",
146
+ tags: ["skills"],
147
+ responseBody: z.object({
148
+ ok: z.boolean(),
149
+ }),
90
150
  handler: ({ params }) => {
91
151
  const result = disableSkill(params.id, ctx());
92
152
  if (!result.success) {
@@ -96,11 +156,21 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
96
156
  },
97
157
  },
98
158
 
99
- // PATCH /v1/skills/:id/config — configure skill
100
159
  {
101
160
  endpoint: "skills/:id/config",
102
161
  method: "PATCH",
103
162
  policyKey: "skills",
163
+ summary: "Configure skill",
164
+ description: "Update skill configuration (env, apiKey, config).",
165
+ tags: ["skills"],
166
+ requestBody: z.object({
167
+ env: z.object({}).passthrough().describe("Environment variables"),
168
+ apiKey: z.string(),
169
+ config: z.object({}).passthrough().describe("Arbitrary config"),
170
+ }),
171
+ responseBody: z.object({
172
+ ok: z.boolean(),
173
+ }),
104
174
  handler: async ({ req, params }) => {
105
175
  const body = (await req.json()) as {
106
176
  env?: Record<string, string>;
@@ -119,11 +189,22 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
119
189
  },
120
190
  },
121
191
 
122
- // POST /v1/skills/install — install skill
123
192
  {
124
193
  endpoint: "skills/install",
125
194
  method: "POST",
126
195
  policyKey: "skills",
196
+ summary: "Install skill",
197
+ description: "Install a skill by slug, URL, or spec.",
198
+ tags: ["skills"],
199
+ requestBody: z.object({
200
+ slug: z.string().describe("Skill slug"),
201
+ url: z.string().describe("Skill URL"),
202
+ spec: z.string().describe("Skill spec"),
203
+ version: z.string(),
204
+ }),
205
+ responseBody: z.object({
206
+ ok: z.boolean(),
207
+ }),
127
208
  handler: async ({ req }) => {
128
209
  const body = (await req.json()) as {
129
210
  slug?: string;
@@ -150,11 +231,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
150
231
  },
151
232
  },
152
233
 
153
- // POST /v1/skills/check-updates — check for updates
154
234
  {
155
235
  endpoint: "skills/check-updates",
156
236
  method: "POST",
157
237
  policyKey: "skills",
238
+ summary: "Check skill updates",
239
+ description: "Check for available updates to installed skills.",
240
+ tags: ["skills"],
241
+ responseBody: z.object({
242
+ data: z.object({}).passthrough().describe("Update availability info"),
243
+ }),
158
244
  handler: async () => {
159
245
  const result = await checkSkillUpdates(ctx());
160
246
  if (!result.success) {
@@ -164,11 +250,23 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
164
250
  },
165
251
  },
166
252
 
167
- // GET /v1/skills/search — search catalog
168
253
  {
169
254
  endpoint: "skills/search",
170
255
  method: "GET",
171
256
  policyKey: "skills",
257
+ summary: "Search skill catalog",
258
+ description: "Search the skill catalog by query string.",
259
+ tags: ["skills"],
260
+ queryParams: [
261
+ {
262
+ name: "q",
263
+ schema: { type: "string" },
264
+ description: "Search query (required)",
265
+ },
266
+ ],
267
+ responseBody: z.object({
268
+ data: z.object({}).passthrough().describe("Search results"),
269
+ }),
172
270
  handler: async ({ url }) => {
173
271
  const query = url.searchParams.get("q") ?? "";
174
272
  if (!query) {
@@ -182,11 +280,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
182
280
  },
183
281
  },
184
282
 
185
- // POST /v1/skills/draft — draft new skill
186
283
  {
187
284
  endpoint: "skills/draft",
188
285
  method: "POST",
189
286
  policyKey: "skills",
287
+ summary: "Draft a skill",
288
+ description: "Generate a skill draft from source text.",
289
+ tags: ["skills"],
290
+ requestBody: z.object({
291
+ sourceText: z.string().describe("Source text for drafting"),
292
+ }),
190
293
  handler: async ({ req }) => {
191
294
  const body = (await req.json()) as { sourceText?: string };
192
295
  if (!body.sourceText || typeof body.sourceText !== "string") {
@@ -204,11 +307,22 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
204
307
  },
205
308
  },
206
309
 
207
- // POST /v1/skills — create skill
208
310
  {
209
311
  endpoint: "skills",
210
312
  method: "POST",
211
313
  policyKey: "skills",
314
+ summary: "Create skill",
315
+ description: "Create a new skill.",
316
+ tags: ["skills"],
317
+ requestBody: z.object({
318
+ skillId: z.string(),
319
+ name: z.string(),
320
+ description: z.string(),
321
+ bodyMarkdown: z.string(),
322
+ }),
323
+ responseBody: z.object({
324
+ ok: z.boolean(),
325
+ }),
212
326
  handler: async ({ req }) => {
213
327
  const body = (await req.json()) as CreateSkillParams;
214
328
  if (
@@ -231,11 +345,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
231
345
  },
232
346
  },
233
347
 
234
- // POST /v1/skills/:id/update — update skill
235
348
  {
236
349
  endpoint: "skills/:id/update",
237
350
  method: "POST",
238
351
  policyKey: "skills",
352
+ summary: "Update skill",
353
+ description: "Update an installed skill to the latest version.",
354
+ tags: ["skills"],
355
+ responseBody: z.object({
356
+ ok: z.boolean(),
357
+ }),
239
358
  handler: async ({ params }) => {
240
359
  const result = await updateSkill(params.id, ctx());
241
360
  if (!result.success) {
@@ -245,11 +364,13 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
245
364
  },
246
365
  },
247
366
 
248
- // GET /v1/skills/:id/inspect — inspect skill details
249
367
  {
250
368
  endpoint: "skills/:id/inspect",
251
369
  method: "GET",
252
370
  policyKey: "skills",
371
+ summary: "Inspect skill",
372
+ description: "Return detailed skill information.",
373
+ tags: ["skills"],
253
374
  handler: async ({ params }) => {
254
375
  const result = await inspectSkill(params.id, ctx());
255
376
  if (result.error && !result.data) {
@@ -269,13 +390,13 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
269
390
  },
270
391
  },
271
392
 
272
- // GET /v1/skills/:id — single skill metadata
273
- // Registered after all literal-segment GET routes (search, inspect, files)
274
- // to avoid shadowing them with the parametric :id match.
275
393
  {
276
394
  endpoint: "skills/:id",
277
395
  method: "GET",
278
396
  policyKey: "skills",
397
+ summary: "Get skill",
398
+ description: "Return a single skill by ID.",
399
+ tags: ["skills"],
279
400
  handler: ({ params }) => {
280
401
  const result = getSkill(params.id, ctx());
281
402
  if ("error" in result) {
@@ -288,11 +409,13 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
288
409
  },
289
410
  },
290
411
 
291
- // DELETE /v1/skills/:id — uninstall skill
292
412
  {
293
413
  endpoint: "skills/:id",
294
414
  method: "DELETE",
295
415
  policyKey: "skills",
416
+ summary: "Uninstall skill",
417
+ description: "Remove an installed skill.",
418
+ tags: ["skills"],
296
419
  handler: async ({ params }) => {
297
420
  const result = await uninstallSkill(params.id, ctx());
298
421
  if (!result.success) {
@@ -5,6 +5,8 @@
5
5
  * sharing business logic with the handlers in
6
6
  * `daemon/handlers/subagents.ts`.
7
7
  */
8
+ import { z } from "zod";
9
+
8
10
  import { getMessages } from "../../memory/conversation-crud.js";
9
11
  import { getSubagentManager } from "../../subagent/index.js";
10
12
  import { getLogger } from "../../util/logger.js";
@@ -121,11 +123,25 @@ export function getSubagentDetail(
121
123
 
122
124
  export function subagentRouteDefinitions(): RouteDefinition[] {
123
125
  return [
124
- // GET /v1/subagents/:id — get subagent detail
125
126
  {
126
127
  endpoint: "subagents/:id",
127
128
  method: "GET",
128
129
  policyKey: "subagents",
130
+ summary: "Get subagent detail",
131
+ description: "Return subagent objective and event history.",
132
+ tags: ["subagents"],
133
+ queryParams: [
134
+ {
135
+ name: "conversationId",
136
+ schema: { type: "string" },
137
+ description: "Parent conversation ID (required)",
138
+ },
139
+ ],
140
+ responseBody: z.object({
141
+ subagentId: z.string(),
142
+ objective: z.string(),
143
+ events: z.array(z.unknown()).describe("Subagent event objects"),
144
+ }),
129
145
  handler: ({ url, params }) => {
130
146
  const conversationId = url.searchParams.get("conversationId");
131
147
  if (!conversationId) {
@@ -152,11 +168,20 @@ export function subagentRouteDefinitions(): RouteDefinition[] {
152
168
  },
153
169
  },
154
170
 
155
- // POST /v1/subagents/:id/abort — abort subagent
156
171
  {
157
172
  endpoint: "subagents/:id/abort",
158
173
  method: "POST",
159
174
  policyKey: "subagents/abort",
175
+ summary: "Abort subagent",
176
+ description: "Abort a running subagent.",
177
+ tags: ["subagents"],
178
+ requestBody: z.object({
179
+ conversationId: z.string(),
180
+ }),
181
+ responseBody: z.object({
182
+ subagentId: z.string(),
183
+ aborted: z.boolean(),
184
+ }),
160
185
  handler: async ({ req, params }) => {
161
186
  const body = (await req.json()) as { conversationId?: string };
162
187
  const conversationId = body.conversationId;
@@ -187,11 +212,21 @@ export function subagentRouteDefinitions(): RouteDefinition[] {
187
212
  },
188
213
  },
189
214
 
190
- // POST /v1/subagents/:id/message — send message to subagent
191
215
  {
192
216
  endpoint: "subagents/:id/message",
193
217
  method: "POST",
194
218
  policyKey: "subagents/message",
219
+ summary: "Send message to subagent",
220
+ description: "Send a text message to a running subagent.",
221
+ tags: ["subagents"],
222
+ requestBody: z.object({
223
+ conversationId: z.string(),
224
+ content: z.string(),
225
+ }),
226
+ responseBody: z.object({
227
+ subagentId: z.string(),
228
+ sent: z.boolean(),
229
+ }),
195
230
  handler: async ({ req, params }) => {
196
231
  const body = (await req.json()) as {
197
232
  conversationId?: string;