@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae

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 (475) 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 +263 -121
  12. package/package.json +2 -2
  13. package/src/__tests__/activation-early-marking.test.ts +120 -0
  14. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  15. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  16. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  17. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  18. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  19. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  20. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  21. package/src/__tests__/agent-loop.test.ts +441 -96
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  23. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  24. package/src/__tests__/anthropic-provider.test.ts +23 -8
  25. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  26. package/src/__tests__/app-control-flow.test.ts +1 -1
  27. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  28. package/src/__tests__/app-executors.test.ts +132 -0
  29. package/src/__tests__/approval-cascade.test.ts +6 -5
  30. package/src/__tests__/approval-routes-http.test.ts +4 -1
  31. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  32. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  33. package/src/__tests__/channel-approvals.test.ts +1 -1
  34. package/src/__tests__/compaction-circuit.test.ts +258 -0
  35. package/src/__tests__/compaction-direct.test.ts +146 -0
  36. package/src/__tests__/compaction-events.test.ts +7 -7
  37. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  38. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  39. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  40. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  41. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  42. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  43. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  44. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  45. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  46. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  47. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  48. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  49. package/src/__tests__/conversation-error.test.ts +15 -1
  50. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  52. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  53. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  55. package/src/__tests__/conversation-queue.test.ts +10 -10
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  57. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  58. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  59. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  60. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  61. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  62. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  64. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  65. package/src/__tests__/conversation-undo.test.ts +2 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  67. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/corrected-target.test.ts +93 -0
  71. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  72. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  73. package/src/__tests__/db-acp-history.test.ts +101 -0
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +1 -1
  76. package/src/__tests__/file-write-tool.test.ts +63 -0
  77. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  78. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  79. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  80. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  81. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  82. package/src/__tests__/heartbeat-service.test.ts +1 -0
  83. package/src/__tests__/history-repair-hook.test.ts +5 -1
  84. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  85. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  86. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  87. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  88. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  89. package/src/__tests__/injector-background-turn.test.ts +13 -23
  90. package/src/__tests__/injector-chain.test.ts +268 -44
  91. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  92. package/src/__tests__/injector-document-comments.test.ts +97 -114
  93. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  94. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  95. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  96. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  97. package/src/__tests__/llm-resolver.test.ts +73 -0
  98. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  99. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  100. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  101. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  102. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  103. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  104. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  105. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  106. package/src/__tests__/plugin-registry.test.ts +3 -49
  107. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  108. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  109. package/src/__tests__/plugin-types.test.ts +0 -70
  110. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  111. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  112. package/src/__tests__/published-app-updater.test.ts +138 -0
  113. package/src/__tests__/reaction-persistence.test.ts +1 -1
  114. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  115. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  116. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  117. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  118. package/src/__tests__/subagent-detail.test.ts +25 -7
  119. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  120. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  121. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  122. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  123. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  124. package/src/__tests__/title-generate-hook.test.ts +5 -1
  125. package/src/__tests__/tool-error-hook.test.ts +1 -1
  126. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  127. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  128. package/src/__tests__/web-fetch.test.ts +45 -0
  129. package/src/acp/__tests__/agent-process.test.ts +161 -0
  130. package/src/acp/__tests__/client-handler.test.ts +40 -0
  131. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  132. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  133. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  134. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  135. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  136. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  137. package/src/acp/agent-process.ts +61 -1
  138. package/src/acp/auto-install.test.ts +280 -0
  139. package/src/acp/auto-install.ts +232 -0
  140. package/src/acp/client-handler.ts +31 -0
  141. package/src/acp/prepare-agent-env.ts +80 -27
  142. package/src/acp/resolve-agent.test.ts +188 -28
  143. package/src/acp/resolve-agent.ts +119 -42
  144. package/src/acp/resume-hint.ts +23 -0
  145. package/src/acp/session-manager.ts +507 -73
  146. package/src/agent/compaction-circuit.ts +60 -102
  147. package/src/agent/loop.ts +403 -251
  148. package/src/api/responses/conversation-message.ts +22 -2
  149. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  150. package/src/approvals/guardian-request-resolvers.ts +27 -1
  151. package/src/background-wake/next-wake.ts +1 -0
  152. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  153. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  154. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  155. package/src/cli/commands/memory-v3.ts +57 -199
  156. package/src/cli/commands/plugins.ts +43 -37
  157. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  158. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  159. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  160. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  161. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  162. package/src/cli/lib/install-from-github.ts +797 -60
  163. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  164. package/src/cli/lib/plugin-details.ts +416 -0
  165. package/src/cli/lib/plugin-marketplace.ts +215 -0
  166. package/src/cli/lib/search-plugins.ts +201 -29
  167. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  168. package/src/config/acp-defaults.test.ts +10 -0
  169. package/src/config/acp-defaults.ts +9 -3
  170. package/src/config/acp-schema.ts +1 -7
  171. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  172. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  173. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  174. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  175. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  176. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  177. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  178. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  180. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  181. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  182. package/src/config/bundled-tool-registry.ts +4 -0
  183. package/src/config/call-site-defaults.ts +0 -3
  184. package/src/config/feature-flag-registry.json +10 -16
  185. package/src/config/llm-resolver.ts +39 -7
  186. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  187. package/src/config/schemas/call-site-catalog.ts +0 -21
  188. package/src/config/schemas/heartbeat.ts +9 -0
  189. package/src/config/schemas/llm.ts +17 -3
  190. package/src/config/schemas/memory-v3.ts +58 -8
  191. package/src/config/seed-inference-profiles.ts +18 -0
  192. package/src/context/compactor.ts +22 -4
  193. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  194. package/src/context/strip-injections.ts +8 -2
  195. package/src/daemon/conversation-agent-loop-handlers.ts +18 -36
  196. package/src/daemon/conversation-agent-loop.ts +308 -1297
  197. package/src/daemon/conversation-error.ts +31 -4
  198. package/src/daemon/conversation-history.ts +1 -1
  199. package/src/daemon/conversation-lifecycle.ts +11 -255
  200. package/src/daemon/conversation-media-retry.ts +19 -6
  201. package/src/daemon/conversation-messaging.ts +17 -0
  202. package/src/daemon/conversation-process.ts +22 -141
  203. package/src/daemon/conversation-queue-manager.ts +8 -0
  204. package/src/daemon/conversation-registry.ts +159 -0
  205. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  206. package/src/daemon/conversation-store.ts +9 -90
  207. package/src/daemon/conversation-surfaces.ts +165 -11
  208. package/src/daemon/conversation-workspace.ts +17 -0
  209. package/src/daemon/conversation.ts +444 -62
  210. package/src/daemon/disk-pressure-policy.ts +0 -1
  211. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  212. package/src/daemon/handlers/config-channels.ts +11 -2
  213. package/src/daemon/handlers/conversations.ts +3 -1
  214. package/src/daemon/handlers/skills.ts +4 -1
  215. package/src/daemon/host-app-control-proxy.ts +72 -57
  216. package/src/daemon/host-proxy-preactivation.ts +1 -3
  217. package/src/daemon/lifecycle.ts +21 -7
  218. package/src/daemon/persist-unsendable-image.ts +62 -25
  219. package/src/daemon/process-message.ts +1 -1
  220. package/src/daemon/server.ts +2 -0
  221. package/src/daemon/tool-side-effects.ts +15 -0
  222. package/src/daemon/wake-conversation-ops.ts +269 -0
  223. package/src/embedded/plugin-api.ts +2 -2
  224. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  225. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  226. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  227. package/src/heartbeat/heartbeat-service.ts +26 -0
  228. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  229. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  230. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  231. package/src/ipc/skill-routes/memory.ts +4 -2
  232. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  233. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  234. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  235. package/src/memory/activation-session-store.ts +43 -0
  236. package/src/memory/conversation-crud.ts +29 -19
  237. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  238. package/src/memory/db-init.ts +6 -0
  239. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  240. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  241. package/src/memory/jobs-worker.ts +25 -1
  242. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  243. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  244. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  245. package/src/memory/migrations/index.ts +3 -0
  246. package/src/memory/onboarding-events-store.ts +66 -18
  247. package/src/memory/schema/acp.ts +4 -0
  248. package/src/memory/schema/infrastructure.ts +13 -0
  249. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  250. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  251. package/src/memory/v2/consolidation-job.ts +2 -63
  252. package/src/memory/v2/types.ts +5 -0
  253. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  254. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  255. package/src/notifications/access-request-copy.ts +298 -0
  256. package/src/notifications/adapters/slack.ts +3 -3
  257. package/src/notifications/adapters/telegram.ts +9 -2
  258. package/src/notifications/copy-composer.ts +49 -267
  259. package/src/notifications/decision-engine.ts +16 -35
  260. package/src/notifications/home-feed-side-effect.ts +1 -6
  261. package/src/plugin-api/constants.ts +4 -0
  262. package/src/plugin-api/index.ts +40 -11
  263. package/src/plugin-api/types.ts +108 -0
  264. package/src/plugins/defaults/compaction/compact.ts +106 -0
  265. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  266. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  267. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  268. package/src/plugins/defaults/compaction/package.json +1 -2
  269. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  270. package/src/plugins/defaults/empty-response/package.json +0 -1
  271. package/src/plugins/defaults/history-repair/package.json +0 -1
  272. package/src/plugins/defaults/index.ts +156 -26
  273. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  274. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  275. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  276. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  277. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  278. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  282. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  283. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  284. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  285. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  286. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  287. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  288. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  289. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  290. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  291. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  292. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  293. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  294. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  295. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  296. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  297. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  298. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  299. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  300. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  301. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  302. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  303. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  304. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  306. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  307. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  308. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  309. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  310. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  311. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  312. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  313. package/src/plugins/defaults/title-generate/package.json +0 -1
  314. package/src/plugins/defaults/tool-error/package.json +0 -1
  315. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  316. package/src/plugins/external-api.ts +12 -2
  317. package/src/plugins/pipeline.ts +6 -293
  318. package/src/plugins/registry.ts +9 -37
  319. package/src/plugins/types.ts +76 -381
  320. package/src/plugins/user-loader.ts +30 -127
  321. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  322. package/src/prompts/system-prompt.ts +61 -10
  323. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  324. package/src/providers/anthropic/client.ts +9 -10
  325. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  326. package/src/providers/inference/logit-bias.ts +55 -0
  327. package/src/providers/model-catalog.ts +34 -0
  328. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  329. package/src/providers/openai/chat-completions-provider.ts +44 -1
  330. package/src/providers/retry.ts +22 -0
  331. package/src/providers/types.ts +6 -0
  332. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  333. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  334. package/src/runtime/agent-wake.ts +108 -209
  335. package/src/runtime/assistant-event-hub.ts +1 -1
  336. package/src/runtime/channel-approvals.ts +1 -1
  337. package/src/runtime/interactive-ui.ts +1 -1
  338. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  339. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  340. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  341. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  342. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  343. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  344. package/src/runtime/routes/acp-routes.test.ts +185 -32
  345. package/src/runtime/routes/acp-routes.ts +328 -30
  346. package/src/runtime/routes/app-management-routes.ts +3 -0
  347. package/src/runtime/routes/approval-routes.ts +1 -1
  348. package/src/runtime/routes/browser-routes.ts +1 -1
  349. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  350. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  351. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  352. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  354. package/src/runtime/routes/conversation-routes.ts +30 -2
  355. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  356. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  357. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  358. package/src/runtime/routes/host-cu-routes.ts +1 -1
  359. package/src/runtime/routes/identity-routes.ts +18 -3
  360. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  361. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  362. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  363. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  364. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  365. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  366. package/src/runtime/routes/playground/helpers.ts +1 -1
  367. package/src/runtime/routes/plugins-routes.ts +337 -35
  368. package/src/runtime/routes/stt-routes.ts +45 -12
  369. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  370. package/src/runtime/routes/work-items-routes.ts +2 -4
  371. package/src/runtime/routes/workspace-routes.ts +50 -15
  372. package/src/runtime/services/conversation-serializer.ts +1 -1
  373. package/src/runtime/verification-outbound-actions.ts +147 -2
  374. package/src/runtime/verification-templates.ts +29 -3
  375. package/src/services/published-app-updater.ts +30 -8
  376. package/src/signals/cancel.ts +2 -4
  377. package/src/subagent/manager.ts +21 -5
  378. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  379. package/src/telemetry/activation-funnel.ts +167 -0
  380. package/src/telemetry/types.ts +13 -0
  381. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  382. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  383. package/src/tools/acp/context.ts +20 -0
  384. package/src/tools/acp/list-agents.test.ts +9 -19
  385. package/src/tools/acp/list-agents.ts +3 -15
  386. package/src/tools/acp/spawn.test.ts +173 -202
  387. package/src/tools/acp/spawn.ts +37 -172
  388. package/src/tools/acp/steer.test.ts +105 -8
  389. package/src/tools/acp/steer.ts +48 -17
  390. package/src/tools/apps/executors.ts +166 -50
  391. package/src/tools/browser/browser-execution.ts +12 -2
  392. package/src/tools/filesystem/write.ts +34 -0
  393. package/src/tools/network/web-fetch.ts +65 -24
  394. package/src/tools/skills/load.ts +1 -1
  395. package/src/tools/subagent/spawn.ts +2 -4
  396. package/src/tools/ui-surface/definitions.ts +32 -5
  397. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  398. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  399. package/docs/plugins.md +0 -832
  400. package/examples/plugins/echo/register.ts +0 -143
  401. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  402. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  403. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  404. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  405. package/src/__tests__/pipeline-runner.test.ts +0 -554
  406. package/src/daemon/wake-target-adapter.ts +0 -253
  407. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  408. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  409. package/src/memory/v3/__tests__/core.test.ts +0 -39
  410. package/src/memory/v3/__tests__/health.test.ts +0 -219
  411. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  412. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  413. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  414. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  415. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  416. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  417. package/src/memory/v3/__tests__/router.test.ts +0 -332
  418. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  419. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  420. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  421. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  422. package/src/memory/v3/assign.ts +0 -268
  423. package/src/memory/v3/capabilities.ts +0 -124
  424. package/src/memory/v3/core.ts +0 -26
  425. package/src/memory/v3/health.ts +0 -0
  426. package/src/memory/v3/maintain-job.ts +0 -314
  427. package/src/memory/v3/needle.ts +0 -115
  428. package/src/memory/v3/orchestrate.ts +0 -126
  429. package/src/memory/v3/page-content.ts +0 -34
  430. package/src/memory/v3/provider-blocks.ts +0 -26
  431. package/src/memory/v3/reconcile.ts +0 -523
  432. package/src/memory/v3/router.ts +0 -190
  433. package/src/memory/v3/selection-log-store.ts +0 -84
  434. package/src/memory/v3/selector.ts +0 -226
  435. package/src/memory/v3/shadow-plugin.ts +0 -411
  436. package/src/memory/v3/snapshot.ts +0 -209
  437. package/src/memory/v3/tree.ts +0 -174
  438. package/src/memory/v3/types.ts +0 -59
  439. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  440. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  441. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  442. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  443. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  444. package/src/plugins/defaults/compaction/register.ts +0 -35
  445. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  446. package/src/plugins/defaults/empty-response/register.ts +0 -23
  447. package/src/plugins/defaults/history-repair/register.ts +0 -24
  448. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  449. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  450. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  451. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  452. package/src/plugins/defaults/title-generate/register.ts +0 -35
  453. package/src/plugins/defaults/tool-error/register.ts +0 -23
  454. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  455. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  456. package/src/proactive-artifact/decision.test.ts +0 -226
  457. package/src/proactive-artifact/decision.ts +0 -165
  458. package/src/proactive-artifact/index.ts +0 -7
  459. package/src/proactive-artifact/job.test.ts +0 -962
  460. package/src/proactive-artifact/job.ts +0 -372
  461. package/src/proactive-artifact/message-copy.ts +0 -58
  462. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  463. package/src/proactive-artifact/trigger-state.ts +0 -123
  464. package/src/util/map-limit.ts +0 -27
  465. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  466. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  467. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  468. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  469. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  470. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  471. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  472. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  473. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  474. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  475. /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,