@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
@@ -1,128 +1,12 @@
1
- import { execFile } from "node:child_process";
1
+ import { basename } from "node:path";
2
2
 
3
+ import { resolveAgentWithAutoInstall } from "../../acp/auto-install.js";
3
4
  import { getAcpSessionManager } from "../../acp/index.js";
4
5
  import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
5
- import { resolveAcpAgent } from "../../acp/resolve-agent.js";
6
- import { DEFAULT_AGENT_NPM_PACKAGES } from "../../config/acp-defaults.js";
7
- import { getLogger } from "../../util/logger.js";
6
+ import { formatResolveFailure } from "../../acp/resolve-agent.js";
7
+ import { claudeResumeHint } from "../../acp/resume-hint.js";
8
8
  import type { ToolContext, ToolExecutionResult } from "../types.js";
9
-
10
- const log = getLogger("acp:spawn");
11
-
12
- /** Per-call timeout for `npm` probes. Best-effort: timeouts are non-fatal. */
13
- const NPM_PROBE_TIMEOUT_MS = 5_000;
14
-
15
- /**
16
- * Cache of resolved version-check outcomes — including `null` for "skipped" —
17
- * keyed by command. Lives for the process lifetime so retries don't reprobe.
18
- */
19
- const adapterVersionCache = new Map<string, AdapterVersionInfo | null>();
20
-
21
- interface AdapterVersionInfo {
22
- outdated: true;
23
- installed: string;
24
- latest: string;
25
- packageName: string;
26
- }
27
-
28
- /**
29
- * Run `execFile` with an AbortController-driven timeout. Returns the stdout
30
- * on success; throws on error or timeout. Caller treats any throw as a
31
- * best-effort skip.
32
- */
33
- function execFileWithTimeout(
34
- command: string,
35
- args: string[],
36
- timeoutMs: number,
37
- ): Promise<string> {
38
- return new Promise((resolve, reject) => {
39
- const controller = new AbortController();
40
- const timer = setTimeout(() => controller.abort(), timeoutMs);
41
- execFile(
42
- command,
43
- args,
44
- { signal: controller.signal, encoding: "utf8" },
45
- (err, stdout) => {
46
- clearTimeout(timer);
47
- if (err) {
48
- reject(err);
49
- return;
50
- }
51
- resolve(stdout);
52
- },
53
- );
54
- });
55
- }
56
-
57
- /**
58
- * Checks if the globally-installed ACP adapter for `command` is outdated.
59
- * Best-effort: any error or timeout returns `null` (skipped). Unknown
60
- * commands also return `null`. Results are cached per-command for the
61
- * process lifetime.
62
- *
63
- * Note: `npm ls -g` doesn't see Homebrew/tarball installs, so a "not found"
64
- * here doesn't mean the binary is missing — it just means we can't compare
65
- * versions. The caller must NEVER block the spawn on this result.
66
- */
67
- async function checkAdapterVersion(
68
- command: string,
69
- ): Promise<AdapterVersionInfo | null> {
70
- if (adapterVersionCache.has(command)) {
71
- return adapterVersionCache.get(command) ?? null;
72
- }
73
-
74
- const packageName = DEFAULT_AGENT_NPM_PACKAGES[command];
75
- if (!packageName) {
76
- adapterVersionCache.set(command, null);
77
- return null;
78
- }
79
-
80
- try {
81
- const [installedRaw, latestRaw] = await Promise.all([
82
- execFileWithTimeout(
83
- "npm",
84
- ["ls", "-g", "--json", packageName],
85
- NPM_PROBE_TIMEOUT_MS,
86
- ),
87
- execFileWithTimeout(
88
- "npm",
89
- ["view", packageName, "version"],
90
- NPM_PROBE_TIMEOUT_MS,
91
- ),
92
- ]);
93
-
94
- const installed =
95
- JSON.parse(installedRaw)?.dependencies?.[packageName]?.version;
96
- const latest = latestRaw.trim();
97
-
98
- if (!installed || !latest || installed === latest) {
99
- adapterVersionCache.set(command, null);
100
- return null;
101
- }
102
-
103
- log.info({ installed, latest, packageName }, "ACP adapter is outdated");
104
- const info: AdapterVersionInfo = {
105
- outdated: true,
106
- installed,
107
- latest,
108
- packageName,
109
- };
110
- adapterVersionCache.set(command, info);
111
- return info;
112
- } catch (err) {
113
- log.warn(
114
- { err, packageName },
115
- "Failed to check ACP adapter version (best-effort, skipping)",
116
- );
117
- adapterVersionCache.set(command, null);
118
- return null;
119
- }
120
- }
121
-
122
- /** @internal — exposed for tests only. */
123
- export function _resetAdapterVersionCacheForTests(): void {
124
- adapterVersionCache.clear();
125
- }
9
+ import { getSendToClient } from "./context.js";
126
10
 
127
11
  export async function executeAcpSpawn(
128
12
  input: Record<string, unknown>,
@@ -135,34 +19,11 @@ export async function executeAcpSpawn(
135
19
  return { content: '"task" is required.', isError: true };
136
20
  }
137
21
 
138
- const resolved = resolveAcpAgent(agent);
139
- if (!resolved.ok) {
140
- switch (resolved.reason) {
141
- case "acp_disabled":
142
- return { content: resolved.hint, isError: true };
143
- case "unknown_agent":
144
- return {
145
- content: `Unknown agent "${agent}". Available: ${resolved.available.join(
146
- ", ",
147
- )}.`,
148
- isError: true,
149
- };
150
- case "binary_not_found":
151
- return {
152
- content: `${resolved.command} is not on PATH. ${resolved.hint}`,
153
- isError: true,
154
- };
155
- default: {
156
- const _exhaustive: never = resolved;
157
- throw new Error(
158
- `Unexpected acp resolver reason: ${(_exhaustive as { reason: string }).reason}`,
159
- );
160
- }
161
- }
162
- }
163
- const sendToClient = context.sendToClient as
164
- | ((msg: { type: string; [key: string]: unknown }) => void)
165
- | undefined;
22
+ // Pure precondition: check for a connected client BEFORE any side effects
23
+ // (auto-install mutates the host via a `bun` global install and can block
24
+ // for up to the install timeout). Without a client the spawn cannot
25
+ // succeed anyway.
26
+ const sendToClient = getSendToClient(context);
166
27
  if (!sendToClient) {
167
28
  return {
168
29
  content: "No client connected - cannot spawn ACP agent.",
@@ -170,6 +31,17 @@ export async function executeAcpSpawn(
170
31
  };
171
32
  }
172
33
 
34
+ // Resolve the agent, silently auto-installing a missing allowlisted
35
+ // adapter binary (see acp/auto-install.ts). Shared with the HTTP route.
36
+ const { resolved, autoInstalledPackage, failureMessage } =
37
+ await resolveAgentWithAutoInstall(agent);
38
+ if (failureMessage) {
39
+ return { content: failureMessage, isError: true };
40
+ }
41
+ if (!resolved.ok) {
42
+ return { content: formatResolveFailure(agent, resolved), isError: true };
43
+ }
44
+
173
45
  // Inject required env vars and preflight via the shared helper. Mirrors
174
46
  // the HTTP route at `runtime/routes/acp-routes.ts:spawnSession` — both
175
47
  // call sites MUST go through `prepareAgentEnv` before `manager.spawn`,
@@ -184,10 +56,6 @@ export async function executeAcpSpawn(
184
56
  return { content: msg, isError: true };
185
57
  }
186
58
 
187
- // Best-effort version check — never blocks the spawn. If outdated, we
188
- // append a non-blocking warning to the success payload.
189
- const versionInfo = await checkAdapterVersion(agentConfig.command);
190
-
191
59
  try {
192
60
  const manager = getAcpSessionManager();
193
61
  const cwd = (input.cwd as string) || context.workingDir;
@@ -197,17 +65,21 @@ export async function executeAcpSpawn(
197
65
  task,
198
66
  cwd,
199
67
  context.conversationId,
200
- sendToClient as (msg: unknown) => void,
68
+ sendToClient,
201
69
  );
202
70
 
203
- // `claude --resume <id>` is Claude Code-specific (the claude-agent-acp
204
- // adapter binary). Other adapters resume differently or not at all,
205
- // so the hint is gated by the resolved binary, not the agent id —
206
- // this stays correct when a user aliases an id to a different binary.
207
- const resumeHint =
208
- agentConfig.command === "claude-agent-acp"
209
- ? ` To resume this session later, run: cd ${cwd} && claude --resume ${protocolSessionId}`
210
- : "";
71
+ // Claude Code-only resume hint; empty for other adapters. Keyed off the
72
+ // resolved command basename (always the real adapter binary). See
73
+ // acp/resume-hint.ts for the gating rationale.
74
+ const hint = claudeResumeHint(
75
+ basename(agentConfig.command),
76
+ cwd,
77
+ protocolSessionId,
78
+ );
79
+ const resumeHint = hint ? ` ${hint}` : "";
80
+ const installNote = autoInstalledPackage
81
+ ? ` Installed ${autoInstalledPackage} automatically.`
82
+ : "";
211
83
  const payload = JSON.stringify({
212
84
  acpSessionId,
213
85
  protocolSessionId,
@@ -216,18 +88,11 @@ export async function executeAcpSpawn(
216
88
  status: "running",
217
89
  message:
218
90
  `ACP agent "${agent}" spawned (session: ${protocolSessionId}). ` +
219
- `Results stream back via SSE. You will be notified when it completes.${resumeHint}`,
91
+ `Results stream back via SSE. You will be notified when it completes.` +
92
+ `${installNote}${resumeHint}`,
220
93
  });
221
94
 
222
- let content = payload;
223
- if (versionInfo) {
224
- content +=
225
- `\n\nNote: ${versionInfo.packageName} is outdated ` +
226
- `(installed: ${versionInfo.installed}, latest: ${versionInfo.latest}). ` +
227
- `To update, run: npm install -g ${versionInfo.packageName}@${versionInfo.latest}`;
228
- }
229
-
230
- return { content, isError: false };
95
+ return { content: payload, isError: false };
231
96
  } catch (err) {
232
97
  const msg =
233
98
  err instanceof Error
@@ -7,6 +7,7 @@ interface SteerCall {
7
7
  instruction: string;
8
8
  }
9
9
 
10
+ // Direct steer (no connected client -> no resume fallback).
10
11
  const steerCalls: SteerCall[] = [];
11
12
  const defaultSteer = (acpSessionId: string, instruction: string) => {
12
13
  steerCalls.push({ acpSessionId, instruction });
@@ -15,6 +16,13 @@ const defaultSteer = (acpSessionId: string, instruction: string) => {
15
16
  let steerImpl: (acpSessionId: string, instruction: string) => Promise<void> =
16
17
  defaultSteer;
17
18
 
19
+ // steerOrResume (connected client path).
20
+ const steerOrResumeCalls: Array<SteerCall & { send: unknown }> = [];
21
+ let steerOrResumeImpl: (
22
+ acpSessionId: string,
23
+ instruction: string,
24
+ ) => Promise<{ resumed: boolean }> = async () => ({ resumed: false });
25
+
18
26
  // Spread the real module's exports so transitive importers that pull other
19
27
  // names from `../../acp/index.js` still resolve at parse time. Bun's `mock.module` is
20
28
  // process-global and returns *exactly* the keys the factory returns.
@@ -24,35 +32,50 @@ mock.module("../../acp/index.js", () => ({
24
32
  getAcpSessionManager: () => ({
25
33
  steer: (acpSessionId: string, instruction: string) =>
26
34
  steerImpl(acpSessionId, instruction),
35
+ steerOrResume: (
36
+ acpSessionId: string,
37
+ instruction: string,
38
+ send: unknown,
39
+ ) => {
40
+ steerOrResumeCalls.push({ acpSessionId, instruction, send });
41
+ return steerOrResumeImpl(acpSessionId, instruction);
42
+ },
27
43
  }),
28
44
  }));
29
45
 
30
46
  const { executeAcpSteer } = await import("./steer.js");
31
47
 
32
- function makeContext(): ToolContext {
48
+ function makeContext(opts?: { withClient?: boolean }): ToolContext {
33
49
  return {
34
50
  workingDir: "/tmp",
35
51
  conversationId: "conv-test",
36
52
  trustClass: "guardian",
37
- };
53
+ ...(opts?.withClient ? { sendToClient: () => {} } : {}),
54
+ } as ToolContext;
38
55
  }
39
56
 
40
57
  beforeEach(() => {
41
58
  steerCalls.length = 0;
59
+ steerOrResumeCalls.length = 0;
42
60
  steerImpl = defaultSteer;
61
+ steerOrResumeImpl = async () => ({ resumed: false });
43
62
  });
44
63
 
45
64
  describe("executeAcpSteer", () => {
46
- test("happy path: returns steered status and forwards args to manager", async () => {
65
+ test("happy path: returns steered status and forwards args to steerOrResume", async () => {
47
66
  const result = await executeAcpSteer(
48
67
  { acp_session_id: "acp-123", instruction: "stop, do X instead" },
49
- makeContext(),
68
+ makeContext({ withClient: true }),
50
69
  );
51
70
 
52
71
  expect(result.isError).toBe(false);
53
- expect(steerCalls).toEqual([
54
- { acpSessionId: "acp-123", instruction: "stop, do X instead" },
55
- ]);
72
+ expect(steerOrResumeCalls).toHaveLength(1);
73
+ expect(steerOrResumeCalls[0]).toMatchObject({
74
+ acpSessionId: "acp-123",
75
+ instruction: "stop, do X instead",
76
+ });
77
+ expect(typeof steerOrResumeCalls[0]!.send).toBe("function");
78
+ expect(steerCalls).toEqual([]);
56
79
 
57
80
  const parsed = JSON.parse(result.content as string);
58
81
  expect(parsed).toEqual({
@@ -71,6 +94,7 @@ describe("executeAcpSteer", () => {
71
94
  expect(result.isError).toBe(true);
72
95
  expect(result.content).toContain('"instruction" is required');
73
96
  expect(steerCalls).toEqual([]);
97
+ expect(steerOrResumeCalls).toEqual([]);
74
98
  });
75
99
 
76
100
  test("missing acp_session_id returns isError", async () => {
@@ -82,9 +106,23 @@ describe("executeAcpSteer", () => {
82
106
  expect(result.isError).toBe(true);
83
107
  expect(result.content).toContain('"acp_session_id" is required');
84
108
  expect(steerCalls).toEqual([]);
109
+ expect(steerOrResumeCalls).toEqual([]);
110
+ });
111
+
112
+ test("no connected client: steers directly and never resumes", async () => {
113
+ const result = await executeAcpSteer(
114
+ { acp_session_id: "acp-direct", instruction: "redirect" },
115
+ makeContext(),
116
+ );
117
+
118
+ expect(result.isError).toBe(false);
119
+ expect(steerCalls).toEqual([
120
+ { acpSessionId: "acp-direct", instruction: "redirect" },
121
+ ]);
122
+ expect(steerOrResumeCalls).toEqual([]);
85
123
  });
86
124
 
87
- test("manager.steer throwing 'session not found' surfaces the error", async () => {
125
+ test("'session not found' without a connected client surfaces the error", async () => {
88
126
  steerImpl = () =>
89
127
  Promise.reject(new Error('ACP session "acp-x" not found'));
90
128
 
@@ -96,5 +134,64 @@ describe("executeAcpSteer", () => {
96
134
  expect(result.isError).toBe(true);
97
135
  expect(result.content).toContain('Could not steer ACP session "acp-x"');
98
136
  expect(result.content).toContain("not found");
137
+ expect(steerOrResumeCalls).toEqual([]);
138
+ });
139
+
140
+ test("resumed session reports the resumed flag and message", async () => {
141
+ steerOrResumeImpl = async () => ({ resumed: true });
142
+
143
+ const result = await executeAcpSteer(
144
+ { acp_session_id: "acp-gone", instruction: "keep going" },
145
+ makeContext({ withClient: true }),
146
+ );
147
+
148
+ expect(result.isError).toBe(false);
149
+ expect(steerOrResumeCalls).toHaveLength(1);
150
+
151
+ const parsed = JSON.parse(result.content as string);
152
+ expect(parsed).toEqual({
153
+ acpSessionId: "acp-gone",
154
+ status: "steered",
155
+ resumed: true,
156
+ message:
157
+ "Session was resumed from history; new instruction is now running.",
158
+ });
159
+ });
160
+
161
+ test("resume failure returns its actionable error message", async () => {
162
+ steerOrResumeImpl = () =>
163
+ Promise.reject(
164
+ new Error(
165
+ 'ACP session "acp-legacy" was recorded before resume support (no working directory persisted) and cannot be resumed. Spawn a new session instead.',
166
+ ),
167
+ );
168
+
169
+ const result = await executeAcpSteer(
170
+ { acp_session_id: "acp-legacy", instruction: "more work" },
171
+ makeContext({ withClient: true }),
172
+ );
173
+
174
+ expect(result.isError).toBe(true);
175
+ expect(result.content).toContain(
176
+ 'Could not steer ACP session "acp-legacy"',
177
+ );
178
+ expect(result.content).toContain("recorded before resume support");
179
+ });
180
+
181
+ test("plain steer errors surface unchanged", async () => {
182
+ steerOrResumeImpl = () =>
183
+ Promise.reject(
184
+ new Error(
185
+ 'ACP session "acp-init" is not running (status: initializing)',
186
+ ),
187
+ );
188
+
189
+ const result = await executeAcpSteer(
190
+ { acp_session_id: "acp-init", instruction: "redirect" },
191
+ makeContext({ withClient: true }),
192
+ );
193
+
194
+ expect(result.isError).toBe(true);
195
+ expect(result.content).toContain("is not running");
99
196
  });
100
197
  });
@@ -1,9 +1,10 @@
1
1
  import { getAcpSessionManager } from "../../acp/index.js";
2
2
  import type { ToolContext, ToolExecutionResult } from "../types.js";
3
+ import { getSendToClient } from "./context.js";
3
4
 
4
5
  export async function executeAcpSteer(
5
6
  input: Record<string, unknown>,
6
- _context: ToolContext,
7
+ context: ToolContext,
7
8
  ): Promise<ToolExecutionResult> {
8
9
  const acpSessionId = input.acp_session_id as string;
9
10
  if (!acpSessionId) {
@@ -15,24 +16,54 @@ export async function executeAcpSteer(
15
16
  return { content: '"instruction" is required.', isError: true };
16
17
  }
17
18
 
18
- try {
19
- const manager = getAcpSessionManager();
20
- await manager.steer(acpSessionId, instruction);
19
+ const manager = getAcpSessionManager();
20
+ const sendToClient = getSendToClient(context);
21
21
 
22
- return {
23
- content: JSON.stringify({
24
- acpSessionId,
25
- status: "steered",
26
- message:
27
- "Interrupted in-flight prompt; new instruction is now running.",
28
- }),
29
- isError: false,
30
- };
22
+ try {
23
+ if (!sendToClient) {
24
+ // Without a connected client there is no one to receive a resumed
25
+ // session's events, so skip the transparent resume fallback and
26
+ // steer the in-memory session only.
27
+ await manager.steer(acpSessionId, instruction);
28
+ return steeredResult(acpSessionId, { resumed: false });
29
+ }
30
+ // Sessions no longer in memory (completed, or lost to a daemon
31
+ // restart) are transparently resumed from persisted history and the
32
+ // instruction fired in the same call. Failure messages carry the
33
+ // actionable hint (e.g. "recorded before resume support", agent
34
+ // capability missing).
35
+ const { resumed } = await manager.steerOrResume(
36
+ acpSessionId,
37
+ instruction,
38
+ sendToClient,
39
+ );
40
+ return steeredResult(acpSessionId, { resumed });
31
41
  } catch (err) {
32
42
  const msg = err instanceof Error ? err.message : String(err);
33
- return {
34
- content: `Could not steer ACP session "${acpSessionId}": ${msg}`,
35
- isError: true,
36
- };
43
+ return steerError(acpSessionId, msg);
37
44
  }
38
45
  }
46
+
47
+ function steeredResult(
48
+ acpSessionId: string,
49
+ opts: { resumed: boolean },
50
+ ): ToolExecutionResult {
51
+ return {
52
+ content: JSON.stringify({
53
+ acpSessionId,
54
+ status: "steered",
55
+ ...(opts.resumed ? { resumed: true } : {}),
56
+ message: opts.resumed
57
+ ? "Session was resumed from history; new instruction is now running."
58
+ : "Interrupted in-flight prompt; new instruction is now running.",
59
+ }),
60
+ isError: false,
61
+ };
62
+ }
63
+
64
+ function steerError(acpSessionId: string, msg: string): ToolExecutionResult {
65
+ return {
66
+ content: `Could not steer ACP session "${acpSessionId}": ${msg}`,
67
+ isError: true,
68
+ };
69
+ }