@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea

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 (570) hide show
  1. package/Dockerfile +20 -4
  2. package/bun.lock +2 -2
  3. package/docker-entrypoint.sh +4 -2
  4. package/docker-init-apt-root.sh +3 -1
  5. package/docker-kata-apt-env.sh +3 -1
  6. package/docker-kata-runtime-family.sh +12 -0
  7. package/docs/architecture/memory.md +1 -1
  8. package/examples/plugins/echo/README.md +61 -66
  9. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  10. package/examples/plugins/echo/hooks/stop.ts +16 -0
  11. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  12. package/examples/plugins/echo/package.json +1 -2
  13. package/examples/plugins/echo/src/emit.ts +19 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  15. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  16. package/openapi.yaml +3378 -335
  17. package/package.json +2 -2
  18. package/scripts/generate-openapi.ts +68 -41
  19. package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  21. package/src/__tests__/agent-loop.test.ts +37 -87
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  23. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  24. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  25. package/src/__tests__/anthropic-provider.test.ts +95 -2
  26. package/src/__tests__/app-control-flow.test.ts +1 -1
  27. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  28. package/src/__tests__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  30. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  31. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  32. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  33. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  34. package/src/__tests__/btw-routes.test.ts +62 -3
  35. package/src/__tests__/build-persisted-content.test.ts +184 -0
  36. package/src/__tests__/catalog-files.test.ts +1 -1
  37. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  38. package/src/__tests__/channel-approvals.test.ts +1 -1
  39. package/src/__tests__/clawhub-files.test.ts +1 -1
  40. package/src/__tests__/compaction-circuit.test.ts +258 -0
  41. package/src/__tests__/compaction-direct.test.ts +132 -0
  42. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  43. package/src/__tests__/config-watcher.test.ts +1 -1
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
  48. package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
  49. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  50. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  51. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  52. package/src/__tests__/conversation-pairing.test.ts +4 -31
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
  55. package/src/__tests__/conversation-queue.test.ts +2 -0
  56. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  57. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  58. package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
  59. package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
  60. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  61. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  62. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  63. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  64. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  65. package/src/__tests__/conversation-title-service.test.ts +135 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
  67. package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  71. package/src/__tests__/db-acp-history.test.ts +101 -0
  72. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  73. package/src/__tests__/dm-persistence.test.ts +5 -1
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +304 -0
  76. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  77. package/src/__tests__/file-write-tool.test.ts +63 -0
  78. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  79. package/src/__tests__/gemini-image-service.test.ts +13 -0
  80. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  81. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  82. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  83. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  84. package/src/__tests__/heartbeat-service.test.ts +1 -0
  85. package/src/__tests__/helpers/mock-provider.ts +110 -0
  86. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  87. package/src/__tests__/history-repair-hook.test.ts +1 -0
  88. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  89. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  90. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  91. package/src/__tests__/identity-routes.test.ts +248 -7
  92. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  93. package/src/__tests__/injector-background-turn.test.ts +3 -9
  94. package/src/__tests__/injector-chain.test.ts +139 -275
  95. package/src/__tests__/injector-disk-pressure.test.ts +75 -41
  96. package/src/__tests__/injector-document-comments.test.ts +3 -3
  97. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  98. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  99. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  100. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  101. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  102. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  103. package/src/__tests__/llm-usage-store.test.ts +223 -1
  104. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  105. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  106. package/src/__tests__/native-web-search.test.ts +191 -0
  107. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  108. package/src/__tests__/openai-image-service.test.ts +17 -0
  109. package/src/__tests__/openai-provider.test.ts +31 -1
  110. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
  111. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  112. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  113. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  114. package/src/__tests__/plugin-api-shim.test.ts +3 -6
  115. package/src/__tests__/plugin-bootstrap.test.ts +14 -40
  116. package/src/__tests__/plugin-registry.test.ts +3 -76
  117. package/src/__tests__/plugin-types.test.ts +0 -193
  118. package/src/__tests__/process-message-display-content.test.ts +6 -2
  119. package/src/__tests__/reaction-persistence.test.ts +1 -1
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  121. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  122. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  123. package/src/__tests__/schedule-routes.test.ts +603 -2
  124. package/src/__tests__/schedule-store.test.ts +41 -0
  125. package/src/__tests__/schedule-tools.test.ts +35 -0
  126. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  127. package/src/__tests__/server-history-render.test.ts +314 -1
  128. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  129. package/src/__tests__/skillssh-files.test.ts +1 -1
  130. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  132. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  133. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  134. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  135. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  136. package/src/__tests__/system-prompt.test.ts +20 -0
  137. package/src/__tests__/task-scheduler.test.ts +162 -1
  138. package/src/__tests__/terminal-tools.test.ts +6 -1
  139. package/src/__tests__/title-generate-hook.test.ts +319 -0
  140. package/src/__tests__/tool-error-hook.test.ts +278 -0
  141. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  142. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  143. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  144. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  145. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  146. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  147. package/src/__tests__/usage-routes.test.ts +285 -1
  148. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  149. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  150. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  151. package/src/acp/__tests__/agent-process.test.ts +161 -0
  152. package/src/acp/__tests__/client-handler.test.ts +40 -0
  153. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  154. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  155. package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
  156. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  157. package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
  158. package/src/acp/agent-process.ts +61 -1
  159. package/src/acp/auto-install.test.ts +196 -0
  160. package/src/acp/auto-install.ts +177 -0
  161. package/src/acp/client-handler.ts +31 -0
  162. package/src/acp/feature-gate.test.ts +48 -0
  163. package/src/acp/feature-gate.ts +34 -0
  164. package/src/acp/prepare-agent-env.ts +83 -29
  165. package/src/acp/resolve-agent.test.ts +320 -7
  166. package/src/acp/resolve-agent.ts +182 -18
  167. package/src/acp/resume-hint.ts +25 -0
  168. package/src/acp/session-manager.ts +495 -73
  169. package/src/acp/types.ts +8 -0
  170. package/src/agent/compaction-circuit.ts +60 -102
  171. package/src/agent/loop.ts +362 -485
  172. package/src/api/events/assistant-thinking-delta.ts +33 -0
  173. package/src/api/events/tool-output-chunk.ts +45 -0
  174. package/src/api/events/tool-use-preview-start.ts +32 -0
  175. package/src/api/events/trace-event.ts +69 -0
  176. package/src/api/index.ts +48 -13
  177. package/src/api/responses/conversation-message.ts +374 -0
  178. package/src/approvals/guardian-request-resolvers.ts +1 -1
  179. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  180. package/src/background-wake/next-wake.ts +1 -0
  181. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  182. package/src/cli/commands/notifications.ts +112 -60
  183. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  184. package/src/config/acp-defaults.test.ts +10 -0
  185. package/src/config/acp-defaults.ts +6 -0
  186. package/src/config/assistant-feature-flags.ts +22 -11
  187. package/src/config/bundled-skills/acp/SKILL.md +83 -31
  188. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  189. package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
  190. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  191. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  192. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  193. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  194. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  195. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  196. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  197. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  198. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  199. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  200. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  201. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  202. package/src/config/bundled-tool-registry.ts +2 -0
  203. package/src/config/feature-flag-cache.ts +3 -3
  204. package/src/config/feature-flag-registry.json +48 -7
  205. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  207. package/src/config/schemas/heartbeat.ts +9 -0
  208. package/src/config/schemas/llm.ts +1 -0
  209. package/src/config/schemas/memory-v2.ts +8 -0
  210. package/src/config/schemas/memory-v3.ts +8 -0
  211. package/src/config/schemas/platform.ts +8 -0
  212. package/src/config/seed-inference-profiles.ts +2 -2
  213. package/src/config/skills.ts +13 -0
  214. package/src/context/compactor.ts +1 -1
  215. package/src/context/strip-injections.ts +128 -0
  216. package/src/context/token-estimator.ts +23 -0
  217. package/src/context/tool-result-truncation.ts +0 -23
  218. package/src/context/window-manager.ts +5 -7
  219. package/src/credential-execution/executable-discovery.ts +16 -0
  220. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  221. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  222. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  223. package/src/daemon/assistant-attachments.ts +1 -1
  224. package/src/daemon/config-watcher.ts +2 -2
  225. package/src/daemon/context-overflow-reducer.ts +0 -1
  226. package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
  227. package/src/daemon/conversation-agent-loop.ts +301 -997
  228. package/src/daemon/conversation-history.ts +5 -4
  229. package/src/daemon/conversation-lifecycle.ts +3 -4
  230. package/src/daemon/conversation-messaging.ts +7 -6
  231. package/src/daemon/conversation-process.ts +11 -16
  232. package/src/daemon/conversation-registry.ts +159 -0
  233. package/src/daemon/conversation-runtime-assembly.ts +218 -398
  234. package/src/daemon/conversation-slash.ts +6 -25
  235. package/src/daemon/conversation-store.ts +9 -90
  236. package/src/daemon/conversation-surfaces.ts +222 -4
  237. package/src/daemon/conversation-tool-setup.ts +2 -29
  238. package/src/daemon/conversation-workspace.ts +17 -0
  239. package/src/daemon/conversation.ts +32 -20
  240. package/src/daemon/external-plugins-bootstrap.ts +17 -18
  241. package/src/daemon/handlers/config-a2a.ts +51 -36
  242. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  243. package/src/daemon/handlers/config-telegram.ts +16 -2
  244. package/src/daemon/handlers/conversations.ts +3 -1
  245. package/src/daemon/handlers/shared.ts +156 -84
  246. package/src/daemon/handlers/skills.ts +42 -10
  247. package/src/daemon/lifecycle.ts +25 -0
  248. package/src/daemon/message-types/apps.ts +1 -29
  249. package/src/daemon/message-types/messages.ts +9 -57
  250. package/src/daemon/message-types/skills.ts +2 -0
  251. package/src/daemon/message-types/surfaces.ts +136 -3
  252. package/src/daemon/now-scratchpad.ts +21 -0
  253. package/src/daemon/orphan-reaper.test.ts +210 -0
  254. package/src/daemon/orphan-reaper.ts +240 -0
  255. package/src/daemon/overflow-reduction-loop.ts +230 -0
  256. package/src/daemon/persist-unsendable-image.ts +117 -0
  257. package/src/daemon/process-message.ts +1 -3
  258. package/src/daemon/server.ts +2 -0
  259. package/src/daemon/trace-emitter.ts +6 -4
  260. package/src/daemon/trust-context.ts +19 -0
  261. package/src/daemon/wake-target-adapter.ts +3 -1
  262. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  263. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  264. package/src/heartbeat/heartbeat-service.ts +26 -0
  265. package/src/home/home-greeting-cache.ts +24 -1
  266. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  267. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  268. package/src/ipc/gateway-client.test.ts +2 -2
  269. package/src/ipc/gateway-client.ts +3 -3
  270. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  271. package/src/ipc/skill-routes/memory.ts +4 -2
  272. package/src/media/gemini-image-service.ts +15 -0
  273. package/src/media/openai-image-service.ts +14 -0
  274. package/src/media/types.ts +34 -0
  275. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  276. package/src/memory/auth-fallback-events-store.ts +94 -0
  277. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  278. package/src/memory/conversation-title-service.ts +65 -41
  279. package/src/memory/db-init.ts +6 -0
  280. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  281. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  282. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  283. package/src/memory/jobs-store.ts +33 -0
  284. package/src/memory/jobs-worker.ts +32 -5
  285. package/src/memory/llm-usage-store.ts +224 -50
  286. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  287. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  288. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  289. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  290. package/src/memory/migrations/index.ts +3 -0
  291. package/src/memory/pkb/autoinject.ts +61 -0
  292. package/src/memory/pkb/context.ts +50 -0
  293. package/src/memory/pkb/types.ts +14 -0
  294. package/src/memory/schedule-attribution-sql.ts +104 -0
  295. package/src/memory/schema/acp.ts +4 -0
  296. package/src/memory/schema/infrastructure.ts +16 -0
  297. package/src/memory/usage-grouped-buckets.ts +6 -1
  298. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
  299. package/src/memory/v2/consolidation-job.ts +14 -5
  300. package/src/notifications/conversation-pairing.ts +8 -15
  301. package/src/notifications/decision-engine.ts +6 -3
  302. package/src/notifications/home-feed-side-effect.ts +12 -1
  303. package/src/permissions/prompter.ts +4 -0
  304. package/src/plugin-api/constants.ts +4 -0
  305. package/src/plugin-api/index.ts +7 -5
  306. package/src/plugin-api/types.ts +151 -1
  307. package/src/plugins/defaults/compaction/compact.ts +59 -0
  308. package/src/plugins/defaults/compaction/package.json +1 -1
  309. package/src/plugins/defaults/compaction/register.ts +8 -19
  310. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  311. package/src/plugins/defaults/empty-response/register.ts +8 -13
  312. package/src/plugins/defaults/index.ts +2 -18
  313. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  314. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  315. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  316. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
  317. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  318. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
  319. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  321. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
  322. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  323. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  324. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  325. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
  326. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
  328. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
  329. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
  330. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
  331. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  332. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  333. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  334. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  335. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  336. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
  337. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
  338. package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
  339. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  340. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  341. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
  342. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  343. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  344. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
  345. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  346. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
  347. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
  348. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  349. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
  350. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  351. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  352. package/src/plugins/defaults/title-generate/package.json +1 -1
  353. package/src/plugins/defaults/title-generate/register.ts +18 -18
  354. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  355. package/src/plugins/defaults/tool-error/package.json +1 -1
  356. package/src/plugins/defaults/tool-error/register.ts +9 -21
  357. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  358. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  359. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  360. package/src/plugins/external-api.ts +2 -2
  361. package/src/plugins/pipeline.ts +6 -305
  362. package/src/plugins/registry.ts +10 -55
  363. package/src/plugins/types.ts +62 -797
  364. package/src/plugins/user-loader.ts +30 -127
  365. package/src/proactive-artifact/aux-message-injector.ts +4 -4
  366. package/src/proactive-artifact/job.test.ts +8 -13
  367. package/src/prompts/__tests__/system-prompt.test.ts +42 -0
  368. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  369. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  370. package/src/prompts/templates/system-sections.ts +15 -0
  371. package/src/providers/anthropic/client.ts +37 -29
  372. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  373. package/src/providers/openai/chat-completions-provider.ts +44 -0
  374. package/src/providers/openrouter/client.ts +1 -0
  375. package/src/providers/placeholder-sentinels.ts +35 -0
  376. package/src/runtime/__tests__/agent-wake.test.ts +10 -6
  377. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  378. package/src/runtime/agent-wake.ts +2 -5
  379. package/src/runtime/assistant-event-hub.ts +37 -7
  380. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  381. package/src/runtime/channel-approvals.ts +1 -1
  382. package/src/runtime/http-router.ts +16 -21
  383. package/src/runtime/http-types.ts +16 -70
  384. package/src/runtime/interactive-ui.ts +1 -1
  385. package/src/runtime/pending-interactions.ts +1 -0
  386. package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
  387. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  388. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  389. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  390. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  391. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  392. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  393. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  394. package/src/runtime/routes/acp-routes.test.ts +89 -25
  395. package/src/runtime/routes/acp-routes.ts +81 -29
  396. package/src/runtime/routes/app-management-routes.ts +6 -117
  397. package/src/runtime/routes/app-routes.ts +13 -15
  398. package/src/runtime/routes/approval-routes.ts +1 -1
  399. package/src/runtime/routes/attachment-routes.ts +26 -15
  400. package/src/runtime/routes/avatar-routes.ts +26 -0
  401. package/src/runtime/routes/browser-routes.ts +1 -1
  402. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  403. package/src/runtime/routes/btw-routes.ts +29 -23
  404. package/src/runtime/routes/consolidation-routes.ts +120 -20
  405. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  406. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  407. package/src/runtime/routes/conversation-query-routes.ts +3 -1
  408. package/src/runtime/routes/conversation-routes.ts +372 -185
  409. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  410. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  411. package/src/runtime/routes/documents-routes.ts +4 -0
  412. package/src/runtime/routes/domain-routes.ts +51 -37
  413. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  414. package/src/runtime/routes/events-routes.ts +28 -34
  415. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  416. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  417. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  418. package/src/runtime/routes/host-cu-routes.ts +1 -1
  419. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  420. package/src/runtime/routes/identity-routes.ts +224 -18
  421. package/src/runtime/routes/image-generation-routes.ts +40 -2
  422. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  423. package/src/runtime/routes/index.ts +2 -0
  424. package/src/runtime/routes/integrations/a2a.ts +12 -10
  425. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  426. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  427. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  428. package/src/runtime/routes/integrations/telegram.ts +6 -0
  429. package/src/runtime/routes/integrations/twilio.ts +42 -0
  430. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  431. package/src/runtime/routes/log-export-routes.ts +8 -0
  432. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  433. package/src/runtime/routes/memory-v3-routes.ts +66 -34
  434. package/src/runtime/routes/oauth-apps.ts +66 -12
  435. package/src/runtime/routes/oauth-providers.ts +44 -5
  436. package/src/runtime/routes/platform-routes.ts +81 -5
  437. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  438. package/src/runtime/routes/playground/force-compact.ts +1 -1
  439. package/src/runtime/routes/playground/helpers.ts +1 -1
  440. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  441. package/src/runtime/routes/schedule-routes.ts +152 -42
  442. package/src/runtime/routes/secret-routes.ts +14 -2
  443. package/src/runtime/routes/skills-routes.ts +43 -14
  444. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  445. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  446. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  447. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  448. package/src/runtime/routes/tts-routes.ts +35 -0
  449. package/src/runtime/routes/types.ts +66 -8
  450. package/src/runtime/routes/usage-routes.ts +47 -39
  451. package/src/runtime/routes/webhook-routes.ts +41 -2
  452. package/src/runtime/routes/work-items-routes.ts +2 -4
  453. package/src/runtime/routes/workspace-routes.ts +4 -0
  454. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  455. package/src/runtime/services/analyze-conversation.ts +2 -2
  456. package/src/runtime/services/conversation-serializer.ts +1 -1
  457. package/src/schedule/schedule-store.ts +20 -1
  458. package/src/schedule/schedule-usage-store.ts +83 -0
  459. package/src/schedule/scheduler.ts +12 -5
  460. package/src/signals/cancel.ts +2 -4
  461. package/src/skills/catalog-files.ts +2 -2
  462. package/src/skills/catalog-install.ts +3 -0
  463. package/src/skills/categories-cache.ts +118 -0
  464. package/src/skills/clawhub-files.ts +1 -2
  465. package/src/skills/skillssh-files.ts +1 -2
  466. package/src/subagent/manager.ts +17 -5
  467. package/src/telemetry/types.ts +29 -1
  468. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  469. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  470. package/src/tools/acp/context.ts +20 -0
  471. package/src/tools/acp/list-agents.test.ts +7 -1
  472. package/src/tools/acp/spawn.test.ts +158 -55
  473. package/src/tools/acp/spawn.ts +47 -72
  474. package/src/tools/acp/steer.test.ts +105 -8
  475. package/src/tools/acp/steer.ts +48 -17
  476. package/src/tools/apps/executors.ts +13 -8
  477. package/src/tools/executor.ts +1 -53
  478. package/src/tools/filesystem/write.ts +34 -0
  479. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  480. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  481. package/src/tools/network/web-search-error.test.ts +248 -0
  482. package/src/tools/network/web-search-error.ts +267 -0
  483. package/src/tools/network/web-search.ts +207 -48
  484. package/src/tools/schedule/create.ts +2 -0
  485. package/src/tools/subagent/spawn.ts +2 -4
  486. package/src/tools/terminal/safe-env.ts +10 -1
  487. package/src/tools/ui-surface/definitions.ts +34 -5
  488. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  489. package/src/tts/provider-catalog.ts +76 -1
  490. package/src/util/mutex.ts +47 -0
  491. package/src/workspace/git-service.ts +1 -42
  492. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  493. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  494. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  495. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  496. package/src/workspace/migrations/registry.ts +6 -0
  497. package/docs/plugins.md +0 -836
  498. package/examples/plugins/echo/register.ts +0 -184
  499. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  500. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  501. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  502. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  503. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  504. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  505. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  506. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  507. package/src/__tests__/pipeline-runner.test.ts +0 -564
  508. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  509. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  510. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  511. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  512. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  513. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  514. package/src/gallery/default-gallery.ts +0 -1359
  515. package/src/gallery/gallery-manifest.ts +0 -28
  516. package/src/home/feature-gate.ts +0 -22
  517. package/src/memory/v3/provider-blocks.ts +0 -16
  518. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  519. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  520. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  521. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  522. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  523. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  524. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  525. package/src/plugins/defaults/injectors/package.json +0 -15
  526. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  527. package/src/plugins/defaults/llm-call/register.ts +0 -45
  528. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  529. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  530. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  531. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  532. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  533. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  534. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  535. package/src/plugins/defaults/persistence/package.json +0 -15
  536. package/src/plugins/defaults/persistence/register.ts +0 -38
  537. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  538. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  539. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  540. package/src/plugins/defaults/token-estimate/package.json +0 -15
  541. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  542. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  543. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  544. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  545. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  546. package/src/plugins/defaults/tool-execute/package.json +0 -15
  547. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  548. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  549. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  550. package/src/skills/category-inference.ts +0 -111
  551. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  552. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  553. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  554. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  555. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  556. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  557. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  558. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
  559. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  560. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  561. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  562. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  563. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  564. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  565. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  566. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  567. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  568. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  569. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  570. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -11,6 +11,7 @@ import { z } from "zod";
11
11
  import {
12
12
  checkpointKey,
13
13
  CK_ITEM_COUNT,
14
+ CK_LAST_ATTEMPT_AT,
14
15
  CK_LAST_GEN_AT,
15
16
  countActiveMemoryNodes,
16
17
  getCheckpointValue,
@@ -42,10 +43,11 @@ const starterItemSchema = z.object({
42
43
 
43
44
  type StarterItem = z.infer<typeof starterItemSchema>;
44
45
 
45
- export const CONVERSATION_STARTERS_STALE_TTL_MS = 24 * 60 * 60 * 1000;
46
+ export const CONVERSATION_STARTERS_STALE_TTL_MS = 4 * 60 * 60 * 1000;
46
47
 
47
- /** Minimum interval between re-enqueue attempts triggered by invalid items. */
48
- const REFRESH_COOLDOWN_MS = 60_000;
48
+ /** Minimum interval between re-enqueue attempts (prevents tight retry loops
49
+ * when generation repeatedly fails or produces 0 valid starters). */
50
+ const REFRESH_COOLDOWN_MS = 5 * 60 * 1000;
49
51
 
50
52
  function hasActiveConversationStarterJob(
51
53
  db: ReturnType<typeof getDb>,
@@ -199,12 +201,16 @@ function handleListConversationStarters({
199
201
  Date.now() - lastGenAt >= CONVERSATION_STARTERS_STALE_TTL_MS;
200
202
  const checkpointAhead = lastCount != null && totalActive < lastCount;
201
203
  let hasActiveJob = hasActiveConversationStarterJob(db, scopeId);
204
+ const lastAttemptAt = parseCheckpointInt(
205
+ getCheckpointValue(checkpointKey(CK_LAST_ATTEMPT_AT, scopeId)),
206
+ );
202
207
  const withinCooldown =
203
- lastGenAt != null && Date.now() - lastGenAt < REFRESH_COOLDOWN_MS;
208
+ lastAttemptAt != null && Date.now() - lastAttemptAt < REFRESH_COOLDOWN_MS;
204
209
  const shouldRefresh =
205
- staleByAge ||
206
- checkpointAhead ||
207
- (invalidItemCount > 0 && totalActive > 0 && !withinCooldown);
210
+ !withinCooldown &&
211
+ (staleByAge ||
212
+ checkpointAhead ||
213
+ (invalidItemCount > 0 && totalActive > 0));
208
214
 
209
215
  if (shouldRefresh && !hasActiveJob && isMemoryEnabled()) {
210
216
  enqueueMemoryJob("generate_conversation_starters", { scopeId });
@@ -46,7 +46,10 @@ interface ImportPayload {
46
46
 
47
47
  // -- Helpers (ported from CLI) --
48
48
 
49
- function resolveTimestamps(conv: ImportConversation): {
49
+ function resolveTimestamps(
50
+ conv: ImportConversation,
51
+ messages: ImportMessage[],
52
+ ): {
50
53
  convCreatedAt: number;
51
54
  convUpdatedAt: number;
52
55
  messageTimestamps: number[];
@@ -54,13 +57,23 @@ function resolveTimestamps(conv: ImportConversation): {
54
57
  const now = Date.now();
55
58
  const convCreatedAt = conv.createdAt ?? now;
56
59
  const convUpdatedAt = conv.updatedAt ?? conv.createdAt ?? now;
57
- const messageTimestamps = conv.messages.map((msg, i) => {
60
+ const messageTimestamps = messages.map((msg, i) => {
58
61
  if (msg.createdAt != null) return msg.createdAt;
59
62
  return convCreatedAt + i;
60
63
  });
61
64
  return { convCreatedAt, convUpdatedAt, messageTimestamps };
62
65
  }
63
66
 
67
+ /**
68
+ * The messages store is UI-facing (`ConversationMessage`), so only renderable
69
+ * turns are persisted. Non-renderable roles (e.g. agent-context `system`
70
+ * rows an export might carry) are dropped here rather than imported — the
71
+ * import still succeeds with the displayable turns.
72
+ */
73
+ function isRenderableRole(role: string): role is "user" | "assistant" {
74
+ return role === "user" || role === "assistant";
75
+ }
76
+
64
77
  // -- Handler --
65
78
 
66
79
  async function handleConversationsImport({ body }: RouteHandlerArgs) {
@@ -99,12 +112,16 @@ async function handleConversationsImport({ body }: RouteHandlerArgs) {
99
112
  }
100
113
  }
101
114
 
115
+ const messages = conv.messages.filter((msg) =>
116
+ isRenderableRole(msg.role),
117
+ );
118
+
102
119
  const { convCreatedAt, convUpdatedAt, messageTimestamps } =
103
- resolveTimestamps(conv);
120
+ resolveTimestamps(conv, messages);
104
121
 
105
122
  const conversation = createConversation(conv.title);
106
123
 
107
- for (const msg of conv.messages) {
124
+ for (const msg of messages) {
108
125
  const contentStr =
109
126
  typeof msg.content === "string"
110
127
  ? msg.content
@@ -144,8 +161,8 @@ async function handleConversationsImport({ body }: RouteHandlerArgs) {
144
161
  }
145
162
 
146
163
  // Index messages
147
- for (let i = 0; i < dbMessages.length && i < conv.messages.length; i++) {
148
- const msg = conv.messages[i];
164
+ for (let i = 0; i < dbMessages.length && i < messages.length; i++) {
165
+ const msg = messages[i];
149
166
  const contentStr =
150
167
  typeof msg.content === "string"
151
168
  ? msg.content
@@ -176,7 +193,7 @@ async function handleConversationsImport({ body }: RouteHandlerArgs) {
176
193
  }
177
194
 
178
195
  imported++;
179
- totalMessages += conv.messages.length;
196
+ totalMessages += messages.length;
180
197
  } catch (err) {
181
198
  errors.push({
182
199
  index: idx,
@@ -243,6 +243,10 @@ export const ROUTES: RouteDefinition[] = [
243
243
  summary: "Export a document as PDF",
244
244
  description: "Render a document to PDF and return the binary content.",
245
245
  tags: ["documents"],
246
+ responseBody: {
247
+ contentType: "application/pdf",
248
+ schema: { type: "string", format: "binary" },
249
+ },
246
250
  handler: async ({ pathParams }) => {
247
251
  const doc = getDocumentById(pathParams!.id);
248
252
  if (!doc) {
@@ -5,6 +5,8 @@
5
5
  * the subdomain to local config so getAssistantDomain() can use it.
6
6
  */
7
7
 
8
+ import { z } from "zod";
9
+
8
10
  import { getApexDomain } from "../../config/env.js";
9
11
  import {
10
12
  loadRawConfig,
@@ -19,18 +21,36 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
19
21
 
20
22
  const log = getLogger("domain-routes");
21
23
 
22
- // ── Helpers ───────────────────────────────────────────────────────────
24
+ // ── Schemas ───────────────────────────────────────────────────────────
23
25
 
24
- interface DomainListResponse {
25
- next?: string | null;
26
- results: {
27
- id: string;
28
- subdomain?: string;
29
- domain?: string;
30
- created_at?: string;
31
- created?: string;
32
- }[];
33
- }
26
+ const DomainEntrySchema = z.object({
27
+ id: z.string(),
28
+ subdomain: z.string().optional(),
29
+ domain: z.string().optional(),
30
+ created_at: z.string().optional(),
31
+ created: z.string().optional(),
32
+ });
33
+
34
+ const DomainListResponseSchema = z.object({
35
+ count: z.number(),
36
+ next: z.string().nullable().optional(),
37
+ previous: z.string().nullable().optional(),
38
+ results: z.array(DomainEntrySchema),
39
+ });
40
+ type DomainListResponse = z.infer<typeof DomainListResponseSchema>;
41
+
42
+ const DomainRegisterResponseSchema = DomainEntrySchema.extend({
43
+ email_error: z.object({ detail: z.string(), code: z.string() }).optional(),
44
+ });
45
+ type DomainRegisterResponse = z.infer<typeof DomainRegisterResponseSchema>;
46
+
47
+ const DomainVerificationStatusResponseSchema = z.object({
48
+ domain: z.string(),
49
+ status: z.string(),
50
+ message: z.string(),
51
+ });
52
+
53
+ // ── Helpers ───────────────────────────────────────────────────────────
34
54
 
35
55
  async function requireClient(): Promise<VellumPlatformClient> {
36
56
  const client = await VellumPlatformClient.create();
@@ -113,20 +133,11 @@ async function handleDomainRegister({ body = {} }: RouteHandlerArgs) {
113
133
  throw new BadRequestError(String(detail));
114
134
  }
115
135
 
116
- const data = (await response.json()) as {
117
- id: string;
118
- subdomain?: string;
119
- domain?: string;
120
- created_at?: string;
121
- created?: string;
122
- email_error?: { detail: string; code: string };
123
- };
136
+ const data = (await response.json()) as DomainRegisterResponse;
124
137
 
125
138
  // Persist the subdomain to config so getAssistantDomain() can use it
126
139
  const registeredSubdomain =
127
- data.subdomain ??
128
- data.domain?.replace(`.${apexDomain}`, "") ??
129
- subdomain;
140
+ data.subdomain ?? data.domain?.replace(`.${apexDomain}`, "") ?? subdomain;
130
141
  if (registeredSubdomain) {
131
142
  const raw = loadRawConfig();
132
143
  setNestedValue(raw, "platform.subdomain", registeredSubdomain);
@@ -146,12 +157,11 @@ async function handleDomainStatus(_args: RouteHandlerArgs) {
146
157
  // Sync subdomain to config if not already cached
147
158
  if (domains.length > 0) {
148
159
  const first = domains[0];
149
- const sub =
150
- first.subdomain ?? first.domain?.replace(`.${apexDomain}`, "");
160
+ const sub = first.subdomain ?? first.domain?.replace(`.${apexDomain}`, "");
151
161
  if (sub) {
152
162
  const raw = loadRawConfig();
153
- const existing = (raw as Record<string, Record<string, unknown>>)
154
- .platform?.subdomain;
163
+ const existing = (raw as Record<string, Record<string, unknown>>).platform
164
+ ?.subdomain;
155
165
  if (existing !== sub) {
156
166
  setNestedValue(raw, "platform.subdomain", sub);
157
167
  saveRawConfig(raw);
@@ -162,9 +172,7 @@ async function handleDomainStatus(_args: RouteHandlerArgs) {
162
172
  return data;
163
173
  }
164
174
 
165
- async function handleDomainVerificationStatus({
166
- body = {},
167
- }: RouteHandlerArgs) {
175
+ async function handleDomainVerificationStatus({ body = {} }: RouteHandlerArgs) {
168
176
  const { domain_id } = body as { domain_id?: string };
169
177
  if (!domain_id) {
170
178
  throw new BadRequestError("domain_id is required");
@@ -179,9 +187,7 @@ async function handleDomainVerificationStatus({
179
187
  );
180
188
  }
181
189
  if (!results?.some((d) => d.id === domain_id)) {
182
- throw new BadRequestError(
183
- "domain_id is not registered for this assistant",
184
- );
190
+ throw new BadRequestError("domain_id is not registered for this assistant");
185
191
  }
186
192
 
187
193
  const response = await client.fetch(
@@ -201,11 +207,9 @@ async function handleDomainVerificationStatus({
201
207
  );
202
208
  }
203
209
 
204
- return (await response.json()) as {
205
- domain: string;
206
- status: string;
207
- message: string;
208
- };
210
+ return (await response.json()) as z.infer<
211
+ typeof DomainVerificationStatusResponseSchema
212
+ >;
209
213
  }
210
214
 
211
215
  // ── Route definitions ─────────────────────────────────────────────────
@@ -222,6 +226,11 @@ export const ROUTES: RouteDefinition[] = [
222
226
  handler: handleDomainRegister,
223
227
  summary: "Register a subdomain for this assistant",
224
228
  tags: ["domain"],
229
+ requestBody: z.object({
230
+ subdomain: z.string().optional(),
231
+ email_username: z.string().optional(),
232
+ }),
233
+ responseBody: DomainRegisterResponseSchema,
225
234
  },
226
235
  {
227
236
  operationId: "domain_status",
@@ -234,6 +243,7 @@ export const ROUTES: RouteDefinition[] = [
234
243
  handler: handleDomainStatus,
235
244
  summary: "Show domain registration and health",
236
245
  tags: ["domain"],
246
+ responseBody: DomainListResponseSchema,
237
247
  },
238
248
  {
239
249
  operationId: "domain_verification_status",
@@ -246,5 +256,9 @@ export const ROUTES: RouteDefinition[] = [
246
256
  handler: handleDomainVerificationStatus,
247
257
  summary: "Get live DNS verification status for a domain",
248
258
  tags: ["domain"],
259
+ requestBody: z.object({
260
+ domain_id: z.string(),
261
+ }),
262
+ responseBody: DomainVerificationStatusResponseSchema,
249
263
  },
250
264
  ];
@@ -0,0 +1,34 @@
1
+ import { BadRequestError } from "./errors.js";
2
+
3
+ export interface EpochMillisRange {
4
+ from: number;
5
+ to: number;
6
+ }
7
+
8
+ export function parseEpochMillisRange(
9
+ queryParams: Record<string, string>,
10
+ ): EpochMillisRange {
11
+ const fromRaw = queryParams.from;
12
+ const toRaw = queryParams.to;
13
+
14
+ if (!fromRaw || !toRaw) {
15
+ throw new BadRequestError(
16
+ 'Missing required query parameters: "from" and "to" (epoch milliseconds)',
17
+ );
18
+ }
19
+
20
+ const from = Number(fromRaw);
21
+ const to = Number(toRaw);
22
+
23
+ if (!Number.isFinite(from) || !Number.isFinite(to)) {
24
+ throw new BadRequestError(
25
+ '"from" and "to" must be valid numbers (epoch milliseconds)',
26
+ );
27
+ }
28
+
29
+ if (from > to) {
30
+ throw new BadRequestError('"from" must be less than or equal to "to"');
31
+ }
32
+
33
+ return { from, to };
34
+ }
@@ -39,9 +39,9 @@ import {
39
39
  AssistantEventHub,
40
40
  assistantEventHub,
41
41
  } from "../assistant-event-hub.js";
42
+ import type { ReplaySubscriber } from "../assistant-stream-state.js";
43
+ import { getReplayWindow } from "../assistant-stream-state.js";
42
44
  import { ACTOR_PRINCIPALS, GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
43
- import type { ReplaySubscriber } from "../conversation-stream-state.js";
44
- import { getReplayWindow } from "../conversation-stream-state.js";
45
45
  import { resolveActorPrincipalIdForLocalGuardian } from "../local-actor-identity.js";
46
46
  import {
47
47
  BadRequestError,
@@ -143,6 +143,12 @@ export interface SseSubscriberInstrumentation {
143
143
  clientId: string | null;
144
144
  interfaceId: string | null;
145
145
  conversationKey: string | null;
146
+ /**
147
+ * Per-connection id assigned by the hub. Distinguishes connections
148
+ * sharing a `clientId` (old vs reconnected) so a shed can be attributed
149
+ * to a specific connection. `null` until the hub subscription is created.
150
+ */
151
+ connectionId: string | null;
146
152
  }
147
153
 
148
154
  export type SseShedReason = "callback_backpressure" | "heartbeat_backpressure";
@@ -177,6 +183,7 @@ export function buildSseShedSentryContext(
177
183
  heartbeats_sent: inst.heartbeatsSent,
178
184
  client_id: inst.clientId,
179
185
  interface_id: inst.interfaceId,
186
+ connection_id: inst.connectionId,
180
187
  event_loop_delay_mean_ms: elDelay.mean_ms,
181
188
  event_loop_delay_p99_ms: elDelay.p99_ms,
182
189
  event_loop_delay_max_ms: elDelay.max_ms,
@@ -216,6 +223,7 @@ const defaultSseShedReporter: SseShedReporter = (reason, inst) => {
216
223
  scope.setTag("sse_shed_reason", reason);
217
224
  if (inst.clientId) scope.setTag("client_id", inst.clientId);
218
225
  if (inst.interfaceId) scope.setTag("interface_id", inst.interfaceId);
226
+ if (inst.connectionId) scope.setTag("connection_id", inst.connectionId);
219
227
  scope.setContext("sse_shed", sentryContext);
220
228
  Sentry.captureMessage(`sse_subscriber_shed:${reason}`);
221
229
  });
@@ -371,6 +379,7 @@ export function handleSubscribeAssistantEvents(
371
379
  clientId,
372
380
  interfaceId,
373
381
  conversationKey: scopeConversationKey,
382
+ connectionId: null,
374
383
  };
375
384
 
376
385
  ensureEventLoopDelayMonitorStarted();
@@ -394,19 +403,6 @@ export function handleSubscribeAssistantEvents(
394
403
  // replay window we just drained.
395
404
  let highWaterReplaySeq = -1;
396
405
 
397
- // Per-conversation subscriber-filtered sequence counters. Incremented
398
- // for each conversation-scoped event this specific subscriber receives
399
- // (after capability/client/interface targeting), producing a gap-free
400
- // sequence from the subscriber's perspective. Clients use `clientSeq`
401
- // for gap detection instead of the global `seq` to avoid false
402
- // positives from targeted events they never receive.
403
- const clientSeqCounters = new Map<string, number>();
404
- function nextClientSeqFor(conversationId: string): number {
405
- const next = (clientSeqCounters.get(conversationId) ?? 0) + 1;
406
- clientSeqCounters.set(conversationId, next);
407
- return next;
408
- }
409
-
410
406
  const callback: AssistantEventCallback = (event) => {
411
407
  const controller = controllerRef;
412
408
  if (!controller) return;
@@ -425,11 +421,7 @@ export function handleSubscribeAssistantEvents(
425
421
  cleanup();
426
422
  return;
427
423
  }
428
- const frame =
429
- event.conversationId != null && event.seq != null
430
- ? { ...event, clientSeq: nextClientSeqFor(event.conversationId) }
431
- : event;
432
- controller.enqueue(encoder.encode(formatSseFrame(frame)));
424
+ controller.enqueue(encoder.encode(formatSseFrame(event)));
433
425
  instrumentation.eventsDelivered += 1;
434
426
  } catch {
435
427
  sub.dispose();
@@ -461,6 +453,9 @@ export function handleSubscribeAssistantEvents(
461
453
  ...subscriberBase,
462
454
  type: "process" as const,
463
455
  });
456
+ // Stamp the hub-assigned connection id so a later backpressure shed can be
457
+ // tied back to this specific connection in logs and Sentry.
458
+ instrumentation.connectionId = sub.connectionId;
464
459
  } catch (err) {
465
460
  if (err instanceof RangeError) {
466
461
  throw new ServiceUnavailableError("Too many concurrent connections");
@@ -479,16 +474,22 @@ export function handleSubscribeAssistantEvents(
479
474
  return;
480
475
  }
481
476
 
482
- // Reconnect replay: when the caller passed lastSeenSeq and the
483
- // subscription is scoped to a single conversation, deliver any
484
- // buffered events the client missed before the first heartbeat.
477
+ // Reconnect replay: when the caller passed lastSeenSeq, deliver
478
+ // any buffered events the client missed before the first
479
+ // heartbeat. `seq` is a single global per-assistant counter, so
480
+ // one cursor resumes the stream regardless of how many
481
+ // conversations are multiplexed on an unfiltered connection.
482
+ // Replay re-applies the subscriber's targeting filter; a
483
+ // conversation-scoped subscription additionally scopes replay to
484
+ // its own conversation (other conversations are never delivered
485
+ // live on that connection, so replaying them would be wrong).
485
486
  //
486
487
  // If the cursor is older than the ring's oldest entry,
487
488
  // `getReplayWindow` returns `null`. We do not surface that to
488
489
  // the client over the wire -- the connection just goes live.
489
490
  // The client detects the gap from the seq jump on its first
490
491
  // live event and refetches via the existing messages API.
491
- if (lastSeenSeq != null && filter.conversationId) {
492
+ if (lastSeenSeq != null) {
492
493
  const replaySubscriber: ReplaySubscriber =
493
494
  clientId && interfaceId
494
495
  ? {
@@ -501,20 +502,13 @@ export function handleSubscribeAssistantEvents(
501
502
  }
502
503
  : { type: "process" };
503
504
  const window = getReplayWindow(
504
- filter.conversationId,
505
505
  lastSeenSeq,
506
506
  replaySubscriber,
507
+ filter.conversationId,
507
508
  );
508
509
  if (window !== null) {
509
510
  for (const replayed of window) {
510
- const frame =
511
- replayed.conversationId != null && replayed.seq != null
512
- ? {
513
- ...replayed,
514
- clientSeq: nextClientSeqFor(replayed.conversationId),
515
- }
516
- : replayed;
517
- controller.enqueue(encoder.encode(formatSseFrame(frame)));
511
+ controller.enqueue(encoder.encode(formatSseFrame(replayed)));
518
512
  instrumentation.eventsDelivered += 1;
519
513
  if (replayed.seq != null && replayed.seq > highWaterReplaySeq) {
520
514
  highWaterReplaySeq = replayed.seq;
@@ -621,7 +615,7 @@ export const ROUTES: RouteDefinition[] = [
621
615
  {
622
616
  name: "lastSeenSeq",
623
617
  description:
624
- "Optional reconnect cursor: the highest per-conversation event seq the client has already applied. When set together with a conversation scope, the daemon replays any buffered events with seq > lastSeenSeq before going live. If the cursor is older than the ring buffer's oldest entry the connection simply goes live; the client is expected to detect the gap from the next event's seq and refetch via the messages API. Must be a non-negative integer.",
618
+ "Optional reconnect cursor: the highest global event seq the client has already applied. `seq` is a single per-assistant counter shared across all conversations, so one cursor resumes the stream regardless of how many conversations are multiplexed on the connection. When set, the daemon replays any buffered events with seq > lastSeenSeq (re-applying the subscriber's targeting/scope filter) before going live. If the cursor is older than the ring buffer's oldest entry the connection simply goes live; the client is expected to detect the gap from the next event's seq and refetch via the messages API. Must be a non-negative integer.",
625
619
  },
626
620
  ],
627
621
  responseHeaders: {
@@ -35,7 +35,14 @@ async function gatewayFetch(
35
35
 
36
36
  // ── Schemas ─────────────────────────────────────────────────────────────
37
37
 
38
- const LEVEL_NAMES = ["trace", "debug", "info", "warn", "error", "fatal"] as const;
38
+ const LEVEL_NAMES = [
39
+ "trace",
40
+ "debug",
41
+ "info",
42
+ "warn",
43
+ "error",
44
+ "fatal",
45
+ ] as const;
39
46
 
40
47
  const GatewayLogsTailParams = z
41
48
  .object({
@@ -45,9 +52,18 @@ const GatewayLogsTailParams = z
45
52
  })
46
53
  .strict();
47
54
 
55
+ const GatewayLogsTailResponseSchema = z.object({
56
+ lines: z.array(z.record(z.string(), z.unknown())),
57
+ truncated: z.boolean(),
58
+ });
59
+ type GatewayLogsTailResponse = z.infer<typeof GatewayLogsTailResponseSchema>;
60
+
48
61
  // ── Handlers ────────────────────────────────────────────────────────────
49
62
 
50
- async function handleGatewayLogsTail({ queryParams = {}, body = {} }: RouteHandlerArgs) {
63
+ async function handleGatewayLogsTail({
64
+ queryParams = {},
65
+ body = {},
66
+ }: RouteHandlerArgs): Promise<GatewayLogsTailResponse> {
51
67
  // HTTP GET delivers filters via queryParams; CLI IPC puts them in body.
52
68
  const source = Object.keys(queryParams).length > 0 ? queryParams : body;
53
69
  const p = GatewayLogsTailParams.parse(source);
@@ -56,7 +72,9 @@ async function handleGatewayLogsTail({ queryParams = {}, body = {} }: RouteHandl
56
72
  if (p.level !== undefined) qs.set("level", p.level);
57
73
  if (p.module !== undefined) qs.set("module", p.module);
58
74
  const query = qs.toString();
59
- return gatewayFetch(`/v1/logs/tail${query ? `?${query}` : ""}`);
75
+ return gatewayFetch(
76
+ `/v1/logs/tail${query ? `?${query}` : ""}`,
77
+ ) as Promise<GatewayLogsTailResponse>;
60
78
  }
61
79
 
62
80
  // ── Route definitions ───────────────────────────────────────────────────
@@ -75,8 +93,12 @@ export const ROUTES: RouteDefinition[] = [
75
93
  description:
76
94
  "Return the last N structured log entries from the gateway log files.",
77
95
  tags: ["gateway-logs"],
96
+ responseBody: GatewayLogsTailResponseSchema,
78
97
  queryParams: [
79
- { name: "n", description: "Number of lines to return (1–1000, default: 10)" },
98
+ {
99
+ name: "n",
100
+ description: "Number of lines to return (1–1000, default: 10)",
101
+ },
80
102
  { name: "level", description: "Minimum pino level name (default: info)" },
81
103
  { name: "module", description: "Filter to exact pino module name" },
82
104
  ],
@@ -18,6 +18,8 @@ import {
18
18
  } from "../../config/loader.js";
19
19
  import { listHeartbeatRuns } from "../../heartbeat/heartbeat-run-store.js";
20
20
  import { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
21
+ import { getConversation } from "../../memory/conversation-crud.js";
22
+ import { getUsageCostForConversationWindow } from "../../memory/llm-usage-store.js";
21
23
  import { readTextFileSync } from "../../util/fs.js";
22
24
  import { getLogger } from "../../util/logger.js";
23
25
  import { getWorkspacePromptPath } from "../../util/platform.js";
@@ -38,19 +40,34 @@ function handleListRuns(queryParams: Record<string, string>) {
38
40
  : 20;
39
41
 
40
42
  const runs = listHeartbeatRuns(limit);
43
+ const now = Date.now();
41
44
  return {
42
- runs: runs.map((r) => ({
43
- id: r.id,
44
- scheduledFor: r.scheduledFor,
45
- startedAt: r.startedAt,
46
- finishedAt: r.finishedAt,
47
- durationMs: r.durationMs,
48
- status: r.status,
49
- skipReason: r.skipReason,
50
- error: r.error,
51
- conversationId: r.conversationId,
52
- createdAt: r.createdAt,
53
- })),
45
+ runs: runs.map((r) => {
46
+ const conversation = r.conversationId
47
+ ? getConversation(r.conversationId)
48
+ : null;
49
+ return {
50
+ id: r.id,
51
+ scheduledFor: r.scheduledFor,
52
+ startedAt: r.startedAt,
53
+ finishedAt: r.finishedAt,
54
+ durationMs: r.durationMs,
55
+ status: r.status,
56
+ skipReason: r.skipReason,
57
+ error: r.error,
58
+ conversationId: r.conversationId,
59
+ conversationExists: conversation != null,
60
+ conversationArchivedAt: conversation?.archivedAt ?? null,
61
+ estimatedCostUsd: r.conversationId
62
+ ? getUsageCostForConversationWindow({
63
+ conversationId: r.conversationId,
64
+ from: r.startedAt ?? r.scheduledFor,
65
+ to: r.finishedAt ?? now,
66
+ })
67
+ : 0,
68
+ createdAt: r.createdAt,
69
+ };
70
+ }),
54
71
  };
55
72
  }
56
73
 
@@ -116,6 +133,9 @@ export const ROUTES: RouteDefinition[] = [
116
133
  skipReason: z.string().nullable(),
117
134
  error: z.string().nullable(),
118
135
  conversationId: z.string().nullable(),
136
+ conversationExists: z.boolean(),
137
+ conversationArchivedAt: z.number().nullable(),
138
+ estimatedCostUsd: z.number(),
119
139
  createdAt: z.number(),
120
140
  }),
121
141
  )
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { z } from "zod";
16
16
 
17
- import { findConversation } from "../../daemon/conversation-store.js";
17
+ import { findConversation } from "../../daemon/conversation-registry.js";
18
18
  import type {
19
19
  HostAppControlResultPayload,
20
20
  HostAppControlState,
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { z } from "zod";
8
8
 
9
- import { findConversation } from "../../daemon/conversation-store.js";
9
+ import { findConversation } from "../../daemon/conversation-registry.js";
10
10
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
11
11
  import {
12
12
  enforceSameActorOrThrow,