@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.8.7",
3
+ "version": "0.8.8-dev.202606052332.17fc8ea",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -33,7 +33,7 @@
33
33
  "prepack": "node ../scripts/prepack-bundled-deps.mjs"
34
34
  },
35
35
  "dependencies": {
36
- "@agentclientprotocol/sdk": "0.16.1",
36
+ "@agentclientprotocol/sdk": "0.25.0",
37
37
  "@anthropic-ai/sdk": "0.78.0",
38
38
  "@google/genai": "1.45.0",
39
39
  "@modelcontextprotocol/sdk": "1.27.1",
@@ -56,12 +56,24 @@ const RouteBodySchemaSchema = z.any().refine(
56
56
  { message: "Expected a Zod schema or a plain JSON Schema object" },
57
57
  );
58
58
 
59
- const RouteRequestBodyVariantSchema = z.object({
59
+ /** Explicit `{ contentType, schema }` body for non-JSON media types. */
60
+ const RouteBodyWithContentTypeSchema = z.object({
60
61
  contentType: z.string(),
61
62
  /** Zod schema OR plain JSON Schema fragment. */
62
63
  schema: z.any(),
63
64
  });
64
65
 
66
+ /**
67
+ * A route's request or success-response body: either a bare Zod/JSON schema
68
+ * (advertised as `application/json`) or an explicit `{ contentType, schema }`
69
+ * pair for non-JSON media (e.g. an `application/octet-stream` upload or binary
70
+ * download).
71
+ */
72
+ const RouteContentBodySchema = z.union([
73
+ RouteBodyWithContentTypeSchema,
74
+ RouteBodySchemaSchema,
75
+ ]);
76
+
65
77
  const RouteAdditionalResponseSchema = z.object({
66
78
  description: z.string(),
67
79
  schema: z.any().optional(),
@@ -79,12 +91,10 @@ const RouteEntrySchema = z.object({
79
91
  tags: z.array(z.string()).optional(),
80
92
  /** Query parameter definitions. */
81
93
  queryParams: z.array(RouteQueryParamSchema).optional(),
82
- /** JSON Schema for the request body. */
83
- requestBody: RouteBodySchemaSchema.optional(),
84
- /** Multi-content-type request body variants (overrides `requestBody` when present). */
85
- requestBodies: z.array(RouteRequestBodyVariantSchema).optional(),
86
- /** JSON Schema for the success response body. */
87
- responseBody: RouteBodySchemaSchema.optional(),
94
+ /** Request body: a bare Zod/JSON schema (JSON) or `{ contentType, schema }`. */
95
+ requestBody: RouteContentBodySchema.optional(),
96
+ /** Success response body: a bare Zod/JSON schema (JSON) or `{ contentType, schema }`. */
97
+ responseBody: RouteContentBodySchema.optional(),
88
98
  /** HTTP status code for the success response. Defaults to "200".
89
99
  * Callable responseStatus values (used at runtime) are ignored here. */
90
100
  responseStatus: z.preprocess(
@@ -292,6 +302,11 @@ interface OpenApiParameter {
292
302
  description?: string;
293
303
  }
294
304
 
305
+ interface OpenApiResponse {
306
+ description: string;
307
+ content?: Record<string, { schema: JSONSchemaObject }>;
308
+ }
309
+
295
310
  interface OpenApiOperation {
296
311
  operationId: string;
297
312
  summary?: string;
@@ -302,13 +317,27 @@ interface OpenApiOperation {
302
317
  required: boolean;
303
318
  content: Record<string, { schema: JSONSchemaObject }>;
304
319
  };
305
- responses: Record<
306
- string,
307
- {
308
- description: string;
309
- content?: Record<string, { schema: JSONSchemaObject }>;
310
- }
311
- >;
320
+ responses: Record<string, OpenApiResponse>;
321
+ }
322
+
323
+ /**
324
+ * Resolve a body declaration (request or success response) into its media type
325
+ * and the schema source to convert. A bare Zod/JSON schema is advertised as
326
+ * `application/json`; the explicit `{ contentType, schema }` form carries its
327
+ * own media type (e.g. `application/octet-stream` for binary bodies).
328
+ */
329
+ function resolveBodyContent(body: unknown): {
330
+ contentType: string;
331
+ schemaSource: unknown;
332
+ } {
333
+ const hasContentType =
334
+ typeof body === "object" && body !== null && "contentType" in body;
335
+ return {
336
+ contentType: hasContentType
337
+ ? (body as { contentType: string }).contentType
338
+ : "application/json",
339
+ schemaSource: hasContentType ? (body as { schema: unknown }).schema : body,
340
+ };
312
341
  }
313
342
 
314
343
  interface OpenApiPathItem {
@@ -420,22 +449,27 @@ function buildSpec(
420
449
  // that enqueue a job and return immediately set responseStatus: "202"
421
450
  // so the generated spec matches the handler's actual response code.
422
451
  const successStatus = entry.responseStatus ?? "200";
452
+ let successResponse: OpenApiResponse = {
453
+ description: "Successful response",
454
+ };
455
+ if (entry.responseBody) {
456
+ const { contentType, schemaSource } = resolveBodyContent(
457
+ entry.responseBody,
458
+ );
459
+ successResponse = {
460
+ description: "Successful response",
461
+ content: {
462
+ [contentType]: { schema: toJSONSchemaObject(schemaSource) },
463
+ },
464
+ };
465
+ }
423
466
  const operation: OpenApiOperation = {
424
467
  operationId,
425
468
  ...(entry.summary ? { summary: entry.summary } : {}),
426
469
  ...(entry.description ? { description: entry.description } : {}),
427
470
  ...(tags ? { tags } : {}),
428
471
  responses: {
429
- [successStatus]: entry.responseBody
430
- ? {
431
- description: "Successful response",
432
- content: {
433
- "application/json": {
434
- schema: toJSONSchemaObject(entry.responseBody),
435
- },
436
- },
437
- }
438
- : { description: "Successful response" },
472
+ [successStatus]: successResponse,
439
473
  },
440
474
  };
441
475
 
@@ -443,26 +477,19 @@ function buildSpec(
443
477
  operation.parameters = parameters;
444
478
  }
445
479
 
446
- // Multi-content-type request bodies take precedence over the single
447
- // application/json requestBody. This lets an endpoint advertise a
448
- // `oneOf`-style choice between `application/octet-stream`,
449
- // `multipart/form-data`, and `application/json` on the same URL.
450
- if (entry.requestBodies && entry.requestBodies.length > 0) {
451
- const content: Record<string, { schema: JSONSchemaObject }> = {};
452
- for (const variant of entry.requestBodies) {
453
- content[variant.contentType] = {
454
- schema: toJSONSchemaObject(variant.schema, {
455
- stripRequiredDefaults: true,
456
- }),
457
- };
458
- }
459
- operation.requestBody = { required: true, content };
460
- } else if (entry.requestBody) {
480
+ // A bare Zod/JSON schema is advertised as `application/json`; the
481
+ // explicit `{ contentType, schema }` form lets a route declare a non-JSON
482
+ // body (e.g. a raw `application/octet-stream` upload) so the generated SDK
483
+ // describes a real body type instead of `never`.
484
+ if (entry.requestBody) {
485
+ const { contentType, schemaSource } = resolveBodyContent(
486
+ entry.requestBody,
487
+ );
461
488
  operation.requestBody = {
462
489
  required: true,
463
490
  content: {
464
- "application/json": {
465
- schema: toJSONSchemaObject(entry.requestBody, {
491
+ [contentType]: {
492
+ schema: toJSONSchemaObject(schemaSource, {
466
493
  stripRequiredDefaults: true,
467
494
  }),
468
495
  },
@@ -1,20 +1,19 @@
1
1
  /**
2
- * Tests for the `agent_loop_exit` instrumentation added in this PR.
2
+ * Tests for the `agent_loop_exit` instrumentation.
3
3
  *
4
4
  * Coverage targets:
5
- * 1. **One emit per run** — the idempotency guard fires once, even if the
6
- * code path would otherwise reach two emit sites (the empty-response
7
- * throw catch-block fallback case).
5
+ * 1. **One emit per run** — the idempotency guard fires once, even when
6
+ * multiple exit conditions stack and the code path would otherwise
7
+ * reach a second emit site.
8
8
  * 2. **Reason matches break site** — for each reachable break site, the
9
9
  * emitted reason is the one documented in `AgentLoopExitReason`.
10
10
  * 3. **Always the last AgentEvent of terminal runs** — consumers can rely on
11
11
  * positional ordering to find it when a run reaches a terminal state.
12
12
  *
13
- * Sites not exercised here (`empty_response_exhausted`, `aborted_via_error`)
14
- * require deeper provider fakery and are best covered by integration tests
15
- * once we wire up the empty-response pipeline mock.
13
+ * Sites not exercised here (`aborted_via_error`) require deeper provider
14
+ * fakery and are best covered by integration tests.
16
15
  */
17
- import { describe, expect, spyOn, test } from "bun:test";
16
+ import { describe, expect, test } from "bun:test";
18
17
 
19
18
  import type {
20
19
  AgentEvent,
@@ -24,7 +23,6 @@ import type {
24
23
  } from "../agent/loop.js";
25
24
  import { AgentLoop, isMaxTokensStopReason } from "../agent/loop.js";
26
25
  import type { TurnContext } from "../plugins/types.js";
27
- import { PluginTimeoutError } from "../plugins/types.js";
28
26
  import type {
29
27
  Message,
30
28
  Provider,
@@ -103,8 +101,8 @@ const userMessage: Message = {
103
101
  };
104
102
 
105
103
  // A turn context whose `contextWindowManager.maybeCompact` returns a canned
106
- // result, so the loop's native compaction pipeline runs without the real
107
- // orchestrator machinery.
104
+ // result, so the loop's compaction call runs without the real orchestrator
105
+ // machinery.
108
106
  function fakeCompactionTurnContext(result: {
109
107
  compacted: boolean;
110
108
  exhausted: boolean;
@@ -120,22 +118,6 @@ function fakeCompactionTurnContext(result: {
120
118
  } as unknown as TurnContext;
121
119
  }
122
120
 
123
- // A turn context whose compaction call times out, exercising the loop's
124
- // PluginTimeoutError handling.
125
- function timeoutCompactionTurnContext(): TurnContext {
126
- return {
127
- requestId: "req-compact",
128
- conversationId: "conv-compact",
129
- turnIndex: 0,
130
- trust: { sourceChannel: "vellum", trustClass: "unknown" },
131
- contextWindowManager: {
132
- maybeCompact: async () => {
133
- throw new PluginTimeoutError("compaction", "default-compaction", 1);
134
- },
135
- },
136
- } as unknown as TurnContext;
137
- }
138
-
139
121
  function lastExitEvent(
140
122
  events: AgentEvent[],
141
123
  ): Extract<AgentEvent, { type: "agent_loop_exit" }> | undefined {
@@ -379,82 +361,44 @@ describe("AgentLoop exit-reason instrumentation", () => {
379
361
  toolExecutor: toolExecutor,
380
362
  });
381
363
 
382
- let prepared = false;
383
- let appliedResult = false;
384
364
  let reinjected = false;
365
+ const events: AgentEvent[] = [];
385
366
  const compaction: MidLoopCompaction = {
386
- prepare: (history) => {
387
- prepared = true;
388
- return { rawHistory: history, options: undefined };
389
- },
390
- applyResult: async () => {
391
- appliedResult = true;
392
- },
393
- reinject: async () => {
367
+ postCompactionHook: async () => {
394
368
  reinjected = true;
395
369
  return [userMessage];
396
370
  },
397
371
  };
398
372
 
399
373
  // WHEN the in-loop budget gate trips at the checkpoint
400
- const result = await loop.run([userMessage], () => {}, {
401
- resolveContextWindow: () => ({
402
- maxInputTokens: 10,
403
- overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
404
- }),
405
- compaction,
406
- turnContext: fakeCompactionTurnContext({
407
- compacted: true,
408
- exhausted: false,
409
- }),
410
- });
374
+ const result = await loop.run(
375
+ [userMessage],
376
+ (event) => {
377
+ events.push(event);
378
+ },
379
+ {
380
+ resolveContextWindow: () => ({
381
+ maxInputTokens: 10,
382
+ overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
383
+ }),
384
+ compaction,
385
+ turnContext: fakeCompactionTurnContext({
386
+ compacted: true,
387
+ exhausted: false,
388
+ }),
389
+ },
390
+ );
411
391
 
412
392
  // THEN the loop runs the compaction ceremony in place and continues to a
413
- // clean exit instead of yielding for budget.
414
- expect(prepared).toBe(true);
415
- expect(appliedResult).toBe(true);
393
+ // clean exit instead of yielding for budget. The durable commit is
394
+ // signalled via a `compaction_completed` event rather than an injected hook.
395
+ expect(events.some((event) => event.type === "compaction_completed")).toBe(
396
+ true,
397
+ );
416
398
  expect(reinjected).toBe(true);
417
399
  expect(result.exitReason).not.toBe("budget");
418
400
  });
419
401
 
420
- test("yields 'budget' when inline compaction times out", async () => {
421
- const { provider } = createMockProvider([
422
- toolUseResponse("t1", "read_file", { path: "/a.txt" }),
423
- textResponse("never reached"),
424
- ]);
425
- const toolExecutor = async () => ({ content: "ok", isError: false });
426
- const loop = new AgentLoop(provider, "system", {
427
- tools: dummyTools,
428
- toolExecutor: toolExecutor,
429
- });
430
-
431
- const compaction: MidLoopCompaction = {
432
- prepare: (history) => ({ rawHistory: history, options: undefined }),
433
- applyResult: async () => {},
434
- reinject: async () => {
435
- throw new Error("reinject must not run after a timeout");
436
- },
437
- };
438
- const recordOutcomeSpy = spyOn(loop.compactionCircuit, "recordOutcome");
439
-
440
- // WHEN the compaction pipeline throws a PluginTimeoutError
441
- const result = await loop.run([userMessage], () => {}, {
442
- resolveContextWindow: () => ({
443
- maxInputTokens: 10,
444
- overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
445
- }),
446
- compaction,
447
- turnContext: timeoutCompactionTurnContext(),
448
- });
449
-
450
- // THEN the loop records the timeout as a compaction failure against its
451
- // own circuit breaker, and yields for budget so the orchestrator can
452
- // escalate.
453
- expect(recordOutcomeSpy).toHaveBeenCalledTimes(1);
454
- expect(recordOutcomeSpy.mock.calls[0]?.[1]).toBe(true);
455
- expect(result.exitReason).toBe("budget");
456
- });
457
-
458
402
  test("yields 'budget' when inline compaction reports exhausted", async () => {
459
403
  const { provider } = createMockProvider([
460
404
  toolUseResponse("t1", "read_file", { path: "/a.txt" }),
@@ -467,10 +411,8 @@ describe("AgentLoop exit-reason instrumentation", () => {
467
411
  });
468
412
 
469
413
  const compaction: MidLoopCompaction = {
470
- prepare: (history) => ({ rawHistory: history, options: undefined }),
471
- applyResult: async () => {},
472
- reinject: async () => {
473
- throw new Error("reinject must not run when exhausted");
414
+ postCompactionHook: async () => {
415
+ throw new Error("postCompactionHook must not run when exhausted");
474
416
  },
475
417
  };
476
418
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Integration tests for the agent loop's `provider_error` recording path.
3
3
  *
4
- * When the `llmCall` pipeline throws (provider rejected the request before
4
+ * When the provider call throws (provider rejected the request before
5
5
  * returning a usable response), the loop must emit a `provider_error` event
6
6
  * carrying the loop-level raw request and the thrown error so downstream
7
7
  * consumers can persist an `llm_request_logs` row. Without this, rejected
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
3
  import type {
4
4
  AgentEvent,
@@ -6,90 +6,24 @@ import type {
6
6
  CheckpointInfo,
7
7
  } from "../agent/loop.js";
8
8
  import { AgentLoop } from "../agent/loop.js";
9
+ import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
9
10
  import type {
10
11
  ContentBlock,
11
12
  Message,
12
13
  Provider,
13
14
  ProviderResponse,
14
- SendMessageOptions,
15
15
  ToolDefinition,
16
16
  } from "../providers/types.js";
17
+ import {
18
+ createMockProvider,
19
+ textResponse,
20
+ toolUseResponse,
21
+ } from "./helpers/mock-provider.js";
17
22
 
18
23
  // ---------------------------------------------------------------------------
19
24
  // Helpers
20
25
  // ---------------------------------------------------------------------------
21
26
 
22
- /** A mock provider that returns pre-configured responses in sequence. */
23
- function createMockProvider(responses: ProviderResponse[]): {
24
- provider: Provider;
25
- calls: {
26
- messages: Message[];
27
- tools?: ToolDefinition[];
28
- systemPrompt?: string;
29
- options?: SendMessageOptions;
30
- }[];
31
- } {
32
- const calls: {
33
- messages: Message[];
34
- tools?: ToolDefinition[];
35
- systemPrompt?: string;
36
- options?: SendMessageOptions;
37
- }[] = [];
38
- let callIndex = 0;
39
-
40
- const provider: Provider = {
41
- name: "mock",
42
- async sendMessage(
43
- messages: Message[],
44
- options?: SendMessageOptions,
45
- ): Promise<ProviderResponse> {
46
- calls.push({
47
- messages: [...messages],
48
- tools: options?.tools,
49
- systemPrompt: options?.systemPrompt,
50
- options,
51
- });
52
- const response = responses[callIndex] ?? responses[responses.length - 1];
53
- callIndex++;
54
-
55
- // Emit streaming events if the response has text blocks
56
- if (options?.onEvent) {
57
- for (const block of response.content) {
58
- if (block.type === "text") {
59
- options.onEvent({ type: "text_delta", text: block.text });
60
- }
61
- }
62
- }
63
-
64
- return response;
65
- },
66
- };
67
-
68
- return { provider, calls };
69
- }
70
-
71
- function textResponse(text: string): ProviderResponse {
72
- return {
73
- content: [{ type: "text", text }],
74
- model: "mock-model",
75
- usage: { inputTokens: 10, outputTokens: 5 },
76
- stopReason: "end_turn",
77
- };
78
- }
79
-
80
- function toolUseResponse(
81
- id: string,
82
- name: string,
83
- input: Record<string, unknown>,
84
- ): ProviderResponse {
85
- return {
86
- content: [{ type: "tool_use", id, name, input }],
87
- model: "mock-model",
88
- usage: { inputTokens: 10, outputTokens: 5 },
89
- stopReason: "tool_use",
90
- };
91
- }
92
-
93
27
  const dummyTools: ToolDefinition[] = [
94
28
  {
95
29
  name: "read_file",
@@ -112,6 +46,13 @@ function collectEvents(events: AgentEvent[]): (event: AgentEvent) => void {
112
46
  // ---------------------------------------------------------------------------
113
47
 
114
48
  describe("AgentLoop", () => {
49
+ // The agent loop fires the `post-tool-use` hook for tool-result
50
+ // truncation, which only runs when the default plugin is registered.
51
+ // Register the defaults so the loop behaves as it does in production.
52
+ beforeEach(() => {
53
+ resetPluginRegistryAndRegisterDefaults();
54
+ });
55
+
115
56
  // 1. Basic text response
116
57
  test("returns history with assistant message for simple text response", async () => {
117
58
  const { provider } = createMockProvider([textResponse("Hi there!")]);
@@ -1569,9 +1510,11 @@ describe("AgentLoop", () => {
1569
1510
  expect(textBlock!.text).toBe("Normal response with no placeholders.");
1570
1511
  });
1571
1512
 
1572
- // Tool error retry nudge — when a tool returns isError: true, the loop
1573
- // should inject a system_notice nudging the LLM to retry instead of ending.
1574
- test("injects retry nudge system_notice when tool returns an error", async () => {
1513
+ // Tool error retry coaching — when a tool returns isError: true, the coaching
1514
+ // notice is appended as a separate text block in the tool-result user message
1515
+ // (not into the tool_result content), nudging the LLM to retry instead of
1516
+ // ending the turn while keeping the tool's actual output clean.
1517
+ test("appends retry coaching as a separate block when a tool returns an error", async () => {
1575
1518
  const { provider, calls } = createMockProvider([
1576
1519
  // First turn: LLM calls a tool that errors
1577
1520
  toolUseResponse("t1", "read_file", { path: "/missing.txt" }),
@@ -1606,7 +1549,8 @@ describe("AgentLoop", () => {
1606
1549
  // Provider should have been called 3 times (error -> retry -> final text)
1607
1550
  expect(calls).toHaveLength(3);
1608
1551
 
1609
- // The second call's messages should contain the retry nudge system_notice
1552
+ // The second call's messages should carry the retry coaching as a separate
1553
+ // text block, with the tool_result's own content left untouched.
1610
1554
  const secondCallMessages = calls[1].messages;
1611
1555
  const toolResultMessage = secondCallMessages[secondCallMessages.length - 1];
1612
1556
  expect(toolResultMessage.role).toBe("user");
@@ -1617,7 +1561,14 @@ describe("AgentLoop", () => {
1617
1561
  );
1618
1562
  expect(retryNudge).toBeDefined();
1619
1563
 
1620
- // The third call should NOT have the retry nudge (successful tool result)
1564
+ // The errored tool_result itself stays the tool's actual output.
1565
+ const erroredToolResult = toolResultMessage.content.find(
1566
+ (b): b is Extract<ContentBlock, { type: "tool_result" }> =>
1567
+ b.type === "tool_result",
1568
+ );
1569
+ expect(erroredToolResult?.content).not.toContain("looks recoverable");
1570
+
1571
+ // The third call should NOT have the retry coaching (successful tool result)
1621
1572
  const thirdCallMessages = calls[2].messages;
1622
1573
  const thirdToolResultMessage =
1623
1574
  thirdCallMessages[thirdCallMessages.length - 1];
@@ -1628,8 +1579,9 @@ describe("AgentLoop", () => {
1628
1579
  expect(noRetryNudge).toBeUndefined();
1629
1580
  });
1630
1581
 
1631
- // Retry nudge stops after MAX_CONSECUTIVE_ERROR_NUDGES (3) consecutive errors
1632
- test("stops injecting retry nudge after 3 consecutive error turns", async () => {
1582
+ // Retry coaching stops after a tool fails 3 times in a row — past that the
1583
+ // error is likely unrecoverable and further coaching only burns tokens.
1584
+ test("stops appending retry coaching after 3 consecutive failures of a tool", async () => {
1633
1585
  const { provider, calls } = createMockProvider([
1634
1586
  // 4 consecutive error turns, then final text
1635
1587
  toolUseResponse("t1", "read_file", { path: "/a" }),
@@ -1654,23 +1606,21 @@ describe("AgentLoop", () => {
1654
1606
 
1655
1607
  expect(calls).toHaveLength(5);
1656
1608
 
1657
- // Helper to check if a call's last user message has the retry nudge
1609
+ // Helper to check if a call's last user message has the retry coaching
1610
+ // (a separate text block, not part of the tool_result content).
1658
1611
  const hasRetryNudge = (callIndex: number): boolean => {
1659
1612
  const msgs = calls[callIndex].messages;
1660
1613
  const lastMsg = msgs[msgs.length - 1];
1661
1614
  return lastMsg.content.some(
1662
- (b) =>
1663
- b.type === "text" &&
1664
- "text" in b &&
1665
- (b as { text: string }).text.includes("looks recoverable"),
1615
+ (b) => b.type === "text" && b.text.includes("looks recoverable"),
1666
1616
  );
1667
1617
  };
1668
1618
 
1669
- // Turns 1-3 should have the nudge
1619
+ // Turns 1-3 should have the coaching
1670
1620
  expect(hasRetryNudge(1)).toBe(true);
1671
1621
  expect(hasRetryNudge(2)).toBe(true);
1672
1622
  expect(hasRetryNudge(3)).toBe(true);
1673
- // Turn 4 should NOT have the nudge (exceeded limit)
1623
+ // Turn 4 should NOT have the coaching (exceeded limit)
1674
1624
  expect(hasRetryNudge(4)).toBe(false);
1675
1625
  });
1676
1626
 
@@ -77,6 +77,8 @@ function makeTarget(): WakeTarget {
77
77
  run: (async (messages: Message[]) => ({
78
78
  history: messages,
79
79
  exitReason: null,
80
+ appendedNewMessages: false,
81
+ newMessages: [],
80
82
  })) as WakeTarget["agentLoop"]["run"],
81
83
  },
82
84
  getMessages: () => history,