@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62

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 (490) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/docs/activation-funnel-telemetry.md +310 -0
  4. package/examples/plugins/echo/README.md +61 -60
  5. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  6. package/examples/plugins/echo/hooks/stop.ts +16 -0
  7. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  8. package/examples/plugins/echo/package.json +1 -2
  9. package/examples/plugins/echo/src/emit.ts +19 -0
  10. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  11. package/openapi.yaml +273 -121
  12. package/package.json +2 -2
  13. package/scripts/sync-llm-catalog.ts +2 -0
  14. package/src/__tests__/activation-early-marking.test.ts +120 -0
  15. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  16. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  17. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  18. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  19. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  21. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  22. package/src/__tests__/agent-loop.test.ts +441 -96
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  24. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  25. package/src/__tests__/anthropic-provider.test.ts +23 -8
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  27. package/src/__tests__/app-control-flow.test.ts +1 -1
  28. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  29. package/src/__tests__/app-executors.test.ts +132 -0
  30. package/src/__tests__/approval-cascade.test.ts +6 -5
  31. package/src/__tests__/approval-routes-http.test.ts +4 -1
  32. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  33. package/src/__tests__/build-persisted-content.test.ts +75 -1
  34. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  35. package/src/__tests__/channel-approvals.test.ts +1 -1
  36. package/src/__tests__/compaction-circuit.test.ts +258 -0
  37. package/src/__tests__/compaction-direct.test.ts +146 -0
  38. package/src/__tests__/compaction-events.test.ts +7 -7
  39. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  40. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  41. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  42. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  43. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  48. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  49. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  50. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  51. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  52. package/src/__tests__/conversation-error.test.ts +15 -1
  53. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  54. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  55. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  56. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  57. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  58. package/src/__tests__/conversation-queue.test.ts +10 -10
  59. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  60. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  61. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  62. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  63. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  64. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  65. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  66. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  67. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  68. package/src/__tests__/conversation-undo.test.ts +2 -2
  69. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  70. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  71. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  72. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  73. package/src/__tests__/corrected-target.test.ts +93 -0
  74. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
  75. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  76. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  77. package/src/__tests__/db-acp-history.test.ts +101 -0
  78. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  79. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  80. package/src/__tests__/empty-response-hook.test.ts +1 -1
  81. package/src/__tests__/file-write-tool.test.ts +63 -0
  82. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  83. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  84. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  85. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  87. package/src/__tests__/heartbeat-service.test.ts +1 -0
  88. package/src/__tests__/history-repair-hook.test.ts +5 -1
  89. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  90. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  91. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  92. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  93. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  94. package/src/__tests__/injector-background-turn.test.ts +13 -23
  95. package/src/__tests__/injector-chain.test.ts +268 -44
  96. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  97. package/src/__tests__/injector-document-comments.test.ts +97 -114
  98. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  99. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  100. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  102. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  103. package/src/__tests__/llm-resolver.test.ts +73 -0
  104. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  105. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  106. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  107. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  108. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  109. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  110. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  111. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  112. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  113. package/src/__tests__/plugin-registry.test.ts +3 -49
  114. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  115. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  116. package/src/__tests__/plugin-types.test.ts +0 -70
  117. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  118. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  119. package/src/__tests__/published-app-updater.test.ts +138 -0
  120. package/src/__tests__/reaction-persistence.test.ts +1 -1
  121. package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  123. package/src/__tests__/server-history-render.test.ts +39 -0
  124. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  125. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  126. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  127. package/src/__tests__/subagent-detail.test.ts +25 -7
  128. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  129. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  130. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  131. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  132. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  133. package/src/__tests__/title-generate-hook.test.ts +5 -1
  134. package/src/__tests__/tool-error-hook.test.ts +1 -1
  135. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  136. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  137. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  138. package/src/__tests__/web-fetch.test.ts +45 -0
  139. package/src/acp/__tests__/agent-process.test.ts +161 -0
  140. package/src/acp/__tests__/client-handler.test.ts +40 -0
  141. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  142. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  143. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  144. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  145. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  146. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  147. package/src/acp/agent-process.ts +61 -1
  148. package/src/acp/auto-install.test.ts +280 -0
  149. package/src/acp/auto-install.ts +232 -0
  150. package/src/acp/client-handler.ts +31 -0
  151. package/src/acp/prepare-agent-env.ts +80 -27
  152. package/src/acp/resolve-agent.test.ts +188 -28
  153. package/src/acp/resolve-agent.ts +119 -42
  154. package/src/acp/resume-hint.ts +23 -0
  155. package/src/acp/session-manager.ts +507 -73
  156. package/src/agent/compaction-circuit.ts +60 -102
  157. package/src/agent/loop.ts +403 -251
  158. package/src/api/events/tool-result.ts +9 -0
  159. package/src/api/events/tool-use-start.ts +7 -0
  160. package/src/api/responses/conversation-message.ts +72 -13
  161. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  162. package/src/approvals/guardian-request-resolvers.ts +27 -1
  163. package/src/background-wake/next-wake.ts +1 -0
  164. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  165. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  166. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  167. package/src/cli/commands/memory-v3.ts +57 -199
  168. package/src/cli/commands/plugins.ts +43 -37
  169. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  170. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  171. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  172. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  173. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  174. package/src/cli/lib/install-from-github.ts +797 -60
  175. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  176. package/src/cli/lib/plugin-details.ts +416 -0
  177. package/src/cli/lib/plugin-marketplace.ts +215 -0
  178. package/src/cli/lib/search-plugins.ts +201 -29
  179. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  180. package/src/config/acp-defaults.test.ts +10 -0
  181. package/src/config/acp-defaults.ts +9 -3
  182. package/src/config/acp-schema.ts +1 -7
  183. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  184. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  185. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  186. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  187. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  188. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  189. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  190. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  191. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  192. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  193. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  194. package/src/config/bundled-tool-registry.ts +4 -0
  195. package/src/config/call-site-defaults.ts +0 -3
  196. package/src/config/feature-flag-registry.json +10 -56
  197. package/src/config/llm-resolver.ts +39 -7
  198. package/src/config/loader.ts +5 -22
  199. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  200. package/src/config/schemas/call-site-catalog.ts +0 -21
  201. package/src/config/schemas/heartbeat.ts +9 -0
  202. package/src/config/schemas/llm.ts +17 -3
  203. package/src/config/schemas/memory-v3.ts +58 -8
  204. package/src/config/seed-inference-profiles.ts +18 -0
  205. package/src/context/compactor.ts +22 -4
  206. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  207. package/src/context/strip-injections.ts +8 -2
  208. package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
  209. package/src/daemon/conversation-agent-loop.ts +308 -1297
  210. package/src/daemon/conversation-error.ts +31 -4
  211. package/src/daemon/conversation-history.ts +1 -1
  212. package/src/daemon/conversation-lifecycle.ts +11 -255
  213. package/src/daemon/conversation-media-retry.ts +19 -6
  214. package/src/daemon/conversation-messaging.ts +17 -0
  215. package/src/daemon/conversation-process.ts +22 -141
  216. package/src/daemon/conversation-queue-manager.ts +8 -0
  217. package/src/daemon/conversation-registry.ts +159 -0
  218. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  219. package/src/daemon/conversation-store.ts +9 -90
  220. package/src/daemon/conversation-surfaces.ts +165 -11
  221. package/src/daemon/conversation-workspace.ts +17 -0
  222. package/src/daemon/conversation.ts +444 -62
  223. package/src/daemon/disk-pressure-policy.ts +0 -1
  224. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  225. package/src/daemon/handlers/config-channels.ts +11 -2
  226. package/src/daemon/handlers/conversations.ts +3 -1
  227. package/src/daemon/handlers/shared.ts +9 -1
  228. package/src/daemon/handlers/skills.ts +4 -1
  229. package/src/daemon/host-app-control-proxy.ts +72 -57
  230. package/src/daemon/host-proxy-preactivation.ts +1 -3
  231. package/src/daemon/lifecycle.ts +21 -7
  232. package/src/daemon/persist-unsendable-image.ts +62 -25
  233. package/src/daemon/process-message.ts +1 -1
  234. package/src/daemon/server.ts +2 -0
  235. package/src/daemon/tool-side-effects.ts +15 -0
  236. package/src/daemon/wake-conversation-ops.ts +269 -0
  237. package/src/embedded/plugin-api.ts +2 -2
  238. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  239. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  240. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  241. package/src/heartbeat/heartbeat-service.ts +26 -0
  242. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  243. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  244. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  245. package/src/ipc/skill-routes/memory.ts +4 -2
  246. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  247. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  248. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  249. package/src/memory/activation-session-store.ts +43 -0
  250. package/src/memory/conversation-crud.ts +29 -19
  251. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  252. package/src/memory/db-init.ts +6 -0
  253. package/src/memory/embedding-backend.ts +5 -22
  254. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  255. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  256. package/src/memory/jobs-worker.ts +25 -1
  257. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  258. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  259. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  260. package/src/memory/migrations/index.ts +3 -0
  261. package/src/memory/onboarding-events-store.ts +66 -18
  262. package/src/memory/schema/acp.ts +4 -0
  263. package/src/memory/schema/infrastructure.ts +13 -0
  264. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  265. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  266. package/src/memory/v2/consolidation-job.ts +2 -63
  267. package/src/memory/v2/types.ts +5 -0
  268. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  269. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  270. package/src/notifications/access-request-copy.ts +298 -0
  271. package/src/notifications/adapters/slack.ts +3 -3
  272. package/src/notifications/adapters/telegram.ts +9 -2
  273. package/src/notifications/copy-composer.ts +49 -267
  274. package/src/notifications/decision-engine.ts +16 -35
  275. package/src/notifications/home-feed-side-effect.ts +1 -6
  276. package/src/plugin-api/constants.ts +4 -0
  277. package/src/plugin-api/index.ts +40 -11
  278. package/src/plugin-api/types.ts +108 -0
  279. package/src/plugins/defaults/compaction/compact.ts +106 -0
  280. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  281. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  282. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  283. package/src/plugins/defaults/compaction/package.json +1 -2
  284. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  285. package/src/plugins/defaults/empty-response/package.json +0 -1
  286. package/src/plugins/defaults/history-repair/package.json +0 -1
  287. package/src/plugins/defaults/index.ts +156 -26
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  291. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  297. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  304. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  306. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  307. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  308. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  309. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  310. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  311. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  312. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  313. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  314. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  315. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  316. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  317. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  318. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  319. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  321. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  322. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  323. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  324. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  325. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  326. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  328. package/src/plugins/defaults/title-generate/package.json +0 -1
  329. package/src/plugins/defaults/tool-error/package.json +0 -1
  330. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  331. package/src/plugins/external-api.ts +12 -2
  332. package/src/plugins/pipeline.ts +6 -293
  333. package/src/plugins/registry.ts +9 -37
  334. package/src/plugins/types.ts +76 -381
  335. package/src/plugins/user-loader.ts +30 -127
  336. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  337. package/src/prompts/system-prompt.ts +61 -10
  338. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  339. package/src/providers/anthropic/client.ts +9 -10
  340. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  341. package/src/providers/inference/logit-bias.ts +55 -0
  342. package/src/providers/model-catalog.ts +57 -0
  343. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  344. package/src/providers/openai/chat-completions-provider.ts +44 -1
  345. package/src/providers/retry.ts +57 -7
  346. package/src/providers/types.ts +6 -0
  347. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  348. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  349. package/src/runtime/agent-wake.ts +108 -209
  350. package/src/runtime/assistant-event-hub.ts +1 -1
  351. package/src/runtime/channel-approvals.ts +1 -1
  352. package/src/runtime/interactive-ui.ts +1 -1
  353. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  354. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  355. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  356. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  357. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  358. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  359. package/src/runtime/routes/acp-routes.test.ts +185 -32
  360. package/src/runtime/routes/acp-routes.ts +328 -30
  361. package/src/runtime/routes/app-management-routes.ts +3 -0
  362. package/src/runtime/routes/approval-routes.ts +1 -1
  363. package/src/runtime/routes/browser-routes.ts +1 -1
  364. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  365. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  366. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  367. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  368. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  369. package/src/runtime/routes/conversation-routes.ts +30 -2
  370. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  371. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  372. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  373. package/src/runtime/routes/host-cu-routes.ts +1 -1
  374. package/src/runtime/routes/identity-routes.ts +18 -3
  375. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  376. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  377. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  378. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  379. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  380. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  381. package/src/runtime/routes/playground/helpers.ts +1 -1
  382. package/src/runtime/routes/plugins-routes.ts +337 -35
  383. package/src/runtime/routes/stt-routes.ts +45 -12
  384. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  385. package/src/runtime/routes/work-items-routes.ts +2 -4
  386. package/src/runtime/routes/workspace-routes.ts +50 -15
  387. package/src/runtime/services/conversation-serializer.ts +1 -1
  388. package/src/runtime/verification-outbound-actions.ts +147 -2
  389. package/src/runtime/verification-templates.ts +29 -3
  390. package/src/services/published-app-updater.ts +30 -8
  391. package/src/signals/cancel.ts +2 -4
  392. package/src/subagent/manager.ts +21 -5
  393. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  394. package/src/telemetry/activation-funnel.ts +167 -0
  395. package/src/telemetry/types.ts +13 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  397. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  398. package/src/tools/acp/context.ts +20 -0
  399. package/src/tools/acp/list-agents.test.ts +9 -19
  400. package/src/tools/acp/list-agents.ts +3 -15
  401. package/src/tools/acp/spawn.test.ts +173 -202
  402. package/src/tools/acp/spawn.ts +37 -172
  403. package/src/tools/acp/steer.test.ts +105 -8
  404. package/src/tools/acp/steer.ts +48 -17
  405. package/src/tools/apps/executors.ts +166 -50
  406. package/src/tools/browser/browser-execution.ts +12 -2
  407. package/src/tools/filesystem/write.ts +34 -0
  408. package/src/tools/network/web-fetch.ts +65 -24
  409. package/src/tools/skills/load.ts +1 -1
  410. package/src/tools/subagent/spawn.ts +2 -4
  411. package/src/tools/ui-surface/definitions.ts +32 -5
  412. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  413. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  414. package/docs/plugins.md +0 -832
  415. package/examples/plugins/echo/register.ts +0 -143
  416. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  417. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  418. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  419. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  420. package/src/__tests__/pipeline-runner.test.ts +0 -554
  421. package/src/daemon/wake-target-adapter.ts +0 -253
  422. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  423. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  424. package/src/memory/v3/__tests__/core.test.ts +0 -39
  425. package/src/memory/v3/__tests__/health.test.ts +0 -219
  426. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  427. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  428. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  429. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  430. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  431. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  432. package/src/memory/v3/__tests__/router.test.ts +0 -332
  433. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  434. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  435. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  436. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  437. package/src/memory/v3/assign.ts +0 -268
  438. package/src/memory/v3/capabilities.ts +0 -124
  439. package/src/memory/v3/core.ts +0 -26
  440. package/src/memory/v3/health.ts +0 -0
  441. package/src/memory/v3/maintain-job.ts +0 -314
  442. package/src/memory/v3/needle.ts +0 -115
  443. package/src/memory/v3/orchestrate.ts +0 -126
  444. package/src/memory/v3/page-content.ts +0 -34
  445. package/src/memory/v3/provider-blocks.ts +0 -26
  446. package/src/memory/v3/reconcile.ts +0 -523
  447. package/src/memory/v3/router.ts +0 -190
  448. package/src/memory/v3/selection-log-store.ts +0 -84
  449. package/src/memory/v3/selector.ts +0 -226
  450. package/src/memory/v3/shadow-plugin.ts +0 -411
  451. package/src/memory/v3/snapshot.ts +0 -209
  452. package/src/memory/v3/tree.ts +0 -174
  453. package/src/memory/v3/types.ts +0 -59
  454. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  455. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  456. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  457. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  458. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  459. package/src/plugins/defaults/compaction/register.ts +0 -35
  460. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  461. package/src/plugins/defaults/empty-response/register.ts +0 -23
  462. package/src/plugins/defaults/history-repair/register.ts +0 -24
  463. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  464. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  465. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  466. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  467. package/src/plugins/defaults/title-generate/register.ts +0 -35
  468. package/src/plugins/defaults/tool-error/register.ts +0 -23
  469. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  470. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  471. package/src/proactive-artifact/decision.test.ts +0 -226
  472. package/src/proactive-artifact/decision.ts +0 -165
  473. package/src/proactive-artifact/index.ts +0 -7
  474. package/src/proactive-artifact/job.test.ts +0 -962
  475. package/src/proactive-artifact/job.ts +0 -372
  476. package/src/proactive-artifact/message-copy.ts +0 -58
  477. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  478. package/src/proactive-artifact/trigger-state.ts +0 -123
  479. package/src/util/map-limit.ts +0 -27
  480. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  481. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  482. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  483. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  484. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  485. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  486. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  487. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  488. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  489. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  490. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -4,23 +4,31 @@
4
4
  * Exposes spawn, steer, cancel, close, sessions, and permission operations
5
5
  * over HTTP and IPC.
6
6
  */
7
- import { desc, eq, inArray } from "drizzle-orm";
7
+ import { randomUUID } from "node:crypto";
8
+
9
+ import { and, desc, eq, inArray, notInArray } from "drizzle-orm";
8
10
  import { z } from "zod";
9
11
 
12
+ import { resolveAgentWithAutoInstall } from "../../acp/auto-install.js";
10
13
  import { getAcpSessionManager } from "../../acp/index.js";
11
14
  import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
12
- import { resolveAcpAgent } from "../../acp/resolve-agent.js";
15
+ import { formatResolveFailure } from "../../acp/resolve-agent.js";
16
+ import { AcpResumeError } from "../../acp/session-manager.js";
13
17
  import type { AcpSessionState } from "../../acp/types.js";
18
+ import { getConfig } from "../../config/loader.js";
14
19
  import { getDb } from "../../memory/db-connection.js";
15
20
  import { rawChanges } from "../../memory/raw-query.js";
16
21
  import { acpSessionHistory } from "../../memory/schema.js";
22
+ import type { UserDecision } from "../../permissions/types.js";
17
23
  import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
18
24
  import { getLogger } from "../../util/logger.js";
19
25
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
26
+ import * as pendingInteractions from "../pending-interactions.js";
20
27
  import {
21
28
  BadRequestError,
22
29
  ConflictError,
23
30
  FailedDependencyError,
31
+ ForbiddenError,
24
32
  NotFoundError,
25
33
  } from "./errors.js";
26
34
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
@@ -51,7 +59,109 @@ type SessionEntry = z.infer<typeof sessionEntrySchema>;
51
59
  // Handlers
52
60
  // ---------------------------------------------------------------------------
53
61
 
54
- async function spawnSession({ body }: RouteHandlerArgs) {
62
+ /**
63
+ * Surface a high-risk confirmation for an ACP route that starts a host agent
64
+ * subprocess (`POST /v1/acp/spawn`, and the resume branch of
65
+ * `POST /v1/acp/:id/steer`) and resolve to the guardian's decision.
66
+ *
67
+ * Unlike the `acp_spawn` / `acp_steer` skill tools (which are dispatched
68
+ * through `ToolExecutor`/`PermissionChecker` and so inherit the descriptors'
69
+ * `risk: "high"` approval prompt), these HTTP/IPC routes reach the ACP
70
+ * session manager directly. A spawned ACP agent is a host subprocess with
71
+ * advertised filesystem + terminal access whose in-session permission
72
+ * requests are auto-allowed, so starting one is exactly the high-risk action
73
+ * the descriptors gate. Without this prompt a caller holding only
74
+ * `chat.write` could start one with no human in the loop — directly via
75
+ * spawn (arbitrary `cwd`), or by resuming a terminal session via steer (its
76
+ * persisted `cwd`, enumerable through `GET /v1/acp/sessions`) (ATL-822).
77
+ *
78
+ * The confirmation is registered with `directResolve` so `POST /v1/confirm`
79
+ * resolves it without a live `Conversation` object. That route requires the
80
+ * bound guardian (`approval.write` + `requireGuardian`) — a strictly higher
81
+ * bar than the `chat.write` these routes demand — so the `chat.write` caller
82
+ * cannot self-approve. Timeout and client disconnect both deny, matching the
83
+ * tool path's "no interactive client → do not run" posture.
84
+ */
85
+ function awaitRouteApproval(args: {
86
+ toolName: string;
87
+ input: Record<string, unknown>;
88
+ conversationId: string;
89
+ signal?: AbortSignal;
90
+ }): Promise<UserDecision> {
91
+ const { toolName, input, conversationId, signal } = args;
92
+ const requestId = randomUUID();
93
+ // Mirror the interactive prompt timeout; fall back to the schema default
94
+ // (300s) if the timeouts block is somehow absent so the gate never wedges.
95
+ const timeoutMs = (getConfig().timeouts?.permissionTimeoutSec ?? 300) * 1000;
96
+
97
+ return new Promise<UserDecision>((resolve) => {
98
+ let settled = false;
99
+ const settle = (
100
+ decision: UserDecision,
101
+ state: "approved" | "rejected" | "cancelled",
102
+ ) => {
103
+ if (settled) return;
104
+ settled = true;
105
+ clearTimeout(timer);
106
+ signal?.removeEventListener("abort", onAbort);
107
+ // Idempotent: the `/v1/confirm` directResolve path already removed the
108
+ // entry before invoking us, so this is a no-op there and only fires
109
+ // `interaction_resolved` for the timeout/abort paths.
110
+ pendingInteractions.resolve(requestId, state);
111
+ resolve(decision);
112
+ };
113
+
114
+ const timer = setTimeout(() => {
115
+ log.warn(
116
+ { requestId, toolName, conversationId },
117
+ "ACP route approval timed out — denying",
118
+ );
119
+ settle("deny", "cancelled");
120
+ }, timeoutMs);
121
+
122
+ const onAbort = () => settle("deny", "cancelled");
123
+ if (signal?.aborted) {
124
+ clearTimeout(timer);
125
+ resolve("deny");
126
+ return;
127
+ }
128
+ signal?.addEventListener("abort", onAbort, { once: true });
129
+
130
+ pendingInteractions.register(requestId, {
131
+ conversationId,
132
+ kind: "confirmation",
133
+ confirmationDetails: {
134
+ toolName,
135
+ input,
136
+ riskLevel: "high",
137
+ executionTarget: "host",
138
+ allowlistOptions: [],
139
+ scopeOptions: [],
140
+ persistentDecisionsAllowed: false,
141
+ },
142
+ directResolve: (decision) =>
143
+ settle(decision, decision === "allow" ? "approved" : "rejected"),
144
+ });
145
+
146
+ broadcastMessage(
147
+ {
148
+ type: "confirmation_request",
149
+ requestId,
150
+ toolName,
151
+ input,
152
+ riskLevel: "high",
153
+ executionTarget: "host",
154
+ allowlistOptions: [],
155
+ scopeOptions: [],
156
+ conversationId,
157
+ persistentDecisionsAllowed: false,
158
+ },
159
+ conversationId,
160
+ );
161
+ });
162
+ }
163
+
164
+ async function spawnSession({ body, abortSignal }: RouteHandlerArgs) {
55
165
  const agent = body?.agent as string | undefined;
56
166
  const task = body?.task as string | undefined;
57
167
  const conversationId = body?.conversationId as string | undefined;
@@ -61,26 +171,35 @@ async function spawnSession({ body }: RouteHandlerArgs) {
61
171
  throw new BadRequestError("agent, task, and conversationId are required");
62
172
  }
63
173
 
64
- const resolved = resolveAcpAgent(agent);
174
+ // High-risk approval gate. Block BEFORE any side effects — resolution can
175
+ // trigger a `bun` global install (auto-install.ts) and `manager.spawn`
176
+ // launches the host subprocess — so an unapproved request mutates nothing.
177
+ const decision = await awaitRouteApproval({
178
+ toolName: "acp_spawn",
179
+ input: { agent, task, cwd },
180
+ conversationId,
181
+ signal: abortSignal,
182
+ });
183
+ if (decision !== "allow") {
184
+ throw new ForbiddenError(
185
+ "Spawning an ACP coding agent requires guardian approval, which was " +
186
+ "not granted.",
187
+ );
188
+ }
189
+
190
+ // Resolve the agent, silently auto-installing a missing allowlisted
191
+ // adapter binary (see acp/auto-install.ts). Shared with the skill-tool
192
+ // path in tools/acp/spawn.ts; only the transport mapping differs.
193
+ const { resolved, failureMessage } = await resolveAgentWithAutoInstall(agent);
194
+ if (failureMessage) {
195
+ throw new FailedDependencyError(failureMessage);
196
+ }
65
197
  if (!resolved.ok) {
66
- switch (resolved.reason) {
67
- case "acp_disabled":
68
- throw new BadRequestError(resolved.hint);
69
- case "unknown_agent":
70
- throw new BadRequestError(
71
- `Unknown agent "${agent}". Available: ${resolved.available.join(", ")}.`,
72
- );
73
- case "binary_not_found":
74
- throw new FailedDependencyError(
75
- `${resolved.command} is not on PATH. ${resolved.hint}`,
76
- );
77
- default: {
78
- const _exhaustive: never = resolved;
79
- throw new Error(
80
- `Unexpected acp resolver reason: ${(_exhaustive as { reason: string }).reason}`,
81
- );
82
- }
198
+ const message = formatResolveFailure(agent, resolved);
199
+ if (resolved.reason === "binary_not_found") {
200
+ throw new FailedDependencyError(message);
83
201
  }
202
+ throw new BadRequestError(message);
84
203
  }
85
204
 
86
205
  // Inject required env vars and preflight via the shared helper. See
@@ -116,13 +235,143 @@ async function steerSession({ pathParams, body }: RouteHandlerArgs) {
116
235
  throw new BadRequestError("instruction is required");
117
236
  }
118
237
 
238
+ // Sessions no longer in memory (completed, or lost to a daemon restart)
239
+ // are transparently resumed from persisted history with the instruction
240
+ // fired in the same call, mirroring the acp_steer skill tool.
241
+ // broadcastMessage plays the sender role spawnSession gives it, so
242
+ // connected clients render the session.
119
243
  const manager = getAcpSessionManager();
244
+
245
+ // Resume re-spawns the host agent subprocess, so it crosses the same
246
+ // high-risk boundary as spawn and needs guardian approval (ATL-822). A
247
+ // steer of a session still active in memory only redirects an
248
+ // already-running, already-approved process, so it is left unprompted —
249
+ // matching "gate before spawning". A resume only happens when the id is
250
+ // absent from memory AND a resumable history row (non-null cwd) exists.
251
+ if (!manager.getActiveAndPendingIds().includes(id)) {
252
+ const resumable = getDb()
253
+ .select({
254
+ cwd: acpSessionHistory.cwd,
255
+ agentId: acpSessionHistory.agentId,
256
+ parentConversationId: acpSessionHistory.parentConversationId,
257
+ })
258
+ .from(acpSessionHistory)
259
+ .where(eq(acpSessionHistory.id, id))
260
+ .get();
261
+ if (resumable?.cwd != null) {
262
+ // Guardian approval is an out-of-band human action that can take far
263
+ // longer than a client's ack timeout (the macOS client uses 10s; the
264
+ // approval window is permissionTimeoutSec, default 300s). Blocking the
265
+ // HTTP response here would surface as a client transport failure even
266
+ // on an eventual approval. Acknowledge immediately and run approval +
267
+ // resume in the background, streaming via SSE like any other ACP
268
+ // activity. The request abortSignal is deliberately NOT threaded into
269
+ // the wait — the request is already complete.
270
+ void approveThenResume({
271
+ id,
272
+ instruction,
273
+ agentId: resumable.agentId,
274
+ cwd: resumable.cwd,
275
+ conversationId: resumable.parentConversationId,
276
+ });
277
+ return { acpSessionId: id, steered: false, approvalPending: true };
278
+ }
279
+ }
280
+
120
281
  try {
121
- await manager.steer(id, instruction);
122
- } catch {
282
+ const { resumed } = await manager.steerOrResume(
283
+ id,
284
+ instruction,
285
+ broadcastMessage,
286
+ );
287
+ return resumed
288
+ ? { acpSessionId: id, steered: true, resumed: true }
289
+ : { acpSessionId: id, steered: true };
290
+ } catch (err) {
291
+ // Resume errors carry the actionable hint (legacy row without cwd,
292
+ // agent capability missing, resolver failures, ...).
293
+ if (err instanceof AcpResumeError) {
294
+ throw new FailedDependencyError(err.message);
295
+ }
296
+ // Unknown ids (no in-memory session, no history row) and plain steer
297
+ // failures both map to 404, as before.
123
298
  throw new NotFoundError("ACP session not found");
124
299
  }
125
- return { acpSessionId: id, steered: true };
300
+ }
301
+
302
+ /**
303
+ * Background worker for an approval-gated ACP resume initiated over the steer
304
+ * route. Awaits the guardian decision (see `awaitRouteApproval`), then resumes
305
+ * via the session manager so the agent's output streams back over SSE. A
306
+ * denial, timeout, or resume failure is surfaced as an `acp_session_error` on
307
+ * the parent conversation so the client isn't left with an optimistic steer
308
+ * row and no follow-up. Never throws — it owns the request lifecycle after the
309
+ * route has already acked.
310
+ */
311
+ async function approveThenResume(args: {
312
+ id: string;
313
+ instruction: string;
314
+ agentId: string;
315
+ cwd: string;
316
+ conversationId: string;
317
+ }): Promise<void> {
318
+ const decision = await awaitRouteApproval({
319
+ toolName: "acp_steer",
320
+ input: {
321
+ acp_session_id: args.id,
322
+ instruction: args.instruction,
323
+ agent: args.agentId,
324
+ cwd: args.cwd,
325
+ },
326
+ conversationId: args.conversationId,
327
+ });
328
+
329
+ if (decision !== "allow") {
330
+ log.info(
331
+ { acpSessionId: args.id, conversationId: args.conversationId },
332
+ "ACP resume approval not granted — skipping resume",
333
+ );
334
+ broadcastMessage(
335
+ {
336
+ type: "acp_session_error",
337
+ // Key the error by the daemon session id (the value the steer route
338
+ // accepts and ACP SSE consumers index by — see registerSession, where
339
+ // state.id is the broadcast acpSessionId), NOT the persisted protocol
340
+ // id. A denied resume never emits acp_session_spawned to map the
341
+ // protocol id, so a protocol-keyed error would be dropped.
342
+ acpSessionId: args.id,
343
+ error: "Resume was not approved.",
344
+ },
345
+ args.conversationId,
346
+ );
347
+ return;
348
+ }
349
+
350
+ try {
351
+ await getAcpSessionManager().steerOrResume(
352
+ args.id,
353
+ args.instruction,
354
+ broadcastMessage,
355
+ );
356
+ } catch (err) {
357
+ const message =
358
+ err instanceof AcpResumeError || err instanceof Error
359
+ ? err.message
360
+ : String(err);
361
+ log.warn(
362
+ { acpSessionId: args.id, conversationId: args.conversationId, err },
363
+ "Approved ACP resume failed",
364
+ );
365
+ broadcastMessage(
366
+ {
367
+ type: "acp_session_error",
368
+ // Daemon session id (route + SSE key), not the protocol id — see above.
369
+ acpSessionId: args.id,
370
+ error: message,
371
+ },
372
+ args.conversationId,
373
+ );
374
+ }
126
375
  }
127
376
 
128
377
  async function cancelSession({ pathParams }: RouteHandlerArgs) {
@@ -161,9 +410,25 @@ function bulkDeleteSessions({ queryParams }: RouteHandlerArgs) {
161
410
  "status query param is required and must be 'completed'",
162
411
  );
163
412
  }
413
+ // Exclude sessions currently active in memory AND ids with a resume in
414
+ // flight (reserved but not yet registered): a resumed session reuses its
415
+ // original id, and its history row keeps the old terminal status until
416
+ // the next terminal upsert - a status-only delete would remove the row
417
+ // out from under the live (or resuming) session, and the later terminal
418
+ // upsert would resurrect it. Mirrors the 409 guard on the single-id
419
+ // delete route.
420
+ const activeIds = getAcpSessionManager().getActiveAndPendingIds();
421
+ const terminalFilter = inArray(
422
+ acpSessionHistory.status,
423
+ TERMINAL_SESSION_STATUSES,
424
+ );
164
425
  getDb()
165
426
  .delete(acpSessionHistory)
166
- .where(inArray(acpSessionHistory.status, TERMINAL_SESSION_STATUSES))
427
+ .where(
428
+ activeIds.length > 0
429
+ ? and(terminalFilter, notInArray(acpSessionHistory.id, activeIds))
430
+ : terminalFilter,
431
+ )
167
432
  .run();
168
433
  const deleted = rawChanges();
169
434
  log.info({ deleted }, "Bulk-cleared terminal ACP session history");
@@ -172,9 +437,10 @@ function bulkDeleteSessions({ queryParams }: RouteHandlerArgs) {
172
437
 
173
438
  function deleteSession({ pathParams }: RouteHandlerArgs) {
174
439
  const id = pathParams?.id as string;
440
+ const manager = getAcpSessionManager();
175
441
 
176
442
  try {
177
- const state = getAcpSessionManager().getStatus(id);
443
+ const state = manager.getStatus(id);
178
444
  if (
179
445
  !Array.isArray(state) &&
180
446
  (state.status === "running" || state.status === "initializing")
@@ -185,7 +451,16 @@ function deleteSession({ pathParams }: RouteHandlerArgs) {
185
451
  }
186
452
  } catch (err) {
187
453
  if (err instanceof ConflictError) throw err;
188
- // Not in memory — fall through to the (idempotent) DB delete.
454
+ // Not registered in memory, but a resume may be in flight (id reserved
455
+ // while awaiting env preparation). Its history row must survive until
456
+ // the resume lands - the later terminal upsert would resurrect a
457
+ // deleted row.
458
+ if (manager.getActiveAndPendingIds().includes(id)) {
459
+ throw new ConflictError(
460
+ `ACP session "${id}" has a resume in flight. Wait for it to finish before deleting.`,
461
+ );
462
+ }
463
+ // Otherwise fall through to the (idempotent) DB delete.
189
464
  }
190
465
 
191
466
  getDb().delete(acpSessionHistory).where(eq(acpSessionHistory.id, id)).run();
@@ -233,7 +508,14 @@ export const ROUTES: RouteDefinition[] = [
233
508
  },
234
509
  handler: steerSession,
235
510
  summary: "Steer ACP session",
236
- description: "Send a steering instruction to an active ACP session.",
511
+ description:
512
+ "Send a steering instruction to an ACP session. Sessions no longer " +
513
+ "in memory (completed, or lost to a daemon restart) are " +
514
+ "transparently resumed from persisted history first, when the agent " +
515
+ "supports ACP session loading. Resuming a terminal session re-spawns " +
516
+ "the host agent, so it requires guardian approval: the route acks " +
517
+ "immediately with approvalPending=true and performs the resume in the " +
518
+ "background once approved, streaming results over SSE.",
237
519
  tags: ["acp"],
238
520
  requestBody: z.object({
239
521
  instruction: z.string(),
@@ -241,6 +523,19 @@ export const ROUTES: RouteDefinition[] = [
241
523
  responseBody: z.object({
242
524
  acpSessionId: z.string(),
243
525
  steered: z.boolean(),
526
+ resumed: z
527
+ .boolean()
528
+ .optional()
529
+ .describe(
530
+ "True when the session was resumed from persisted history before steering.",
531
+ ),
532
+ approvalPending: z
533
+ .boolean()
534
+ .optional()
535
+ .describe(
536
+ "True when the steer triggered a guardian-approval-gated resume " +
537
+ "that will run asynchronously; watch SSE for the outcome.",
538
+ ),
244
539
  }),
245
540
  },
246
541
  {
@@ -324,7 +619,9 @@ export const ROUTES: RouteDefinition[] = [
324
619
  summary: "Bulk-clear terminal ACP sessions",
325
620
  description:
326
621
  "Remove every terminal-state row (completed/failed/cancelled) from " +
327
- "the persisted acp_session_history table.",
622
+ "the persisted acp_session_history table. Rows whose session is " +
623
+ "currently active in memory (e.g. resumed) or has a resume in " +
624
+ "flight are excluded.",
328
625
  tags: ["acp"],
329
626
  queryParams: [
330
627
  {
@@ -350,7 +647,8 @@ export const ROUTES: RouteDefinition[] = [
350
647
  summary: "Delete ACP session from history",
351
648
  description:
352
649
  "Remove a persisted ACP session row. Rejects with 409 when the " +
353
- "session is still active in memory; idempotent for unknown ids.",
650
+ "session is still active in memory or has a resume in flight; " +
651
+ "idempotent for unknown ids.",
354
652
  tags: ["acp"],
355
653
  responseBody: z.object({
356
654
  deleted: z.boolean(),
@@ -89,6 +89,7 @@ function listAppsFiltered(apps?: AppDefinition[]): Array<{
89
89
  description?: string;
90
90
  icon?: string;
91
91
  createdAt: number;
92
+ updatedAt: number;
92
93
  version: string;
93
94
  contentId: string;
94
95
  }> {
@@ -101,6 +102,7 @@ function listAppsFiltered(apps?: AppDefinition[]): Array<{
101
102
  description: a.description,
102
103
  icon: a.icon,
103
104
  createdAt: a.createdAt,
105
+ updatedAt: a.updatedAt,
104
106
  version,
105
107
  contentId,
106
108
  };
@@ -751,6 +753,7 @@ export const ROUTES: RouteDefinition[] = [
751
753
  description: z.string().optional(),
752
754
  icon: z.string().optional(),
753
755
  createdAt: z.number(),
756
+ updatedAt: z.number(),
754
757
  version: z.string(),
755
758
  contentId: z.string(),
756
759
  }),
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { z } from "zod";
11
11
 
12
- import { findConversation } from "../../daemon/conversation-store.js";
12
+ import { findConversation } from "../../daemon/conversation-registry.js";
13
13
  import { getConversationByKey } from "../../memory/conversation-key-store.js";
14
14
  import type { UserDecision } from "../../permissions/types.js";
15
15
  import { getLogger } from "../../util/logger.js";
@@ -16,7 +16,7 @@ import {
16
16
  BROWSER_OPERATIONS,
17
17
  type BrowserOperation,
18
18
  } from "../../browser/types.js";
19
- import { findConversation } from "../../daemon/conversation-store.js";
19
+ import { findConversation } from "../../daemon/conversation-registry.js";
20
20
  import type { ContentBlock } from "../../providers/types.js";
21
21
  import { LOCAL_PRINCIPALS } from "../auth/route-policy.js";
22
22
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { z } from "zod";
9
9
 
10
- import { findConversation } from "../../daemon/conversation-store.js";
10
+ import { findConversation } from "../../daemon/conversation-registry.js";
11
11
  import { getCdpClient } from "../../tools/browser/cdp-client/factory.js";
12
12
  import {
13
13
  clearPinnedTab,
@@ -73,11 +73,7 @@ async function handleBrowserTabs({ body = {} }: RouteHandlerArgs) {
73
73
  ? result.clientId
74
74
  : undefined;
75
75
  if (result?.tabId !== undefined) {
76
- setPinnedTab(
77
- resolvedConversationId,
78
- String(result.tabId),
79
- clientId,
80
- );
76
+ setPinnedTab(resolvedConversationId, String(result.tabId), clientId);
81
77
  }
82
78
  return { ok: true, tab: result };
83
79
  } finally {
@@ -88,10 +84,10 @@ async function handleBrowserTabs({ body = {} }: RouteHandlerArgs) {
88
84
  if (command === "new") {
89
85
  const cdp = getCdpClient(context, cdpOptions);
90
86
  try {
91
- const result = await cdp.send<{ tabId?: number | string; clientId?: string }>(
92
- "Vellum.createTab",
93
- {},
94
- );
87
+ const result = await cdp.send<{
88
+ tabId?: number | string;
89
+ clientId?: string;
90
+ }>("Vellum.createTab", {});
95
91
  // Normalise to string for internal use (setCdpSessionId / setPinnedTab)
96
92
  // and keep the numeric form for the API response.
97
93
  const newTabIdStr: string | undefined =
@@ -23,6 +23,7 @@ import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
23
23
  import { revokePendingSessions } from "../channel-verification-service.js";
24
24
  import {
25
25
  cancelOutbound,
26
+ deliverVerificationEmail,
26
27
  deliverVerificationSlack,
27
28
  normalizeTelegramDestination,
28
29
  resendOutbound,
@@ -139,11 +140,15 @@ export async function handleCreateVerificationSession({
139
140
  verificationRateLimiter.recordFailure(rateLimitKey);
140
141
  }
141
142
 
142
- // Dispatch Slack DM delivery from the daemon process (not sandboxed).
143
+ // Dispatch delivery from the daemon process (not sandboxed).
143
144
  if (result._pendingSlackDm) {
144
145
  const { userId, text, assistantId: aid } = result._pendingSlackDm;
145
146
  deliverVerificationSlack(userId, text, aid);
146
147
  }
148
+ if (result._pendingEmail) {
149
+ const { to, text, subject, assistantId: aid } = result._pendingEmail;
150
+ deliverVerificationEmail(to, text, subject, aid);
151
+ }
147
152
 
148
153
  if (!result.success) {
149
154
  if (result.error === "rate_limited") {
@@ -157,8 +162,8 @@ export async function handleCreateVerificationSession({
157
162
  );
158
163
  }
159
164
 
160
- // Strip internal field from the response
161
- const { _pendingSlackDm: _, ...publicResult } = result;
165
+ // Strip internal fields from the response
166
+ const { _pendingSlackDm: _, _pendingEmail: __, ...publicResult } = result;
162
167
  return publicResult;
163
168
  }
164
169
 
@@ -204,11 +209,15 @@ export async function handleResendVerificationSession({
204
209
 
205
210
  const result = resendOutbound({ channel, originConversationId });
206
211
 
207
- // Dispatch Slack DM delivery from the daemon process (not sandboxed).
212
+ // Dispatch delivery from the daemon process (not sandboxed).
208
213
  if (result._pendingSlackDm) {
209
214
  const { userId, text, assistantId: aid } = result._pendingSlackDm;
210
215
  deliverVerificationSlack(userId, text, aid);
211
216
  }
217
+ if (result._pendingEmail) {
218
+ const { to, text, subject, assistantId: aid } = result._pendingEmail;
219
+ deliverVerificationEmail(to, text, subject, aid);
220
+ }
212
221
 
213
222
  if (!result.success) {
214
223
  if (result.error === "rate_limited") {
@@ -221,7 +230,7 @@ export async function handleResendVerificationSession({
221
230
  );
222
231
  }
223
232
 
224
- const { _pendingSlackDm: _, ...publicResult } = result;
233
+ const { _pendingSlackDm: _, _pendingEmail: __, ...publicResult } = result;
225
234
  return publicResult;
226
235
  }
227
236
 
@@ -9,7 +9,7 @@
9
9
  import { v4 as uuid } from "uuid";
10
10
  import { z } from "zod";
11
11
 
12
- import { findConversation } from "../../daemon/conversation-store.js";
12
+ import { findConversation } from "../../daemon/conversation-registry.js";
13
13
  import { clearAllConversations as clearAllActive } from "../../daemon/handlers/conversations.js";
14
14
  import { formatJson, formatMarkdown } from "../../export/formatter.js";
15
15
  import { ipcCall as ipcCallGateway } from "../../ipc/gateway-client.js";
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { z } from "zod";
11
11
 
12
- import { findConversation } from "../../daemon/conversation-store.js";
12
+ import { findConversation } from "../../daemon/conversation-registry.js";
13
13
  import {
14
14
  type Confidence,
15
15
  getAttentionStateByConversationIds,
@@ -72,7 +72,7 @@ import { type LogRow } from "../../memory/llm-request-log-store.js";
72
72
  import { getMemoryRecallLogByMessageIds } from "../../memory/memory-recall-log-store.js";
73
73
  import { getMemoryV2ActivationLogByMessageIds } from "../../memory/memory-v2-activation-log-store.js";
74
74
  import { MEMORY_V2_CONSOLIDATION_SOURCE } from "../../memory/v2/constants.js";
75
- import { getMemoryV3SelectionForInspector } from "../../memory/v3/selection-log-store.js";
75
+ import { getMemoryV3SelectionForInspector } from "../../plugins/defaults/memory-v3-shadow/selection-log-store.js";
76
76
  import {
77
77
  createConnection,
78
78
  listConnections,