@vellumai/assistant 0.8.6 → 0.8.7-dev.202606052118.34cd356

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 (1078) hide show
  1. package/AGENTS.md +4 -4
  2. package/Dockerfile +21 -4
  3. package/bun.lock +13 -4
  4. package/docker-entrypoint.sh +12 -8
  5. package/docker-init-apt-root.sh +3 -1
  6. package/docker-kata-apt-env.sh +3 -1
  7. package/docker-kata-runtime-family.sh +12 -0
  8. package/docs/architecture/memory.md +1 -1
  9. package/docs/plugins.md +110 -83
  10. package/examples/plugins/echo/README.md +13 -12
  11. package/examples/plugins/echo/register.ts +0 -54
  12. package/knip.json +1 -0
  13. package/node_modules/@vellumai/environments/bun.lock +24 -0
  14. package/node_modules/@vellumai/environments/package.json +18 -0
  15. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
  16. package/node_modules/@vellumai/environments/src/index.ts +11 -0
  17. package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
  18. package/node_modules/@vellumai/environments/src/types.ts +70 -0
  19. package/node_modules/@vellumai/environments/tsconfig.json +20 -0
  20. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
  21. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
  22. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  23. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +13 -8
  24. package/openapi.yaml +6964 -539
  25. package/package.json +8 -4
  26. package/scripts/generate-openapi.ts +88 -54
  27. package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
  28. package/src/__tests__/agent-loop-exit-reason.test.ts +188 -45
  29. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
  30. package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
  31. package/src/__tests__/agent-loop-provider-error-recording.test.ts +7 -5
  32. package/src/__tests__/agent-loop-thinking.test.ts +17 -12
  33. package/src/__tests__/agent-loop.test.ts +238 -422
  34. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +6 -2
  35. package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
  36. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  37. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  38. package/src/__tests__/anthropic-provider.test.ts +296 -57
  39. package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
  40. package/src/__tests__/app-control-flow.test.ts +6 -1
  41. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  42. package/src/__tests__/approval-cascade.test.ts +4 -11
  43. package/src/__tests__/approval-routes-http.test.ts +8 -3
  44. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  45. package/src/__tests__/assistant-event.test.ts +15 -0
  46. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  47. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  48. package/src/__tests__/assistant-stream-state.test.ts +645 -0
  49. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  50. package/src/__tests__/avatar-e2e.test.ts +7 -37
  51. package/src/__tests__/avatar-generator.test.ts +12 -42
  52. package/src/__tests__/avatar-identity-sync.test.ts +28 -3
  53. package/src/__tests__/background-shell-bash.test.ts +3 -7
  54. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  55. package/src/__tests__/btw-routes.test.ts +69 -15
  56. package/src/__tests__/build-persisted-content.test.ts +184 -0
  57. package/src/__tests__/call-pointer-messages.test.ts +5 -3
  58. package/src/__tests__/call-site-routing-provider.test.ts +22 -40
  59. package/src/__tests__/catalog-files.test.ts +1 -0
  60. package/src/__tests__/channel-approval-routes.test.ts +49 -21
  61. package/src/__tests__/channel-approvals.test.ts +4 -2
  62. package/src/__tests__/channel-invite-transport.test.ts +1 -5
  63. package/src/__tests__/channel-readiness-routes.test.ts +0 -4
  64. package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
  65. package/src/__tests__/channel-retry-sweep.test.ts +71 -79
  66. package/src/__tests__/clawhub-files.test.ts +1 -0
  67. package/src/__tests__/compaction-circuit.test.ts +258 -0
  68. package/src/__tests__/compaction-direct.test.ts +132 -0
  69. package/src/__tests__/compaction-events.test.ts +5 -17
  70. package/src/__tests__/compaction-trail-store.test.ts +1 -79
  71. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  72. package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
  73. package/src/__tests__/computer-use-tools.test.ts +2 -2
  74. package/src/__tests__/config-watcher.test.ts +28 -0
  75. package/src/__tests__/context-search-agent-runner.test.ts +6 -3
  76. package/src/__tests__/context-token-estimator.test.ts +34 -0
  77. package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
  78. package/src/__tests__/conversation-abort-tool-results.test.ts +70 -25
  79. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +9 -7
  80. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -34
  81. package/src/__tests__/conversation-agent-loop-overflow.test.ts +476 -963
  82. package/src/__tests__/conversation-agent-loop.test.ts +823 -1321
  83. package/src/__tests__/conversation-analysis-routes.test.ts +7 -3
  84. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  85. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  86. package/src/__tests__/conversation-clear-safety.test.ts +20 -10
  87. package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
  88. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  89. package/src/__tests__/conversation-disk-view.test.ts +10 -17
  90. package/src/__tests__/conversation-fork-crud.test.ts +86 -172
  91. package/src/__tests__/conversation-fork-route.test.ts +16 -14
  92. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  93. package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
  94. package/src/__tests__/conversation-lifecycle.test.ts +3 -2
  95. package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
  96. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
  97. package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
  98. package/src/__tests__/conversation-pairing.test.ts +10 -7
  99. package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
  100. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -0
  101. package/src/__tests__/conversation-process-callsite.test.ts +27 -30
  102. package/src/__tests__/conversation-provider-retry-repair.test.ts +80 -51
  103. package/src/__tests__/conversation-queue.test.ts +272 -164
  104. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -2
  105. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  106. package/src/__tests__/conversation-routes-slash-commands.test.ts +8 -7
  107. package/src/__tests__/conversation-runtime-assembly.test.ts +317 -313
  108. package/src/__tests__/conversation-runtime-workspace.test.ts +114 -36
  109. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  110. package/src/__tests__/conversation-slash-queue.test.ts +42 -31
  111. package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
  112. package/src/__tests__/conversation-speed-override.test.ts +8 -22
  113. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  114. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +90 -15
  115. package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
  116. package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
  117. package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
  118. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  119. package/src/__tests__/conversation-title-service.test.ts +135 -2
  120. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
  121. package/src/__tests__/conversation-unread-route.test.ts +14 -2
  122. package/src/__tests__/conversation-usage.test.ts +0 -2
  123. package/src/__tests__/conversation-wipe.test.ts +1 -1
  124. package/src/__tests__/conversation-workspace-cache-state.test.ts +20 -17
  125. package/src/__tests__/conversation-workspace-injection.test.ts +114 -23
  126. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +34 -13
  127. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  128. package/src/__tests__/credential-execution-tools.test.ts +1 -2
  129. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  130. package/src/__tests__/cross-provider-web-search.test.ts +220 -3
  131. package/src/__tests__/cu-unified-flow.test.ts +26 -1
  132. package/src/__tests__/db-acp-history.test.ts +101 -0
  133. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -0
  134. package/src/__tests__/disk-pressure-guard.test.ts +66 -0
  135. package/src/__tests__/disk-pressure-routes.test.ts +9 -2
  136. package/src/__tests__/dm-persistence.test.ts +12 -3
  137. package/src/__tests__/dynamic-page-surface.test.ts +99 -0
  138. package/src/__tests__/edit-propagation.test.ts +1 -2
  139. package/src/__tests__/empty-response-hook.test.ts +304 -0
  140. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  141. package/src/__tests__/file-write-tool.test.ts +63 -0
  142. package/src/__tests__/filing-service.test.ts +2 -2
  143. package/src/__tests__/first-greeting.test.ts +55 -14
  144. package/src/__tests__/gemini-image-service.test.ts +13 -0
  145. package/src/__tests__/gemini-inline-media.test.ts +78 -0
  146. package/src/__tests__/gemini-provider.test.ts +351 -28
  147. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  148. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  149. package/src/__tests__/guardian-routing-state.test.ts +60 -71
  150. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +10 -8
  151. package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
  152. package/src/__tests__/heartbeat-service.test.ts +3 -1
  153. package/src/__tests__/helpers/mock-provider.ts +110 -0
  154. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  155. package/src/__tests__/history-repair-hook.test.ts +162 -0
  156. package/src/__tests__/history-repair-observability.test.ts +1 -1
  157. package/src/__tests__/history-repair.test.ts +2 -1
  158. package/src/__tests__/host-app-control-proxy.test.ts +2 -0
  159. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  160. package/src/__tests__/host-cu-proxy.test.ts +2 -0
  161. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  162. package/src/__tests__/host-file-edit-tool.test.ts +4 -2
  163. package/src/__tests__/host-file-proxy.test.ts +31 -0
  164. package/src/__tests__/host-file-read-tool.test.ts +4 -2
  165. package/src/__tests__/host-file-write-tool.test.ts +9 -3
  166. package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
  167. package/src/__tests__/host-shell-tool.test.ts +9 -4
  168. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  169. package/src/__tests__/identity-intro-cache.test.ts +47 -114
  170. package/src/__tests__/identity-routes.test.ts +248 -7
  171. package/src/__tests__/inbound-slack-persistence.test.ts +12 -3
  172. package/src/__tests__/injector-background-turn.test.ts +3 -9
  173. package/src/__tests__/injector-chain.test.ts +139 -275
  174. package/src/__tests__/injector-disk-pressure.test.ts +75 -41
  175. package/src/__tests__/injector-document-comments.test.ts +3 -3
  176. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  177. package/src/__tests__/injector-v3-suppression.test.ts +214 -0
  178. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  179. package/src/__tests__/list-messages-attachments.test.ts +7 -8
  180. package/src/__tests__/list-messages-hidden-metadata.test.ts +55 -15
  181. package/src/__tests__/list-messages-page-latest.test.ts +60 -1
  182. package/src/__tests__/list-messages-tool-merge.test.ts +56 -6
  183. package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
  184. package/src/__tests__/llm-resolver.test.ts +23 -47
  185. package/src/__tests__/llm-usage-store.test.ts +268 -1
  186. package/src/__tests__/log-export-routes.test.ts +59 -0
  187. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
  188. package/src/__tests__/mcp-auth-routes.test.ts +15 -10
  189. package/src/__tests__/mcp-health-check.test.ts +18 -13
  190. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  191. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  192. package/src/__tests__/messaging-send-tool.test.ts +8 -4
  193. package/src/__tests__/migration-export-http.test.ts +12 -12
  194. package/src/__tests__/migration-import-commit-http.test.ts +8 -8
  195. package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
  196. package/src/__tests__/migration-validate-http.test.ts +3 -3
  197. package/src/__tests__/native-web-search.test.ts +205 -20
  198. package/src/__tests__/notification-decision-identity.test.ts +9 -18
  199. package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
  200. package/src/__tests__/oauth-commands-routes.test.ts +1 -1
  201. package/src/__tests__/onboarding-template-contract.test.ts +12 -0
  202. package/src/__tests__/openai-image-service.test.ts +17 -0
  203. package/src/__tests__/openai-provider.test.ts +97 -71
  204. package/src/__tests__/openai-responses-provider.test.ts +21 -77
  205. package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
  206. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -286
  207. package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
  208. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  209. package/src/__tests__/persistence-secret-redaction.test.ts +3 -1
  210. package/src/__tests__/pipeline-runner.test.ts +31 -43
  211. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  212. package/src/__tests__/plugin-bootstrap.test.ts +62 -51
  213. package/src/__tests__/plugin-registry.test.ts +0 -27
  214. package/src/__tests__/plugin-route-contribution.test.ts +6 -16
  215. package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
  216. package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
  217. package/src/__tests__/plugin-types.test.ts +8 -173
  218. package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
  219. package/src/__tests__/process-message-background-slack.test.ts +17 -16
  220. package/src/__tests__/process-message-display-content.test.ts +36 -44
  221. package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
  222. package/src/__tests__/provider-error-scenarios.test.ts +7 -6
  223. package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
  224. package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
  225. package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
  226. package/src/__tests__/provider-usage-tracking.test.ts +0 -6
  227. package/src/__tests__/ratelimit.test.ts +9 -4
  228. package/src/__tests__/reaction-persistence.test.ts +1 -1
  229. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  230. package/src/__tests__/relay-server.test.ts +20 -13
  231. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  232. package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
  233. package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
  234. package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
  235. package/src/__tests__/runtime-events-sse-reconnect.test.ts +390 -0
  236. package/src/__tests__/schedule-routes.test.ts +683 -12
  237. package/src/__tests__/schedule-store.test.ts +108 -0
  238. package/src/__tests__/schedule-tools.test.ts +160 -0
  239. package/src/__tests__/secret-ingress-http.test.ts +2 -2
  240. package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
  241. package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
  242. package/src/__tests__/secret-response-routing.test.ts +13 -11
  243. package/src/__tests__/send-endpoint-busy.test.ts +6 -2
  244. package/src/__tests__/server-history-render.test.ts +314 -1
  245. package/src/__tests__/shell-observability.test.ts +249 -0
  246. package/src/__tests__/skill-feature-flags-integration.test.ts +44 -11
  247. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  248. package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
  249. package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
  250. package/src/__tests__/skillssh-files.test.ts +1 -0
  251. package/src/__tests__/starter-task-flow.test.ts +6 -6
  252. package/src/__tests__/strip-memory-injections.test.ts +102 -14
  253. package/src/__tests__/subagent-call-site-routing.test.ts +3 -3
  254. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  255. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  256. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  257. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  258. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  259. package/src/__tests__/suggestion-routes.test.ts +3 -3
  260. package/src/__tests__/sync-message-contract.test.ts +19 -16
  261. package/src/__tests__/system-prompt.test.ts +74 -0
  262. package/src/__tests__/task-scheduler.test.ts +162 -1
  263. package/src/__tests__/terminal-tools.test.ts +9 -25
  264. package/src/__tests__/thread-backfill.test.ts +4 -9
  265. package/src/__tests__/title-generate-hook.test.ts +319 -0
  266. package/src/__tests__/tool-error-hook.test.ts +278 -0
  267. package/src/__tests__/tool-preview-lifecycle.test.ts +481 -16
  268. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  269. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  270. package/src/__tests__/tool-result-truncation.test.ts +1 -1
  271. package/src/__tests__/tools-audio-read.test.ts +113 -0
  272. package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
  273. package/src/__tests__/turn-events-store.test.ts +11 -7
  274. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  275. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  276. package/src/__tests__/usage-routes.test.ts +285 -1
  277. package/src/__tests__/user-plugin-loader.test.ts +2 -2
  278. package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
  279. package/src/__tests__/voice-session-bridge.test.ts +19 -10
  280. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  281. package/src/acp/__tests__/agent-process.test.ts +161 -0
  282. package/src/acp/__tests__/client-handler.test.ts +40 -0
  283. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  284. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  285. package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
  286. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  287. package/src/acp/__tests__/session-manager-resume.test.ts +695 -0
  288. package/src/acp/agent-process.ts +61 -1
  289. package/src/acp/auto-install.test.ts +125 -0
  290. package/src/acp/auto-install.ts +174 -0
  291. package/src/acp/client-handler.ts +31 -0
  292. package/src/acp/feature-gate.test.ts +48 -0
  293. package/src/acp/feature-gate.ts +34 -0
  294. package/src/acp/prepare-agent-env.ts +52 -11
  295. package/src/acp/resolve-agent.test.ts +147 -6
  296. package/src/acp/resolve-agent.ts +81 -7
  297. package/src/acp/resume-hint.ts +22 -0
  298. package/src/acp/session-manager.ts +487 -71
  299. package/src/agent/compaction-circuit.ts +98 -0
  300. package/src/agent/loop.ts +651 -450
  301. package/src/api/README.md +19 -17
  302. package/src/api/constants/tool-execution.ts +21 -0
  303. package/src/api/events/assistant-activity-state.ts +75 -0
  304. package/src/api/events/assistant-outbound-attachment.ts +25 -27
  305. package/src/api/events/assistant-text-delta.ts +6 -8
  306. package/src/api/events/assistant-thinking-delta.ts +33 -0
  307. package/src/api/events/assistant-turn-start.ts +5 -7
  308. package/src/api/events/avatar-updated.ts +24 -0
  309. package/src/api/events/compaction-circuit-closed.ts +26 -0
  310. package/src/api/events/compaction-circuit-open.ts +28 -0
  311. package/src/api/events/confirmation-request.ts +114 -0
  312. package/src/api/events/contact-request.ts +33 -0
  313. package/src/api/events/conversation-error.ts +77 -0
  314. package/src/api/events/conversation-list-invalidated.ts +38 -0
  315. package/src/api/events/conversation-title-updated.ts +24 -0
  316. package/src/api/events/disk-pressure-status-changed.ts +61 -0
  317. package/src/api/events/document-comment-created.ts +24 -28
  318. package/src/api/events/document-comment-deleted.ts +6 -8
  319. package/src/api/events/document-comment-reopened.ts +6 -8
  320. package/src/api/events/document-comment-resolved.ts +8 -10
  321. package/src/api/events/document-editor-update.ts +27 -0
  322. package/src/api/events/error.ts +32 -0
  323. package/src/api/events/generation-cancelled.ts +4 -6
  324. package/src/api/events/generation-handoff.ts +13 -15
  325. package/src/api/events/home-feed-updated.ts +26 -0
  326. package/src/api/events/identity-changed.ts +32 -0
  327. package/src/api/events/interaction-resolved.ts +50 -0
  328. package/src/api/events/message-complete.ts +10 -12
  329. package/src/api/events/message-dequeued.ts +21 -0
  330. package/src/api/events/message-queued-deleted.ts +23 -0
  331. package/src/api/events/message-queued.ts +22 -0
  332. package/src/api/events/message-request-complete.ts +29 -0
  333. package/src/api/events/navigate-settings.ts +20 -0
  334. package/src/api/events/notification-intent.ts +33 -0
  335. package/src/api/events/open-url.ts +6 -8
  336. package/src/api/events/question-request.ts +67 -0
  337. package/src/api/events/relationship-state-updated.ts +4 -6
  338. package/src/api/events/secret-request.ts +42 -0
  339. package/src/api/events/subagent-event.ts +79 -0
  340. package/src/api/events/subagent-spawned.ts +40 -0
  341. package/src/api/events/subagent-status-changed.ts +65 -0
  342. package/src/api/events/sync-changed.ts +29 -0
  343. package/src/api/events/tool-output-chunk.ts +45 -0
  344. package/src/api/events/tool-result.ts +129 -0
  345. package/src/api/events/tool-use-preview-start.ts +32 -0
  346. package/src/api/events/tool-use-start.ts +8 -10
  347. package/src/api/events/trace-event.ts +69 -0
  348. package/src/api/events/turn-profile-auto-routed.ts +28 -0
  349. package/src/api/events/ui-surface-complete.ts +30 -0
  350. package/src/api/events/ui-surface-dismiss.ts +22 -0
  351. package/src/api/events/ui-surface-show.ts +67 -0
  352. package/src/api/events/ui-surface-update.ts +26 -0
  353. package/src/api/events/usage-update.ts +34 -0
  354. package/src/api/events/user-message-echo.ts +35 -0
  355. package/src/api/index.ts +389 -0
  356. package/src/api/requests/dictation.ts +45 -0
  357. package/src/api/responses/conversation-message.ts +374 -0
  358. package/src/api/responses/disk-pressure-status.ts +26 -0
  359. package/src/api/responses/home.ts +217 -0
  360. package/src/api/responses/llm-context-response.ts +2 -0
  361. package/src/api/responses/memory-v3-selection-log.ts +50 -0
  362. package/src/api/responses/subagent-detail.ts +48 -0
  363. package/src/approvals/guardian-decision-primitive.ts +7 -15
  364. package/src/approvals/guardian-request-resolvers.ts +7 -10
  365. package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
  366. package/src/avatar/__tests__/avatar-store.test.ts +198 -0
  367. package/src/avatar/avatar-manifest.ts +195 -0
  368. package/src/avatar/avatar-store.ts +113 -0
  369. package/src/avatar/traits-png-sync.ts +8 -2
  370. package/src/background-wake/next-wake.test.ts +31 -1
  371. package/src/background-wake/next-wake.ts +5 -1
  372. package/src/calls/call-conversation-messages.ts +6 -4
  373. package/src/calls/guardian-action-sweep.ts +6 -4
  374. package/src/calls/relay-server.ts +12 -8
  375. package/src/calls/voice-session-bridge.ts +13 -27
  376. package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
  377. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  378. package/src/cli/commands/avatar.ts +17 -11
  379. package/src/cli/commands/conversations.ts +15 -1
  380. package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
  381. package/src/cli/commands/db/__tests__/status.test.ts +253 -0
  382. package/src/cli/commands/db/format.ts +48 -0
  383. package/src/cli/commands/db/index.ts +29 -0
  384. package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
  385. package/src/cli/commands/db/repair-step-integrity.ts +146 -0
  386. package/src/cli/commands/db/repair-steps.ts +164 -0
  387. package/src/cli/commands/db/repair.ts +141 -0
  388. package/src/cli/commands/db/status.ts +366 -0
  389. package/src/cli/commands/memory-v3.ts +159 -445
  390. package/src/cli/commands/notifications.ts +112 -60
  391. package/src/cli/lib/cli-colors.ts +24 -6
  392. package/src/cli/program.ts +4 -5
  393. package/src/config/__tests__/feature-flag-registry-guard.test.ts +4 -4
  394. package/src/config/acp-defaults.test.ts +10 -0
  395. package/src/config/acp-defaults.ts +6 -0
  396. package/src/config/assistant-feature-flags.ts +24 -13
  397. package/src/config/bundled-skills/acp/SKILL.md +64 -30
  398. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  399. package/src/config/bundled-skills/app-builder/SKILL.md +224 -387
  400. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  401. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  402. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  403. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  404. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  405. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  406. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  407. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  408. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  409. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  410. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  411. package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
  412. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  413. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
  414. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  415. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  416. package/src/config/bundled-tool-registry.ts +2 -0
  417. package/src/config/call-site-defaults.ts +2 -7
  418. package/src/config/feature-flag-cache.ts +3 -3
  419. package/src/config/feature-flag-registry.json +68 -12
  420. package/src/config/schemas/__tests__/memory-v2.test.ts +2 -226
  421. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  422. package/src/config/schemas/call-site-catalog.ts +8 -15
  423. package/src/config/schemas/heartbeat.ts +9 -0
  424. package/src/config/schemas/llm.ts +3 -3
  425. package/src/config/schemas/memory-lifecycle.ts +24 -0
  426. package/src/config/schemas/memory-v2.ts +8 -253
  427. package/src/config/schemas/memory-v3.ts +47 -0
  428. package/src/config/schemas/memory.ts +6 -1
  429. package/src/config/schemas/platform.ts +8 -0
  430. package/src/config/schemas/timeouts.ts +3 -1
  431. package/src/config/seed-inference-profiles.ts +2 -2
  432. package/src/config/skills.ts +13 -0
  433. package/src/context/compactor.ts +55 -32
  434. package/src/context/strip-injections.ts +128 -0
  435. package/src/context/token-estimator.ts +42 -0
  436. package/src/context/tool-result-truncation.ts +1 -66
  437. package/src/context/window-manager.ts +141 -26
  438. package/src/credential-execution/executable-discovery.ts +16 -0
  439. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  440. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
  441. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  442. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  443. package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
  444. package/src/daemon/approval-generators.ts +4 -4
  445. package/src/daemon/assistant-attachments.ts +1 -1
  446. package/src/daemon/config-watcher.ts +7 -1
  447. package/src/daemon/context-overflow-reducer.ts +0 -1
  448. package/src/daemon/conversation-agent-loop-handlers.ts +793 -215
  449. package/src/daemon/conversation-agent-loop.ts +487 -1478
  450. package/src/daemon/conversation-error.ts +7 -7
  451. package/src/daemon/conversation-history.ts +27 -10
  452. package/src/daemon/conversation-launch.ts +4 -8
  453. package/src/daemon/conversation-lifecycle.ts +13 -42
  454. package/src/daemon/conversation-messaging.ts +8 -9
  455. package/src/daemon/conversation-notifiers.ts +7 -5
  456. package/src/daemon/conversation-process.ts +109 -93
  457. package/src/daemon/conversation-registry.ts +159 -0
  458. package/src/daemon/conversation-runtime-assembly.ts +209 -382
  459. package/src/daemon/conversation-slash.ts +6 -25
  460. package/src/daemon/conversation-store.ts +15 -95
  461. package/src/daemon/conversation-surfaces.ts +277 -73
  462. package/src/daemon/conversation-tool-setup.ts +5 -29
  463. package/src/daemon/conversation-workspace.ts +17 -0
  464. package/src/daemon/conversation.ts +123 -146
  465. package/src/daemon/daemon-skill-host.ts +2 -6
  466. package/src/daemon/disk-pressure-guard.ts +35 -29
  467. package/src/daemon/external-plugins-bootstrap.ts +53 -32
  468. package/src/daemon/first-greeting.ts +26 -4
  469. package/src/daemon/guardian-action-generators.ts +2 -2
  470. package/src/daemon/handlers/config-a2a.ts +51 -36
  471. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  472. package/src/daemon/handlers/config-telegram.ts +16 -2
  473. package/src/daemon/handlers/conversations.ts +9 -23
  474. package/src/daemon/handlers/shared.ts +158 -82
  475. package/src/daemon/handlers/skills.ts +53 -20
  476. package/src/daemon/host-app-control-proxy.ts +54 -1
  477. package/src/daemon/host-cu-proxy.ts +46 -22
  478. package/src/daemon/host-file-proxy.ts +25 -1
  479. package/src/daemon/host-proxy-preactivation.ts +25 -6
  480. package/src/daemon/lifecycle.ts +53 -55
  481. package/src/daemon/message-protocol.ts +2 -3
  482. package/src/daemon/message-provenance.ts +49 -0
  483. package/src/daemon/message-types/apps.ts +1 -29
  484. package/src/daemon/message-types/contacts.ts +3 -20
  485. package/src/daemon/message-types/conversations.ts +13 -111
  486. package/src/daemon/message-types/documents.ts +3 -9
  487. package/src/daemon/message-types/home.ts +4 -17
  488. package/src/daemon/message-types/integrations.ts +2 -6
  489. package/src/daemon/message-types/messages.ts +37 -400
  490. package/src/daemon/message-types/notifications.ts +2 -32
  491. package/src/daemon/message-types/settings.ts +3 -8
  492. package/src/daemon/message-types/skills.ts +4 -0
  493. package/src/daemon/message-types/surfaces.ts +138 -3
  494. package/src/daemon/message-types/sync.ts +12 -25
  495. package/src/daemon/message-types/workspace.ts +3 -11
  496. package/src/daemon/now-scratchpad.ts +21 -0
  497. package/src/daemon/orphan-reaper.test.ts +210 -0
  498. package/src/daemon/orphan-reaper.ts +240 -0
  499. package/src/daemon/overflow-reduction-loop.ts +230 -0
  500. package/src/daemon/persist-unsendable-image.ts +117 -0
  501. package/src/daemon/process-message.ts +50 -49
  502. package/src/daemon/server.ts +14 -0
  503. package/src/daemon/tool-side-effects.ts +10 -7
  504. package/src/daemon/trace-emitter.ts +6 -4
  505. package/src/daemon/trust-context.ts +32 -0
  506. package/src/daemon/wake-target-adapter.ts +14 -2
  507. package/src/heartbeat/__tests__/heartbeat-service.test.ts +6 -1
  508. package/src/heartbeat/heartbeat-run-store.ts +54 -1
  509. package/src/heartbeat/heartbeat-service.ts +42 -0
  510. package/src/home/feed-types.ts +36 -221
  511. package/src/home/home-greeting-cache.ts +24 -1
  512. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  513. package/src/ipc/__tests__/email-ipc.test.ts +0 -9
  514. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  515. package/src/ipc/gateway-client.test.ts +2 -2
  516. package/src/ipc/gateway-client.ts +3 -3
  517. package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
  518. package/src/ipc/routes/route-adapter.ts +45 -6
  519. package/src/ipc/skill-routes/__tests__/memory.test.ts +33 -9
  520. package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
  521. package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
  522. package/src/ipc/skill-routes/memory.ts +29 -14
  523. package/src/ipc/skill-routes/providers.ts +5 -6
  524. package/src/ipc/skill-routes/registries.ts +13 -61
  525. package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
  526. package/src/media/gemini-image-service.ts +15 -0
  527. package/src/media/openai-image-service.ts +14 -0
  528. package/src/media/types.ts +34 -0
  529. package/src/memory/__tests__/conversation-queries.test.ts +192 -8
  530. package/src/memory/__tests__/db-maintenance.test.ts +128 -0
  531. package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
  532. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  533. package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
  534. package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
  535. package/src/memory/auth-fallback-events-store.ts +94 -0
  536. package/src/memory/context-search/agent-runner.ts +2 -4
  537. package/src/memory/conversation-crud.ts +39 -8
  538. package/src/memory/conversation-queries.ts +78 -22
  539. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  540. package/src/memory/conversation-title-service.ts +65 -41
  541. package/src/memory/db-init.ts +14 -0
  542. package/src/memory/db-maintenance.ts +18 -2
  543. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  544. package/src/memory/graph/consolidation.ts +8 -11
  545. package/src/memory/graph/conversation-graph-memory.ts +106 -8
  546. package/src/memory/graph/extraction.ts +6 -9
  547. package/src/memory/graph/narrative.ts +2 -2
  548. package/src/memory/graph/pattern-scan.ts +2 -2
  549. package/src/memory/graph/retriever.ts +20 -26
  550. package/src/memory/graph/tools.ts +4 -4
  551. package/src/memory/job-handlers/conversation-starters.ts +45 -34
  552. package/src/memory/job-handlers/summarization.ts +1 -2
  553. package/src/memory/jobs-store.ts +36 -1
  554. package/src/memory/jobs-worker.ts +82 -43
  555. package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
  556. package/src/memory/llm-request-log-source-local.ts +0 -11
  557. package/src/memory/llm-request-log-source.ts +9 -25
  558. package/src/memory/llm-request-log-store.ts +0 -41
  559. package/src/memory/llm-usage-store.ts +234 -50
  560. package/src/memory/memory-marker.ts +17 -0
  561. package/src/memory/memory-retrospective-job.ts +6 -2
  562. package/src/memory/memory-v2-activation-log-store.ts +1 -83
  563. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  564. package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
  565. package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
  566. package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
  567. package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
  568. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  569. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  570. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  571. package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
  572. package/src/memory/migrations/index.ts +7 -0
  573. package/src/memory/pkb/autoinject.ts +61 -0
  574. package/src/memory/pkb/context.ts +50 -0
  575. package/src/memory/pkb/types.ts +14 -0
  576. package/src/memory/schedule-attribution-sql.ts +104 -0
  577. package/src/memory/schema/acp.ts +4 -0
  578. package/src/memory/schema/infrastructure.ts +27 -0
  579. package/src/memory/usage-grouped-buckets.ts +6 -1
  580. package/src/memory/v2/__tests__/consolidation-job.test.ts +125 -1
  581. package/src/memory/v2/__tests__/migration.test.ts +11 -3
  582. package/src/memory/v2/__tests__/page-index.test.ts +37 -1
  583. package/src/memory/v2/__tests__/router.test.ts +14 -4
  584. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
  585. package/src/memory/v2/backfill-jobs.ts +6 -0
  586. package/src/memory/v2/consolidation-job.ts +99 -10
  587. package/src/memory/v2/migration.ts +5 -3
  588. package/src/memory/v2/page-index.ts +11 -0
  589. package/src/memory/v2/router.ts +8 -11
  590. package/src/memory/v2/sweep-job.ts +8 -11
  591. package/src/memory/v2/types.ts +1 -0
  592. package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
  593. package/src/messaging/providers/slack/render-transcript.ts +2 -2
  594. package/src/messaging/style-analyzer.ts +8 -11
  595. package/src/notifications/conversation-pairing.ts +8 -13
  596. package/src/notifications/decision-engine.ts +16 -16
  597. package/src/notifications/home-feed-side-effect.ts +12 -1
  598. package/src/notifications/preference-extractor.ts +11 -14
  599. package/src/permissions/prompter.ts +46 -36
  600. package/src/permissions/question-prompter.test.ts +35 -26
  601. package/src/permissions/question-prompter.ts +6 -10
  602. package/src/plugin-api/constants.ts +4 -0
  603. package/src/plugin-api/index.ts +10 -1
  604. package/src/plugin-api/types.ts +176 -4
  605. package/src/plugins/defaults/compaction/compact.ts +59 -0
  606. package/src/plugins/defaults/compaction/package.json +15 -0
  607. package/src/plugins/defaults/compaction/register.ts +24 -0
  608. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  609. package/src/plugins/defaults/empty-response/package.json +15 -0
  610. package/src/plugins/defaults/empty-response/register.ts +23 -0
  611. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
  612. package/src/plugins/defaults/history-repair/package.json +15 -0
  613. package/src/plugins/defaults/history-repair/register.ts +24 -0
  614. package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
  615. package/src/plugins/defaults/index.ts +22 -49
  616. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  617. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  618. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  619. package/src/plugins/defaults/{injectors.ts → memory-retrieval/injectors.ts} +295 -112
  620. package/src/plugins/defaults/memory-v3-shadow/__tests__/assign.test.ts +242 -0
  621. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +118 -0
  622. package/src/plugins/defaults/memory-v3-shadow/__tests__/core.test.ts +39 -0
  623. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +36 -0
  624. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +37 -0
  625. package/src/plugins/defaults/memory-v3-shadow/__tests__/health.test.ts +219 -0
  626. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +330 -0
  627. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +288 -0
  628. package/src/plugins/defaults/memory-v3-shadow/__tests__/needle.test.ts +107 -0
  629. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +436 -0
  630. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  631. package/src/plugins/defaults/memory-v3-shadow/__tests__/reconcile.test.ts +274 -0
  632. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +61 -0
  633. package/src/plugins/defaults/memory-v3-shadow/__tests__/router.test.ts +332 -0
  634. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +179 -0
  635. package/src/plugins/defaults/memory-v3-shadow/__tests__/selector.test.ts +470 -0
  636. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +432 -0
  637. package/src/plugins/defaults/memory-v3-shadow/__tests__/snapshot.test.ts +168 -0
  638. package/src/plugins/defaults/memory-v3-shadow/__tests__/tree.test.ts +192 -0
  639. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +54 -0
  640. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +106 -0
  641. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +44 -0
  642. package/src/plugins/defaults/memory-v3-shadow/assign.ts +268 -0
  643. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +124 -0
  644. package/src/plugins/defaults/memory-v3-shadow/core.ts +26 -0
  645. package/src/plugins/defaults/memory-v3-shadow/data/README.md +84 -0
  646. package/src/plugins/defaults/memory-v3-shadow/data/assignments.json +5 -0
  647. package/src/plugins/defaults/memory-v3-shadow/data/core.json +1 -0
  648. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-x.md +9 -0
  649. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-y.md +9 -0
  650. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-b/topic-z.md +9 -0
  651. package/src/plugins/defaults/memory-v3-shadow/health.ts +0 -0
  652. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  653. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  654. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  655. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  656. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +314 -0
  657. package/src/plugins/defaults/memory-v3-shadow/needle.ts +115 -0
  658. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +126 -0
  659. package/src/plugins/defaults/memory-v3-shadow/package.json +15 -0
  660. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +34 -0
  661. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  662. package/src/plugins/defaults/memory-v3-shadow/reconcile.ts +523 -0
  663. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  664. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +32 -0
  665. package/src/plugins/defaults/memory-v3-shadow/router.ts +190 -0
  666. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +84 -0
  667. package/src/plugins/defaults/memory-v3-shadow/selector.ts +226 -0
  668. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +349 -0
  669. package/src/plugins/defaults/memory-v3-shadow/snapshot.ts +209 -0
  670. package/src/plugins/defaults/memory-v3-shadow/tree.ts +174 -0
  671. package/src/plugins/defaults/memory-v3-shadow/types.ts +59 -0
  672. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +88 -0
  673. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  674. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  675. package/src/plugins/defaults/title-generate/package.json +15 -0
  676. package/src/plugins/defaults/title-generate/register.ts +35 -0
  677. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  678. package/src/plugins/defaults/tool-error/package.json +15 -0
  679. package/src/plugins/defaults/tool-error/register.ts +23 -0
  680. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  681. package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
  682. package/src/plugins/defaults/tool-result-truncate/register.ts +24 -0
  683. package/src/plugins/defaults/tool-result-truncate/terminal.ts +132 -0
  684. package/src/plugins/external-plugin-loader.ts +2 -2
  685. package/src/plugins/pipeline.ts +8 -35
  686. package/src/plugins/registry.ts +8 -25
  687. package/src/plugins/types.ts +62 -721
  688. package/src/plugins/user-loader.ts +4 -3
  689. package/src/proactive-artifact/aux-message-injector.ts +4 -5
  690. package/src/proactive-artifact/job.test.ts +28 -21
  691. package/src/proactive-artifact/job.ts +3 -1
  692. package/src/prompts/__tests__/system-prompt.test.ts +42 -0
  693. package/src/prompts/sections.ts +20 -7
  694. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  695. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
  696. package/src/prompts/templates/BOOTSTRAP.md +7 -3
  697. package/src/prompts/templates/system-sections.ts +21 -0
  698. package/src/providers/__tests__/retry-callsite.test.ts +25 -25
  699. package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
  700. package/src/providers/anthropic/client.ts +61 -34
  701. package/src/providers/call-site-routing.ts +1 -9
  702. package/src/providers/gemini/client.ts +152 -34
  703. package/src/providers/gemini/inline-media.ts +74 -0
  704. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -2
  705. package/src/providers/openai/chat-completions-provider.ts +45 -4
  706. package/src/providers/openai/responses-provider.ts +1 -4
  707. package/src/providers/openrouter/client.ts +2 -6
  708. package/src/providers/placeholder-sentinels.ts +35 -0
  709. package/src/providers/provider-send-message.ts +6 -6
  710. package/src/providers/ratelimit.ts +1 -9
  711. package/src/providers/retry.ts +0 -5
  712. package/src/providers/types.ts +11 -2
  713. package/src/providers/usage-tracking.ts +1 -9
  714. package/src/runtime/__tests__/agent-wake.test.ts +141 -32
  715. package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
  716. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  717. package/src/runtime/agent-wake.ts +95 -23
  718. package/src/runtime/assistant-event-hub.ts +38 -8
  719. package/src/runtime/assistant-stream-state.ts +368 -0
  720. package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
  721. package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
  722. package/src/runtime/auth/route-policy.ts +42 -1079
  723. package/src/runtime/background-job-runner.ts +1 -4
  724. package/src/runtime/btw-sidechain.ts +3 -1
  725. package/src/runtime/channel-approvals.ts +4 -15
  726. package/src/runtime/channel-invite-transport.ts +5 -6
  727. package/src/runtime/channel-readiness-service.ts +2 -5
  728. package/src/runtime/channel-retry-sweep.ts +12 -16
  729. package/src/runtime/http-router.ts +35 -43
  730. package/src/runtime/http-types.ts +23 -71
  731. package/src/runtime/interactive-ui.ts +1 -1
  732. package/src/runtime/invite-instruction-generator.ts +3 -3
  733. package/src/runtime/pending-interactions.ts +3 -2
  734. package/src/runtime/routes/__tests__/acp-routes.test.ts +253 -55
  735. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
  736. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  737. package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
  738. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
  739. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
  740. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  741. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
  742. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  743. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
  744. package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
  745. package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
  746. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
  747. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  748. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  749. package/src/runtime/routes/__tests__/tts-routes.test.ts +9 -5
  750. package/src/runtime/routes/acp-routes.test.ts +186 -100
  751. package/src/runtime/routes/acp-routes.ts +110 -35
  752. package/src/runtime/routes/app-management-routes.ts +93 -131
  753. package/src/runtime/routes/app-routes.ts +38 -20
  754. package/src/runtime/routes/approval-routes.ts +17 -5
  755. package/src/runtime/routes/attachment-routes.ts +51 -16
  756. package/src/runtime/routes/audio-routes.ts +1 -0
  757. package/src/runtime/routes/audit-routes.ts +5 -0
  758. package/src/runtime/routes/auth-routes.ts +5 -0
  759. package/src/runtime/routes/avatar-routes.ts +264 -59
  760. package/src/runtime/routes/background-tool-routes.ts +9 -0
  761. package/src/runtime/routes/background-wake-routes.ts +13 -3
  762. package/src/runtime/routes/backup-routes.ts +45 -0
  763. package/src/runtime/routes/bookmark-routes.ts +13 -0
  764. package/src/runtime/routes/brain-graph-routes.ts +9 -0
  765. package/src/runtime/routes/browser-routes.ts +6 -1
  766. package/src/runtime/routes/browser-tabs-routes.ts +11 -10
  767. package/src/runtime/routes/btw-routes.ts +34 -24
  768. package/src/runtime/routes/cache-routes.ts +13 -0
  769. package/src/runtime/routes/call-routes.ts +21 -10
  770. package/src/runtime/routes/channel-availability-routes.ts +5 -1
  771. package/src/runtime/routes/channel-readiness-routes.ts +37 -4
  772. package/src/runtime/routes/channel-route-definitions.ts +21 -0
  773. package/src/runtime/routes/channel-verification-routes.ts +21 -0
  774. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
  775. package/src/runtime/routes/client-routes.ts +9 -0
  776. package/src/runtime/routes/consolidation-routes.ts +133 -25
  777. package/src/runtime/routes/contact-prompt-routes.ts +9 -0
  778. package/src/runtime/routes/contact-routes.ts +90 -23
  779. package/src/runtime/routes/content-source-routes.ts +5 -1
  780. package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
  781. package/src/runtime/routes/conversation-attention-routes.ts +5 -0
  782. package/src/runtime/routes/conversation-cli-routes.ts +54 -7
  783. package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
  784. package/src/runtime/routes/conversation-list-routes.ts +81 -12
  785. package/src/runtime/routes/conversation-management-routes.ts +57 -14
  786. package/src/runtime/routes/conversation-query-routes.ts +90 -41
  787. package/src/runtime/routes/conversation-routes.ts +446 -204
  788. package/src/runtime/routes/conversation-starter-routes.ts +35 -20
  789. package/src/runtime/routes/conversations-import-routes.ts +30 -8
  790. package/src/runtime/routes/credential-prompt-routes.ts +5 -0
  791. package/src/runtime/routes/credential-routes.ts +25 -6
  792. package/src/runtime/routes/debug-bash-routes.ts +5 -0
  793. package/src/runtime/routes/debug-routes.ts +11 -2
  794. package/src/runtime/routes/defer-routes.ts +13 -0
  795. package/src/runtime/routes/diagnostics-routes.ts +37 -46
  796. package/src/runtime/routes/disk-pressure-routes.ts +17 -31
  797. package/src/runtime/routes/document-comments-routes.ts +46 -27
  798. package/src/runtime/routes/documents-routes.ts +25 -10
  799. package/src/runtime/routes/domain-routes.ts +98 -51
  800. package/src/runtime/routes/email-routes.ts +33 -0
  801. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  802. package/src/runtime/routes/events-routes.ts +107 -8
  803. package/src/runtime/routes/filing-routes.ts +9 -4
  804. package/src/runtime/routes/gateway-log-routes.ts +31 -4
  805. package/src/runtime/routes/global-search-routes.ts +53 -50
  806. package/src/runtime/routes/group-routes.ts +21 -5
  807. package/src/runtime/routes/guardian-action-routes.ts +9 -0
  808. package/src/runtime/routes/guardian-approval-interception.ts +0 -31
  809. package/src/runtime/routes/heartbeat-routes.ts +57 -21
  810. package/src/runtime/routes/home-feed-routes.ts +23 -19
  811. package/src/runtime/routes/home-state-routes.ts +8 -40
  812. package/src/runtime/routes/host-app-control-routes.ts +6 -1
  813. package/src/runtime/routes/host-bash-routes.ts +5 -0
  814. package/src/runtime/routes/host-browser-routes.ts +13 -0
  815. package/src/runtime/routes/host-cu-routes.ts +6 -1
  816. package/src/runtime/routes/host-file-routes.ts +26 -6
  817. package/src/runtime/routes/host-transfer-routes.ts +13 -2
  818. package/src/runtime/routes/http-adapter.ts +1 -2
  819. package/src/runtime/routes/identity-intro-cache.ts +28 -40
  820. package/src/runtime/routes/identity-routes.ts +236 -20
  821. package/src/runtime/routes/image-generation-routes.ts +45 -2
  822. package/src/runtime/routes/inbound-message-handler.ts +16 -12
  823. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
  824. package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
  825. package/src/runtime/routes/index.ts +2 -0
  826. package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
  827. package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
  828. package/src/runtime/routes/inference-send-routes.ts +11 -11
  829. package/src/runtime/routes/integrations/a2a.ts +32 -7
  830. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  831. package/src/runtime/routes/integrations/slack/channel.ts +23 -3
  832. package/src/runtime/routes/integrations/slack/share.ts +36 -8
  833. package/src/runtime/routes/integrations/telegram.ts +34 -9
  834. package/src/runtime/routes/integrations/twilio.ts +77 -7
  835. package/src/runtime/routes/integrations/vercel.ts +3 -3
  836. package/src/runtime/routes/internal-oauth-routes.ts +5 -0
  837. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  838. package/src/runtime/routes/internal-twilio-routes.ts +13 -0
  839. package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
  840. package/src/runtime/routes/log-export-routes.ts +36 -10
  841. package/src/runtime/routes/mcp-auth-routes.ts +25 -0
  842. package/src/runtime/routes/memory-item-routes.ts +21 -10
  843. package/src/runtime/routes/memory-v2-routes.ts +105 -44
  844. package/src/runtime/routes/memory-v3-routes.ts +306 -408
  845. package/src/runtime/routes/migration-rollback-routes.ts +5 -1
  846. package/src/runtime/routes/migration-routes.ts +29 -0
  847. package/src/runtime/routes/notification-routes.ts +17 -1
  848. package/src/runtime/routes/oauth-apps.ts +99 -23
  849. package/src/runtime/routes/oauth-commands-routes.ts +37 -14
  850. package/src/runtime/routes/oauth-connect-routes.ts +9 -0
  851. package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
  852. package/src/runtime/routes/oauth-providers.ts +79 -15
  853. package/src/runtime/routes/platform-routes.ts +102 -5
  854. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +9 -6
  855. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
  856. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
  857. package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
  858. package/src/runtime/routes/playground/force-compact.ts +2 -2
  859. package/src/runtime/routes/playground/helpers.ts +1 -2
  860. package/src/runtime/routes/playground/inject-failures.ts +13 -8
  861. package/src/runtime/routes/playground/reset-circuit.ts +14 -9
  862. package/src/runtime/routes/playground/seed-conversation.ts +1 -1
  863. package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
  864. package/src/runtime/routes/playground/state.ts +4 -3
  865. package/src/runtime/routes/plugins-routes.ts +22 -19
  866. package/src/runtime/routes/profiler-routes.ts +17 -4
  867. package/src/runtime/routes/ps-routes.ts +5 -0
  868. package/src/runtime/routes/publish-routes.ts +13 -3
  869. package/src/runtime/routes/question-routes.ts +5 -0
  870. package/src/runtime/routes/recording-routes.ts +25 -12
  871. package/src/runtime/routes/rename-conversation-routes.ts +10 -0
  872. package/src/runtime/routes/sanity-routes.ts +9 -2
  873. package/src/runtime/routes/schedule-routes.ts +288 -88
  874. package/src/runtime/routes/secret-routes.ts +31 -6
  875. package/src/runtime/routes/sequence-routes.ts +33 -0
  876. package/src/runtime/routes/settings-routes.ts +65 -19
  877. package/src/runtime/routes/skills-routes.ts +166 -73
  878. package/src/runtime/routes/slack-channel-routes.ts +5 -0
  879. package/src/runtime/routes/stt-routes.ts +13 -6
  880. package/src/runtime/routes/subagents-routes.ts +24 -18
  881. package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
  882. package/src/runtime/routes/surface-action-routes.ts +9 -0
  883. package/src/runtime/routes/surface-content-routes.ts +10 -2
  884. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  885. package/src/runtime/routes/task-routes.ts +37 -0
  886. package/src/runtime/routes/telemetry-routes.ts +9 -0
  887. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  888. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  889. package/src/runtime/routes/trace-event-routes.ts +42 -1
  890. package/src/runtime/routes/trust-rules-routes.ts +31 -2
  891. package/src/runtime/routes/tts-routes.ts +48 -6
  892. package/src/runtime/routes/types.ts +83 -16
  893. package/src/runtime/routes/ui-request-routes.ts +5 -0
  894. package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
  895. package/src/runtime/routes/usage-routes.ts +118 -42
  896. package/src/runtime/routes/user-routes-cli.ts +9 -0
  897. package/src/runtime/routes/user-routes.ts +5 -1
  898. package/src/runtime/routes/wake-conversation-routes.ts +5 -0
  899. package/src/runtime/routes/watcher-routes.ts +21 -0
  900. package/src/runtime/routes/webhook-routes.ts +50 -2
  901. package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
  902. package/src/runtime/routes/work-items-routes.ts +49 -23
  903. package/src/runtime/routes/workspace-commit-routes.ts +5 -0
  904. package/src/runtime/routes/workspace-routes.test.ts +42 -0
  905. package/src/runtime/routes/workspace-routes.ts +124 -9
  906. package/src/runtime/services/__tests__/analyze-conversation.test.ts +8 -4
  907. package/src/runtime/services/analyze-conversation.ts +5 -8
  908. package/src/runtime/services/conversation-serializer.ts +24 -2
  909. package/src/runtime/sync/resource-sync-events.ts +16 -2
  910. package/src/runtime/sync/sync-publisher.ts +2 -2
  911. package/src/schedule/run-script.ts +28 -3
  912. package/src/schedule/schedule-store.ts +28 -1
  913. package/src/schedule/schedule-usage-store.ts +83 -0
  914. package/src/schedule/scheduler.ts +15 -6
  915. package/src/signals/cancel.ts +2 -4
  916. package/src/signals/user-message.ts +5 -8
  917. package/src/skills/catalog-files.ts +4 -1
  918. package/src/skills/catalog-install.ts +3 -0
  919. package/src/skills/categories-cache.ts +118 -0
  920. package/src/skills/clawhub-files.ts +1 -0
  921. package/src/skills/skillssh-files.ts +1 -0
  922. package/src/subagent/manager.ts +20 -11
  923. package/src/telemetry/types.ts +55 -1
  924. package/src/telemetry/usage-telemetry-reporter.test.ts +250 -4
  925. package/src/telemetry/usage-telemetry-reporter.ts +88 -2
  926. package/src/tools/acp/context.ts +20 -0
  927. package/src/tools/acp/list-agents.test.ts +7 -1
  928. package/src/tools/acp/spawn.test.ts +198 -93
  929. package/src/tools/acp/spawn.ts +32 -70
  930. package/src/tools/acp/steer.test.ts +105 -8
  931. package/src/tools/acp/steer.ts +48 -17
  932. package/src/tools/apps/definitions.ts +8 -4
  933. package/src/tools/apps/executors.ts +13 -8
  934. package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
  935. package/src/tools/ask-question/ask-question-tool.ts +85 -90
  936. package/src/tools/computer-use/definitions.ts +28 -24
  937. package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
  938. package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
  939. package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
  940. package/src/tools/credentials/vault.ts +112 -111
  941. package/src/tools/execution-target.ts +1 -1
  942. package/src/tools/execution-timeout.ts +3 -4
  943. package/src/tools/executor.ts +1 -53
  944. package/src/tools/filesystem/edit.ts +45 -42
  945. package/src/tools/filesystem/list.ts +33 -30
  946. package/src/tools/filesystem/read.ts +54 -35
  947. package/src/tools/filesystem/write.ts +69 -32
  948. package/src/tools/host-filesystem/edit.ts +44 -42
  949. package/src/tools/host-filesystem/read.ts +49 -35
  950. package/src/tools/host-filesystem/transfer.ts +121 -108
  951. package/src/tools/host-filesystem/write.ts +33 -31
  952. package/src/tools/host-terminal/host-shell.ts +50 -48
  953. package/src/tools/memory/register.ts +23 -24
  954. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  955. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  956. package/src/tools/network/web-fetch.ts +49 -46
  957. package/src/tools/network/web-search-error.test.ts +248 -0
  958. package/src/tools/network/web-search-error.ts +267 -0
  959. package/src/tools/network/web-search.ts +223 -61
  960. package/src/tools/registry.ts +39 -16
  961. package/src/tools/schedule/create.ts +13 -0
  962. package/src/tools/schedule/update.ts +16 -0
  963. package/src/tools/shared/filesystem/audio-read.ts +122 -0
  964. package/src/tools/shared/filesystem/image-read.ts +1 -1
  965. package/src/tools/skills/execute.ts +34 -31
  966. package/src/tools/skills/load.ts +29 -23
  967. package/src/tools/subagent/notify-parent.ts +35 -32
  968. package/src/tools/subagent/spawn.ts +2 -4
  969. package/src/tools/system/avatar-generator.ts +13 -22
  970. package/src/tools/system/request-permission.ts +30 -27
  971. package/src/tools/terminal/safe-env.ts +10 -1
  972. package/src/tools/terminal/shell.ts +190 -61
  973. package/src/tools/tool-defaults.ts +20 -9
  974. package/src/tools/tool-manifest.ts +4 -4
  975. package/src/tools/types.ts +74 -23
  976. package/src/tools/ui-surface/definitions.ts +99 -10
  977. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  978. package/src/tts/provider-catalog.ts +76 -1
  979. package/src/usage/types.ts +10 -0
  980. package/src/util/errors.ts +2 -2
  981. package/src/util/map-limit.ts +27 -0
  982. package/src/util/mutex.ts +47 -0
  983. package/src/util/platform.ts +15 -12
  984. package/src/work-items/work-item-runner.ts +7 -2
  985. package/src/workspace/git-service.ts +1 -42
  986. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
  987. package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
  988. package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
  989. package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
  990. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  991. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  992. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  993. package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
  994. package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
  995. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
  996. package/src/workspace/migrations/registry.ts +12 -0
  997. package/src/workspace/provider-commit-message-generator.ts +15 -17
  998. package/tsconfig.json +4 -1
  999. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  1000. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  1001. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  1002. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -262
  1003. package/src/__tests__/empty-response-pipeline.test.ts +0 -301
  1004. package/src/__tests__/history-repair-pipeline.test.ts +0 -396
  1005. package/src/__tests__/llm-call-pipeline.test.ts +0 -281
  1006. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  1007. package/src/__tests__/persistence-pipeline.test.ts +0 -514
  1008. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  1009. package/src/__tests__/token-estimate-pipeline.test.ts +0 -481
  1010. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  1011. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  1012. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -344
  1013. package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
  1014. package/src/cli/commands/memory-v3-render.ts +0 -491
  1015. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  1016. package/src/daemon/message-types/disk-pressure.ts +0 -9
  1017. package/src/email/feature-gate.ts +0 -23
  1018. package/src/gallery/default-gallery.ts +0 -1359
  1019. package/src/gallery/gallery-manifest.ts +0 -28
  1020. package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
  1021. package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
  1022. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
  1023. package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
  1024. package/src/memory/v3/__tests__/edges.test.ts +0 -706
  1025. package/src/memory/v3/__tests__/filter.test.ts +0 -560
  1026. package/src/memory/v3/__tests__/gate.test.ts +0 -637
  1027. package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
  1028. package/src/memory/v3/__tests__/loop.test.ts +0 -775
  1029. package/src/memory/v3/__tests__/retriever.test.ts +0 -226
  1030. package/src/memory/v3/__tests__/scouts.test.ts +0 -489
  1031. package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
  1032. package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
  1033. package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
  1034. package/src/memory/v3/__tests__/traversal.test.ts +0 -508
  1035. package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
  1036. package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
  1037. package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
  1038. package/src/memory/v3/__tests__/validate.test.ts +0 -277
  1039. package/src/memory/v3/auto-edges.ts +0 -223
  1040. package/src/memory/v3/coactivation-store.ts +0 -124
  1041. package/src/memory/v3/consolidation-job.ts +0 -323
  1042. package/src/memory/v3/coretrieval-seed.ts +0 -240
  1043. package/src/memory/v3/edge-learning-job.ts +0 -160
  1044. package/src/memory/v3/edges.ts +0 -286
  1045. package/src/memory/v3/filter.ts +0 -286
  1046. package/src/memory/v3/gate.ts +0 -349
  1047. package/src/memory/v3/index-composition.ts +0 -126
  1048. package/src/memory/v3/llm-capture.ts +0 -46
  1049. package/src/memory/v3/loop.ts +0 -430
  1050. package/src/memory/v3/maintenance.ts +0 -144
  1051. package/src/memory/v3/prompt-context.ts +0 -33
  1052. package/src/memory/v3/prompts/consolidation.ts +0 -458
  1053. package/src/memory/v3/prompts/system-prompts.ts +0 -196
  1054. package/src/memory/v3/retriever.ts +0 -33
  1055. package/src/memory/v3/scouts.ts +0 -431
  1056. package/src/memory/v3/shadow-diff.ts +0 -287
  1057. package/src/memory/v3/shadow-middleware.ts +0 -347
  1058. package/src/memory/v3/traversal.ts +0 -211
  1059. package/src/memory/v3/tree-index.ts +0 -237
  1060. package/src/memory/v3/tree-store.ts +0 -394
  1061. package/src/memory/v3/tree-walk.ts +0 -356
  1062. package/src/memory/v3/types.ts +0 -65
  1063. package/src/memory/v3/validate.ts +0 -323
  1064. package/src/plugins/defaults/circuit-breaker.ts +0 -141
  1065. package/src/plugins/defaults/compaction.ts +0 -141
  1066. package/src/plugins/defaults/empty-response.ts +0 -124
  1067. package/src/plugins/defaults/history-repair.ts +0 -83
  1068. package/src/plugins/defaults/llm-call.ts +0 -77
  1069. package/src/plugins/defaults/memory-retrieval.ts +0 -219
  1070. package/src/plugins/defaults/overflow-reduce.ts +0 -185
  1071. package/src/plugins/defaults/persistence.ts +0 -146
  1072. package/src/plugins/defaults/title-generate.ts +0 -90
  1073. package/src/plugins/defaults/token-estimate.ts +0 -101
  1074. package/src/plugins/defaults/tool-error.ts +0 -119
  1075. package/src/plugins/defaults/tool-execute.ts +0 -87
  1076. package/src/plugins/defaults/tool-result-truncate.ts +0 -84
  1077. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
  1078. package/src/skills/category-inference.ts +0 -111
@@ -14,15 +14,11 @@
14
14
  import { createRequire } from "node:module";
15
15
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
16
16
 
17
- import type {
18
- AgentEvent,
19
- CheckpointDecision,
20
- CheckpointInfo,
21
- } from "../agent/loop.js";
17
+ import type { LoopToolExecutor } from "../agent/loop.js";
22
18
  import type { LLMConfig } from "../config/schemas/llm.js";
23
19
  import type { ServerMessage } from "../daemon/message-protocol.js";
24
20
  import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
25
- import type { ContentBlock, Message } from "../providers/types.js";
21
+ import type { Message, Provider, ToolDefinition } from "../providers/types.js";
26
22
 
27
23
  const conversationCrudRealSnapshot = {
28
24
  ...(createRequire(import.meta.url)(
@@ -91,6 +87,7 @@ mock.module("../config/loader.js", () => ({
91
87
  memory: { retrieval: { scratchpadInjection: { enabled: true } } },
92
88
  ui: {},
93
89
  compaction: { enabled: true, autoThreshold: 0.7 },
90
+ conversations: { skipAutoRetitling: true },
94
91
  }),
95
92
  loadRawConfig: () => ({}),
96
93
  saveRawConfig: () => {},
@@ -102,10 +99,10 @@ mock.module("../config/loader.js", () => ({
102
99
  // Token estimator — controllable per-test via mockEstimateTokens.
103
100
  // Can be a number (constant), a no-arg function, or a function that
104
101
  // receives the messages array for dynamic behavior based on content.
105
- // Both the calibrated entry point (`estimatePromptTokens`, used in the
106
- // convergence path) and the raw entry point (`estimatePromptTokensRaw`,
107
- // used by the default `tokenEstimate` plugin pipeline for preflight/mid-
108
- // loop) are stubbed so either call site can drive the test.
102
+ // Both the calibrated entry point (`estimatePromptTokens`, which backs the
103
+ // preflight overflow gate and the convergence path) and the raw entry point
104
+ // (`estimatePromptTokensRaw`, used by the pre-send calibration capture) are
105
+ // stubbed so either call site can drive the test.
109
106
  let mockEstimateTokens: number | ((msgs?: Message[]) => number) = 1000;
110
107
  mock.module("../context/token-estimator.js", () => ({
111
108
  estimatePromptTokens: (msgs: Message[]) =>
@@ -116,8 +113,16 @@ mock.module("../context/token-estimator.js", () => ({
116
113
  typeof mockEstimateTokens === "function"
117
114
  ? mockEstimateTokens(msgs)
118
115
  : mockEstimateTokens,
119
- // Default plugin multiplies-in tool tokens via this helper; 0 keeps the
120
- // stubbed raw value unchanged.
116
+ // The preflight overflow gate calls this calibrated wrapper directly, so it
117
+ // must honor `mockEstimateTokens` too — otherwise the real implementation
118
+ // (which sums tool tokens onto the real calibrated estimate) ignores the
119
+ // per-test value and the overflow scenarios below never trigger.
120
+ estimatePromptTokensWithTools: (history: Message[]) =>
121
+ typeof mockEstimateTokens === "function"
122
+ ? mockEstimateTokens(history)
123
+ : mockEstimateTokens,
124
+ // `estimatePromptTokensWithTools` folds tool tokens in via this helper; 0
125
+ // keeps the stubbed value unchanged.
121
126
  estimateToolsTokens: () => 0,
122
127
  // Conversation agent loop now calls this helper to canonicalize the
123
128
  // provider key shared with the calibration system. The tests here
@@ -269,15 +274,6 @@ mock.module("../daemon/conversation-runtime-assembly.js", () => ({
269
274
  blocks: {},
270
275
  }),
271
276
  stripInjectionsForCompaction: (msgs: Message[]) => msgs,
272
- findLastInjectedNowContent: () => null,
273
- readNowScratchpad: () => null,
274
- readPkbContext: () => null,
275
- getPkbAutoInjectList: () => [
276
- "INDEX.md",
277
- "essentials.md",
278
- "threads.md",
279
- "buffer.md",
280
- ],
281
277
  isSlackChannelConversation: () => false,
282
278
  getSlackCompactionWatermarkForPrefix: () => null,
283
279
  loadSlackChronologicalContext: () => null,
@@ -291,7 +287,7 @@ mock.module("../daemon/date-context.js", () => ({
291
287
  formatTurnTimestamp: () => "2026-01-01 (Thursday) 00:00:00 +00:00 (UTC)",
292
288
  }));
293
289
 
294
- mock.module("../daemon/history-repair.js", () => ({
290
+ mock.module("../plugins/defaults/history-repair/terminal.js", () => ({
295
291
  repairHistory: (msgs: Message[]) => ({
296
292
  messages: msgs,
297
293
  stats: {
@@ -425,37 +421,55 @@ mock.module("../memory/archive-store.js", () => ({
425
421
 
426
422
  // ── Imports (after mocks) ────────────────────────────────────────────
427
423
 
424
+ import { AgentLoop } from "../agent/loop.js";
428
425
  import {
429
426
  type AgentLoopConversationContext,
430
427
  runAgentLoopImpl,
431
428
  } from "../daemon/conversation-agent-loop.js";
429
+ import {
430
+ createMockProvider,
431
+ type ScriptedResponse,
432
+ textResponse,
433
+ toolUseResponse,
434
+ } from "./helpers/mock-provider.js";
432
435
 
433
436
  // ── Test helpers ─────────────────────────────────────────────────────
434
437
 
435
- type AgentLoopRun = (
436
- messages: Message[],
437
- onEvent: (event: AgentEvent) => void,
438
- signal?: AbortSignal,
439
- requestId?: string,
440
- onCheckpoint?: (
441
- checkpoint: CheckpointInfo,
442
- ) => CheckpointDecision | Promise<CheckpointDecision>,
443
- ) => Promise<Message[]>;
444
-
445
438
  function makeCtx(
446
439
  overrides?: Partial<AgentLoopConversationContext> & {
447
- agentLoopRun?: AgentLoopRun;
440
+ providerResponses?: ScriptedResponse[];
441
+ loopProvider?: Provider;
442
+ loopTools?: ToolDefinition[];
443
+ toolExecutor?: LoopToolExecutor;
448
444
  },
449
445
  ): AgentLoopConversationContext {
450
- const agentLoopRun =
451
- overrides?.agentLoopRun ??
452
- (async (messages: Message[]) => [
453
- ...messages,
454
- {
455
- role: "assistant" as const,
456
- content: [{ type: "text" as const, text: "response" }],
457
- },
458
- ]);
446
+ const {
447
+ providerResponses,
448
+ loopProvider,
449
+ loopTools,
450
+ toolExecutor,
451
+ ...ctxOverrides
452
+ } = overrides ?? {};
453
+ const conversationId = ctxOverrides.conversationId ?? "test-conv";
454
+
455
+ // Drive the real `AgentLoop` against a scripted provider, mocking only the
456
+ // provider HTTP boundary. The loop owns its mid-loop budget gate, inline
457
+ // compaction, and event emission, so these overflow tests exercise the real
458
+ // escalation/persistence path.
459
+ const loopProviderName =
460
+ (ctxOverrides.provider as { name?: string } | undefined)?.name ??
461
+ "mock-provider";
462
+ const provider =
463
+ loopProvider ??
464
+ createMockProvider(
465
+ providerResponses ?? [textResponse("response")],
466
+ loopProviderName,
467
+ ).provider;
468
+ const agentLoop = new AgentLoop(provider, "system prompt", {
469
+ conversationId,
470
+ tools: loopTools ?? [],
471
+ toolExecutor,
472
+ });
459
473
 
460
474
  return {
461
475
  conversationId: "test-conv",
@@ -463,18 +477,16 @@ function makeCtx(
463
477
  { role: "user", content: [{ type: "text", text: "Hello" }] },
464
478
  ] as Message[],
465
479
  processing: true,
480
+ isProcessing(this: { processing: boolean }) {
481
+ return this.processing;
482
+ },
483
+ setProcessing(this: { processing: boolean }, value: boolean) {
484
+ this.processing = value;
485
+ },
466
486
  abortController: new AbortController(),
467
487
  currentRequestId: "test-req",
468
488
 
469
- agentLoop: {
470
- run: agentLoopRun,
471
- getToolTokenBudget: () => 0,
472
- getResolvedTools: () => [],
473
- // Tests in this file don't exercise calibration, so returning
474
- // undefined is fine — the estimator falls back to the per-provider
475
- // aggregate key.
476
- getActiveModel: () => undefined,
477
- } as unknown as AgentLoopConversationContext["agentLoop"],
489
+ agentLoop,
478
490
  provider: {
479
491
  name: "mock-provider",
480
492
  sendMessage: async () => ({
@@ -503,8 +515,6 @@ function makeCtx(
503
515
  currentTurnSurfaces: [],
504
516
 
505
517
  workingDir: "/tmp",
506
- workspaceTopLevelContext: null,
507
- workspaceTopLevelDirty: false,
508
518
  channelCapabilities: undefined,
509
519
  commandIntent: undefined,
510
520
  trustContext: undefined,
@@ -541,7 +551,6 @@ function makeCtx(
541
551
  getWorkspaceGitService: () => ({ ensureInitialized: async () => {} }),
542
552
  commitTurnChanges: async () => {},
543
553
 
544
- refreshWorkspaceTopLevelContextIfNeeded: () => {},
545
554
  markWorkspaceTopLevelDirty: () => {},
546
555
  emitActivityState: () => {},
547
556
  getQueueDepth: () => 0,
@@ -567,9 +576,10 @@ function makeCtx(
567
576
  injectedTokens: 0,
568
577
  }),
569
578
  retrackCachedNodes: () => {},
579
+ recordPkbQueryVectors: () => {},
570
580
  } as unknown as AgentLoopConversationContext["graphMemory"],
571
581
 
572
- ...overrides,
582
+ ...ctxOverrides,
573
583
  } as AgentLoopConversationContext;
574
584
  }
575
585
 
@@ -638,15 +648,15 @@ beforeEach(() => {
638
648
  recordUsageMock.mockClear();
639
649
  setAgentLoopExitReasonOnLatestLogMock.mockClear();
640
650
  addMessageMock.mockClear();
641
- // Reset the plugin registry and re-register every default so the
642
- // orchestrator's pipelines (`overflowReduce`, `persistence`, …) dispatch to
643
- // the default middleware, which in turn hits the mocked collaborators
644
- // (`reduceContextOverflow`, `syncMessageToDisk`, …) these tests install.
651
+ // Reset the plugin registry and re-register every default so the compaction
652
+ // pipeline dispatches to the default middleware, which in turn hits the
653
+ // mocked collaborators (`syncMessageToDisk`, …) these tests install.
645
654
  resetPluginRegistryAndRegisterDefaults();
646
655
  });
647
656
 
648
657
  describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
649
658
  test("usage update context max follows active main-agent profile budget", async () => {
659
+ // GIVEN an active main-agent profile that narrows the context budget
650
660
  mockLlmConfig = {
651
661
  ...structuredClone(defaultLlmConfig),
652
662
  activeProfile: "short-context",
@@ -658,27 +668,22 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
658
668
  },
659
669
  };
660
670
 
671
+ // AND a provider turn that reports 12k input tokens of usage
661
672
  const ctx = makeCtx({
662
- agentLoopRun: async (messages, onEvent) => {
663
- onEvent({
664
- type: "usage",
665
- inputTokens: 12_000,
666
- outputTokens: 300,
673
+ providerResponses: [
674
+ {
675
+ content: [{ type: "text", text: "response" }],
667
676
  model: "mock-model",
668
- providerDurationMs: 25,
669
- });
670
- return [
671
- ...messages,
672
- {
673
- role: "assistant" as const,
674
- content: [{ type: "text" as const, text: "response" }],
675
- },
676
- ];
677
- },
677
+ usage: { inputTokens: 12_000, outputTokens: 300 },
678
+ stopReason: "end_turn",
679
+ },
680
+ ],
678
681
  });
679
682
 
683
+ // WHEN the turn runs to completion
680
684
  await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
681
685
 
686
+ // THEN the recorded main-agent usage carries the profile's max budget
682
687
  const mainAgentUsageCall = recordUsageMock.mock.calls.find(
683
688
  (call) => call[5] === "main_agent",
684
689
  );
@@ -691,10 +696,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
691
696
 
692
697
  // ── Test 1 ────────────────────────────────────────────────────────
693
698
  // BUG: When the agent loop makes progress (adds messages to history)
694
- // before hitting context_too_large, the convergence loop at line 864
695
- // checks `updatedHistory.length === preRunHistoryLength` which is
696
- // false when progress was made. This means the reducer is never
697
- // invoked — the error is surfaced immediately at line 1163-1175
699
+ // before hitting context_too_large, the convergence loop's progress
700
+ // check must recognize that the loop appended messages. If it fails to,
701
+ // the reducer is never invoked the error is surfaced immediately
698
702
  // without any compaction attempt.
699
703
  //
700
704
  // Expected behavior (PR 2 fix): After progress + context_too_large,
@@ -734,125 +738,31 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
734
738
  };
735
739
  };
736
740
 
737
- let agentLoopCallCount = 0;
738
- const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
739
- // Prime the assistant row anchor production code emits this from
740
- // `AgentLoop.run` just before `provider.sendMessage`.
741
- await onEvent({ type: "llm_call_started" });
742
- agentLoopCallCount++;
743
- if (agentLoopCallCount === 1) {
744
- // Simulate: agent makes progress (tool calls + results added)
745
- // then hits context_too_large on next LLM call
746
- const progressMessages: Message[] = [
747
- ...messages,
748
- {
749
- role: "assistant" as const,
750
- content: [
751
- { type: "text", text: "Let me check that." },
752
- {
753
- type: "tool_use",
754
- id: "tu-progress",
755
- name: "bash",
756
- input: { command: "ls" },
757
- },
758
- ] as ContentBlock[],
759
- },
760
- {
761
- role: "user" as const,
762
- content: [
763
- {
764
- type: "tool_result",
765
- tool_use_id: "tu-progress",
766
- content: "file1.ts\nfile2.ts",
767
- is_error: false,
768
- },
769
- ] as ContentBlock[],
770
- },
771
- ];
741
+ // Run 1 makes progress (a tool turn) then the following provider call
742
+ // rejects with a context_too_large error; after the convergence reducer
743
+ // compacts, the rerun recovers with plain text.
744
+ const { provider } = createMockProvider([
745
+ toolUseResponse("tu-progress", "bash", { command: "ls" }),
746
+ new Error("prompt is too long: 242201 tokens > 200000 maximum"),
747
+ textResponse("recovered after compaction"),
748
+ ]);
772
749
 
773
- // Emit events for the progress that was made
774
- onEvent({
775
- type: "tool_use",
776
- id: "tu-progress",
750
+ const ctx = makeCtx({
751
+ loopProvider: provider,
752
+ loopTools: [
753
+ {
777
754
  name: "bash",
778
- input: { command: "ls" },
779
- });
780
- onEvent({
781
- type: "tool_result",
782
- toolUseId: "tu-progress",
783
- content: "file1.ts\nfile2.ts",
784
- isError: false,
785
- });
786
- onEvent({
787
- type: "message_complete",
788
- message: {
789
- role: "assistant",
790
- content: [
791
- { type: "text", text: "Let me check that." },
792
- {
793
- type: "tool_use",
794
- id: "tu-progress",
795
- name: "bash",
796
- input: { command: "ls" },
797
- },
798
- ],
755
+ description: "Run a shell command",
756
+ input_schema: {
757
+ type: "object",
758
+ properties: { command: { type: "string" } },
799
759
  },
800
- });
801
- onEvent({
802
- type: "usage",
803
- inputTokens: 100,
804
- outputTokens: 50,
805
- model: "test-model",
806
- providerDurationMs: 100,
807
- });
808
-
809
- // Then context_too_large error occurs on the *next* LLM call
810
- onEvent({
811
- type: "error",
812
- error: new Error(
813
- "prompt is too long: 242201 tokens > 200000 maximum",
814
- ),
815
- });
816
- onEvent({
817
- type: "usage",
818
- inputTokens: 0,
819
- outputTokens: 0,
820
- model: "test-model",
821
- providerDurationMs: 10,
822
- });
823
-
824
- // Return the history WITH progress (more messages than input)
825
- return progressMessages;
826
- }
827
-
828
- // Second call (after compaction): succeed
829
- onEvent({
830
- type: "message_complete",
831
- message: {
832
- role: "assistant",
833
- content: [{ type: "text", text: "recovered after compaction" }],
834
760
  },
835
- });
836
- onEvent({
837
- type: "usage",
838
- inputTokens: 50,
839
- outputTokens: 25,
840
- model: "test-model",
841
- providerDurationMs: 100,
842
- });
843
- return [
844
- ...messages,
845
- {
846
- role: "assistant" as const,
847
- content: [
848
- { type: "text", text: "recovered after compaction" },
849
- ] as ContentBlock[],
850
- },
851
- ];
852
- };
853
-
854
- const ctx = makeCtx({
855
- agentLoopRun,
761
+ ],
762
+ toolExecutor: async () => ({
763
+ content: "file1.ts\nfile2.ts",
764
+ isError: false,
765
+ }),
856
766
  contextWindowManager: {
857
767
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
858
768
  maybeCompact: async () => ({ compacted: false }),
@@ -881,13 +791,14 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
881
791
  // This test should PASS against current code (when no progress is made).
882
792
  test("overflow recovery compacts below limit even when estimation underestimates", async () => {
883
793
  const events: ServerMessage[] = [];
884
- let callCount = 0;
885
794
  let reducerCalled = false;
886
795
 
887
- // Estimator says 185k (below 190k budget = 200k * 0.95)
796
+ // GIVEN the estimator reports 185k under the 190k preflight budget
797
+ // (200k * 0.95), so the turn proceeds to the provider rather than
798
+ // compacting up front.
888
799
  mockEstimateTokens = 185_000;
889
800
 
890
- // Reducer successfully compacts
801
+ // AND the post-run convergence reducer successfully compacts
891
802
  mockReducerStepFn = (msgs: Message[]) => {
892
803
  reducerCalled = true;
893
804
  return {
@@ -917,96 +828,46 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
917
828
  };
918
829
  };
919
830
 
920
- const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
921
- // Prime the assistant row anchor production code emits this from
922
- // `AgentLoop.run` just before `provider.sendMessage`.
923
- await onEvent({ type: "llm_call_started" });
924
- callCount++;
925
- if (callCount === 1) {
926
- // Provider rejects with "prompt is too long: 242201 tokens > 200000"
927
- // even though estimator said 185k
928
- onEvent({
929
- type: "error",
930
- error: new Error(
931
- "prompt is too long: 242201 tokens > 200000 maximum",
932
- ),
933
- });
934
- onEvent({
935
- type: "usage",
936
- inputTokens: 0,
937
- outputTokens: 0,
938
- model: "test-model",
939
- providerDurationMs: 10,
940
- });
941
- // No progress — return same messages
942
- return messages;
943
- }
944
- // Second call succeeds
945
- onEvent({
946
- type: "message_complete",
947
- message: {
948
- role: "assistant",
949
- content: [{ type: "text", text: "recovered" }],
950
- },
951
- });
952
- onEvent({
953
- type: "usage",
954
- inputTokens: 80_000,
955
- outputTokens: 200,
956
- model: "test-model",
957
- providerDurationMs: 500,
958
- });
959
- return [
960
- ...messages,
961
- {
962
- role: "assistant" as const,
963
- content: [{ type: "text", text: "recovered" }] as ContentBlock[],
964
- },
965
- ];
966
- };
831
+ // AND a provider that rejects the first call as too long (revealing the
832
+ // real 242k count the estimator missed), then succeeds on the rerun.
833
+ const { provider, calls } = createMockProvider([
834
+ new Error("prompt is too long: 242201 tokens > 200000 maximum"),
835
+ textResponse("recovered"),
836
+ ]);
967
837
 
968
838
  const ctx = makeCtx({
969
- agentLoopRun,
839
+ loopProvider: provider,
970
840
  contextWindowManager: {
971
841
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
972
842
  maybeCompact: async () => ({ compacted: false }),
973
843
  } as unknown as AgentLoopConversationContext["contextWindowManager"],
974
844
  });
975
845
 
846
+ // WHEN the turn runs
976
847
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
977
848
 
978
- // The reducer should be called in the convergence loop
849
+ // THEN the convergence reducer ran and the rerun recovered without a
850
+ // user-facing conversation_error.
979
851
  expect(reducerCalled).toBe(true);
980
- // Should recover without conversation_error
981
852
  const conversationError = events.find(
982
853
  (e) => e.type === "conversation_error",
983
854
  );
984
855
  expect(conversationError).toBeUndefined();
985
- expect(callCount).toBe(2);
856
+ expect(calls.length).toBe(2);
986
857
  });
987
858
 
988
859
  // ── Test 3 ────────────────────────────────────────────────────────
989
- // BUG: When the provider rejection reveals actual token count (e.g.,
990
- // "242201 tokens > 200000"), the reducer should target a budget below
991
- // the actual limit (not below the estimator's inaccurate budget).
992
- // Currently the reducer always uses `preflightBudget` (190k) as the
993
- // target, but the actual tokens were 242k so 190k is already too
994
- // high relative to the real count. The target should be adjusted
995
- // downward based on the observed mismatch.
996
- //
997
- // Expected behavior (PR 4 fix): `targetInputTokensOverride` should
998
- // be adjusted based on the ratio between estimated and actual tokens.
999
- // BUG: The targetTokens passed to the reducer is preflightBudget = 190k.
1000
- // But when the actual token count is 242k (1.31x the estimate of 185k),
1001
- // the target should be adjusted downward to account for the estimation
1002
- // inaccuracy. For example: 190k / 1.31 ≈ 145k.
1003
- // Planned fix: targetInputTokensOverride should be adjusted based on
1004
- // the ratio between estimated and actual tokens.
860
+ // When the provider rejection reveals the actual token count (e.g.,
861
+ // "242201 tokens > 200000"), the overflow reducer's `targetTokens`
862
+ // should be a budget below the actual limit, not below the estimator's
863
+ // inaccurate budget. With a preflightBudget of 190k but an actual count
864
+ // of 242k (1.31x the estimate of 185k), the target is adjusted downward
865
+ // based on the observed mismatch (190k / 1.31 145k) so the reducer
866
+ // converges toward the real ceiling rather than the optimistic estimate.
1005
867
  test.todo(
1006
868
  "forced compaction targets a lower budget when estimation has been inaccurate",
1007
869
  async () => {
1008
870
  const events: ServerMessage[] = [];
1009
- let callCount = 0;
1010
871
  let capturedTargetTokens: number | undefined;
1011
872
 
1012
873
  // Estimator says 185k (below 190k budget = 200k * 0.95)
@@ -1042,55 +903,16 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1042
903
  };
1043
904
  };
1044
905
 
1045
- const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
1046
- // Prime the assistant row anchor production code emits this from
1047
- // `AgentLoop.run` just before `provider.sendMessage`.
1048
- await onEvent({ type: "llm_call_started" });
1049
- callCount++;
1050
- if (callCount === 1) {
1051
- // Provider rejects: actual tokens 242201, way above estimate of 185k
1052
- onEvent({
1053
- type: "error",
1054
- error: new Error(
1055
- "prompt is too long: 242201 tokens > 200000 maximum",
1056
- ),
1057
- });
1058
- onEvent({
1059
- type: "usage",
1060
- inputTokens: 0,
1061
- outputTokens: 0,
1062
- model: "test-model",
1063
- providerDurationMs: 10,
1064
- });
1065
- // No progress — return same messages
1066
- return messages;
1067
- }
1068
- // Second call succeeds after compaction
1069
- onEvent({
1070
- type: "message_complete",
1071
- message: {
1072
- role: "assistant",
1073
- content: [{ type: "text", text: "recovered" }],
1074
- },
1075
- });
1076
- onEvent({
1077
- type: "usage",
1078
- inputTokens: 80_000,
1079
- outputTokens: 200,
1080
- model: "test-model",
1081
- providerDurationMs: 500,
1082
- });
1083
- return [
1084
- ...messages,
1085
- {
1086
- role: "assistant" as const,
1087
- content: [{ type: "text", text: "recovered" }] as ContentBlock[],
1088
- },
1089
- ];
1090
- };
906
+ // The provider rejects the first call with a context_too_large error
907
+ // (actual tokens 242201, far above the 185k estimate); after forced
908
+ // compaction re-targets a lower budget, the rerun recovers with text.
909
+ const { provider, calls } = createMockProvider([
910
+ new Error("prompt is too long: 242201 tokens > 200000 maximum"),
911
+ textResponse("recovered"),
912
+ ]);
1091
913
 
1092
914
  const ctx = makeCtx({
1093
- agentLoopRun,
915
+ loopProvider: provider,
1094
916
  contextWindowManager: {
1095
917
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1096
918
  maybeCompact: async () => ({ compacted: false }),
@@ -1120,7 +942,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1120
942
  (e) => e.type === "conversation_error",
1121
943
  );
1122
944
  expect(conversationError).toBeUndefined();
1123
- expect(callCount).toBe(2);
945
+ expect(calls.length).toBe(2);
1124
946
  },
1125
947
  );
1126
948
 
@@ -1134,7 +956,6 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1134
956
  async () => {
1135
957
  const events: ServerMessage[] = [];
1136
958
  const longHistory = buildLongConversation(75);
1137
- let callCount = 0;
1138
959
  let reducerCalled = false;
1139
960
 
1140
961
  // Estimator says ~195k — just above budget so preflight reducer runs
@@ -1170,38 +991,14 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1170
991
  };
1171
992
  };
1172
993
 
1173
- const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
1174
- // Prime the assistant row anchor production code emits this from
1175
- // `AgentLoop.run` just before `provider.sendMessage`.
1176
- await onEvent({ type: "llm_call_started" });
1177
- callCount++;
1178
- onEvent({
1179
- type: "message_complete",
1180
- message: {
1181
- role: "assistant",
1182
- content: [{ type: "text", text: "Here's the analysis..." }],
1183
- },
1184
- });
1185
- onEvent({
1186
- type: "usage",
1187
- inputTokens: 50_000,
1188
- outputTokens: 300,
1189
- model: "test-model",
1190
- providerDurationMs: 800,
1191
- });
1192
- return [
1193
- ...messages,
1194
- {
1195
- role: "assistant" as const,
1196
- content: [
1197
- { type: "text", text: "Here's the analysis..." },
1198
- ] as ContentBlock[],
1199
- },
1200
- ];
1201
- };
994
+ // After the preflight reducer compacts the long history under budget,
995
+ // a single provider call completes the turn with plain text.
996
+ const { provider, calls } = createMockProvider([
997
+ textResponse("Here's the analysis..."),
998
+ ]);
1202
999
 
1203
1000
  const ctx = makeCtx({
1204
- agentLoopRun,
1001
+ loopProvider: provider,
1205
1002
  messages: longHistory,
1206
1003
  contextWindowManager: {
1207
1004
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
@@ -1216,7 +1013,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1216
1013
  // Preflight should trigger the reducer since 195k > 190k budget
1217
1014
  expect(reducerCalled).toBe(true);
1218
1015
  // Should succeed
1219
- expect(callCount).toBe(1);
1016
+ expect(calls.length).toBe(1);
1220
1017
  const conversationError = events.find(
1221
1018
  (e) => e.type === "conversation_error",
1222
1019
  );
@@ -1260,118 +1057,31 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1260
1057
  };
1261
1058
  };
1262
1059
 
1263
- let agentLoopCallCount = 0;
1264
- const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
1265
- // Prime the assistant row anchor — production code emits this from
1266
- // `AgentLoop.run` just before `provider.sendMessage`.
1267
- await onEvent({ type: "llm_call_started" });
1268
- agentLoopCallCount++;
1269
- if (agentLoopCallCount === 1) {
1270
- // Agent makes progress (tool calls succeed, messages grow)
1271
- const progressMessages: Message[] = [
1272
- ...messages,
1273
- {
1274
- role: "assistant" as const,
1275
- content: [
1276
- { type: "text", text: "Running analysis..." },
1277
- {
1278
- type: "tool_use",
1279
- id: "tu-1",
1280
- name: "bash",
1281
- input: { command: "find . -name '*.ts'" },
1282
- },
1283
- ] as ContentBlock[],
1284
- },
1285
- {
1286
- role: "user" as const,
1287
- content: [
1288
- {
1289
- type: "tool_result",
1290
- tool_use_id: "tu-1",
1291
- content: "file1.ts\nfile2.ts\nfile3.ts",
1292
- is_error: false,
1293
- },
1294
- ] as ContentBlock[],
1295
- },
1296
- ];
1060
+ // Run 1 makes progress (a tool turn) then the following provider call
1061
+ // rejects with context_too_large; after emergency compaction the rerun
1062
+ // recovers with plain text.
1063
+ const { provider } = createMockProvider([
1064
+ toolUseResponse("tu-1", "bash", { command: "find . -name '*.ts'" }),
1065
+ new Error("context_length_exceeded"),
1066
+ textResponse("recovered"),
1067
+ ]);
1297
1068
 
1298
- onEvent({
1299
- type: "tool_use",
1300
- id: "tu-1",
1069
+ const ctx = makeCtx({
1070
+ loopProvider: provider,
1071
+ loopTools: [
1072
+ {
1301
1073
  name: "bash",
1302
- input: { command: "find . -name '*.ts'" },
1303
- });
1304
- onEvent({
1305
- type: "tool_result",
1306
- toolUseId: "tu-1",
1307
- content: "file1.ts\nfile2.ts\nfile3.ts",
1308
- isError: false,
1309
- });
1310
- onEvent({
1311
- type: "message_complete",
1312
- message: {
1313
- role: "assistant",
1314
- content: [
1315
- { type: "text", text: "Running analysis..." },
1316
- {
1317
- type: "tool_use",
1318
- id: "tu-1",
1319
- name: "bash",
1320
- input: { command: "find . -name '*.ts'" },
1321
- },
1322
- ],
1074
+ description: "Run a shell command",
1075
+ input_schema: {
1076
+ type: "object",
1077
+ properties: { command: { type: "string" } },
1323
1078
  },
1324
- });
1325
- onEvent({
1326
- type: "usage",
1327
- inputTokens: 190_000,
1328
- outputTokens: 100,
1329
- model: "test-model",
1330
- providerDurationMs: 200,
1331
- });
1332
-
1333
- // Then context_too_large on the next LLM call within the loop
1334
- onEvent({
1335
- type: "error",
1336
- error: new Error("context_length_exceeded"),
1337
- });
1338
- onEvent({
1339
- type: "usage",
1340
- inputTokens: 0,
1341
- outputTokens: 0,
1342
- model: "test-model",
1343
- providerDurationMs: 10,
1344
- });
1345
-
1346
- return progressMessages;
1347
- }
1348
-
1349
- // After emergency compaction, succeed
1350
- onEvent({
1351
- type: "message_complete",
1352
- message: {
1353
- role: "assistant",
1354
- content: [{ type: "text", text: "recovered" }],
1355
1079
  },
1356
- });
1357
- onEvent({
1358
- type: "usage",
1359
- inputTokens: 50_000,
1360
- outputTokens: 100,
1361
- model: "test-model",
1362
- providerDurationMs: 200,
1363
- });
1364
- return [
1365
- ...messages,
1366
- {
1367
- role: "assistant" as const,
1368
- content: [{ type: "text", text: "recovered" }] as ContentBlock[],
1369
- },
1370
- ];
1371
- };
1372
-
1373
- const ctx = makeCtx({
1374
- agentLoopRun,
1080
+ ],
1081
+ toolExecutor: async () => ({
1082
+ content: "file1.ts\nfile2.ts\nfile3.ts",
1083
+ isError: false,
1084
+ }),
1375
1085
  contextWindowManager: {
1376
1086
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1377
1087
  maybeCompact: async (
@@ -1448,117 +1158,30 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1448
1158
  return 170_000;
1449
1159
  };
1450
1160
 
1451
- let agentLoopCallCount = 0;
1452
- const agentLoopRun: AgentLoopRun = async (
1453
- messages,
1454
- onEvent,
1455
- _signal,
1456
- _requestId,
1457
- onCheckpoint,
1458
- ) => {
1459
- // Prime the assistant row anchor — production code emits this from
1460
- // `AgentLoop.run` just before `provider.sendMessage`.
1461
- await onEvent({ type: "llm_call_started" });
1462
- agentLoopCallCount++;
1463
-
1464
- if (agentLoopCallCount === 1) {
1465
- // Simulate a tool round: assistant calls a tool, results come back
1466
- const withProgress: Message[] = [
1467
- ...messages,
1468
- {
1469
- role: "assistant" as const,
1470
- content: [
1471
- { type: "text", text: "Let me check." },
1472
- {
1473
- type: "tool_use",
1474
- id: "tu-1",
1475
- name: "bash",
1476
- input: { command: "ls" },
1477
- },
1478
- ] as ContentBlock[],
1479
- },
1480
- {
1481
- role: "user" as const,
1482
- content: [
1483
- {
1484
- type: "tool_result",
1485
- tool_use_id: "tu-1",
1486
- content: "file1.ts\nfile2.ts",
1487
- is_error: false,
1488
- },
1489
- ] as ContentBlock[],
1490
- },
1491
- ];
1492
-
1493
- onEvent({
1494
- type: "message_complete",
1495
- message: {
1496
- role: "assistant",
1497
- content: [
1498
- { type: "text", text: "Let me check." },
1499
- {
1500
- type: "tool_use",
1501
- id: "tu-1",
1502
- name: "bash",
1503
- input: { command: "ls" },
1504
- },
1505
- ],
1506
- },
1507
- });
1508
- onEvent({
1509
- type: "usage",
1510
- inputTokens: 100,
1511
- outputTokens: 50,
1512
- model: "test-model",
1513
- providerDurationMs: 100,
1514
- });
1515
-
1516
- // Call onCheckpoint — this should trigger the mid-loop budget check
1517
- // which sees 170_000 > 161_500 and returns "yield"
1518
- if (onCheckpoint) {
1519
- const decision = await onCheckpoint({
1520
- turnIndex: 0,
1521
- toolCount: 1,
1522
- hasToolUse: true,
1523
- history: withProgress,
1524
- });
1525
- if (decision === "yield") {
1526
- // Agent loop stops when checkpoint yields
1527
- return withProgress;
1528
- }
1529
- }
1530
-
1531
- return withProgress;
1532
- }
1161
+ // A tool round trips the mid-loop budget gate (170k > 161_500); the
1162
+ // gate compacts in place (productive) and the loop continues, so the
1163
+ // post-compaction provider call completes the turn with plain text.
1164
+ const { provider, calls } = createMockProvider([
1165
+ toolUseResponse("tu-1", "bash", { command: "ls" }),
1166
+ textResponse("done after compaction"),
1167
+ ]);
1533
1168
 
1534
- // Second call (after compaction): complete successfully
1535
- onEvent({
1536
- type: "message_complete",
1537
- message: {
1538
- role: "assistant",
1539
- content: [{ type: "text", text: "done after compaction" }],
1540
- },
1541
- });
1542
- onEvent({
1543
- type: "usage",
1544
- inputTokens: 50,
1545
- outputTokens: 25,
1546
- model: "test-model",
1547
- providerDurationMs: 100,
1548
- });
1549
- return [
1550
- ...messages,
1169
+ const ctx = makeCtx({
1170
+ loopProvider: provider,
1171
+ loopTools: [
1551
1172
  {
1552
- role: "assistant" as const,
1553
- content: [
1554
- { type: "text", text: "done after compaction" },
1555
- ] as ContentBlock[],
1173
+ name: "bash",
1174
+ description: "Run a shell command",
1175
+ input_schema: {
1176
+ type: "object",
1177
+ properties: { command: { type: "string" } },
1178
+ },
1556
1179
  },
1557
- ];
1558
- };
1559
-
1560
- const ctx = makeCtx({
1561
- agentLoopRun,
1180
+ ],
1181
+ toolExecutor: async () => ({
1182
+ content: "file1.ts\nfile2.ts",
1183
+ isError: false,
1184
+ }),
1562
1185
  contextWindowManager: {
1563
1186
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1564
1187
  maybeCompact: async () => {
@@ -1592,8 +1215,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1592
1215
  // The mid-loop budget check should have triggered compaction
1593
1216
  expect(compactionCalled).toBe(true);
1594
1217
 
1595
- // Agent loop should have been called twice: once before yield, once after compaction
1596
- expect(agentLoopCallCount).toBe(2);
1218
+ // Provider called twice: the tool turn that tripped the gate, then the
1219
+ // post-compaction turn that completed the run.
1220
+ expect(calls.length).toBe(2);
1597
1221
 
1598
1222
  // No conversation_error should be emitted
1599
1223
  const conversationError = events.find(
@@ -1634,110 +1258,36 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1634
1258
  return 175_000;
1635
1259
  };
1636
1260
 
1637
- let agentLoopCallCount = 0;
1638
1261
  let contextTooLargeEmitted = false;
1639
1262
 
1640
- const agentLoopRun: AgentLoopRun = async (
1641
- messages,
1642
- onEvent,
1643
- _signal,
1644
- _requestId,
1645
- onCheckpoint,
1646
- ) => {
1647
- // Prime the assistant row anchor — production code emits this from
1648
- // `AgentLoop.run` just before `provider.sendMessage`.
1649
- await onEvent({ type: "llm_call_started" });
1650
- agentLoopCallCount++;
1651
-
1652
- if (agentLoopCallCount === 1) {
1653
- const currentHistory = [...messages];
1654
-
1655
- // Simulate 5 tool rounds — but the checkpoint should yield at round 3
1656
- for (let i = 0; i < 5; i++) {
1657
- const toolId = `tu-${i}`;
1658
- const assistantMsg: Message = {
1659
- role: "assistant" as const,
1660
- content: [
1661
- { type: "text", text: `Step ${i}` },
1662
- {
1663
- type: "tool_use",
1664
- id: toolId,
1665
- name: "bash",
1666
- input: { command: `cmd-${i}` },
1667
- },
1668
- ] as ContentBlock[],
1669
- };
1670
- const resultMsg: Message = {
1671
- role: "user" as const,
1672
- content: [
1673
- {
1674
- type: "tool_result",
1675
- tool_use_id: toolId,
1676
- content: "x".repeat(10_000),
1677
- is_error: false,
1678
- },
1679
- ] as ContentBlock[],
1680
- };
1681
- currentHistory.push(assistantMsg, resultMsg);
1682
-
1683
- onEvent({
1684
- type: "message_complete",
1685
- message: assistantMsg,
1686
- });
1687
- onEvent({
1688
- type: "usage",
1689
- inputTokens: 50_000 + i * 20_000,
1690
- outputTokens: 50,
1691
- model: "test-model",
1692
- providerDurationMs: 100,
1693
- });
1694
-
1695
- if (onCheckpoint) {
1696
- const decision = await onCheckpoint({
1697
- turnIndex: i,
1698
- toolCount: 1,
1699
- hasToolUse: true,
1700
- history: currentHistory,
1701
- });
1702
- if (decision === "yield") {
1703
- return currentHistory;
1704
- }
1705
- }
1706
- }
1707
-
1708
- return currentHistory;
1709
- }
1263
+ // Each tool round produces a large result; the estimate grows with each
1264
+ // checkpoint until tool round 3 trips the mid-loop gate (175k > 161_500).
1265
+ // Compaction runs in place (productive) and the loop continues, so the
1266
+ // following plain-text provider call completes the turn. The provider
1267
+ // never rejects with context_too_large.
1268
+ const { provider, calls } = createMockProvider([
1269
+ toolUseResponse("tu-0", "bash", { command: "cmd-0" }),
1270
+ toolUseResponse("tu-1", "bash", { command: "cmd-1" }),
1271
+ toolUseResponse("tu-2", "bash", { command: "cmd-2" }),
1272
+ textResponse("completed after mid-loop compaction"),
1273
+ ]);
1710
1274
 
1711
- // Second call (after compaction): complete
1712
- onEvent({
1713
- type: "message_complete",
1714
- message: {
1715
- role: "assistant",
1716
- content: [
1717
- { type: "text", text: "completed after mid-loop compaction" },
1718
- ],
1719
- },
1720
- });
1721
- onEvent({
1722
- type: "usage",
1723
- inputTokens: 60_000,
1724
- outputTokens: 100,
1725
- model: "test-model",
1726
- providerDurationMs: 200,
1727
- });
1728
- return [
1729
- ...messages,
1275
+ const ctx = makeCtx({
1276
+ loopProvider: provider,
1277
+ loopTools: [
1730
1278
  {
1731
- role: "assistant" as const,
1732
- content: [
1733
- { type: "text", text: "completed after mid-loop compaction" },
1734
- ] as ContentBlock[],
1279
+ name: "bash",
1280
+ description: "Run a shell command",
1281
+ input_schema: {
1282
+ type: "object",
1283
+ properties: { command: { type: "string" } },
1284
+ },
1735
1285
  },
1736
- ];
1737
- };
1738
-
1739
- const ctx = makeCtx({
1740
- agentLoopRun,
1286
+ ],
1287
+ toolExecutor: async () => ({
1288
+ content: "x".repeat(10_000),
1289
+ isError: false,
1290
+ }),
1741
1291
  contextWindowManager: {
1742
1292
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1743
1293
  maybeCompact: async () => {
@@ -1784,8 +1334,9 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1784
1334
  // The provider should NEVER have rejected with context_too_large
1785
1335
  expect(contextTooLargeEmitted).toBe(false);
1786
1336
 
1787
- // Agent loop called twice: once (yielded at tool 3), once after compaction
1788
- expect(agentLoopCallCount).toBe(2);
1337
+ // Provider called four times: three tool rounds (the third trips the
1338
+ // mid-loop gate) plus the post-compaction text turn that completes.
1339
+ expect(calls.length).toBe(4);
1789
1340
 
1790
1341
  // No conversation_error
1791
1342
  const conversationError = events.find(
@@ -1814,88 +1365,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1814
1365
  return 170_000;
1815
1366
  };
1816
1367
 
1817
- let agentLoopCallCount = 0;
1818
- const agentLoopRun: AgentLoopRun = async (
1819
- messages,
1820
- onEvent,
1821
- _signal,
1822
- _requestId,
1823
- onCheckpoint,
1824
- ) => {
1825
- // Prime the assistant row anchor — production code emits this from
1826
- // `AgentLoop.run` just before `provider.sendMessage`.
1827
- await onEvent({ type: "llm_call_started" });
1828
- agentLoopCallCount++;
1829
-
1830
- // Every call: simulate tool progress then yield at checkpoint
1831
- const withProgress: Message[] = [
1832
- ...messages,
1833
- {
1834
- role: "assistant" as const,
1835
- content: [
1836
- { type: "text", text: `Tool call ${agentLoopCallCount}` },
1837
- {
1838
- type: "tool_use",
1839
- id: `tu-${agentLoopCallCount}`,
1840
- name: "bash",
1841
- input: { command: "ls" },
1842
- },
1843
- ] as ContentBlock[],
1844
- },
1845
- {
1846
- role: "user" as const,
1847
- content: [
1848
- {
1849
- type: "tool_result",
1850
- tool_use_id: `tu-${agentLoopCallCount}`,
1851
- content: "output",
1852
- is_error: false,
1853
- },
1854
- ] as ContentBlock[],
1855
- },
1856
- ];
1857
-
1858
- onEvent({
1859
- type: "message_complete",
1860
- message: {
1861
- role: "assistant",
1862
- content: [
1863
- { type: "text", text: `Tool call ${agentLoopCallCount}` },
1864
- {
1865
- type: "tool_use",
1866
- id: `tu-${agentLoopCallCount}`,
1867
- name: "bash",
1868
- input: { command: "ls" },
1869
- },
1870
- ],
1871
- },
1872
- });
1873
- onEvent({
1874
- type: "usage",
1875
- inputTokens: 100,
1876
- outputTokens: 50,
1877
- model: "test-model",
1878
- providerDurationMs: 100,
1879
- });
1880
-
1881
- // Always yield at checkpoint — simulates compaction not helping
1882
- if (onCheckpoint) {
1883
- const decision = await onCheckpoint({
1884
- turnIndex: 0,
1885
- toolCount: 1,
1886
- hasToolUse: true,
1887
- history: withProgress,
1888
- });
1889
- if (decision === "yield") {
1890
- return withProgress;
1891
- }
1892
- }
1893
-
1894
- return withProgress;
1895
- };
1896
-
1897
- let compactionCallCount = 0;
1898
- // Convergence reducer: reduce tokens enough to succeed
1368
+ // The convergence reducer reduces tokens enough for the rerun to recover.
1899
1369
  let convergenceReducerCalled = false;
1900
1370
  mockReducerStepFn = (msgs: Message[]) => {
1901
1371
  convergenceReducerCalled = true;
@@ -1911,15 +1381,43 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1911
1381
  };
1912
1382
  };
1913
1383
 
1384
+ // Every provider call returns a tool_use, so each loop run does a tool
1385
+ // turn that trips the mid-loop budget gate. On the initial run the gate
1386
+ // calls compaction (which surfaces `exhausted: true`); the convergence
1387
+ // rerun runs without a compaction hook and yields "budget" directly.
1388
+ // With the reducer exhausted, the convergence loop terminates with the
1389
+ // turn still over budget and the orchestrator stamps `context_too_large`.
1390
+ const { provider, calls } = createMockProvider([
1391
+ toolUseResponse("tu-1", "bash", { command: "ls" }),
1392
+ ]);
1393
+
1394
+ let compactionCallCount = 0;
1914
1395
  const ctx = makeCtx({
1915
- agentLoopRun,
1396
+ loopProvider: provider,
1397
+ loopTools: [
1398
+ {
1399
+ name: "bash",
1400
+ description: "Run a shell command",
1401
+ input_schema: {
1402
+ type: "object",
1403
+ properties: { command: { type: "string" } },
1404
+ },
1405
+ },
1406
+ ],
1407
+ toolExecutor: async () => ({ content: "output", isError: false }),
1916
1408
  contextWindowManager: {
1917
1409
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1918
1410
  maybeCompact: async () => {
1919
1411
  compactionCallCount++;
1920
- // Compaction "succeeds" but doesn't actually shrink enough
1412
+ // Compaction's internal retry budget is exhausted — the
1413
+ // compactor itself ran maxAttempts passes and still couldn't
1414
+ // drop below the auto-threshold. `maybeCompact` surfaces this
1415
+ // via `exhausted: true` so the loop yields "budget" and the
1416
+ // orchestrator escalates straight to the convergence loop
1417
+ // instead of looping on a stuck compactor.
1921
1418
  return {
1922
1419
  compacted: true,
1420
+ exhausted: true,
1923
1421
  messages: [
1924
1422
  {
1925
1423
  role: "user" as const,
@@ -1944,14 +1442,20 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1944
1442
 
1945
1443
  await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
1946
1444
 
1947
- // 1 initial auto-compact + 3 mid-loop compaction attempts = 4 total
1948
- expect(compactionCallCount).toBe(4);
1445
+ // 1 initial auto-compact + 1 mid-loop compaction = 2 total. The
1446
+ // first mid-loop call surfaces `exhausted: true`, so the
1447
+ // orchestrator escalates immediately without retrying maybeCompact
1448
+ // — the retry budget for the compactor itself lives inside
1449
+ // `ContextWindowManager.maybeCompact`.
1450
+ expect(compactionCallCount).toBe(2);
1949
1451
 
1950
- // Agent loop: 1 initial + 3 mid-loop re-entries + 1 convergence re-run = 5 calls
1951
- expect(agentLoopCallCount).toBe(5);
1452
+ // Provider calls: 1 initial tool turn (yields budget) + 1 convergence
1453
+ // rerun that recovers. No mid-loop re-entries because the orchestrator
1454
+ // broke out on `exhausted` before re-invoking the loop.
1455
+ expect(calls.length).toBe(2);
1952
1456
 
1953
- // After exhausting mid-loop attempts, the convergence loop should
1954
- // have been triggered (contextTooLargeDetected set to true)
1457
+ // After the compactor exhausted itself, the convergence loop
1458
+ // should have been triggered (contextTooLargeDetected set to true)
1955
1459
  expect(convergenceReducerCalled).toBe(true);
1956
1460
  expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
1957
1461
  "test-conv",
@@ -1959,6 +1463,102 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1959
1463
  );
1960
1464
  });
1961
1465
 
1466
+ // ── Test 8b ───────────────────────────────────────────────────────
1467
+ // Counterpart to Test 8: when a mid-loop `maybeCompact` returns
1468
+ // productive (`compacted: true`, no `exhausted` flag), the loop
1469
+ // compacts in place and continues the run itself — it never yields
1470
+ // "budget", so the orchestrator does not escalate to the convergence
1471
+ // loop. Mid-loop iteration is now wholly internal to `AgentLoop.run`;
1472
+ // the orchestrator only reacts to the binary `exhausted`/timeout
1473
+ // signal carried back as a "budget" exit.
1474
+ test("productive mid-loop compaction continues in place without escalating", async () => {
1475
+ const events: ServerMessage[] = [];
1476
+
1477
+ // Budget = 200_000 * 0.95 = 190_000
1478
+ // Mid-loop threshold = 190_000 * 0.85 = 161_500
1479
+ let estimateCallCount = 0;
1480
+ mockEstimateTokens = () => {
1481
+ estimateCallCount++;
1482
+ // Preflight: below budget.
1483
+ if (estimateCallCount === 1) return 100_000;
1484
+ // Every checkpoint estimate: above threshold — always trips the
1485
+ // yield. Simulates a long turn where each tool call's result
1486
+ // inflates the context past 85% even after a successful compaction.
1487
+ return 170_000;
1488
+ };
1489
+
1490
+ // A single tool round reaches one checkpoint; the in-loop budget gate
1491
+ // trips there and compaction runs in place. The loop continues the run
1492
+ // itself — the following provider call returns plain text and the turn
1493
+ // completes — so the orchestrator never re-enters the convergence loop.
1494
+ const { provider, calls } = createMockProvider([
1495
+ toolUseResponse("tu-1", "bash", { command: "ls" }),
1496
+ textResponse("final answer"),
1497
+ ]);
1498
+
1499
+ // Compaction reports `estimatedInputTokens` well below the 161_500
1500
+ // threshold — the "compaction is productive" signal (no `exhausted`
1501
+ // flag) that lets the loop continue in place.
1502
+ let compactionCallCount = 0;
1503
+ const ctx = makeCtx({
1504
+ loopProvider: provider,
1505
+ loopTools: [
1506
+ {
1507
+ name: "bash",
1508
+ description: "Run a shell command",
1509
+ input_schema: {
1510
+ type: "object",
1511
+ properties: { command: { type: "string" } },
1512
+ },
1513
+ },
1514
+ ],
1515
+ toolExecutor: async () => ({ content: "output", isError: false }),
1516
+ contextWindowManager: {
1517
+ shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1518
+ maybeCompact: async () => {
1519
+ compactionCallCount++;
1520
+ return {
1521
+ compacted: true,
1522
+ messages: [
1523
+ {
1524
+ role: "user" as const,
1525
+ content: [{ type: "text", text: "Hello" }],
1526
+ },
1527
+ ] as Message[],
1528
+ compactedPersistedMessages: 5,
1529
+ summaryText: "Compaction summary",
1530
+ previousEstimatedInputTokens: 170_000,
1531
+ estimatedInputTokens: 100_000,
1532
+ maxInputTokens: 200_000,
1533
+ thresholdTokens: 160_000,
1534
+ compactedMessages: 10,
1535
+ summaryCalls: 1,
1536
+ summaryInputTokens: 500,
1537
+ summaryOutputTokens: 200,
1538
+ summaryModel: "mock-model",
1539
+ };
1540
+ },
1541
+ } as unknown as AgentLoopConversationContext["contextWindowManager"],
1542
+ });
1543
+
1544
+ await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
1545
+
1546
+ // 1 initial auto-compact + 1 productive mid-loop compaction.
1547
+ expect(compactionCallCount).toBe(2);
1548
+ // The loop continued in place after compacting: a tool turn followed by
1549
+ // the post-compaction text turn, both within a single run.
1550
+ expect(calls.length).toBe(2);
1551
+
1552
+ // No escalation to the convergence loop because the mid-loop
1553
+ // `maybeCompact` returned productive (no `exhausted` flag), and the turn
1554
+ // completed normally.
1555
+ expect(setAgentLoopExitReasonOnLatestLogMock).not.toHaveBeenCalledWith(
1556
+ "test-conv",
1557
+ "context_too_large",
1558
+ );
1559
+ expect(events.find((e) => e.type === "conversation_error")).toBeUndefined();
1560
+ });
1561
+
1962
1562
  // ── Test 9 ────────────────────────────────────────────────────────
1963
1563
  // When the convergence loop reruns the agent loop and it still yields
1964
1564
  // at checkpoint (yieldedForBudget), the loop must continue reducing
@@ -1978,84 +1578,13 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
1978
1578
  return 170_000;
1979
1579
  };
1980
1580
 
1981
- let agentLoopCallCount = 0;
1982
- const agentLoopRun: AgentLoopRun = async (
1983
- messages,
1984
- onEvent,
1985
- _signal,
1986
- _requestId,
1987
- onCheckpoint,
1988
- ) => {
1989
- // Prime the assistant row anchor — production code emits this from
1990
- // `AgentLoop.run` just before `provider.sendMessage`.
1991
- await onEvent({ type: "llm_call_started" });
1992
- agentLoopCallCount++;
1993
-
1994
- const withProgress: Message[] = [
1995
- ...messages,
1996
- {
1997
- role: "assistant" as const,
1998
- content: [
1999
- { type: "text", text: `Tool call ${agentLoopCallCount}` },
2000
- {
2001
- type: "tool_use",
2002
- id: `tu-${agentLoopCallCount}`,
2003
- name: "bash",
2004
- input: { command: "ls" },
2005
- },
2006
- ] as ContentBlock[],
2007
- },
2008
- {
2009
- role: "user" as const,
2010
- content: [
2011
- {
2012
- type: "tool_result",
2013
- tool_use_id: `tu-${agentLoopCallCount}`,
2014
- content: "output",
2015
- is_error: false,
2016
- },
2017
- ] as ContentBlock[],
2018
- },
2019
- ];
2020
-
2021
- onEvent({
2022
- type: "message_complete",
2023
- message: {
2024
- role: "assistant",
2025
- content: [
2026
- { type: "text", text: `Tool call ${agentLoopCallCount}` },
2027
- {
2028
- type: "tool_use",
2029
- id: `tu-${agentLoopCallCount}`,
2030
- name: "bash",
2031
- input: { command: "ls" },
2032
- },
2033
- ],
2034
- },
2035
- });
2036
- onEvent({
2037
- type: "usage",
2038
- inputTokens: 100,
2039
- outputTokens: 50,
2040
- model: "test-model",
2041
- providerDurationMs: 100,
2042
- });
2043
-
2044
- // Always yield at checkpoint — simulates reduction not helping enough
2045
- if (onCheckpoint) {
2046
- const decision = await onCheckpoint({
2047
- turnIndex: 0,
2048
- toolCount: 1,
2049
- hasToolUse: true,
2050
- history: withProgress,
2051
- });
2052
- if (decision === "yield") {
2053
- return withProgress;
2054
- }
2055
- }
2056
-
2057
- return withProgress;
2058
- };
1581
+ // Every provider call returns a tool_use, so each loop run does a tool
1582
+ // turn that trips the mid-loop budget gate and yields "budget". The
1583
+ // initial run's gate calls compaction (exhausted); the convergence
1584
+ // reruns run without a compaction hook and yield directly.
1585
+ const { provider, calls } = createMockProvider([
1586
+ toolUseResponse("tu-1", "bash", { command: "ls" }),
1587
+ ]);
2059
1588
 
2060
1589
  // Convergence reducer: first call returns non-exhausted, second returns exhausted
2061
1590
  let reducerCallCount = 0;
@@ -2087,9 +1616,25 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
2087
1616
  };
2088
1617
 
2089
1618
  const ctx = makeCtx({
2090
- agentLoopRun,
1619
+ loopProvider: provider,
1620
+ loopTools: [
1621
+ {
1622
+ name: "bash",
1623
+ description: "Run a shell command",
1624
+ input_schema: {
1625
+ type: "object",
1626
+ properties: { command: { type: "string" } },
1627
+ },
1628
+ },
1629
+ ],
1630
+ toolExecutor: async () => ({ content: "output", isError: false }),
2091
1631
  contextWindowManager: {
2092
1632
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
1633
+ // Under the new architecture (Compaction Re-homing Arc, Bullet 1)
1634
+ // the retry budget lives inside `ContextWindowManager._maybeCompact`,
1635
+ // so a single daemon-level call represents the full manager retry
1636
+ // sequence. Signal `exhausted: true` immediately to escalate the
1637
+ // mid-loop to the convergence reducer.
2093
1638
  maybeCompact: async () => ({
2094
1639
  compacted: true,
2095
1640
  messages: [
@@ -2109,6 +1654,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
2109
1654
  summaryInputTokens: 500,
2110
1655
  summaryOutputTokens: 200,
2111
1656
  summaryModel: "mock-model",
1657
+ exhausted: true,
2112
1658
  }),
2113
1659
  } as unknown as AgentLoopConversationContext["contextWindowManager"],
2114
1660
  });
@@ -2119,8 +1665,11 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
2119
1665
  // once more after yieldedForBudget triggered re-entry
2120
1666
  expect(reducerCallCount).toBe(2);
2121
1667
 
2122
- // Agent loop: 1 initial + 3 mid-loop re-entries + 2 convergence re-runs = 6 calls
2123
- expect(agentLoopCallCount).toBe(6);
1668
+ // Provider calls: 1 initial run + 2 convergence reruns = 3 calls, each a
1669
+ // tool turn that yields "budget". The mid-loop no longer drives
1670
+ // daemon-level retries — the manager owns its retry budget and signals
1671
+ // exhaustion via the `exhausted` flag.
1672
+ expect(calls.length).toBe(3);
2124
1673
  expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
2125
1674
  "test-conv",
2126
1675
  "context_too_large",
@@ -2220,35 +1769,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
2220
1769
  };
2221
1770
  };
2222
1771
 
2223
- const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
2224
- // Prime the assistant row anchor production code emits this from
2225
- // `AgentLoop.run` just before `provider.sendMessage`.
2226
- await onEvent({ type: "llm_call_started" });
2227
- onEvent({
2228
- type: "message_complete",
2229
- message: {
2230
- role: "assistant",
2231
- content: [{ type: "text", text: "done" }],
2232
- },
2233
- });
2234
- onEvent({
2235
- type: "usage",
2236
- inputTokens: 170_000,
2237
- outputTokens: 200,
2238
- model: "test-model",
2239
- providerDurationMs: 500,
2240
- });
2241
- return [
2242
- ...messages,
2243
- {
2244
- role: "assistant" as const,
2245
- content: [{ type: "text", text: "done" }] as ContentBlock[],
2246
- },
2247
- ];
2248
- };
2249
-
1772
+ // The preflight overflow reducer runs in the orchestrator before the loop,
1773
+ // so a single successful provider turn is enough to drive the path.
2250
1774
  const ctx = makeCtx({
2251
- agentLoopRun,
1775
+ providerResponses: [textResponse("done")],
2252
1776
  contextWindowManager: {
2253
1777
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
2254
1778
  maybeCompact: async () => ({ compacted: false }),
@@ -2319,111 +1843,100 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
2319
1843
  // emergency compaction + final agentLoop.run path executes.
2320
1844
  mockOverflowAction = "auto_compress_latest_turn";
2321
1845
 
2322
- let agentLoopCallCount = 0;
2323
- const agentLoopRun: AgentLoopRun = async (
2324
- messages,
2325
- onEvent,
2326
- _signal,
2327
- _requestId,
2328
- onCheckpoint,
2329
- ) => {
2330
- // Prime the assistant row anchor — production code emits this from
2331
- // `AgentLoop.run` just before `provider.sendMessage`.
2332
- await onEvent({ type: "llm_call_started" });
2333
- agentLoopCallCount++;
2334
-
2335
- const withProgress: Message[] = [
2336
- ...messages,
2337
- {
2338
- role: "assistant" as const,
2339
- content: [
2340
- { type: "text", text: `tool call ${agentLoopCallCount}` },
2341
- {
2342
- type: "tool_use",
2343
- id: `tu-${agentLoopCallCount}`,
2344
- name: "bash",
2345
- input: { command: "ls" },
2346
- },
2347
- ] as ContentBlock[],
2348
- },
2349
- {
2350
- role: "user" as const,
2351
- content: [
2352
- {
2353
- type: "tool_result",
2354
- tool_use_id: `tu-${agentLoopCallCount}`,
2355
- content: "output",
2356
- is_error: false,
2357
- },
2358
- ] as ContentBlock[],
2359
- },
2360
- ];
2361
-
2362
- onEvent({
2363
- type: "message_complete",
2364
- message: {
2365
- role: "assistant",
2366
- content: [
2367
- { type: "text", text: `tool call ${agentLoopCallCount}` },
2368
- {
2369
- type: "tool_use",
2370
- id: `tu-${agentLoopCallCount}`,
2371
- name: "bash",
2372
- input: { command: "ls" },
2373
- },
2374
- ],
2375
- },
2376
- });
2377
- onEvent({
2378
- type: "usage",
2379
- inputTokens: 100,
2380
- outputTokens: 50,
2381
- model: "test-model",
2382
- providerDurationMs: 100,
2383
- });
2384
-
2385
- // Every checkpoint yields — including the final auto_compress rerun.
2386
- if (onCheckpoint) {
2387
- const decision = await onCheckpoint({
2388
- turnIndex: 0,
2389
- toolCount: 1,
2390
- hasToolUse: true,
2391
- history: withProgress,
2392
- });
2393
- if (decision === "yield") {
2394
- return withProgress;
2395
- }
2396
- }
2397
-
2398
- return withProgress;
2399
- };
1846
+ // Every provider call returns a tool_use, so each loop run does a tool
1847
+ // turn that trips the mid-loop budget gate and yields "budget" —
1848
+ // including the final auto_compress rerun.
1849
+ const { provider } = createMockProvider([
1850
+ toolUseResponse("tu-1", "bash", { command: "ls" }),
1851
+ ]);
2400
1852
 
1853
+ // `maybeCompact` is invoked through three distinct call sites:
1854
+ // 1. Start-of-turn compaction (no `force` option) — return a no-op
1855
+ // so the start-of-turn pass doesn't perturb state. The mock's
1856
+ // `shouldCompact` already returns `needed: false`, but the
1857
+ // orchestrator still invokes the compaction pipeline.
1858
+ // 2. Mid-loop after the initial agent-loop yield (`force: true`) —
1859
+ // must signal `exhausted: true` so the daemon escalates to the
1860
+ // convergence reducer instead of looping forever.
1861
+ // 3. auto_compress_latest_turn emergency compaction (`force: true`,
1862
+ // `minKeepRecentUserTurns: 0`) — succeeds and drops tokens below
1863
+ // threshold; the subsequent rerun yields again and is classified
1864
+ // as BUDGET_YIELD_UNRECOVERED.
1865
+ let forcedMaybeCompactCallCount = 0;
2401
1866
  const ctx = makeCtx({
2402
- agentLoopRun,
1867
+ loopProvider: provider,
1868
+ loopTools: [
1869
+ {
1870
+ name: "bash",
1871
+ description: "Run a shell command",
1872
+ input_schema: {
1873
+ type: "object",
1874
+ properties: { command: { type: "string" } },
1875
+ },
1876
+ },
1877
+ ],
1878
+ toolExecutor: async () => ({ content: "output", isError: false }),
2403
1879
  contextWindowManager: {
2404
1880
  shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
2405
- // The compaction pipeline (default terminal) routes through this
2406
- // for the emergency `auto_compress_latest_turn` path.
2407
- maybeCompact: async () => ({
2408
- compacted: true,
2409
- messages: [
2410
- {
2411
- role: "user" as const,
2412
- content: [{ type: "text", text: "compacted" }],
2413
- },
2414
- ] as Message[],
2415
- compactedPersistedMessages: 5,
2416
- summaryText: "Emergency summary",
2417
- previousEstimatedInputTokens: 170_000,
2418
- estimatedInputTokens: 90_000,
2419
- maxInputTokens: 200_000,
2420
- thresholdTokens: 160_000,
2421
- compactedMessages: 10,
2422
- summaryCalls: 1,
2423
- summaryInputTokens: 500,
2424
- summaryOutputTokens: 200,
2425
- summaryModel: "mock-model",
2426
- }),
1881
+ maybeCompact: async (
1882
+ _msgs: Message[],
1883
+ _signal: AbortSignal,
1884
+ opts?: { force?: boolean },
1885
+ ) => {
1886
+ // Start-of-turn calls pass no `force` option; route them to a
1887
+ // no-op so only the mid-loop and emergency paths drive the test.
1888
+ if (!opts?.force) {
1889
+ return { compacted: false };
1890
+ }
1891
+ forcedMaybeCompactCallCount++;
1892
+ if (forcedMaybeCompactCallCount === 1) {
1893
+ // Mid-loop call — under the new architecture (Compaction
1894
+ // Re-homing Arc, Bullet 1) the manager owns its own retry
1895
+ // budget; signal exhaustion to escalate to convergence.
1896
+ return {
1897
+ compacted: true,
1898
+ messages: [
1899
+ {
1900
+ role: "user" as const,
1901
+ content: [{ type: "text", text: "mid-loop compacted" }],
1902
+ },
1903
+ ] as Message[],
1904
+ compactedPersistedMessages: 5,
1905
+ summaryText: "Mid-loop summary",
1906
+ previousEstimatedInputTokens: 170_000,
1907
+ estimatedInputTokens: 165_000,
1908
+ maxInputTokens: 200_000,
1909
+ thresholdTokens: 160_000,
1910
+ compactedMessages: 10,
1911
+ summaryCalls: 1,
1912
+ summaryInputTokens: 500,
1913
+ summaryOutputTokens: 200,
1914
+ summaryModel: "mock-model",
1915
+ exhausted: true,
1916
+ };
1917
+ }
1918
+ // Emergency compaction call from auto_compress_latest_turn.
1919
+ return {
1920
+ compacted: true,
1921
+ messages: [
1922
+ {
1923
+ role: "user" as const,
1924
+ content: [{ type: "text", text: "compacted" }],
1925
+ },
1926
+ ] as Message[],
1927
+ compactedPersistedMessages: 5,
1928
+ summaryText: "Emergency summary",
1929
+ previousEstimatedInputTokens: 170_000,
1930
+ estimatedInputTokens: 90_000,
1931
+ maxInputTokens: 200_000,
1932
+ thresholdTokens: 160_000,
1933
+ compactedMessages: 10,
1934
+ summaryCalls: 1,
1935
+ summaryInputTokens: 500,
1936
+ summaryOutputTokens: 200,
1937
+ summaryModel: "mock-model",
1938
+ };
1939
+ },
2427
1940
  } as unknown as AgentLoopConversationContext["contextWindowManager"],
2428
1941
  });
2429
1942