@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
@@ -36,6 +36,41 @@ import { ACTOR_PRINCIPALS } from "../../auth/route-policy.js";
36
36
  import { BadRequestError, InternalError } from "../errors.js";
37
37
  import type { RouteDefinition, RouteHandlerArgs } from "../types.js";
38
38
 
39
+ // ---------------------------------------------------------------------------
40
+ // Response schemas
41
+ // ---------------------------------------------------------------------------
42
+
43
+ const TwilioPhoneNumberSchema = z.object({
44
+ phoneNumber: z.string(),
45
+ friendlyName: z.string(),
46
+ capabilities: z.object({ voice: z.boolean() }),
47
+ });
48
+
49
+ const TwilioConfigResultSchema = z.object({
50
+ success: z.boolean(),
51
+ hasCredentials: z.boolean(),
52
+ accountSid: z.string().optional(),
53
+ phoneNumber: z.string().optional(),
54
+ });
55
+
56
+ const TwilioCredentialsResultSchema = z.object({
57
+ success: z.boolean(),
58
+ hasCredentials: z.boolean(),
59
+ });
60
+
61
+ const TwilioNumbersResultSchema = z.object({
62
+ success: z.boolean(),
63
+ hasCredentials: z.boolean(),
64
+ numbers: z.array(TwilioPhoneNumberSchema),
65
+ });
66
+
67
+ const TwilioNumberMutationResultSchema = z.object({
68
+ success: z.boolean(),
69
+ hasCredentials: z.boolean(),
70
+ phoneNumber: z.string().optional(),
71
+ warning: z.string().optional(),
72
+ });
73
+
39
74
  // ---------------------------------------------------------------------------
40
75
  // Shared helpers
41
76
  // ---------------------------------------------------------------------------
@@ -362,6 +397,7 @@ export const ROUTES: RouteDefinition[] = [
362
397
  summary: "Get Twilio config",
363
398
  description: "Return current Twilio configuration status.",
364
399
  tags: ["integrations"],
400
+ responseBody: TwilioConfigResultSchema,
365
401
  handler: () => handleGetTwilioConfig(),
366
402
  },
367
403
  {
@@ -380,6 +416,7 @@ export const ROUTES: RouteDefinition[] = [
380
416
  accountSid: z.string().describe("Twilio account SID"),
381
417
  authToken: z.string().describe("Twilio auth token"),
382
418
  }),
419
+ responseBody: TwilioCredentialsResultSchema,
383
420
  },
384
421
  {
385
422
  operationId: "integrations_twilio_credentials_delete",
@@ -392,6 +429,7 @@ export const ROUTES: RouteDefinition[] = [
392
429
  summary: "Clear Twilio credentials",
393
430
  description: "Remove stored Twilio credentials.",
394
431
  tags: ["integrations"],
432
+ responseBody: TwilioCredentialsResultSchema,
395
433
  handler: () => handleClearTwilioCredentials(),
396
434
  },
397
435
  {
@@ -405,6 +443,7 @@ export const ROUTES: RouteDefinition[] = [
405
443
  summary: "List Twilio numbers",
406
444
  description: "List phone numbers on the Twilio account.",
407
445
  tags: ["integrations"],
446
+ responseBody: TwilioNumbersResultSchema,
408
447
  handler: () => handleListTwilioNumbers(),
409
448
  },
410
449
  {
@@ -418,6 +457,7 @@ export const ROUTES: RouteDefinition[] = [
418
457
  summary: "Provision Twilio number",
419
458
  description: "Search for and provision a new phone number.",
420
459
  tags: ["integrations"],
460
+ responseBody: TwilioNumberMutationResultSchema,
421
461
  handler: handleProvisionTwilioNumber,
422
462
  },
423
463
  {
@@ -431,6 +471,7 @@ export const ROUTES: RouteDefinition[] = [
431
471
  summary: "Assign Twilio number",
432
472
  description: "Assign an existing phone number to this assistant.",
433
473
  tags: ["integrations"],
474
+ responseBody: TwilioNumberMutationResultSchema,
434
475
  handler: handleAssignTwilioNumber,
435
476
  },
436
477
  {
@@ -444,6 +485,7 @@ export const ROUTES: RouteDefinition[] = [
444
485
  summary: "Release Twilio number",
445
486
  description: "Release a phone number back to Twilio.",
446
487
  tags: ["integrations"],
488
+ responseBody: TwilioNumberMutationResultSchema,
447
489
  handler: handleReleaseTwilioNumber,
448
490
  },
449
491
  ];
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Internal telemetry routes — receive operational signals forwarded from the
3
+ * gateway over the internal (service-token) transport.
4
+ *
5
+ * POST /v1/internal/telemetry/auth-fallback — record aggregated counts of
6
+ * requests served via the legacy loopback auth fallback. The gateway counts
7
+ * fallbacks in memory and flushes them here per window; the usage telemetry
8
+ * reporter ships the persisted rows to the platform.
9
+ */
10
+
11
+ import { z } from "zod";
12
+
13
+ import {
14
+ type AuthFallbackCount,
15
+ recordAuthFallbackCounts,
16
+ } from "../../memory/auth-fallback-events-store.js";
17
+ import { getLogger } from "../../util/logger.js";
18
+ import { GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
19
+ import { BadRequestError } from "./errors.js";
20
+ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
21
+
22
+ const log = getLogger("internal-telemetry-routes");
23
+
24
+ const authFallbackBody = z.object({
25
+ window_start: z.number().int().nonnegative(),
26
+ window_end: z.number().int().nonnegative(),
27
+ counts: z
28
+ .array(
29
+ z.object({
30
+ guard: z.string().min(1),
31
+ path: z.string().min(1),
32
+ failure_kind: z.string().min(1),
33
+ count: z.number().int().positive(),
34
+ }),
35
+ )
36
+ .min(1),
37
+ });
38
+
39
+ function handleRecordAuthFallback({ body }: RouteHandlerArgs) {
40
+ const parsed = authFallbackBody.safeParse(body);
41
+ if (!parsed.success) {
42
+ throw new BadRequestError(
43
+ `Invalid auth-fallback payload: ${parsed.error.message}`,
44
+ );
45
+ }
46
+ const { window_start, window_end, counts } = parsed.data;
47
+ const mapped: AuthFallbackCount[] = counts.map((c) => ({
48
+ guard: c.guard,
49
+ path: c.path,
50
+ failureKind: c.failure_kind,
51
+ count: c.count,
52
+ }));
53
+
54
+ const recorded = recordAuthFallbackCounts(window_start, window_end, mapped);
55
+ if (recorded === 0) {
56
+ // collectUsageData disabled — counts dropped to honor the opt-out.
57
+ return { skipped: true };
58
+ }
59
+ log.debug({ recorded }, "Recorded auth-fallback counts");
60
+ return { recorded };
61
+ }
62
+
63
+ export const ROUTES: RouteDefinition[] = [
64
+ {
65
+ operationId: "internal_telemetry_auth_fallback",
66
+ endpoint: "internal/telemetry/auth-fallback",
67
+ method: "POST",
68
+ policy: {
69
+ requiredScopes: ["internal.write"],
70
+ allowedPrincipalTypes: GATEWAY_PRINCIPALS,
71
+ },
72
+ summary: "Record auth-fallback counts",
73
+ description:
74
+ "Receives aggregated legacy-loopback auth-fallback counts forwarded by " +
75
+ "the gateway and persists them for telemetry reporting.",
76
+ tags: ["internal", "telemetry"],
77
+ requestBody: authFallbackBody,
78
+ responseBody: z.union([
79
+ z.object({ recorded: z.number().int().nonnegative() }),
80
+ z.object({
81
+ skipped: z
82
+ .literal(true)
83
+ .describe("Counts dropped because usage data collection is disabled"),
84
+ }),
85
+ ]),
86
+ handler: handleRecordAuthFallback,
87
+ },
88
+ ];
@@ -508,6 +508,10 @@ export const ROUTES: RouteDefinition[] = [
508
508
  "Export audit records, assistant logs, and config as a tar.gz archive.",
509
509
  tags: ["export"],
510
510
  requestBody: exportRequestBody,
511
+ responseBody: {
512
+ contentType: "application/gzip",
513
+ schema: { type: "string", format: "binary" },
514
+ },
511
515
  responseHeaders: {
512
516
  "Content-Type": "application/gzip",
513
517
  "Content-Disposition": 'attachment; filename="logs.tar.gz"',
@@ -532,6 +536,10 @@ export const ROUTES: RouteDefinition[] = [
532
536
  "Alias for /v1/export. Export audit records, assistant logs, and config as a tar.gz archive.",
533
537
  tags: ["export"],
534
538
  requestBody: exportRequestBody,
539
+ responseBody: {
540
+ contentType: "application/gzip",
541
+ schema: { type: "string", format: "binary" },
542
+ },
535
543
  responseHeaders: {
536
544
  "Content-Type": "application/gzip",
537
545
  "Content-Disposition": 'attachment; filename="logs.tar.gz"',
@@ -210,14 +210,20 @@ async function handleGetConceptPage({
210
210
 
211
211
  const MemoryV2ListConceptPagesParams = z.object({}).strict();
212
212
 
213
- export type MemoryV2ListConceptPagesResult = {
214
- pages: Array<{
215
- slug: string;
216
- bodyBytes: number;
217
- edgeCount: number;
218
- updatedAtMs: number;
219
- }>;
220
- };
213
+ export const MemoryV2ListConceptPagesResultSchema = z.object({
214
+ pages: z.array(
215
+ z.object({
216
+ slug: z.string(),
217
+ bodyBytes: z.number(),
218
+ edgeCount: z.number(),
219
+ updatedAtMs: z.number(),
220
+ }),
221
+ ),
222
+ });
223
+
224
+ export type MemoryV2ListConceptPagesResult = z.infer<
225
+ typeof MemoryV2ListConceptPagesResultSchema
226
+ >;
221
227
 
222
228
  async function handleListConceptPages({
223
229
  body = {},
@@ -732,6 +738,7 @@ export const ROUTES: RouteDefinition[] = [
732
738
  "Returns slugs, body sizes, edge counts, and last-modified timestamps for every concept page on disk. Read-only; used by the desktop About → Memories surface to render a browse-able list.",
733
739
  tags: ["memory"],
734
740
  requestBody: MemoryV2ListConceptPagesParams,
741
+ responseBody: MemoryV2ListConceptPagesResultSchema,
735
742
  },
736
743
  {
737
744
  operationId: "memory_v2_reembed_skills",
@@ -18,6 +18,8 @@
18
18
  import { writeFile } from "node:fs/promises";
19
19
  import { join } from "node:path";
20
20
 
21
+ import { z } from "zod";
22
+
21
23
  import { getPageIndex } from "../../memory/v2/page-index.js";
22
24
  import { loadCore } from "../../memory/v3/core.js";
23
25
  import { computeV3Health, renderV3Health } from "../../memory/v3/health.js";
@@ -88,18 +90,19 @@ async function loadTreeAndSlugs(deps?: MemoryV3Deps): Promise<{
88
90
  // health
89
91
  // ---------------------------------------------------------------------------
90
92
 
91
- export interface MemoryV3HealthResult {
93
+ const MemoryV3HealthResultSchema = z.object({
92
94
  /** Pre-rendered, human-readable report. Empty string when all-green. */
93
- rendered: string;
95
+ rendered: z.string(),
94
96
  /** The structural counts, for `--json` consumers. */
95
- counts: {
96
- unassigned: number;
97
- danglingRefs: number;
98
- novelClusters: number;
99
- oversizedLeaves: number;
100
- tinyLeaves: number;
101
- };
102
- }
97
+ counts: z.object({
98
+ unassigned: z.number(),
99
+ danglingRefs: z.number(),
100
+ novelClusters: z.number(),
101
+ oversizedLeaves: z.number(),
102
+ tinyLeaves: z.number(),
103
+ }),
104
+ });
105
+ export type MemoryV3HealthResult = z.infer<typeof MemoryV3HealthResultSchema>;
103
106
 
104
107
  export async function handleMemoryV3Health(
105
108
  deps?: MemoryV3Deps,
@@ -123,26 +126,28 @@ export async function handleMemoryV3Health(
123
126
  // set-core
124
127
  // ---------------------------------------------------------------------------
125
128
 
126
- export interface MemoryV3SetCoreBody {
129
+ const MemoryV3SetCoreBodySchema = z.object({
127
130
  /** Leaves to add to the always-on core set. */
128
- add?: LeafPath[];
131
+ add: z.array(z.string()).optional(),
129
132
  /** Leaves to remove from the always-on core set. */
130
- remove?: LeafPath[];
133
+ remove: z.array(z.string()).optional(),
131
134
  /**
132
135
  * When true, persist the new core to `core.json` and invalidate the lanes.
133
136
  * When false (default), compute the preview WITHOUT writing.
134
137
  */
135
- write?: boolean;
136
- }
138
+ write: z.boolean().optional(),
139
+ });
140
+ export type MemoryV3SetCoreBody = z.infer<typeof MemoryV3SetCoreBodySchema>;
137
141
 
138
- export interface MemoryV3SetCoreResult {
142
+ const MemoryV3SetCoreResultSchema = z.object({
139
143
  /** The core leaf set that would result (or did result, when `write`). */
140
- nextCore: LeafPath[];
144
+ nextCore: z.array(z.string()),
141
145
  /** Number of unique page slugs the new core set pins always-on. */
142
- alwaysOnPageCount: number;
146
+ alwaysOnPageCount: z.number(),
143
147
  /** Whether `core.json` was written. */
144
- written: boolean;
145
- }
148
+ written: z.boolean(),
149
+ });
150
+ export type MemoryV3SetCoreResult = z.infer<typeof MemoryV3SetCoreResultSchema>;
146
151
 
147
152
  /** Wire-format error code for a `set-core` add referencing an unknown leaf. */
148
153
  export const MEMORY_V3_UNKNOWN_LEAF_CODE = "MEMORY_V3_UNKNOWN_LEAF";
@@ -206,11 +211,20 @@ export async function handleMemoryV3SetCore(
206
211
  // reconcile
207
212
  // ---------------------------------------------------------------------------
208
213
 
209
- export interface MemoryV3ReconcileResult {
210
- renames: Array<{ id?: string; oldPath: LeafPath; newPath: LeafPath }>;
211
- deleted: LeafPath[];
212
- prunedCore: LeafPath[];
213
- }
214
+ const MemoryV3ReconcileResultSchema = z.object({
215
+ renames: z.array(
216
+ z.object({
217
+ id: z.string().optional(),
218
+ oldPath: z.string(),
219
+ newPath: z.string(),
220
+ }),
221
+ ),
222
+ deleted: z.array(z.string()),
223
+ prunedCore: z.array(z.string()),
224
+ });
225
+ export type MemoryV3ReconcileResult = z.infer<
226
+ typeof MemoryV3ReconcileResultSchema
227
+ >;
214
228
 
215
229
  /**
216
230
  * Reconcile page + core references against the live on-disk tree.
@@ -253,9 +267,12 @@ async function loadPrevLeaves(dataDir: string): Promise<LeafRef[]> {
253
267
  // rebuild-index
254
268
  // ---------------------------------------------------------------------------
255
269
 
256
- export interface MemoryV3RebuildIndexResult {
257
- ok: true;
258
- }
270
+ const MemoryV3RebuildIndexResultSchema = z.object({
271
+ ok: z.literal(true),
272
+ });
273
+ export type MemoryV3RebuildIndexResult = z.infer<
274
+ typeof MemoryV3RebuildIndexResultSchema
275
+ >;
259
276
 
260
277
  /**
261
278
  * Invalidate the v3 shadow lanes so the next turn rebuilds the tree/needle
@@ -299,6 +316,7 @@ export const ROUTES: RouteDefinition[] = [
299
316
  handler: () => handleMemoryV3Health(),
300
317
  summary: "Print the v3 structural health report (read-only)",
301
318
  tags: ["memory"],
319
+ responseBody: MemoryV3HealthResultSchema,
302
320
  },
303
321
  {
304
322
  operationId: "memory_v3_set_core",
@@ -317,6 +335,8 @@ export const ROUTES: RouteDefinition[] = [
317
335
  },
318
336
  summary: "Add/remove always-on core leaves (validates + previews cost)",
319
337
  tags: ["memory"],
338
+ requestBody: MemoryV3SetCoreBodySchema,
339
+ responseBody: MemoryV3SetCoreResultSchema,
320
340
  },
321
341
  {
322
342
  operationId: "memory_v3_reconcile",
@@ -327,6 +347,7 @@ export const ROUTES: RouteDefinition[] = [
327
347
  summary:
328
348
  "v1 convergence/prune pass over page+core refs (no rename detection without a prior snapshot)",
329
349
  tags: ["memory"],
350
+ responseBody: MemoryV3ReconcileResultSchema,
330
351
  },
331
352
  {
332
353
  operationId: "memory_v3_rebuild_index",
@@ -336,5 +357,6 @@ export const ROUTES: RouteDefinition[] = [
336
357
  handler: () => handleMemoryV3RebuildIndex(),
337
358
  summary: "Invalidate the v3 lanes so the next turn rebuilds",
338
359
  tags: ["memory"],
360
+ responseBody: MemoryV3RebuildIndexResultSchema,
339
361
  },
340
362
  ];
@@ -6,6 +6,8 @@
6
6
  * bearer-token authenticated via the standard runtime auth middleware.
7
7
  */
8
8
 
9
+ import { z } from "zod";
10
+
9
11
  import { orchestrateOAuthConnect } from "../../oauth/connect-orchestrator.js";
10
12
  import {
11
13
  deleteApp,
@@ -22,11 +24,8 @@ import {
22
24
  } from "../../oauth/oauth-store.js";
23
25
  import { serializeProviderSummary } from "../../oauth/provider-serializer.js";
24
26
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
25
- import {
26
- BadRequestError,
27
- InternalError,
28
- NotFoundError,
29
- } from "./errors.js";
27
+ import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
28
+ import { oauthProviderSummarySchema } from "./oauth-providers.js";
30
29
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
31
30
 
32
31
  function parseGrantedScopes(
@@ -131,17 +130,14 @@ async function handleUpsertApp({ body = {} }: RouteHandlerArgs) {
131
130
  const clientId = b.client_id as string | undefined;
132
131
 
133
132
  if (!providerKey || !clientId) {
134
- throw new BadRequestError(
135
- "provider_key and client_id are required",
136
- );
133
+ throw new BadRequestError("provider_key and client_id are required");
137
134
  }
138
135
 
139
136
  const clientSecretOpts = b.client_secret
140
137
  ? { clientSecretValue: b.client_secret as string }
141
138
  : b.client_secret_credential_path
142
139
  ? {
143
- clientSecretCredentialPath:
144
- b.client_secret_credential_path as string,
140
+ clientSecretCredentialPath: b.client_secret_credential_path as string,
145
141
  }
146
142
  : undefined;
147
143
 
@@ -297,6 +293,38 @@ async function handleConnectApp({ pathParams = {}, body }: RouteHandlerArgs) {
297
293
  return { ok: true };
298
294
  }
299
295
 
296
+ // ---------------------------------------------------------------------------
297
+ // Schemas
298
+ // ---------------------------------------------------------------------------
299
+
300
+ /** Custom OAuth app registration (snake_case wire shape from `formatAppRow`). */
301
+ const oauthAppSchema = z.object({
302
+ id: z.string(),
303
+ provider_key: z.string(),
304
+ client_id: z.string(),
305
+ created_at: z.number(),
306
+ updated_at: z.number(),
307
+ });
308
+
309
+ /** OAuth connection linked to a custom OAuth app. */
310
+ const oauthAppConnectionSchema = z.object({
311
+ id: z.string(),
312
+ provider_key: z.string(),
313
+ account_info: z.string().nullable(),
314
+ granted_scopes: z.array(z.string()),
315
+ status: z.string(),
316
+ has_refresh_token: z.boolean(),
317
+ expires_at: z.number().nullable(),
318
+ created_at: z.number(),
319
+ updated_at: z.number(),
320
+ });
321
+
322
+ const oauthAppCredentialsSchema = z.object({
323
+ provider_key: z.string(),
324
+ client_id: z.string(),
325
+ client_secret: z.string(),
326
+ });
327
+
300
328
  // ---------------------------------------------------------------------------
301
329
  // Route definitions
302
330
  // ---------------------------------------------------------------------------
@@ -321,6 +349,10 @@ export const ROUTES: RouteDefinition[] = [
321
349
  description: "OAuth provider key to filter by",
322
350
  },
323
351
  ],
352
+ responseBody: z.object({
353
+ provider: oauthProviderSummarySchema.nullable(),
354
+ apps: z.array(oauthAppSchema),
355
+ }),
324
356
  handler: handleListApps,
325
357
  },
326
358
  {
@@ -352,6 +384,7 @@ export const ROUTES: RouteDefinition[] = [
352
384
  description: "OAuth client ID (requires provider)",
353
385
  },
354
386
  ],
387
+ responseBody: z.object({ app: oauthAppSchema }),
355
388
  handler: handleGetApp,
356
389
  },
357
390
  {
@@ -366,6 +399,13 @@ export const ROUTES: RouteDefinition[] = [
366
399
  description:
367
400
  "Create or return an existing OAuth app registration. Updates client secret if provided.",
368
401
  tags: ["oauth"],
402
+ requestBody: z.object({
403
+ provider_key: z.string(),
404
+ client_id: z.string(),
405
+ client_secret: z.string().optional(),
406
+ client_secret_credential_path: z.string().optional(),
407
+ }),
408
+ responseBody: z.object({ app: oauthAppSchema }),
369
409
  handler: handleUpsertApp,
370
410
  },
371
411
  {
@@ -379,6 +419,8 @@ export const ROUTES: RouteDefinition[] = [
379
419
  summary: "Create OAuth app",
380
420
  description: "Register a new OAuth app with client credentials.",
381
421
  tags: ["oauth"],
422
+ requestBody: oauthAppCredentialsSchema,
423
+ responseBody: z.object({ app: oauthAppSchema }),
382
424
  responseStatus: "201",
383
425
  handler: handleCreateApp,
384
426
  },
@@ -391,9 +433,9 @@ export const ROUTES: RouteDefinition[] = [
391
433
  allowedPrincipalTypes: ACTOR_PRINCIPALS,
392
434
  },
393
435
  summary: "Delete OAuth app",
394
- description:
395
- "Delete an OAuth app and disconnect all its connections.",
436
+ description: "Delete an OAuth app and disconnect all its connections.",
396
437
  tags: ["oauth"],
438
+ responseBody: z.object({ ok: z.boolean() }),
397
439
  handler: handleDeleteApp,
398
440
  },
399
441
  {
@@ -407,6 +449,9 @@ export const ROUTES: RouteDefinition[] = [
407
449
  summary: "List OAuth connections",
408
450
  description: "List connections for an OAuth app.",
409
451
  tags: ["oauth"],
452
+ responseBody: z.object({
453
+ connections: z.array(oauthAppConnectionSchema),
454
+ }),
410
455
  handler: handleListConnections,
411
456
  },
412
457
  {
@@ -420,6 +465,7 @@ export const ROUTES: RouteDefinition[] = [
420
465
  summary: "Disconnect OAuth connection",
421
466
  description: "Disconnect a single OAuth connection.",
422
467
  tags: ["oauth"],
468
+ responseBody: z.object({ ok: z.boolean() }),
423
469
  handler: handleDeleteConnection,
424
470
  },
425
471
  {
@@ -433,6 +479,14 @@ export const ROUTES: RouteDefinition[] = [
433
479
  summary: "Start OAuth connect",
434
480
  description: "Start an OAuth connect flow for an app.",
435
481
  tags: ["oauth"],
482
+ requestBody: z.object({
483
+ scopes: z.array(z.string()).optional(),
484
+ callback_transport: z.enum(["loopback", "gateway"]).optional(),
485
+ }),
486
+ responseBody: z.union([
487
+ z.object({ auth_url: z.string(), state: z.string() }),
488
+ z.object({ ok: z.literal(true) }),
489
+ ]),
436
490
  handler: handleConnectApp,
437
491
  },
438
492
  ];
@@ -6,6 +6,8 @@
6
6
  * the standard runtime auth middleware.
7
7
  */
8
8
 
9
+ import { z } from "zod";
10
+
9
11
  import { loadConfig } from "../../config/loader.js";
10
12
  import { getOAuthCallbackUrl } from "../../inbound/public-ingress-urls.js";
11
13
  import {
@@ -220,7 +222,8 @@ function handleUpdateProvider({
220
222
  if (b.default_scopes !== undefined) params.defaultScopes = b.default_scopes;
221
223
  if (b.available_scopes !== undefined)
222
224
  params.availableScopes = b.available_scopes;
223
- if (b.scope_separator !== undefined) params.scopeSeparator = b.scope_separator;
225
+ if (b.scope_separator !== undefined)
226
+ params.scopeSeparator = b.scope_separator;
224
227
  if (b.token_endpoint_auth_method !== undefined)
225
228
  params.tokenEndpointAuthMethod = b.token_endpoint_auth_method;
226
229
  if (b.token_exchange_body_format !== undefined)
@@ -245,13 +248,15 @@ function handleUpdateProvider({
245
248
  params.injectionTemplates = b.injection_templates;
246
249
  if (b.app_type !== undefined) params.appType = b.app_type;
247
250
  if (b.identity_url !== undefined) params.identityUrl = b.identity_url;
248
- if (b.identity_method !== undefined) params.identityMethod = b.identity_method;
251
+ if (b.identity_method !== undefined)
252
+ params.identityMethod = b.identity_method;
249
253
  if (b.identity_headers !== undefined)
250
254
  params.identityHeaders = b.identity_headers;
251
255
  if (b.identity_body !== undefined) params.identityBody = b.identity_body;
252
256
  if (b.identity_response_paths !== undefined)
253
257
  params.identityResponsePaths = b.identity_response_paths;
254
- if (b.identity_format !== undefined) params.identityFormat = b.identity_format;
258
+ if (b.identity_format !== undefined)
259
+ params.identityFormat = b.identity_format;
255
260
  if (b.identity_ok_field !== undefined)
256
261
  params.identityOkField = b.identity_ok_field;
257
262
  if (b.setup_notes !== undefined) params.setupNotes = b.setup_notes;
@@ -321,6 +326,37 @@ async function handleDeleteProvider({
321
326
  // Route definitions
322
327
  // ---------------------------------------------------------------------------
323
328
 
329
+ /**
330
+ * Lightweight summary projection of an OAuth provider returned by the catalog
331
+ * list endpoint. Mirrors `SerializedProviderSummary` from the provider
332
+ * serializer (snake_case to match the HTTP API convention).
333
+ */
334
+ export const oauthProviderSummarySchema = z.object({
335
+ provider_key: z.string(),
336
+ display_name: z.string().nullable(),
337
+ description: z.string().nullable(),
338
+ dashboard_url: z.string().nullable(),
339
+ client_id_placeholder: z.string().nullable(),
340
+ requires_client_secret: z.boolean(),
341
+ logo_url: z.string().nullable(),
342
+ supports_managed_mode: z.boolean(),
343
+ managed_service_is_paid: z.boolean(),
344
+ feature_flag: z.string().nullable(),
345
+ });
346
+
347
+ /**
348
+ * Response for the single-provider detail endpoint. The `provider` field is the
349
+ * full serialized provider configuration — an open-ended, admin-defined object
350
+ * whose keys depend on the stored row (parsed JSON blobs, identity templates,
351
+ * and forwarded columns), so it is intentionally typed as an open record rather
352
+ * than a closed schema. `oauth_callback_url` is the ingress callback URL the
353
+ * web UI consumes (null when ingress is not configured).
354
+ */
355
+ export const oauthProviderDetailSchema = z.object({
356
+ provider: z.record(z.string(), z.unknown()),
357
+ oauth_callback_url: z.string().nullable(),
358
+ });
359
+
324
360
  export const ROUTES: RouteDefinition[] = [
325
361
  {
326
362
  operationId: "oauth_providers_get",
@@ -331,8 +367,7 @@ export const ROUTES: RouteDefinition[] = [
331
367
  allowedPrincipalTypes: ACTOR_PRINCIPALS,
332
368
  },
333
369
  summary: "List OAuth providers",
334
- description:
335
- "List all registered OAuth providers with optional filtering.",
370
+ description: "List all registered OAuth providers with optional filtering.",
336
371
  tags: ["oauth"],
337
372
  handler: handleListProviders,
338
373
  queryParams: [
@@ -342,6 +377,9 @@ export const ROUTES: RouteDefinition[] = [
342
377
  description: "Filter by managed mode support (true/false)",
343
378
  },
344
379
  ],
380
+ responseBody: z.object({
381
+ providers: z.array(oauthProviderSummarySchema),
382
+ }),
345
383
  },
346
384
  {
347
385
  operationId: "oauth_providers_by_providerKey_get",
@@ -355,6 +393,7 @@ export const ROUTES: RouteDefinition[] = [
355
393
  description: "Get a single OAuth provider by key.",
356
394
  tags: ["oauth"],
357
395
  handler: handleGetProvider,
396
+ responseBody: oauthProviderDetailSchema,
358
397
  },
359
398
  {
360
399
  operationId: "oauth_providers_post",