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

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