@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
@@ -42,6 +42,10 @@ import { getDb } from "../memory/db-connection.js";
42
42
  import { initializeDb } from "../memory/db-init.js";
43
43
  import { messages } from "../memory/schema.js";
44
44
  import { writeSlackMetadata } from "../messaging/providers/slack/message-metadata.js";
45
+ import {
46
+ _resetStreamStateForTesting,
47
+ recordPersistedSeq,
48
+ } from "../runtime/assistant-stream-state.js";
45
49
  import { handleListMessages } from "../runtime/routes/conversation-routes.js";
46
50
  import { BadRequestError } from "../runtime/routes/errors.js";
47
51
 
@@ -107,6 +111,7 @@ interface ListResponse {
107
111
  hasMore?: boolean;
108
112
  oldestTimestamp?: number | null;
109
113
  oldestMessageId?: string | null;
114
+ seq?: number | null;
110
115
  }
111
116
 
112
117
  function callList(query: Record<string, string>): ListResponse {
@@ -118,9 +123,64 @@ function callList(query: Record<string, string>): ListResponse {
118
123
  describe("handleListMessages page=latest", () => {
119
124
  beforeEach(() => {
120
125
  resetTables();
126
+ _resetStreamStateForTesting();
121
127
  mockAssistantName = null;
122
128
  });
123
129
 
130
+ describe("persisted seq", () => {
131
+ test("returns the recorded persisted seq for the conversation", () => {
132
+ /**
133
+ * The snapshot must advertise the `seq` of the last durably-persisted
134
+ * event so a client can align it with the `/events` stream.
135
+ */
136
+
137
+ // GIVEN a conversation with persisted messages
138
+ const conv = createConversation();
139
+ seedMessages(conv.id, 3);
140
+ // AND the daemon has recorded a persisted seq for it
141
+ recordPersistedSeq(conv.id, 42);
142
+
143
+ // WHEN the snapshot is fetched
144
+ const body = callList({ conversationId: conv.id, page: "latest" });
145
+
146
+ // THEN the response carries that seq
147
+ expect(body.seq).toBe(42);
148
+ });
149
+
150
+ test("returns null seq when nothing has been persisted in-process", () => {
151
+ /**
152
+ * A cold conversation (or one aged out / post-restart) reports no seq,
153
+ * signalling the client to cold-start rather than align to a stale
154
+ * position.
155
+ */
156
+
157
+ // GIVEN a conversation with no recorded persisted seq
158
+ const conv = createConversation();
159
+ seedMessages(conv.id, 2);
160
+
161
+ // WHEN the snapshot is fetched
162
+ const body = callList({ conversationId: conv.id, page: "latest" });
163
+
164
+ // THEN seq is null
165
+ expect(body.seq).toBeNull();
166
+ });
167
+
168
+ test("the no-pagination path also returns the persisted seq", () => {
169
+ /** `seq` is present on every resolved-conversation response shape. */
170
+
171
+ // GIVEN a conversation with a recorded persisted seq
172
+ const conv = createConversation();
173
+ seedMessages(conv.id, 2);
174
+ recordPersistedSeq(conv.id, 7);
175
+
176
+ // WHEN fetched with no pagination params
177
+ const body = callList({ conversationId: conv.id });
178
+
179
+ // THEN the seq still rides along
180
+ expect(body.seq).toBe(7);
181
+ });
182
+ });
183
+
124
184
  test("page=latest with no limit returns all messages chronologically", () => {
125
185
  const conv = createConversation();
126
186
  seedMessages(conv.id, 120);
@@ -57,6 +57,8 @@ interface MessagePayload {
57
57
  role: string;
58
58
  toolCalls?: ToolCallPayload[];
59
59
  textSegments?: string[];
60
+ contentOrder?: string[];
61
+ contentBlocks?: Array<{ type: string; [key: string]: unknown }>;
60
62
  }
61
63
 
62
64
  describe("handleListMessages tool_result merging", () => {
@@ -106,6 +108,24 @@ describe("handleListMessages tool_result merging", () => {
106
108
  expect(toolCalls).toHaveLength(1);
107
109
  expect(toolCalls![0].name).toBe("bash");
108
110
  expect(toolCalls![0].result).toBe("file1.txt\nfile2.txt");
111
+
112
+ // The unified contentBlocks projection ships alongside the legacy arrays,
113
+ // in contentOrder order, with the tool_result already paired onto the
114
+ // tool_use block.
115
+ expect(body.messages[1].contentOrder).toEqual(["text:0", "tool:0"]);
116
+ expect(body.messages[1].contentBlocks).toEqual([
117
+ { type: "text", text: "Running command." },
118
+ {
119
+ type: "tool_use",
120
+ toolCall: {
121
+ id: "tu1",
122
+ name: "bash",
123
+ input: { command: "ls" },
124
+ result: "file1.txt\nfile2.txt",
125
+ isError: false,
126
+ },
127
+ },
128
+ ]);
109
129
  });
110
130
 
111
131
  test("merges multiple tool_results into matching tool_uses", async () => {
@@ -6,7 +6,7 @@ mock.module("../util/logger.js", () => ({
6
6
  getLogger: () => makeMockLogger(),
7
7
  }));
8
8
 
9
- import { getDb } from "../memory/db-connection.js";
9
+ import { getDb, getSqlite } from "../memory/db-connection.js";
10
10
  import { initializeDb } from "../memory/db-init.js";
11
11
  import {
12
12
  getUsageDayBuckets,
@@ -456,6 +456,228 @@ describe("getUsageTotals", () => {
456
456
  });
457
457
  });
458
458
 
459
+ describe("usage aggregation schedule filters", () => {
460
+ beforeEach(() => {
461
+ const db = getDb();
462
+ db.run(`DELETE FROM llm_usage_events`);
463
+ db.run(`DELETE FROM cron_runs`);
464
+ db.run(`DELETE FROM cron_jobs`);
465
+ });
466
+
467
+ function insertScheduleJob(id: string, name: string): void {
468
+ const now = Date.UTC(2026, 0, 1);
469
+ getSqlite().run(
470
+ `INSERT INTO cron_jobs (
471
+ id,
472
+ name,
473
+ cron_expression,
474
+ message,
475
+ next_run_at,
476
+ created_by,
477
+ created_at,
478
+ updated_at
479
+ ) VALUES (?, ?, '* * * * *', 'Example scheduled task', ?, 'user', ?, ?)`,
480
+ [id, name, now, now, now],
481
+ );
482
+ }
483
+
484
+ function insertScheduleRun({
485
+ id,
486
+ scheduleId,
487
+ conversationId,
488
+ startedAt,
489
+ finishedAt,
490
+ }: {
491
+ id: string;
492
+ scheduleId: string;
493
+ conversationId: string;
494
+ startedAt: number;
495
+ finishedAt: number | null;
496
+ }): void {
497
+ getSqlite().run(
498
+ `INSERT INTO cron_runs (
499
+ id,
500
+ job_id,
501
+ status,
502
+ started_at,
503
+ finished_at,
504
+ conversation_id,
505
+ created_at
506
+ ) VALUES (?, ?, 'ok', ?, ?, ?, ?)`,
507
+ [id, scheduleId, startedAt, finishedAt, conversationId, startedAt],
508
+ );
509
+ }
510
+
511
+ function seedScheduleUsage(): void {
512
+ insertScheduleJob("schedule-a", "Morning summary");
513
+ insertScheduleJob("schedule-b", "Nightly sync");
514
+ insertScheduleRun({
515
+ id: "run-a-1",
516
+ scheduleId: "schedule-a",
517
+ conversationId: "conv-reused",
518
+ startedAt: 1_000,
519
+ finishedAt: 2_000,
520
+ });
521
+ insertScheduleRun({
522
+ id: "run-b-1",
523
+ scheduleId: "schedule-b",
524
+ conversationId: "conv-reused",
525
+ startedAt: 3_000,
526
+ finishedAt: 3_500,
527
+ });
528
+
529
+ insertEventAt(
530
+ 900,
531
+ { conversationId: "conv-reused", inputTokens: 90 },
532
+ { estimatedCostUsd: 0.09, pricingStatus: "priced" },
533
+ );
534
+ insertEventAt(
535
+ 1_000,
536
+ {
537
+ conversationId: "conv-reused",
538
+ callSite: "mainAgent",
539
+ inputTokens: 100,
540
+ },
541
+ { estimatedCostUsd: 0.1, pricingStatus: "priced" },
542
+ );
543
+ insertEventAt(
544
+ 1_500,
545
+ {
546
+ conversationId: "conv-reused",
547
+ callSite: "mainAgent",
548
+ inputTokens: 200,
549
+ },
550
+ { estimatedCostUsd: 0.2, pricingStatus: "priced" },
551
+ );
552
+ insertEventAt(
553
+ 2_000,
554
+ {
555
+ conversationId: "conv-reused",
556
+ callSite: "mainAgent",
557
+ inputTokens: 300,
558
+ },
559
+ { estimatedCostUsd: 0.3, pricingStatus: "priced" },
560
+ );
561
+ insertEventAt(
562
+ 2_500,
563
+ { conversationId: "conv-reused", inputTokens: 400 },
564
+ { estimatedCostUsd: 0.4, pricingStatus: "priced" },
565
+ );
566
+ insertEventAt(
567
+ 3_200,
568
+ {
569
+ conversationId: "conv-reused",
570
+ provider: "openai",
571
+ inputTokens: 500,
572
+ },
573
+ { estimatedCostUsd: 0.5, pricingStatus: "priced" },
574
+ );
575
+ insertEventAt(
576
+ 1_500,
577
+ { conversationId: "conv-other", inputTokens: 800 },
578
+ { estimatedCostUsd: 0.8, pricingStatus: "priced" },
579
+ );
580
+ }
581
+
582
+ test("filters totals, buckets, breakdowns, and series by cron run windows", () => {
583
+ seedScheduleUsage();
584
+ const range = { from: 0, to: 4_000 };
585
+ const filter = { scheduleId: "schedule-a" };
586
+
587
+ const totals = getUsageTotals(range, filter);
588
+ expect(totals.eventCount).toBe(3);
589
+ expect(totals.totalInputTokens).toBe(600);
590
+ expect(totals.totalEstimatedCostUsd).toBeCloseTo(0.6);
591
+
592
+ const dailyBuckets = getUsageDayBuckets(range, "UTC", {}, filter);
593
+ expect(dailyBuckets).toHaveLength(1);
594
+ expect(dailyBuckets[0].totalInputTokens).toBe(600);
595
+ expect(dailyBuckets[0].eventCount).toBe(3);
596
+
597
+ const hourlyBuckets = getUsageHourBuckets(range, "UTC", {}, filter);
598
+ expect(hourlyBuckets).toHaveLength(1);
599
+ expect(hourlyBuckets[0].totalInputTokens).toBe(600);
600
+ expect(hourlyBuckets[0].eventCount).toBe(3);
601
+
602
+ const breakdown = getUsageGroupBreakdown(range, "provider", filter);
603
+ expect(breakdown).toHaveLength(1);
604
+ expect(breakdown[0].group).toBe("anthropic");
605
+ expect(breakdown[0].totalInputTokens).toBe(600);
606
+ expect(breakdown[0].eventCount).toBe(3);
607
+
608
+ const series = getUsageGroupedSeries(
609
+ range,
610
+ "call_site",
611
+ "daily",
612
+ "UTC",
613
+ {},
614
+ filter,
615
+ );
616
+ expect(series).toHaveLength(1);
617
+ expect(series[0].totalInputTokens).toBe(600);
618
+ expect(series[0].groups["null:call_site"]).toBeUndefined();
619
+ expect(series[0].groups["null:schedule"]).toBeUndefined();
620
+ expect(series[0].groups["value:mainAgent"]).toMatchObject({
621
+ group: "Main Agent",
622
+ totalInputTokens: 600,
623
+ eventCount: 3,
624
+ });
625
+ });
626
+
627
+ test("groups schedule-attributed usage by schedule id with schedule names", () => {
628
+ seedScheduleUsage();
629
+ const range = { from: 0, to: 4_000 };
630
+
631
+ const breakdown = getUsageGroupBreakdown(range, "schedule");
632
+ const scheduleA = breakdown.find((row) => row.groupKey === "schedule-a");
633
+ const scheduleB = breakdown.find((row) => row.groupKey === "schedule-b");
634
+ const other = breakdown.find((row) => row.groupKey === null);
635
+
636
+ expect(scheduleA).toMatchObject({
637
+ group: "Morning summary",
638
+ groupId: "schedule-a",
639
+ groupKey: "schedule-a",
640
+ totalInputTokens: 600,
641
+ eventCount: 3,
642
+ });
643
+ expect(scheduleB).toMatchObject({
644
+ group: "Nightly sync",
645
+ groupId: "schedule-b",
646
+ groupKey: "schedule-b",
647
+ totalInputTokens: 500,
648
+ eventCount: 1,
649
+ });
650
+ expect(other).toMatchObject({
651
+ group: "Other",
652
+ groupId: null,
653
+ groupKey: null,
654
+ totalInputTokens: 1_290,
655
+ eventCount: 3,
656
+ });
657
+
658
+ const series = getUsageGroupedSeries(range, "schedule", "daily", "UTC", {});
659
+ expect(series).toHaveLength(1);
660
+ expect(series[0].groups["value:schedule-a"]).toMatchObject({
661
+ group: "Morning summary",
662
+ groupKey: "schedule-a",
663
+ totalInputTokens: 600,
664
+ eventCount: 3,
665
+ });
666
+ expect(series[0].groups["value:schedule-b"]).toMatchObject({
667
+ group: "Nightly sync",
668
+ groupKey: "schedule-b",
669
+ totalInputTokens: 500,
670
+ eventCount: 1,
671
+ });
672
+ expect(series[0].groups["null:schedule"]).toMatchObject({
673
+ group: "Other",
674
+ groupKey: null,
675
+ totalInputTokens: 1_290,
676
+ eventCount: 3,
677
+ });
678
+ });
679
+ });
680
+
459
681
  describe("getUsageDayBuckets", () => {
460
682
  beforeEach(() => {
461
683
  const db = getDb();
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Tests for the default `user-prompt-submit-temp` hook (memory retrieval).
3
+ *
4
+ * Covers the retrieval behavior, the side effects the hook owns (injected-block
5
+ * metadata, recall log, `memory_recalled` event), trust gating, error
6
+ * propagation, and abort-signal forwarding. Uses `mock.module` to stub the
7
+ * persistence helpers so the test doesn't touch the developer's real
8
+ * `~/.vellum` or database. The memory graph handle is a hand-rolled fake
9
+ * passed on the hook context — the hook only needs `prepareMemory`.
10
+ */
11
+
12
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
13
+
14
+ // Stub the persistence helpers BEFORE importing the module under test so the
15
+ // bindings resolve through the mocks.
16
+ const updateMessageMetadataMock = mock((_id: string, _updates: unknown) => {});
17
+ mock.module("../memory/conversation-crud.js", () => ({
18
+ updateMessageMetadata: updateMessageMetadataMock,
19
+ }));
20
+
21
+ const recordMemoryRecallLogMock = mock((_entry: unknown) => {});
22
+ mock.module("../memory/memory-recall-log-store.js", () => ({
23
+ recordMemoryRecallLog: recordMemoryRecallLogMock,
24
+ }));
25
+
26
+ import type { AssistantConfig } from "../config/schema.js";
27
+ import type { ServerMessage } from "../daemon/message-protocol.js";
28
+ import type { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
29
+ import type { QdrantSparseVector } from "../memory/qdrant-client.js";
30
+ import userPromptSubmitMemoryRetrieval, {
31
+ type MemoryRetrievalHookContext,
32
+ } from "../plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.js";
33
+ import type { Message } from "../providers/types.js";
34
+
35
+ /** Canonical metrics payload the graph retriever attaches to a real hit. */
36
+ function makeMetrics() {
37
+ return {
38
+ embeddingProvider: "openai",
39
+ embeddingModel: "text-embedding-3-small",
40
+ semanticHits: 2,
41
+ mergedCount: 3,
42
+ selectedCount: 1,
43
+ tier1Count: 1,
44
+ tier2Count: 0,
45
+ hybridSearchLatencyMs: 5,
46
+ sparseVectorUsed: true,
47
+ topCandidates: [
48
+ {
49
+ nodeId: "node-1",
50
+ type: "fact",
51
+ score: 0.9,
52
+ semanticSimilarity: 0.8,
53
+ recencyBoost: 0.1,
54
+ },
55
+ ],
56
+ queryContext: "query-context",
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Fake graph-memory whose `prepareMemory` returns a canonical result. The hook
62
+ * unpacks this return value onto `ctx.latestMessages` and records the selected
63
+ * PKB query pair back onto the handle via `recordPkbQueryVectors`, so tests
64
+ * can assert those outputs by comparing object identity.
65
+ */
66
+ function makeFakeGraphMemory(overrides?: {
67
+ messages?: Message[];
68
+ injectedTokens?: number;
69
+ injectedBlockText?: string | null;
70
+ metrics?: ReturnType<typeof makeMetrics> | null;
71
+ queryVector?: number[];
72
+ sparseVector?: QdrantSparseVector;
73
+ userQueryVector?: number[];
74
+ userQuerySparseVector?: QdrantSparseVector;
75
+ }): {
76
+ memory: ConversationGraphMemory;
77
+ prepareMemoryMock: ReturnType<typeof mock>;
78
+ recordPkbQueryVectorsMock: ReturnType<typeof mock>;
79
+ } {
80
+ const returnValue = {
81
+ runMessages: overrides?.messages ?? [],
82
+ injectedTokens: overrides?.injectedTokens ?? 0,
83
+ latencyMs: 0,
84
+ mode: "none" as const,
85
+ injectedBlockText:
86
+ overrides?.injectedBlockText === undefined
87
+ ? null
88
+ : overrides.injectedBlockText,
89
+ metrics: overrides?.metrics ?? null,
90
+ queryVector: overrides?.queryVector,
91
+ sparseVector: overrides?.sparseVector,
92
+ userQueryVector: overrides?.userQueryVector,
93
+ userQuerySparseVector: overrides?.userQuerySparseVector,
94
+ };
95
+ const prepareMemoryMock = mock(async () => returnValue);
96
+ const recordPkbQueryVectorsMock = mock(() => {});
97
+ const memory = {
98
+ prepareMemory: prepareMemoryMock,
99
+ recordPkbQueryVectors: recordPkbQueryVectorsMock,
100
+ } as unknown as ConversationGraphMemory;
101
+ return { memory, prepareMemoryMock, recordPkbQueryVectorsMock };
102
+ }
103
+
104
+ function makeHookCtx(
105
+ overrides: Partial<MemoryRetrievalHookContext> = {},
106
+ ): MemoryRetrievalHookContext {
107
+ const { memory } = makeFakeGraphMemory();
108
+ return {
109
+ graphMemory: memory,
110
+ config: {} as AssistantConfig,
111
+ onEvent: () => {},
112
+ isTrustedActor: true,
113
+ conversationId: "conv-test",
114
+ userMessageId: "msg-test",
115
+ logger: {
116
+ warn: () => {},
117
+ } as unknown as MemoryRetrievalHookContext["logger"],
118
+ signal: new AbortController().signal,
119
+ latestMessages: [],
120
+ ...overrides,
121
+ };
122
+ }
123
+
124
+ beforeEach(() => {
125
+ updateMessageMetadataMock.mockReset();
126
+ recordMemoryRecallLogMock.mockReset();
127
+ });
128
+
129
+ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
130
+ test("adopts the injected run messages when the actor is trusted", async () => {
131
+ const injected: Message[] = [
132
+ { role: "user", content: [{ type: "text", text: "injected" }] },
133
+ ];
134
+ const { memory, prepareMemoryMock } = makeFakeGraphMemory({
135
+ messages: injected,
136
+ });
137
+ const ctx = makeHookCtx({ graphMemory: memory, isTrustedActor: true });
138
+
139
+ await userPromptSubmitMemoryRetrieval(ctx);
140
+
141
+ expect(prepareMemoryMock).toHaveBeenCalledTimes(1);
142
+ // The hook adopts the retriever's injected message array verbatim —
143
+ // consumers in the agent loop rely on that identity.
144
+ expect(ctx.latestMessages).toBe(injected);
145
+ });
146
+
147
+ test("selects the user-query dense/sparse pair when present, else the summary pair", async () => {
148
+ const userDense = [1, 1, 1];
149
+ const userSparse: QdrantSparseVector = { indices: [0], values: [1] };
150
+ const summaryDense = [2, 2, 2];
151
+ const summarySparse: QdrantSparseVector = { indices: [1], values: [2] };
152
+
153
+ const withUserQuery = makeFakeGraphMemory({
154
+ queryVector: summaryDense,
155
+ sparseVector: summarySparse,
156
+ userQueryVector: userDense,
157
+ userQuerySparseVector: userSparse,
158
+ });
159
+ const userCtx = makeHookCtx({ graphMemory: withUserQuery.memory });
160
+ await userPromptSubmitMemoryRetrieval(userCtx);
161
+ // User-query pair wins — never crossed with the summary signal — and is
162
+ // recorded back onto the graph handle for the PKB-reminder injector.
163
+ expect(withUserQuery.recordPkbQueryVectorsMock).toHaveBeenCalledWith(
164
+ userDense,
165
+ userSparse,
166
+ );
167
+
168
+ const summaryOnly = makeFakeGraphMemory({
169
+ queryVector: summaryDense,
170
+ sparseVector: summarySparse,
171
+ });
172
+ const summaryCtx = makeHookCtx({ graphMemory: summaryOnly.memory });
173
+ await userPromptSubmitMemoryRetrieval(summaryCtx);
174
+ expect(summaryOnly.recordPkbQueryVectorsMock).toHaveBeenCalledWith(
175
+ summaryDense,
176
+ summarySparse,
177
+ );
178
+ });
179
+
180
+ test("skips graph retrieval and side effects for untrusted actors", async () => {
181
+ const { memory, prepareMemoryMock, recordPkbQueryVectorsMock } =
182
+ makeFakeGraphMemory();
183
+ const seeded: Message[] = [
184
+ { role: "user", content: [{ type: "text", text: "seeded" }] },
185
+ ];
186
+ const ctx = makeHookCtx({
187
+ graphMemory: memory,
188
+ isTrustedActor: false,
189
+ latestMessages: seeded,
190
+ });
191
+
192
+ await userPromptSubmitMemoryRetrieval(ctx);
193
+
194
+ expect(prepareMemoryMock).not.toHaveBeenCalled();
195
+ // No graph retrieval ran: the working array stays the seeded input and no
196
+ // PKB query pair is recorded onto the graph handle.
197
+ expect(ctx.latestMessages).toBe(seeded);
198
+ expect(recordPkbQueryVectorsMock).not.toHaveBeenCalled();
199
+ expect(recordMemoryRecallLogMock).not.toHaveBeenCalled();
200
+ expect(updateMessageMetadataMock).not.toHaveBeenCalled();
201
+ });
202
+
203
+ test("persists injected block, recall log, and emits memory_recalled", async () => {
204
+ const received: ServerMessage[] = [];
205
+ const { memory } = makeFakeGraphMemory({
206
+ injectedBlockText: "injected-block",
207
+ metrics: makeMetrics(),
208
+ });
209
+ const ctx = makeHookCtx({
210
+ graphMemory: memory,
211
+ onEvent: (msg) => received.push(msg),
212
+ userMessageId: "msg-42",
213
+ conversationId: "conv-42",
214
+ });
215
+
216
+ await userPromptSubmitMemoryRetrieval(ctx);
217
+
218
+ expect(updateMessageMetadataMock).toHaveBeenCalledWith("msg-42", {
219
+ memoryInjectedBlock: "injected-block",
220
+ });
221
+ expect(recordMemoryRecallLogMock).toHaveBeenCalledTimes(1);
222
+ const logEntry = recordMemoryRecallLogMock.mock.calls[0]?.[0] as {
223
+ conversationId: string;
224
+ reason: string;
225
+ };
226
+ expect(logEntry.conversationId).toBe("conv-42");
227
+ expect(logEntry.reason).toBe("graph:none");
228
+ expect(received).toHaveLength(1);
229
+ expect(received[0]?.type).toBe("memory_recalled");
230
+ });
231
+
232
+ test("skips metadata persist when no block text is injected", async () => {
233
+ const { memory } = makeFakeGraphMemory({ injectedBlockText: null });
234
+ const ctx = makeHookCtx({ graphMemory: memory });
235
+
236
+ await userPromptSubmitMemoryRetrieval(ctx);
237
+
238
+ expect(updateMessageMetadataMock).not.toHaveBeenCalled();
239
+ // The recall log is still written even without an injected block.
240
+ expect(recordMemoryRecallLogMock).toHaveBeenCalledTimes(1);
241
+ });
242
+
243
+ test("propagates errors from prepareMemory rather than swallowing them", async () => {
244
+ // Memory is critical — failures must surface to the caller (the agent
245
+ // loop) rather than silently degrading to an empty graph result.
246
+ const failingPrepare = mock(
247
+ (
248
+ _msgs: Message[],
249
+ _cfg: AssistantConfig,
250
+ _signal: AbortSignal,
251
+ _onEvent: (msg: ServerMessage) => void,
252
+ ) => Promise.reject(new Error("retrieval failed")),
253
+ );
254
+ const graphMemory = {
255
+ prepareMemory: failingPrepare,
256
+ } as unknown as ConversationGraphMemory;
257
+ const ctx = makeHookCtx({ graphMemory, isTrustedActor: true });
258
+
259
+ await expect(userPromptSubmitMemoryRetrieval(ctx)).rejects.toThrow(
260
+ "retrieval failed",
261
+ );
262
+ });
263
+
264
+ test("forwards the context abort signal into prepareMemory", async () => {
265
+ // The hook hands its `ctx.signal` straight to `prepareMemory` so an
266
+ // external cancel aborts the underlying retrieval.
267
+ let capturedSignal: AbortSignal | undefined;
268
+ const prepareMemoryMock = mock(
269
+ async (
270
+ _msgs: Message[],
271
+ _cfg: AssistantConfig,
272
+ signal: AbortSignal,
273
+ _onEvent: (msg: ServerMessage) => void,
274
+ ) => {
275
+ capturedSignal = signal;
276
+ return {
277
+ runMessages: [],
278
+ injectedTokens: 0,
279
+ latencyMs: 0,
280
+ mode: "none" as const,
281
+ injectedBlockText: null,
282
+ metrics: null,
283
+ };
284
+ },
285
+ );
286
+ const graphMemory = {
287
+ prepareMemory: prepareMemoryMock,
288
+ recordPkbQueryVectors: mock(() => {}),
289
+ } as unknown as ConversationGraphMemory;
290
+ const controller = new AbortController();
291
+ const ctx = makeHookCtx({ graphMemory, signal: controller.signal });
292
+
293
+ await userPromptSubmitMemoryRetrieval(ctx);
294
+
295
+ expect(capturedSignal).toBe(controller.signal);
296
+ });
297
+ });