@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea

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 (570) hide show
  1. package/Dockerfile +20 -4
  2. package/bun.lock +2 -2
  3. package/docker-entrypoint.sh +4 -2
  4. package/docker-init-apt-root.sh +3 -1
  5. package/docker-kata-apt-env.sh +3 -1
  6. package/docker-kata-runtime-family.sh +12 -0
  7. package/docs/architecture/memory.md +1 -1
  8. package/examples/plugins/echo/README.md +61 -66
  9. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  10. package/examples/plugins/echo/hooks/stop.ts +16 -0
  11. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  12. package/examples/plugins/echo/package.json +1 -2
  13. package/examples/plugins/echo/src/emit.ts +19 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  15. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  16. package/openapi.yaml +3378 -335
  17. package/package.json +2 -2
  18. package/scripts/generate-openapi.ts +68 -41
  19. package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  21. package/src/__tests__/agent-loop.test.ts +37 -87
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  23. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  24. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  25. package/src/__tests__/anthropic-provider.test.ts +95 -2
  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__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  30. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  31. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  32. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  33. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  34. package/src/__tests__/btw-routes.test.ts +62 -3
  35. package/src/__tests__/build-persisted-content.test.ts +184 -0
  36. package/src/__tests__/catalog-files.test.ts +1 -1
  37. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  38. package/src/__tests__/channel-approvals.test.ts +1 -1
  39. package/src/__tests__/clawhub-files.test.ts +1 -1
  40. package/src/__tests__/compaction-circuit.test.ts +258 -0
  41. package/src/__tests__/compaction-direct.test.ts +132 -0
  42. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  43. package/src/__tests__/config-watcher.test.ts +1 -1
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
  48. package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
  49. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  50. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  51. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  52. package/src/__tests__/conversation-pairing.test.ts +4 -31
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
  55. package/src/__tests__/conversation-queue.test.ts +2 -0
  56. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  57. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  58. package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
  59. package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
  60. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  61. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  62. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  63. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  64. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  65. package/src/__tests__/conversation-title-service.test.ts +135 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
  67. package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  71. package/src/__tests__/db-acp-history.test.ts +101 -0
  72. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  73. package/src/__tests__/dm-persistence.test.ts +5 -1
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +304 -0
  76. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  77. package/src/__tests__/file-write-tool.test.ts +63 -0
  78. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  79. package/src/__tests__/gemini-image-service.test.ts +13 -0
  80. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  81. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  82. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  83. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  84. package/src/__tests__/heartbeat-service.test.ts +1 -0
  85. package/src/__tests__/helpers/mock-provider.ts +110 -0
  86. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  87. package/src/__tests__/history-repair-hook.test.ts +1 -0
  88. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  89. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  90. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  91. package/src/__tests__/identity-routes.test.ts +248 -7
  92. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  93. package/src/__tests__/injector-background-turn.test.ts +3 -9
  94. package/src/__tests__/injector-chain.test.ts +139 -275
  95. package/src/__tests__/injector-disk-pressure.test.ts +75 -41
  96. package/src/__tests__/injector-document-comments.test.ts +3 -3
  97. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  98. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  99. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  100. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  101. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  102. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  103. package/src/__tests__/llm-usage-store.test.ts +223 -1
  104. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  105. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  106. package/src/__tests__/native-web-search.test.ts +191 -0
  107. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  108. package/src/__tests__/openai-image-service.test.ts +17 -0
  109. package/src/__tests__/openai-provider.test.ts +31 -1
  110. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
  111. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  112. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  113. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  114. package/src/__tests__/plugin-api-shim.test.ts +3 -6
  115. package/src/__tests__/plugin-bootstrap.test.ts +14 -40
  116. package/src/__tests__/plugin-registry.test.ts +3 -76
  117. package/src/__tests__/plugin-types.test.ts +0 -193
  118. package/src/__tests__/process-message-display-content.test.ts +6 -2
  119. package/src/__tests__/reaction-persistence.test.ts +1 -1
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  121. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  122. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  123. package/src/__tests__/schedule-routes.test.ts +603 -2
  124. package/src/__tests__/schedule-store.test.ts +41 -0
  125. package/src/__tests__/schedule-tools.test.ts +35 -0
  126. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  127. package/src/__tests__/server-history-render.test.ts +314 -1
  128. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  129. package/src/__tests__/skillssh-files.test.ts +1 -1
  130. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  132. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  133. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  134. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  135. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  136. package/src/__tests__/system-prompt.test.ts +20 -0
  137. package/src/__tests__/task-scheduler.test.ts +162 -1
  138. package/src/__tests__/terminal-tools.test.ts +6 -1
  139. package/src/__tests__/title-generate-hook.test.ts +319 -0
  140. package/src/__tests__/tool-error-hook.test.ts +278 -0
  141. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  142. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  143. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  144. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  145. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  146. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  147. package/src/__tests__/usage-routes.test.ts +285 -1
  148. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  149. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  150. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  151. package/src/acp/__tests__/agent-process.test.ts +161 -0
  152. package/src/acp/__tests__/client-handler.test.ts +40 -0
  153. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  154. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  155. package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
  156. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  157. package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
  158. package/src/acp/agent-process.ts +61 -1
  159. package/src/acp/auto-install.test.ts +196 -0
  160. package/src/acp/auto-install.ts +177 -0
  161. package/src/acp/client-handler.ts +31 -0
  162. package/src/acp/feature-gate.test.ts +48 -0
  163. package/src/acp/feature-gate.ts +34 -0
  164. package/src/acp/prepare-agent-env.ts +83 -29
  165. package/src/acp/resolve-agent.test.ts +320 -7
  166. package/src/acp/resolve-agent.ts +182 -18
  167. package/src/acp/resume-hint.ts +25 -0
  168. package/src/acp/session-manager.ts +495 -73
  169. package/src/acp/types.ts +8 -0
  170. package/src/agent/compaction-circuit.ts +60 -102
  171. package/src/agent/loop.ts +362 -485
  172. package/src/api/events/assistant-thinking-delta.ts +33 -0
  173. package/src/api/events/tool-output-chunk.ts +45 -0
  174. package/src/api/events/tool-use-preview-start.ts +32 -0
  175. package/src/api/events/trace-event.ts +69 -0
  176. package/src/api/index.ts +48 -13
  177. package/src/api/responses/conversation-message.ts +374 -0
  178. package/src/approvals/guardian-request-resolvers.ts +1 -1
  179. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  180. package/src/background-wake/next-wake.ts +1 -0
  181. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  182. package/src/cli/commands/notifications.ts +112 -60
  183. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  184. package/src/config/acp-defaults.test.ts +10 -0
  185. package/src/config/acp-defaults.ts +6 -0
  186. package/src/config/assistant-feature-flags.ts +22 -11
  187. package/src/config/bundled-skills/acp/SKILL.md +83 -31
  188. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  189. package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
  190. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  191. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  192. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  193. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  194. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  195. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  196. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  197. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  198. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  199. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  200. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  201. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  202. package/src/config/bundled-tool-registry.ts +2 -0
  203. package/src/config/feature-flag-cache.ts +3 -3
  204. package/src/config/feature-flag-registry.json +48 -7
  205. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  207. package/src/config/schemas/heartbeat.ts +9 -0
  208. package/src/config/schemas/llm.ts +1 -0
  209. package/src/config/schemas/memory-v2.ts +8 -0
  210. package/src/config/schemas/memory-v3.ts +8 -0
  211. package/src/config/schemas/platform.ts +8 -0
  212. package/src/config/seed-inference-profiles.ts +2 -2
  213. package/src/config/skills.ts +13 -0
  214. package/src/context/compactor.ts +1 -1
  215. package/src/context/strip-injections.ts +128 -0
  216. package/src/context/token-estimator.ts +23 -0
  217. package/src/context/tool-result-truncation.ts +0 -23
  218. package/src/context/window-manager.ts +5 -7
  219. package/src/credential-execution/executable-discovery.ts +16 -0
  220. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  221. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  222. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  223. package/src/daemon/assistant-attachments.ts +1 -1
  224. package/src/daemon/config-watcher.ts +2 -2
  225. package/src/daemon/context-overflow-reducer.ts +0 -1
  226. package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
  227. package/src/daemon/conversation-agent-loop.ts +301 -997
  228. package/src/daemon/conversation-history.ts +5 -4
  229. package/src/daemon/conversation-lifecycle.ts +3 -4
  230. package/src/daemon/conversation-messaging.ts +7 -6
  231. package/src/daemon/conversation-process.ts +11 -16
  232. package/src/daemon/conversation-registry.ts +159 -0
  233. package/src/daemon/conversation-runtime-assembly.ts +218 -398
  234. package/src/daemon/conversation-slash.ts +6 -25
  235. package/src/daemon/conversation-store.ts +9 -90
  236. package/src/daemon/conversation-surfaces.ts +222 -4
  237. package/src/daemon/conversation-tool-setup.ts +2 -29
  238. package/src/daemon/conversation-workspace.ts +17 -0
  239. package/src/daemon/conversation.ts +32 -20
  240. package/src/daemon/external-plugins-bootstrap.ts +17 -18
  241. package/src/daemon/handlers/config-a2a.ts +51 -36
  242. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  243. package/src/daemon/handlers/config-telegram.ts +16 -2
  244. package/src/daemon/handlers/conversations.ts +3 -1
  245. package/src/daemon/handlers/shared.ts +156 -84
  246. package/src/daemon/handlers/skills.ts +42 -10
  247. package/src/daemon/lifecycle.ts +25 -0
  248. package/src/daemon/message-types/apps.ts +1 -29
  249. package/src/daemon/message-types/messages.ts +9 -57
  250. package/src/daemon/message-types/skills.ts +2 -0
  251. package/src/daemon/message-types/surfaces.ts +136 -3
  252. package/src/daemon/now-scratchpad.ts +21 -0
  253. package/src/daemon/orphan-reaper.test.ts +210 -0
  254. package/src/daemon/orphan-reaper.ts +240 -0
  255. package/src/daemon/overflow-reduction-loop.ts +230 -0
  256. package/src/daemon/persist-unsendable-image.ts +117 -0
  257. package/src/daemon/process-message.ts +1 -3
  258. package/src/daemon/server.ts +2 -0
  259. package/src/daemon/trace-emitter.ts +6 -4
  260. package/src/daemon/trust-context.ts +19 -0
  261. package/src/daemon/wake-target-adapter.ts +3 -1
  262. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  263. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  264. package/src/heartbeat/heartbeat-service.ts +26 -0
  265. package/src/home/home-greeting-cache.ts +24 -1
  266. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  267. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  268. package/src/ipc/gateway-client.test.ts +2 -2
  269. package/src/ipc/gateway-client.ts +3 -3
  270. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  271. package/src/ipc/skill-routes/memory.ts +4 -2
  272. package/src/media/gemini-image-service.ts +15 -0
  273. package/src/media/openai-image-service.ts +14 -0
  274. package/src/media/types.ts +34 -0
  275. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  276. package/src/memory/auth-fallback-events-store.ts +94 -0
  277. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  278. package/src/memory/conversation-title-service.ts +65 -41
  279. package/src/memory/db-init.ts +6 -0
  280. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  281. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  282. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  283. package/src/memory/jobs-store.ts +33 -0
  284. package/src/memory/jobs-worker.ts +32 -5
  285. package/src/memory/llm-usage-store.ts +224 -50
  286. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  287. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  288. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  289. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  290. package/src/memory/migrations/index.ts +3 -0
  291. package/src/memory/pkb/autoinject.ts +61 -0
  292. package/src/memory/pkb/context.ts +50 -0
  293. package/src/memory/pkb/types.ts +14 -0
  294. package/src/memory/schedule-attribution-sql.ts +104 -0
  295. package/src/memory/schema/acp.ts +4 -0
  296. package/src/memory/schema/infrastructure.ts +16 -0
  297. package/src/memory/usage-grouped-buckets.ts +6 -1
  298. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
  299. package/src/memory/v2/consolidation-job.ts +14 -5
  300. package/src/notifications/conversation-pairing.ts +8 -15
  301. package/src/notifications/decision-engine.ts +6 -3
  302. package/src/notifications/home-feed-side-effect.ts +12 -1
  303. package/src/permissions/prompter.ts +4 -0
  304. package/src/plugin-api/constants.ts +4 -0
  305. package/src/plugin-api/index.ts +7 -5
  306. package/src/plugin-api/types.ts +151 -1
  307. package/src/plugins/defaults/compaction/compact.ts +59 -0
  308. package/src/plugins/defaults/compaction/package.json +1 -1
  309. package/src/plugins/defaults/compaction/register.ts +8 -19
  310. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  311. package/src/plugins/defaults/empty-response/register.ts +8 -13
  312. package/src/plugins/defaults/index.ts +2 -18
  313. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  314. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  315. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  316. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
  317. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  318. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
  319. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  321. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
  322. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  323. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  324. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  325. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
  326. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
  328. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
  329. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
  330. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
  331. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  332. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  333. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  334. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  335. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  336. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
  337. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
  338. package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
  339. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  340. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  341. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
  342. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  343. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  344. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
  345. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  346. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
  347. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
  348. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  349. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
  350. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  351. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  352. package/src/plugins/defaults/title-generate/package.json +1 -1
  353. package/src/plugins/defaults/title-generate/register.ts +18 -18
  354. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  355. package/src/plugins/defaults/tool-error/package.json +1 -1
  356. package/src/plugins/defaults/tool-error/register.ts +9 -21
  357. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  358. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  359. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  360. package/src/plugins/external-api.ts +2 -2
  361. package/src/plugins/pipeline.ts +6 -305
  362. package/src/plugins/registry.ts +10 -55
  363. package/src/plugins/types.ts +62 -797
  364. package/src/plugins/user-loader.ts +30 -127
  365. package/src/proactive-artifact/aux-message-injector.ts +4 -4
  366. package/src/proactive-artifact/job.test.ts +8 -13
  367. package/src/prompts/__tests__/system-prompt.test.ts +42 -0
  368. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  369. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  370. package/src/prompts/templates/system-sections.ts +15 -0
  371. package/src/providers/anthropic/client.ts +37 -29
  372. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  373. package/src/providers/openai/chat-completions-provider.ts +44 -0
  374. package/src/providers/openrouter/client.ts +1 -0
  375. package/src/providers/placeholder-sentinels.ts +35 -0
  376. package/src/runtime/__tests__/agent-wake.test.ts +10 -6
  377. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  378. package/src/runtime/agent-wake.ts +2 -5
  379. package/src/runtime/assistant-event-hub.ts +37 -7
  380. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  381. package/src/runtime/channel-approvals.ts +1 -1
  382. package/src/runtime/http-router.ts +16 -21
  383. package/src/runtime/http-types.ts +16 -70
  384. package/src/runtime/interactive-ui.ts +1 -1
  385. package/src/runtime/pending-interactions.ts +1 -0
  386. package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
  387. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  388. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  389. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  390. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  391. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  392. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  393. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  394. package/src/runtime/routes/acp-routes.test.ts +89 -25
  395. package/src/runtime/routes/acp-routes.ts +81 -29
  396. package/src/runtime/routes/app-management-routes.ts +6 -117
  397. package/src/runtime/routes/app-routes.ts +13 -15
  398. package/src/runtime/routes/approval-routes.ts +1 -1
  399. package/src/runtime/routes/attachment-routes.ts +26 -15
  400. package/src/runtime/routes/avatar-routes.ts +26 -0
  401. package/src/runtime/routes/browser-routes.ts +1 -1
  402. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  403. package/src/runtime/routes/btw-routes.ts +29 -23
  404. package/src/runtime/routes/consolidation-routes.ts +120 -20
  405. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  406. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  407. package/src/runtime/routes/conversation-query-routes.ts +3 -1
  408. package/src/runtime/routes/conversation-routes.ts +372 -185
  409. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  410. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  411. package/src/runtime/routes/documents-routes.ts +4 -0
  412. package/src/runtime/routes/domain-routes.ts +51 -37
  413. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  414. package/src/runtime/routes/events-routes.ts +28 -34
  415. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  416. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  417. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  418. package/src/runtime/routes/host-cu-routes.ts +1 -1
  419. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  420. package/src/runtime/routes/identity-routes.ts +224 -18
  421. package/src/runtime/routes/image-generation-routes.ts +40 -2
  422. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  423. package/src/runtime/routes/index.ts +2 -0
  424. package/src/runtime/routes/integrations/a2a.ts +12 -10
  425. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  426. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  427. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  428. package/src/runtime/routes/integrations/telegram.ts +6 -0
  429. package/src/runtime/routes/integrations/twilio.ts +42 -0
  430. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  431. package/src/runtime/routes/log-export-routes.ts +8 -0
  432. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  433. package/src/runtime/routes/memory-v3-routes.ts +66 -34
  434. package/src/runtime/routes/oauth-apps.ts +66 -12
  435. package/src/runtime/routes/oauth-providers.ts +44 -5
  436. package/src/runtime/routes/platform-routes.ts +81 -5
  437. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  438. package/src/runtime/routes/playground/force-compact.ts +1 -1
  439. package/src/runtime/routes/playground/helpers.ts +1 -1
  440. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  441. package/src/runtime/routes/schedule-routes.ts +152 -42
  442. package/src/runtime/routes/secret-routes.ts +14 -2
  443. package/src/runtime/routes/skills-routes.ts +43 -14
  444. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  445. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  446. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  447. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  448. package/src/runtime/routes/tts-routes.ts +35 -0
  449. package/src/runtime/routes/types.ts +66 -8
  450. package/src/runtime/routes/usage-routes.ts +47 -39
  451. package/src/runtime/routes/webhook-routes.ts +41 -2
  452. package/src/runtime/routes/work-items-routes.ts +2 -4
  453. package/src/runtime/routes/workspace-routes.ts +4 -0
  454. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  455. package/src/runtime/services/analyze-conversation.ts +2 -2
  456. package/src/runtime/services/conversation-serializer.ts +1 -1
  457. package/src/schedule/schedule-store.ts +20 -1
  458. package/src/schedule/schedule-usage-store.ts +83 -0
  459. package/src/schedule/scheduler.ts +12 -5
  460. package/src/signals/cancel.ts +2 -4
  461. package/src/skills/catalog-files.ts +2 -2
  462. package/src/skills/catalog-install.ts +3 -0
  463. package/src/skills/categories-cache.ts +118 -0
  464. package/src/skills/clawhub-files.ts +1 -2
  465. package/src/skills/skillssh-files.ts +1 -2
  466. package/src/subagent/manager.ts +17 -5
  467. package/src/telemetry/types.ts +29 -1
  468. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  469. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  470. package/src/tools/acp/context.ts +20 -0
  471. package/src/tools/acp/list-agents.test.ts +7 -1
  472. package/src/tools/acp/spawn.test.ts +158 -55
  473. package/src/tools/acp/spawn.ts +47 -72
  474. package/src/tools/acp/steer.test.ts +105 -8
  475. package/src/tools/acp/steer.ts +48 -17
  476. package/src/tools/apps/executors.ts +13 -8
  477. package/src/tools/executor.ts +1 -53
  478. package/src/tools/filesystem/write.ts +34 -0
  479. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  480. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  481. package/src/tools/network/web-search-error.test.ts +248 -0
  482. package/src/tools/network/web-search-error.ts +267 -0
  483. package/src/tools/network/web-search.ts +207 -48
  484. package/src/tools/schedule/create.ts +2 -0
  485. package/src/tools/subagent/spawn.ts +2 -4
  486. package/src/tools/terminal/safe-env.ts +10 -1
  487. package/src/tools/ui-surface/definitions.ts +34 -5
  488. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  489. package/src/tts/provider-catalog.ts +76 -1
  490. package/src/util/mutex.ts +47 -0
  491. package/src/workspace/git-service.ts +1 -42
  492. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  493. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  494. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  495. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  496. package/src/workspace/migrations/registry.ts +6 -0
  497. package/docs/plugins.md +0 -836
  498. package/examples/plugins/echo/register.ts +0 -184
  499. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  500. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  501. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  502. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  503. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  504. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  505. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  506. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  507. package/src/__tests__/pipeline-runner.test.ts +0 -564
  508. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  509. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  510. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  511. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  512. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  513. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  514. package/src/gallery/default-gallery.ts +0 -1359
  515. package/src/gallery/gallery-manifest.ts +0 -28
  516. package/src/home/feature-gate.ts +0 -22
  517. package/src/memory/v3/provider-blocks.ts +0 -16
  518. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  519. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  520. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  521. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  522. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  523. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  524. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  525. package/src/plugins/defaults/injectors/package.json +0 -15
  526. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  527. package/src/plugins/defaults/llm-call/register.ts +0 -45
  528. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  529. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  530. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  531. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  532. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  533. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  534. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  535. package/src/plugins/defaults/persistence/package.json +0 -15
  536. package/src/plugins/defaults/persistence/register.ts +0 -38
  537. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  538. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  539. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  540. package/src/plugins/defaults/token-estimate/package.json +0 -15
  541. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  542. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  543. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  544. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  545. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  546. package/src/plugins/defaults/tool-execute/package.json +0 -15
  547. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  548. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  549. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  550. package/src/skills/category-inference.ts +0 -111
  551. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  552. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  553. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  554. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  555. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  556. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  557. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  558. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
  559. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  560. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  561. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  562. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  563. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  564. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  565. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  566. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  567. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  568. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  569. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  570. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -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
+ }
@@ -76,14 +76,12 @@ export interface AppCreateInput {
76
76
  name: string;
77
77
  description?: string;
78
78
  schema_json?: string;
79
- /**
80
- * Retired single-file shortcut. Kept in the type so legacy or stale callers
81
- * get a clear tool error instead of silently creating a v2 app with stale
82
- * scaffold content.
83
- */
79
+ /** Retired single-file shortcut. Returns a guidance error. */
84
80
  html?: unknown;
85
- /** Retired single-file multi-page shortcut. */
81
+ /** Retired single-file multi-page shortcut. Returns a guidance error. */
86
82
  pages?: unknown;
83
+ /** Lenient alias. Folded into preview.icon when preview.icon is absent. */
84
+ icon?: unknown;
87
85
  auto_open?: boolean;
88
86
  preview?: Record<string, unknown>;
89
87
  source_files?: Record<string, string>;
@@ -98,11 +96,14 @@ export async function executeAppCreate(
98
96
  const description = input.description;
99
97
  const schemaJson = input.schema_json ?? "{}";
100
98
 
99
+ // Retired shortcut: a top-level `html` is no longer accepted. Reject with a
100
+ // helpful message (rather than a cryptic schema error) so the model writes a
101
+ // multi-file TSX app under src/ instead.
101
102
  if (Object.prototype.hasOwnProperty.call(input, "html")) {
102
103
  return {
103
104
  content: JSON.stringify({
104
105
  error:
105
- "app_create no longer accepts html. Create the app scaffold, write src/index.html and src/main.tsx with file_write, then call app_refresh.",
106
+ "app_create no longer accepts html. Build a multi-file TSX app under src/ (src/index.html + src/main.tsx + src/App.tsx) instead.",
106
107
  }),
107
108
  isError: true,
108
109
  };
@@ -157,7 +158,11 @@ export async function executeAppCreate(
157
158
 
158
159
  // Extract icon from preview if provided - only persist emoji-like values,
159
160
  // not URLs which would render as raw strings in UI and bundle manifests.
160
- const rawIcon = preview?.icon as string | undefined;
161
+ // Lenient alias: a top-level `icon` is folded in when preview.icon is absent.
162
+ const rawIcon = (preview?.icon ??
163
+ (typeof input.icon === "string" ? input.icon : undefined)) as
164
+ | string
165
+ | undefined;
161
166
  const icon = rawIcon && !rawIcon.startsWith("http") ? rawIcon : undefined;
162
167
 
163
168
  const app = store.createApp({
@@ -1,18 +1,10 @@
1
1
  import { readFileSync } from "node:fs";
2
2
 
3
- import { parseChannelId } from "../channels/types.js";
4
3
  import { getConfig } from "../config/loader.js";
5
4
  import { bridgeCesApproval } from "../credential-execution/approval-bridge.js";
6
5
  import { isCesShellLockdownEnabled } from "../credential-execution/feature-gates.js";
7
6
  import { PermissionPrompter } from "../permissions/prompter.js";
8
7
  import { RiskLevel } from "../permissions/types.js";
9
- import { runPipeline } from "../plugins/pipeline.js";
10
- import { getMiddlewaresFor } from "../plugins/registry.js";
11
- import type {
12
- ToolExecuteArgs,
13
- ToolExecuteResult,
14
- TurnContext,
15
- } from "../plugins/types.js";
16
8
  import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
17
9
  import { redactSensitiveFields } from "../security/redaction.js";
18
10
  import { TokenExpiredError } from "../security/token-manager.js";
@@ -52,54 +44,10 @@ export class ToolExecutor {
52
44
  name: string,
53
45
  input: Record<string, unknown>,
54
46
  context: ToolContext,
55
- /**
56
- * Optional per-turn context threaded in by the agent loop. Production
57
- * sites propagate the orchestrator-built `TurnContext` (real
58
- * `conversationId`, trust cascade, attached `contextWindowManager`) so
59
- * middleware registered on the `toolExecute` pipeline sees the same
60
- * context every other pipeline slot uses. When omitted (CLI/test
61
- * invocations that call `ToolExecutor.execute` directly), the executor
62
- * synthesizes a fallback context from the {@link ToolContext}.
63
- */
64
- turnContext?: TurnContext,
65
47
  ): Promise<ToolExecutionResult> {
66
- // Prefer the orchestrator-supplied `turnContext` so the pipeline sees
67
- // the real conversation identity, per-turn trust, and context-window
68
- // manager. When absent (CLI / test invocations that bypass the agent
69
- // loop), synthesize a minimal context from the `ToolContext`.
70
- const turnCtx: TurnContext = turnContext ?? {
71
- requestId: context.requestId ?? "",
72
- conversationId: context.conversationId,
73
- turnIndex: 0,
74
- trust: {
75
- sourceChannel: parseChannelId(context.executionChannel) ?? "vellum",
76
- trustClass: context.trustClass,
77
- },
78
- };
79
-
80
- const middlewares = getMiddlewaresFor("toolExecute");
81
48
  const { name: executionName, input: executionInput } =
82
49
  resolveToolInvocationAlias(name, input, context.allowedToolNames);
83
- const pipelineArgs: ToolExecuteArgs = {
84
- name: executionName,
85
- input: executionInput,
86
- context,
87
- };
88
-
89
- // No pipeline-level timeout: `executeInternal` already wraps the real
90
- // tool invocation in `executeWithTimeout`, which is the sole enforcer
91
- // of the per-tool budget. The pipeline itself runs untimed so that
92
- // upstream phases (permission checks, approval waits, middleware) are
93
- // not racing the tool budget — only the actual tool call is. Runaway
94
- // middleware is a plugin-health concern handled by per-plugin timeouts,
95
- // not here.
96
- return runPipeline<ToolExecuteArgs, ToolExecuteResult>(
97
- "toolExecute",
98
- middlewares,
99
- (args) => this.executeInternal(args.name, args.input, args.context),
100
- pipelineArgs,
101
- turnCtx,
102
- );
50
+ return this.executeInternal(executionName, executionInput, context);
103
51
  }
104
52
 
105
53
  private async executeInternal(
@@ -1,5 +1,6 @@
1
1
  import { join, resolve, sep } from "node:path";
2
2
 
3
+ import { getAppsDir } from "../../memory/app-store.js";
3
4
  import { enqueuePkbIndexJob } from "../../memory/jobs/embed-pkb-file.js";
4
5
  import { PKB_WORKSPACE_SCOPE } from "../../memory/pkb/types.js";
5
6
  import { RiskLevel } from "../../permissions/types.js";
@@ -17,6 +18,29 @@ import type {
17
18
 
18
19
  const logger = getLogger("file-write");
19
20
 
21
+ /**
22
+ * Detects an attempt to write a self-contained interactive HTML document —
23
+ * the signature of a "visualization" / "artifact" the model should be
24
+ * building as a real app via the app-builder skill, not dumping as a loose
25
+ * file. We deliberately trip ONLY on standalone docs with a substantial
26
+ * INLINE script (the data + rendering live in the file). app-builder's own
27
+ * scaffold writes a thin shell whose only script is an external module
28
+ * (`<script type="module" src="/src/main.tsx">`) with an empty body, so this
29
+ * never blocks the app-builder workflow it redirects to.
30
+ */
31
+ const STANDALONE_HTML_RE = /<!doctype\s+html|<html[\s>]/i;
32
+ const INLINE_SCRIPT_RE = /<script\b(?![^>]*\bsrc=)[^>]*>[\s\S]{400,}?<\/script>/i;
33
+
34
+ function isSelfContainedArtifactHtml(path: string, content: string): boolean {
35
+ if (!/\.html?$/i.test(path)) return false;
36
+ if (content.length < 3000) return false;
37
+ if (!STANDALONE_HTML_RE.test(content)) return false;
38
+ return INLINE_SCRIPT_RE.test(content);
39
+ }
40
+
41
+ const ARTIFACT_REDIRECT_MESSAGE =
42
+ 'Error: This looks like a self-contained interactive visualization/artifact written as a loose HTML file. Do not build these as standalone .html files. Load the app-builder skill first with `skill_load` using `skill: "app-builder"`, then build it as a real persistent app with `app_create` (the skill loads frontend-design for the visual pass and provides chart widgets). If this genuinely needs to be a raw HTML file inside an existing code project, write it under that project folder.';
43
+
20
44
  /**
21
45
  * Returns `true` iff `absPath` is an absolute path that resolves strictly
22
46
  * inside `pkbRoot`. Matches the containment semantics used elsewhere in the
@@ -82,6 +106,16 @@ export const fileWriteTool = {
82
106
  };
83
107
  }
84
108
 
109
+ // Redirect self-contained interactive HTML artifacts to app-builder.
110
+ // Exempt writes that land inside the apps directory (app-builder's own
111
+ // scaffold/iteration writes) so we never block the workflow we point to.
112
+ if (isSelfContainedArtifactHtml(rawPath, fileContent)) {
113
+ const candidate = resolve(context.workingDir, rawPath);
114
+ if (!isInsidePkbRoot(candidate, getAppsDir())) {
115
+ return { content: ARTIFACT_REDIRECT_MESSAGE, isError: true };
116
+ }
117
+ }
118
+
85
119
  const ops = new FileSystemOps((path, opts) =>
86
120
  sandboxPolicy(path, context.workingDir, opts),
87
121
  );
@@ -1,5 +1,7 @@
1
1
  import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
+ import { WEB_SEARCH_BACKEND_FAILURE_MESSAGE } from "../web-search-error.js";
4
+
3
5
  // Mutable mock state - set per test
4
6
  let mockWebSearchProvider: string | undefined = "perplexity";
5
7
  let mockBraveSecureKey: string | undefined;
@@ -32,7 +34,9 @@ mock.module("../../../security/secure-keys.js", () => ({
32
34
  },
33
35
  }));
34
36
 
37
+ const realLogger = await import("../../../util/logger.js");
35
38
  mock.module("../../../util/logger.js", () => ({
39
+ ...realLogger,
36
40
  getLogger: () =>
37
41
  new Proxy({} as Record<string, unknown>, {
38
42
  get: () => () => {},
@@ -169,7 +173,9 @@ describe("web_search activity metadata", () => {
169
173
  expect(meta.provider).toBe("perplexity");
170
174
  expect(meta.resultCount).toBe(0);
171
175
  expect(meta.results).toEqual([]);
172
- expect(meta.errorMessage).toContain("rate limit exceeded");
176
+ // Post-retry rate limits now surface the centralized friendly recoverable
177
+ // copy (ATL-727) rather than provider-specific rate-limit wording.
178
+ expect(meta.errorMessage).toBe(WEB_SEARCH_BACKEND_FAILURE_MESSAGE);
173
179
  });
174
180
 
175
181
  // ---- Tavily -------------------------------------------------------------
@@ -1,5 +1,7 @@
1
1
  import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
+ import { WEB_SEARCH_BACKEND_FAILURE_MESSAGE } from "../web-search-error.js";
4
+
3
5
  // Mutable mock state - set per test
4
6
  let mockWebSearchProvider: string | undefined = "perplexity";
5
7
  let mockWebSearchMode: string | undefined = "your-own";
@@ -42,7 +44,9 @@ mock.module("../../../security/secure-keys.js", () => ({
42
44
  },
43
45
  }));
44
46
 
47
+ const realLogger = await import("../../../util/logger.js");
45
48
  mock.module("../../../util/logger.js", () => ({
49
+ ...realLogger,
46
50
  getLogger: () =>
47
51
  new Proxy({} as Record<string, unknown>, {
48
52
  get: () => () => {},
@@ -201,7 +205,8 @@ describe("web_search tool", () => {
201
205
 
202
206
  const result = await execute({ query: "test" });
203
207
  expect(result.isError).toBe(true);
204
- expect(result.content).toContain("rate limit exceeded");
208
+ // Post-retry rate limits surface the friendly recoverable copy (ATL-727).
209
+ expect(result.content).toBe(WEB_SEARCH_BACKEND_FAILURE_MESSAGE);
205
210
  // 1 initial + 3 retries = 4 calls
206
211
  expect(callCount).toBe(4);
207
212
  });
@@ -214,7 +219,9 @@ describe("web_search tool", () => {
214
219
 
215
220
  const result = await execute({ query: "test" });
216
221
  expect(result.isError).toBe(true);
217
- expect(result.content).toContain("status 500");
222
+ // 5xx is a backend failure -> friendly recoverable copy, no raw status.
223
+ expect(result.content).toBe(WEB_SEARCH_BACKEND_FAILURE_MESSAGE);
224
+ expect(result.content).not.toContain("500");
218
225
  });
219
226
 
220
227
  // ---- Brave provider -----------------------------------------------------
@@ -673,7 +680,8 @@ describe("web_search tool", () => {
673
680
 
674
681
  const result = await execute({ query: "test" });
675
682
  expect(result.isError).toBe(true);
676
- expect(result.content).toContain("rate limit exceeded");
683
+ // Post-retry rate limits surface the friendly recoverable copy (ATL-727).
684
+ expect(result.content).toBe(WEB_SEARCH_BACKEND_FAILURE_MESSAGE);
677
685
  expect(callCount).toBe(4);
678
686
  });
679
687