@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
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Tests for `annotatePersistedAssistantMessage` persisting `_activityMetadata`
3
+ * (web_search / web_fetch) alongside the tool call.
4
+ *
5
+ * Without this annotation the tool activity card (e.g. WebSearchProgressCard)
6
+ * is lost on a history reopen — the snapshot only carries the plain result
7
+ * text. External provider tools (brave/perplexity/tavily, web_fetch) resolve
8
+ * their activity only when the `tool_result` lands, after `message_complete`
9
+ * already persisted the block, so they are stamped here. Native server tools
10
+ * (Anthropic web_search) resolve before `message_complete` and are stamped at
11
+ * persist time in `buildPersistedAssistantContent` (covered separately in
12
+ * build-persisted-content.test.ts).
13
+ *
14
+ * The test exercises the populate → annotate → persist round-trip:
15
+ * handleToolResult(event with activityMetadata)
16
+ * → state.toolActivityMetadata captures it
17
+ * → annotatePersistedAssistantMessage writes _activityMetadata onto the row
18
+ * → updateMessageContent receives the JSON-serialized output
19
+ *
20
+ * Read-side coverage (renderHistoryContent in handlers/shared.ts) lives in
21
+ * server-history-render.test.ts.
22
+ */
23
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
24
+
25
+ // ── Mock platform (must precede imports that read it) ─────────────────────────
26
+ mock.module("../util/logger.js", () => ({
27
+ getLogger: () =>
28
+ new Proxy({} as Record<string, unknown>, {
29
+ get: () => () => {},
30
+ }),
31
+ }));
32
+
33
+ mock.module("../config/loader.js", () => ({
34
+ getConfig: () => ({
35
+ skills: {
36
+ entries: {},
37
+ load: { extraDirs: [], watch: false, watchDebounceMs: 0 },
38
+ install: { nodeManager: "npm" },
39
+ allowBundled: null,
40
+ remoteProviders: {
41
+ skillssh: { enabled: true },
42
+ clawhub: { enabled: true },
43
+ },
44
+ remotePolicy: {
45
+ blockSuspicious: true,
46
+ blockMalware: true,
47
+ maxSkillsShRisk: "medium",
48
+ },
49
+ },
50
+ }),
51
+ loadConfig: () => ({}),
52
+ }));
53
+
54
+ let mockedRowContent = "";
55
+ const updates: Array<{ id: string; content: string }> = [];
56
+
57
+ mock.module("../memory/conversation-crud.js", () => ({
58
+ addMessage: () => ({ id: "mock-msg-id" }),
59
+ getMessageById: (id: string) =>
60
+ mockedRowContent ? { id, content: mockedRowContent } : null,
61
+ updateMessageContent: (id: string, content: string) => {
62
+ updates.push({ id, content });
63
+ },
64
+ provenanceFromTrustContext: () => ({}),
65
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
66
+ }));
67
+
68
+ mock.module("../memory/llm-request-log-store.js", () => ({
69
+ recordRequestLog: () => {},
70
+ backfillMessageIdOnLogs: () => {},
71
+ }));
72
+
73
+ // ── Imports (after mocks) ─────────────────────────────────────────────────────
74
+ import type {
75
+ EventHandlerDeps,
76
+ EventHandlerState,
77
+ } from "../daemon/conversation-agent-loop-handlers.js";
78
+ import {
79
+ createEventHandlerState,
80
+ handleToolResult,
81
+ } from "../daemon/conversation-agent-loop-handlers.js";
82
+ import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
83
+
84
+ // ── Helpers ───────────────────────────────────────────────────────────────────
85
+
86
+ function makeDeps(): EventHandlerDeps {
87
+ return {
88
+ ctx: {
89
+ conversationId: "test-conv",
90
+ provider: { name: "anthropic" },
91
+ traceEmitter: { emit: () => {} },
92
+ streamThinking: false,
93
+ emitActivityState: () => {},
94
+ markWorkspaceTopLevelDirty: () => {},
95
+ currentTurnSurfaces: [],
96
+ } as unknown as EventHandlerDeps["ctx"],
97
+ onEvent: () => {},
98
+ reqId: "test-req",
99
+ isFirstMessage: false,
100
+ shouldGenerateTitle: false,
101
+ rlog: new Proxy({} as Record<string, unknown>, {
102
+ get: () => () => {},
103
+ }) as unknown as EventHandlerDeps["rlog"],
104
+ turnChannelContext: {
105
+ userMessageChannel: "vellum",
106
+ assistantMessageChannel: "vellum",
107
+ } as unknown as EventHandlerDeps["turnChannelContext"],
108
+ turnInterfaceContext: {
109
+ userMessageInterface: "web",
110
+ assistantMessageInterface: "web",
111
+ } as unknown as EventHandlerDeps["turnInterfaceContext"],
112
+ applyCompaction: async () => {},
113
+ };
114
+ }
115
+
116
+ function setupState(toolUseId: string): EventHandlerState {
117
+ const state = createEventHandlerState();
118
+ state.lastAssistantMessageId = "msg-1";
119
+ state.toolUseIdToName.set(toolUseId, "web_search");
120
+ state.toolCallTimestamps.set(toolUseId, { startedAt: Date.now() });
121
+ state.currentTurnToolUseIds.push(toolUseId);
122
+ return state;
123
+ }
124
+
125
+ function findBlockById(
126
+ rawContent: string,
127
+ id: string,
128
+ ): Record<string, unknown> {
129
+ const parsed = JSON.parse(rawContent) as Array<Record<string, unknown>>;
130
+ const block = parsed.find((b) => b.id === id);
131
+ if (!block) throw new Error(`block ${id} not found`);
132
+ return block;
133
+ }
134
+
135
+ const webSearchActivity: ToolActivityMetadata = {
136
+ webSearch: {
137
+ query: "vellum docs",
138
+ provider: "brave",
139
+ resultCount: 2,
140
+ durationMs: 142,
141
+ results: [
142
+ {
143
+ rank: 1,
144
+ title: "Vellum",
145
+ url: "https://vellum.ai",
146
+ domain: "vellum.ai",
147
+ },
148
+ {
149
+ rank: 2,
150
+ title: "Docs",
151
+ url: "https://docs.vellum.ai",
152
+ domain: "docs.vellum.ai",
153
+ },
154
+ ],
155
+ },
156
+ };
157
+
158
+ // ── Tests ─────────────────────────────────────────────────────────────────────
159
+
160
+ describe("annotatePersistedAssistantMessage — activityMetadata", () => {
161
+ beforeEach(() => {
162
+ updates.length = 0;
163
+ mockedRowContent = "";
164
+ });
165
+
166
+ test("persists activityMetadata from the live tool_result event onto the tool_use block", () => {
167
+ // GIVEN a persisted tool_use block for an external web_search tool
168
+ const toolUseId = "tu_web_search";
169
+ const state = setupState(toolUseId);
170
+ mockedRowContent = JSON.stringify([
171
+ {
172
+ type: "tool_use",
173
+ id: toolUseId,
174
+ name: "web_search",
175
+ input: { query: "vellum docs" },
176
+ },
177
+ ]);
178
+
179
+ // WHEN the tool result lands carrying activityMetadata
180
+ handleToolResult(state, makeDeps(), {
181
+ type: "tool_result",
182
+ toolUseId,
183
+ content: "results",
184
+ isError: false,
185
+ activityMetadata: webSearchActivity,
186
+ });
187
+
188
+ // THEN the metadata is stamped on the persisted block verbatim
189
+ expect(updates).toHaveLength(1);
190
+ const block = findBlockById(updates[0].content, toolUseId);
191
+ expect(block._activityMetadata).toEqual(webSearchActivity);
192
+ });
193
+
194
+ test("leaves native server_tool_use blocks untouched (stamped at persist time)", () => {
195
+ // GIVEN an external tool completes (to trigger annotation) AND a native
196
+ // server_tool_use block whose activity was captured at server_tool_complete
197
+ const externalId = "tu_external";
198
+ const nativeId = "srvtu_native_search";
199
+ const state = setupState(externalId);
200
+ state.toolActivityMetadata.set(nativeId, webSearchActivity);
201
+ mockedRowContent = JSON.stringify([
202
+ {
203
+ type: "server_tool_use",
204
+ id: nativeId,
205
+ name: "web_search",
206
+ input: { query: "vellum docs" },
207
+ },
208
+ {
209
+ type: "tool_use",
210
+ id: externalId,
211
+ name: "bash",
212
+ input: { command: "ls" },
213
+ },
214
+ ]);
215
+
216
+ // WHEN the external tool result lands (no activity of its own)
217
+ handleToolResult(state, makeDeps(), {
218
+ type: "tool_result",
219
+ toolUseId: externalId,
220
+ content: "ok",
221
+ isError: false,
222
+ });
223
+
224
+ // THEN the annotate pass does not stamp the server_tool_use block — native
225
+ // activity is stamped earlier by `buildPersistedAssistantContent` (covered
226
+ // in build-persisted-content.test.ts), and the unrelated external tool_use
227
+ // block carries no activity of its own
228
+ expect(updates).toHaveLength(1);
229
+ const nativeBlock = findBlockById(updates[0].content, nativeId);
230
+ expect(nativeBlock._activityMetadata).toBeUndefined();
231
+ const externalBlock = findBlockById(updates[0].content, externalId);
232
+ expect(externalBlock._activityMetadata).toBeUndefined();
233
+ });
234
+
235
+ test("omits activityMetadata when the tool produced none", () => {
236
+ // GIVEN a non-activity tool
237
+ const toolUseId = "tu_plain";
238
+ const state = setupState(toolUseId);
239
+ state.toolUseIdToName.set(toolUseId, "bash");
240
+ mockedRowContent = JSON.stringify([
241
+ {
242
+ type: "tool_use",
243
+ id: toolUseId,
244
+ name: "bash",
245
+ input: { command: "ls" },
246
+ },
247
+ ]);
248
+
249
+ // WHEN the result lands with no activityMetadata
250
+ handleToolResult(state, makeDeps(), {
251
+ type: "tool_result",
252
+ toolUseId,
253
+ content: "ok",
254
+ isError: false,
255
+ });
256
+
257
+ // THEN no _activityMetadata is written
258
+ expect(updates).toHaveLength(1);
259
+ const block = findBlockById(updates[0].content, toolUseId);
260
+ expect(block._activityMetadata).toBeUndefined();
261
+ });
262
+ });
@@ -103,6 +103,7 @@ function makeDeps(): EventHandlerDeps {
103
103
  userMessageInterface: "web",
104
104
  assistantMessageInterface: "web",
105
105
  } as unknown as EventHandlerDeps["turnInterfaceContext"],
106
+ applyCompaction: async () => {},
106
107
  };
107
108
  }
108
109
 
@@ -120,9 +121,7 @@ function findPersistedToolUse(
120
121
  toolUseId: string,
121
122
  ): Record<string, unknown> {
122
123
  const parsed = JSON.parse(rawContent) as Array<Record<string, unknown>>;
123
- const block = parsed.find(
124
- (b) => b.type === "tool_use" && b.id === toolUseId,
125
- );
124
+ const block = parsed.find((b) => b.type === "tool_use" && b.id === toolUseId);
126
125
  if (!block) throw new Error(`tool_use block ${toolUseId} not found`);
127
126
  return block;
128
127
  }
@@ -102,12 +102,13 @@ mock.module("@anthropic-ai/sdk", () => ({
102
102
  }));
103
103
 
104
104
  // Import after mocking
105
+ import { cachedTextBlock } from "../plugins/defaults/memory-v3-shadow/provider-blocks.js";
106
+ import { AnthropicProvider } from "../providers/anthropic/client.js";
105
107
  import {
106
- AnthropicProvider,
107
108
  isPlaceholderSentinelText,
108
109
  PLACEHOLDER_BLOCKS_OMITTED,
109
110
  PLACEHOLDER_EMPTY_TURN,
110
- } from "../providers/anthropic/client.js";
111
+ } from "../providers/placeholder-sentinels.js";
111
112
 
112
113
  // ---------------------------------------------------------------------------
113
114
  // Helpers
@@ -323,6 +324,98 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
323
324
  expect(tools[0].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
324
325
  });
325
326
 
327
+ test("preserves a caller's 1h block cache_control and sends the extended-cache beta (non-Haiku)", async () => {
328
+ // v3's `cachedTextBlock` stamps a stable prefix block with a 1h TTL; the
329
+ // non-Haiku path must forward it unchanged and send the beta header.
330
+ await provider.sendMessage([
331
+ {
332
+ role: "user",
333
+ content: [
334
+ cachedTextBlock("stable pages block"),
335
+ { type: "text", text: "volatile current message" },
336
+ ],
337
+ },
338
+ ]);
339
+
340
+ const messages = lastStreamParams!.messages as Array<{
341
+ content: Array<{ cache_control?: { type: string; ttl?: string } }>;
342
+ }>;
343
+ expect(messages[0].content[0].cache_control).toEqual({
344
+ type: "ephemeral",
345
+ ttl: "1h",
346
+ });
347
+ expect(lastStreamParams!.betas).toContain("extended-cache-ttl-2025-04-11");
348
+ });
349
+
350
+ test("strips ttl from a caller's block cache_control and omits the beta for Haiku", async () => {
351
+ // Haiku doesn't support the extended-cache-ttl beta, so a caller-stamped
352
+ // 1h ttl (e.g. from `cachedTextBlock`) must be stripped before sending.
353
+ const haiku = new AnthropicProvider(
354
+ "sk-ant-test",
355
+ "claude-haiku-4-5-20251001",
356
+ );
357
+ await haiku.sendMessage([
358
+ {
359
+ role: "user",
360
+ content: [
361
+ cachedTextBlock("stable pages block"),
362
+ { type: "text", text: "volatile current message" },
363
+ ],
364
+ },
365
+ ]);
366
+
367
+ const messages = lastStreamParams!.messages as Array<{
368
+ content: Array<{ cache_control?: { type: string; ttl?: string } }>;
369
+ }>;
370
+ expect(messages[0].content[0].cache_control).toEqual({ type: "ephemeral" });
371
+ expect(messages[0].content[0].cache_control).not.toHaveProperty("ttl");
372
+ expect(
373
+ (lastStreamParams!.betas as string[] | undefined) ?? [],
374
+ ).not.toContain("extended-cache-ttl-2025-04-11");
375
+ });
376
+
377
+ test("v3-shape call (system + tools + cached prefix block) stays within the 4-breakpoint cap", async () => {
378
+ // Mirrors a v3 L2 selector call: a system prompt, a forced tool, and a user
379
+ // message whose stable <pages> block carries a cache_control breakpoint
380
+ // followed by the volatile current-message block. The preserved prefix
381
+ // breakpoint plus the client's system/tools/turn-start anchors must total
382
+ // exactly Anthropic's max of 4.
383
+ await provider.sendMessage(
384
+ [
385
+ {
386
+ role: "user",
387
+ content: [
388
+ cachedTextBlock("stable pages block"),
389
+ { type: "text", text: "volatile current message" },
390
+ ],
391
+ },
392
+ ],
393
+ { systemPrompt: "Select relevant pages.", tools: [sampleTools[0]] },
394
+ );
395
+
396
+ let breakpoints = 0;
397
+ const system = lastStreamParams!.system as
398
+ | Array<{ cache_control?: unknown }>
399
+ | undefined;
400
+ for (const b of system ?? []) if (b.cache_control) breakpoints++;
401
+ const tools = lastStreamParams!.tools as
402
+ | Array<{ cache_control?: unknown }>
403
+ | undefined;
404
+ for (const t of tools ?? []) if (t.cache_control) breakpoints++;
405
+ const messages = lastStreamParams!.messages as Array<{
406
+ content: Array<{ cache_control?: { type: string; ttl?: string } }>;
407
+ }>;
408
+ for (const m of messages)
409
+ for (const b of m.content) if (b.cache_control) breakpoints++;
410
+
411
+ expect(breakpoints).toBe(4);
412
+ // The stable pages block specifically must hold the preserved breakpoint.
413
+ expect(messages[0].content[0].cache_control).toEqual({
414
+ type: "ephemeral",
415
+ ttl: "1h",
416
+ });
417
+ });
418
+
326
419
  test("no tools param when tools are omitted", async () => {
327
420
  await provider.sendMessage([userMsg("Hi")]);
328
421
 
@@ -103,7 +103,7 @@ const { ROUTES } = await import("../runtime/routes/host-app-control-routes.js");
103
103
  const { surfaceProxyResolver } =
104
104
  await import("../daemon/conversation-surfaces.js");
105
105
  const { setConversation, clearConversations } =
106
- await import("../daemon/conversation-store.js");
106
+ await import("../daemon/conversation-registry.js");
107
107
  type SurfaceConversationContext =
108
108
  import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
109
109
 
@@ -19,6 +19,7 @@ const ALLOWLIST = new Set([
19
19
  "assistant/src/memory/app-store.ts", // defines getAppsDir
20
20
  "assistant/src/memory/app-git-service.ts", // uses getAppsDir for git repo root, not per-app paths
21
21
  "assistant/src/daemon/app-source-watcher.ts", // uses getAppsDir for recursive fs.watch root, not per-app paths
22
+ "assistant/src/tools/filesystem/write.ts", // uses getAppsDir as an exemption root for the artifact-HTML guard, not per-app paths
22
23
  ]);
23
24
 
24
25
  function isTestFile(filePath: string): boolean {
@@ -119,12 +119,15 @@ mock.module("../permissions/trust-store.js", () => ({
119
119
  // ---------------------------------------------------------------------------
120
120
  let _conversationFactory: (() => Conversation) | undefined;
121
121
 
122
- mock.module("../daemon/conversation-store.js", () => ({
122
+ mock.module("../daemon/conversation-registry.js", () => ({
123
123
  findConversation: () => {
124
124
  // Return the current test session for any conversation ID lookup.
125
125
  if (!_conversationFactory) return undefined;
126
126
  return _conversationFactory();
127
127
  },
128
+ }));
129
+
130
+ mock.module("../daemon/conversation-store.js", () => ({
128
131
  getOrCreateConversation: async () => {
129
132
  if (!_conversationFactory)
130
133
  throw new Error("_conversationFactory not set in test");
@@ -189,6 +189,31 @@ describe("AssistantEventHub — unsubscribe cleanup", () => {
189
189
  expect(s.active).toBe(false);
190
190
  });
191
191
 
192
+ test("assigns a distinct per-connection id to each subscription", () => {
193
+ const hub = new AssistantEventHub();
194
+
195
+ // Two connections sharing one clientId (an old connection and the
196
+ // reconnect that supersedes it) must be distinguishable by connection
197
+ // id so logs can be attributed to a specific connection.
198
+ const first = hub.subscribe({
199
+ type: "client",
200
+ clientId: "client-1",
201
+ interfaceId: "macos",
202
+ capabilities: [],
203
+ callback: () => {},
204
+ });
205
+ const second = hub.subscribe({
206
+ type: "client",
207
+ clientId: "client-1",
208
+ interfaceId: "macos",
209
+ capabilities: [],
210
+ callback: () => {},
211
+ });
212
+
213
+ expect(first.connectionId).not.toBe(second.connectionId);
214
+ expect(second.connectionId).toMatch(/^conn-/);
215
+ });
216
+
192
217
  test("subscriberCount reflects live subscriptions only", () => {
193
218
  const hub = new AssistantEventHub();
194
219
 
@@ -36,6 +36,7 @@ interface ShedReport {
36
36
  client_id: string | null;
37
37
  interface_id: string | null;
38
38
  conversation_key: string | null;
39
+ connection_id: string | null;
39
40
  subscription_age_ms: number;
40
41
  }
41
42
 
@@ -54,6 +55,7 @@ function makeReporterCaptor(): {
54
55
  client_id: inst.clientId,
55
56
  interface_id: inst.interfaceId,
56
57
  conversation_key: inst.conversationKey,
58
+ connection_id: inst.connectionId,
57
59
  subscription_age_ms: Date.now() - inst.subscribedAtMs,
58
60
  });
59
61
  },
@@ -181,6 +183,10 @@ describe("SSE route — backpressure shed observability", () => {
181
183
  expect(reports.length).toBe(1);
182
184
  expect(reports[0]?.client_id).toBe("client-abc");
183
185
  expect(reports[0]?.interface_id).toBe("macos");
186
+ // The hub-assigned per-connection id is threaded into the shed
187
+ // report so a shed can be attributed to a specific connection even
188
+ // when several share a clientId across reconnects.
189
+ expect(reports[0]?.connection_id).toMatch(/^conn-/);
184
190
 
185
191
  ac.abort();
186
192
  });
@@ -195,6 +201,7 @@ describe("buildSseShedSentryContext", () => {
195
201
  interfaceId: "macos",
196
202
  // Channel-backed conversation key embedding a phone number.
197
203
  conversationKey: "asst:self:whatsapp:447123456789",
204
+ connectionId: "conn-1",
198
205
  };
199
206
  const elDelay = { mean_ms: 1.2, p99_ms: 3.4, max_ms: 5.6 };
200
207
 
@@ -224,6 +231,7 @@ describe("buildSseShedSentryContext", () => {
224
231
  heartbeats_sent: 3,
225
232
  client_id: "client-xyz",
226
233
  interface_id: "macos",
234
+ connection_id: "conn-1",
227
235
  event_loop_delay_mean_ms: 1.2,
228
236
  event_loop_delay_p99_ms: 3.4,
229
237
  event_loop_delay_max_ms: 5.6,