@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
@@ -17,7 +17,9 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
17
17
 
18
18
  /**
19
19
  * IPC params for `addMessage()`. `role` is constrained to the
20
- * `MessageRole` union. `metadata` is a free-form record (validated
20
+ * renderable conversation roles (`user`/`assistant`): the messages store
21
+ * is UI-facing (`ConversationMessage`), so agent-context `system` rows are
22
+ * not accepted here. `metadata` is a free-form record (validated
21
23
  * downstream by `messageMetadataSchema` with a warn-and-store fallback).
22
24
  * `skipIndexing` and `clientMessageId` mirror `AddMessageOptions`.
23
25
  *
@@ -28,7 +30,7 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
28
30
  */
29
31
  const MemoryAddMessageParams = z.object({
30
32
  conversationId: z.string().min(1),
31
- role: z.enum(["user", "assistant", "system"]),
33
+ role: z.enum(["user", "assistant"]),
32
34
  content: z.string(),
33
35
  metadata: z.record(z.string(), z.unknown()).optional(),
34
36
  skipIndexing: z.boolean().optional(),
@@ -151,6 +151,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
151
151
 
152
152
  test("enqueues consolidate when v2 is on and no checkpoint exists", () => {
153
153
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
154
+ writeBuffer(15);
154
155
 
155
156
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
156
157
 
@@ -177,6 +178,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
177
178
 
178
179
  test("enqueues consolidate again once the interval elapses", () => {
179
180
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
181
+ writeBuffer(15);
180
182
 
181
183
  const now = Date.now();
182
184
  // Stamp checkpoint to >1h ago.
@@ -193,6 +195,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
193
195
 
194
196
  test("respects a custom consolidation_interval_hours value", () => {
195
197
  const config = buildConfig({ v2Enabled: true, intervalHours: 6 });
198
+ writeBuffer(15);
196
199
 
197
200
  const now = Date.now();
198
201
  // 4h elapsed — under the configured 6h interval.
@@ -216,6 +219,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
216
219
 
217
220
  test("v1 maintenance entries are suppressed when v2 is active", () => {
218
221
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
222
+ writeBuffer(15);
219
223
 
220
224
  // No checkpoints set — every entry would be due if it were scheduled.
221
225
  deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
@@ -426,3 +430,86 @@ describe("maybeEnqueueGraphMaintenanceJobs — buffer-size trigger", () => {
426
430
  expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
427
431
  });
428
432
  });
433
+
434
+ describe("maybeEnqueueGraphMaintenanceJobs — min buffer lines noop", () => {
435
+ test("skips scheduled consolidation when buffer is under 10 lines", () => {
436
+ // GIVEN v2 consolidation is enabled and the interval has elapsed
437
+ const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
438
+ const now = Date.now();
439
+ setMemoryCheckpoint(
440
+ CONSOLIDATE_CHECKPOINT_KEY,
441
+ String(now - 2 * 60 * 60 * 1000),
442
+ );
443
+ // AND the buffer has fewer than 10 lines
444
+ writeBuffer(5);
445
+
446
+ // WHEN the schedule runs
447
+ maybeEnqueueGraphMaintenanceJobs(config, now);
448
+
449
+ // THEN no consolidation job is enqueued
450
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
451
+ // AND the checkpoint advances so the skip doesn't re-fire next tick
452
+ expect(getMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY)).toBe(String(now));
453
+ });
454
+
455
+ test("allows scheduled consolidation when buffer has exactly 10 lines", () => {
456
+ // GIVEN v2 consolidation is enabled and the interval has elapsed
457
+ const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
458
+ const now = Date.now();
459
+ setMemoryCheckpoint(
460
+ CONSOLIDATE_CHECKPOINT_KEY,
461
+ String(now - 2 * 60 * 60 * 1000),
462
+ );
463
+ // AND the buffer has exactly 10 lines
464
+ writeBuffer(10);
465
+
466
+ // WHEN the schedule runs
467
+ maybeEnqueueGraphMaintenanceJobs(config, now);
468
+
469
+ // THEN consolidation is enqueued
470
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
471
+ });
472
+
473
+ test("skips scheduled consolidation when buffer file is missing", () => {
474
+ // GIVEN v2 consolidation is enabled and the interval has elapsed
475
+ const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
476
+ const now = Date.now();
477
+ setMemoryCheckpoint(
478
+ CONSOLIDATE_CHECKPOINT_KEY,
479
+ String(now - 2 * 60 * 60 * 1000),
480
+ );
481
+ // AND no buffer file exists (0 lines)
482
+ removeBuffer();
483
+
484
+ // WHEN the schedule runs
485
+ maybeEnqueueGraphMaintenanceJobs(config, now);
486
+
487
+ // THEN no consolidation job is enqueued
488
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
489
+ // AND the checkpoint advances
490
+ expect(getMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY)).toBe(String(now));
491
+ });
492
+
493
+ test("size-based trigger is independent of the min-lines noop", () => {
494
+ // GIVEN the time-based interval has elapsed but the buffer has fewer
495
+ // than 10 lines, while exceeding the configured maxBufferLines threshold
496
+ const config = buildConfig({
497
+ v2Enabled: true,
498
+ intervalHours: 1,
499
+ maxBufferLines: 5,
500
+ });
501
+ const now = Date.now();
502
+ setMemoryCheckpoint(
503
+ CONSOLIDATE_CHECKPOINT_KEY,
504
+ String(now - 2 * 60 * 60 * 1000),
505
+ );
506
+ writeBuffer(8);
507
+
508
+ // WHEN the schedule runs
509
+ maybeEnqueueGraphMaintenanceJobs(config, now);
510
+
511
+ // THEN consolidation is enqueued via the size trigger despite the
512
+ // time-based schedule being nooped (8 < 10 min lines, but 8 >= 5 max)
513
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
514
+ });
515
+ });
@@ -277,6 +277,7 @@ export interface MessageRow {
277
277
  content: string;
278
278
  createdAt: number;
279
279
  metadata: string | null;
280
+ clientMessageId: string | null;
280
281
  }
281
282
 
282
283
  const parseMessage = createRowMapper<typeof messages.$inferSelect, MessageRow>({
@@ -286,6 +287,7 @@ const parseMessage = createRowMapper<typeof messages.$inferSelect, MessageRow>({
286
287
  content: "content",
287
288
  createdAt: "createdAt",
288
289
  metadata: "metadata",
290
+ clientMessageId: "clientMessageId",
289
291
  });
290
292
 
291
293
  export type ConversationCreateType = "standard" | "background" | "scheduled";
@@ -1903,21 +1905,30 @@ export function listActiveInferenceProfileSessions(
1903
1905
  }
1904
1906
 
1905
1907
  /**
1906
- * Resolve the per-turn inference-profile override from an already-loaded
1907
- * conversation row. Returns the row's `inferenceProfile` for interactive
1908
- * conversations, `undefined` for automation threads (subagent fan-out,
1909
- * scheduled tasks, update bulletins) so they run on the workspace defaults
1910
- * rather than inheriting an interactive override.
1911
- *
1912
- * Prefer this row-based form when the caller already needs to read the
1913
- * conversation row for other reasons (e.g. the agent loop's title check).
1908
+ * The conversation fields needed to resolve a per-turn inference-profile
1909
+ * override. Satisfied by both the DB {@link ConversationRow} and the live
1910
+ * in-memory `Conversation`, so callers can derive the override from whichever
1911
+ * representation they already hold without a redundant row fetch.
1912
+ */
1913
+ export interface OverrideProfileFields {
1914
+ conversationType?: string | null;
1915
+ inferenceProfile?: string | null;
1916
+ inferenceProfileExpiresAt?: number | null;
1917
+ }
1918
+
1919
+ /**
1920
+ * Resolve the per-turn inference-profile override from a conversation's
1921
+ * fields. Returns the `inferenceProfile` for interactive conversations,
1922
+ * `undefined` for automation threads (subagent fan-out, scheduled tasks,
1923
+ * update bulletins) so they run on the workspace defaults rather than
1924
+ * inheriting an interactive override.
1914
1925
  */
1915
- export function getConversationOverrideProfileFromRow(
1916
- conv: ConversationRow | null,
1926
+ export function resolveOverrideProfile(
1927
+ fields: OverrideProfileFields | null,
1917
1928
  ): string | undefined {
1918
1929
  if (
1919
- conv?.conversationType === "background" ||
1920
- conv?.conversationType === "scheduled"
1930
+ fields?.conversationType === "background" ||
1931
+ fields?.conversationType === "scheduled"
1921
1932
  ) {
1922
1933
  return undefined;
1923
1934
  }
@@ -1931,24 +1942,23 @@ export function getConversationOverrideProfileFromRow(
1931
1942
  // Without this, a session at the exact-expiry millisecond would be served
1932
1943
  // for one extra turn here while being cleared by the reaper.
1933
1944
  if (
1934
- conv?.inferenceProfileExpiresAt != null &&
1935
- conv.inferenceProfileExpiresAt <= Date.now()
1945
+ fields?.inferenceProfileExpiresAt != null &&
1946
+ fields.inferenceProfileExpiresAt <= Date.now()
1936
1947
  ) {
1937
1948
  return undefined;
1938
1949
  }
1939
- return conv?.inferenceProfile ?? undefined;
1950
+ return fields?.inferenceProfile ?? undefined;
1940
1951
  }
1941
1952
 
1942
1953
  /**
1943
1954
  * Resolve the per-turn inference-profile override by conversation id.
1944
- * Convenience wrapper around `getConversationOverrideProfileFromRow` for
1945
- * standalone callers (e.g. subagent spawn, opportunity-wake) that don't
1946
- * already have the row in hand.
1955
+ * Convenience wrapper for standalone callers (e.g. subagent spawn,
1956
+ * opportunity-wake) that don't already have the conversation in hand.
1947
1957
  */
1948
1958
  export function getConversationOverrideProfile(
1949
1959
  conversationId: string,
1950
1960
  ): string | undefined {
1951
- return getConversationOverrideProfileFromRow(getConversation(conversationId));
1961
+ return resolveOverrideProfile(getConversation(conversationId));
1952
1962
  }
1953
1963
 
1954
1964
  export function setLastNotifiedInferenceProfile(
@@ -15,6 +15,7 @@ import { memoryCheckpoints } from "./schema.js";
15
15
  export const CK_ITEM_COUNT = "conversation_starters:item_count_at_last_gen";
16
16
  export const CK_BATCH = "conversation_starters:generation_batch";
17
17
  export const CK_LAST_GEN_AT = "conversation_starters:last_gen_at";
18
+ export const CK_LAST_ATTEMPT_AT = "conversation_starters:last_attempt_at";
18
19
 
19
20
  export function checkpointKey(base: string, scopeId: string): string {
20
21
  return `${base}:${scopeId}`;
@@ -41,6 +41,7 @@ import {
41
41
  migrate230AcpSessionHistory,
42
42
  migrate231RepairMemoryGraphEventDates,
43
43
  migrateA2ATasks,
44
+ migrateAcpSessionHistoryCwd,
44
45
  migrateActivationState,
45
46
  migrateActivationStateFkCascade,
46
47
  migrateAddConversationInferenceProfile,
@@ -478,6 +479,7 @@ export function initializeDb(): void {
478
479
  migrateScheduleSourceConversation,
479
480
  migrateMessagesRoleCreatedAtIndex,
480
481
  createAuthFallbackEventsTable,
482
+ migrateAcpSessionHistoryCwd,
481
483
  ];
482
484
 
483
485
  // Run each migration step, catching and logging individual failures so one
@@ -22,6 +22,7 @@ import {
22
22
  checkpointKey,
23
23
  CK_BATCH,
24
24
  CK_ITEM_COUNT,
25
+ CK_LAST_ATTEMPT_AT,
25
26
  CK_LAST_GEN_AT,
26
27
  countActiveMemoryNodes,
27
28
  getCheckpointValue,
@@ -336,6 +337,7 @@ Bad → Good (incomplete phrase → complete):
336
337
  config: {
337
338
  callSite: "conversationStarters" as const,
338
339
  max_tokens: 2048,
340
+ temperature: 0.7,
339
341
  tool_choice: {
340
342
  type: "tool" as const,
341
343
  name: "store_conversation_starters",
@@ -395,18 +397,27 @@ export async function generateConversationStartersJob(
395
397
  const db = getDb();
396
398
  const now = Date.now();
397
399
 
400
+ // Record attempt time so the route handler's cooldown prevents rapid retries
401
+ // even when generation produces 0 valid starters.
402
+ upsertCheckpoint(
403
+ checkpointKey(CK_LAST_ATTEMPT_AT, scopeId),
404
+ String(now),
405
+ now,
406
+ );
407
+
398
408
  const starters = await generateStarters(scopeId);
399
409
  if (starters.length === 0) {
400
410
  log.info({ scopeId }, "No conversation starters generated");
401
411
 
402
- // Sync checkpoints so both `staleByAge` and `checkpointAhead` clear.
412
+ // Sync the item count checkpoint so `checkpointAhead` clears, but do NOT
413
+ // update `CK_LAST_GEN_AT` — the stale TTL should trigger a retry on the
414
+ // next GET rather than blocking retries for the full TTL window.
403
415
  const totalActive = countActiveMemoryNodes(scopeId);
404
416
  upsertCheckpoint(
405
417
  checkpointKey(CK_ITEM_COUNT, scopeId),
406
418
  String(totalActive),
407
419
  now,
408
420
  );
409
- upsertCheckpoint(checkpointKey(CK_LAST_GEN_AT, scopeId), String(now), now);
410
421
  return;
411
422
  }
412
423
 
@@ -143,9 +143,8 @@ afterAll(() => {
143
143
  // `getWorkspaceDir()` resolves to the tmpdir.
144
144
  const { DEFAULT_CONFIG } = await import("../../../config/defaults.js");
145
145
  const { getDb } = await import("../../db-connection.js");
146
- const { resetDbForTesting } = await import(
147
- "../../../__tests__/db-test-helpers.js"
148
- );
146
+ const { resetDbForTesting } =
147
+ await import("../../../__tests__/db-test-helpers.js");
149
148
  const { initializeDb } = await import("../../db-init.js");
150
149
  const { memoryEmbeddings, memoryJobs } = await import("../../schema.js");
151
150
  const { claimMemoryJobs } = await import("../../jobs-store.js");
@@ -187,12 +186,14 @@ function makeJob(payload: Record<string, unknown>): MemoryJob {
187
186
 
188
187
  beforeEach(() => {
189
188
  resetDbForTesting();
189
+ // The first run pays the full cold-start migration chain; bump the hook
190
+ // timeout above bun's 5s default so the leading test doesn't flake in CI.
190
191
  initializeDb();
191
192
  embedWithBackendCalls.length = 0;
192
193
  upsertCalls.length = 0;
193
194
  deleteCalls.length = 0;
194
195
  _resetQdrantBreaker();
195
- });
196
+ }, 30_000);
196
197
 
197
198
  afterEach(() => {
198
199
  // Clean up any pages written between tests so each scenario starts fresh.
@@ -8,6 +8,7 @@ import {
8
8
  diskPressureBackgroundSkipLogFields,
9
9
  shouldLogDiskPressureBackgroundSkip,
10
10
  } from "../daemon/disk-pressure-background-gate.js";
11
+ import { maintainJob as memoryV3MaintainJob } from "../plugins/defaults/memory-v3-shadow/maintain-job.js";
11
12
  import { getLogger } from "../util/logger.js";
12
13
  import { getWorkspaceDir } from "../util/platform.js";
13
14
  import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
@@ -87,7 +88,6 @@ import {
87
88
  memoryV2ConsolidateJob,
88
89
  } from "./v2/consolidation-job.js";
89
90
  import { memoryV2SweepJob } from "./v2/sweep-job.js";
90
- import { maintainJob as memoryV3MaintainJob } from "./v3/maintain-job.js";
91
91
 
92
92
  const log = getLogger("memory-jobs-worker");
93
93
 
@@ -95,6 +95,16 @@ const AUTOMATIC_CONSOLIDATION_JOB_PAYLOAD = {
95
95
  trigger: MEMORY_V2_CONSOLIDATION_JOB_TRIGGERS.automatic,
96
96
  } as const;
97
97
 
98
+ /**
99
+ * Minimum buffer entries required for a scheduled consolidation run. The
100
+ * time-based schedule noops when `memory/buffer.md` has fewer non-empty lines
101
+ * than this threshold — the LLM cost of a full consolidation pass outweighs
102
+ * the benefit when the buffer is nearly empty. Mirrors the heartbeat
103
+ * max-consecutive-runs skip pattern. Manual "Run now" and the size-based
104
+ * trigger are not affected.
105
+ */
106
+ export const MIN_BUFFER_LINES_FOR_CONSOLIDATION = 10;
107
+
98
108
  /**
99
109
  * V1 job types that read or write the v1 Qdrant collection via
100
110
  * `getQdrantClient()`. When `memory.v2.enabled` is true, the v1 client is
@@ -815,6 +825,20 @@ export function maybeEnqueueGraphMaintenanceJobs(
815
825
  for (const { key, intervalMs, jobType } of schedule) {
816
826
  const lastRun = parseInt(getMemoryCheckpoint(key) ?? "0", 10);
817
827
  if (nowMs - lastRun >= intervalMs) {
828
+ // Noop scheduled consolidation when the buffer has too few entries to
829
+ // justify an LLM run — mirrors the heartbeat max-consecutive-runs skip.
830
+ // The checkpoint advances so the next check fires after the regular
831
+ // interval. Manual "Run now" is unaffected (routes layer, not schedule).
832
+ if (jobType === consolidateEntry.jobType) {
833
+ const bufferPath = join(getWorkspaceDir(), "memory", "buffer.md");
834
+ if (countBufferLines(bufferPath) < MIN_BUFFER_LINES_FOR_CONSOLIDATION) {
835
+ log.debug(
836
+ "Scheduled consolidation skipped: buffer under minimum line threshold",
837
+ );
838
+ setMemoryCheckpoint(key, String(nowMs));
839
+ continue;
840
+ }
841
+ }
818
842
  const payload =
819
843
  jobType === consolidateEntry.jobType
820
844
  ? AUTOMATIC_CONSOLIDATION_JOB_PAYLOAD
@@ -0,0 +1,36 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+ import { withCrashRecovery } from "./validate-migration-state.js";
4
+
5
+ const CHECKPOINT_KEY = "migration_acp_session_history_cwd_v1";
6
+
7
+ const TABLE = "acp_session_history";
8
+ const COLUMN = "cwd";
9
+
10
+ /**
11
+ * Add a nullable `cwd TEXT` column to `acp_session_history`.
12
+ *
13
+ * Records the working directory the ACP agent process was spawned with so
14
+ * that a persisted session can later be resumed (`session/load` /
15
+ * `session/resume` both require the original cwd to reconstruct params).
16
+ *
17
+ * `NULL` for rows persisted before this migration ran — legacy sessions
18
+ * simply are not resumable.
19
+ *
20
+ * Idempotent — the PRAGMA guard makes re-running a no-op once the column
21
+ * exists.
22
+ */
23
+ export function migrateAcpSessionHistoryCwd(database: DrizzleDb): void {
24
+ withCrashRecovery(database, CHECKPOINT_KEY, () => {
25
+ const raw = getSqliteFrom(database);
26
+
27
+ const columns = raw.query(`PRAGMA table_info(${TABLE})`).all() as Array<{
28
+ name: string;
29
+ }>;
30
+ const columnNames = new Set(columns.map((c) => c.name));
31
+
32
+ if (!columnNames.has(COLUMN)) {
33
+ raw.exec(`ALTER TABLE ${TABLE} ADD COLUMN ${COLUMN} TEXT`);
34
+ }
35
+ });
36
+ }
@@ -259,6 +259,7 @@ export { migrateScheduleScriptTimeout } from "./269-schedule-script-timeout.js";
259
259
  export { migrateMessagesRoleCreatedAtIndex } from "./270-messages-role-created-at-index.js";
260
260
  export { migrateScheduleSourceConversation } from "./270-schedule-source-conversation.js";
261
261
  export { createAuthFallbackEventsTable } from "./271-create-auth-fallback-events.js";
262
+ export { migrateAcpSessionHistoryCwd } from "./272-acp-session-history-cwd.js";
262
263
  export {
263
264
  MIGRATION_REGISTRY,
264
265
  type MigrationRegistryEntry,
@@ -20,6 +20,10 @@ export const acpSessionHistory = sqliteTable(
20
20
  stopReason: text("stop_reason"),
21
21
  error: text("error"),
22
22
  eventLogJson: text("event_log_json").notNull().default("[]"),
23
+ // Working directory the agent process was spawned with. Required to
24
+ // resume a persisted session; null for rows written before migration
25
+ // 272 (those sessions are not resumable).
26
+ cwd: text("cwd"),
23
27
  },
24
28
  (table) => [
25
29
  index("idx_acp_session_history_started_at").on(table.startedAt),
@@ -121,7 +121,7 @@ mock.module("../../../config/assistant-feature-flags.js", () => ({
121
121
 
122
122
  let renderedHealth = "";
123
123
  let computeThrows = false;
124
- mock.module("../../v3/health.js", () => ({
124
+ mock.module("../../../plugins/defaults/memory-v3-shadow/health.js", () => ({
125
125
  computeV3Health: () => {
126
126
  if (computeThrows) throw new Error("simulated health compute failure");
127
127
  return {};
@@ -129,12 +129,12 @@ mock.module("../../v3/health.js", () => ({
129
129
  renderV3Health: () => renderedHealth,
130
130
  }));
131
131
 
132
- mock.module("../../v3/tree.js", () => ({
132
+ mock.module("../../../plugins/defaults/memory-v3-shadow/tree.js", () => ({
133
133
  loadLeafTree: async () => ({ leaves: new Map(), byPage: new Map() }),
134
134
  resolveDataDir: () => "/tmp/v3-data-stub",
135
135
  }));
136
136
 
137
- mock.module("../../v3/core.js", () => ({
137
+ mock.module("../../../plugins/defaults/memory-v3-shadow/core.js", () => ({
138
138
  loadCore: async () => new Set<string>(),
139
139
  }));
140
140
 
@@ -63,6 +63,19 @@ import { dirname, join } from "node:path";
63
63
 
64
64
  import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
65
65
  import type { AssistantConfig } from "../../config/types.js";
66
+ import { loadCore } from "../../plugins/defaults/memory-v3-shadow/core.js";
67
+ import {
68
+ computeV3Health,
69
+ renderV3Health,
70
+ } from "../../plugins/defaults/memory-v3-shadow/health.js";
71
+ import {
72
+ loadLeafTree,
73
+ resolveDataDir,
74
+ } from "../../plugins/defaults/memory-v3-shadow/tree.js";
75
+ import type {
76
+ LeafPath,
77
+ Slug,
78
+ } from "../../plugins/defaults/memory-v3-shadow/types.js";
66
79
  import { runBackgroundJob } from "../../runtime/background-job-runner.js";
67
80
  import { getLogger } from "../../util/logger.js";
68
81
  import { getWorkspaceDir } from "../../util/platform.js";
@@ -74,10 +87,6 @@ import {
74
87
  type MemoryJobType,
75
88
  } from "../jobs-store.js";
76
89
  import { getPageIndex } from "../v2/page-index.js";
77
- import { loadCore } from "../v3/core.js";
78
- import { computeV3Health, renderV3Health } from "../v3/health.js";
79
- import { loadLeafTree, resolveDataDir } from "../v3/tree.js";
80
- import type { LeafPath, Slug } from "../v3/types.js";
81
90
  import { MEMORY_V2_CONSOLIDATION_SOURCE } from "./constants.js";
82
91
  import { resolveConsolidationPrompt } from "./prompts/consolidation.js";
83
92
 
@@ -20,10 +20,14 @@ export const HOOKS = {
20
20
  SHUTDOWN: "shutdown",
21
21
  /** Fires once per user turn, immediately before the agent loop receives `runMessages`. */
22
22
  USER_PROMPT_SUBMIT: "user-prompt-submit",
23
+ /** Fires immediately before each provider call. A hook may edit the outbound request (e.g. the system prompt) and opt the turn into deferred output streaming. */
24
+ PRE_MODEL_CALL: "pre-model-call",
23
25
  /** Fires once per tool result, after the tool returns and before the result is sent to the provider. */
24
26
  POST_TOOL_USE: "post-tool-use",
25
27
  /** Fires when the model yields a response with no tool calls — the run's stop boundary. Decides whether to stop or continue with a follow-up turn. */
26
28
  STOP: "stop",
29
+ /** Fires for each finalized assistant message, before it is persisted/streamed-final. A hook may transform the message content. */
30
+ ASSISTANT_MESSAGE: "assistant-message",
27
31
  } as const;
28
32
 
29
33
  /** Union of every hook name declared in {@link HOOKS}. */
@@ -24,31 +24,32 @@
24
24
  * - {@link PluginShutdownContext} — passed to `shutdown` hook at teardown
25
25
  * - {@link UserPromptSubmitContext} — passed to `user-prompt-submit` hook,
26
26
  * fired immediately before the agent loop receives a user's prompt
27
+ * - {@link PreModelCallContext} — passed to `pre-model-call` hook, fired
28
+ * before each provider call to edit the request / defer output streaming
27
29
  * - {@link PostToolUseContext} — passed to `post-tool-use` hook, fired once
28
30
  * per tool result before it joins the provider-bound history
29
31
  * - {@link StopContext} — passed to `stop` hook, fired when the model yields
30
32
  * a response with no tool calls
33
+ * - {@link AssistantMessageContext} — passed to `assistant-message` hook,
34
+ * fired for each finalized assistant message to transform its content
31
35
  * - {@link PluginHookFn} — signature every lifecycle hook implements
32
36
  * - {@link PluginLogger} — pino-compatible logger shape on the contexts
33
37
  * - {@link ToolDefinition} — author-facing tool spec (default-export shape
34
38
  * for both plugin tool files and workspace tool files)
35
39
  * - {@link ToolContext} — passed to a plugin tool's `execute` method
36
40
  * - {@link ToolExecutionResult} — return shape of a plugin tool's `execute`
37
- *
38
- * Pipeline-argument types (`MemoryArgs`, `CompactionArgs`, etc.) currently
39
- * live in `assistant/src/plugins/types.ts` and have not yet migrated into
40
- * this package. A follow-up PR will move them into this surface as the
41
- * per-pipeline schemas stabilize.
42
41
  */
43
42
 
44
43
  export type { HookName } from "./constants.js";
45
44
  export { HOOKS } from "./constants.js";
46
45
  export type {
46
+ AssistantMessageContext,
47
47
  PluginHookFn,
48
48
  PluginInitContext,
49
49
  PluginLogger,
50
50
  PluginShutdownContext,
51
51
  PostToolUseContext,
52
+ PreModelCallContext,
52
53
  StopContext,
53
54
  StopDecision,
54
55
  ToolContext,
@@ -4,6 +4,7 @@
4
4
  * removing is breaking and gated on a major bump.
5
5
  */
6
6
 
7
+ import type { LLMCallSite } from "../config/schemas/llm.js";
7
8
  import type {
8
9
  ContentBlock,
9
10
  Message,
@@ -51,8 +52,10 @@ export interface PluginLogger {
51
52
  * - `init` — {@link PluginInitContext}
52
53
  * - `shutdown` — {@link PluginShutdownContext}
53
54
  * - `user-prompt-submit` — {@link UserPromptSubmitContext}
55
+ * - `pre-model-call` — {@link PreModelCallContext}
54
56
  * - `post-tool-use` — {@link PostToolUseContext}
55
57
  * - `stop` — {@link StopContext}
58
+ * - `assistant-message` — {@link AssistantMessageContext}
56
59
  */
57
60
  export type PluginHookFn<TCtx = unknown> = (ctx: TCtx) => Promise<TCtx | void>;
58
61
 
@@ -288,3 +291,75 @@ export interface StopContext {
288
291
  */
289
292
  readonly logger: PluginLogger;
290
293
  }
294
+
295
+ // ─── Pre-model-call hook context ─────────────────────────────────────────────
296
+
297
+ /**
298
+ * Context passed to the `pre-model-call` hook. Fires immediately before each
299
+ * provider call — once per model call within a turn, including tool-result
300
+ * follow-up calls. Because it runs for every provider call (background, subagent,
301
+ * and compaction work can share a conversation), hooks MUST self-gate on
302
+ * {@link callSite} / {@link conversationId} before acting.
303
+ *
304
+ * A hook may edit the outbound request by replacing {@link systemPrompt}, and may
305
+ * opt this turn into deferred output streaming via {@link deferAssistantOutput}.
306
+ * Mutate the context in place or return a new one; throwing is contained by the
307
+ * loop (the call proceeds with the original request).
308
+ */
309
+ export interface PreModelCallContext {
310
+ /** Conversation ID the call belongs to. */
311
+ readonly conversationId: string;
312
+ /**
313
+ * The call site this provider call serves — `"mainAgent"` for the user-facing
314
+ * reply, or a background/utility site. Omitted by call sites that don't tag one.
315
+ */
316
+ readonly callSite?: LLMCallSite;
317
+ /**
318
+ * The system prompt about to be sent. A hook may replace it (e.g. strip or
319
+ * append a section); the loop sends the resulting value.
320
+ */
321
+ systemPrompt: string | undefined;
322
+ /**
323
+ * Seeded `false`. When a hook sets it `true`, the loop suppresses this turn's
324
+ * live assistant `text_delta` stream; an `assistant-message` hook is then
325
+ * expected to produce the text the client sees (emitted once, after the reply
326
+ * is finalized). Lets a plugin replace streamed output wholesale — e.g.
327
+ * redaction that needs the full message — instead of leaking the raw stream.
328
+ */
329
+ deferAssistantOutput: boolean;
330
+ /** Logger scoped to the current turn (tag structured fields with `{ plugin }`). */
331
+ readonly logger: PluginLogger;
332
+ }
333
+
334
+ // ─── Assistant-message hook context ──────────────────────────────────────────
335
+
336
+ /**
337
+ * Context passed to the `assistant-message` hook. Fires for each finalized
338
+ * assistant message — once per model call, at the message-complete boundary —
339
+ * before the message is persisted and (if deferred) streamed-final. Unlike
340
+ * {@link StopContext}'s read-only `responseContent` (which exists for the stop
341
+ * decision), {@link content} is mutable: the loop adopts the hook's result as the
342
+ * persisted and streamed message.
343
+ *
344
+ * Fires on tool-bearing turns too (a reply can carry both text and `tool_use`),
345
+ * so a hook should transform only the blocks it owns and leave others — notably
346
+ * `tool_use` — intact. Runs for every finalized message regardless of call site;
347
+ * hooks MUST self-gate on {@link callSite} / {@link conversationId}. Mutate in
348
+ * place or return a new context; throwing is contained by the loop (the original
349
+ * content is kept).
350
+ */
351
+ export interface AssistantMessageContext {
352
+ /** Conversation ID the message belongs to. */
353
+ readonly conversationId: string;
354
+ /** The call site this message serves — `"mainAgent"` for the user-facing reply. */
355
+ readonly callSite?: LLMCallSite;
356
+ /**
357
+ * The finalized message content. Mutable — transform the text blocks and leave
358
+ * `tool_use` (and other non-text blocks) intact.
359
+ */
360
+ content: ContentBlock[];
361
+ /** Provider-reported stop reason for the turn, when reported. */
362
+ readonly stopReason: string | null | undefined;
363
+ /** Logger scoped to the current turn (tag structured fields with `{ plugin }`). */
364
+ readonly logger: PluginLogger;
365
+ }