@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
@@ -179,6 +179,47 @@ describe("createSchedule (cron)", () => {
179
179
  expect(retrieved!.cronExpression).toBe("0 * * * *");
180
180
  });
181
181
 
182
+ test("defaults source conversation metadata to null", () => {
183
+ const job = createSchedule({
184
+ name: "No source conversation",
185
+ cronExpression: "0 9 * * *",
186
+ message: "daily check",
187
+ syntax: "cron",
188
+ });
189
+
190
+ expect(job.createdFromConversationId).toBeNull();
191
+ expect(getSchedule(job.id)!.createdFromConversationId).toBeNull();
192
+
193
+ const raw = getRawDb()
194
+ .query("SELECT created_from_conversation_id FROM cron_jobs WHERE id = ?")
195
+ .get(job.id) as { created_from_conversation_id: string | null };
196
+ expect(raw.created_from_conversation_id).toBeNull();
197
+ });
198
+
199
+ test("persists source conversation metadata through create, list, and update", () => {
200
+ const job = createSchedule({
201
+ name: "With source conversation",
202
+ cronExpression: "0 9 * * *",
203
+ message: "daily check",
204
+ syntax: "cron",
205
+ createdFromConversationId: "conv-source",
206
+ });
207
+
208
+ expect(job.createdFromConversationId).toBe("conv-source");
209
+ expect(getSchedule(job.id)!.createdFromConversationId).toBe("conv-source");
210
+ expect(listSchedules()[0].createdFromConversationId).toBe("conv-source");
211
+
212
+ const updated = updateSchedule(job.id, {
213
+ createdFromConversationId: "conv-updated",
214
+ });
215
+ expect(updated!.createdFromConversationId).toBe("conv-updated");
216
+
217
+ const cleared = updateSchedule(job.id, {
218
+ createdFromConversationId: null,
219
+ });
220
+ expect(cleared!.createdFromConversationId).toBeNull();
221
+ });
222
+
182
223
  test("stores schedule_syntax in the DB row", () => {
183
224
  const job = createSchedule({
184
225
  name: "Syntax check",
@@ -66,6 +66,23 @@ describe("schedule_create tool", () => {
66
66
  expect(result.content).toContain("Enabled: true");
67
67
  });
68
68
 
69
+ test("persists the creating conversation for recurring schedules", async () => {
70
+ const result = await executeScheduleCreate(
71
+ {
72
+ name: "Recurring source",
73
+ expression: "0 9 * * *",
74
+ message: "remember the source",
75
+ },
76
+ ctx,
77
+ );
78
+
79
+ expect(result.isError).toBe(false);
80
+ const row = getRawDb()
81
+ .query("SELECT created_from_conversation_id FROM cron_jobs LIMIT 1")
82
+ .get() as { created_from_conversation_id: string | null };
83
+ expect(row.created_from_conversation_id).toBe(ctx.conversationId);
84
+ });
85
+
69
86
  test("creates a disabled schedule", async () => {
70
87
  const result = await executeScheduleCreate(
71
88
  {
@@ -192,6 +209,24 @@ describe("schedule_create with fire_at (one-shot)", () => {
192
209
  expect(result.content).toContain("Status: active");
193
210
  });
194
211
 
212
+ test("persists the creating conversation for one-shot schedules", async () => {
213
+ const futureDate = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
214
+ const result = await executeScheduleCreate(
215
+ {
216
+ name: "One-shot source",
217
+ fire_at: futureDate,
218
+ message: "remember this source too",
219
+ },
220
+ ctx,
221
+ );
222
+
223
+ expect(result.isError).toBe(false);
224
+ const row = getRawDb()
225
+ .query("SELECT created_from_conversation_id FROM cron_jobs LIMIT 1")
226
+ .get() as { created_from_conversation_id: string | null };
227
+ expect(row.created_from_conversation_id).toBe(ctx.conversationId);
228
+ });
229
+
195
230
  test("rejects fire_at that is not valid ISO 8601", async () => {
196
231
  const result = await executeScheduleCreate(
197
232
  {
@@ -91,11 +91,14 @@ mock.module("../config/loader.js", () => ({
91
91
  let _conversationFactory: (() => Conversation) | undefined;
92
92
  let _approvalGenerator: unknown;
93
93
 
94
- mock.module("../daemon/conversation-store.js", () => ({
94
+ mock.module("../daemon/conversation-registry.js", () => ({
95
95
  findConversation: () => {
96
96
  if (!_conversationFactory) return undefined;
97
97
  return _conversationFactory();
98
98
  },
99
+ }));
100
+
101
+ mock.module("../daemon/conversation-store.js", () => ({
99
102
  getOrCreateConversation: async (..._args: unknown[]) => {
100
103
  if (!_conversationFactory)
101
104
  throw new Error("_conversationFactory not set in test");
@@ -8,6 +8,7 @@ mock.module("../util/logger.js", () => ({
8
8
  }));
9
9
 
10
10
  import { renderHistoryContent } from "../daemon/handlers/shared.js";
11
+ import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
11
12
  import {
12
13
  getAttachmentsForMessage,
13
14
  linkAttachmentToMessage,
@@ -226,7 +227,7 @@ describe("renderHistoryContent", () => {
226
227
 
227
228
  expect(output.text).toBe("");
228
229
  expect(output.toolCalls).toEqual([
229
- { name: "web_fetch", input: { url: "https://example.com" } },
230
+ { id: "tu_1", name: "web_fetch", input: { url: "https://example.com" } },
230
231
  ]);
231
232
  expect(output.toolCallsBeforeText).toBe(true);
232
233
  });
@@ -244,6 +245,7 @@ describe("renderHistoryContent", () => {
244
245
 
245
246
  expect(output.toolCalls).toEqual([
246
247
  {
248
+ id: "tu_1",
247
249
  name: "bash",
248
250
  input: { command: "ls" },
249
251
  result: "file1.txt\nfile2.txt",
@@ -265,6 +267,7 @@ describe("renderHistoryContent", () => {
265
267
 
266
268
  expect(output.toolCalls).toEqual([
267
269
  {
270
+ id: "tu_1",
268
271
  name: "bash",
269
272
  input: { command: "bad" },
270
273
  result: "command not found",
@@ -273,6 +276,74 @@ describe("renderHistoryContent", () => {
273
276
  ]);
274
277
  });
275
278
 
279
+ test("omits id when the tool_use block carries none", () => {
280
+ const output = renderHistoryContent([
281
+ { type: "tool_use", name: "bash", input: { command: "ls" } },
282
+ ]);
283
+
284
+ // No provider id on the block — emit the entry without an `id` rather than
285
+ // materializing an empty string, so clients fall back to a synthesized id.
286
+ expect(output.toolCalls).toEqual([
287
+ { name: "bash", input: { command: "ls" } },
288
+ ]);
289
+ expect(output.toolCalls[0]).not.toHaveProperty("id");
290
+ });
291
+
292
+ test("carries the provider id for server_tool_use blocks", () => {
293
+ const output = renderHistoryContent([
294
+ {
295
+ type: "server_tool_use",
296
+ id: "srvtu_1",
297
+ name: "web_search",
298
+ input: { query: "vellum" },
299
+ },
300
+ ]);
301
+
302
+ expect(output.toolCalls).toEqual([
303
+ { id: "srvtu_1", name: "web_search", input: { query: "vellum" } },
304
+ ]);
305
+ });
306
+
307
+ test("synthesizes a positional id when a tool_use lacks a provider id", () => {
308
+ const output = renderHistoryContent(
309
+ [
310
+ { type: "tool_use", name: "bash", input: { command: "ls" } },
311
+ { type: "tool_use", name: "bash", input: { command: "pwd" } },
312
+ ],
313
+ undefined,
314
+ "msg-1",
315
+ );
316
+
317
+ // Same positional scheme the web client used to synthesize, so snapshot and
318
+ // stream tool calls stay keyed consistently and the client can drop its own
319
+ // fallback once it no longer skews ahead of the daemon.
320
+ expect(output.toolCalls.map((tc) => tc.id)).toEqual([
321
+ "tool-history-msg-1-0",
322
+ "tool-history-msg-1-1",
323
+ ]);
324
+ });
325
+
326
+ test("keeps the provider id and only synthesizes for blocks missing one", () => {
327
+ const output = renderHistoryContent(
328
+ [
329
+ {
330
+ type: "tool_use",
331
+ id: "tu_1",
332
+ name: "bash",
333
+ input: { command: "ls" },
334
+ },
335
+ { type: "tool_use", name: "bash", input: { command: "pwd" } },
336
+ ],
337
+ undefined,
338
+ "msg-1",
339
+ );
340
+
341
+ expect(output.toolCalls.map((tc) => tc.id)).toEqual([
342
+ "tu_1",
343
+ "tool-history-msg-1-1",
344
+ ]);
345
+ });
346
+
276
347
  // ── Persisted risk-option ladders (Phase B of conflation track) ─────────────
277
348
 
278
349
  test("hydrates persisted _risk*Options annotations onto tool calls", () => {
@@ -315,6 +386,79 @@ describe("renderHistoryContent", () => {
315
386
  expect(entry.riskDirectoryScopeOptions).toEqual(directoryScopeOptions);
316
387
  });
317
388
 
389
+ // ── Persisted tool activity (web_search / web_fetch) ────────────────────────
390
+
391
+ test("hydrates persisted _activityMetadata onto a tool_use block", () => {
392
+ // Mirrors what `annotatePersistedAssistantMessage` writes so the activity
393
+ // card survives a history reopen instead of degrading to plain text.
394
+ const activityMetadata: ToolActivityMetadata = {
395
+ webSearch: {
396
+ query: "vellum docs",
397
+ provider: "brave",
398
+ resultCount: 1,
399
+ durationMs: 120,
400
+ results: [
401
+ {
402
+ rank: 1,
403
+ title: "Vellum",
404
+ url: "https://vellum.ai",
405
+ domain: "vellum.ai",
406
+ },
407
+ ],
408
+ },
409
+ };
410
+
411
+ const output = renderHistoryContent([
412
+ {
413
+ type: "tool_use",
414
+ id: "tu_1",
415
+ name: "web_search",
416
+ input: { query: "vellum docs" },
417
+ _activityMetadata: activityMetadata,
418
+ },
419
+ ]);
420
+
421
+ expect(output.toolCalls[0].activityMetadata).toEqual(activityMetadata);
422
+ });
423
+
424
+ test("hydrates persisted _activityMetadata onto a server_tool_use block", () => {
425
+ const activityMetadata: ToolActivityMetadata = {
426
+ webSearch: {
427
+ query: "native search",
428
+ provider: "anthropic-native",
429
+ resultCount: 0,
430
+ durationMs: 80,
431
+ results: [],
432
+ },
433
+ };
434
+
435
+ const output = renderHistoryContent([
436
+ {
437
+ type: "server_tool_use",
438
+ id: "srvtu_1",
439
+ name: "web_search",
440
+ input: { query: "native search" },
441
+ _activityMetadata: activityMetadata,
442
+ },
443
+ ]);
444
+
445
+ expect(output.toolCalls[0].activityMetadata).toEqual(activityMetadata);
446
+ });
447
+
448
+ test("ignores non-object _activityMetadata annotations", () => {
449
+ const output = renderHistoryContent([
450
+ {
451
+ type: "tool_use",
452
+ id: "tu_1",
453
+ name: "web_search",
454
+ input: { query: "x" },
455
+ _activityMetadata: "not an object",
456
+ },
457
+ ]);
458
+
459
+ expect(output.toolCalls[0].activityMetadata).toBeUndefined();
460
+ });
461
+
318
462
  test("ignores non-array _risk*Options annotations", () => {
319
463
  // Defensive: a malformed persisted block should not throw or coerce.
320
464
  const output = renderHistoryContent([
@@ -355,6 +499,46 @@ describe("renderHistoryContent", () => {
355
499
  expect(entry.riskDirectoryScopeOptions).toBeUndefined();
356
500
  });
357
501
 
502
+ test("reads back a persisted confirmation decision from the closed enum", () => {
503
+ // GIVEN a persisted tool_use block stamped with a recorded decision
504
+ const output = renderHistoryContent([
505
+ {
506
+ type: "tool_use",
507
+ id: "tu_1",
508
+ name: "bash",
509
+ input: { command: "rm file" },
510
+ _confirmationDecision: "denied",
511
+ _confirmationLabel: "Run Command",
512
+ },
513
+ ]);
514
+
515
+ // WHEN it is rendered into a history tool call
516
+ const [entry] = output.toolCalls;
517
+
518
+ // THEN the decision survives verbatim alongside its label
519
+ expect(entry.confirmationDecision).toBe("denied");
520
+ expect(entry.confirmationLabel).toBe("Run Command");
521
+ });
522
+
523
+ test("drops a _confirmationDecision outside the closed enum", () => {
524
+ // GIVEN a malformed persisted decision the daemon never writes
525
+ const output = renderHistoryContent([
526
+ {
527
+ type: "tool_use",
528
+ id: "tu_1",
529
+ name: "bash",
530
+ input: { command: "ls" },
531
+ _confirmationDecision: "bogus",
532
+ },
533
+ ]);
534
+
535
+ // WHEN it is rendered into a history tool call
536
+ const [entry] = output.toolCalls;
537
+
538
+ // THEN the unknown value is not surfaced on the wire
539
+ expect(entry.confirmationDecision).toBeUndefined();
540
+ });
541
+
358
542
  test("handles mixed text and tool blocks", () => {
359
543
  const output = renderHistoryContent([
360
544
  { type: "text", text: "Let me look that up." },
@@ -569,6 +753,135 @@ describe("renderHistoryContent", () => {
569
753
  });
570
754
  });
571
755
 
756
+ describe("renderHistoryContent contentBlocks", () => {
757
+ test("builds an ordered block array while walking interleaved content", () => {
758
+ // GIVEN a turn that interleaves text, reasoning, a tool call, a surface,
759
+ // and trailing text in the raw model content
760
+ const output = renderHistoryContent([
761
+ { type: "text", text: "before tool" },
762
+ { type: "thinking", thinking: "reasoning", signature: "sig" },
763
+ { type: "tool_use", id: "t1", name: "run_command", input: { cmd: "ls" } },
764
+ { type: "tool_result", tool_use_id: "t1", content: "file.txt" },
765
+ {
766
+ type: "ui_surface",
767
+ surfaceId: "s1",
768
+ surfaceType: "ui_card",
769
+ data: {},
770
+ },
771
+ { type: "text", text: "after tool" },
772
+ ]);
773
+
774
+ // THEN contentBlocks mirrors the walk order
775
+ expect(output.contentBlocks.map((b) => b.type)).toEqual([
776
+ "text",
777
+ "thinking",
778
+ "tool_use",
779
+ "surface",
780
+ "text",
781
+ ]);
782
+ expect(output.contentBlocks[0]).toEqual({
783
+ type: "text",
784
+ text: "before tool",
785
+ });
786
+ expect(output.contentBlocks[1]).toEqual({
787
+ type: "thinking",
788
+ thinking: "reasoning",
789
+ });
790
+ expect(output.contentBlocks[4]).toEqual({
791
+ type: "text",
792
+ text: "after tool",
793
+ });
794
+ // AND the tool_use / surface blocks reuse the same objects the positional
795
+ // arrays hold, so the tool result paired in later is reflected here too
796
+ const toolBlock = output.contentBlocks[2];
797
+ expect(toolBlock.type === "tool_use" && toolBlock.toolCall).toBe(
798
+ output.toolCalls[0],
799
+ );
800
+ expect(output.toolCalls[0].result).toBe("file.txt");
801
+ const surfaceBlock = output.contentBlocks[3];
802
+ expect(surfaceBlock.type === "surface" && surfaceBlock.surface).toBe(
803
+ output.surfaces[0],
804
+ );
805
+ // AND no attachment block appears (this turn has no file blocks)
806
+ expect(output.contentBlocks.some((b) => b.type === "attachment")).toBe(
807
+ false,
808
+ );
809
+ });
810
+
811
+ const pdfBlock = {
812
+ type: "file",
813
+ source: {
814
+ type: "base64",
815
+ media_type: "application/pdf",
816
+ filename: "spec.pdf",
817
+ data: Buffer.from("hi").toString("base64"),
818
+ },
819
+ } as const;
820
+ const pdfAttachment = {
821
+ id: "att-1",
822
+ filename: "spec.pdf",
823
+ mimeType: "application/pdf",
824
+ sizeBytes: 2,
825
+ kind: "file",
826
+ } as const;
827
+
828
+ test("inlines an attachment block when hydrated metadata is supplied", () => {
829
+ // GIVEN a turn with text, a file attachment, then more text, and the
830
+ // caller supplies the DB-hydrated metadata for that file block
831
+ const output = renderHistoryContent(
832
+ [
833
+ { type: "text", text: "see file" },
834
+ pdfBlock,
835
+ { type: "text", text: "thanks" },
836
+ ],
837
+ [pdfAttachment],
838
+ );
839
+
840
+ // THEN the attachment block is placed inline between the two text blocks
841
+ expect(output.contentBlocks).toEqual([
842
+ { type: "text", text: "see file" },
843
+ { type: "attachment", attachment: pdfAttachment },
844
+ { type: "text", text: "thanks" },
845
+ ]);
846
+ });
847
+
848
+ test("omits the file block from contentBlocks when no metadata is supplied", () => {
849
+ // GIVEN the same turn but no hydrated metadata (the file still ships via
850
+ // the positional attachments array, just not as an inline block)
851
+ const output = renderHistoryContent([
852
+ { type: "text", text: "see file" },
853
+ pdfBlock,
854
+ { type: "text", text: "thanks" },
855
+ ]);
856
+
857
+ expect(output.contentBlocks).toEqual([
858
+ { type: "text", text: "see file" },
859
+ { type: "text", text: "thanks" },
860
+ ]);
861
+ expect(output.attachments.length).toBe(1);
862
+ });
863
+
864
+ test("excludes the trailing attachment-description segment from blocks", () => {
865
+ // GIVEN an attachment-only turn (the legacy text body carries a synthetic
866
+ // attachment description segment for clients without attachment UI)
867
+ const output = renderHistoryContent([pdfBlock], [pdfAttachment]);
868
+
869
+ // THEN the only block is the inlined attachment — the synthetic text
870
+ // segment stays in textSegments but never pollutes contentBlocks
871
+ expect(output.contentBlocks).toEqual([
872
+ { type: "attachment", attachment: pdfAttachment },
873
+ ]);
874
+ expect(output.textSegments.length).toBe(1);
875
+ });
876
+
877
+ test("emits a single text block for the non-array fallback", () => {
878
+ expect(renderHistoryContent("raw string").contentBlocks).toEqual([
879
+ { type: "text", text: "raw string" },
880
+ ]);
881
+ expect(renderHistoryContent(null).contentBlocks).toEqual([]);
882
+ });
883
+ });
884
+
572
885
  describe("getAttachmentsForMessage", () => {
573
886
  beforeEach(() => {
574
887
  const db = getDb();
@@ -5,6 +5,8 @@
5
5
  * parses it via the real frontmatter parser, and verifies that `skillFlagKey()`
6
6
  * returns the correct key and `resolveSkillStates()` correctly gates the skill.
7
7
  */
8
+ import { readFileSync } from "node:fs";
9
+ import { fileURLToPath } from "node:url";
8
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
9
11
 
10
12
  import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
@@ -225,3 +227,34 @@ describe("frontmatter feature-flag integration", () => {
225
227
  expect(resolvedOff.length).toBe(0);
226
228
  });
227
229
  });
230
+
231
+ // ---------------------------------------------------------------------------
232
+ // Bundled ACP skill: discoverability when ACP is disabled
233
+ // ---------------------------------------------------------------------------
234
+
235
+ describe("bundled acp skill discoverability", () => {
236
+ test("acp skill resolves with the flag off and config.acp disabled (no frontmatter flag gate)", () => {
237
+ // The ACP skill carries its own first-time-setup instructions, so it must
238
+ // stay visible even when the acp flag and config.acp.enabled are both off.
239
+ // Runtime enforcement happens in the ACP tools via isAcpEnabled instead.
240
+ const skillMdPath = fileURLToPath(
241
+ new URL("../config/bundled-skills/acp/SKILL.md", import.meta.url),
242
+ );
243
+ const skillMd = readFileSync(skillMdPath, "utf8");
244
+
245
+ const skill = buildSkillSummary("acp", skillMd);
246
+ expect(skill).not.toBeNull();
247
+ expect(skill!.featureFlag).toBeUndefined();
248
+ expect(skillFlagKey(skill!)).toBeUndefined();
249
+
250
+ // acp flag at its registry default (off) and config.acp disabled.
251
+ const config = makeConfig({
252
+ acp: { enabled: false, maxConcurrentSessions: 4, agents: {} },
253
+ } as Partial<AssistantConfig>);
254
+
255
+ const resolved = resolveSkillStates([skill!], config);
256
+ expect(resolved.length).toBe(1);
257
+ expect(resolved[0].summary.id).toBe("acp");
258
+ expect(resolved[0].state).toBe("enabled");
259
+ });
260
+ });
@@ -384,7 +384,7 @@ describe("toSlimSkill", () => {
384
384
  expect(slim!.kind).toBe("catalog");
385
385
  expect(slim!.status).toBe("available");
386
386
  expect(slim!.origin).toBe("skillssh");
387
- expect(slim!.category).toBe("knowledge");
387
+ expect(slim!.category).toBe("integrations");
388
388
  expect((slim as any).slug).toBe("owner/repo/my-skill");
389
389
  expect((slim as any).sourceRepo).toBe("owner/repo");
390
390
  expect((slim as any).installs).toBe(0);
@@ -174,7 +174,7 @@ import { LLMSchema } from "../config/schemas/llm.js";
174
174
  import {
175
175
  clearConversations,
176
176
  setConversation,
177
- } from "../daemon/conversation-store.js";
177
+ } from "../daemon/conversation-registry.js";
178
178
  import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
179
179
  import { SubagentManager } from "../subagent/manager.js";
180
180
 
@@ -11,7 +11,7 @@ const capturedNotifications: {
11
11
  message: string;
12
12
  }[] = [];
13
13
 
14
- mock.module("../daemon/conversation-store.js", () => ({
14
+ mock.module("../daemon/conversation-registry.js", () => ({
15
15
  findConversation: (id: string) => ({
16
16
  enqueueMessage: (options: { content: string }) => {
17
17
  capturedNotifications.push({
@@ -23,8 +23,6 @@ mock.module("../daemon/conversation-store.js", () => ({
23
23
  persistUserMessage: async () => ({ id: "mock-msg", deduplicated: false }),
24
24
  runAgentLoop: async () => {},
25
25
  }),
26
- addConversation: () => {},
27
- removeConversation: () => {},
28
26
  }));
29
27
 
30
28
  mock.module("../runtime/assistant-event-hub.js", () => ({
@@ -4,7 +4,7 @@ import {
4
4
  clearConversations,
5
5
  findConversation,
6
6
  setConversation,
7
- } from "../daemon/conversation-store.js";
7
+ } from "../daemon/conversation-registry.js";
8
8
  import type { ServerMessage } from "../daemon/message-protocol.js";
9
9
  import type { Message } from "../providers/types.js";
10
10
  import { SubagentManager } from "../subagent/manager.js";
@@ -11,7 +11,7 @@ const capturedNotifications: {
11
11
  message: string;
12
12
  }[] = [];
13
13
 
14
- mock.module("../daemon/conversation-store.js", () => ({
14
+ mock.module("../daemon/conversation-registry.js", () => ({
15
15
  findConversation: (id: string) => ({
16
16
  enqueueMessage: (options: { content: string }) => {
17
17
  capturedNotifications.push({
@@ -23,8 +23,6 @@ mock.module("../daemon/conversation-store.js", () => ({
23
23
  persistUserMessage: async () => ({ id: "mock-msg", deduplicated: false }),
24
24
  runAgentLoop: async () => {},
25
25
  }),
26
- addConversation: () => {},
27
- removeConversation: () => {},
28
26
  }));
29
27
 
30
28
  mock.module("../runtime/assistant-event-hub.js", () => ({
@@ -36,7 +36,7 @@ mock.module("../memory/conversation-crud.js", () => ({
36
36
  */
37
37
  const capturedMessages: string[] = [];
38
38
 
39
- mock.module("../daemon/conversation-store.js", () => ({
39
+ mock.module("../daemon/conversation-registry.js", () => ({
40
40
  findConversation: (_id: string) => ({
41
41
  enqueueMessage: (options: { content: string }) => {
42
42
  capturedMessages.push(options.content);
@@ -45,8 +45,6 @@ mock.module("../daemon/conversation-store.js", () => ({
45
45
  persistUserMessage: async () => ({ id: "mock-msg", deduplicated: false }),
46
46
  runAgentLoop: async () => {},
47
47
  }),
48
- addConversation: () => {},
49
- removeConversation: () => {},
50
48
  }));
51
49
 
52
50
  mock.module("../runtime/assistant-event-hub.js", () => ({
@@ -32,7 +32,7 @@ mock.module("../memory/conversation-crud.js", () => ({
32
32
  import {
33
33
  clearConversations,
34
34
  setConversation,
35
- } from "../daemon/conversation-store.js";
35
+ } from "../daemon/conversation-registry.js";
36
36
  import type { Message } from "../providers/types.js";
37
37
  import { getSubagentManager } from "../subagent/index.js";
38
38
  import { executeSubagentSpawn } from "../tools/subagent/spawn.js";
@@ -673,6 +673,26 @@ describe("buildSystemPrompt", () => {
673
673
  expect(result).toContain("Batch independent tool calls");
674
674
  });
675
675
 
676
+ test("bundled communication section renders and sorts before the parallel-tool-calls block", () => {
677
+ // `01-communication` sorts ahead of `01-parallel-tool-calls`, so the
678
+ // communication guidance leads the operational sections.
679
+ const result = buildSystemPrompt();
680
+ expect(result).toContain("## Communication");
681
+ // The core rule: deliberation belongs in private thinking, not text.
682
+ expect(result).toContain(
683
+ "in your private thinking — never in user-facing text",
684
+ );
685
+ // Closes by deferring to the user's established communication preferences.
686
+ expect(result).toContain(
687
+ "Always prioritize communication preferences that you've established",
688
+ );
689
+ const communicationIdx = result.indexOf("## Communication");
690
+ const parallelIdx = result.indexOf("<use_parallel_tool_calls>");
691
+ expect(communicationIdx).toBeGreaterThan(-1);
692
+ expect(parallelIdx).toBeGreaterThan(-1);
693
+ expect(communicationIdx).toBeLessThan(parallelIdx);
694
+ });
695
+
676
696
  test("workspace prefix with frontmatter renders body at the very top", () => {
677
697
  mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
678
698
  writeFileSync(