@vellumai/assistant 0.8.8 → 0.8.9-staging.2

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 (360) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/examples/plugins/echo/README.md +61 -60
  4. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  5. package/examples/plugins/echo/hooks/stop.ts +16 -0
  6. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  7. package/examples/plugins/echo/package.json +1 -2
  8. package/examples/plugins/echo/src/emit.ts +19 -0
  9. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  10. package/openapi.yaml +235 -6
  11. package/package.json +2 -2
  12. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  13. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  14. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  15. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  17. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  18. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  19. package/src/__tests__/agent-loop.test.ts +441 -96
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  21. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  22. package/src/__tests__/anthropic-provider.test.ts +1 -1
  23. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  24. package/src/__tests__/app-control-flow.test.ts +1 -1
  25. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  26. package/src/__tests__/app-executors.test.ts +132 -0
  27. package/src/__tests__/approval-cascade.test.ts +5 -4
  28. package/src/__tests__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  30. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  31. package/src/__tests__/channel-approvals.test.ts +1 -1
  32. package/src/__tests__/compaction-circuit.test.ts +258 -0
  33. package/src/__tests__/compaction-direct.test.ts +132 -0
  34. package/src/__tests__/compaction-events.test.ts +5 -5
  35. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  36. package/src/__tests__/context-overflow-reducer.test.ts +1 -1
  37. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -4
  38. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  39. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  40. package/src/__tests__/conversation-agent-loop-overflow.test.ts +142 -218
  41. package/src/__tests__/conversation-agent-loop.test.ts +297 -586
  42. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  43. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -4
  44. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  45. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  46. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -4
  47. package/src/__tests__/conversation-process-callsite.test.ts +14 -14
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +70 -65
  49. package/src/__tests__/conversation-queue.test.ts +9 -9
  50. package/src/__tests__/conversation-runtime-assembly.test.ts +923 -231
  51. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  52. package/src/__tests__/conversation-slash-queue.test.ts +6 -4
  53. package/src/__tests__/conversation-slash-unknown.test.ts +6 -4
  54. package/src/__tests__/conversation-speed-override.test.ts +10 -9
  55. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  56. package/src/__tests__/conversation-workspace-cache-state.test.ts +23 -20
  57. package/src/__tests__/conversation-workspace-injection.test.ts +68 -6
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +14 -11
  59. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  61. package/src/__tests__/db-acp-history.test.ts +101 -0
  62. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  63. package/src/__tests__/empty-response-hook.test.ts +1 -1
  64. package/src/__tests__/file-write-tool.test.ts +63 -0
  65. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  66. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  67. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  68. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  69. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  70. package/src/__tests__/heartbeat-service.test.ts +1 -0
  71. package/src/__tests__/history-repair-hook.test.ts +1 -1
  72. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  73. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  74. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  75. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  76. package/src/__tests__/injector-background-turn.test.ts +13 -23
  77. package/src/__tests__/injector-chain.test.ts +268 -44
  78. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  79. package/src/__tests__/injector-document-comments.test.ts +97 -114
  80. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  81. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  82. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  83. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  84. package/src/__tests__/memory-retrieval-hook.test.ts +131 -26
  85. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  86. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  87. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  88. package/src/__tests__/plugin-bootstrap.test.ts +12 -23
  89. package/src/__tests__/plugin-registry.test.ts +3 -49
  90. package/src/__tests__/plugin-types.test.ts +0 -70
  91. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  92. package/src/__tests__/reaction-persistence.test.ts +1 -1
  93. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  94. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  95. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  96. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  97. package/src/__tests__/subagent-detail.test.ts +25 -7
  98. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  99. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  100. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  101. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  102. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  103. package/src/__tests__/title-generate-hook.test.ts +1 -1
  104. package/src/__tests__/tool-error-hook.test.ts +1 -1
  105. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  106. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  107. package/src/acp/__tests__/agent-process.test.ts +161 -0
  108. package/src/acp/__tests__/client-handler.test.ts +40 -0
  109. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  110. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  111. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  112. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  113. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  114. package/src/acp/agent-process.ts +61 -1
  115. package/src/acp/auto-install.test.ts +280 -0
  116. package/src/acp/auto-install.ts +232 -0
  117. package/src/acp/client-handler.ts +31 -0
  118. package/src/acp/feature-gate.test.ts +48 -0
  119. package/src/acp/feature-gate.ts +34 -0
  120. package/src/acp/prepare-agent-env.ts +80 -27
  121. package/src/acp/resolve-agent.test.ts +225 -9
  122. package/src/acp/resolve-agent.ts +122 -17
  123. package/src/acp/resume-hint.ts +23 -0
  124. package/src/acp/session-manager.ts +507 -73
  125. package/src/agent/compaction-circuit.ts +60 -102
  126. package/src/agent/loop.ts +414 -248
  127. package/src/api/responses/conversation-message.ts +14 -1
  128. package/src/approvals/guardian-request-resolvers.ts +1 -1
  129. package/src/background-wake/next-wake.ts +1 -0
  130. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  131. package/src/cli/commands/plugins.ts +43 -37
  132. package/src/cli/lib/__tests__/install-from-github.test.ts +429 -111
  133. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  134. package/src/cli/lib/__tests__/plugin-details.test.ts +372 -0
  135. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +220 -0
  136. package/src/cli/lib/__tests__/search-plugins.test.ts +226 -32
  137. package/src/cli/lib/install-from-github.ts +464 -55
  138. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  139. package/src/cli/lib/plugin-details.ts +409 -0
  140. package/src/cli/lib/plugin-marketplace.ts +197 -0
  141. package/src/cli/lib/search-plugins.ts +195 -29
  142. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  143. package/src/config/acp-defaults.test.ts +10 -0
  144. package/src/config/acp-defaults.ts +6 -0
  145. package/src/config/bundled-skills/acp/SKILL.md +85 -33
  146. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  147. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  148. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  149. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  150. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  151. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  152. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  153. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  154. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  155. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  156. package/src/config/bundled-tool-registry.ts +4 -0
  157. package/src/config/call-site-defaults.ts +0 -2
  158. package/src/config/feature-flag-registry.json +15 -6
  159. package/src/config/schemas/call-site-catalog.ts +0 -14
  160. package/src/config/schemas/heartbeat.ts +9 -0
  161. package/src/config/schemas/llm.ts +0 -2
  162. package/src/context/compactor.ts +22 -4
  163. package/src/context/strip-injections.ts +8 -2
  164. package/src/context/window-manager.ts +27 -13
  165. package/src/daemon/conversation-agent-loop-handlers.ts +10 -35
  166. package/src/daemon/conversation-agent-loop.ts +175 -1055
  167. package/src/daemon/conversation-lifecycle.ts +11 -255
  168. package/src/daemon/conversation-process.ts +8 -136
  169. package/src/daemon/conversation-registry.ts +159 -0
  170. package/src/daemon/conversation-runtime-assembly.ts +293 -392
  171. package/src/daemon/conversation-store.ts +9 -90
  172. package/src/daemon/conversation-surfaces.ts +24 -8
  173. package/src/daemon/conversation-workspace.ts +17 -0
  174. package/src/daemon/conversation.ts +404 -57
  175. package/src/daemon/disk-pressure-policy.ts +0 -1
  176. package/src/daemon/external-plugins-bootstrap.ts +14 -19
  177. package/src/daemon/handlers/conversations.ts +3 -1
  178. package/src/daemon/handlers/skills.ts +4 -1
  179. package/src/daemon/host-proxy-preactivation.ts +1 -3
  180. package/src/daemon/lifecycle.ts +21 -7
  181. package/src/daemon/server.ts +2 -0
  182. package/src/daemon/wake-conversation-ops.ts +269 -0
  183. package/src/embedded/plugin-api.ts +2 -2
  184. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  185. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  186. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  187. package/src/heartbeat/heartbeat-service.ts +26 -0
  188. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  189. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  190. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  191. package/src/ipc/skill-routes/memory.ts +4 -2
  192. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  193. package/src/memory/conversation-crud.ts +29 -19
  194. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  195. package/src/memory/db-init.ts +2 -0
  196. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  197. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  198. package/src/memory/jobs-worker.ts +25 -1
  199. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  200. package/src/memory/migrations/index.ts +1 -0
  201. package/src/memory/schema/acp.ts +4 -0
  202. package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
  203. package/src/memory/v2/consolidation-job.ts +13 -4
  204. package/src/plugin-api/constants.ts +4 -0
  205. package/src/plugin-api/index.ts +6 -5
  206. package/src/plugin-api/types.ts +75 -0
  207. package/src/plugins/defaults/compaction/compact.ts +59 -0
  208. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +7 -7
  209. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  210. package/src/plugins/defaults/compaction/package.json +1 -2
  211. package/src/plugins/defaults/empty-response/package.json +0 -1
  212. package/src/plugins/defaults/history-repair/package.json +0 -1
  213. package/src/plugins/defaults/index.ts +135 -26
  214. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  215. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +184 -74
  216. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  217. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  218. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  219. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  220. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +9 -6
  221. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  222. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +8 -5
  223. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  224. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  225. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +10 -5
  226. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  227. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +5 -5
  228. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +16 -17
  229. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -2
  230. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +9 -5
  231. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +5 -2
  232. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  233. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  234. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  235. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +15 -8
  236. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +2 -2
  237. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  238. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  239. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/provider-blocks.ts +1 -1
  240. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +7 -3
  241. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  242. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +5 -5
  243. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  244. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +7 -7
  245. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +32 -94
  246. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  247. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +1 -1
  248. package/src/plugins/defaults/title-generate/package.json +0 -1
  249. package/src/plugins/defaults/tool-error/package.json +0 -1
  250. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  251. package/src/plugins/pipeline.ts +6 -293
  252. package/src/plugins/registry.ts +9 -37
  253. package/src/plugins/types.ts +76 -381
  254. package/src/plugins/user-loader.ts +30 -127
  255. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  256. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +35 -3
  257. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  258. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  259. package/src/runtime/agent-wake.ts +108 -209
  260. package/src/runtime/assistant-event-hub.ts +1 -1
  261. package/src/runtime/channel-approvals.ts +1 -1
  262. package/src/runtime/interactive-ui.ts +1 -1
  263. package/src/runtime/routes/__tests__/acp-routes.test.ts +315 -55
  264. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  265. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  266. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  267. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  268. package/src/runtime/routes/acp-routes.test.ts +89 -25
  269. package/src/runtime/routes/acp-routes.ts +81 -29
  270. package/src/runtime/routes/approval-routes.ts +1 -1
  271. package/src/runtime/routes/browser-routes.ts +1 -1
  272. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  273. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  274. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  275. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  276. package/src/runtime/routes/conversation-routes.ts +28 -2
  277. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  278. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  279. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  280. package/src/runtime/routes/host-cu-routes.ts +1 -1
  281. package/src/runtime/routes/identity-routes.ts +18 -3
  282. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  283. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  284. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  285. package/src/runtime/routes/memory-v3-routes.ts +16 -6
  286. package/src/runtime/routes/playground/helpers.ts +1 -1
  287. package/src/runtime/routes/plugins-routes.ts +337 -35
  288. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  289. package/src/runtime/routes/work-items-routes.ts +2 -4
  290. package/src/runtime/services/conversation-serializer.ts +1 -1
  291. package/src/signals/cancel.ts +2 -4
  292. package/src/subagent/manager.ts +21 -5
  293. package/src/tools/acp/context.ts +20 -0
  294. package/src/tools/acp/list-agents.test.ts +8 -2
  295. package/src/tools/acp/spawn.test.ts +176 -195
  296. package/src/tools/acp/spawn.ts +37 -172
  297. package/src/tools/acp/steer.test.ts +105 -8
  298. package/src/tools/acp/steer.ts +48 -17
  299. package/src/tools/apps/executors.ts +166 -50
  300. package/src/tools/filesystem/write.ts +34 -0
  301. package/src/tools/subagent/spawn.ts +2 -4
  302. package/src/tools/ui-surface/definitions.ts +25 -5
  303. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  304. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  305. package/docs/plugins.md +0 -832
  306. package/examples/plugins/echo/register.ts +0 -143
  307. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  308. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  309. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  310. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  311. package/src/__tests__/pipeline-runner.test.ts +0 -554
  312. package/src/__tests__/plugin-external-api.test.ts +0 -68
  313. package/src/daemon/wake-target-adapter.ts +0 -253
  314. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  315. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  316. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  317. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  318. package/src/plugins/defaults/compaction/register.ts +0 -35
  319. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  320. package/src/plugins/defaults/empty-response/register.ts +0 -23
  321. package/src/plugins/defaults/history-repair/register.ts +0 -24
  322. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  323. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  324. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  325. package/src/plugins/defaults/title-generate/register.ts +0 -35
  326. package/src/plugins/defaults/tool-error/register.ts +0 -23
  327. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  328. package/src/plugins/external-api.ts +0 -104
  329. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  330. package/src/proactive-artifact/decision.test.ts +0 -226
  331. package/src/proactive-artifact/decision.ts +0 -165
  332. package/src/proactive-artifact/index.ts +0 -7
  333. package/src/proactive-artifact/job.test.ts +0 -962
  334. package/src/proactive-artifact/job.ts +0 -372
  335. package/src/proactive-artifact/message-copy.ts +0 -58
  336. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  337. package/src/proactive-artifact/trigger-state.ts +0 -123
  338. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  339. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  340. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  341. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  342. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +0 -0
  343. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  344. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/provider-blocks.test.ts +0 -0
  345. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  346. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  347. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  348. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  349. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  350. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  351. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  352. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  353. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  354. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  355. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  356. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  357. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
  358. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  359. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  360. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -39,6 +39,20 @@ const conversationRuntimeAssemblyRealSnapshot = {
39
39
 
40
40
  // ── Module mocks (must precede imports of the module under test) ─────
41
41
 
42
+ // The real AgentLoop resolves the per-conversation ContextWindowManager from
43
+ // the compaction store keyed by conversationId. These overflow tests build
44
+ // fake conversations whose manager is a canned stub, so register each stub in a
45
+ // map the mocked store reads from.
46
+ const fakeContextWindowManagers = new Map<string, unknown>();
47
+ mock.module("../plugins/defaults/compaction/manager-store.js", () => ({
48
+ createContextWindowManager: () => undefined,
49
+ getContextWindowManager: (conversationId: string) =>
50
+ fakeContextWindowManagers.get(conversationId),
51
+ disposeContextWindowManager: (conversationId: string) => {
52
+ fakeContextWindowManagers.delete(conversationId);
53
+ },
54
+ }));
55
+
42
56
  mock.module("../util/logger.js", () => ({
43
57
  getLogger: () =>
44
58
  new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
@@ -100,7 +114,7 @@ mock.module("../config/loader.js", () => ({
100
114
  // Can be a number (constant), a no-arg function, or a function that
101
115
  // receives the messages array for dynamic behavior based on content.
102
116
  // Both the calibrated entry point (`estimatePromptTokens`, which backs the
103
- // preflight overflow gate and the convergence path) and the raw entry point
117
+ // loop's budget gate and the convergence path) and the raw entry point
104
118
  // (`estimatePromptTokensRaw`, used by the pre-send calibration capture) are
105
119
  // stubbed so either call site can drive the test.
106
120
  let mockEstimateTokens: number | ((msgs?: Message[]) => number) = 1000;
@@ -113,7 +127,7 @@ mock.module("../context/token-estimator.js", () => ({
113
127
  typeof mockEstimateTokens === "function"
114
128
  ? mockEstimateTokens(msgs)
115
129
  : mockEstimateTokens,
116
- // The preflight overflow gate calls this calibrated wrapper directly, so it
130
+ // The loop's budget gate calls this calibrated wrapper directly, so it
117
131
  // must honor `mockEstimateTokens` too — otherwise the real implementation
118
132
  // (which sums tool tokens onto the real calibrated estimate) ignores the
119
133
  // per-test value and the overflow scenarios below never trigger.
@@ -137,35 +151,38 @@ mock.module("../context/token-estimator.js", () => ({
137
151
  let mockReducerStepFn:
138
152
  | ((msgs: Message[], cfg: unknown, state: unknown) => unknown)
139
153
  | null = null;
140
- mock.module("../daemon/context-overflow-reducer.js", () => ({
141
- createInitialReducerState: () => ({
142
- appliedTiers: [],
143
- injectionMode: "full" as const,
144
- exhausted: false,
154
+ mock.module(
155
+ "../plugins/defaults/compaction/context-overflow-reducer.js",
156
+ () => ({
157
+ createInitialReducerState: () => ({
158
+ appliedTiers: [],
159
+ injectionMode: "full" as const,
160
+ exhausted: false,
161
+ }),
162
+ reduceContextOverflow: async (
163
+ msgs: Message[],
164
+ cfg: unknown,
165
+ state: unknown,
166
+ ) => {
167
+ if (mockReducerStepFn) return mockReducerStepFn(msgs, cfg, state);
168
+ return {
169
+ messages: msgs,
170
+ tier: "forced_compaction",
171
+ state: {
172
+ appliedTiers: [
173
+ "forced_compaction",
174
+ "tool_result_truncation",
175
+ "media_stubbing",
176
+ "injection_downgrade",
177
+ ],
178
+ injectionMode: "full",
179
+ exhausted: true,
180
+ },
181
+ estimatedTokens: 1000,
182
+ };
183
+ },
145
184
  }),
146
- reduceContextOverflow: async (
147
- msgs: Message[],
148
- cfg: unknown,
149
- state: unknown,
150
- ) => {
151
- if (mockReducerStepFn) return mockReducerStepFn(msgs, cfg, state);
152
- return {
153
- messages: msgs,
154
- tier: "forced_compaction",
155
- state: {
156
- appliedTiers: [
157
- "forced_compaction",
158
- "tool_result_truncation",
159
- "media_stubbing",
160
- "injection_downgrade",
161
- ],
162
- injectionMode: "full",
163
- exhausted: true,
164
- },
165
- estimatedTokens: 1000,
166
- };
167
- },
168
- }));
185
+ );
169
186
 
170
187
  // Policy: default to fail_gracefully
171
188
  let mockOverflowAction: string = "fail_gracefully";
@@ -202,7 +219,7 @@ mock.module("../memory/conversation-crud.js", () => ({
202
219
  updateMessageMetadata: () => {},
203
220
  setLastNotifiedInferenceProfile: () => {},
204
221
  getLastUserTimestampBefore: () => 0,
205
- getConversationOverrideProfileFromRow: () => undefined,
222
+ resolveOverrideProfile: () => undefined,
206
223
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
207
224
  }));
208
225
 
@@ -422,10 +439,8 @@ mock.module("../memory/archive-store.js", () => ({
422
439
  // ── Imports (after mocks) ────────────────────────────────────────────
423
440
 
424
441
  import { AgentLoop } from "../agent/loop.js";
425
- import {
426
- type AgentLoopConversationContext,
427
- runAgentLoopImpl,
428
- } from "../daemon/conversation-agent-loop.js";
442
+ import type { Conversation } from "../daemon/conversation.js";
443
+ import { runAgentLoopImpl } from "../daemon/conversation-agent-loop.js";
429
444
  import {
430
445
  createMockProvider,
431
446
  type ScriptedResponse,
@@ -436,13 +451,13 @@ import {
436
451
  // ── Test helpers ─────────────────────────────────────────────────────
437
452
 
438
453
  function makeCtx(
439
- overrides?: Partial<AgentLoopConversationContext> & {
454
+ overrides?: Partial<Conversation> & {
440
455
  providerResponses?: ScriptedResponse[];
441
456
  loopProvider?: Provider;
442
457
  loopTools?: ToolDefinition[];
443
458
  toolExecutor?: LoopToolExecutor;
444
459
  },
445
- ): AgentLoopConversationContext {
460
+ ): Conversation {
446
461
  const {
447
462
  providerResponses,
448
463
  loopProvider,
@@ -465,13 +480,15 @@ function makeCtx(
465
480
  providerResponses ?? [textResponse("response")],
466
481
  loopProviderName,
467
482
  ).provider;
468
- const agentLoop = new AgentLoop(provider, "system prompt", {
483
+ const agentLoop = new AgentLoop({
484
+ provider: provider,
485
+ systemPrompt: "system prompt",
469
486
  conversationId,
470
487
  tools: loopTools ?? [],
471
488
  toolExecutor,
472
489
  });
473
490
 
474
- return {
491
+ const ctx = {
475
492
  conversationId: "test-conv",
476
493
  messages: [
477
494
  { role: "user", content: [{ type: "text", text: "Hello" }] },
@@ -495,13 +512,14 @@ function makeCtx(
495
512
  usage: { inputTokens: 0, outputTokens: 0 },
496
513
  stopReason: "end_turn",
497
514
  }),
498
- } as unknown as AgentLoopConversationContext["provider"],
515
+ } as unknown as Conversation["provider"],
499
516
  systemPrompt: "system prompt",
500
517
 
501
518
  contextWindowManager: {
519
+ updateConfig: () => {},
502
520
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
503
521
  maybeCompact: async () => ({ compacted: false }),
504
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
522
+ } as unknown as Conversation["contextWindowManager"],
505
523
  contextCompactedMessageCount: 0,
506
524
  contextCompactedAt: null,
507
525
 
@@ -515,8 +533,6 @@ function makeCtx(
515
533
  currentTurnSurfaces: [],
516
534
 
517
535
  workingDir: "/tmp",
518
- workspaceTopLevelContext: null,
519
- workspaceTopLevelDirty: false,
520
536
  channelCapabilities: undefined,
521
537
  commandIntent: undefined,
522
538
  trustContext: undefined,
@@ -526,15 +542,15 @@ function makeCtx(
526
542
  preactivatedSkillIds: undefined,
527
543
  skillProjectionState: new Map(),
528
544
  skillProjectionCache:
529
- new Map() as unknown as AgentLoopConversationContext["skillProjectionCache"],
545
+ new Map() as unknown as Conversation["skillProjectionCache"],
530
546
 
531
547
  traceEmitter: {
532
548
  emit: () => {},
533
- } as unknown as AgentLoopConversationContext["traceEmitter"],
549
+ } as unknown as Conversation["traceEmitter"],
534
550
  profiler: {
535
551
  startRequest: () => {},
536
552
  emitSummary: () => {},
537
- } as unknown as AgentLoopConversationContext["profiler"],
553
+ } as unknown as Conversation["profiler"],
538
554
  usageStats: {
539
555
  totalInputTokens: 0,
540
556
  totalOutputTokens: 0,
@@ -547,13 +563,12 @@ function makeCtx(
547
563
  lastAttachmentWarnings: [],
548
564
 
549
565
  hasNoClient: false,
550
- prompter: {} as unknown as AgentLoopConversationContext["prompter"],
551
- queue: {} as unknown as AgentLoopConversationContext["queue"],
566
+ prompter: {} as unknown as Conversation["prompter"],
567
+ queue: {} as unknown as Conversation["queue"],
552
568
 
553
569
  getWorkspaceGitService: () => ({ ensureInitialized: async () => {} }),
554
570
  commitTurnChanges: async () => {},
555
571
 
556
- refreshWorkspaceTopLevelContextIfNeeded: () => {},
557
572
  markWorkspaceTopLevelDirty: () => {},
558
573
  emitActivityState: () => {},
559
574
  getQueueDepth: () => 0,
@@ -580,10 +595,12 @@ function makeCtx(
580
595
  }),
581
596
  retrackCachedNodes: () => {},
582
597
  recordPkbQueryVectors: () => {},
583
- } as unknown as AgentLoopConversationContext["graphMemory"],
598
+ } as unknown as Conversation["graphMemory"],
584
599
 
585
600
  ...ctxOverrides,
586
- } as AgentLoopConversationContext;
601
+ } as unknown as Conversation;
602
+ fakeContextWindowManagers.set(conversationId, ctx.contextWindowManager);
603
+ return ctx;
587
604
  }
588
605
 
589
606
  /**
@@ -651,10 +668,9 @@ beforeEach(() => {
651
668
  recordUsageMock.mockClear();
652
669
  setAgentLoopExitReasonOnLatestLogMock.mockClear();
653
670
  addMessageMock.mockClear();
654
- // Reset the plugin registry and re-register every default so the
655
- // orchestrator's pipelines (`overflowReduce`, `persistence`, …) dispatch to
656
- // the default middleware, which in turn hits the mocked collaborators
657
- // (`reduceContextOverflow`, `syncMessageToDisk`, …) these tests install.
671
+ // Reset the plugin registry and re-register every default so the compaction
672
+ // pipeline dispatches to the default middleware, which in turn hits the
673
+ // mocked collaborators (`syncMessageToDisk`, …) these tests install.
658
674
  resetPluginRegistryAndRegisterDefaults();
659
675
  });
660
676
 
@@ -768,9 +784,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
768
784
  isError: false,
769
785
  }),
770
786
  contextWindowManager: {
787
+ updateConfig: () => {},
771
788
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
772
789
  maybeCompact: async () => ({ compacted: false }),
773
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
790
+ } as unknown as Conversation["contextWindowManager"],
774
791
  });
775
792
 
776
793
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
@@ -797,9 +814,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
797
814
  const events: ServerMessage[] = [];
798
815
  let reducerCalled = false;
799
816
 
800
- // GIVEN the estimator reports 185k under the 190k preflight budget
801
- // (200k * 0.95), so the turn proceeds to the provider rather than
802
- // compacting up front.
817
+ // GIVEN the estimator reports 185k and the context manager's compaction
818
+ // is a no-op, so the first call proceeds to the provider without any
819
+ // up-front reduction.
803
820
  mockEstimateTokens = 185_000;
804
821
 
805
822
  // AND the post-run convergence reducer successfully compacts
@@ -842,9 +859,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
842
859
  const ctx = makeCtx({
843
860
  loopProvider: provider,
844
861
  contextWindowManager: {
862
+ updateConfig: () => {},
845
863
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
846
864
  maybeCompact: async () => ({ compacted: false }),
847
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
865
+ } as unknown as Conversation["contextWindowManager"],
848
866
  });
849
867
 
850
868
  // WHEN the turn runs
@@ -918,9 +936,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
918
936
  const ctx = makeCtx({
919
937
  loopProvider: provider,
920
938
  contextWindowManager: {
939
+ updateConfig: () => {},
921
940
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
922
941
  maybeCompact: async () => ({ compacted: false }),
923
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
942
+ } as unknown as Conversation["contextWindowManager"],
924
943
  });
925
944
 
926
945
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
@@ -1005,9 +1024,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1005
1024
  loopProvider: provider,
1006
1025
  messages: longHistory,
1007
1026
  contextWindowManager: {
1027
+ updateConfig: () => {},
1008
1028
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1009
1029
  maybeCompact: async () => ({ compacted: false }),
1010
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1030
+ } as unknown as Conversation["contextWindowManager"],
1011
1031
  });
1012
1032
 
1013
1033
  await runAgentLoopImpl(ctx, "analyze this", "msg-1", (msg) =>
@@ -1087,6 +1107,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1087
1107
  isError: false,
1088
1108
  }),
1089
1109
  contextWindowManager: {
1110
+ updateConfig: () => {},
1090
1111
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1091
1112
  maybeCompact: async (
1092
1113
  _msgs: Message[],
@@ -1118,7 +1139,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1118
1139
  }
1119
1140
  return { compacted: false };
1120
1141
  },
1121
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1142
+ } as unknown as Conversation["contextWindowManager"],
1122
1143
  });
1123
1144
 
1124
1145
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
@@ -1187,6 +1208,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1187
1208
  isError: false,
1188
1209
  }),
1189
1210
  contextWindowManager: {
1211
+ updateConfig: () => {},
1190
1212
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1191
1213
  maybeCompact: async () => {
1192
1214
  compactionCalled = true;
@@ -1211,7 +1233,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1211
1233
  summaryModel: "mock-model",
1212
1234
  };
1213
1235
  },
1214
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1236
+ } as unknown as Conversation["contextWindowManager"],
1215
1237
  });
1216
1238
 
1217
1239
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
@@ -1293,6 +1315,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1293
1315
  isError: false,
1294
1316
  }),
1295
1317
  contextWindowManager: {
1318
+ updateConfig: () => {},
1296
1319
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1297
1320
  maybeCompact: async () => {
1298
1321
  compactionCalled = true;
@@ -1317,7 +1340,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1317
1340
  summaryModel: "mock-model",
1318
1341
  };
1319
1342
  },
1320
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1343
+ } as unknown as Conversation["contextWindowManager"],
1321
1344
  });
1322
1345
 
1323
1346
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => {
@@ -1360,14 +1383,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1360
1383
 
1361
1384
  // Budget = 200_000 * 0.95 = 190_000
1362
1385
  // Mid-loop threshold = 190_000 * 0.85 = 161_500
1363
- let estimateCallCount = 0;
1364
- mockEstimateTokens = () => {
1365
- estimateCallCount++;
1366
- // Preflight: below budget
1367
- if (estimateCallCount === 1) return 100_000;
1368
- // Every checkpoint call: above threshold — always triggers yield
1369
- return 170_000;
1370
- };
1386
+ // Every estimate is above the threshold, so the first-call gate compacts
1387
+ // before the first provider call and every checkpoint trips the yield.
1388
+ mockEstimateTokens = 170_000;
1371
1389
 
1372
1390
  // The convergence reducer reduces tokens enough for the rerun to recover.
1373
1391
  let convergenceReducerCalled = false;
@@ -1410,6 +1428,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1410
1428
  ],
1411
1429
  toolExecutor: async () => ({ content: "output", isError: false }),
1412
1430
  contextWindowManager: {
1431
+ updateConfig: () => {},
1413
1432
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1414
1433
  maybeCompact: async () => {
1415
1434
  compactionCallCount++;
@@ -1441,7 +1460,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1441
1460
  summaryModel: "mock-model",
1442
1461
  };
1443
1462
  },
1444
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1463
+ } as unknown as Conversation["contextWindowManager"],
1445
1464
  });
1446
1465
 
1447
1466
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
@@ -1480,16 +1499,11 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1480
1499
 
1481
1500
  // Budget = 200_000 * 0.95 = 190_000
1482
1501
  // Mid-loop threshold = 190_000 * 0.85 = 161_500
1483
- let estimateCallCount = 0;
1484
- mockEstimateTokens = () => {
1485
- estimateCallCount++;
1486
- // Preflight: below budget.
1487
- if (estimateCallCount === 1) return 100_000;
1488
- // Every checkpoint estimate: above threshold — always trips the
1489
- // yield. Simulates a long turn where each tool call's result
1490
- // inflates the context past 85% even after a successful compaction.
1491
- return 170_000;
1492
- };
1502
+ // Every estimate is above the threshold: the first-call gate compacts
1503
+ // before the first provider call, and each subsequent checkpoint trips
1504
+ // the yield even after a successful compaction (each tool result inflates
1505
+ // the context back past 85%).
1506
+ mockEstimateTokens = 170_000;
1493
1507
 
1494
1508
  // A single tool round reaches one checkpoint; the in-loop budget gate
1495
1509
  // trips there and compaction runs in place. The loop continues the run
@@ -1518,6 +1532,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1518
1532
  ],
1519
1533
  toolExecutor: async () => ({ content: "output", isError: false }),
1520
1534
  contextWindowManager: {
1535
+ updateConfig: () => {},
1521
1536
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1522
1537
  maybeCompact: async () => {
1523
1538
  compactionCallCount++;
@@ -1542,7 +1557,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1542
1557
  summaryModel: "mock-model",
1543
1558
  };
1544
1559
  },
1545
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1560
+ } as unknown as Conversation["contextWindowManager"],
1546
1561
  });
1547
1562
 
1548
1563
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
@@ -1633,6 +1648,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1633
1648
  ],
1634
1649
  toolExecutor: async () => ({ content: "output", isError: false }),
1635
1650
  contextWindowManager: {
1651
+ updateConfig: () => {},
1636
1652
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1637
1653
  // Under the new architecture (Compaction Re-homing Arc, Bullet 1)
1638
1654
  // the retry budget lives inside `ContextWindowManager._maybeCompact`,
@@ -1660,7 +1676,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1660
1676
  summaryModel: "mock-model",
1661
1677
  exhausted: true,
1662
1678
  }),
1663
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1679
+ } as unknown as Conversation["contextWindowManager"],
1664
1680
  });
1665
1681
 
1666
1682
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
@@ -1680,124 +1696,6 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1680
1696
  );
1681
1697
  });
1682
1698
 
1683
- // ── Test 8 ────────────────────────────────────────────────────────
1684
- // BUG: The preflight overflow reducer's budget check uses
1685
- // step.estimatedTokens (computed on bare ctx.messages) without
1686
- // accounting for tokens added by applyRuntimeInjections(). This
1687
- // causes the reducer to stop early when the bare estimate is under
1688
- // budget, even though post-injection tokens exceed it — leading to
1689
- // a wasted provider round-trip that gets rejected.
1690
- //
1691
- // After fix: the budget check re-estimates on runMessages (with
1692
- // injections) so the reducer continues to the next tier.
1693
- test("preflight reducer continues when post-injection tokens exceed budget", async () => {
1694
- const events: ServerMessage[] = [];
1695
-
1696
- // Injections add an extra message, bumping the token count.
1697
- const injectionMessage: Message = {
1698
- role: "user" as const,
1699
- content: [
1700
- {
1701
- type: "text" as const,
1702
- text: "injected context " + "x".repeat(500),
1703
- },
1704
- ],
1705
- };
1706
- mockApplyRuntimeInjections = (msgs) => [...msgs, injectionMessage];
1707
-
1708
- // Budget = 200_000 * 0.95 = 190_000
1709
- // The estimator returns different values based on whether the
1710
- // injection message is present:
1711
- // - bare history (no injection msg) → 195_000 (triggers preflight)
1712
- // - after tier 1 bare → 185_000 (under budget, would stop early without fix)
1713
- // - after tier 1 with injection → 195_000 (still over budget)
1714
- // - after tier 2 bare → 170_000
1715
- // - after tier 2 with injection → 175_000 (under budget, reducer stops)
1716
- let reducerCallCount = 0;
1717
- mockEstimateTokens = (msgs?: Message[]) => {
1718
- const hasInjection = msgs?.some(
1719
- (m) =>
1720
- m.role === "user" &&
1721
- Array.isArray(m.content) &&
1722
- m.content.some(
1723
- (b: { type: string; text?: string }) =>
1724
- b.type === "text" &&
1725
- typeof b.text === "string" &&
1726
- b.text.startsWith("injected context"),
1727
- ),
1728
- );
1729
- if (reducerCallCount === 0) {
1730
- // Before any reduction: preflight check on runMessages (with injection)
1731
- return 195_000;
1732
- }
1733
- if (reducerCallCount === 1) {
1734
- // After tier 1
1735
- return hasInjection ? 195_000 : 185_000;
1736
- }
1737
- // After tier 2
1738
- return hasInjection ? 175_000 : 170_000;
1739
- };
1740
-
1741
- mockReducerStepFn = (msgs: Message[]) => {
1742
- reducerCallCount++;
1743
- const tier =
1744
- reducerCallCount === 1 ? "forced_compaction" : "tool_result_truncation";
1745
- return {
1746
- messages: msgs,
1747
- tier,
1748
- state: {
1749
- appliedTiers:
1750
- reducerCallCount === 1
1751
- ? ["forced_compaction"]
1752
- : ["forced_compaction", "tool_result_truncation"],
1753
- injectionMode: "full" as const,
1754
- exhausted: reducerCallCount >= 2,
1755
- },
1756
- // Bare-history estimate (what the reducer sees on ctx.messages)
1757
- estimatedTokens: reducerCallCount === 1 ? 185_000 : 170_000,
1758
- compactionResult: {
1759
- compacted: true,
1760
- messages: msgs,
1761
- compactedPersistedMessages: 5,
1762
- summaryText: "Summary",
1763
- previousEstimatedInputTokens: 195_000,
1764
- estimatedInputTokens: reducerCallCount === 1 ? 185_000 : 170_000,
1765
- maxInputTokens: 200_000,
1766
- thresholdTokens: 160_000,
1767
- compactedMessages: 10,
1768
- summaryCalls: 1,
1769
- summaryInputTokens: 500,
1770
- summaryOutputTokens: 200,
1771
- summaryModel: "mock-model",
1772
- },
1773
- };
1774
- };
1775
-
1776
- // The preflight overflow reducer runs in the orchestrator before the loop,
1777
- // so a single successful provider turn is enough to drive the path.
1778
- const ctx = makeCtx({
1779
- providerResponses: [textResponse("done")],
1780
- contextWindowManager: {
1781
- shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1782
- maybeCompact: async () => ({ compacted: false }),
1783
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1784
- });
1785
-
1786
- await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
1787
-
1788
- // The reducer must be called twice — the first tier's bare estimate
1789
- // (185k) is under budget (190k), but post-injection tokens (195k)
1790
- // still exceed it. Without the fix, the reducer would stop after
1791
- // tier 1 and the provider call would likely fail.
1792
- expect(reducerCallCount).toBe(2);
1793
-
1794
- // Should succeed without errors
1795
- const conversationError = events.find(
1796
- (e) => e.type === "conversation_error",
1797
- );
1798
- expect(conversationError).toBeUndefined();
1799
- });
1800
-
1801
1699
  // ── Test 9 ────────────────────────────────────────────────────────
1802
1700
  // When the `auto_compress_latest_turn` rerun (the last layer of the
1803
1701
  // overflow-recovery ladder) still yields at the mid-loop checkpoint,
@@ -1854,14 +1752,15 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1854
1752
  toolUseResponse("tu-1", "bash", { command: "ls" }),
1855
1753
  ]);
1856
1754
 
1857
- // `maybeCompact` is invoked through three distinct call sites:
1858
- // 1. Start-of-turn compaction (no `force` option) return a no-op
1859
- // so the start-of-turn pass doesn't perturb state. The mock's
1860
- // `shouldCompact` already returns `needed: false`, but the
1861
- // orchestrator still invokes the compaction pipeline.
1862
- // 2. Mid-loop after the initial agent-loop yield (`force: true`) —
1863
- // must signal `exhausted: true` so the daemon escalates to the
1864
- // convergence reducer instead of looping forever.
1755
+ // Every forced `maybeCompact` is invoked through three distinct call
1756
+ // sites, all owned by the loop's pre-call budget gate / recovery ladder:
1757
+ // 1. First-call (turn-start) gate (`force: true`) the loop now owns
1758
+ // the turn-start compaction. It succeeds, but the mocked estimate
1759
+ // stays above the mid-loop threshold, so the turn proceeds and the
1760
+ // mid-loop gate still trips on the next iteration.
1761
+ // 2. Mid-loop after the first tool turn (`force: true`) must signal
1762
+ // `exhausted: true` so the daemon escalates to the convergence
1763
+ // reducer instead of looping forever.
1865
1764
  // 3. auto_compress_latest_turn emergency compaction (`force: true`,
1866
1765
  // `minKeepRecentUserTurns: 0`) — succeeds and drops tokens below
1867
1766
  // threshold; the subsequent rerun yields again and is classified
@@ -1881,22 +1780,47 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1881
1780
  ],
1882
1781
  toolExecutor: async () => ({ content: "output", isError: false }),
1883
1782
  contextWindowManager: {
1783
+ updateConfig: () => {},
1884
1784
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1885
1785
  maybeCompact: async (
1886
1786
  _msgs: Message[],
1887
1787
  _signal: AbortSignal,
1888
1788
  opts?: { force?: boolean },
1889
1789
  ) => {
1890
- // Start-of-turn calls pass no `force` option; route them to a
1891
- // no-op so only the mid-loop and emergency paths drive the test.
1790
+ // Only forced compactions drive this test; any non-forced probe is
1791
+ // a no-op.
1892
1792
  if (!opts?.force) {
1893
1793
  return { compacted: false };
1894
1794
  }
1895
1795
  forcedMaybeCompactCallCount++;
1896
1796
  if (forcedMaybeCompactCallCount === 1) {
1897
- // Mid-loop call under the new architecture (Compaction
1898
- // Re-homing Arc, Bullet 1) the manager owns its own retry
1899
- // budget; signal exhaustion to escalate to convergence.
1797
+ // First-call (turn-start) gate: succeeds, but the mocked estimate
1798
+ // stays above the threshold so the turn proceeds to the call and
1799
+ // the mid-loop gate still trips next iteration.
1800
+ return {
1801
+ compacted: true,
1802
+ messages: [
1803
+ {
1804
+ role: "user" as const,
1805
+ content: [{ type: "text", text: "turn-start compacted" }],
1806
+ },
1807
+ ] as Message[],
1808
+ compactedPersistedMessages: 5,
1809
+ summaryText: "Turn-start summary",
1810
+ previousEstimatedInputTokens: 170_000,
1811
+ estimatedInputTokens: 165_000,
1812
+ maxInputTokens: 200_000,
1813
+ thresholdTokens: 160_000,
1814
+ compactedMessages: 10,
1815
+ summaryCalls: 1,
1816
+ summaryInputTokens: 500,
1817
+ summaryOutputTokens: 200,
1818
+ summaryModel: "mock-model",
1819
+ };
1820
+ }
1821
+ if (forcedMaybeCompactCallCount === 2) {
1822
+ // Mid-loop call — the manager owns its own retry budget; signal
1823
+ // exhaustion to escalate to convergence.
1900
1824
  return {
1901
1825
  compacted: true,
1902
1826
  messages: [
@@ -1941,7 +1865,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1941
1865
  summaryModel: "mock-model",
1942
1866
  };
1943
1867
  },
1944
- } as unknown as AgentLoopConversationContext["contextWindowManager"],
1868
+ } as unknown as Conversation["contextWindowManager"],
1945
1869
  });
1946
1870
 
1947
1871
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));