@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,564 +0,0 @@
1
- /**
2
- * Unit tests for `plugins/pipeline.ts`.
3
- *
4
- * Covers:
5
- * - Onion composition order (outer → inner → terminal → inner → outer).
6
- * - Short-circuit (middleware that omits `next` — terminal is skipped).
7
- * - Error propagation (no internal try/catch — errors flow unchanged).
8
- * - Timeout (breached budget rejects with `PluginTimeoutError`).
9
- * - Log shape (one structured record per invocation, every field typed).
10
- */
11
-
12
- import { beforeEach, describe, expect, test } from "bun:test";
13
-
14
- import type { TrustContext } from "../daemon/trust-context.js";
15
- import {
16
- composeMiddleware,
17
- DEFAULT_TIMEOUTS,
18
- runPipeline,
19
- } from "../plugins/pipeline.js";
20
- import {
21
- type Middleware,
22
- PluginTimeoutError,
23
- type TurnContext,
24
- } from "../plugins/types.js";
25
-
26
- // A minimal fake pino-compatible logger. The pipeline runner detects a
27
- // `logger` slot on the context (shape `{ info(record, msg?) }`) and falls
28
- // back to the module logger only when that slot is absent. Tests pass this
29
- // fake in via `ctx.logger` so the runner emits into our capture buffer
30
- // instead of real stderr.
31
- type LogCall = [record: Record<string, unknown>, msg?: string];
32
-
33
- function makeFakeLogger(): {
34
- calls: LogCall[];
35
- info: (record: Record<string, unknown>, msg?: string) => void;
36
- warn: () => void;
37
- error: () => void;
38
- debug: () => void;
39
- trace: () => void;
40
- fatal: () => void;
41
- } {
42
- const calls: LogCall[] = [];
43
- return {
44
- calls,
45
- info: (record, msg) => {
46
- calls.push([record, msg]);
47
- },
48
- warn: () => {},
49
- error: () => {},
50
- debug: () => {},
51
- trace: () => {},
52
- fatal: () => {},
53
- };
54
- }
55
-
56
- let fakeLogger = makeFakeLogger();
57
-
58
- const trust: TrustContext = {
59
- sourceChannel: "vellum",
60
- trustClass: "guardian",
61
- };
62
-
63
- function makeCtx(overrides: Partial<TurnContext> = {}): TurnContext {
64
- return {
65
- requestId: "req-test",
66
- conversationId: "conv-test",
67
- turnIndex: 3,
68
- trust,
69
- // The runner reads `(ctx as { logger?: unknown }).logger` — we cast
70
- // through the partial type to attach it without widening TurnContext.
71
- ...({ logger: fakeLogger } as Partial<TurnContext>),
72
- ...overrides,
73
- };
74
- }
75
-
76
- beforeEach(() => {
77
- fakeLogger = makeFakeLogger();
78
- });
79
-
80
- type Args = { value: number };
81
- type Result = { value: number };
82
-
83
- describe("composeMiddleware", () => {
84
- test("invokes layers in outer→inner→terminal→inner→outer order", async () => {
85
- const trace: string[] = [];
86
-
87
- const outer: Middleware<Args, Result> = async (args, next) => {
88
- trace.push("outer:before");
89
- const result = await next(args);
90
- trace.push("outer:after");
91
- return result;
92
- };
93
-
94
- const inner: Middleware<Args, Result> = async (args, next) => {
95
- trace.push("inner:before");
96
- const result = await next(args);
97
- trace.push("inner:after");
98
- return result;
99
- };
100
-
101
- const terminal = async (args: Args): Promise<Result> => {
102
- trace.push("terminal");
103
- return { value: args.value * 2 };
104
- };
105
-
106
- const composed = composeMiddleware<Args, Result>([outer, inner], terminal);
107
- const result = await composed({ value: 7 }, makeCtx());
108
-
109
- expect(result).toEqual({ value: 14 });
110
- expect(trace).toEqual([
111
- "outer:before",
112
- "inner:before",
113
- "terminal",
114
- "inner:after",
115
- "outer:after",
116
- ]);
117
- });
118
-
119
- test("middleware that omits `next` short-circuits the chain", async () => {
120
- const trace: string[] = [];
121
-
122
- const shortCircuit: Middleware<Args, Result> = async (_args, _next) => {
123
- trace.push("short-circuit");
124
- return { value: 99 };
125
- };
126
-
127
- const inner: Middleware<Args, Result> = async (args, next) => {
128
- trace.push("inner");
129
- return next(args);
130
- };
131
-
132
- const terminal = async (_args: Args): Promise<Result> => {
133
- trace.push("terminal");
134
- return { value: 0 };
135
- };
136
-
137
- const composed = composeMiddleware<Args, Result>(
138
- [shortCircuit, inner],
139
- terminal,
140
- );
141
- const result = await composed({ value: 1 }, makeCtx());
142
-
143
- expect(result).toEqual({ value: 99 });
144
- expect(trace).toEqual(["short-circuit"]);
145
- });
146
-
147
- test("empty middleware list reduces to the terminal handler", async () => {
148
- const terminal = async (args: Args): Promise<Result> => ({
149
- value: args.value + 1,
150
- });
151
- const composed = composeMiddleware<Args, Result>([], terminal);
152
- const result = await composed({ value: 10 }, makeCtx());
153
- expect(result).toEqual({ value: 11 });
154
- });
155
- });
156
-
157
- describe("runPipeline — error propagation", () => {
158
- test("errors thrown by middleware bubble through unchanged", async () => {
159
- class Boom extends Error {
160
- override readonly name = "Boom";
161
- }
162
-
163
- const thrower: Middleware<Args, Result> = async () => {
164
- throw new Boom("detonated");
165
- };
166
-
167
- const terminal = async (_args: Args): Promise<Result> => {
168
- throw new Error("terminal should not run");
169
- };
170
-
171
- await expect(
172
- runPipeline(
173
- "persistence",
174
- [thrower],
175
- terminal,
176
- { value: 1 },
177
- makeCtx(),
178
- DEFAULT_TIMEOUTS.persistence,
179
- ),
180
- ).rejects.toBeInstanceOf(Boom);
181
- });
182
-
183
- test("errors thrown by the terminal handler bubble through unchanged", async () => {
184
- const terminal = async (_args: Args): Promise<Result> => {
185
- throw new TypeError("from terminal");
186
- };
187
-
188
- await expect(
189
- runPipeline(
190
- "persistence",
191
- [],
192
- terminal,
193
- { value: 1 },
194
- makeCtx(),
195
- DEFAULT_TIMEOUTS.persistence,
196
- ),
197
- ).rejects.toBeInstanceOf(TypeError);
198
- });
199
- });
200
-
201
- describe("runPipeline — timeout", () => {
202
- test("breached budget rejects with PluginTimeoutError carrying pipeline + plugin name", async () => {
203
- const sleeper: Middleware<Args, Result> = async (_args, _next) =>
204
- new Promise<Result>((resolve) => {
205
- setTimeout(() => resolve({ value: 0 }), 200);
206
- });
207
-
208
- const terminal = async (_args: Args): Promise<Result> => ({ value: 0 });
209
-
210
- let caught: unknown;
211
- try {
212
- await runPipeline(
213
- "memoryRetrieval",
214
- [sleeper],
215
- terminal,
216
- { value: 1 },
217
- makeCtx({ pluginName: "slow-plugin" }),
218
- 20,
219
- );
220
- } catch (err) {
221
- caught = err;
222
- }
223
-
224
- expect(caught).toBeInstanceOf(PluginTimeoutError);
225
- const tErr = caught as PluginTimeoutError;
226
- expect(tErr.pipeline).toBe("memoryRetrieval");
227
- expect(tErr.pluginName).toBe("slow-plugin");
228
- expect(tErr.elapsedMs).toBeGreaterThanOrEqual(0);
229
- expect(tErr.message).toContain("memoryRetrieval");
230
- expect(tErr.message).toContain("slow-plugin");
231
- });
232
-
233
- test("fast pipeline does not arm the timer redundantly", async () => {
234
- const terminal = async (args: Args): Promise<Result> => ({
235
- value: args.value,
236
- });
237
- const result = await runPipeline(
238
- "tokenEstimate",
239
- [],
240
- terminal,
241
- { value: 42 },
242
- makeCtx(),
243
- DEFAULT_TIMEOUTS.tokenEstimate,
244
- );
245
- expect(result).toEqual({ value: 42 });
246
- });
247
-
248
- test("null timeout skips the race entirely", async () => {
249
- // llmCall has DEFAULT_TIMEOUTS.llmCall === null — runner must not arm a
250
- // timer. We verify by completing after an artificial 30ms wait and
251
- // confirming success without interference.
252
- const sleeper: Middleware<Args, Result> = async (args, next) =>
253
- new Promise<Result>((resolve) => {
254
- setTimeout(() => resolve(next(args)), 30);
255
- });
256
- const terminal = async (_args: Args): Promise<Result> => ({ value: 1 });
257
- const result = await runPipeline(
258
- "llmCall",
259
- [sleeper],
260
- terminal,
261
- { value: 0 },
262
- makeCtx(),
263
- DEFAULT_TIMEOUTS.llmCall,
264
- );
265
- expect(result).toEqual({ value: 1 });
266
- });
267
- });
268
-
269
- describe("runPipeline — timeout aborts linked signal", () => {
270
- test("abort signal on args is fired when the timeout trips", async () => {
271
- const callerController = new AbortController();
272
- type SignalArgs = { value: number; signal: AbortSignal };
273
-
274
- let observedAbortedAtCallStart = false;
275
- let observedAbortedAtCallEnd = false;
276
-
277
- const sleeper: Middleware<SignalArgs, Result> = async (
278
- innerArgs,
279
- _next,
280
- ) => {
281
- observedAbortedAtCallStart = innerArgs.signal.aborted;
282
- return new Promise<Result>((resolve, reject) => {
283
- innerArgs.signal.addEventListener("abort", () => {
284
- observedAbortedAtCallEnd = innerArgs.signal.aborted;
285
- reject(Object.assign(new Error("aborted"), { name: "AbortError" }));
286
- });
287
- // Keep running past the timeout if the signal doesn't fire.
288
- setTimeout(() => resolve({ value: 0 }), 500);
289
- });
290
- };
291
-
292
- const terminal = async (_args: SignalArgs): Promise<Result> => ({
293
- value: 0,
294
- });
295
-
296
- await expect(
297
- runPipeline<SignalArgs, Result>(
298
- "compaction",
299
- [sleeper],
300
- terminal,
301
- { value: 1, signal: callerController.signal },
302
- makeCtx(),
303
- 15,
304
- ),
305
- ).rejects.toBeInstanceOf(PluginTimeoutError);
306
-
307
- expect(observedAbortedAtCallStart).toBe(false);
308
- expect(observedAbortedAtCallEnd).toBe(true);
309
- // Caller's own signal must not be touched — the runner only aborts
310
- // its internal linked signal, not the caller-owned controller.
311
- expect(callerController.signal.aborted).toBe(false);
312
-
313
- // Log record still reports timeout outcome + correct fields even though
314
- // the inner middleware rejected with AbortError; the outer race still
315
- // wins with the PluginTimeoutError.
316
- const [record] = fakeLogger.calls[0]!;
317
- expect(record.outcome).toBe("timeout");
318
- expect(record.errorName).toBe("PluginTimeoutError");
319
- });
320
-
321
- test("caller-side abort still propagates to the inner call", async () => {
322
- const callerController = new AbortController();
323
- type SignalArgs = { signal: AbortSignal };
324
-
325
- let innerSignalAborted = false;
326
-
327
- const sleeper: Middleware<SignalArgs, Result> = async (innerArgs) => {
328
- return new Promise<Result>((_resolve, reject) => {
329
- innerArgs.signal.addEventListener("abort", () => {
330
- innerSignalAborted = innerArgs.signal.aborted;
331
- reject(Object.assign(new Error("aborted"), { name: "AbortError" }));
332
- });
333
- });
334
- };
335
-
336
- const terminal = async (_args: SignalArgs): Promise<Result> => ({
337
- value: 0,
338
- });
339
-
340
- // Fire a caller-side abort after a short delay.
341
- setTimeout(() => callerController.abort(), 10);
342
-
343
- await expect(
344
- runPipeline<SignalArgs, Result>(
345
- "compaction",
346
- [sleeper],
347
- terminal,
348
- { signal: callerController.signal },
349
- makeCtx(),
350
- 10000,
351
- ),
352
- ).rejects.toMatchObject({ name: "AbortError" });
353
-
354
- expect(innerSignalAborted).toBe(true);
355
- expect(callerController.signal.aborted).toBe(true);
356
- });
357
-
358
- test("args without an AbortSignal property is passed through unchanged", async () => {
359
- // Sanity — pipelines that don't carry a signal (persistence, tokenEstimate)
360
- // see identical args identity as before the abort-linking change.
361
- const args: Args = { value: 42 };
362
- let seen: Args | undefined;
363
- const terminal = async (innerArgs: Args): Promise<Result> => {
364
- seen = innerArgs;
365
- return { value: innerArgs.value };
366
- };
367
- await runPipeline(
368
- "persistence",
369
- [],
370
- terminal,
371
- args,
372
- makeCtx(),
373
- DEFAULT_TIMEOUTS.persistence,
374
- );
375
- expect(seen).toBe(args);
376
- });
377
- });
378
-
379
- describe("runPipeline — structured log record", () => {
380
- test("success emits one record with every documented field present", async () => {
381
- const namedOuter: Middleware<Args, Result> = async function outerMw(
382
- args,
383
- next,
384
- ) {
385
- return next(args);
386
- };
387
- const terminal = async (args: Args): Promise<Result> => ({
388
- value: args.value,
389
- });
390
-
391
- await runPipeline(
392
- "compaction",
393
- [namedOuter],
394
- terminal,
395
- { value: 7 },
396
- makeCtx(),
397
- DEFAULT_TIMEOUTS.compaction,
398
- );
399
-
400
- expect(fakeLogger.calls.length).toBe(1);
401
- const [record, msg] = fakeLogger.calls[0]!;
402
- expect(msg).toBe("plugin.pipeline");
403
- expect(record.event).toBe("plugin.pipeline");
404
- expect(record.pipeline).toBe("compaction");
405
- expect(record.chain).toEqual(["outerMw"]);
406
- expect(record.outcome).toBe("success");
407
- expect(typeof record.durationMs).toBe("number");
408
- expect(record.durationMs).toBeGreaterThanOrEqual(0);
409
- expect(record.requestId).toBe("req-test");
410
- expect(record.conversationId).toBe("conv-test");
411
- expect(record.turnIndex).toBe(3);
412
- // pluginName is only present when ctx carries one.
413
- expect(record.pluginName).toBeUndefined();
414
- // Error fields absent on success.
415
- expect(record.errorName).toBeUndefined();
416
- expect(record.errorMessage).toBeUndefined();
417
- expect(record.errorStack).toBeUndefined();
418
- });
419
-
420
- test("error path records outcome=error + error fields + plugin name", async () => {
421
- class Boom extends Error {
422
- override readonly name = "BoomError";
423
- }
424
- const thrower: Middleware<Args, Result> = async () => {
425
- throw new Boom("kaboom");
426
- };
427
- const terminal = async (_args: Args): Promise<Result> => ({ value: 0 });
428
-
429
- await expect(
430
- runPipeline(
431
- "toolError",
432
- [thrower],
433
- terminal,
434
- { value: 1 },
435
- makeCtx({ pluginName: "noisy-plugin" }),
436
- DEFAULT_TIMEOUTS.toolError,
437
- ),
438
- ).rejects.toBeInstanceOf(Boom);
439
-
440
- expect(fakeLogger.calls.length).toBe(1);
441
- const [record] = fakeLogger.calls[0]!;
442
- expect(record.outcome).toBe("error");
443
- expect(record.pipeline).toBe("toolError");
444
- expect(record.errorName).toBe("BoomError");
445
- expect(record.errorMessage).toBe("kaboom");
446
- expect(typeof record.errorStack).toBe("string");
447
- expect(record.pluginName).toBe("noisy-plugin");
448
- });
449
-
450
- test("timeout path records outcome=timeout + PluginTimeoutError fields", async () => {
451
- const sleeper: Middleware<Args, Result> = async (_args, _next) =>
452
- new Promise<Result>((resolve) => {
453
- setTimeout(() => resolve({ value: 0 }), 200);
454
- });
455
- const terminal = async (_args: Args): Promise<Result> => ({ value: 0 });
456
-
457
- await expect(
458
- runPipeline(
459
- "emptyResponse",
460
- [sleeper],
461
- terminal,
462
- { value: 1 },
463
- makeCtx({ pluginName: "slow-plugin" }),
464
- 15,
465
- ),
466
- ).rejects.toBeInstanceOf(PluginTimeoutError);
467
-
468
- expect(fakeLogger.calls.length).toBe(1);
469
- const [record] = fakeLogger.calls[0]!;
470
- expect(record.outcome).toBe("timeout");
471
- expect(record.pipeline).toBe("emptyResponse");
472
- expect(record.errorName).toBe("PluginTimeoutError");
473
- expect(String(record.errorMessage)).toContain("emptyResponse");
474
- expect(String(record.errorMessage)).toContain("slow-plugin");
475
- expect(record.timeoutMs).toBe(15);
476
- expect(record.pluginName).toBe("slow-plugin");
477
- });
478
-
479
- test("null timeout omits timeoutMs field from the log record", async () => {
480
- const terminal = async (args: Args): Promise<Result> => ({
481
- value: args.value,
482
- });
483
- await runPipeline(
484
- "llmCall",
485
- [],
486
- terminal,
487
- { value: 5 },
488
- makeCtx(),
489
- DEFAULT_TIMEOUTS.llmCall,
490
- );
491
-
492
- expect(fakeLogger.calls.length).toBe(1);
493
- const [record] = fakeLogger.calls[0]!;
494
- expect(record.pipeline).toBe("llmCall");
495
- expect(record.outcome).toBe("success");
496
- expect(record.timeoutMs).toBeUndefined();
497
- });
498
-
499
- test("turnIndex is omitted when unset on the context", async () => {
500
- const terminal = async (_args: Args): Promise<Result> => ({ value: 0 });
501
- const ctxNoTurn = {
502
- requestId: "r",
503
- conversationId: "c",
504
- turnIndex: undefined as unknown as number,
505
- trust,
506
- logger: fakeLogger,
507
- } as TurnContext;
508
- await runPipeline(
509
- "persistence",
510
- [],
511
- terminal,
512
- { value: 0 },
513
- ctxNoTurn,
514
- null,
515
- );
516
- const [record] = fakeLogger.calls[0]!;
517
- expect(record.turnIndex).toBeUndefined();
518
- });
519
-
520
- test("chain list has one entry per middleware in registration order", async () => {
521
- const a: Middleware<Args, Result> = async function outerA(args, next) {
522
- return next(args);
523
- };
524
- const b: Middleware<Args, Result> = async function middleB(args, next) {
525
- return next(args);
526
- };
527
- const c: Middleware<Args, Result> = async function innerC(args, next) {
528
- return next(args);
529
- };
530
- const terminal = async (args: Args): Promise<Result> => ({
531
- value: args.value,
532
- });
533
- await runPipeline(
534
- "tokenEstimate",
535
- [a, b, c],
536
- terminal,
537
- { value: 0 },
538
- makeCtx(),
539
- DEFAULT_TIMEOUTS.tokenEstimate,
540
- );
541
- const [record] = fakeLogger.calls[0]!;
542
- expect(record.chain).toEqual(["outerA", "middleB", "innerC"]);
543
- });
544
- });
545
-
546
- describe("DEFAULT_TIMEOUTS", () => {
547
- test("matches the design-doc table exactly", () => {
548
- expect(DEFAULT_TIMEOUTS).toEqual({
549
- turn: null,
550
- llmCall: null,
551
- toolExecute: null,
552
- memoryRetrieval: null,
553
- tokenEstimate: null,
554
- compaction: null,
555
- overflowReduce: null,
556
- persistence: null,
557
- titleGenerate: null,
558
- toolResultTruncate: null,
559
- emptyResponse: null,
560
- toolError: null,
561
- circuitBreaker: null,
562
- });
563
- });
564
- });