@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
@@ -1,241 +0,0 @@
1
- /**
2
- * Tests for the `toolError` pipeline (PR 19).
3
- *
4
- * Covers:
5
- * - Default plugin nudges on the first error turn and keeps nudging up to the
6
- * `maxConsecutiveErrorNudges` cap.
7
- * - Default plugin suppresses the nudge once the cap is exceeded (the error is
8
- * likely unrecoverable — burning tokens on more nudges is wasteful).
9
- * - Default plugin uses the canonical {@link DEFAULT_TOOL_ERROR_NUDGE_TEXT}.
10
- * - Default plugin skips when `hasToolError` is false, regardless of the
11
- * consecutive counter (no error this turn → nothing to nudge).
12
- * - Swapping in a user plugin that provides its own `toolError` middleware
13
- * changes the nudge text end-to-end through `runPipeline`.
14
- */
15
-
16
- import { beforeEach, describe, expect, test } from "bun:test";
17
-
18
- import type { TrustContext } from "../daemon/trust-context.js";
19
- import { defaultToolErrorPlugin } from "../plugins/defaults/tool-error/register.js";
20
- import {
21
- DEFAULT_TOOL_ERROR_NUDGE_TEXT,
22
- defaultToolErrorTerminal,
23
- } from "../plugins/defaults/tool-error/terminal.js";
24
- import { runPipeline } from "../plugins/pipeline.js";
25
- import {
26
- getMiddlewaresFor,
27
- registerPlugin,
28
- resetPluginRegistryForTests,
29
- } from "../plugins/registry.js";
30
- import {
31
- type Middleware,
32
- type Plugin,
33
- type ToolErrorArgs,
34
- type ToolErrorDecision,
35
- type TurnContext,
36
- } from "../plugins/types.js";
37
-
38
- const trust: TrustContext = {
39
- sourceChannel: "vellum",
40
- trustClass: "guardian",
41
- };
42
-
43
- function makeCtx(): TurnContext {
44
- return {
45
- requestId: "req-tool-error-test",
46
- conversationId: "conv-tool-error-test",
47
- turnIndex: 0,
48
- trust,
49
- };
50
- }
51
-
52
- async function runToolErrorPipeline(
53
- args: ToolErrorArgs,
54
- ): Promise<ToolErrorDecision> {
55
- // Mirror the production call site in `agent/loop.ts`: the pipeline terminal
56
- // is `defaultToolErrorTerminal`, not a no-op. The default plugin's
57
- // middleware is a passthrough that calls `next(args)`, so the decision
58
- // logic lives in the terminal.
59
- return runPipeline<ToolErrorArgs, ToolErrorDecision>(
60
- "toolError",
61
- getMiddlewaresFor("toolError"),
62
- async (pipelineArgs) => defaultToolErrorTerminal(pipelineArgs),
63
- args,
64
- makeCtx(),
65
- 500,
66
- );
67
- }
68
-
69
- describe("toolError pipeline", () => {
70
- describe("default plugin", () => {
71
- beforeEach(() => {
72
- resetPluginRegistryForTests();
73
- registerPlugin(defaultToolErrorPlugin);
74
- });
75
-
76
- test("nudges on first error turn with canonical text", async () => {
77
- const decision = await runToolErrorPipeline({
78
- hasToolError: true,
79
- consecutiveErrorTurns: 1,
80
- maxConsecutiveErrorNudges: 3,
81
- });
82
- expect(decision.action).toBe("nudge");
83
- if (decision.action === "nudge") {
84
- expect(decision.nudgeText).toBe(DEFAULT_TOOL_ERROR_NUDGE_TEXT);
85
- }
86
- });
87
-
88
- test("keeps nudging up to and including the cap", async () => {
89
- // Cap of 3: turns 1, 2, and 3 all nudge. Turn 4 is past the cap.
90
- for (let turn = 1; turn <= 3; turn++) {
91
- const decision = await runToolErrorPipeline({
92
- hasToolError: true,
93
- consecutiveErrorTurns: turn,
94
- maxConsecutiveErrorNudges: 3,
95
- });
96
- expect(decision.action).toBe("nudge");
97
- }
98
- });
99
-
100
- test("suppresses the nudge once the consecutive counter exceeds the cap", async () => {
101
- const decision = await runToolErrorPipeline({
102
- hasToolError: true,
103
- consecutiveErrorTurns: 4,
104
- maxConsecutiveErrorNudges: 3,
105
- });
106
- expect(decision.action).toBe("skip");
107
- });
108
-
109
- test("skips when there is no tool error this turn, regardless of counter", async () => {
110
- // Counter is non-zero (the previous turn errored) but this turn succeeded,
111
- // so nothing to nudge about.
112
- const decision = await runToolErrorPipeline({
113
- hasToolError: false,
114
- consecutiveErrorTurns: 2,
115
- maxConsecutiveErrorNudges: 3,
116
- });
117
- expect(decision.action).toBe("skip");
118
- });
119
-
120
- test("honors a caller-supplied cap of zero (never nudges)", async () => {
121
- // Some call-sites may want to disable nudging entirely by passing cap = 0.
122
- // The decision logic uses `<=`, so counter 0 with cap 0 does nudge; counter
123
- // 1 with cap 0 suppresses. The cap is inclusive.
124
- const turn1 = await runToolErrorPipeline({
125
- hasToolError: true,
126
- consecutiveErrorTurns: 1,
127
- maxConsecutiveErrorNudges: 0,
128
- });
129
- expect(turn1.action).toBe("skip");
130
- });
131
- });
132
-
133
- describe("user-supplied plugin", () => {
134
- beforeEach(() => {
135
- resetPluginRegistryForTests();
136
- });
137
-
138
- test("swapping in a plugin changes the nudge text", async () => {
139
- const customText = "<system_notice>Custom error hint.</system_notice>";
140
- const customMiddleware: Middleware<
141
- ToolErrorArgs,
142
- ToolErrorDecision
143
- > = async (args) => {
144
- if (args.hasToolError) {
145
- return { action: "nudge", nudgeText: customText };
146
- }
147
- return { action: "skip" };
148
- };
149
- const customPlugin: Plugin = {
150
- manifest: {
151
- name: "custom-tool-error",
152
- version: "0.0.1",
153
- },
154
- middleware: { toolError: customMiddleware },
155
- };
156
- registerPlugin(customPlugin);
157
-
158
- const decision = await runToolErrorPipeline({
159
- hasToolError: true,
160
- consecutiveErrorTurns: 1,
161
- maxConsecutiveErrorNudges: 3,
162
- });
163
- expect(decision.action).toBe("nudge");
164
- if (decision.action === "nudge") {
165
- expect(decision.nudgeText).toBe(customText);
166
- }
167
- });
168
-
169
- test("swapping in a plugin can suppress nudges even when the default would nudge", async () => {
170
- const suppressingMiddleware: Middleware<
171
- ToolErrorArgs,
172
- ToolErrorDecision
173
- > = async () => ({ action: "skip" });
174
- const plugin: Plugin = {
175
- manifest: {
176
- name: "no-nudge",
177
- version: "0.0.1",
178
- },
179
- middleware: { toolError: suppressingMiddleware },
180
- };
181
- registerPlugin(plugin);
182
-
183
- const decision = await runToolErrorPipeline({
184
- hasToolError: true,
185
- consecutiveErrorTurns: 1,
186
- maxConsecutiveErrorNudges: 3,
187
- });
188
- expect(decision.action).toBe("skip");
189
- });
190
-
191
- test("terminal produces the nudge when no plugin is registered", async () => {
192
- // No registerPlugin call — the registry is empty for this slot. The
193
- // pipeline terminal is `defaultToolErrorTerminal`, so direct AgentLoop
194
- // callers that skip `bootstrapPlugins()` still get the nudge even
195
- // without any registered middleware.
196
- const decision = await runToolErrorPipeline({
197
- hasToolError: true,
198
- consecutiveErrorTurns: 1,
199
- maxConsecutiveErrorNudges: 3,
200
- });
201
- expect(decision.action).toBe("nudge");
202
- if (decision.action === "nudge") {
203
- expect(decision.nudgeText).toBe(DEFAULT_TOOL_ERROR_NUDGE_TEXT);
204
- }
205
- });
206
-
207
- test("user plugin registered AFTER the default still runs (no shadowing)", async () => {
208
- // Production registration order: defaults load first via the side-effect
209
- // imports in `defaults/index.ts`, then user plugins register on top via
210
- // `bootstrapPlugins()`. The user's middleware ends up at a deeper onion
211
- // layer than the default. If the default's middleware were to bypass
212
- // `next` and call the decision logic directly, the user middleware
213
- // would never run — this test guards against that regression.
214
- registerPlugin(defaultToolErrorPlugin);
215
-
216
- let userMiddlewareRan = false;
217
- const userMiddleware: Middleware<
218
- ToolErrorArgs,
219
- ToolErrorDecision
220
- > = async (args, next) => {
221
- userMiddlewareRan = true;
222
- return next(args);
223
- };
224
- registerPlugin({
225
- manifest: {
226
- name: "late-user-plugin",
227
- version: "0.0.1",
228
- },
229
- middleware: { toolError: userMiddleware },
230
- });
231
-
232
- await runToolErrorPipeline({
233
- hasToolError: true,
234
- consecutiveErrorTurns: 1,
235
- maxConsecutiveErrorNudges: 3,
236
- });
237
-
238
- expect(userMiddlewareRan).toBe(true);
239
- });
240
- });
241
- });
@@ -1,417 +0,0 @@
1
- /**
2
- * Pipeline wrapper tests for `ToolExecutor.execute` (PR 16).
3
- *
4
- * Covers:
5
- * - The public `execute` method routes through `runPipeline("toolExecute",
6
- * ...)` so `getMiddlewaresFor("toolExecute")` middleware participates.
7
- * - The default `toolExecute` plugin (passthrough) preserves the original
8
- * execution path — result and side effects match the unwrapped executor.
9
- * - A spy middleware observes the full tool invocation (name, input, ctx).
10
- * - A short-circuit middleware intercepts the call and supplies a custom
11
- * result without hitting the real tool.
12
- *
13
- * These tests reuse the same module mocks as `tool-executor.test.ts` so the
14
- * permission check, risk classifier, and tool registry are stubbed; the
15
- * focus here is the pipeline wrapper, not the internal execution body.
16
- */
17
-
18
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
19
-
20
- import type { ToolExecutionResult } from "../tools/types.js";
21
-
22
- // ── Config mock ───────────────────────────────────────────────────────
23
- const mockConfig = {
24
- provider: "anthropic",
25
- model: "test",
26
- maxTokens: 4096,
27
- dataDir: "/tmp",
28
- timeouts: {
29
- shellDefaultTimeoutSec: 120,
30
- shellMaxTimeoutSec: 600,
31
- permissionTimeoutSec: 300,
32
- toolExecutionTimeoutSec: 120,
33
- },
34
- sandbox: {
35
- enabled: false,
36
- backend: "native" as const,
37
- docker: {
38
- image: "vellum-sandbox:latest",
39
- cpus: 1,
40
- memoryMb: 512,
41
- pidsLimit: 256,
42
- network: "none" as const,
43
- },
44
- },
45
- rateLimit: { maxRequestsPerMinute: 0 },
46
- secretDetection: {
47
- enabled: false,
48
- },
49
- permissions: {
50
- mode: "workspace" as const,
51
- },
52
- };
53
-
54
- mock.module("../config/loader.js", () => ({
55
- getConfig: () => mockConfig,
56
- loadConfig: () => mockConfig,
57
- invalidateConfigCache: () => {},
58
- loadRawConfig: () => ({}),
59
- saveRawConfig: () => {},
60
- getNestedValue: () => undefined,
61
- setNestedValue: () => {},
62
- }));
63
-
64
- // ── Logger mock ──────────────────────────────────────────────────────
65
- // Bun's `mock.module` persists across test files until explicitly
66
- // restored, so this mock can leak into `plugin-bootstrap.test.ts`. That
67
- // file inspects `ctx.logger` (populated via `log.child({ plugin })`), so
68
- // we return a Proxy whose `.child(...)` yields another Proxy with the
69
- // same shape — the bootstrap test's `expect(ctx.logger).toBeDefined()`
70
- // then passes regardless of test-file ordering.
71
- function makeFakeLoggerProxy(): object {
72
- return new Proxy({} as Record<string, unknown>, {
73
- get: (_target, prop) => {
74
- if (prop === "child") return () => makeFakeLoggerProxy();
75
- return () => {};
76
- },
77
- });
78
- }
79
- mock.module("../util/logger.js", () => ({
80
- getLogger: (_name?: string) => makeFakeLoggerProxy(),
81
- truncateForLog: (value: string) => value,
82
- }));
83
-
84
- // ── Permission checker — always allow so execution reaches the tool ──
85
- mock.module("../permissions/checker.js", () => ({
86
- classifyRisk: async () => ({ level: "low" }),
87
- check: async () => ({ decision: "allow", reason: "allowed" }),
88
- generateAllowlistOptions: () => [
89
- { label: "exact", description: "exact", pattern: "exact" },
90
- ],
91
- generateScopeOptions: () => [{ label: "/tmp", scope: "/tmp" }],
92
- }));
93
-
94
- // ── Tool usage store stub ────────────────────────────────────────────
95
- mock.module("../memory/tool-usage-store.js", () => ({
96
- recordToolInvocation: () => {},
97
- getRecentInvocations: () => [],
98
- rotateToolInvocations: async () => 0,
99
- }));
100
-
101
- // ── Tool registry: return a stub tool whose execute records the call ─
102
- let lastToolCall: { name: string; input: Record<string, unknown> } | undefined;
103
- let fakeToolResult: ToolExecutionResult = {
104
- content: "real tool output",
105
- isError: false,
106
- };
107
-
108
- mock.module("../tools/registry.js", () => ({
109
- getTool: (name: string) => {
110
- if (name === "unknown_tool") return undefined;
111
- return {
112
- name,
113
- description: "test tool",
114
- category: "test",
115
- defaultRiskLevel: "low",
116
- input_schema: {},
117
- execute: async (input: Record<string, unknown>) => {
118
- lastToolCall = { name, input };
119
- return fakeToolResult;
120
- },
121
- };
122
- },
123
- getAllTools: () => [],
124
- }));
125
-
126
- mock.module("../tools/shared/filesystem/path-policy.js", () => ({
127
- sandboxPolicy: () => ({ ok: false }),
128
- hostPolicy: () => ({ ok: false }),
129
- }));
130
-
131
- // ── Redaction + token manager so the executor's imports resolve ──────
132
- mock.module("../security/redaction.js", () => ({
133
- redactSensitiveFields: (input: Record<string, unknown>) => input,
134
- }));
135
-
136
- mock.module("../security/token-manager.js", () => ({
137
- TokenExpiredError: class TokenExpiredError extends Error {},
138
- }));
139
-
140
- // ── Imports — after mock.module so the executor under test picks them up ──
141
- import { PermissionPrompter } from "../permissions/prompter.js";
142
- import { defaultToolExecutePlugin } from "../plugins/defaults/tool-execute/register.js";
143
- import {
144
- getMiddlewaresFor,
145
- registerPlugin,
146
- resetPluginRegistryForTests,
147
- } from "../plugins/registry.js";
148
- import type {
149
- Middleware,
150
- Plugin,
151
- ToolExecuteArgs,
152
- ToolExecuteResult,
153
- } from "../plugins/types.js";
154
- import { ToolExecutor } from "../tools/executor.js";
155
- import type { ToolContext } from "../tools/types.js";
156
-
157
- function makeContext(overrides?: Partial<ToolContext>): ToolContext {
158
- return {
159
- workingDir: "/tmp/project",
160
- conversationId: "conversation-pipeline",
161
- trustClass: "guardian",
162
- ...overrides,
163
- };
164
- }
165
-
166
- function makePrompter(): PermissionPrompter {
167
- return {
168
- prompt: async () => ({ decision: "allow" as const }),
169
- resolveConfirmation: () => {},
170
- updateSender: () => {},
171
- dispose: () => {},
172
- } as unknown as PermissionPrompter;
173
- }
174
-
175
- afterAll(() => {
176
- mock.restore();
177
- });
178
-
179
- beforeEach(() => {
180
- resetPluginRegistryForTests();
181
- lastToolCall = undefined;
182
- fakeToolResult = { content: "real tool output", isError: false };
183
- });
184
-
185
- describe("ToolExecutor.execute → toolExecute pipeline", () => {
186
- test("default pipeline (no plugins) runs the same execution path", async () => {
187
- // With no plugins registered, the pipeline has an empty middleware
188
- // array and the terminal (executeInternal) runs directly. The
189
- // observable result must match the unwrapped behavior.
190
- const executor = new ToolExecutor(makePrompter());
191
- const result = await executor.execute(
192
- "file_read",
193
- { path: "README.md" },
194
- makeContext(),
195
- );
196
-
197
- expect(result.isError).toBe(false);
198
- expect(result.content).toBe("real tool output");
199
- expect(lastToolCall).toEqual({
200
- name: "file_read",
201
- input: { path: "README.md" },
202
- });
203
- });
204
-
205
- test("default tool-execute plugin: registering the passthrough preserves behavior", async () => {
206
- // The default plugin is a passthrough whose middleware forwards to
207
- // `next`. Registering it should not change observable behavior —
208
- // the terminal still runs and returns the real tool result.
209
- registerPlugin(defaultToolExecutePlugin);
210
-
211
- // Sanity: the registry now reports exactly one middleware for the
212
- // `toolExecute` slot, named `defaultToolExecute`.
213
- const middlewares = getMiddlewaresFor("toolExecute");
214
- expect(middlewares).toHaveLength(1);
215
- expect(middlewares[0]?.name).toBe("defaultToolExecute");
216
-
217
- const executor = new ToolExecutor(makePrompter());
218
- const result = await executor.execute(
219
- "file_read",
220
- { path: "README.md" },
221
- makeContext(),
222
- );
223
-
224
- expect(result.isError).toBe(false);
225
- expect(result.content).toBe("real tool output");
226
- expect(lastToolCall).toEqual({
227
- name: "file_read",
228
- input: { path: "README.md" },
229
- });
230
- });
231
-
232
- test("spy middleware observes the full tool invocation (name, input, ctx)", async () => {
233
- let observedArgs: ToolExecuteArgs | undefined;
234
- let observedTurnCtx:
235
- | { conversationId: string; requestId: string }
236
- | undefined;
237
-
238
- const spyMiddleware: Middleware<ToolExecuteArgs, ToolExecuteResult> =
239
- async function spy(args, next, ctx) {
240
- observedArgs = args;
241
- observedTurnCtx = {
242
- conversationId: ctx.conversationId,
243
- requestId: ctx.requestId,
244
- };
245
- return next(args);
246
- };
247
-
248
- const spyPlugin: Plugin = {
249
- manifest: {
250
- name: "spy-tool-execute",
251
- version: "0.0.1",
252
- },
253
- middleware: { toolExecute: spyMiddleware },
254
- };
255
- registerPlugin(spyPlugin);
256
-
257
- const executor = new ToolExecutor(makePrompter());
258
- const ctx = makeContext({
259
- conversationId: "conv-spy",
260
- requestId: "req-spy",
261
- });
262
- const result = await executor.execute(
263
- "bash",
264
- { command: "echo hi", timeout_seconds: 10 },
265
- ctx,
266
- );
267
-
268
- // Spy observed the full args
269
- expect(observedArgs).toBeDefined();
270
- expect(observedArgs!.name).toBe("bash");
271
- expect(observedArgs!.input).toEqual({
272
- command: "echo hi",
273
- timeout_seconds: 10,
274
- });
275
- expect(observedArgs!.context).toBe(ctx);
276
-
277
- // Spy observed the synthesized TurnContext carrying conversation +
278
- // request IDs from the ToolContext.
279
- expect(observedTurnCtx).toEqual({
280
- conversationId: "conv-spy",
281
- requestId: "req-spy",
282
- });
283
-
284
- // Terminal still ran — result reflects the real tool output.
285
- expect(result.isError).toBe(false);
286
- expect(result.content).toBe("real tool output");
287
- expect(lastToolCall).toEqual({
288
- name: "bash",
289
- input: { command: "echo hi", timeout_seconds: 10 },
290
- });
291
- });
292
-
293
- test("short-circuit middleware intercepts and supplies a custom result", async () => {
294
- const syntheticResult: ToolExecuteResult = {
295
- content: "synthesized by middleware",
296
- isError: false,
297
- };
298
-
299
- const shortCircuit: Middleware<ToolExecuteArgs, ToolExecuteResult> =
300
- async function shortCircuitMw(_args, _next) {
301
- // Intentionally omit `next` — the terminal (real tool execution)
302
- // must not run.
303
- return syntheticResult;
304
- };
305
-
306
- const interceptPlugin: Plugin = {
307
- manifest: {
308
- name: "short-circuit-tool-execute",
309
- version: "0.0.1",
310
- },
311
- middleware: { toolExecute: shortCircuit },
312
- };
313
- registerPlugin(interceptPlugin);
314
-
315
- const executor = new ToolExecutor(makePrompter());
316
- const result = await executor.execute(
317
- "file_write",
318
- { path: "dangerous.txt", content: "should not run" },
319
- makeContext(),
320
- );
321
-
322
- expect(result).toEqual(syntheticResult);
323
- // The real tool execute must NOT have been called.
324
- expect(lastToolCall).toBeUndefined();
325
- });
326
-
327
- test("slow middleware does not trip a pipeline-level timeout", async () => {
328
- // Regression: the pipeline must NOT arm a timer — `executeWithTimeout`
329
- // inside `executeInternal` is the sole enforcer of the per-tool budget
330
- // and only wraps the actual tool call. Upstream phases (permission
331
- // checks, approval waits, middleware) must not race the tool budget,
332
- // because that would break the `execute()` never-throws contract when
333
- // a slow phase (e.g. a human clicking "allow") exceeds the budget.
334
- const slow: Middleware<ToolExecuteArgs, ToolExecuteResult> =
335
- async function slowMw(args, next) {
336
- await new Promise((resolve) => setTimeout(resolve, 50));
337
- return next(args);
338
- };
339
- registerPlugin({
340
- manifest: {
341
- name: "slow-tool-execute",
342
- version: "0.0.1",
343
- },
344
- middleware: { toolExecute: slow },
345
- });
346
-
347
- const prev = mockConfig.timeouts.toolExecutionTimeoutSec;
348
- mockConfig.timeouts.toolExecutionTimeoutSec = 0.01;
349
- try {
350
- const executor = new ToolExecutor(makePrompter());
351
- const result = await executor.execute(
352
- "file_read",
353
- { path: "README.md" },
354
- makeContext(),
355
- );
356
- // Middleware phase (50ms) exceeds the per-tool budget (10ms), but
357
- // that budget is only enforced inside `executeWithTimeout` around
358
- // the tool invocation itself. The terminal runs and succeeds.
359
- expect(result.isError).toBe(false);
360
- expect(result.content).toBe("real tool output");
361
- } finally {
362
- mockConfig.timeouts.toolExecutionTimeoutSec = prev;
363
- }
364
- });
365
-
366
- test("multiple middlewares compose in registration order (outer-first)", async () => {
367
- const trace: string[] = [];
368
-
369
- const outer: Middleware<ToolExecuteArgs, ToolExecuteResult> =
370
- async function outerMw(args, next) {
371
- trace.push("outer:before");
372
- const result = await next(args);
373
- trace.push("outer:after");
374
- return result;
375
- };
376
- const inner: Middleware<ToolExecuteArgs, ToolExecuteResult> =
377
- async function innerMw(args, next) {
378
- trace.push("inner:before");
379
- const result = await next(args);
380
- trace.push("inner:after");
381
- return result;
382
- };
383
-
384
- registerPlugin({
385
- manifest: {
386
- name: "outer-tool-execute",
387
- version: "0.0.1",
388
- },
389
- middleware: { toolExecute: outer },
390
- });
391
- registerPlugin({
392
- manifest: {
393
- name: "inner-tool-execute",
394
- version: "0.0.1",
395
- },
396
- middleware: { toolExecute: inner },
397
- });
398
-
399
- const executor = new ToolExecutor(makePrompter());
400
- const result = await executor.execute(
401
- "file_read",
402
- { path: "README.md" },
403
- makeContext(),
404
- );
405
-
406
- expect(result.isError).toBe(false);
407
- // Outer middleware wraps inner (registration order = onion order),
408
- // so the trace is outer:before → inner:before → terminal →
409
- // inner:after → outer:after.
410
- expect(trace).toEqual([
411
- "outer:before",
412
- "inner:before",
413
- "inner:after",
414
- "outer:after",
415
- ]);
416
- });
417
- });