@vellumai/assistant 0.8.8 → 0.8.9-staging.2

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 (360) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/examples/plugins/echo/README.md +61 -60
  4. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  5. package/examples/plugins/echo/hooks/stop.ts +16 -0
  6. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  7. package/examples/plugins/echo/package.json +1 -2
  8. package/examples/plugins/echo/src/emit.ts +19 -0
  9. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  10. package/openapi.yaml +235 -6
  11. package/package.json +2 -2
  12. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  13. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  14. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  15. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  17. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  18. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  19. package/src/__tests__/agent-loop.test.ts +441 -96
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  21. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  22. package/src/__tests__/anthropic-provider.test.ts +1 -1
  23. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  24. package/src/__tests__/app-control-flow.test.ts +1 -1
  25. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  26. package/src/__tests__/app-executors.test.ts +132 -0
  27. package/src/__tests__/approval-cascade.test.ts +5 -4
  28. package/src/__tests__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  30. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  31. package/src/__tests__/channel-approvals.test.ts +1 -1
  32. package/src/__tests__/compaction-circuit.test.ts +258 -0
  33. package/src/__tests__/compaction-direct.test.ts +132 -0
  34. package/src/__tests__/compaction-events.test.ts +5 -5
  35. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  36. package/src/__tests__/context-overflow-reducer.test.ts +1 -1
  37. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -4
  38. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  39. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  40. package/src/__tests__/conversation-agent-loop-overflow.test.ts +142 -218
  41. package/src/__tests__/conversation-agent-loop.test.ts +297 -586
  42. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  43. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -4
  44. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  45. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  46. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -4
  47. package/src/__tests__/conversation-process-callsite.test.ts +14 -14
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +70 -65
  49. package/src/__tests__/conversation-queue.test.ts +9 -9
  50. package/src/__tests__/conversation-runtime-assembly.test.ts +923 -231
  51. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  52. package/src/__tests__/conversation-slash-queue.test.ts +6 -4
  53. package/src/__tests__/conversation-slash-unknown.test.ts +6 -4
  54. package/src/__tests__/conversation-speed-override.test.ts +10 -9
  55. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  56. package/src/__tests__/conversation-workspace-cache-state.test.ts +23 -20
  57. package/src/__tests__/conversation-workspace-injection.test.ts +68 -6
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +14 -11
  59. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  61. package/src/__tests__/db-acp-history.test.ts +101 -0
  62. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  63. package/src/__tests__/empty-response-hook.test.ts +1 -1
  64. package/src/__tests__/file-write-tool.test.ts +63 -0
  65. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  66. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  67. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  68. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  69. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  70. package/src/__tests__/heartbeat-service.test.ts +1 -0
  71. package/src/__tests__/history-repair-hook.test.ts +1 -1
  72. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  73. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  74. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  75. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  76. package/src/__tests__/injector-background-turn.test.ts +13 -23
  77. package/src/__tests__/injector-chain.test.ts +268 -44
  78. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  79. package/src/__tests__/injector-document-comments.test.ts +97 -114
  80. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  81. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  82. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  83. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  84. package/src/__tests__/memory-retrieval-hook.test.ts +131 -26
  85. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  86. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  87. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  88. package/src/__tests__/plugin-bootstrap.test.ts +12 -23
  89. package/src/__tests__/plugin-registry.test.ts +3 -49
  90. package/src/__tests__/plugin-types.test.ts +0 -70
  91. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  92. package/src/__tests__/reaction-persistence.test.ts +1 -1
  93. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  94. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  95. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  96. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  97. package/src/__tests__/subagent-detail.test.ts +25 -7
  98. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  99. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  100. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  101. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  102. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  103. package/src/__tests__/title-generate-hook.test.ts +1 -1
  104. package/src/__tests__/tool-error-hook.test.ts +1 -1
  105. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  106. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  107. package/src/acp/__tests__/agent-process.test.ts +161 -0
  108. package/src/acp/__tests__/client-handler.test.ts +40 -0
  109. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  110. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  111. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  112. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  113. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  114. package/src/acp/agent-process.ts +61 -1
  115. package/src/acp/auto-install.test.ts +280 -0
  116. package/src/acp/auto-install.ts +232 -0
  117. package/src/acp/client-handler.ts +31 -0
  118. package/src/acp/feature-gate.test.ts +48 -0
  119. package/src/acp/feature-gate.ts +34 -0
  120. package/src/acp/prepare-agent-env.ts +80 -27
  121. package/src/acp/resolve-agent.test.ts +225 -9
  122. package/src/acp/resolve-agent.ts +122 -17
  123. package/src/acp/resume-hint.ts +23 -0
  124. package/src/acp/session-manager.ts +507 -73
  125. package/src/agent/compaction-circuit.ts +60 -102
  126. package/src/agent/loop.ts +414 -248
  127. package/src/api/responses/conversation-message.ts +14 -1
  128. package/src/approvals/guardian-request-resolvers.ts +1 -1
  129. package/src/background-wake/next-wake.ts +1 -0
  130. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  131. package/src/cli/commands/plugins.ts +43 -37
  132. package/src/cli/lib/__tests__/install-from-github.test.ts +429 -111
  133. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  134. package/src/cli/lib/__tests__/plugin-details.test.ts +372 -0
  135. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +220 -0
  136. package/src/cli/lib/__tests__/search-plugins.test.ts +226 -32
  137. package/src/cli/lib/install-from-github.ts +464 -55
  138. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  139. package/src/cli/lib/plugin-details.ts +409 -0
  140. package/src/cli/lib/plugin-marketplace.ts +197 -0
  141. package/src/cli/lib/search-plugins.ts +195 -29
  142. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  143. package/src/config/acp-defaults.test.ts +10 -0
  144. package/src/config/acp-defaults.ts +6 -0
  145. package/src/config/bundled-skills/acp/SKILL.md +85 -33
  146. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  147. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  148. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  149. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  150. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  151. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  152. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  153. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  154. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  155. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  156. package/src/config/bundled-tool-registry.ts +4 -0
  157. package/src/config/call-site-defaults.ts +0 -2
  158. package/src/config/feature-flag-registry.json +15 -6
  159. package/src/config/schemas/call-site-catalog.ts +0 -14
  160. package/src/config/schemas/heartbeat.ts +9 -0
  161. package/src/config/schemas/llm.ts +0 -2
  162. package/src/context/compactor.ts +22 -4
  163. package/src/context/strip-injections.ts +8 -2
  164. package/src/context/window-manager.ts +27 -13
  165. package/src/daemon/conversation-agent-loop-handlers.ts +10 -35
  166. package/src/daemon/conversation-agent-loop.ts +175 -1055
  167. package/src/daemon/conversation-lifecycle.ts +11 -255
  168. package/src/daemon/conversation-process.ts +8 -136
  169. package/src/daemon/conversation-registry.ts +159 -0
  170. package/src/daemon/conversation-runtime-assembly.ts +293 -392
  171. package/src/daemon/conversation-store.ts +9 -90
  172. package/src/daemon/conversation-surfaces.ts +24 -8
  173. package/src/daemon/conversation-workspace.ts +17 -0
  174. package/src/daemon/conversation.ts +404 -57
  175. package/src/daemon/disk-pressure-policy.ts +0 -1
  176. package/src/daemon/external-plugins-bootstrap.ts +14 -19
  177. package/src/daemon/handlers/conversations.ts +3 -1
  178. package/src/daemon/handlers/skills.ts +4 -1
  179. package/src/daemon/host-proxy-preactivation.ts +1 -3
  180. package/src/daemon/lifecycle.ts +21 -7
  181. package/src/daemon/server.ts +2 -0
  182. package/src/daemon/wake-conversation-ops.ts +269 -0
  183. package/src/embedded/plugin-api.ts +2 -2
  184. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  185. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  186. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  187. package/src/heartbeat/heartbeat-service.ts +26 -0
  188. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  189. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  190. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  191. package/src/ipc/skill-routes/memory.ts +4 -2
  192. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  193. package/src/memory/conversation-crud.ts +29 -19
  194. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  195. package/src/memory/db-init.ts +2 -0
  196. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  197. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  198. package/src/memory/jobs-worker.ts +25 -1
  199. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  200. package/src/memory/migrations/index.ts +1 -0
  201. package/src/memory/schema/acp.ts +4 -0
  202. package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
  203. package/src/memory/v2/consolidation-job.ts +13 -4
  204. package/src/plugin-api/constants.ts +4 -0
  205. package/src/plugin-api/index.ts +6 -5
  206. package/src/plugin-api/types.ts +75 -0
  207. package/src/plugins/defaults/compaction/compact.ts +59 -0
  208. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +7 -7
  209. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  210. package/src/plugins/defaults/compaction/package.json +1 -2
  211. package/src/plugins/defaults/empty-response/package.json +0 -1
  212. package/src/plugins/defaults/history-repair/package.json +0 -1
  213. package/src/plugins/defaults/index.ts +135 -26
  214. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  215. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +184 -74
  216. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  217. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  218. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  219. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  220. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +9 -6
  221. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  222. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +8 -5
  223. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  224. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  225. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +10 -5
  226. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  227. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +5 -5
  228. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +16 -17
  229. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -2
  230. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +9 -5
  231. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +5 -2
  232. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  233. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  234. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  235. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +15 -8
  236. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +2 -2
  237. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  238. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  239. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/provider-blocks.ts +1 -1
  240. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +7 -3
  241. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  242. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +5 -5
  243. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  244. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +7 -7
  245. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +32 -94
  246. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  247. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +1 -1
  248. package/src/plugins/defaults/title-generate/package.json +0 -1
  249. package/src/plugins/defaults/tool-error/package.json +0 -1
  250. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  251. package/src/plugins/pipeline.ts +6 -293
  252. package/src/plugins/registry.ts +9 -37
  253. package/src/plugins/types.ts +76 -381
  254. package/src/plugins/user-loader.ts +30 -127
  255. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  256. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +35 -3
  257. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  258. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  259. package/src/runtime/agent-wake.ts +108 -209
  260. package/src/runtime/assistant-event-hub.ts +1 -1
  261. package/src/runtime/channel-approvals.ts +1 -1
  262. package/src/runtime/interactive-ui.ts +1 -1
  263. package/src/runtime/routes/__tests__/acp-routes.test.ts +315 -55
  264. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  265. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  266. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  267. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  268. package/src/runtime/routes/acp-routes.test.ts +89 -25
  269. package/src/runtime/routes/acp-routes.ts +81 -29
  270. package/src/runtime/routes/approval-routes.ts +1 -1
  271. package/src/runtime/routes/browser-routes.ts +1 -1
  272. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  273. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  274. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  275. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  276. package/src/runtime/routes/conversation-routes.ts +28 -2
  277. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  278. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  279. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  280. package/src/runtime/routes/host-cu-routes.ts +1 -1
  281. package/src/runtime/routes/identity-routes.ts +18 -3
  282. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  283. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  284. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  285. package/src/runtime/routes/memory-v3-routes.ts +16 -6
  286. package/src/runtime/routes/playground/helpers.ts +1 -1
  287. package/src/runtime/routes/plugins-routes.ts +337 -35
  288. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  289. package/src/runtime/routes/work-items-routes.ts +2 -4
  290. package/src/runtime/services/conversation-serializer.ts +1 -1
  291. package/src/signals/cancel.ts +2 -4
  292. package/src/subagent/manager.ts +21 -5
  293. package/src/tools/acp/context.ts +20 -0
  294. package/src/tools/acp/list-agents.test.ts +8 -2
  295. package/src/tools/acp/spawn.test.ts +176 -195
  296. package/src/tools/acp/spawn.ts +37 -172
  297. package/src/tools/acp/steer.test.ts +105 -8
  298. package/src/tools/acp/steer.ts +48 -17
  299. package/src/tools/apps/executors.ts +166 -50
  300. package/src/tools/filesystem/write.ts +34 -0
  301. package/src/tools/subagent/spawn.ts +2 -4
  302. package/src/tools/ui-surface/definitions.ts +25 -5
  303. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  304. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  305. package/docs/plugins.md +0 -832
  306. package/examples/plugins/echo/register.ts +0 -143
  307. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  308. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  309. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  310. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  311. package/src/__tests__/pipeline-runner.test.ts +0 -554
  312. package/src/__tests__/plugin-external-api.test.ts +0 -68
  313. package/src/daemon/wake-target-adapter.ts +0 -253
  314. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  315. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  316. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  317. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  318. package/src/plugins/defaults/compaction/register.ts +0 -35
  319. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  320. package/src/plugins/defaults/empty-response/register.ts +0 -23
  321. package/src/plugins/defaults/history-repair/register.ts +0 -24
  322. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  323. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  324. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  325. package/src/plugins/defaults/title-generate/register.ts +0 -35
  326. package/src/plugins/defaults/tool-error/register.ts +0 -23
  327. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  328. package/src/plugins/external-api.ts +0 -104
  329. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  330. package/src/proactive-artifact/decision.test.ts +0 -226
  331. package/src/proactive-artifact/decision.ts +0 -165
  332. package/src/proactive-artifact/index.ts +0 -7
  333. package/src/proactive-artifact/job.test.ts +0 -962
  334. package/src/proactive-artifact/job.ts +0 -372
  335. package/src/proactive-artifact/message-copy.ts +0 -58
  336. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  337. package/src/proactive-artifact/trigger-state.ts +0 -123
  338. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  339. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  340. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  341. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  342. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +0 -0
  343. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  344. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/provider-blocks.test.ts +0 -0
  345. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  346. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  347. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  348. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  349. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  350. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  351. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  352. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  353. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  354. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  355. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  356. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  357. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
  358. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  359. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  360. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -34,22 +34,19 @@ describe("buildShimSource", () => {
34
34
  test("emits a globalThis trampoline + one binding per export", () => {
35
35
  const source = buildShimSource(
36
36
  ["foo", "bar"],
37
- Symbol.for("vellum.plugin-api.v1"),
37
+ Symbol.for("vellum.plugin-api"),
38
38
  );
39
39
  expect(source).toBe(
40
- `const api = globalThis[Symbol.for("vellum.plugin-api.v1")];\n` +
40
+ `const api = globalThis[Symbol.for("vellum.plugin-api")];\n` +
41
41
  `export const foo = api.foo;\n` +
42
42
  `export const bar = api.bar;\n`,
43
43
  );
44
44
  });
45
45
 
46
46
  test("handles an empty export list (today's types-only surface)", () => {
47
- const source = buildShimSource(
48
- [],
49
- Symbol.for("vellum.plugin-api.v1"),
50
- );
47
+ const source = buildShimSource([], Symbol.for("vellum.plugin-api"));
51
48
  expect(source).toBe(
52
- `const api = globalThis[Symbol.for("vellum.plugin-api.v1")];\n`,
49
+ `const api = globalThis[Symbol.for("vellum.plugin-api")];\n`,
53
50
  );
54
51
  });
55
52
  });
@@ -109,7 +106,7 @@ describe("ensurePluginApiShim", () => {
109
106
  const pluginDir = join(workspaceDir, "plugins", "fake-plugin");
110
107
  await mkdir(pluginDir, { recursive: true });
111
108
  await writeFile(
112
- join(pluginDir, "register.js"),
109
+ join(pluginDir, "probe.js"),
113
110
  `import * as api from "@vellumai/plugin-api";\nexport { api };\n`,
114
111
  );
115
112
 
@@ -118,7 +115,7 @@ describe("ensurePluginApiShim", () => {
118
115
  // → plugin-api namespace. If any link in that chain is broken, this
119
116
  // import throws.
120
117
  const mod: { api: Record<string, unknown> } = await import(
121
- join(pluginDir, "register.js")
118
+ join(pluginDir, "probe.js")
122
119
  );
123
120
  expect(mod.api).toBeDefined();
124
121
  });
@@ -38,13 +38,11 @@ import { RiskLevel } from "../permissions/types.js";
38
38
  import { registerDefaultPlugins } from "../plugins/defaults/index.js";
39
39
  import {
40
40
  closeRegistration,
41
- getMiddlewaresFor,
42
41
  getRegisteredPlugins,
43
42
  registerPlugin,
44
43
  resetPluginRegistryForTests,
45
44
  } from "../plugins/registry.js";
46
45
  import {
47
- type PipelineMiddlewareMap,
48
46
  type Plugin,
49
47
  PluginExecutionError,
50
48
  type PluginInitContext,
@@ -485,33 +483,24 @@ describe("plugin bootstrap", () => {
485
483
  expect(initFired).toBe(false);
486
484
  });
487
485
 
488
- test("requiresFlag disabled: plugin middleware is dropped from the registry", async () => {
489
- // Regression: prior to the unregisterPlugin() call on the flag-gated skip
490
- // path, `getMiddlewaresFor()` iterated over every entry in
491
- // `registeredPlugins` so a gated-off plugin's middleware still ran on
492
- // every pipeline invocation even though `init()` had never fired to set up
493
- // the state it depended on.
494
- setOverridesForTesting({ "plugin-middleware-disabled": false });
495
-
496
- const gatedMiddleware: PipelineMiddlewareMap["compaction"] = async (
497
- args,
498
- next,
499
- ) => next(args);
486
+ test("requiresFlag disabled: the skipped plugin is removed from the registry", async () => {
487
+ // Regression: a flag-gated skip must call `unregisterPlugin()` so the
488
+ // gated-off plugin does not linger in `registeredPlugins` with its
489
+ // `init()` never having fired to set up the state it depends on.
490
+ setOverridesForTesting({ "plugin-registry-disabled": false });
491
+
500
492
  const plugin = buildPlugin(
501
- "gated-middleware",
502
- {
503
- middleware: { compaction: gatedMiddleware },
504
- },
505
- { requiresFlag: ["plugin-middleware-disabled"] },
493
+ "gated-registry",
494
+ {},
495
+ { requiresFlag: ["plugin-registry-disabled"] },
506
496
  );
507
497
  registerPlugin(plugin);
508
498
 
509
499
  await bootstrapPlugins();
510
500
 
511
- // The middleware slot must not expose the flag-gated plugin's contribution.
512
- // The default plugins also contribute compaction middleware, so we key on
513
- // identity rather than asserting an empty list.
514
- expect(getMiddlewaresFor("compaction")).not.toContain(gatedMiddleware);
501
+ // The gated-off plugin must not survive in the registry snapshot.
502
+ const names = getRegisteredPlugins().map((p) => p.manifest.name);
503
+ expect(names).not.toContain("gated-registry");
515
504
  });
516
505
 
517
506
  test("requiresFlag disabled: no shutdown hook entry installed for the skipped plugin", async () => {
@@ -2,26 +2,19 @@
2
2
  * Tests for the plugin registry (PR 13).
3
3
  *
4
4
  * Covers successful registration, required-field and duplicate-name
5
- * validation, capability-version negotiation error messaging, injector
6
- * ordering, and middleware collection order.
5
+ * validation, capability-version negotiation error messaging, and
6
+ * registration order.
7
7
  */
8
8
 
9
9
  import { beforeEach, describe, expect, test } from "bun:test";
10
10
 
11
11
  import {
12
12
  closeRegistration,
13
- getMiddlewaresFor,
14
13
  getRegisteredPlugins,
15
14
  registerPlugin,
16
15
  resetPluginRegistryForTests,
17
16
  } from "../plugins/registry.js";
18
- import {
19
- type CompactionArgs,
20
- type CompactionResult,
21
- type Middleware,
22
- type Plugin,
23
- PluginExecutionError,
24
- } from "../plugins/types.js";
17
+ import { type Plugin, PluginExecutionError } from "../plugins/types.js";
25
18
 
26
19
  /** Build a minimal, valid plugin with the given name and optional extras. */
27
20
  function buildPlugin(
@@ -147,45 +140,6 @@ describe("plugin registry", () => {
147
140
  expect(() => registerPlugin(bad)).toThrow(/manifest\.version is required/);
148
141
  });
149
142
 
150
- test("getMiddlewaresFor returns middleware in registration order", () => {
151
- const firstMw: Middleware<CompactionArgs, CompactionResult> = async (
152
- args,
153
- next,
154
- ) => next(args);
155
- const secondMw: Middleware<CompactionArgs, CompactionResult> = async (
156
- args,
157
- next,
158
- ) => next(args);
159
-
160
- registerPlugin(
161
- buildPlugin("plugin-first", { middleware: { compaction: firstMw } }),
162
- );
163
- registerPlugin(
164
- buildPlugin("plugin-second", { middleware: { compaction: secondMw } }),
165
- );
166
-
167
- const middlewares = getMiddlewaresFor("compaction");
168
- expect(middlewares).toHaveLength(2);
169
- // Identity comparison proves the middleware instances come back in
170
- // registration order — outer→inner composition semantics belong to the
171
- // pipeline runner (PR 12), not the registry.
172
- expect(middlewares[0]).toBe(firstMw);
173
- expect(middlewares[1]).toBe(secondMw);
174
- });
175
-
176
- test("getMiddlewaresFor skips plugins without a middleware for the pipeline", () => {
177
- const mw: Middleware<CompactionArgs, CompactionResult> = async (
178
- args,
179
- next,
180
- ) => next(args);
181
- registerPlugin(buildPlugin("bare"));
182
- registerPlugin(buildPlugin("has-mw", { middleware: { compaction: mw } }));
183
-
184
- const middlewares = getMiddlewaresFor("compaction");
185
- expect(middlewares).toHaveLength(1);
186
- expect(middlewares[0]).toBe(mw);
187
- });
188
-
189
143
  test("getRegisteredPlugins reflects registration order", () => {
190
144
  registerPlugin(buildPlugin("one"));
191
145
  registerPlugin(buildPlugin("two"));
@@ -13,18 +13,10 @@ import { describe, expect, test } from "bun:test";
13
13
  import type { TrustContext } from "../daemon/trust-context.js";
14
14
  import { RiskLevel } from "../permissions/types.js";
15
15
  import {
16
- type CircuitBreakerArgs,
17
- type CircuitBreakerResult,
18
- type CompactionArgs,
19
- type CompactionResult,
20
- type Middleware,
21
- type OverflowReduceArgs,
22
- type OverflowReduceResult,
23
16
  type Plugin,
24
17
  PluginExecutionError,
25
18
  type PluginInitContext,
26
19
  type PluginManifest,
27
- PluginTimeoutError,
28
20
  type TurnContext,
29
21
  } from "../plugins/types.js";
30
22
  import type { Tool } from "../tools/types.js";
@@ -38,7 +30,6 @@ const sampleTurnContext: TurnContext = {
38
30
  requestId: "req-abc",
39
31
  conversationId: "conv-xyz",
40
32
  turnIndex: 0,
41
- pluginName: "sample-plugin",
42
33
  trust: sampleTrust,
43
34
  };
44
35
 
@@ -52,43 +43,6 @@ describe("plugin core types", () => {
52
43
  config: { parse: (input: unknown) => input },
53
44
  };
54
45
 
55
- // `overflowReduce` has a concrete arg/result shape (PR 23). Uses a
56
- // dedicated passthrough that returns a structurally-correct result so
57
- // `satisfies Plugin` keeps verifying the signature.
58
- const overflowReducePassthrough: Middleware<
59
- OverflowReduceArgs,
60
- OverflowReduceResult
61
- > = async (args, _next, _ctx) => ({
62
- messages: args.messages,
63
- runMessages: args.runMessages,
64
- injectionMode: "full",
65
- reducerState: {
66
- appliedTiers: [],
67
- injectionMode: "full",
68
- exhausted: true,
69
- },
70
- reducerCompacted: false,
71
- attempts: 0,
72
- });
73
-
74
- // Slot-specific passthrough for the `compaction` pipeline — PR 25
75
- // narrowed its args/result away from the generic `{ input: unknown }`
76
- // placeholder, so the generic `passthrough` no longer satisfies its
77
- // middleware signature. This dedicated middleware keeps the shape-only
78
- // assertion for the slot without forcing every other slot to narrow.
79
- const compactionPassthrough: Middleware<
80
- CompactionArgs,
81
- CompactionResult
82
- > = async (args, next, _ctx) => next(args);
83
-
84
- // `circuitBreaker` carries a concrete arg shape (pipeline wrapping
85
- // landed ahead of the other slots) so it needs its own passthrough
86
- // rather than reusing the generic placeholder.
87
- const circuitPassthrough: Middleware<
88
- CircuitBreakerArgs,
89
- CircuitBreakerResult
90
- > = async (args, next, _ctx) => next(args);
91
-
92
46
  const sampleTool: Tool = {
93
47
  name: "sample-tool",
94
48
  description: "Sample plugin tool",
@@ -132,34 +86,10 @@ describe("plugin core types", () => {
132
86
  body: "## Sample\n\nPlugin-provided skill body.",
133
87
  },
134
88
  ],
135
- middleware: {
136
- compaction: compactionPassthrough,
137
- overflowReduce: overflowReducePassthrough,
138
- circuitBreaker: circuitPassthrough,
139
- },
140
89
  } satisfies Plugin;
141
90
 
142
91
  // Minimal runtime check so the test body is non-empty.
143
92
  expect(plugin.manifest.name).toBe("sample-plugin");
144
- expect(plugin.middleware.compaction).toBe(compactionPassthrough);
145
- });
146
-
147
- test("PluginTimeoutError carries pipeline, plugin, and elapsed fields", () => {
148
- const err = new PluginTimeoutError("compaction", "sample-plugin", 30000);
149
- expect(err).toBeInstanceOf(Error);
150
- expect(err.name).toBe("PluginTimeoutError");
151
- expect(err.pipeline).toBe("compaction");
152
- expect(err.pluginName).toBe("sample-plugin");
153
- expect(err.elapsedMs).toBe(30000);
154
- expect(err.message).toContain("compaction");
155
- expect(err.message).toContain("30000");
156
- expect(err.message).toContain("sample-plugin");
157
- });
158
-
159
- test("PluginTimeoutError omits plugin suffix when unknown", () => {
160
- const err = new PluginTimeoutError("circuitBreaker", undefined, 1234);
161
- expect(err.pluginName).toBeUndefined();
162
- expect(err.message).not.toContain("offending plugin");
163
93
  });
164
94
 
165
95
  test("PluginExecutionError carries the plugin name and message", () => {
@@ -0,0 +1,109 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { preModelCallSanitize } from "../agent/loop.js";
4
+ import type { Message } from "../providers/types.js";
5
+
6
+ /**
7
+ * `preModelCallSanitize` is the loop's single pre-send transform: it converts
8
+ * historical `web_search_tool_result` blocks to text alongside the media and
9
+ * AX-tree strips, so every provider call — first call, post-compaction, and
10
+ * recovery reruns — is sanitized in one place. These tests guard that the
11
+ * helper actually performs the web-search conversion and is idempotent.
12
+ */
13
+ describe("preModelCallSanitize", () => {
14
+ test("passes through history with nothing to sanitize", () => {
15
+ // GIVEN a plain conversation with no media, AX trees, or web-search blocks
16
+ const messages: Message[] = [
17
+ { role: "user", content: [{ type: "text", text: "Hello" }] },
18
+ { role: "assistant", content: [{ type: "text", text: "Hi" }] },
19
+ ];
20
+
21
+ // WHEN the loop sanitizes the outbound history
22
+ const result = preModelCallSanitize(messages);
23
+
24
+ // THEN the history is returned unchanged
25
+ expect(result).toEqual(messages);
26
+ });
27
+
28
+ test("converts historical web_search_tool_result blocks to text summaries", () => {
29
+ // GIVEN an assistant turn whose web_search_tool_result carries an opaque
30
+ // encrypted_content token that would be rejected if replayed
31
+ const messages: Message[] = [
32
+ { role: "user", content: [{ type: "text", text: "Search cats" }] },
33
+ {
34
+ role: "assistant",
35
+ content: [
36
+ {
37
+ type: "server_tool_use",
38
+ id: "stu_1",
39
+ name: "web_search",
40
+ input: { query: "cats" },
41
+ },
42
+ {
43
+ type: "web_search_tool_result",
44
+ tool_use_id: "stu_1",
45
+ content: [
46
+ {
47
+ type: "web_search_result",
48
+ url: "https://cats.com",
49
+ title: "Cats!",
50
+ encrypted_content: "expired_token_1",
51
+ },
52
+ ],
53
+ },
54
+ ],
55
+ },
56
+ ];
57
+
58
+ // WHEN the loop sanitizes the outbound history
59
+ const result = preModelCallSanitize(messages);
60
+
61
+ // THEN the opaque block is replaced with a plaintext title+URL summary and
62
+ // the paired server_tool_use is dropped, so no expired token is replayed
63
+ const assistantMsg = result[1];
64
+ expect(assistantMsg.content.map((b) => b.type)).toEqual(["text"]);
65
+ const summary = assistantMsg.content[0];
66
+ expect(summary.type).toBe("text");
67
+ if (summary.type === "text") {
68
+ expect(summary.text).toContain("Cats!");
69
+ expect(summary.text).toContain("https://cats.com");
70
+ expect(summary.text).not.toContain("expired_token_1");
71
+ }
72
+ });
73
+
74
+ test("is idempotent — re-sanitizing already-sanitized history is a no-op", () => {
75
+ // GIVEN history that has already been sanitized once
76
+ const messages: Message[] = [
77
+ {
78
+ role: "assistant",
79
+ content: [
80
+ {
81
+ type: "server_tool_use",
82
+ id: "stu_A",
83
+ name: "web_search",
84
+ input: { query: "alpha" },
85
+ },
86
+ {
87
+ type: "web_search_tool_result",
88
+ tool_use_id: "stu_A",
89
+ content: [
90
+ {
91
+ type: "web_search_result",
92
+ url: "https://a.example",
93
+ title: "A",
94
+ encrypted_content: "tok_A",
95
+ },
96
+ ],
97
+ },
98
+ ],
99
+ },
100
+ ];
101
+ const once = preModelCallSanitize(messages);
102
+
103
+ // WHEN it is sanitized a second time (every outbound call re-runs the helper)
104
+ const twice = preModelCallSanitize(once);
105
+
106
+ // THEN the second pass changes nothing
107
+ expect(twice).toEqual(once);
108
+ });
109
+ });
@@ -27,7 +27,7 @@ mock.module("../config/env.js", () => ({
27
27
  }));
28
28
 
29
29
  const _conversationMocks = new Map<string, unknown>();
30
- mock.module("../daemon/conversation-store.js", () => ({
30
+ mock.module("../daemon/conversation-registry.js", () => ({
31
31
  findConversation: (id: string) => _conversationMocks.get(id),
32
32
  }));
33
33
 
@@ -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");
@@ -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
+ });
@@ -14,7 +14,7 @@ import type { Message } from "../providers/types.js";
14
14
  /**
15
15
  * Minimal reproduction of repairPendingToolUseBlocks logic for direct
16
16
  * unit testing. The real implementation lives in conversation-process.ts.
17
- * This mirrors it exactly to avoid needing the full ProcessConversationContext.
17
+ * This mirrors it exactly to avoid needing the full Conversation.
18
18
  */
19
19
  function repairPendingToolUseBlocks(messages: Message[]): Message[] {
20
20
  if (messages.length === 0) return messages;
@@ -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
 
@@ -18,6 +18,7 @@ function msg(role: string, content: unknown[]): MessageRow {
18
18
  content: JSON.stringify(content),
19
19
  createdAt: Date.now(),
20
20
  metadata: null,
21
+ clientMessageId: null,
21
22
  };
22
23
  }
23
24
 
@@ -26,10 +27,19 @@ describe("parseSubagentMessages", () => {
26
27
  const messages = [
27
28
  msg("user", [{ type: "text", text: "Do something" }]),
28
29
  msg("assistant", [
29
- { type: "tool_use", id: "t1", name: "web_search", input: { query: "test" } },
30
+ {
31
+ type: "tool_use",
32
+ id: "t1",
33
+ name: "web_search",
34
+ input: { query: "test" },
35
+ },
30
36
  ]),
31
37
  msg("user", [
32
- { type: "tool_result", tool_use_id: "t1", content: "Search results here" },
38
+ {
39
+ type: "tool_result",
40
+ tool_use_id: "t1",
41
+ content: "Search results here",
42
+ },
33
43
  ]),
34
44
  ];
35
45
 
@@ -44,7 +54,12 @@ describe("parseSubagentMessages", () => {
44
54
  const messages = [
45
55
  msg("user", [{ type: "text", text: "Do something" }]),
46
56
  msg("assistant", [
47
- { type: "tool_use", id: "t2", name: "file_read", input: { file_path: "/tmp/test.txt" } },
57
+ {
58
+ type: "tool_use",
59
+ id: "t2",
60
+ name: "file_read",
61
+ input: { file_path: "/tmp/test.txt" },
62
+ },
48
63
  ]),
49
64
  msg("user", [
50
65
  {
@@ -69,11 +84,14 @@ describe("parseSubagentMessages", () => {
69
84
  const messages = [
70
85
  msg("user", [{ type: "text", text: "Do something" }]),
71
86
  msg("assistant", [
72
- { type: "tool_use", id: "t3", name: "bash", input: { command: "echo hi" } },
73
- ]),
74
- msg("user", [
75
- { type: "tool_result", tool_use_id: "t3", content: null },
87
+ {
88
+ type: "tool_use",
89
+ id: "t3",
90
+ name: "bash",
91
+ input: { command: "echo hi" },
92
+ },
76
93
  ]),
94
+ msg("user", [{ type: "tool_result", tool_use_id: "t3", content: null }]),
77
95
  ];
78
96
 
79
97
  const result = parseSubagentMessages("sub-1", messages);
@@ -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";
@@ -51,9 +51,9 @@ import type {
51
51
  StopContext,
52
52
  UserPromptSubmitContext,
53
53
  } from "../plugin-api/types.js";
54
+ import { defaultTitleGeneratePlugin } from "../plugins/defaults/index.js";
54
55
  import stop from "../plugins/defaults/title-generate/hooks/stop.js";
55
56
  import userPromptSubmit from "../plugins/defaults/title-generate/hooks/user-prompt-submit.js";
56
- import { defaultTitleGeneratePlugin } from "../plugins/defaults/title-generate/register.js";
57
57
  import { runHook } from "../plugins/pipeline.js";
58
58
  import {
59
59
  registerPlugin,
@@ -18,10 +18,10 @@ import { beforeEach, describe, expect, test } from "bun:test";
18
18
 
19
19
  import { HOOKS } from "../plugin-api/constants.js";
20
20
  import type { PluginLogger, PostToolUseContext } from "../plugin-api/types.js";
21
+ import { defaultToolErrorPlugin } from "../plugins/defaults/index.js";
21
22
  import postToolUse, {
22
23
  TOOL_ERROR_NUDGE_TEXT,
23
24
  } from "../plugins/defaults/tool-error/hooks/post-tool-use.js";
24
- import { defaultToolErrorPlugin } from "../plugins/defaults/tool-error/register.js";
25
25
  import { runHook } from "../plugins/pipeline.js";
26
26
  import {
27
27
  registerPlugin,
@@ -16,8 +16,8 @@ import { beforeEach, describe, expect, test } from "bun:test";
16
16
 
17
17
  import { HOOKS } from "../plugin-api/constants.js";
18
18
  import type { PluginLogger, PostToolUseContext } from "../plugin-api/types.js";
19
+ import { defaultToolResultTruncatePlugin } from "../plugins/defaults/index.js";
19
20
  import postToolUse from "../plugins/defaults/tool-result-truncate/hooks/post-tool-use.js";
20
- import { defaultToolResultTruncatePlugin } from "../plugins/defaults/tool-result-truncate/register.js";
21
21
  import {
22
22
  truncateToolResult,
23
23
  TRUNCATION_SUFFIX,