@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
@@ -43,7 +43,7 @@ import { appendEventToStream } from "../signals/event-stream.js";
43
43
  import { getLogger } from "../util/logger.js";
44
44
  import type { AssistantEvent } from "./assistant-event.js";
45
45
  import { buildAssistantEvent } from "./assistant-event.js";
46
- import { stampAndBuffer } from "./conversation-stream-state.js";
46
+ import { stampAndBuffer } from "./assistant-stream-state.js";
47
47
 
48
48
  const log = getLogger("assistant-event-hub");
49
49
 
@@ -64,6 +64,13 @@ export interface AssistantEventSubscription {
64
64
  dispose(): void;
65
65
  /** True until `dispose()` has been called. */
66
66
  readonly active: boolean;
67
+ /**
68
+ * Per-connection identifier, unique within the hub instance. Distinguishes
69
+ * connections that share a `clientId` (e.g. an old connection and the new
70
+ * one that replaced it on reconnect) so subscribe / dispose / shed log
71
+ * lines can be attributed to a specific connection.
72
+ */
73
+ readonly connectionId: string;
67
74
  }
68
75
 
69
76
  // ── Subscriber entries (discriminated union) ─────────────────────────────────
@@ -75,6 +82,13 @@ interface BaseSubscriberEntry {
75
82
  onEvict: () => void;
76
83
  connectedAt: Date;
77
84
  lastActiveAt: Date;
85
+ /**
86
+ * Per-connection identifier, unique within the hub instance. Two entries
87
+ * with the same `clientId` (old vs reconnected connection) get distinct
88
+ * connection ids, making them traceable across subscribe / dispose / shed
89
+ * logs.
90
+ */
91
+ connectionId: string;
78
92
  }
79
93
 
80
94
  interface ClientEntry extends BaseSubscriberEntry {
@@ -106,10 +120,15 @@ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
106
120
  ? Omit<T, K>
107
121
  : never;
108
122
 
109
- /** Input shape for `subscribe()` — hub fills `active`, `connectedAt`, `lastActiveAt` and defaults `filter`/`onEvict`. */
123
+ /** Input shape for `subscribe()` — hub fills `active`, `connectedAt`, `lastActiveAt`, `connectionId` and defaults `filter`/`onEvict`. */
110
124
  type SubscriberInput = DistributiveOmit<
111
125
  SubscriberEntry,
112
- "active" | "connectedAt" | "lastActiveAt" | "filter" | "onEvict"
126
+ | "active"
127
+ | "connectedAt"
128
+ | "lastActiveAt"
129
+ | "filter"
130
+ | "onEvict"
131
+ | "connectionId"
113
132
  > & {
114
133
  filter?: AssistantEventFilter;
115
134
  onEvict?: () => void;
@@ -132,6 +151,8 @@ type SubscriberInput = DistributiveOmit<
132
151
  export class AssistantEventHub {
133
152
  private readonly subscribers = new Set<SubscriberEntry>();
134
153
  private readonly maxSubscribers: number;
154
+ /** Monotonic source for per-connection ids, scoped to this hub. */
155
+ private connectionCounter = 0;
135
156
 
136
157
  constructor(options?: { maxSubscribers?: number }) {
137
158
  this.maxSubscribers = options?.maxSubscribers ?? Infinity;
@@ -173,7 +194,11 @@ export class AssistantEventHub {
173
194
  }
174
195
  if (stale.length > 0) {
175
196
  log.info(
176
- { clientId: subscriber.clientId, count: stale.length },
197
+ {
198
+ clientId: subscriber.clientId,
199
+ count: stale.length,
200
+ disposedConnectionIds: stale.map((entry) => entry.connectionId),
201
+ },
177
202
  "disposed stale subscribers for reconnecting client",
178
203
  );
179
204
  }
@@ -196,6 +221,7 @@ export class AssistantEventHub {
196
221
  }
197
222
 
198
223
  const now = new Date();
224
+ const connectionId = `conn-${++this.connectionCounter}`;
199
225
  const entry: SubscriberEntry = {
200
226
  ...subscriber,
201
227
  filter: subscriber.filter ?? {},
@@ -203,6 +229,7 @@ export class AssistantEventHub {
203
229
  active: true,
204
230
  connectedAt: now,
205
231
  lastActiveAt: now,
232
+ connectionId,
206
233
  } as SubscriberEntry;
207
234
 
208
235
  if (entry.type === "client") {
@@ -211,11 +238,12 @@ export class AssistantEventHub {
211
238
  clientId: entry.clientId,
212
239
  interfaceId: entry.interfaceId,
213
240
  capabilities: entry.capabilities,
241
+ connectionId,
214
242
  },
215
243
  "subscriber registered (client)",
216
244
  );
217
245
  } else {
218
- log.info("subscriber registered (process)");
246
+ log.info({ connectionId }, "subscriber registered (process)");
219
247
  }
220
248
 
221
249
  this.subscribers.add(entry);
@@ -230,17 +258,19 @@ export class AssistantEventHub {
230
258
  {
231
259
  clientId: entry.clientId,
232
260
  interfaceId: entry.interfaceId,
261
+ connectionId,
233
262
  },
234
263
  "subscriber unregistered (client)",
235
264
  );
236
265
  } else {
237
- log.info("subscriber unregistered (process)");
266
+ log.info({ connectionId }, "subscriber unregistered (process)");
238
267
  }
239
268
  }
240
269
  },
241
270
  get active() {
242
271
  return entry.active;
243
272
  },
273
+ connectionId,
244
274
  };
245
275
  }
246
276
 
@@ -1,11 +1,19 @@
1
1
  /**
2
- * Conversation Stream State -- per-conversation SSE sequence counter and
3
- * ring buffer for `Last-Event-ID` replay (B7 Unit 1).
2
+ * Assistant Stream State -- a single per-assistant (per-daemon-process)
3
+ * SSE sequence counter and ring buffer for `Last-Event-ID` replay.
4
4
  *
5
5
  * Every conversation-scoped outbound event picks up a monotonic `seq`
6
- * number from this module. The same event is also pushed onto a bounded
7
- * ring buffer so a reconnecting client can request replay of events the
8
- * daemon emitted while it was disconnected.
6
+ * from one global counter shared across all conversations, and is pushed
7
+ * onto one shared ring buffer. A reconnecting client presents the highest
8
+ * `seq` it has applied; the daemon replays everything newer from the ring
9
+ * -- re-applying the subscriber's targeting/scope filter -- then goes
10
+ * live.
11
+ *
12
+ * A single global seq space means the reconnect cursor is one number, not
13
+ * a per-conversation map: on one ordered SSE connection the client has
14
+ * received a contiguous prefix of the global stream, so "highest seq
15
+ * applied" is a valid resume point no matter how many conversations are
16
+ * multiplexed on the connection.
9
17
  *
10
18
  * Bounds (oldest evicted first; first bound hit wins):
11
19
  * - Count: 200 events
@@ -13,9 +21,23 @@
13
21
  * - Age: 30 seconds
14
22
  *
15
23
  * The ring is in-memory and per-daemon-process. After a daemon restart
16
- * all seqs reset and reconnecting clients fall through to the snapshot
17
- * path (delivered by B7 Unit 2). The ring is sized generously enough
18
- * that a typical refresh round-trip (~1-3s) is well within window.
24
+ * the seq resets and reconnecting clients fall through to the snapshot
25
+ * path. The ring is sized generously enough that a typical refresh
26
+ * round-trip (~1-3s) is well within window.
27
+ *
28
+ * Persisted-seq map: alongside the live counter and ring, this module
29
+ * tracks, per conversation, the `seq` of the last event whose content is
30
+ * durably committed to the message rows (`persistedSeqByConversation`).
31
+ * The `/messages` snapshot returns this value so a client can align the
32
+ * snapshot with the stream: "these rows reflect all of this
33
+ * conversation's events through `seq = S`." It is recorded at each
34
+ * persistence flush (assistant rows persist incrementally, debounced, so
35
+ * the snapshot can lag the live counter) -- never the live counter
36
+ * itself, which would over-claim events that have streamed but not yet
37
+ * been written. It shares the live counter's lifetime by design: both
38
+ * are in-memory and reset together on restart, so a stored value can
39
+ * never dangle against a fresh counter. The map is LRU-bounded; an
40
+ * evicted conversation reports no seq and the client cold-starts.
19
41
  */
20
42
 
21
43
  import type { AssistantEvent } from "./assistant-event.js";
@@ -26,6 +48,16 @@ const RING_COUNT_LIMIT = 200;
26
48
  const RING_SIZE_LIMIT_BYTES = 256 * 1024;
27
49
  const RING_AGE_LIMIT_MS = 30_000;
28
50
 
51
+ /**
52
+ * Cap on how many conversations retain a persisted-seq entry. Unlike the
53
+ * ring (which the live stream needs only briefly), the persisted-seq map
54
+ * grows with the number of conversations that have ever streamed in this
55
+ * process. Bound it LRU so it can't grow without limit; an evicted
56
+ * conversation simply reports no seq on its next `/messages` and the
57
+ * client cold-starts, which is harmless.
58
+ */
59
+ const PERSISTED_SEQ_CONVERSATION_LIMIT = 1024;
60
+
29
61
  // ── Types ────────────────────────────────────────────────────────────
30
62
 
31
63
  /**
@@ -63,30 +95,33 @@ interface RingEntry {
63
95
  targeting?: EventTargeting;
64
96
  }
65
97
 
66
- interface ConversationStreamState {
98
+ interface AssistantStreamState {
67
99
  nextSeq: number;
68
100
  ring: RingEntry[];
69
101
  totalSizeBytes: number;
102
+ /**
103
+ * Per-conversation `seq` of the last event durably committed to the
104
+ * message rows. Insertion order is maintained as an LRU recency list:
105
+ * the oldest key is evicted first once the map exceeds
106
+ * {@link PERSISTED_SEQ_CONVERSATION_LIMIT}.
107
+ */
108
+ persistedSeqByConversation: Map<string, number>;
70
109
  }
71
110
 
72
111
  // ── State ────────────────────────────────────────────────────────────
73
112
 
74
- const streams = new Map<string, ConversationStreamState>();
75
-
76
- function getOrCreate(conversationId: string): ConversationStreamState {
77
- let state = streams.get(conversationId);
78
- if (!state) {
79
- state = { nextSeq: 1, ring: [], totalSizeBytes: 0 };
80
- streams.set(conversationId, state);
81
- }
82
- return state;
83
- }
113
+ const state: AssistantStreamState = {
114
+ nextSeq: 1,
115
+ ring: [],
116
+ totalSizeBytes: 0,
117
+ persistedSeqByConversation: new Map(),
118
+ };
84
119
 
85
120
  // ── Public API ───────────────────────────────────────────────────────
86
121
 
87
122
  /**
88
- * Assign a monotonic `seq` to a conversation-scoped event and push it
89
- * onto the ring buffer. No-op when `event.conversationId` is absent
123
+ * Assign a monotonic global `seq` to a conversation-scoped event and push
124
+ * it onto the ring buffer. No-op when `event.conversationId` is absent
90
125
  * (unscoped broadcasts are never replayable).
91
126
  *
92
127
  * When `options.targeting` is provided, the metadata is stored on the
@@ -101,10 +136,8 @@ export function stampAndBuffer(
101
136
  event: AssistantEvent,
102
137
  options?: { targeting?: EventTargeting },
103
138
  ): void {
104
- const cid = event.conversationId;
105
- if (cid == null) return;
139
+ if (event.conversationId == null) return;
106
140
 
107
- const state = getOrCreate(cid);
108
141
  event.seq = state.nextSeq++;
109
142
 
110
143
  // Approximate size by serialized JSON length. This is the same
@@ -123,43 +156,36 @@ export function stampAndBuffer(
123
156
  state.ring.push(entry);
124
157
  state.totalSizeBytes += sizeBytes;
125
158
 
126
- evict(state);
159
+ evict();
127
160
  }
128
161
 
129
162
  /**
130
- * Replay events with `seq > lastSeenSeq` for a given conversation.
163
+ * Replay events with `seq > lastSeenSeq` from the single global ring.
131
164
  * Returns `null` when the requested cursor is older than the oldest
132
165
  * buffered entry -- callers should fall back to a snapshot resync.
133
166
  *
134
167
  * When `subscriber` is provided, entries carrying targeting metadata
135
168
  * are filtered using the same rules as the live `publish()` path in
136
- * `AssistantEventHub`. This prevents targeted events from leaking to
137
- * subscribers outside their intended delivery set on reconnect.
138
- * When `subscriber` is omitted, all entries are returned unfiltered
139
- * (backwards-compatible behaviour).
169
+ * `AssistantEventHub`, so targeted events do not leak to subscribers
170
+ * outside their intended delivery set on reconnect.
171
+ *
172
+ * When `conversationId` is provided, only that conversation's events are
173
+ * returned -- a conversation-scoped subscription only delivers its own
174
+ * conversation live, so replaying any other conversation's gap would
175
+ * push events the client will never receive again live.
140
176
  *
141
- * Sweeps age-expired entries at read time so an idle conversation
142
- * cannot serve stale deltas past the 30-second window (eviction
143
- * only runs on `stampAndBuffer`, so without this an idle stream
144
- * would retain its tail until the next write). When the sweep
145
- * drains the ring entirely, the conversation's state entry is
146
- * dropped to keep the global map from growing unboundedly with
147
- * inactive conversations.
177
+ * Sweeps age-expired entries at read time so an idle stream cannot serve
178
+ * stale deltas past the 30-second window (eviction otherwise only runs on
179
+ * `stampAndBuffer`).
148
180
  */
149
181
  export function getReplayWindow(
150
- conversationId: string,
151
182
  lastSeenSeq: number,
152
183
  subscriber?: ReplaySubscriber,
184
+ conversationId?: string,
153
185
  ): readonly AssistantEvent[] | null {
154
- const state = streams.get(conversationId);
155
- if (!state) return [];
186
+ evict();
156
187
 
157
- evict(state);
158
-
159
- if (state.ring.length === 0) {
160
- streams.delete(conversationId);
161
- return [];
162
- }
188
+ if (state.ring.length === 0) return [];
163
189
 
164
190
  const oldest = state.ring[0]?.seq ?? Infinity;
165
191
  if (lastSeenSeq < oldest - 1) return null;
@@ -168,39 +194,87 @@ export function getReplayWindow(
168
194
  .filter(
169
195
  (entry) =>
170
196
  entry.seq > lastSeenSeq &&
197
+ (conversationId == null ||
198
+ entry.event.conversationId === conversationId) &&
171
199
  (subscriber == null || matchesSubscriber(entry, subscriber)),
172
200
  )
173
201
  .map((entry) => entry.event);
174
202
  }
175
203
 
176
204
  /**
177
- * Drop all state for a conversation. Currently unused -- the ring
178
- * self-evicts by age -- but exposed for explicit dispose flows
179
- * (e.g. when a conversation is deleted).
205
+ * Current high-water `seq` -- the value last assigned by
206
+ * {@link stampAndBuffer}, or `0` when nothing has been stamped yet in
207
+ * this process.
208
+ *
209
+ * Read synchronously right after emitting an event to learn that event's
210
+ * `seq`: `stampAndBuffer` runs inline on the publish path (before the
211
+ * async fanout), so no other event can interleave between the emit
212
+ * returning and this read on the single-threaded event loop.
213
+ */
214
+ export function getCurrentSeq(): number {
215
+ return state.nextSeq - 1;
216
+ }
217
+
218
+ /**
219
+ * Record that conversation `conversationId` has durably persisted all of
220
+ * its events through `seq`. Called at each persistence flush with the
221
+ * `seq` of the last event whose content the write committed.
222
+ *
223
+ * Monotonic: a lower `seq` never regresses a higher one (out-of-order
224
+ * async commits are clamped). LRU-bounded by
225
+ * {@link PERSISTED_SEQ_CONVERSATION_LIMIT}: re-recording refreshes
226
+ * recency, and the oldest conversation is evicted once the cap is
227
+ * exceeded. Non-positive or non-finite `seq` values are ignored.
228
+ */
229
+ export function recordPersistedSeq(conversationId: string, seq: number): void {
230
+ if (!Number.isFinite(seq) || seq <= 0) return;
231
+
232
+ const map = state.persistedSeqByConversation;
233
+ const prev = map.get(conversationId);
234
+ if (prev !== undefined) {
235
+ // Re-insert to move this key to the most-recently-used end.
236
+ map.delete(conversationId);
237
+ map.set(conversationId, Math.max(prev, seq));
238
+ return;
239
+ }
240
+
241
+ map.set(conversationId, seq);
242
+ if (map.size > PERSISTED_SEQ_CONVERSATION_LIMIT) {
243
+ const oldestKey = map.keys().next().value;
244
+ if (oldestKey !== undefined) map.delete(oldestKey);
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Highest `seq` durably persisted for `conversationId`, or `null` when
250
+ * none has been recorded in this process (cold conversation, or evicted
251
+ * from the LRU map). Returned by `/messages` so a client can align the
252
+ * snapshot with the live stream.
180
253
  */
181
- export function clearConversationStream(conversationId: string): void {
182
- streams.delete(conversationId);
254
+ export function getPersistedSeq(conversationId: string): number | null {
255
+ return state.persistedSeqByConversation.get(conversationId) ?? null;
183
256
  }
184
257
 
185
258
  /**
186
259
  * Reset all stream state. Test-only.
187
260
  */
188
- export function _resetConversationStreamsForTesting(): void {
189
- streams.clear();
261
+ export function _resetStreamStateForTesting(): void {
262
+ state.nextSeq = 1;
263
+ state.ring = [];
264
+ state.totalSizeBytes = 0;
265
+ state.persistedSeqByConversation.clear();
190
266
  }
191
267
 
192
268
  /**
193
269
  * Read-only inspector for tests.
194
270
  */
195
- export function _peekStreamForTesting(conversationId: string): {
271
+ export function _peekStreamForTesting(): {
196
272
  nextSeq: number;
197
273
  ringLength: number;
198
274
  totalSizeBytes: number;
199
275
  oldestSeq: number | null;
200
276
  newestSeq: number | null;
201
- } | null {
202
- const state = streams.get(conversationId);
203
- if (!state) return null;
277
+ } {
204
278
  return {
205
279
  nextSeq: state.nextSeq,
206
280
  ringLength: state.ring.length,
@@ -276,7 +350,7 @@ function matchesSubscriber(
276
350
  return true;
277
351
  }
278
352
 
279
- function evict(state: ConversationStreamState): void {
353
+ function evict(): void {
280
354
  const now = Date.now();
281
355
  while (state.ring.length > 0) {
282
356
  const head = state.ring[0];
@@ -19,7 +19,12 @@ import { httpError } from "./http-errors.js";
19
19
  import { withErrorHandling } from "./middleware/error-handler.js";
20
20
  import { routeDefinitionsToHTTPRoutes } from "./routes/http-adapter.js";
21
21
  import { ROUTES } from "./routes/index.js";
22
- import type { RouteLoggingConfig, RoutePathParam } from "./routes/types.js";
22
+ import type {
23
+ RouteLoggingConfig,
24
+ RoutePathParam,
25
+ RouteRequestBody,
26
+ RouteResponseBody,
27
+ } from "./routes/types.js";
23
28
 
24
29
  // ---------------------------------------------------------------------------
25
30
  // Route definition types
@@ -64,17 +69,6 @@ export interface RouteAdditionalResponse {
64
69
  schema?: RouteBodySchema | Record<string, unknown>;
65
70
  }
66
71
 
67
- /**
68
- * Request-body variant keyed by Content-Type. Use this when an endpoint
69
- * accepts multiple body shapes (e.g. `application/octet-stream` OR
70
- * `application/json`). For the common single-JSON case, use `requestBody`.
71
- */
72
- export interface RouteRequestBodyVariant {
73
- contentType: string;
74
- /** Zod schema or plain JSON Schema fragment. Plain objects are embedded verbatim. */
75
- schema: RouteBodySchema | Record<string, unknown>;
76
- }
77
-
78
72
  /**
79
73
  * A single route entry in the declarative table.
80
74
  *
@@ -110,17 +104,18 @@ export interface HTTPRouteDefinition {
110
104
  tags?: string[];
111
105
  /** Query parameter definitions for the operation. */
112
106
  queryParams?: RouteQueryParam[];
113
- /** Zod schema for the request body (POST/PUT/PATCH/DELETE). */
114
- requestBody?: RouteBodySchema;
115
107
  /**
116
- * Alternate request-body variants keyed by Content-Type. When set,
117
- * overrides `requestBody` in the generated OpenAPI spec use this for
118
- * endpoints that accept multiple body shapes on the same URL (e.g.
119
- * raw bytes OR JSON URL).
108
+ * Request body for POST/PUT/PATCH/DELETE. A bare Zod schema is advertised
109
+ * as `application/json`; use the `{ contentType, schema }` form for non-JSON
110
+ * bodies (e.g. a raw `application/octet-stream` upload).
111
+ */
112
+ requestBody?: RouteRequestBody;
113
+ /**
114
+ * Success response body. A bare Zod schema is advertised as
115
+ * `application/json`; use the `{ contentType, schema }` form for non-JSON
116
+ * responses (e.g. a binary `application/octet-stream` download).
120
117
  */
121
- requestBodies?: RouteRequestBodyVariant[];
122
- /** Zod schema for the success response body. */
123
- responseBody?: RouteBodySchema;
118
+ responseBody?: RouteResponseBody;
124
119
  /**
125
120
  * HTTP status code for the documented success response. Defaults to 200.
126
121
  * Set to "202" for async endpoints that enqueue a job and return
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * Shared types for the runtime HTTP server and its route handlers.
3
3
  */
4
+ import type {
5
+ ConversationMessage,
6
+ ConversationMessageAttachment,
7
+ } from "../api/responses/conversation-message.js";
4
8
  import type { ChannelId, InterfaceId } from "../channels/types.js";
5
9
  import type { LLMCallSite } from "../config/schemas/llm.js";
6
10
  import type { Conversation } from "../daemon/conversation.js";
@@ -137,74 +141,16 @@ export interface RuntimeHttpServerOptions {
137
141
  hostname?: string;
138
142
  }
139
143
 
140
- export interface RuntimeAttachmentMetadata {
141
- id: string;
142
- filename: string;
143
- mimeType: string;
144
- sizeBytes: number;
145
- kind: string;
146
- data?: string;
147
- thumbnailData?: string;
148
- fileBacked?: boolean;
149
- }
144
+ /**
145
+ * Structured attachment metadata returned on a history row. Canonical wire
146
+ * shape lives in `@vellumai/assistant-api`; aliased here so route modules can
147
+ * keep importing the runtime-local name.
148
+ */
149
+ export type RuntimeAttachmentMetadata = ConversationMessageAttachment;
150
150
 
151
- export interface RuntimeMessagePayload {
152
- id: string;
153
- /**
154
- * Server message ids that were folded into this display row when consecutive
155
- * assistant messages were consolidated for history rendering.
156
- */
157
- mergedMessageIds?: string[];
158
- role: string;
159
- timestamp: string;
160
- attachments: RuntimeAttachmentMetadata[];
161
- toolCalls?: Array<{
162
- name: string;
163
- input: Record<string, unknown>;
164
- result?: string;
165
- isError?: boolean;
166
- riskLevel?: string;
167
- riskReason?: string;
168
- autoApproved?: boolean;
169
- approvalMode?: string;
170
- approvalReason?: string;
171
- riskThreshold?: string;
172
- }>;
173
- surfaces?: Array<{
174
- surfaceId: string;
175
- surfaceType: string;
176
- title?: string;
177
- data: Record<string, unknown>;
178
- actions?: unknown[];
179
- display?: string;
180
- }>;
181
- textSegments?: string[];
182
- thinkingSegments?: string[];
183
- contentOrder?: string[];
184
- subagentNotification?: {
185
- subagentId: string;
186
- label: string;
187
- status: string;
188
- error?: string;
189
- conversationId?: string;
190
- objective?: string;
191
- };
192
- slackMessage?: {
193
- channelId: string;
194
- channelName?: string;
195
- channelTs: string;
196
- threadTs?: string;
197
- sender?: {
198
- displayName?: string;
199
- externalUserId?: string;
200
- };
201
- messageLink?: {
202
- appUrl?: string;
203
- webUrl?: string;
204
- };
205
- threadLink?: {
206
- appUrl?: string;
207
- webUrl?: string;
208
- };
209
- };
210
- }
151
+ /**
152
+ * The daemon's history-row payload. Canonical wire contract lives in
153
+ * `@vellumai/assistant-api` (`responses/conversation-message.ts`) so the
154
+ * producer and every consumer (web, CLI, evals) derive from one source.
155
+ */
156
+ export type RuntimeMessagePayload = ConversationMessage;
@@ -38,6 +38,7 @@ export interface ConfirmationDetails {
38
38
  pattern: string;
39
39
  }>;
40
40
  scopeOptions: Array<{ label: string; scope: string }>;
41
+ directoryScopeOptions?: Array<{ label: string; scope: string }>;
41
42
  persistentDecisionsAllowed?: boolean;
42
43
  /** ACP tool kind from the agent (e.g. "read", "edit", "execute"). */
43
44
  acpToolKind?: string;