@vellumai/assistant 0.8.7 → 0.8.8

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 (387) hide show
  1. package/Dockerfile +20 -4
  2. package/docker-entrypoint.sh +4 -2
  3. package/docker-init-apt-root.sh +3 -1
  4. package/docker-kata-apt-env.sh +3 -1
  5. package/docker-kata-runtime-family.sh +12 -0
  6. package/docs/architecture/memory.md +1 -1
  7. package/docs/plugins.md +75 -79
  8. package/examples/plugins/echo/README.md +6 -12
  9. package/examples/plugins/echo/register.ts +0 -41
  10. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  11. package/openapi.yaml +3381 -348
  12. package/package.json +1 -1
  13. package/scripts/generate-openapi.ts +68 -41
  14. package/src/__tests__/agent-loop-exit-reason.test.ts +34 -39
  15. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  16. package/src/__tests__/agent-loop.test.ts +37 -87
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  18. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  19. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  20. package/src/__tests__/anthropic-provider.test.ts +95 -2
  21. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  22. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  23. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  24. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  25. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  26. package/src/__tests__/btw-routes.test.ts +62 -3
  27. package/src/__tests__/build-persisted-content.test.ts +184 -0
  28. package/src/__tests__/catalog-files.test.ts +1 -1
  29. package/src/__tests__/clawhub-files.test.ts +1 -1
  30. package/src/__tests__/compaction-pipeline.test.ts +1 -1
  31. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  32. package/src/__tests__/config-watcher.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -2
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +313 -1136
  37. package/src/__tests__/conversation-agent-loop.test.ts +596 -1616
  38. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  39. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  40. package/src/__tests__/conversation-pairing.test.ts +4 -31
  41. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +26 -5
  43. package/src/__tests__/conversation-queue.test.ts +2 -0
  44. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +170 -229
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +3 -24
  48. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  49. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  50. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  51. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  52. package/src/__tests__/conversation-title-service.test.ts +135 -2
  53. package/src/__tests__/conversation-workspace-injection.test.ts +6 -1
  54. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  55. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  56. package/src/__tests__/dm-persistence.test.ts +5 -1
  57. package/src/__tests__/empty-response-hook.test.ts +304 -0
  58. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  59. package/src/__tests__/gemini-image-service.test.ts +13 -0
  60. package/src/__tests__/helpers/mock-provider.ts +110 -0
  61. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  62. package/src/__tests__/history-repair-hook.test.ts +1 -0
  63. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  64. package/src/__tests__/identity-routes.test.ts +248 -7
  65. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  66. package/src/__tests__/injector-background-turn.test.ts +2 -8
  67. package/src/__tests__/injector-chain.test.ts +106 -270
  68. package/src/__tests__/injector-disk-pressure.test.ts +3 -12
  69. package/src/__tests__/injector-document-comments.test.ts +2 -2
  70. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  71. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  72. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  73. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  74. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  75. package/src/__tests__/llm-usage-store.test.ts +223 -1
  76. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  77. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  78. package/src/__tests__/native-web-search.test.ts +191 -0
  79. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  80. package/src/__tests__/openai-image-service.test.ts +17 -0
  81. package/src/__tests__/openai-provider.test.ts +31 -1
  82. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  83. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  84. package/src/__tests__/pipeline-runner.test.ts +29 -39
  85. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  86. package/src/__tests__/plugin-bootstrap.test.ts +13 -28
  87. package/src/__tests__/plugin-registry.test.ts +0 -27
  88. package/src/__tests__/plugin-types.test.ts +2 -125
  89. package/src/__tests__/process-message-display-content.test.ts +6 -2
  90. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  91. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  92. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  93. package/src/__tests__/schedule-routes.test.ts +603 -2
  94. package/src/__tests__/schedule-store.test.ts +41 -0
  95. package/src/__tests__/schedule-tools.test.ts +35 -0
  96. package/src/__tests__/server-history-render.test.ts +314 -1
  97. package/src/__tests__/skillssh-files.test.ts +1 -1
  98. package/src/__tests__/system-prompt.test.ts +20 -0
  99. package/src/__tests__/task-scheduler.test.ts +162 -1
  100. package/src/__tests__/terminal-tools.test.ts +6 -1
  101. package/src/__tests__/title-generate-hook.test.ts +319 -0
  102. package/src/__tests__/tool-error-hook.test.ts +278 -0
  103. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  104. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  105. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  106. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  107. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  108. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  109. package/src/__tests__/usage-routes.test.ts +285 -1
  110. package/src/__tests__/user-plugin-loader.test.ts +2 -2
  111. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  112. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  113. package/src/agent/loop.ts +346 -442
  114. package/src/api/events/assistant-thinking-delta.ts +33 -0
  115. package/src/api/events/tool-output-chunk.ts +45 -0
  116. package/src/api/events/tool-use-preview-start.ts +32 -0
  117. package/src/api/events/trace-event.ts +69 -0
  118. package/src/api/index.ts +48 -13
  119. package/src/api/responses/conversation-message.ts +368 -0
  120. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  121. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  122. package/src/cli/commands/notifications.ts +112 -60
  123. package/src/config/assistant-feature-flags.ts +22 -11
  124. package/src/config/bundled-skills/app-builder/SKILL.md +3 -20
  125. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  126. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  127. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  128. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  129. package/src/config/bundled-skills/document-editor/SKILL.md +1 -1
  130. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  131. package/src/config/feature-flag-cache.ts +3 -3
  132. package/src/config/feature-flag-registry.json +35 -3
  133. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  134. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  135. package/src/config/schemas/llm.ts +1 -0
  136. package/src/config/schemas/memory-v2.ts +8 -0
  137. package/src/config/schemas/memory-v3.ts +8 -0
  138. package/src/config/schemas/platform.ts +8 -0
  139. package/src/config/seed-inference-profiles.ts +2 -2
  140. package/src/config/skills.ts +13 -0
  141. package/src/context/compactor.ts +1 -1
  142. package/src/context/strip-injections.ts +122 -0
  143. package/src/context/token-estimator.ts +23 -0
  144. package/src/context/tool-result-truncation.ts +0 -23
  145. package/src/context/window-manager.ts +3 -6
  146. package/src/credential-execution/executable-discovery.ts +16 -0
  147. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  148. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  149. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  150. package/src/daemon/assistant-attachments.ts +1 -1
  151. package/src/daemon/config-watcher.ts +2 -2
  152. package/src/daemon/context-overflow-reducer.ts +0 -1
  153. package/src/daemon/conversation-agent-loop-handlers.ts +605 -153
  154. package/src/daemon/conversation-agent-loop.ts +281 -760
  155. package/src/daemon/conversation-history.ts +5 -4
  156. package/src/daemon/conversation-lifecycle.ts +3 -4
  157. package/src/daemon/conversation-messaging.ts +7 -6
  158. package/src/daemon/conversation-process.ts +11 -16
  159. package/src/daemon/conversation-runtime-assembly.ts +130 -347
  160. package/src/daemon/conversation-slash.ts +6 -25
  161. package/src/daemon/conversation-surfaces.ts +222 -4
  162. package/src/daemon/conversation-tool-setup.ts +2 -29
  163. package/src/daemon/conversation.ts +32 -14
  164. package/src/daemon/external-plugins-bootstrap.ts +9 -10
  165. package/src/daemon/handlers/config-a2a.ts +51 -36
  166. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  167. package/src/daemon/handlers/config-telegram.ts +16 -2
  168. package/src/daemon/handlers/shared.ts +156 -84
  169. package/src/daemon/handlers/skills.ts +39 -10
  170. package/src/daemon/lifecycle.ts +4 -0
  171. package/src/daemon/message-types/apps.ts +1 -29
  172. package/src/daemon/message-types/messages.ts +9 -57
  173. package/src/daemon/message-types/skills.ts +2 -0
  174. package/src/daemon/message-types/surfaces.ts +136 -3
  175. package/src/daemon/now-scratchpad.ts +21 -0
  176. package/src/daemon/orphan-reaper.test.ts +210 -0
  177. package/src/daemon/orphan-reaper.ts +240 -0
  178. package/src/daemon/persist-unsendable-image.ts +117 -0
  179. package/src/daemon/process-message.ts +1 -3
  180. package/src/daemon/trace-emitter.ts +6 -4
  181. package/src/daemon/trust-context.ts +19 -0
  182. package/src/daemon/wake-target-adapter.ts +3 -1
  183. package/src/home/home-greeting-cache.ts +24 -1
  184. package/src/ipc/gateway-client.test.ts +2 -2
  185. package/src/ipc/gateway-client.ts +3 -3
  186. package/src/media/gemini-image-service.ts +15 -0
  187. package/src/media/openai-image-service.ts +14 -0
  188. package/src/media/types.ts +34 -0
  189. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  190. package/src/memory/auth-fallback-events-store.ts +94 -0
  191. package/src/memory/conversation-title-service.ts +65 -41
  192. package/src/memory/db-init.ts +4 -0
  193. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  194. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  195. package/src/memory/jobs-store.ts +33 -0
  196. package/src/memory/jobs-worker.ts +31 -4
  197. package/src/memory/llm-usage-store.ts +224 -50
  198. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  199. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  200. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  201. package/src/memory/migrations/index.ts +2 -0
  202. package/src/memory/pkb/autoinject.ts +61 -0
  203. package/src/memory/pkb/context.ts +50 -0
  204. package/src/memory/pkb/types.ts +14 -0
  205. package/src/memory/schedule-attribution-sql.ts +104 -0
  206. package/src/memory/schema/infrastructure.ts +16 -0
  207. package/src/memory/usage-grouped-buckets.ts +6 -1
  208. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -1
  209. package/src/memory/v2/consolidation-job.ts +1 -1
  210. package/src/memory/v3/__tests__/health.test.ts +16 -0
  211. package/src/memory/v3/__tests__/orchestrate.test.ts +45 -9
  212. package/src/memory/v3/__tests__/provider-blocks.test.ts +13 -0
  213. package/src/memory/v3/__tests__/router.test.ts +101 -29
  214. package/src/memory/v3/__tests__/selector.test.ts +93 -27
  215. package/src/memory/v3/__tests__/shadow-plugin.test.ts +23 -5
  216. package/src/memory/v3/health.ts +0 -0
  217. package/src/memory/v3/llm-retry.ts +32 -0
  218. package/src/memory/v3/orchestrate.ts +26 -14
  219. package/src/memory/v3/provider-blocks.ts +15 -5
  220. package/src/memory/v3/router.ts +48 -42
  221. package/src/memory/v3/selector.ts +57 -42
  222. package/src/memory/v3/shadow-plugin.ts +47 -15
  223. package/src/memory/v3/types.ts +8 -0
  224. package/src/notifications/conversation-pairing.ts +8 -15
  225. package/src/notifications/decision-engine.ts +6 -3
  226. package/src/notifications/home-feed-side-effect.ts +12 -1
  227. package/src/permissions/prompter.ts +4 -0
  228. package/src/plugin-api/constants.ts +4 -0
  229. package/src/plugin-api/index.ts +8 -1
  230. package/src/plugin-api/types.ts +151 -1
  231. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  232. package/src/plugins/defaults/empty-response/register.ts +8 -13
  233. package/src/plugins/defaults/index.ts +1 -15
  234. package/src/plugins/defaults/injectors/register.ts +243 -74
  235. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +91 -0
  236. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  237. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  238. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  239. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  240. package/src/plugins/defaults/title-generate/package.json +1 -1
  241. package/src/plugins/defaults/title-generate/register.ts +18 -18
  242. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  243. package/src/plugins/defaults/tool-error/package.json +1 -1
  244. package/src/plugins/defaults/tool-error/register.ts +9 -21
  245. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  246. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  247. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  248. package/src/plugins/pipeline.ts +6 -18
  249. package/src/plugins/registry.ts +8 -25
  250. package/src/plugins/types.ts +43 -474
  251. package/src/proactive-artifact/aux-message-injector.ts +3 -3
  252. package/src/proactive-artifact/job.test.ts +7 -12
  253. package/src/prompts/__tests__/system-prompt.test.ts +36 -0
  254. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +62 -0
  255. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  256. package/src/prompts/templates/system-sections.ts +15 -0
  257. package/src/providers/anthropic/client.ts +37 -29
  258. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  259. package/src/providers/openai/chat-completions-provider.ts +44 -0
  260. package/src/providers/openrouter/client.ts +1 -0
  261. package/src/providers/placeholder-sentinels.ts +35 -0
  262. package/src/runtime/__tests__/agent-wake.test.ts +5 -1
  263. package/src/runtime/agent-wake.ts +2 -2
  264. package/src/runtime/assistant-event-hub.ts +36 -6
  265. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  266. package/src/runtime/http-router.ts +16 -21
  267. package/src/runtime/http-types.ts +16 -70
  268. package/src/runtime/pending-interactions.ts +1 -0
  269. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  270. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  271. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  272. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  273. package/src/runtime/routes/app-management-routes.ts +6 -117
  274. package/src/runtime/routes/app-routes.ts +13 -15
  275. package/src/runtime/routes/attachment-routes.ts +26 -15
  276. package/src/runtime/routes/avatar-routes.ts +26 -0
  277. package/src/runtime/routes/btw-routes.ts +29 -23
  278. package/src/runtime/routes/consolidation-routes.ts +120 -20
  279. package/src/runtime/routes/conversation-query-routes.ts +2 -0
  280. package/src/runtime/routes/conversation-routes.ts +358 -184
  281. package/src/runtime/routes/documents-routes.ts +4 -0
  282. package/src/runtime/routes/domain-routes.ts +51 -37
  283. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  284. package/src/runtime/routes/events-routes.ts +28 -34
  285. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  286. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  287. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  288. package/src/runtime/routes/identity-routes.ts +208 -17
  289. package/src/runtime/routes/image-generation-routes.ts +40 -2
  290. package/src/runtime/routes/index.ts +2 -0
  291. package/src/runtime/routes/integrations/a2a.ts +12 -10
  292. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  293. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  294. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  295. package/src/runtime/routes/integrations/telegram.ts +6 -0
  296. package/src/runtime/routes/integrations/twilio.ts +42 -0
  297. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  298. package/src/runtime/routes/log-export-routes.ts +8 -0
  299. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  300. package/src/runtime/routes/memory-v3-routes.ts +50 -28
  301. package/src/runtime/routes/oauth-apps.ts +66 -12
  302. package/src/runtime/routes/oauth-providers.ts +44 -5
  303. package/src/runtime/routes/platform-routes.ts +81 -5
  304. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  305. package/src/runtime/routes/playground/force-compact.ts +1 -1
  306. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  307. package/src/runtime/routes/schedule-routes.ts +152 -42
  308. package/src/runtime/routes/secret-routes.ts +14 -2
  309. package/src/runtime/routes/skills-routes.ts +43 -14
  310. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  311. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  312. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  313. package/src/runtime/routes/tts-routes.ts +35 -0
  314. package/src/runtime/routes/types.ts +66 -8
  315. package/src/runtime/routes/usage-routes.ts +47 -39
  316. package/src/runtime/routes/webhook-routes.ts +41 -2
  317. package/src/runtime/routes/workspace-routes.ts +4 -0
  318. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  319. package/src/runtime/services/analyze-conversation.ts +2 -2
  320. package/src/schedule/schedule-store.ts +20 -1
  321. package/src/schedule/schedule-usage-store.ts +83 -0
  322. package/src/schedule/scheduler.ts +12 -5
  323. package/src/skills/catalog-files.ts +2 -2
  324. package/src/skills/catalog-install.ts +3 -0
  325. package/src/skills/categories-cache.ts +118 -0
  326. package/src/skills/clawhub-files.ts +1 -2
  327. package/src/skills/skillssh-files.ts +1 -2
  328. package/src/telemetry/types.ts +29 -1
  329. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  330. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  331. package/src/tools/executor.ts +1 -53
  332. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  333. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  334. package/src/tools/network/web-search-error.test.ts +248 -0
  335. package/src/tools/network/web-search-error.ts +267 -0
  336. package/src/tools/network/web-search.ts +207 -48
  337. package/src/tools/schedule/create.ts +2 -0
  338. package/src/tools/terminal/safe-env.ts +10 -1
  339. package/src/tools/ui-surface/definitions.ts +9 -1
  340. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  341. package/src/tts/provider-catalog.ts +76 -1
  342. package/src/util/mutex.ts +47 -0
  343. package/src/workspace/git-service.ts +1 -42
  344. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  345. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  346. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +93 -0
  347. package/src/workspace/migrations/registry.ts +6 -0
  348. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  349. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  350. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  351. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  352. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  353. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  354. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  355. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  356. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  357. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  358. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  359. package/src/gallery/default-gallery.ts +0 -1359
  360. package/src/gallery/gallery-manifest.ts +0 -28
  361. package/src/home/feature-gate.ts +0 -22
  362. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  363. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  364. package/src/plugins/defaults/injectors/package.json +0 -15
  365. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  366. package/src/plugins/defaults/llm-call/package.json +0 -15
  367. package/src/plugins/defaults/llm-call/register.ts +0 -45
  368. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  369. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  370. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  371. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  372. package/src/plugins/defaults/persistence/package.json +0 -15
  373. package/src/plugins/defaults/persistence/register.ts +0 -38
  374. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  375. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  376. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  377. package/src/plugins/defaults/token-estimate/package.json +0 -15
  378. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  379. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  380. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  381. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  382. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  383. package/src/plugins/defaults/tool-execute/package.json +0 -15
  384. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  385. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  386. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  387. package/src/skills/category-inference.ts +0 -111
@@ -14,6 +14,8 @@
14
14
  * registered callback routes for this assistant.
15
15
  */
16
16
 
17
+ import { z } from "zod";
18
+
17
19
  import { isPlatformRemote } from "../../config/env-registry.js";
18
20
  import {
19
21
  registerCallbackRoute,
@@ -47,13 +49,80 @@ const CREDENTIAL_KEYS = {
47
49
  userId: { service: "vellum", field: "platform_user_id" },
48
50
  } as const;
49
51
 
52
+ // ---------------------------------------------------------------------------
53
+ // Schemas
54
+ // ---------------------------------------------------------------------------
55
+
56
+ const VelayTunnelStatusSchema = z.object({
57
+ connected: z.boolean(),
58
+ publicUrl: z.string().nullable(),
59
+ });
60
+
61
+ const PlatformStatusResponseSchema = z.object({
62
+ isPlatform: z.boolean(),
63
+ baseUrl: z.string(),
64
+ assistantId: z.string(),
65
+ hasAssistantApiKey: z.boolean(),
66
+ hasWebhookSecret: z.boolean(),
67
+ available: z.boolean(),
68
+ organizationId: z.string().nullable(),
69
+ userId: z.string().nullable(),
70
+ velayTunnel: VelayTunnelStatusSchema.nullable(),
71
+ });
72
+ type PlatformStatusResponse = z.infer<typeof PlatformStatusResponseSchema>;
73
+
74
+ const PlatformConnectResponseSchema = z.object({
75
+ alreadyConnected: z.boolean().optional(),
76
+ baseUrl: z.string().optional(),
77
+ showPlatformLogin: z.boolean().optional(),
78
+ });
79
+ type PlatformConnectResponse = z.infer<typeof PlatformConnectResponseSchema>;
80
+
81
+ const PlatformDisconnectResponseSchema = z.object({
82
+ disconnected: z.literal(true),
83
+ previousBaseUrl: z.string().nullable(),
84
+ });
85
+ type PlatformDisconnectResponse = z.infer<
86
+ typeof PlatformDisconnectResponseSchema
87
+ >;
88
+
89
+ const CallbackRouteRegisterRequestSchema = z.object({
90
+ path: z.string(),
91
+ type: z.string(),
92
+ });
93
+
94
+ const CallbackRouteRegisterResponseSchema = z.object({
95
+ callbackUrl: z.string(),
96
+ callbackPath: z.string(),
97
+ type: z.string(),
98
+ });
99
+ type CallbackRouteRegisterResponse = z.infer<
100
+ typeof CallbackRouteRegisterResponseSchema
101
+ >;
102
+
103
+ const CallbackRouteSchema = z.object({
104
+ id: z.string(),
105
+ assistant_id: z.string(),
106
+ type: z.string(),
107
+ callback_path: z.string(),
108
+ callback_url: z.string(),
109
+ source_identifier: z.string().nullable(),
110
+ });
111
+
112
+ const CallbackRoutesListResponseSchema = z.object({
113
+ routes: z.array(CallbackRouteSchema),
114
+ });
115
+ type CallbackRoutesListResponse = z.infer<
116
+ typeof CallbackRoutesListResponseSchema
117
+ >;
118
+
50
119
  // ---------------------------------------------------------------------------
51
120
  // Handlers
52
121
  // ---------------------------------------------------------------------------
53
122
 
54
123
  async function handlePlatformStatus(
55
124
  _args: RouteHandlerArgs,
56
- ): Promise<unknown> {
125
+ ): Promise<PlatformStatusResponse> {
57
126
  const [context, velayTunnel] = await Promise.all([
58
127
  resolvePlatformCallbackRegistrationContext(),
59
128
  ipcGetVelayStatus().catch(() => null),
@@ -94,7 +163,7 @@ async function handlePlatformStatus(
94
163
 
95
164
  async function handlePlatformConnect(
96
165
  _args: RouteHandlerArgs,
97
- ): Promise<unknown> {
166
+ ): Promise<PlatformConnectResponse> {
98
167
  // Check if already connected
99
168
  const [existingUrl, existingApiKey] = await Promise.all([
100
169
  getSecureKeyAsync(
@@ -128,7 +197,7 @@ async function handlePlatformConnect(
128
197
 
129
198
  async function handlePlatformDisconnect(
130
199
  _args: RouteHandlerArgs,
131
- ): Promise<unknown> {
200
+ ): Promise<PlatformDisconnectResponse> {
132
201
  // Reject if running inside a platform host
133
202
  if (isPlatformRemote()) {
134
203
  throw new UnprocessableEntityError(
@@ -197,7 +266,7 @@ async function handlePlatformDisconnect(
197
266
 
198
267
  async function handleCallbackRoutesRegister(
199
268
  args: RouteHandlerArgs,
200
- ): Promise<unknown> {
269
+ ): Promise<CallbackRouteRegisterResponse> {
201
270
  const { path, type } = (args.body ?? {}) as {
202
271
  path?: string;
203
272
  type?: string;
@@ -235,7 +304,7 @@ async function handleCallbackRoutesRegister(
235
304
 
236
305
  async function handleCallbackRoutesList(
237
306
  _args: RouteHandlerArgs,
238
- ): Promise<unknown> {
307
+ ): Promise<CallbackRoutesListResponse> {
239
308
  const context = await resolvePlatformCallbackRegistrationContext();
240
309
 
241
310
  if (!context.platformBaseUrl || !context.authHeader) {
@@ -274,6 +343,7 @@ async function handleCallbackRoutesList(
274
343
  type: string;
275
344
  callback_path: string;
276
345
  callback_url: string;
346
+ source_identifier: string | null;
277
347
  }>;
278
348
 
279
349
  return { routes };
@@ -297,6 +367,7 @@ export const ROUTES: RouteDefinition[] = [
297
367
  "Aggregates platform context, credentials, assistant ID, webhook secret, and Velay tunnel status.",
298
368
  tags: ["platform"],
299
369
  handler: handlePlatformStatus,
370
+ responseBody: PlatformStatusResponseSchema,
300
371
  },
301
372
  {
302
373
  operationId: "platform_connect",
@@ -311,6 +382,7 @@ export const ROUTES: RouteDefinition[] = [
311
382
  "Checks existing credentials and emits the show_platform_login signal for connected clients to show a login UI.",
312
383
  tags: ["platform"],
313
384
  handler: handlePlatformConnect,
385
+ responseBody: PlatformConnectResponseSchema,
314
386
  },
315
387
  {
316
388
  operationId: "platform_disconnect",
@@ -325,6 +397,7 @@ export const ROUTES: RouteDefinition[] = [
325
397
  "Deletes stored platform credentials and emits platform_disconnected signal to connected clients.",
326
398
  tags: ["platform"],
327
399
  handler: handlePlatformDisconnect,
400
+ responseBody: PlatformDisconnectResponseSchema,
328
401
  },
329
402
  {
330
403
  operationId: "platform_callback_routes_register",
@@ -339,6 +412,8 @@ export const ROUTES: RouteDefinition[] = [
339
412
  "Registers a callback route with the platform gateway for inbound provider webhooks.",
340
413
  tags: ["platform"],
341
414
  handler: handleCallbackRoutesRegister,
415
+ requestBody: CallbackRouteRegisterRequestSchema,
416
+ responseBody: CallbackRouteRegisterResponseSchema,
342
417
  },
343
418
  {
344
419
  operationId: "platform_callback_routes_list",
@@ -353,5 +428,6 @@ export const ROUTES: RouteDefinition[] = [
353
428
  "Lists all callback routes registered with the platform for this assistant.",
354
429
  tags: ["platform"],
355
430
  handler: handleCallbackRoutesList,
431
+ responseBody: CallbackRoutesListResponseSchema,
356
432
  },
357
433
  ];
@@ -65,8 +65,12 @@ function makeFakeConversation(
65
65
  ...options.result,
66
66
  };
67
67
 
68
+ let processing = options.processing ?? false;
68
69
  const fake = {
69
- processing: options.processing ?? false,
70
+ isProcessing: () => processing,
71
+ setProcessing: (value: boolean) => {
72
+ processing = value;
73
+ },
70
74
  getMessages(): Message[] {
71
75
  if (!returnedAfter && calls === 0) return messagesBefore;
72
76
  return messagesAfter;
@@ -85,9 +89,7 @@ function makeFakeConversation(
85
89
  }
86
90
 
87
91
  function findRoute() {
88
- const route = ROUTES.find(
89
- (r) => r.operationId === "playgroundForceCompact",
90
- );
92
+ const route = ROUTES.find((r) => r.operationId === "playgroundForceCompact");
91
93
  if (!route) throw new Error("force-compact route not registered");
92
94
  return route;
93
95
  }
@@ -35,7 +35,7 @@ export const ROUTES: RouteDefinition[] = [
35
35
  throwConversationNotFound(id);
36
36
  }
37
37
 
38
- if (conversation.processing) {
38
+ if (conversation.isProcessing()) {
39
39
  throw new ConflictError(
40
40
  "Compaction already in progress for this conversation",
41
41
  );
@@ -23,6 +23,10 @@ const RenameConversationBody = z.object({
23
23
  title: z.string().min(1),
24
24
  });
25
25
 
26
+ const RenameConversationResponse = z.object({
27
+ ok: z.literal(true),
28
+ });
29
+
26
30
  export const ROUTES: RouteDefinition[] = [
27
31
  {
28
32
  operationId: "rename_conversation",
@@ -36,6 +40,7 @@ export const ROUTES: RouteDefinition[] = [
36
40
  description: "Update the display title of a conversation.",
37
41
  tags: ["conversations"],
38
42
  requestBody: RenameConversationBody,
43
+ responseBody: RenameConversationResponse,
39
44
  handler: ({ body, headers }) => {
40
45
  const parsed = RenameConversationBody.safeParse(body);
41
46
  if (!parsed.success) {
@@ -11,6 +11,7 @@ import { getOrCreateConversation } from "../../daemon/conversation-store.js";
11
11
  import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../../daemon/trust-context.js";
12
12
  import { bootstrapConversation } from "../../memory/conversation-bootstrap.js";
13
13
  import { getConversation } from "../../memory/conversation-crud.js";
14
+ import { getUsageCostForConversationWindow } from "../../memory/llm-usage-store.js";
14
15
  import { normalizeScheduleSyntax } from "../../schedule/recurrence-types.js";
15
16
  import {
16
17
  runScript,
@@ -27,10 +28,13 @@ import {
27
28
  getSchedule,
28
29
  getScheduleRuns,
29
30
  listSchedules,
31
+ setScheduleRunConversationId,
30
32
  updateSchedule,
31
33
  } from "../../schedule/schedule-store.js";
34
+ import { getScheduleUsageSummaries } from "../../schedule/schedule-usage-store.js";
32
35
  import { getLogger } from "../../util/logger.js";
33
36
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
37
+ import { parseEpochMillisRange } from "./epoch-millis-range.js";
34
38
  import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
35
39
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
36
40
 
@@ -57,6 +61,9 @@ const scheduleSchema = z.object({
57
61
  maxRetries: z.number(),
58
62
  retryBackoffMs: z.number(),
59
63
  timeoutMs: z.number().nullable(),
64
+ createdFromConversationId: z.string().nullable(),
65
+ createdFromConversationExists: z.boolean(),
66
+ createdFromConversationArchivedAt: z.number().nullable(),
60
67
  description: z.string().nullable(),
61
68
  mode: z.enum(["notify", "execute", "script", "wake"]),
62
69
  status: z.enum(["active", "firing", "fired", "cancelled"]),
@@ -76,48 +83,96 @@ const scheduleRunSchema = z.object({
76
83
  output: z.string().nullable(),
77
84
  error: z.string().nullable(),
78
85
  conversationId: z.string().nullable(),
86
+ conversationExists: z.boolean(),
87
+ conversationArchivedAt: z.number().nullable(),
88
+ estimatedCostUsd: z.number(),
79
89
  createdAt: z.number(),
80
90
  });
81
91
 
92
+ const scheduleUsageSummarySchema = z.object({
93
+ scheduleId: z.string(),
94
+ runCount: z.number(),
95
+ totalEstimatedCostUsd: z.number(),
96
+ eventCount: z.number(),
97
+ });
98
+
82
99
  // ---------------------------------------------------------------------------
83
100
  // Handlers (transport-agnostic)
84
101
  // ---------------------------------------------------------------------------
85
102
 
103
+ interface CreatedFromConversationState {
104
+ exists: boolean;
105
+ archivedAt: number | null;
106
+ }
107
+
108
+ function getCreatedFromConversationState(
109
+ conversationId: string | null,
110
+ cache: Map<string, CreatedFromConversationState>,
111
+ ): CreatedFromConversationState {
112
+ if (!conversationId) {
113
+ return { exists: false, archivedAt: null };
114
+ }
115
+
116
+ const cached = cache.get(conversationId);
117
+ if (cached) return cached;
118
+
119
+ const conversation = getConversation(conversationId);
120
+ const state = {
121
+ exists: conversation !== null,
122
+ archivedAt: conversation?.archivedAt ?? null,
123
+ };
124
+ cache.set(conversationId, state);
125
+ return state;
126
+ }
127
+
86
128
  function handleListSchedules(queryParams: Record<string, string>) {
87
129
  const includeAll = queryParams.include_all === "true";
88
130
  const jobs = listSchedules();
89
131
  const filtered = includeAll
90
132
  ? jobs
91
133
  : jobs.filter((j) => j.createdBy !== "defer");
134
+ const sourceConversationCache = new Map<
135
+ string,
136
+ CreatedFromConversationState
137
+ >();
92
138
  return {
93
- schedules: filtered.map((j) => ({
94
- id: j.id,
95
- name: j.name,
96
- enabled: j.enabled,
97
- syntax: j.syntax,
98
- expression: j.expression,
99
- cronExpression: j.cronExpression,
100
- timezone: j.timezone,
101
- message: j.message,
102
- script: j.script,
103
- nextRunAt: j.nextRunAt,
104
- lastRunAt: j.lastRunAt,
105
- lastStatus: j.lastStatus,
106
- retryCount: j.retryCount,
107
- maxRetries: j.maxRetries,
108
- retryBackoffMs: j.retryBackoffMs,
109
- timeoutMs: j.timeoutMs,
110
- description:
111
- j.syntax === "cron"
112
- ? describeCronExpression(j.cronExpression)
113
- : j.expression,
114
- mode: j.mode,
115
- status: j.status,
116
- routingIntent: j.routingIntent,
117
- reuseConversation: j.reuseConversation,
118
- wakeConversationId: j.wakeConversationId,
119
- isOneShot: j.cronExpression == null,
120
- })),
139
+ schedules: filtered.map((j) => {
140
+ const sourceConversation = getCreatedFromConversationState(
141
+ j.createdFromConversationId,
142
+ sourceConversationCache,
143
+ );
144
+ return {
145
+ id: j.id,
146
+ name: j.name,
147
+ enabled: j.enabled,
148
+ syntax: j.syntax,
149
+ expression: j.expression,
150
+ cronExpression: j.cronExpression,
151
+ timezone: j.timezone,
152
+ message: j.message,
153
+ script: j.script,
154
+ nextRunAt: j.nextRunAt,
155
+ lastRunAt: j.lastRunAt,
156
+ lastStatus: j.lastStatus,
157
+ retryCount: j.retryCount,
158
+ maxRetries: j.maxRetries,
159
+ retryBackoffMs: j.retryBackoffMs,
160
+ timeoutMs: j.timeoutMs,
161
+ createdFromConversationId: j.createdFromConversationId,
162
+ createdFromConversationExists: sourceConversation.exists,
163
+ createdFromConversationArchivedAt: sourceConversation.archivedAt,
164
+ description:
165
+ j.syntax === "cron"
166
+ ? describeCronExpression(j.cronExpression)
167
+ : j.expression,
168
+ mode: j.mode,
169
+ status: j.status,
170
+ routingIntent: j.routingIntent,
171
+ reuseConversation: j.reuseConversation,
172
+ wakeConversationId: j.wakeConversationId,
173
+ isOneShot: j.cronExpression == null,
174
+ };
175
+ }),
121
176
  };
122
177
  }
123
178
 
@@ -291,22 +346,42 @@ function handleListScheduleRuns(
291
346
  ? Math.min(Math.max(Math.floor(rawLimit), 1), 100)
292
347
  : 10;
293
348
  const runs = getScheduleRuns(id, limit);
349
+ const now = Date.now();
294
350
  return {
295
- runs: runs.map((r) => ({
296
- id: r.id,
297
- jobId: r.jobId,
298
- status: r.status,
299
- startedAt: r.startedAt,
300
- finishedAt: r.finishedAt,
301
- durationMs: r.durationMs,
302
- output: r.output,
303
- error: r.error,
304
- conversationId: r.conversationId,
305
- createdAt: r.createdAt,
306
- })),
351
+ runs: runs.map((r) => {
352
+ const conversation = r.conversationId
353
+ ? getConversation(r.conversationId)
354
+ : null;
355
+ return {
356
+ id: r.id,
357
+ jobId: r.jobId,
358
+ status: r.status,
359
+ startedAt: r.startedAt,
360
+ finishedAt: r.finishedAt,
361
+ durationMs: r.durationMs,
362
+ output: r.output,
363
+ error: r.error,
364
+ conversationId: r.conversationId,
365
+ conversationExists: conversation != null,
366
+ conversationArchivedAt: conversation?.archivedAt ?? null,
367
+ estimatedCostUsd: r.conversationId
368
+ ? getUsageCostForConversationWindow({
369
+ conversationId: r.conversationId,
370
+ from: r.startedAt,
371
+ to: r.finishedAt ?? now,
372
+ })
373
+ : 0,
374
+ createdAt: r.createdAt,
375
+ };
376
+ }),
307
377
  };
308
378
  }
309
379
 
380
+ function handleScheduleUsageSummary(queryParams: Record<string, string>) {
381
+ const range = parseEpochMillisRange(queryParams);
382
+ return { summaries: getScheduleUsageSummaries(range) };
383
+ }
384
+
310
385
  // ---------------------------------------------------------------------------
311
386
  // Shared route definitions (HTTP + IPC)
312
387
  // ---------------------------------------------------------------------------
@@ -337,6 +412,40 @@ export const ROUTES: RouteDefinition[] = [
337
412
  handler: ({ queryParams }: RouteHandlerArgs) =>
338
413
  handleListSchedules(queryParams ?? {}),
339
414
  },
415
+ {
416
+ operationId: "getScheduleUsageSummary",
417
+ endpoint: "schedules/usage-summary",
418
+ method: "GET",
419
+ policy: {
420
+ requiredScopes: ["settings.read"],
421
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
422
+ },
423
+ summary: "Get schedule usage summaries",
424
+ description:
425
+ "Return per-schedule run counts and usage totals for a time range.",
426
+ tags: ["schedules"],
427
+ queryParams: [
428
+ {
429
+ name: "from",
430
+ type: "integer",
431
+ required: true,
432
+ description: "Start epoch millis (required)",
433
+ },
434
+ {
435
+ name: "to",
436
+ type: "integer",
437
+ required: true,
438
+ description: "End epoch millis (required)",
439
+ },
440
+ ],
441
+ responseBody: z.object({
442
+ summaries: z
443
+ .array(scheduleUsageSummarySchema)
444
+ .describe("Schedule usage summary rows"),
445
+ }),
446
+ handler: ({ queryParams }: RouteHandlerArgs) =>
447
+ handleScheduleUsageSummary(queryParams ?? {}),
448
+ },
340
449
  {
341
450
  operationId: "createSchedule",
342
451
  endpoint: "schedules",
@@ -551,6 +660,7 @@ async function handleRunScheduleNow(id: string) {
551
660
  const taskMatch = schedule.message.match(/^run_task:(\S+)$/);
552
661
  if (taskMatch) {
553
662
  const taskId = taskMatch[1];
663
+ const runId = createScheduleRun(schedule.id, null);
554
664
  try {
555
665
  log.info(
556
666
  { jobId: schedule.id, name: schedule.name, taskId },
@@ -577,7 +687,7 @@ async function handleRunScheduleNow(id: string) {
577
687
  },
578
688
  );
579
689
 
580
- const runId = createScheduleRun(schedule.id, result.conversationId);
690
+ setScheduleRunConversationId(runId, result.conversationId);
581
691
  if (result.status === "failed") {
582
692
  completeScheduleRun(runId, {
583
693
  status: "error",
@@ -598,7 +708,7 @@ async function handleRunScheduleNow(id: string) {
598
708
  origin: "schedule",
599
709
  systemHint: `Schedule (manual): ${schedule.name}`,
600
710
  });
601
- const runId = createScheduleRun(schedule.id, fallbackConversation.id);
711
+ setScheduleRunConversationId(runId, fallbackConversation.id);
602
712
  completeScheduleRun(runId, { status: "error", error: message });
603
713
  }
604
714
  return handleListSchedules({});
@@ -684,9 +684,21 @@ export const ROUTES: RouteDefinition[] = [
684
684
  tags: ["secrets"],
685
685
  responseBody: z.object({
686
686
  secrets: z
687
- .array(z.unknown())
687
+ .array(
688
+ z.object({
689
+ type: z.enum(["api_key", "credential"]),
690
+ name: z.string(),
691
+ }),
692
+ )
688
693
  .describe("List of secret metadata entries, each with type and name"),
689
- accounts: z.array(z.unknown()).describe("Alias for secrets (same data)"),
694
+ accounts: z
695
+ .array(
696
+ z.object({
697
+ type: z.enum(["api_key", "credential"]),
698
+ name: z.string(),
699
+ }),
700
+ )
701
+ .describe("Alias for secrets (same data)"),
690
702
  }),
691
703
  handler: handleListSecrets,
692
704
  },
@@ -26,6 +26,7 @@ import {
26
26
  uninstallSkill,
27
27
  updateSkill,
28
28
  } from "../../daemon/handlers/skills.js";
29
+ import { getCategories } from "../../skills/categories-cache.js";
29
30
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
30
31
  import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
31
32
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
@@ -41,19 +42,11 @@ const slimSkillBase = {
41
42
  id: z.string(),
42
43
  name: z.string(),
43
44
  description: z.string(),
45
+ icon: z.string().optional(),
44
46
  emoji: z.string().optional(),
45
47
  kind: z.enum(["bundled", "installed", "catalog"]),
46
48
  status: z.enum(["enabled", "disabled", "available"]),
47
- category: z.enum([
48
- "communication",
49
- "productivity",
50
- "development",
51
- "media",
52
- "automation",
53
- "webSocial",
54
- "knowledge",
55
- "integration",
56
- ]),
49
+ category: z.string(),
57
50
  };
58
51
 
59
52
  const slimSkillSchema = z.discriminatedUnion("origin", [
@@ -220,6 +213,33 @@ export const ROUTES: RouteDefinition[] = [
220
213
  return { skills };
221
214
  },
222
215
  },
216
+ {
217
+ operationId: "listSkillCategories",
218
+ endpoint: "skills/categories",
219
+ method: "GET",
220
+ policy: {
221
+ requiredScopes: ["settings.read"],
222
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
223
+ },
224
+ summary: "List skill categories",
225
+ description:
226
+ "Return all skill category definitions with labels, icons, and descriptions.",
227
+ tags: ["skills"],
228
+ responseBody: z.object({
229
+ categories: z.array(
230
+ z.object({
231
+ slug: z.string(),
232
+ label: z.string(),
233
+ description: z.string(),
234
+ icon: z.string(),
235
+ }),
236
+ ),
237
+ }),
238
+ handler: async () => {
239
+ const categories = await getCategories();
240
+ return { categories };
241
+ },
242
+ },
223
243
  {
224
244
  operationId: "getSkillFileContent",
225
245
  endpoint: "skills/:id/files/content",
@@ -546,10 +566,19 @@ export const ROUTES: RouteDefinition[] = [
546
566
  description: "Install a skill by slug, URL, or spec.",
547
567
  tags: ["skills"],
548
568
  requestBody: z.object({
549
- slug: z.string().describe("Skill slug"),
550
- url: z.string().describe("Skill URL"),
551
- spec: z.string().describe("Skill spec"),
552
- version: z.string(),
569
+ slug: z
570
+ .string()
571
+ .optional()
572
+ .describe("Skill slug. One of slug, url, or spec is required."),
573
+ url: z
574
+ .string()
575
+ .optional()
576
+ .describe("Skill URL. One of slug, url, or spec is required."),
577
+ spec: z
578
+ .string()
579
+ .optional()
580
+ .describe("Skill spec. One of slug, url, or spec is required."),
581
+ version: z.string().optional().describe("Specific version to install"),
553
582
  origin: z
554
583
  .enum(["clawhub", "skillssh"])
555
584
  .optional()