@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
@@ -6,6 +6,7 @@ import type {
6
6
  import { RiskLevel } from "../../permissions/types.js";
7
7
  import { getProviderKeyAsync } from "../../security/secure-keys.js";
8
8
  import { wrapUntrustedContent } from "../../security/untrusted-content.js";
9
+ import { isAbortReason } from "../../util/abort-reasons.js";
9
10
  import { faviconUrlForDomain } from "../../util/favicon.js";
10
11
  import { getLogger } from "../../util/logger.js";
11
12
  import {
@@ -22,6 +23,11 @@ import type {
22
23
  } from "../types.js";
23
24
  import { extractDomain } from "./domain-normalize.js";
24
25
  import type { ManagedSearchProxyResult } from "./managed-search-proxy.js";
26
+ import {
27
+ classifyWebSearchFailure,
28
+ logWebSearchBackendFailure,
29
+ WEB_SEARCH_BACKEND_FAILURE_MESSAGE,
30
+ } from "./web-search-error.js";
25
31
 
26
32
  const log = getLogger("web-search");
27
33
 
@@ -381,6 +387,115 @@ function errorResult(
381
387
  };
382
388
  }
383
389
 
390
+ /**
391
+ * Wrap an already-read provider response body so {@link backendFailureResult}
392
+ * forwards it into the classifier's internal-only `rawDetail` (telemetry). The
393
+ * classifier reads `error.message`; `buildRawDetail` truncates to ≤500 chars.
394
+ * Returns `undefined` for an empty body so we don't pad `rawDetail` with noise.
395
+ * The body must NEVER reach user-facing `content`/`errorMessage`.
396
+ */
397
+ function rawBodyDetail(body: unknown): { message: string } | undefined {
398
+ if (body == null) return undefined;
399
+ const text =
400
+ typeof body === "string" ? body : safeStringifyBody(body);
401
+ const trimmed = text.trim();
402
+ return trimmed ? { message: trimmed } : undefined;
403
+ }
404
+
405
+ function safeStringifyBody(body: unknown): string {
406
+ try {
407
+ return JSON.stringify(body);
408
+ } catch {
409
+ return String(body);
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Build a {@link ToolExecutionResult} for a genuine backend/transport failure
415
+ * (5xx, post-retry rate-limit, thrown network/timeout error). Routes the raw
416
+ * detail through {@link classifyWebSearchFailure}: when it is a backend failure
417
+ * we surface the friendly recoverable copy (the bare sentence so the model
418
+ * reads it as guidance — retry / continue-without-search / paste-details —
419
+ * rather than fabricating) in both the model-facing `content` and the client
420
+ * `errorMessage`, and log the raw detail via telemetry. Non-backend categories
421
+ * (e.g. an unexpected 4xx) fall back to {@link errorResult} with `fallback`.
422
+ *
423
+ * Raw provider JSON / status text must never reach `content` or `errorMessage`;
424
+ * only `rawDetail` (internal-only) captures it for the log.
425
+ */
426
+ function backendFailureResult(
427
+ query: string,
428
+ provider: WebSearchProvider,
429
+ startedAt: number,
430
+ raw: { error?: unknown; statusCode?: number; errorCode?: string },
431
+ fallback: string,
432
+ ): ToolExecutionResult {
433
+ const classification = classifyWebSearchFailure({
434
+ isError: true,
435
+ error: raw.error,
436
+ statusCode: raw.statusCode,
437
+ errorCode: raw.errorCode,
438
+ });
439
+
440
+ if (!classification.isBackendFailure) {
441
+ return errorResult(query, provider, startedAt, fallback);
442
+ }
443
+
444
+ logWebSearchBackendFailure(log, {
445
+ provider,
446
+ errorCategory: classification.category,
447
+ rawDetail: classification.rawDetail,
448
+ fallbackShown: true,
449
+ queryLength: query.length,
450
+ });
451
+
452
+ return {
453
+ content: WEB_SEARCH_BACKEND_FAILURE_MESSAGE,
454
+ isError: true,
455
+ activityMetadata: {
456
+ webSearch: {
457
+ query,
458
+ provider,
459
+ resultCount: 0,
460
+ durationMs: Date.now() - startedAt,
461
+ results: [],
462
+ errorMessage: WEB_SEARCH_BACKEND_FAILURE_MESSAGE,
463
+ },
464
+ },
465
+ };
466
+ }
467
+
468
+ /**
469
+ * Route a thrown fetch error (network/timeout) through {@link backendFailureResult}
470
+ * as a `backend_unavailable` candidate, falling back to a `Web search failed: …`
471
+ * error for non-backend throws (e.g. a JSON parse error).
472
+ *
473
+ * If the caller aborted the request (`signal.aborted` — the user hit Stop/Esc,
474
+ * or an external caller cancelled), the thrown error is re-thrown so the
475
+ * executor's existing cancellation handling takes over. A user-cancel must NOT
476
+ * surface the friendly backend copy or emit `web_search_backend_failure`
477
+ * telemetry. Internal fetch timeouts (where the caller's signal is not aborted)
478
+ * still route to the friendly backend result.
479
+ */
480
+ function networkFailureResult(
481
+ query: string,
482
+ provider: WebSearchProvider,
483
+ startedAt: number,
484
+ err: unknown,
485
+ signal?: AbortSignal,
486
+ ): ToolExecutionResult {
487
+ if (signal?.aborted || isAbortReason((err as { reason?: unknown })?.reason)) {
488
+ throw err;
489
+ }
490
+ return backendFailureResult(
491
+ query,
492
+ provider,
493
+ startedAt,
494
+ { error: err },
495
+ `Web search failed: ${err instanceof Error ? err.message : String(err)}`,
496
+ );
497
+ }
498
+
384
499
  async function executeBraveSearch(
385
500
  query: string,
386
501
  count: number,
@@ -396,21 +511,26 @@ async function executeBraveSearch(
396
511
  const startedAt = Date.now();
397
512
 
398
513
  for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
399
- const response = await fetch(url, {
400
- headers: {
401
- Accept: "application/json",
402
- "Accept-Encoding": "gzip",
403
- "X-Subscription-Token": apiKey,
404
- },
405
- signal,
406
- });
514
+ let response: Response;
515
+ try {
516
+ response = await fetch(url, {
517
+ headers: {
518
+ Accept: "application/json",
519
+ "Accept-Encoding": "gzip",
520
+ "X-Subscription-Token": apiKey,
521
+ },
522
+ signal,
523
+ });
524
+ } catch (err) {
525
+ return networkFailureResult(query, "brave", startedAt, err, signal);
526
+ }
407
527
 
408
528
  if (response.ok) {
409
529
  const data = (await response.json()) as BraveSearchResponse;
410
530
  return successfulBraveResult(data, query, startedAt);
411
531
  }
412
532
 
413
- await response.text();
533
+ const bodyText = await response.text();
414
534
 
415
535
  if (response.status === 401 || response.status === 403) {
416
536
  return errorResult(
@@ -436,20 +556,22 @@ async function executeBraveSearch(
436
556
  }
437
557
 
438
558
  log.warn({ status: response.status }, "Brave Search API error");
439
- return errorResult(
559
+ return backendFailureResult(
440
560
  query,
441
561
  "brave",
442
562
  startedAt,
563
+ { statusCode: response.status, error: rawBodyDetail(bodyText) },
443
564
  response.status === 429
444
565
  ? "Brave Search rate limit exceeded after retries. Try again shortly."
445
566
  : `Brave Search API returned status ${response.status}`,
446
567
  );
447
568
  }
448
569
 
449
- return errorResult(
570
+ return backendFailureResult(
450
571
  query,
451
572
  "brave",
452
573
  startedAt,
574
+ { statusCode: 429 },
453
575
  "Brave Search rate limit exceeded after retries. Try again shortly.",
454
576
  );
455
577
  }
@@ -478,6 +600,23 @@ async function executeManagedBraveSearch(
478
600
  );
479
601
 
480
602
  if (!proxyResult.ok) {
603
+ // Keep billing/auth/unavailable mapping as specific copy; route genuine
604
+ // platform 5xx (transport-level failures) to the friendly backend helper.
605
+ if (
606
+ proxyResult.kind === "platform-error" &&
607
+ proxyResult.status >= 500
608
+ ) {
609
+ return backendFailureResult(
610
+ query,
611
+ "brave",
612
+ startedAt,
613
+ {
614
+ statusCode: proxyResult.status,
615
+ error: rawBodyDetail(proxyResult.body),
616
+ },
617
+ managedSearchProxyErrorMessage(proxyResult),
618
+ );
619
+ }
481
620
  return errorResult(
482
621
  query,
483
622
  "brave",
@@ -503,12 +642,18 @@ async function executeManagedBraveSearch(
503
642
  );
504
643
  }
505
644
 
506
- if (proxyResult.status === 429) {
507
- return errorResult(
645
+ if (proxyResult.status === 429 || proxyResult.status >= 500) {
646
+ return backendFailureResult(
508
647
  query,
509
648
  "brave",
510
649
  startedAt,
511
- "Managed Brave Search rate limit exceeded. Try again shortly.",
650
+ {
651
+ statusCode: proxyResult.status,
652
+ error: rawBodyDetail(proxyResult.body),
653
+ },
654
+ proxyResult.status === 429
655
+ ? "Managed Brave Search rate limit exceeded. Try again shortly."
656
+ : `Managed Brave Search provider returned status ${proxyResult.status}`,
512
657
  );
513
658
  }
514
659
 
@@ -545,18 +690,23 @@ async function executePerplexitySearch(
545
690
  ): Promise<ToolExecutionResult> {
546
691
  const startedAt = Date.now();
547
692
  for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
548
- const response = await fetch(PERPLEXITY_API_URL, {
549
- method: "POST",
550
- headers: {
551
- "Content-Type": "application/json",
552
- Authorization: `Bearer ${apiKey}`,
553
- },
554
- body: JSON.stringify({
555
- model: "sonar",
556
- messages: [{ role: "user", content: query }],
557
- }),
558
- signal,
559
- });
693
+ let response: Response;
694
+ try {
695
+ response = await fetch(PERPLEXITY_API_URL, {
696
+ method: "POST",
697
+ headers: {
698
+ "Content-Type": "application/json",
699
+ Authorization: `Bearer ${apiKey}`,
700
+ },
701
+ body: JSON.stringify({
702
+ model: "sonar",
703
+ messages: [{ role: "user", content: query }],
704
+ }),
705
+ signal,
706
+ });
707
+ } catch (err) {
708
+ return networkFailureResult(query, "perplexity", startedAt, err, signal);
709
+ }
560
710
 
561
711
  if (response.ok) {
562
712
  const data = (await response.json()) as PerplexityResponse;
@@ -574,7 +724,7 @@ async function executePerplexitySearch(
574
724
  };
575
725
  }
576
726
 
577
- await response.text();
727
+ const bodyText = await response.text();
578
728
 
579
729
  if (response.status === 401 || response.status === 403) {
580
730
  return errorResult(
@@ -600,20 +750,22 @@ async function executePerplexitySearch(
600
750
  }
601
751
 
602
752
  log.warn({ status: response.status }, "Perplexity API error");
603
- return errorResult(
753
+ return backendFailureResult(
604
754
  query,
605
755
  "perplexity",
606
756
  startedAt,
757
+ { statusCode: response.status, error: rawBodyDetail(bodyText) },
607
758
  response.status === 429
608
759
  ? "Perplexity rate limit exceeded after retries. Try again shortly."
609
760
  : `Perplexity API returned status ${response.status}`,
610
761
  );
611
762
  }
612
763
 
613
- return errorResult(
764
+ return backendFailureResult(
614
765
  query,
615
766
  "perplexity",
616
767
  startedAt,
768
+ { statusCode: 429 },
617
769
  "Perplexity rate limit exceeded after retries. Try again shortly.",
618
770
  );
619
771
  }
@@ -638,16 +790,21 @@ async function executeTavilySearch(
638
790
  const startedAt = Date.now();
639
791
 
640
792
  for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
641
- const response = await fetch(TAVILY_API_URL, {
642
- method: "POST",
643
- headers: {
644
- "Content-Type": "application/json",
645
- Authorization: `Bearer ${apiKey}`,
646
- "X-Client-Source": "vellum-assistant",
647
- },
648
- body: JSON.stringify(body),
649
- signal,
650
- });
793
+ let response: Response;
794
+ try {
795
+ response = await fetch(TAVILY_API_URL, {
796
+ method: "POST",
797
+ headers: {
798
+ "Content-Type": "application/json",
799
+ Authorization: `Bearer ${apiKey}`,
800
+ "X-Client-Source": "vellum-assistant",
801
+ },
802
+ body: JSON.stringify(body),
803
+ signal,
804
+ });
805
+ } catch (err) {
806
+ return networkFailureResult(query, "tavily", startedAt, err, signal);
807
+ }
651
808
 
652
809
  if (response.ok) {
653
810
  const data = (await response.json()) as TavilySearchResponse;
@@ -665,7 +822,7 @@ async function executeTavilySearch(
665
822
  };
666
823
  }
667
824
 
668
- await response.text();
825
+ const bodyText = await response.text();
669
826
 
670
827
  if (response.status === 401 || response.status === 403) {
671
828
  return errorResult(
@@ -691,20 +848,22 @@ async function executeTavilySearch(
691
848
  }
692
849
 
693
850
  log.warn({ status: response.status }, "Tavily Search API error");
694
- return errorResult(
851
+ return backendFailureResult(
695
852
  query,
696
853
  "tavily",
697
854
  startedAt,
855
+ { statusCode: response.status, error: rawBodyDetail(bodyText) },
698
856
  response.status === 429
699
857
  ? "Tavily Search rate limit exceeded after retries. Try again shortly."
700
858
  : `Tavily Search API returned status ${response.status}`,
701
859
  );
702
860
  }
703
861
 
704
- return errorResult(
862
+ return backendFailureResult(
705
863
  query,
706
864
  "tavily",
707
865
  startedAt,
866
+ { statusCode: 429 },
708
867
  "Tavily Search rate limit exceeded after retries. Try again shortly.",
709
868
  );
710
869
  }
@@ -844,13 +1003,13 @@ export const webSearchTool = {
844
1003
  signal: context.signal,
845
1004
  });
846
1005
  } catch (err) {
847
- const msg = err instanceof Error ? err.message : String(err);
848
1006
  log.error({ err }, "Managed web search failed");
849
- return errorResult(
1007
+ return networkFailureResult(
850
1008
  query,
851
1009
  "brave",
852
1010
  startedAt,
853
- `Managed web search failed: ${msg}`,
1011
+ err,
1012
+ context.signal,
854
1013
  );
855
1014
  }
856
1015
  }
@@ -897,13 +1056,13 @@ export const webSearchTool = {
897
1056
  signal: context.signal,
898
1057
  });
899
1058
  } catch (err) {
900
- const msg = err instanceof Error ? err.message : String(err);
901
1059
  log.error({ err }, "Web search failed");
902
- return errorResult(
1060
+ return networkFailureResult(
903
1061
  query,
904
1062
  provider,
905
1063
  startedAt,
906
- `Web search failed: ${msg}`,
1064
+ err,
1065
+ context.signal,
907
1066
  );
908
1067
  }
909
1068
  },
@@ -144,6 +144,7 @@ export async function executeScheduleCreate(
144
144
  maxRetries,
145
145
  retryBackoffMs,
146
146
  timeoutMs,
147
+ createdFromConversationId: context.conversationId,
147
148
  });
148
149
 
149
150
  const fireDate = formatLocalDate(job.nextRunAt);
@@ -225,6 +226,7 @@ export async function executeScheduleCreate(
225
226
  maxRetries,
226
227
  retryBackoffMs,
227
228
  timeoutMs,
229
+ createdFromConversationId: context.conversationId,
228
230
  });
229
231
 
230
232
  const scheduleDescription =
@@ -1,4 +1,4 @@
1
- import { findConversation } from "../../daemon/conversation-store.js";
1
+ import { findConversation } from "../../daemon/conversation-registry.js";
2
2
  import { getConversationOverrideProfile } from "../../memory/conversation-crud.js";
3
3
  import type { Message } from "../../providers/types.js";
4
4
  import { getSubagentManager } from "../../subagent/index.js";
@@ -102,9 +102,7 @@ export async function executeSubagentSpawn(
102
102
  ...(inheritedOverrideProfile
103
103
  ? { overrideProfile: inheritedOverrideProfile }
104
104
  : {}),
105
- ...(context.toolUseId
106
- ? { parentToolUseId: context.toolUseId }
107
- : {}),
105
+ ...(context.toolUseId ? { parentToolUseId: context.toolUseId } : {}),
108
106
  ...forkFields,
109
107
  },
110
108
  sendToClient as (msg: unknown) => void,
@@ -69,6 +69,15 @@ export const KATA_SAFE_ENV_VARS = [
69
69
  export const KATA_INJECTED_ENV_VARS = ["LD_LIBRARY_PATH"] as const;
70
70
 
71
71
  const KATA_APT_DATA_ROOT = "/data/system";
72
+ const KATA_FAMILY_SANDBOX_RUNTIMES = new Set([
73
+ "kata",
74
+ "firecracker",
75
+ "cloud-hypervisor",
76
+ ]);
77
+
78
+ function isKataFamilyRuntime(runtime: string | undefined): boolean {
79
+ return runtime != null && KATA_FAMILY_SANDBOX_RUNTIMES.has(runtime);
80
+ }
72
81
 
73
82
  function kataAptPaths(dataRoot: string): string[] {
74
83
  return [
@@ -118,7 +127,7 @@ function appendUniquePathEntries(
118
127
 
119
128
  export function buildSanitizedEnv(): Record<string, string> {
120
129
  const env: Record<string, string> = {};
121
- const isKataRuntime = process.env.VELLUM_SANDBOX_RUNTIME === "kata";
130
+ const isKataRuntime = isKataFamilyRuntime(process.env.VELLUM_SANDBOX_RUNTIME);
122
131
  const safeEnvVars = isKataRuntime
123
132
  ? [...SAFE_ENV_VARS, ...KATA_SAFE_ENV_VARS]
124
133
  : SAFE_ENV_VARS;
@@ -19,7 +19,7 @@ import type {
19
19
  // ---------------------------------------------------------------------------
20
20
 
21
21
  const APP_BUILDER_ARTIFACT_RE =
22
- /\b(app|apps|application|applications|website|websites|site|sites|dashboard|dashboards|game|games|calculator|calculators|tracker|trackers|visualization|visualizations|visualisation|visualisations|tool|tools|utility|utilities|counter|counters)\b/i;
22
+ /\b(app|apps|application|applications|website|websites|site|sites|dashboard|dashboards|game|games|calculator|calculators|tracker|trackers|visualization|visualizations|visualisation|visualisations|visualize|visualise|artifact|artifacts|chart|charts|graph|graphs|tool|tools|utility|utilities|counter|counters)\b/i;
23
23
  const APP_BUILDER_BUILD_RE =
24
24
  /\b(build|building|built|create|creating|created|make|making|made|generate|generating|generated)\b/i;
25
25
 
@@ -58,11 +58,32 @@ function isDynamicPageAppSubstitute(input: Record<string, unknown>): boolean {
58
58
  }
59
59
 
60
60
  const text = collectRoutingText(input).join(" ");
61
- if (!APP_BUILDER_ARTIFACT_RE.test(text)) {
62
- return false;
61
+ if (
62
+ APP_BUILDER_ARTIFACT_RE.test(text) &&
63
+ (APP_BUILDER_BUILD_RE.test(text) || /\b(app|application)\b/i.test(text))
64
+ ) {
65
+ return true;
63
66
  }
64
67
 
65
- return APP_BUILDER_BUILD_RE.test(text) || /\b(app|application)\b/i.test(text);
68
+ // Second signal: even when the model gives the surface a clean,
69
+ // non-app-sounding title (dodging the text regex above), substantial
70
+ // interactive HTML is an app being smuggled in as a transient surface.
71
+ // A genuinely transient page is small and static; an app has real
72
+ // scripted markup. Keep the bar high so simple snippets still pass.
73
+ return isSubstantialInteractiveHtml(input);
74
+ }
75
+
76
+ const INTERACTIVE_HTML_RE = /<script\b|on[a-z]+\s*=|addEventListener|new Chart\b|window\.vellum\b/i;
77
+
78
+ function isSubstantialInteractiveHtml(
79
+ input: Record<string, unknown>,
80
+ ): boolean {
81
+ const data = asRecord(input.data);
82
+ const html = data?.html;
83
+ if (typeof html !== "string") {
84
+ return false;
85
+ }
86
+ return html.length > 2000 && INTERACTIVE_HTML_RE.test(html);
66
87
  }
67
88
 
68
89
  function collectRoutingText(input: Record<string, unknown>): string[] {
@@ -105,13 +126,17 @@ export const uiShowTool = {
105
126
  'Surface structured data or UI in the conversation. For long-form writing use the document skill. For interactive apps, dashboards, games, calculators, or durable tools, call `skill_load` with `skill: "app-builder"` and use the app-builder workflow; do not use `dynamic_page` as a substitute for a persistent app. App-like `dynamic_page` calls are rejected.\n\n' +
106
127
  "Surface types (data shapes):\n" +
107
128
  '- card: { title, subtitle?, body, metadata?: [{ label, value }], template?, templateData? }. Templates: "weather_forecast" (native weather widget), "task_progress" (live step tracker - update via ui_update on data.templateData; shape: { title, status: "in_progress"|"completed"|"failed", steps: [{ label, status: "pending"|"in_progress"|"completed"|"failed", detail? }] })\n' +
129
+ "- copy_block: { text, label?, language? }. Shows copyable text with a visible copy button; use for prompts, commands, paths, or snippets the user should copy.\n" +
130
+ '- choice: { description?, options: [{ id, title, description?, recommended?, data? }], selectionMode?: "single"|"multiple", commitOnSelect?, submitLabel? }. Single-select choices commit on option click by default. Use for outcome offers and follow-up choices; mark the strongest option with recommended: true.\n' +
131
+ "- oauth_connect: { providerKey, displayName?, description?, logoUrl? }. Shows a managed OAuth connection CTA in chat; use when the current task needs a managed integration account (Google, Linear, GitHub, etc.) instead of asking the user to visit settings or attempting OAuth through shell/tools. The client supplies the CTA label. Do not include OAuth scopes in the surface; managed providers use the platform's configured scopes.\n" +
108
132
  '- table: { columns: [{ id, label }], rows: [{ id, cells: Record<id, string | { text, icon?, iconColor?: "success"|"warning"|"error"|"muted" }>, selectable?, selected? }], selectionMode?: "none"|"single"|"multiple", caption? }\n' +
109
133
  '- form: { description?, fields: [{ id, type: "text"|"textarea"|"select"|"toggle"|"number"|"password", label, placeholder?, required?, defaultValue?, options?: [{ label, value }] }], submitLabel? }. Multi-page: { pages: [{ id, title, description?, fields }], pageLabels?: { next?, back?, submit? }, submitLabel? }\n' +
110
134
  '- list: { items: [{ id, title, subtitle?, icon?, selected? }], selectionMode: "single"|"multiple"|"none" }\n' +
111
135
  "- confirmation: { message, detail?, confirmLabel?, confirmedLabel?, cancelLabel?, destructive? }\n" +
112
136
  "- dynamic_page: { html, width?, height?, preview?: { title, subtitle?, description?, icon?, metrics?: [{ label, value }] } }\n" +
113
137
  "- file_upload: { prompt, acceptedTypes?, maxFiles? }\n" +
114
- "- task_preferences: {} (no data needed — categories are rendered client-side)\n\n" +
138
+ "- task_preferences: {} (no data needed — categories are rendered client-side)\n" +
139
+ '- work_result: { eyebrow?, status?: "completed"|"partial"|"failed"|"in_progress", summary?, metrics?: [{ label, value, detail?, tone?: "neutral"|"positive"|"warning"|"negative" }], sections?: [{ id?, title, description?, type?: "items"|"timeline"|"diff"|"artifacts"|"warnings", items?: [{ id?, title, description?, status?, tone?, metadata?: [{ label, value }], href? }], diffs?: [{ label?, before?, after? }] }] }. Shows a structured receipt after real work: what changed, what was skipped, proof points, and next actions. Keep display-only unless explicit follow-up buttons are needed.\n\n' +
115
140
  "Proactively show a task_progress card before multi-step or long-running work (web searches, file operations, research). Show it before your first tool call, then update steps as work progresses.",
116
141
  category: "ui-surface",
117
142
  defaultRiskLevel: RiskLevel.Low,
@@ -124,6 +149,9 @@ export const uiShowTool = {
124
149
  type: "string",
125
150
  enum: [
126
151
  "card",
152
+ "choice",
153
+ "copy_block",
154
+ "oauth_connect",
127
155
  "form",
128
156
  "list",
129
157
  "table",
@@ -131,6 +159,7 @@ export const uiShowTool = {
131
159
  "dynamic_page",
132
160
  "file_upload",
133
161
  "task_preferences",
162
+ "work_result",
134
163
  ],
135
164
  description: "The type of surface to display",
136
165
  },
@@ -30,8 +30,10 @@ import {
30
30
  interface ClientCatalogProvider {
31
31
  id: string;
32
32
  displayName: string;
33
+ subtitle?: string;
34
+ supportsVoiceSelection?: boolean;
33
35
  credentialMode?: string;
34
- credentialsGuide?: { url: string };
36
+ credentialsGuide?: { description: string; url: string; linkLabel: string };
35
37
  }
36
38
 
37
39
  interface ClientCatalog {
@@ -144,6 +146,88 @@ describe("TTS provider catalog / client artifact consistency", () => {
144
146
  }
145
147
  });
146
148
 
149
+ // -- Display field parity --------------------------------------------------
150
+
151
+ test("subtitle matches between assistant catalog and client artifact", () => {
152
+ const violations: string[] = [];
153
+ for (const clientEntry of clientCatalog.providers) {
154
+ try {
155
+ const assistantEntry = getCatalogProvider(clientEntry.id as any);
156
+ if (clientEntry.subtitle !== assistantEntry.subtitle) {
157
+ violations.push(
158
+ `"${clientEntry.id}": client="${clientEntry.subtitle}" vs assistant="${assistantEntry.subtitle}"`,
159
+ );
160
+ }
161
+ } catch {
162
+ // Unknown ID — covered by provider ID parity tests above.
163
+ }
164
+ }
165
+ if (violations.length > 0) {
166
+ expect(violations, "Subtitle mismatch:\n" + violations.join("\n")).toEqual(
167
+ [],
168
+ );
169
+ }
170
+ });
171
+
172
+ test("supportsVoiceSelection matches between assistant catalog and client artifact", () => {
173
+ const violations: string[] = [];
174
+ for (const clientEntry of clientCatalog.providers) {
175
+ try {
176
+ const assistantEntry = getCatalogProvider(clientEntry.id as any);
177
+ if (
178
+ clientEntry.supportsVoiceSelection !==
179
+ assistantEntry.supportsVoiceSelection
180
+ ) {
181
+ violations.push(
182
+ `"${clientEntry.id}": client=${clientEntry.supportsVoiceSelection} vs assistant=${assistantEntry.supportsVoiceSelection}`,
183
+ );
184
+ }
185
+ } catch {
186
+ // Unknown ID — covered by provider ID parity tests above.
187
+ }
188
+ }
189
+ if (violations.length > 0) {
190
+ expect(
191
+ violations,
192
+ "supportsVoiceSelection mismatch:\n" + violations.join("\n"),
193
+ ).toEqual([]);
194
+ }
195
+ });
196
+
197
+ test("credentialsGuide matches between assistant catalog and client artifact", () => {
198
+ const violations: string[] = [];
199
+ for (const clientEntry of clientCatalog.providers) {
200
+ try {
201
+ const assistantEntry = getCatalogProvider(clientEntry.id as any);
202
+ const cg = clientEntry.credentialsGuide;
203
+ const ag = assistantEntry.credentialsGuide;
204
+ if (cg && ag) {
205
+ if (cg.url !== ag.url) {
206
+ violations.push(`"${clientEntry.id}": credentialsGuide.url mismatch`);
207
+ }
208
+ if (cg.description !== ag.description) {
209
+ violations.push(
210
+ `"${clientEntry.id}": credentialsGuide.description mismatch`,
211
+ );
212
+ }
213
+ if (cg.linkLabel !== ag.linkLabel) {
214
+ violations.push(
215
+ `"${clientEntry.id}": credentialsGuide.linkLabel mismatch`,
216
+ );
217
+ }
218
+ }
219
+ } catch {
220
+ // Unknown ID — covered by provider ID parity tests above.
221
+ }
222
+ }
223
+ if (violations.length > 0) {
224
+ expect(
225
+ violations,
226
+ "credentialsGuide mismatch:\n" + violations.join("\n"),
227
+ ).toEqual([]);
228
+ }
229
+ });
230
+
147
231
  // -- Structural sanity ----------------------------------------------------
148
232
 
149
233
  test("client artifact version is a positive integer", () => {