@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,24 +1,17 @@
1
1
  /**
2
- * Unit tests for the default `overflowReduce` plugin (PR 23).
2
+ * Unit tests for `runOverflowReductionLoop` the direct-call overflow
3
+ * reducer driver.
3
4
  *
4
- * Two goals:
5
- * 1. The default middleware produces results **identical** to the historical
6
- * inline tier loop for a golden set of over-budget histories. We exercise
7
- * this by running the same inputs through two pathsthe pipeline and a
8
- * faithful re-implementation of the pre-PR-23 inline loop and asserting
9
- * the final `(messages, runMessages, injectionMode, reducerState,
10
- * reducerCompacted, attempts)` tuple matches byte-for-byte.
11
- * 2. A user-registered spy middleware observes **every** reduction attempt
12
- * when wrapped around the default. This covers the onion-composition
13
- * contract: the spy sees each call from the outside and can count
14
- * iterations without changing reducer behavior.
15
- *
16
- * The test creates its own plugin registry via
17
- * `resetPluginRegistryForTests()` and re-registers the default before each
18
- * case so the registry is deterministic across runs.
5
+ * The default loop produces results **identical** to the historical inline
6
+ * tier loop for a golden set of over-budget histories. We exercise this by
7
+ * running the same inputs through two paths `runOverflowReductionLoop` and
8
+ * a faithful re-implementation of the original inline loop — and asserting
9
+ * the final `(messages, runMessages, injectionMode, reducerState,
10
+ * reducerCompacted, attempts)` tuple matches byte-for-byte. Additional cases
11
+ * cover the two abort gates and the `reinjectForMode` two-flag semantics.
19
12
  */
20
13
 
21
- import { beforeEach, describe, expect, test } from "bun:test";
14
+ import { describe, expect, test } from "bun:test";
22
15
 
23
16
  import { estimatePromptTokens } from "../context/token-estimator.js";
24
17
  import type {
@@ -32,22 +25,10 @@ import {
32
25
  type ReducerState,
33
26
  } from "../daemon/context-overflow-reducer.js";
34
27
  import type { InjectionMode } from "../daemon/conversation-runtime-assembly.js";
35
- import type { TrustContext } from "../daemon/trust-context.js";
36
- import defaultOverflowReduceMiddleware from "../plugins/defaults/overflow-reduce/middlewares/overflowReduce.js";
37
- import { defaultOverflowReducePlugin } from "../plugins/defaults/overflow-reduce/register.js";
38
- import { runPipeline } from "../plugins/pipeline.js";
39
28
  import {
40
- getMiddlewaresFor,
41
- registerPlugin,
42
- resetPluginRegistryForTests,
43
- } from "../plugins/registry.js";
44
- import type {
45
- Middleware,
46
- OverflowReduceArgs,
47
- OverflowReduceResult,
48
- Plugin,
49
- TurnContext,
50
- } from "../plugins/types.js";
29
+ type OverflowReduceArgs,
30
+ runOverflowReductionLoop,
31
+ } from "../daemon/overflow-reduction-loop.js";
51
32
  import type { Message } from "../providers/types.js";
52
33
 
53
34
  // ── Fixtures ────────────────────────────────────────────────────────────────
@@ -87,21 +68,6 @@ const CONTEXT_WINDOW = {
87
68
  },
88
69
  };
89
70
 
90
- const TRUST: TrustContext = {
91
- sourceChannel: "vellum",
92
- trustClass: "guardian",
93
- };
94
-
95
- function makeTurnContext(overrides: Partial<TurnContext> = {}): TurnContext {
96
- return {
97
- requestId: "req-overflow-test",
98
- conversationId: "conv-overflow-test",
99
- turnIndex: 0,
100
- trust: TRUST,
101
- ...overrides,
102
- };
103
- }
104
-
105
71
  /**
106
72
  * Minimal compaction stub — always compacts to a one-message summary so the
107
73
  * reducer's forced-compaction tier succeeds. Mirrors `makeCompactFn` from
@@ -146,9 +112,9 @@ function makeCompactFn(
146
112
  }
147
113
 
148
114
  /**
149
- * Faithful re-implementation of the pre-PR-23 inline tier loop — lives in
115
+ * Faithful re-implementation of the original inline tier loop — lives in
150
116
  * this test file rather than the production module so we have an immutable
151
- * baseline the default middleware can be diffed against. If either
117
+ * baseline `runOverflowReductionLoop` can be diffed against. If either
152
118
  * implementation drifts, the golden-output cases below fail.
153
119
  *
154
120
  * The function intentionally avoids any side effects on external state — no
@@ -267,9 +233,9 @@ function buildArgs(messages: Message[]): {
267
233
 
268
234
  // Identity reinject: the test harness does not exercise the full
269
235
  // `applyRuntimeInjections` pipeline; it simply tracks how many times the
270
- // orchestrator would have been asked to rebuild `runMessages` so the spy
271
- // middleware can attribute each iteration. Returns the reducer's latest
272
- // `messages` untouched — real orchestrator code re-injects runtime blocks.
236
+ // orchestrator would have been asked to rebuild `runMessages`. Returns the
237
+ // reducer's latest `messages` untouched real orchestrator code re-injects
238
+ // runtime blocks.
273
239
  const reinjectForMode = async (
274
240
  reducedMessages: Message[],
275
241
  mode: InjectionMode,
@@ -295,7 +261,7 @@ function buildArgs(messages: Message[]): {
295
261
  toolTokenBudget: 0,
296
262
  maxAttempts: CONTEXT_WINDOW.overflowRecovery.maxAttempts,
297
263
  // `OverflowReduceArgs.compactFn` types `options` as `unknown` to avoid
298
- // leaking the `ContextWindowCompactOptions` shape into the plugin
264
+ // leaking the `ContextWindowCompactOptions` shape into the loop's args
299
265
  // surface. The test helper produces a real `ContextWindowCompactOptions`
300
266
  // signature, so we trampoline through a widened wrapper.
301
267
  compactFn: (msgs, signal, opts) =>
@@ -315,14 +281,10 @@ function buildArgs(messages: Message[]): {
315
281
 
316
282
  // ── Test suite ──────────────────────────────────────────────────────────────
317
283
 
318
- describe("overflow-reduce pipeline", () => {
319
- beforeEach(() => {
320
- resetPluginRegistryForTests();
321
- registerPlugin(defaultOverflowReducePlugin);
322
- });
323
-
324
- describe("default middleware matches historical inline loop", () => {
284
+ describe("runOverflowReductionLoop", () => {
285
+ describe("matches historical inline loop", () => {
325
286
  test("large tool-result history — identical reduced output", async () => {
287
+ // GIVEN an over-budget history dominated by a large tool result.
326
288
  const longToolResult = "r".repeat(8000);
327
289
  const goldenHistory: Message[] = [
328
290
  msg("user", "Start"),
@@ -332,27 +294,13 @@ describe("overflow-reduce pipeline", () => {
332
294
  msg("user", "Next"),
333
295
  ];
334
296
 
335
- const pipelineBuild = buildArgs(goldenHistory);
297
+ // AND two independently-built arg sets over the SAME fixture so the
298
+ // direct call and the inline baseline never share a `compactFn`.
299
+ const directBuild = buildArgs(goldenHistory);
336
300
  const inlineBuild = buildArgs(goldenHistory);
337
301
 
338
- // Run both paths against the SAME fixture. `buildArgs` gives each
339
- // call its own `compactFn` instance so nothing leaks between runs.
340
- const pipelineResult = await runPipeline<
341
- OverflowReduceArgs,
342
- OverflowReduceResult
343
- >(
344
- "overflowReduce",
345
- getMiddlewaresFor("overflowReduce"),
346
- // Sentinel terminal — the default middleware doesn't call next,
347
- // so this must never fire. Assert that invariant here.
348
- async () => {
349
- throw new Error("terminal unexpectedly reached");
350
- },
351
- pipelineBuild.args,
352
- makeTurnContext(),
353
- 30000,
354
- );
355
-
302
+ // WHEN we reduce via the direct loop and the inline baseline.
303
+ const directResult = await runOverflowReductionLoop(directBuild.args);
356
304
  const inlineResult = await runInlineBaseline({
357
305
  messages: goldenHistory,
358
306
  runMessages: goldenHistory,
@@ -367,41 +315,27 @@ describe("overflow-reduce pipeline", () => {
367
315
  estimatePostInjection: inlineBuild.args.estimatePostInjection,
368
316
  });
369
317
 
370
- // Byte-for-byte match across every field the orchestrator relies on.
371
- expect(pipelineResult.messages).toEqual(inlineResult.messages);
372
- expect(pipelineResult.runMessages).toEqual(inlineResult.runMessages);
373
- expect(pipelineResult.injectionMode).toBe(inlineResult.injectionMode);
374
- expect(pipelineResult.reducerState).toEqual(inlineResult.reducerState);
375
- expect(pipelineResult.reducerCompacted).toBe(
376
- inlineResult.reducerCompacted,
377
- );
378
- expect(pipelineResult.attempts).toBe(inlineResult.attempts);
318
+ // THEN every field the orchestrator relies on matches byte-for-byte.
319
+ expect(directResult.messages).toEqual(inlineResult.messages);
320
+ expect(directResult.runMessages).toEqual(inlineResult.runMessages);
321
+ expect(directResult.injectionMode).toBe(inlineResult.injectionMode);
322
+ expect(directResult.reducerState).toEqual(inlineResult.reducerState);
323
+ expect(directResult.reducerCompacted).toBe(inlineResult.reducerCompacted);
324
+ expect(directResult.attempts).toBe(inlineResult.attempts);
379
325
  });
380
326
 
381
327
  test("small conversation that fits after first reduction — single attempt", async () => {
382
- // A history that's already within budget so the first `applyForcedCompaction`
383
- // brings us under — the loop must exit without iterating further.
328
+ // GIVEN a history that the first forced compaction brings under budget.
384
329
  const smallHistory: Message[] = [
385
330
  msg("user", "Hello"),
386
331
  msg("assistant", "Hi there — how can I help?"),
387
332
  ];
388
333
 
389
- const pipelineBuild = buildArgs(smallHistory);
334
+ const directBuild = buildArgs(smallHistory);
390
335
  const inlineBuild = buildArgs(smallHistory);
391
336
 
392
- const pipelineResult = await runPipeline<
393
- OverflowReduceArgs,
394
- OverflowReduceResult
395
- >(
396
- "overflowReduce",
397
- getMiddlewaresFor("overflowReduce"),
398
- async () => {
399
- throw new Error("terminal unexpectedly reached");
400
- },
401
- pipelineBuild.args,
402
- makeTurnContext(),
403
- 30000,
404
- );
337
+ // WHEN we reduce via the direct loop and the inline baseline.
338
+ const directResult = await runOverflowReductionLoop(directBuild.args);
405
339
  const inlineResult = await runInlineBaseline({
406
340
  messages: smallHistory,
407
341
  runMessages: smallHistory,
@@ -416,149 +350,17 @@ describe("overflow-reduce pipeline", () => {
416
350
  estimatePostInjection: inlineBuild.args.estimatePostInjection,
417
351
  });
418
352
 
419
- expect(pipelineResult.attempts).toBe(inlineResult.attempts);
420
- expect(pipelineResult.attempts).toBeGreaterThanOrEqual(1);
421
- expect(pipelineResult.messages).toEqual(inlineResult.messages);
422
- expect(pipelineResult.reducerCompacted).toBe(
423
- inlineResult.reducerCompacted,
424
- );
425
- });
426
- });
427
-
428
- describe("spy middleware observes each reduction attempt", () => {
429
- test("spy sees one invocation when the default converges in one step", async () => {
430
- const history: Message[] = [msg("user", "Hello"), msg("assistant", "Hi")];
431
-
432
- // Spy tracks the args passed into its layer. It must forward via
433
- // `next` so the default still fires.
434
- const spyCalls: Array<{
435
- hadMessages: number;
436
- budget: number;
437
- attempts: number;
438
- }> = [];
439
- const spy: Middleware<OverflowReduceArgs, OverflowReduceResult> =
440
- async function spyMiddleware(args, next, _ctx) {
441
- spyCalls.push({
442
- hadMessages: args.messages.length,
443
- budget: args.preflightBudget,
444
- attempts: 0, // populated after next() from the result
445
- });
446
- const result = await next(args);
447
- spyCalls[spyCalls.length - 1]!.attempts = result.attempts;
448
- return result;
449
- };
450
- const spyPlugin: Plugin = {
451
- manifest: {
452
- name: "spy-overflow",
453
- version: "0.0.1",
454
- },
455
- middleware: { overflowReduce: spy },
456
- };
457
- // Register spy first so it wraps the default (registration order =
458
- // outer→inner). The default therefore runs as the spy's downstream.
459
- resetPluginRegistryForTests();
460
- registerPlugin(spyPlugin);
461
- registerPlugin(defaultOverflowReducePlugin);
462
-
463
- const { args } = buildArgs(history);
464
- const result = await runPipeline<
465
- OverflowReduceArgs,
466
- OverflowReduceResult
467
- >(
468
- "overflowReduce",
469
- getMiddlewaresFor("overflowReduce"),
470
- async () => {
471
- throw new Error("terminal unexpectedly reached");
472
- },
473
- args,
474
- makeTurnContext(),
475
- 30000,
476
- );
477
-
478
- // Spy was called exactly once — the pipeline invokes each middleware
479
- // once per pipeline call, not once per reducer iteration. Iteration
480
- // count shows up in the result.attempts field.
481
- expect(spyCalls).toHaveLength(1);
482
- expect(spyCalls[0]?.hadMessages).toBe(2);
483
- expect(spyCalls[0]?.budget).toBe(1000);
484
- expect(spyCalls[0]?.attempts).toBe(result.attempts);
485
- expect(result.attempts).toBeGreaterThanOrEqual(1);
486
- });
487
-
488
- test("spy can short-circuit the default by not calling next", async () => {
489
- const history: Message[] = [msg("user", "Hi")];
490
-
491
- const shortCircuit: Middleware<OverflowReduceArgs, OverflowReduceResult> =
492
- async function shortCircuitMiddleware(args, _next, _ctx) {
493
- // Returns a synthetic "no-op" result — the default is never invoked.
494
- return {
495
- messages: args.messages,
496
- runMessages: args.runMessages,
497
- injectionMode: "minimal",
498
- reducerState: {
499
- appliedTiers: ["injection_downgrade"],
500
- injectionMode: "minimal",
501
- exhausted: true,
502
- },
503
- reducerCompacted: false,
504
- attempts: 0,
505
- };
506
- };
507
- resetPluginRegistryForTests();
508
- registerPlugin({
509
- manifest: {
510
- name: "short-circuit-overflow",
511
- version: "0.0.1",
512
- },
513
- middleware: { overflowReduce: shortCircuit },
514
- });
515
- registerPlugin(defaultOverflowReducePlugin);
516
-
517
- const { args, compactionResults, reinjectCalls } = buildArgs(history);
518
- const result = await runPipeline<
519
- OverflowReduceArgs,
520
- OverflowReduceResult
521
- >(
522
- "overflowReduce",
523
- getMiddlewaresFor("overflowReduce"),
524
- async () => {
525
- throw new Error("terminal unexpectedly reached");
526
- },
527
- args,
528
- makeTurnContext(),
529
- 30000,
530
- );
531
-
532
- // Because the outer middleware short-circuited, the default never
533
- // ran — no compactFn invocations, no reinject callbacks.
534
- expect(result.injectionMode).toBe("minimal");
535
- expect(result.attempts).toBe(0);
536
- expect(compactionResults).toHaveLength(0);
537
- expect(reinjectCalls).toHaveLength(0);
538
- });
539
- });
540
-
541
- describe("direct middleware invocation", () => {
542
- test("default middleware without the pipeline runner still executes the tier loop", async () => {
543
- const history: Message[] = [msg("user", "Hi")];
544
- const { args } = buildArgs(history);
545
-
546
- const result = await defaultOverflowReduceMiddleware(
547
- args,
548
- async () => {
549
- throw new Error("next should not be invoked by the default");
550
- },
551
- makeTurnContext(),
552
- );
553
-
554
- expect(result.attempts).toBeGreaterThanOrEqual(1);
555
- expect(result.reducerState.appliedTiers.length).toBeGreaterThanOrEqual(1);
353
+ // THEN both paths converge in the same single attempt with equal output.
354
+ expect(directResult.attempts).toBe(inlineResult.attempts);
355
+ expect(directResult.attempts).toBeGreaterThanOrEqual(1);
356
+ expect(directResult.messages).toEqual(inlineResult.messages);
357
+ expect(directResult.reducerCompacted).toBe(inlineResult.reducerCompacted);
556
358
  });
557
359
  });
558
360
 
559
361
  describe("abort signal propagation", () => {
560
- test("middleware bails between iterations when abortSignal fires", async () => {
561
- // History that won't converge in one step multiple iterations.
362
+ test("bails between iterations when abortSignal fires", async () => {
363
+ // GIVEN a history that won't converge in one step (multiple iterations).
562
364
  const longToolResult = "r".repeat(8000);
563
365
  const history: Message[] = [
564
366
  msg("user", "Start"),
@@ -569,9 +371,8 @@ describe("overflow-reduce pipeline", () => {
569
371
 
570
372
  const controller = new AbortController();
571
373
  const build = buildArgs(history);
572
- // Abort on the first `estimatePostInjection` simulates the
573
- // pipeline-level timeout firing mid-turn. The next loop iteration
574
- // must see the signal and throw rather than starting another round.
374
+ // AND an estimator that aborts on its first call while reporting
375
+ // over-budget so without the abort gate another iteration would run.
575
376
  let estimateCalls = 0;
576
377
  const aborting: OverflowReduceArgs = {
577
378
  ...build.args,
@@ -579,26 +380,18 @@ describe("overflow-reduce pipeline", () => {
579
380
  estimatePostInjection: () => {
580
381
  estimateCalls++;
581
382
  if (estimateCalls === 1) controller.abort();
582
- // Return a value that guarantees another iteration would fire
583
- // without the abort gate.
584
383
  return build.args.preflightBudget + 1_000_000;
585
384
  },
586
385
  };
587
386
 
588
- await expect(
589
- defaultOverflowReduceMiddleware(
590
- aborting,
591
- async () => {
592
- throw new Error("next should not be invoked");
593
- },
594
- makeTurnContext(),
595
- ),
596
- ).rejects.toThrow();
597
- // Exactly one iteration ran; the abort gate stopped the next round.
387
+ // WHEN the loop runs THEN it throws on the post-side-effect abort gate.
388
+ await expect(runOverflowReductionLoop(aborting)).rejects.toThrow();
389
+ // AND exactly one iteration ran; the gate stopped the next round.
598
390
  expect(estimateCalls).toBe(1);
599
391
  });
600
392
 
601
- test("middleware refuses to start when abortSignal is already aborted", async () => {
393
+ test("refuses to start when abortSignal is already aborted", async () => {
394
+ // GIVEN an already-aborted signal.
602
395
  const history: Message[] = [msg("user", "Hi")];
603
396
  const controller = new AbortController();
604
397
  controller.abort();
@@ -608,16 +401,9 @@ describe("overflow-reduce pipeline", () => {
608
401
  abortSignal: controller.signal,
609
402
  };
610
403
 
611
- await expect(
612
- defaultOverflowReduceMiddleware(
613
- args,
614
- async () => {
615
- throw new Error("next should not be invoked");
616
- },
617
- makeTurnContext(),
618
- ),
619
- ).rejects.toThrow();
620
- // Reducer never ran — zero compaction and reinject callbacks observed.
404
+ // WHEN the loop runs THEN it throws before the reducer ever runs.
405
+ await expect(runOverflowReductionLoop(args)).rejects.toThrow();
406
+ // AND no compaction or reinject callbacks were observed.
621
407
  expect(build.compactionResults).toHaveLength(0);
622
408
  expect(build.reinjectCalls).toHaveLength(0);
623
409
  });
@@ -625,10 +411,10 @@ describe("overflow-reduce pipeline", () => {
625
411
 
626
412
  describe("reinjectForMode two-flag semantics", () => {
627
413
  test("stepCompacted reflects current iteration; accumulatedCompacted stays sticky", async () => {
628
- // Force multiple iterations by returning over-budget until the loop
629
- // exits on maxAttempts. First iteration compacts (stepCompacted=true);
630
- // subsequent iterations run other tiers (stepCompacted=false), but
631
- // accumulatedCompacted must remain true for slack suppression.
414
+ // GIVEN a history that stays over-budget so the loop runs every tier:
415
+ // the first iteration compacts (stepCompacted=true), later iterations
416
+ // run other tiers (stepCompacted=false), but accumulatedCompacted must
417
+ // remain true for slack suppression.
632
418
  const longToolResult = "r".repeat(8000);
633
419
  const history: Message[] = [
634
420
  msg("user", "Start"),
@@ -642,19 +428,13 @@ describe("overflow-reduce pipeline", () => {
642
428
  estimatePostInjection: () => build.args.preflightBudget + 1_000_000,
643
429
  };
644
430
 
645
- await defaultOverflowReduceMiddleware(
646
- overBudget,
647
- async () => {
648
- throw new Error("next should not be invoked");
649
- },
650
- makeTurnContext(),
651
- );
431
+ // WHEN the loop runs to exhaustion / maxAttempts.
432
+ await runOverflowReductionLoop(overBudget);
652
433
 
653
- // At least one compaction attempt happened.
434
+ // THEN at least one compaction iteration happened.
654
435
  expect(build.reinjectCalls.length).toBeGreaterThanOrEqual(1);
655
- // The first iteration that compacted set accumulatedCompacted=true,
656
- // and every subsequent call continues to see it true even when
657
- // that iteration's own step did NOT compact.
436
+ // AND once an iteration compacted, accumulatedCompacted stays true for
437
+ // every subsequent iteration even when that step did not compact.
658
438
  const firstCompactedAt = build.reinjectCalls.findIndex(
659
439
  (c) => c.stepCompacted,
660
440
  );