@vellumai/assistant 0.8.6 → 0.8.7

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 (891) hide show
  1. package/AGENTS.md +4 -4
  2. package/Dockerfile +1 -0
  3. package/bun.lock +11 -2
  4. package/docker-entrypoint.sh +8 -6
  5. package/docs/plugins.md +63 -28
  6. package/examples/plugins/echo/register.ts +4 -7
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/environments/bun.lock +24 -0
  9. package/node_modules/@vellumai/environments/package.json +18 -0
  10. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
  11. package/node_modules/@vellumai/environments/src/index.ts +11 -0
  12. package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
  13. package/node_modules/@vellumai/environments/src/types.ts +70 -0
  14. package/node_modules/@vellumai/environments/tsconfig.json +20 -0
  15. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
  16. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
  17. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +6 -2
  18. package/openapi.yaml +3735 -353
  19. package/package.json +7 -3
  20. package/scripts/generate-openapi.ts +20 -13
  21. package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
  22. package/src/__tests__/agent-loop-exit-reason.test.ts +240 -39
  23. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
  24. package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
  25. package/src/__tests__/agent-loop-provider-error-recording.test.ts +6 -4
  26. package/src/__tests__/agent-loop-thinking.test.ts +17 -12
  27. package/src/__tests__/agent-loop.test.ts +207 -341
  28. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +4 -2
  29. package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
  30. package/src/__tests__/anthropic-provider.test.ts +201 -55
  31. package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
  32. package/src/__tests__/app-control-flow.test.ts +5 -0
  33. package/src/__tests__/approval-cascade.test.ts +4 -11
  34. package/src/__tests__/approval-routes-http.test.ts +4 -2
  35. package/src/__tests__/assistant-event.test.ts +15 -0
  36. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  37. package/src/__tests__/avatar-e2e.test.ts +7 -37
  38. package/src/__tests__/avatar-generator.test.ts +12 -42
  39. package/src/__tests__/avatar-identity-sync.test.ts +28 -3
  40. package/src/__tests__/background-shell-bash.test.ts +3 -7
  41. package/src/__tests__/btw-routes.test.ts +7 -12
  42. package/src/__tests__/call-pointer-messages.test.ts +5 -3
  43. package/src/__tests__/call-site-routing-provider.test.ts +22 -40
  44. package/src/__tests__/catalog-files.test.ts +1 -0
  45. package/src/__tests__/channel-approval-routes.test.ts +48 -20
  46. package/src/__tests__/channel-approvals.test.ts +3 -1
  47. package/src/__tests__/channel-invite-transport.test.ts +1 -5
  48. package/src/__tests__/channel-readiness-routes.test.ts +0 -4
  49. package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
  50. package/src/__tests__/channel-retry-sweep.test.ts +71 -79
  51. package/src/__tests__/circuit-breaker-pipeline.test.ts +3 -3
  52. package/src/__tests__/clawhub-files.test.ts +1 -0
  53. package/src/__tests__/compaction-events.test.ts +5 -17
  54. package/src/__tests__/compaction-pipeline.test.ts +1 -1
  55. package/src/__tests__/compaction-timeout-recovery.test.ts +37 -48
  56. package/src/__tests__/compaction-trail-store.test.ts +1 -79
  57. package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
  58. package/src/__tests__/computer-use-tools.test.ts +2 -2
  59. package/src/__tests__/config-watcher.test.ts +28 -0
  60. package/src/__tests__/context-search-agent-runner.test.ts +6 -3
  61. package/src/__tests__/context-token-estimator.test.ts +34 -0
  62. package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
  63. package/src/__tests__/conversation-abort-tool-results.test.ts +14 -7
  64. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -2
  65. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +12 -27
  66. package/src/__tests__/conversation-agent-loop-overflow.test.ts +430 -90
  67. package/src/__tests__/conversation-agent-loop.test.ts +581 -62
  68. package/src/__tests__/conversation-analysis-routes.test.ts +1 -3
  69. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  70. package/src/__tests__/conversation-clear-safety.test.ts +20 -10
  71. package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
  72. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  73. package/src/__tests__/conversation-disk-view.test.ts +10 -17
  74. package/src/__tests__/conversation-fork-crud.test.ts +86 -172
  75. package/src/__tests__/conversation-fork-route.test.ts +16 -14
  76. package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
  77. package/src/__tests__/conversation-lifecycle.test.ts +3 -2
  78. package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
  79. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
  80. package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
  81. package/src/__tests__/conversation-pairing.test.ts +34 -4
  82. package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
  83. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -0
  84. package/src/__tests__/conversation-process-callsite.test.ts +27 -30
  85. package/src/__tests__/conversation-provider-retry-repair.test.ts +53 -44
  86. package/src/__tests__/conversation-queue.test.ts +270 -164
  87. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -2
  88. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  89. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -2
  90. package/src/__tests__/conversation-runtime-assembly.test.ts +20 -22
  91. package/src/__tests__/conversation-runtime-workspace.test.ts +19 -1
  92. package/src/__tests__/conversation-slash-queue.test.ts +37 -31
  93. package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
  94. package/src/__tests__/conversation-speed-override.test.ts +8 -22
  95. package/src/__tests__/conversation-stream-state.test.ts +484 -0
  96. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +6 -15
  97. package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
  98. package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
  99. package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
  100. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
  101. package/src/__tests__/conversation-unread-route.test.ts +14 -2
  102. package/src/__tests__/conversation-usage.test.ts +0 -2
  103. package/src/__tests__/conversation-wipe.test.ts +1 -1
  104. package/src/__tests__/conversation-workspace-cache-state.test.ts +3 -1
  105. package/src/__tests__/conversation-workspace-injection.test.ts +48 -22
  106. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +27 -7
  107. package/src/__tests__/credential-execution-tools.test.ts +1 -2
  108. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  109. package/src/__tests__/cross-provider-web-search.test.ts +6 -2
  110. package/src/__tests__/cu-unified-flow.test.ts +26 -1
  111. package/src/__tests__/db-schedule-syntax-migration.test.ts +11 -0
  112. package/src/__tests__/disk-pressure-guard.test.ts +66 -0
  113. package/src/__tests__/disk-pressure-routes.test.ts +9 -2
  114. package/src/__tests__/dm-persistence.test.ts +7 -2
  115. package/src/__tests__/dynamic-page-surface.test.ts +68 -0
  116. package/src/__tests__/edit-propagation.test.ts +1 -2
  117. package/src/__tests__/empty-response-pipeline.test.ts +127 -5
  118. package/src/__tests__/filing-service.test.ts +2 -2
  119. package/src/__tests__/first-greeting.test.ts +55 -14
  120. package/src/__tests__/gemini-inline-media.test.ts +78 -0
  121. package/src/__tests__/gemini-provider.test.ts +351 -28
  122. package/src/__tests__/guardian-routing-state.test.ts +60 -71
  123. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +9 -7
  124. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  125. package/src/__tests__/heartbeat-service.test.ts +2 -1
  126. package/src/__tests__/history-repair-hook.test.ts +161 -0
  127. package/src/__tests__/history-repair-observability.test.ts +1 -1
  128. package/src/__tests__/history-repair.test.ts +2 -1
  129. package/src/__tests__/host-app-control-proxy.test.ts +2 -0
  130. package/src/__tests__/host-cu-proxy.test.ts +2 -0
  131. package/src/__tests__/host-file-edit-tool.test.ts +4 -2
  132. package/src/__tests__/host-file-proxy.test.ts +31 -0
  133. package/src/__tests__/host-file-read-tool.test.ts +4 -2
  134. package/src/__tests__/host-file-write-tool.test.ts +9 -3
  135. package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
  136. package/src/__tests__/host-shell-tool.test.ts +9 -4
  137. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  138. package/src/__tests__/identity-intro-cache.test.ts +35 -14
  139. package/src/__tests__/inbound-slack-persistence.test.ts +7 -2
  140. package/src/__tests__/injector-background-turn.test.ts +1 -1
  141. package/src/__tests__/injector-chain.test.ts +1 -1
  142. package/src/__tests__/injector-disk-pressure.test.ts +1 -1
  143. package/src/__tests__/injector-document-comments.test.ts +1 -1
  144. package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
  145. package/src/__tests__/injector-v3-suppression.test.ts +220 -0
  146. package/src/__tests__/list-messages-attachments.test.ts +7 -8
  147. package/src/__tests__/list-messages-hidden-metadata.test.ts +17 -15
  148. package/src/__tests__/list-messages-page-latest.test.ts +0 -1
  149. package/src/__tests__/list-messages-tool-merge.test.ts +36 -6
  150. package/src/__tests__/llm-call-pipeline.test.ts +21 -15
  151. package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
  152. package/src/__tests__/llm-resolver.test.ts +23 -47
  153. package/src/__tests__/llm-usage-store.test.ts +45 -0
  154. package/src/__tests__/log-export-routes.test.ts +59 -0
  155. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
  156. package/src/__tests__/mcp-auth-routes.test.ts +15 -10
  157. package/src/__tests__/mcp-health-check.test.ts +18 -13
  158. package/src/__tests__/memory-retrieval-pipeline.test.ts +1 -1
  159. package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
  160. package/src/__tests__/messaging-send-tool.test.ts +8 -4
  161. package/src/__tests__/migration-export-http.test.ts +12 -12
  162. package/src/__tests__/migration-import-commit-http.test.ts +8 -8
  163. package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
  164. package/src/__tests__/migration-validate-http.test.ts +3 -3
  165. package/src/__tests__/native-web-search.test.ts +14 -20
  166. package/src/__tests__/notification-decision-identity.test.ts +9 -18
  167. package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
  168. package/src/__tests__/oauth-commands-routes.test.ts +1 -1
  169. package/src/__tests__/onboarding-template-contract.test.ts +10 -0
  170. package/src/__tests__/openai-provider.test.ts +66 -70
  171. package/src/__tests__/openai-responses-provider.test.ts +21 -77
  172. package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
  173. package/src/__tests__/overflow-reduce-pipeline.test.ts +2 -4
  174. package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
  175. package/src/__tests__/persistence-pipeline.test.ts +15 -26
  176. package/src/__tests__/persistence-secret-redaction.test.ts +2 -1
  177. package/src/__tests__/pipeline-runner.test.ts +2 -3
  178. package/src/__tests__/plugin-bootstrap.test.ts +51 -25
  179. package/src/__tests__/plugin-route-contribution.test.ts +6 -16
  180. package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
  181. package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
  182. package/src/__tests__/plugin-types.test.ts +7 -14
  183. package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
  184. package/src/__tests__/process-message-background-slack.test.ts +17 -16
  185. package/src/__tests__/process-message-display-content.test.ts +30 -42
  186. package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
  187. package/src/__tests__/provider-error-scenarios.test.ts +7 -6
  188. package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
  189. package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
  190. package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
  191. package/src/__tests__/provider-usage-tracking.test.ts +0 -6
  192. package/src/__tests__/ratelimit.test.ts +9 -4
  193. package/src/__tests__/relay-server.test.ts +20 -13
  194. package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
  195. package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
  196. package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
  197. package/src/__tests__/runtime-events-sse-reconnect.test.ts +353 -0
  198. package/src/__tests__/schedule-routes.test.ts +80 -10
  199. package/src/__tests__/schedule-store.test.ts +67 -0
  200. package/src/__tests__/schedule-tools.test.ts +125 -0
  201. package/src/__tests__/secret-ingress-http.test.ts +2 -2
  202. package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
  203. package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
  204. package/src/__tests__/secret-response-routing.test.ts +13 -11
  205. package/src/__tests__/send-endpoint-busy.test.ts +2 -1
  206. package/src/__tests__/shell-observability.test.ts +249 -0
  207. package/src/__tests__/skill-feature-flags-integration.test.ts +11 -11
  208. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  209. package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
  210. package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
  211. package/src/__tests__/skillssh-files.test.ts +1 -0
  212. package/src/__tests__/starter-task-flow.test.ts +6 -6
  213. package/src/__tests__/strip-memory-injections.test.ts +102 -14
  214. package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
  215. package/src/__tests__/suggestion-routes.test.ts +3 -3
  216. package/src/__tests__/sync-message-contract.test.ts +19 -16
  217. package/src/__tests__/system-prompt.test.ts +54 -0
  218. package/src/__tests__/terminal-tools.test.ts +3 -24
  219. package/src/__tests__/thread-backfill.test.ts +4 -9
  220. package/src/__tests__/title-generate-pipeline.test.ts +1 -1
  221. package/src/__tests__/token-estimate-pipeline.test.ts +2 -4
  222. package/src/__tests__/tool-error-pipeline.test.ts +2 -2
  223. package/src/__tests__/tool-execute-pipeline.test.ts +1 -1
  224. package/src/__tests__/tool-preview-lifecycle.test.ts +13 -11
  225. package/src/__tests__/tool-result-truncate-pipeline.test.ts +9 -12
  226. package/src/__tests__/tool-result-truncation.test.ts +3 -1
  227. package/src/__tests__/tools-audio-read.test.ts +113 -0
  228. package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
  229. package/src/__tests__/turn-events-store.test.ts +11 -7
  230. package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
  231. package/src/__tests__/voice-session-bridge.test.ts +13 -7
  232. package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
  233. package/src/acp/prepare-agent-env.ts +52 -11
  234. package/src/agent/compaction-circuit.ts +140 -0
  235. package/src/agent/loop.ts +409 -85
  236. package/src/api/README.md +19 -17
  237. package/src/api/constants/tool-execution.ts +21 -0
  238. package/src/api/events/assistant-activity-state.ts +75 -0
  239. package/src/api/events/assistant-outbound-attachment.ts +25 -27
  240. package/src/api/events/assistant-text-delta.ts +6 -8
  241. package/src/api/events/assistant-turn-start.ts +5 -7
  242. package/src/api/events/avatar-updated.ts +24 -0
  243. package/src/api/events/compaction-circuit-closed.ts +26 -0
  244. package/src/api/events/compaction-circuit-open.ts +28 -0
  245. package/src/api/events/confirmation-request.ts +114 -0
  246. package/src/api/events/contact-request.ts +33 -0
  247. package/src/api/events/conversation-error.ts +77 -0
  248. package/src/api/events/conversation-list-invalidated.ts +38 -0
  249. package/src/api/events/conversation-title-updated.ts +24 -0
  250. package/src/api/events/disk-pressure-status-changed.ts +61 -0
  251. package/src/api/events/document-comment-created.ts +24 -28
  252. package/src/api/events/document-comment-deleted.ts +6 -8
  253. package/src/api/events/document-comment-reopened.ts +6 -8
  254. package/src/api/events/document-comment-resolved.ts +8 -10
  255. package/src/api/events/document-editor-update.ts +27 -0
  256. package/src/api/events/error.ts +32 -0
  257. package/src/api/events/generation-cancelled.ts +4 -6
  258. package/src/api/events/generation-handoff.ts +13 -15
  259. package/src/api/events/home-feed-updated.ts +26 -0
  260. package/src/api/events/identity-changed.ts +32 -0
  261. package/src/api/events/interaction-resolved.ts +50 -0
  262. package/src/api/events/message-complete.ts +10 -12
  263. package/src/api/events/message-dequeued.ts +21 -0
  264. package/src/api/events/message-queued-deleted.ts +23 -0
  265. package/src/api/events/message-queued.ts +22 -0
  266. package/src/api/events/message-request-complete.ts +29 -0
  267. package/src/api/events/navigate-settings.ts +20 -0
  268. package/src/api/events/notification-intent.ts +33 -0
  269. package/src/api/events/open-url.ts +6 -8
  270. package/src/api/events/question-request.ts +67 -0
  271. package/src/api/events/relationship-state-updated.ts +4 -6
  272. package/src/api/events/secret-request.ts +42 -0
  273. package/src/api/events/subagent-event.ts +79 -0
  274. package/src/api/events/subagent-spawned.ts +40 -0
  275. package/src/api/events/subagent-status-changed.ts +65 -0
  276. package/src/api/events/sync-changed.ts +29 -0
  277. package/src/api/events/tool-result.ts +129 -0
  278. package/src/api/events/tool-use-start.ts +8 -10
  279. package/src/api/events/turn-profile-auto-routed.ts +28 -0
  280. package/src/api/events/ui-surface-complete.ts +30 -0
  281. package/src/api/events/ui-surface-dismiss.ts +22 -0
  282. package/src/api/events/ui-surface-show.ts +67 -0
  283. package/src/api/events/ui-surface-update.ts +26 -0
  284. package/src/api/events/usage-update.ts +34 -0
  285. package/src/api/events/user-message-echo.ts +35 -0
  286. package/src/api/index.ts +354 -0
  287. package/src/api/requests/dictation.ts +45 -0
  288. package/src/api/responses/disk-pressure-status.ts +26 -0
  289. package/src/api/responses/home.ts +217 -0
  290. package/src/api/responses/llm-context-response.ts +2 -0
  291. package/src/api/responses/memory-v3-selection-log.ts +50 -0
  292. package/src/api/responses/subagent-detail.ts +48 -0
  293. package/src/approvals/guardian-decision-primitive.ts +7 -15
  294. package/src/approvals/guardian-request-resolvers.ts +6 -9
  295. package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
  296. package/src/avatar/__tests__/avatar-store.test.ts +193 -0
  297. package/src/avatar/avatar-manifest.ts +195 -0
  298. package/src/avatar/avatar-store.ts +113 -0
  299. package/src/avatar/traits-png-sync.ts +8 -2
  300. package/src/background-wake/next-wake.test.ts +31 -1
  301. package/src/background-wake/next-wake.ts +4 -1
  302. package/src/calls/call-conversation-messages.ts +6 -4
  303. package/src/calls/guardian-action-sweep.ts +6 -4
  304. package/src/calls/relay-server.ts +12 -8
  305. package/src/calls/voice-session-bridge.ts +13 -27
  306. package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
  307. package/src/cli/commands/avatar.ts +17 -11
  308. package/src/cli/commands/conversations.ts +15 -1
  309. package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
  310. package/src/cli/commands/db/__tests__/status.test.ts +253 -0
  311. package/src/cli/commands/db/format.ts +48 -0
  312. package/src/cli/commands/db/index.ts +29 -0
  313. package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
  314. package/src/cli/commands/db/repair-step-integrity.ts +146 -0
  315. package/src/cli/commands/db/repair-steps.ts +164 -0
  316. package/src/cli/commands/db/repair.ts +141 -0
  317. package/src/cli/commands/db/status.ts +366 -0
  318. package/src/cli/commands/memory-v3.ts +159 -445
  319. package/src/cli/lib/cli-colors.ts +24 -6
  320. package/src/cli/program.ts +4 -5
  321. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  322. package/src/config/assistant-feature-flags.ts +2 -2
  323. package/src/config/bundled-skills/app-builder/SKILL.md +14 -3
  324. package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
  325. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
  326. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  327. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  328. package/src/config/call-site-defaults.ts +2 -7
  329. package/src/config/feature-flag-registry.json +25 -9
  330. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -226
  331. package/src/config/schemas/call-site-catalog.ts +8 -15
  332. package/src/config/schemas/llm.ts +2 -3
  333. package/src/config/schemas/memory-lifecycle.ts +24 -0
  334. package/src/config/schemas/memory-v2.ts +0 -253
  335. package/src/config/schemas/memory-v3.ts +39 -0
  336. package/src/config/schemas/memory.ts +6 -1
  337. package/src/config/schemas/timeouts.ts +3 -1
  338. package/src/context/compactor.ts +54 -31
  339. package/src/context/token-estimator.ts +19 -0
  340. package/src/context/tool-result-truncation.ts +1 -43
  341. package/src/context/window-manager.ts +138 -20
  342. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
  343. package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
  344. package/src/daemon/approval-generators.ts +4 -4
  345. package/src/daemon/config-watcher.ts +7 -1
  346. package/src/daemon/conversation-agent-loop-handlers.ts +225 -88
  347. package/src/daemon/conversation-agent-loop.ts +284 -584
  348. package/src/daemon/conversation-error.ts +7 -7
  349. package/src/daemon/conversation-history.ts +22 -6
  350. package/src/daemon/conversation-launch.ts +4 -8
  351. package/src/daemon/conversation-lifecycle.ts +10 -38
  352. package/src/daemon/conversation-messaging.ts +1 -3
  353. package/src/daemon/conversation-notifiers.ts +7 -5
  354. package/src/daemon/conversation-process.ts +100 -79
  355. package/src/daemon/conversation-runtime-assembly.ts +47 -21
  356. package/src/daemon/conversation-store.ts +6 -5
  357. package/src/daemon/conversation-surfaces.ts +55 -69
  358. package/src/daemon/conversation-tool-setup.ts +3 -0
  359. package/src/daemon/conversation.ts +91 -126
  360. package/src/daemon/daemon-skill-host.ts +2 -6
  361. package/src/daemon/disk-pressure-guard.ts +35 -29
  362. package/src/daemon/external-plugins-bootstrap.ts +46 -24
  363. package/src/daemon/first-greeting.ts +26 -4
  364. package/src/daemon/guardian-action-generators.ts +2 -2
  365. package/src/daemon/handlers/conversations.ts +6 -22
  366. package/src/daemon/handlers/shared.ts +4 -0
  367. package/src/daemon/handlers/skills.ts +15 -14
  368. package/src/daemon/host-app-control-proxy.ts +54 -1
  369. package/src/daemon/host-cu-proxy.ts +46 -22
  370. package/src/daemon/host-file-proxy.ts +25 -1
  371. package/src/daemon/host-proxy-preactivation.ts +25 -6
  372. package/src/daemon/lifecycle.ts +28 -55
  373. package/src/daemon/message-protocol.ts +2 -3
  374. package/src/daemon/message-provenance.ts +49 -0
  375. package/src/daemon/message-types/contacts.ts +3 -20
  376. package/src/daemon/message-types/conversations.ts +13 -111
  377. package/src/daemon/message-types/documents.ts +3 -9
  378. package/src/daemon/message-types/home.ts +4 -17
  379. package/src/daemon/message-types/integrations.ts +2 -6
  380. package/src/daemon/message-types/messages.ts +28 -343
  381. package/src/daemon/message-types/notifications.ts +2 -32
  382. package/src/daemon/message-types/settings.ts +3 -8
  383. package/src/daemon/message-types/skills.ts +2 -0
  384. package/src/daemon/message-types/surfaces.ts +2 -0
  385. package/src/daemon/message-types/sync.ts +12 -25
  386. package/src/daemon/message-types/workspace.ts +3 -11
  387. package/src/daemon/process-message.ts +49 -46
  388. package/src/daemon/server.ts +12 -0
  389. package/src/daemon/tool-side-effects.ts +10 -7
  390. package/src/daemon/trust-context.ts +13 -0
  391. package/src/daemon/wake-target-adapter.ts +11 -1
  392. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -1
  393. package/src/heartbeat/heartbeat-run-store.ts +31 -0
  394. package/src/heartbeat/heartbeat-service.ts +16 -0
  395. package/src/home/feature-gate.ts +22 -0
  396. package/src/home/feed-types.ts +36 -221
  397. package/src/ipc/__tests__/email-ipc.test.ts +0 -9
  398. package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
  399. package/src/ipc/routes/route-adapter.ts +45 -6
  400. package/src/ipc/skill-routes/__tests__/memory.test.ts +18 -9
  401. package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
  402. package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
  403. package/src/ipc/skill-routes/memory.ts +26 -13
  404. package/src/ipc/skill-routes/providers.ts +5 -6
  405. package/src/ipc/skill-routes/registries.ts +13 -61
  406. package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
  407. package/src/memory/__tests__/conversation-queries.test.ts +192 -8
  408. package/src/memory/__tests__/db-maintenance.test.ts +128 -0
  409. package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
  410. package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
  411. package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
  412. package/src/memory/context-search/agent-runner.ts +2 -4
  413. package/src/memory/conversation-crud.ts +39 -8
  414. package/src/memory/conversation-queries.ts +78 -22
  415. package/src/memory/db-init.ts +8 -0
  416. package/src/memory/db-maintenance.ts +18 -2
  417. package/src/memory/graph/consolidation.ts +8 -11
  418. package/src/memory/graph/conversation-graph-memory.ts +41 -8
  419. package/src/memory/graph/extraction.ts +6 -9
  420. package/src/memory/graph/narrative.ts +2 -2
  421. package/src/memory/graph/pattern-scan.ts +2 -2
  422. package/src/memory/graph/retriever.ts +20 -26
  423. package/src/memory/graph/tools.ts +4 -4
  424. package/src/memory/job-handlers/conversation-starters.ts +32 -32
  425. package/src/memory/job-handlers/summarization.ts +1 -2
  426. package/src/memory/jobs-store.ts +3 -1
  427. package/src/memory/jobs-worker.ts +51 -39
  428. package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
  429. package/src/memory/llm-request-log-source-local.ts +0 -11
  430. package/src/memory/llm-request-log-source.ts +9 -25
  431. package/src/memory/llm-request-log-store.ts +0 -41
  432. package/src/memory/llm-usage-store.ts +10 -0
  433. package/src/memory/memory-marker.ts +17 -0
  434. package/src/memory/memory-retrospective-job.ts +6 -2
  435. package/src/memory/memory-v2-activation-log-store.ts +1 -83
  436. package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
  437. package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
  438. package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
  439. package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
  440. package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
  441. package/src/memory/migrations/index.ts +4 -0
  442. package/src/memory/schema/infrastructure.ts +11 -0
  443. package/src/memory/v2/__tests__/consolidation-job.test.ts +124 -0
  444. package/src/memory/v2/__tests__/migration.test.ts +11 -3
  445. package/src/memory/v2/__tests__/page-index.test.ts +37 -1
  446. package/src/memory/v2/__tests__/router.test.ts +14 -4
  447. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
  448. package/src/memory/v2/backfill-jobs.ts +6 -0
  449. package/src/memory/v2/consolidation-job.ts +89 -9
  450. package/src/memory/v2/migration.ts +5 -3
  451. package/src/memory/v2/page-index.ts +11 -0
  452. package/src/memory/v2/router.ts +8 -11
  453. package/src/memory/v2/sweep-job.ts +8 -11
  454. package/src/memory/v2/types.ts +1 -0
  455. package/src/memory/v3/__tests__/assign.test.ts +242 -0
  456. package/src/memory/v3/__tests__/capabilities.test.ts +118 -0
  457. package/src/memory/v3/__tests__/core.test.ts +39 -0
  458. package/src/memory/v3/__tests__/fixtures/eval-turns.json +36 -0
  459. package/src/memory/v3/__tests__/fixtures/live-turns.json +37 -0
  460. package/src/memory/v3/__tests__/health.test.ts +203 -0
  461. package/src/memory/v3/__tests__/live-integration.test.ts +330 -0
  462. package/src/memory/v3/__tests__/maintain-job.test.ts +288 -0
  463. package/src/memory/v3/__tests__/needle.test.ts +107 -0
  464. package/src/memory/v3/__tests__/orchestrate.test.ts +400 -0
  465. package/src/memory/v3/__tests__/reconcile.test.ts +274 -0
  466. package/src/memory/v3/__tests__/render-injection.test.ts +61 -0
  467. package/src/memory/v3/__tests__/router.test.ts +260 -0
  468. package/src/memory/v3/__tests__/selection-log-store.test.ts +179 -0
  469. package/src/memory/v3/__tests__/selector.test.ts +404 -0
  470. package/src/memory/v3/__tests__/shadow-plugin.test.ts +414 -0
  471. package/src/memory/v3/__tests__/snapshot.test.ts +168 -0
  472. package/src/memory/v3/__tests__/tree.test.ts +192 -0
  473. package/src/memory/v3/__tests__/types.test.ts +54 -0
  474. package/src/memory/v3/__tests__/working-set-eviction.test.ts +106 -0
  475. package/src/memory/v3/__tests__/working-set-skeleton.test.ts +44 -0
  476. package/src/memory/v3/assign.ts +268 -0
  477. package/src/memory/v3/capabilities.ts +124 -0
  478. package/src/memory/v3/core.ts +26 -0
  479. package/src/memory/v3/data/README.md +84 -0
  480. package/src/memory/v3/data/assignments.json +5 -0
  481. package/src/memory/v3/data/core.json +1 -0
  482. package/src/memory/v3/data/leaves/domain-a/topic-x.md +9 -0
  483. package/src/memory/v3/data/leaves/domain-a/topic-y.md +9 -0
  484. package/src/memory/v3/data/leaves/domain-b/topic-z.md +9 -0
  485. package/src/memory/v3/health.ts +0 -0
  486. package/src/memory/v3/maintain-job.ts +314 -0
  487. package/src/memory/v3/needle.ts +115 -0
  488. package/src/memory/v3/orchestrate.ts +114 -0
  489. package/src/memory/v3/page-content.ts +34 -0
  490. package/src/memory/v3/provider-blocks.ts +16 -0
  491. package/src/memory/v3/reconcile.ts +523 -0
  492. package/src/memory/v3/render-injection.ts +32 -0
  493. package/src/memory/v3/router.ts +184 -0
  494. package/src/memory/v3/selection-log-store.ts +84 -0
  495. package/src/memory/v3/selector.ts +211 -0
  496. package/src/memory/v3/shadow-plugin.ts +379 -0
  497. package/src/memory/v3/snapshot.ts +209 -0
  498. package/src/memory/v3/tree.ts +174 -0
  499. package/src/memory/v3/types.ts +46 -60
  500. package/src/memory/v3/working-set.ts +88 -0
  501. package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
  502. package/src/messaging/providers/slack/render-transcript.ts +2 -2
  503. package/src/messaging/style-analyzer.ts +8 -11
  504. package/src/notifications/conversation-pairing.ts +8 -6
  505. package/src/notifications/decision-engine.ts +10 -13
  506. package/src/notifications/preference-extractor.ts +11 -14
  507. package/src/permissions/prompter.ts +42 -36
  508. package/src/permissions/question-prompter.test.ts +35 -26
  509. package/src/permissions/question-prompter.ts +6 -10
  510. package/src/plugin-api/index.ts +2 -0
  511. package/src/plugin-api/types.ts +25 -3
  512. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
  513. package/src/plugins/defaults/circuit-breaker/package.json +15 -0
  514. package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
  515. package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
  516. package/src/plugins/defaults/compaction/package.json +15 -0
  517. package/src/plugins/defaults/compaction/register.ts +35 -0
  518. package/src/plugins/defaults/compaction/terminal.ts +73 -0
  519. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +22 -0
  520. package/src/plugins/defaults/empty-response/package.json +15 -0
  521. package/src/plugins/defaults/empty-response/register.ts +28 -0
  522. package/src/plugins/defaults/empty-response/terminal.ts +106 -0
  523. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
  524. package/src/plugins/defaults/history-repair/package.json +15 -0
  525. package/src/plugins/defaults/history-repair/register.ts +24 -0
  526. package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
  527. package/src/plugins/defaults/index.ts +29 -40
  528. package/src/plugins/defaults/injectors/package.json +15 -0
  529. package/src/plugins/defaults/{injectors.ts → injectors/register.ts} +14 -38
  530. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +17 -0
  531. package/src/plugins/defaults/llm-call/package.json +15 -0
  532. package/src/plugins/defaults/{llm-call.ts → llm-call/register.ts} +6 -38
  533. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +17 -0
  534. package/src/plugins/defaults/memory-retrieval/package.json +15 -0
  535. package/src/plugins/defaults/{memory-retrieval.ts → memory-retrieval/register.ts} +10 -48
  536. package/src/plugins/defaults/{overflow-reduce.ts → overflow-reduce/middlewares/overflowReduce.ts} +18 -77
  537. package/src/plugins/defaults/overflow-reduce/package.json +15 -0
  538. package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
  539. package/src/plugins/defaults/persistence/middlewares/persistence.ts +19 -0
  540. package/src/plugins/defaults/persistence/package.json +15 -0
  541. package/src/plugins/defaults/persistence/register.ts +38 -0
  542. package/src/plugins/defaults/persistence/terminal.ts +83 -0
  543. package/src/plugins/defaults/title-generate/package.json +15 -0
  544. package/src/plugins/defaults/title-generate/register.ts +35 -0
  545. package/src/plugins/defaults/title-generate/terminal.ts +31 -0
  546. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +23 -0
  547. package/src/plugins/defaults/token-estimate/package.json +15 -0
  548. package/src/plugins/defaults/token-estimate/register.ts +34 -0
  549. package/src/plugins/defaults/token-estimate/terminal.ts +40 -0
  550. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +21 -0
  551. package/src/plugins/defaults/tool-error/package.json +15 -0
  552. package/src/plugins/defaults/tool-error/register.ts +35 -0
  553. package/src/plugins/defaults/tool-error/terminal.ts +47 -0
  554. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +23 -0
  555. package/src/plugins/defaults/tool-execute/package.json +15 -0
  556. package/src/plugins/defaults/{tool-execute.ts → tool-execute/register.ts} +8 -46
  557. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +23 -0
  558. package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
  559. package/src/plugins/defaults/tool-result-truncate/register.ts +35 -0
  560. package/src/plugins/defaults/tool-result-truncate/terminal.ts +113 -0
  561. package/src/plugins/defaults/tool-result-truncate/types.ts +22 -0
  562. package/src/plugins/external-plugin-loader.ts +2 -2
  563. package/src/plugins/pipeline.ts +0 -12
  564. package/src/plugins/types.ts +51 -90
  565. package/src/plugins/user-loader.ts +4 -3
  566. package/src/proactive-artifact/aux-message-injector.ts +0 -1
  567. package/src/proactive-artifact/job.test.ts +20 -8
  568. package/src/proactive-artifact/job.ts +3 -1
  569. package/src/prompts/sections.ts +20 -7
  570. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
  571. package/src/prompts/templates/BOOTSTRAP.md +5 -1
  572. package/src/prompts/templates/system-sections.ts +6 -0
  573. package/src/providers/__tests__/retry-callsite.test.ts +25 -25
  574. package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
  575. package/src/providers/anthropic/client.ts +24 -5
  576. package/src/providers/call-site-routing.ts +1 -9
  577. package/src/providers/gemini/client.ts +152 -34
  578. package/src/providers/gemini/inline-media.ts +74 -0
  579. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +0 -2
  580. package/src/providers/openai/chat-completions-provider.ts +1 -4
  581. package/src/providers/openai/responses-provider.ts +1 -4
  582. package/src/providers/openrouter/client.ts +1 -6
  583. package/src/providers/provider-send-message.ts +6 -6
  584. package/src/providers/ratelimit.ts +1 -9
  585. package/src/providers/retry.ts +0 -5
  586. package/src/providers/types.ts +11 -2
  587. package/src/providers/usage-tracking.ts +1 -9
  588. package/src/runtime/__tests__/agent-wake.test.ts +131 -26
  589. package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
  590. package/src/runtime/agent-wake.ts +93 -18
  591. package/src/runtime/assistant-event-hub.ts +2 -2
  592. package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
  593. package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
  594. package/src/runtime/auth/route-policy.ts +42 -1079
  595. package/src/runtime/background-job-runner.ts +1 -4
  596. package/src/runtime/btw-sidechain.ts +3 -1
  597. package/src/runtime/channel-approvals.ts +3 -14
  598. package/src/runtime/channel-invite-transport.ts +5 -6
  599. package/src/runtime/channel-readiness-service.ts +2 -5
  600. package/src/runtime/channel-retry-sweep.ts +12 -16
  601. package/src/runtime/conversation-stream-state.ts +294 -0
  602. package/src/runtime/http-router.ts +19 -22
  603. package/src/runtime/http-types.ts +12 -6
  604. package/src/runtime/invite-instruction-generator.ts +3 -3
  605. package/src/runtime/pending-interactions.ts +2 -2
  606. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
  607. package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
  608. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
  609. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
  610. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
  611. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
  612. package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
  613. package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
  614. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
  615. package/src/runtime/routes/__tests__/tts-routes.test.ts +3 -3
  616. package/src/runtime/routes/acp-routes.test.ts +97 -75
  617. package/src/runtime/routes/acp-routes.ts +29 -6
  618. package/src/runtime/routes/app-management-routes.ts +97 -24
  619. package/src/runtime/routes/app-routes.ts +25 -5
  620. package/src/runtime/routes/approval-routes.ts +16 -4
  621. package/src/runtime/routes/attachment-routes.ts +25 -1
  622. package/src/runtime/routes/audio-routes.ts +1 -0
  623. package/src/runtime/routes/audit-routes.ts +5 -0
  624. package/src/runtime/routes/auth-routes.ts +5 -0
  625. package/src/runtime/routes/avatar-routes.ts +238 -59
  626. package/src/runtime/routes/background-tool-routes.ts +9 -0
  627. package/src/runtime/routes/background-wake-routes.ts +13 -3
  628. package/src/runtime/routes/backup-routes.ts +45 -0
  629. package/src/runtime/routes/bookmark-routes.ts +13 -0
  630. package/src/runtime/routes/brain-graph-routes.ts +9 -0
  631. package/src/runtime/routes/browser-routes.ts +5 -0
  632. package/src/runtime/routes/browser-tabs-routes.ts +5 -0
  633. package/src/runtime/routes/btw-routes.ts +5 -1
  634. package/src/runtime/routes/cache-routes.ts +13 -0
  635. package/src/runtime/routes/call-routes.ts +21 -10
  636. package/src/runtime/routes/channel-availability-routes.ts +5 -1
  637. package/src/runtime/routes/channel-readiness-routes.ts +37 -4
  638. package/src/runtime/routes/channel-route-definitions.ts +21 -0
  639. package/src/runtime/routes/channel-verification-routes.ts +21 -0
  640. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
  641. package/src/runtime/routes/client-routes.ts +9 -0
  642. package/src/runtime/routes/consolidation-routes.ts +13 -5
  643. package/src/runtime/routes/contact-prompt-routes.ts +9 -0
  644. package/src/runtime/routes/contact-routes.ts +90 -23
  645. package/src/runtime/routes/content-source-routes.ts +5 -1
  646. package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
  647. package/src/runtime/routes/conversation-attention-routes.ts +5 -0
  648. package/src/runtime/routes/conversation-cli-routes.ts +54 -7
  649. package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
  650. package/src/runtime/routes/conversation-list-routes.ts +81 -12
  651. package/src/runtime/routes/conversation-management-routes.ts +57 -14
  652. package/src/runtime/routes/conversation-query-routes.ts +88 -41
  653. package/src/runtime/routes/conversation-routes.ts +74 -19
  654. package/src/runtime/routes/conversation-starter-routes.ts +22 -13
  655. package/src/runtime/routes/conversations-import-routes.ts +6 -1
  656. package/src/runtime/routes/credential-prompt-routes.ts +5 -0
  657. package/src/runtime/routes/credential-routes.ts +25 -6
  658. package/src/runtime/routes/debug-bash-routes.ts +5 -0
  659. package/src/runtime/routes/debug-routes.ts +11 -2
  660. package/src/runtime/routes/defer-routes.ts +13 -0
  661. package/src/runtime/routes/diagnostics-routes.ts +37 -46
  662. package/src/runtime/routes/disk-pressure-routes.ts +17 -31
  663. package/src/runtime/routes/document-comments-routes.ts +46 -27
  664. package/src/runtime/routes/documents-routes.ts +21 -10
  665. package/src/runtime/routes/domain-routes.ts +61 -28
  666. package/src/runtime/routes/email-routes.ts +33 -0
  667. package/src/runtime/routes/events-routes.ts +114 -9
  668. package/src/runtime/routes/filing-routes.ts +9 -4
  669. package/src/runtime/routes/gateway-log-routes.ts +5 -0
  670. package/src/runtime/routes/global-search-routes.ts +53 -50
  671. package/src/runtime/routes/group-routes.ts +21 -5
  672. package/src/runtime/routes/guardian-action-routes.ts +9 -0
  673. package/src/runtime/routes/guardian-approval-interception.ts +0 -31
  674. package/src/runtime/routes/heartbeat-routes.ts +25 -9
  675. package/src/runtime/routes/home-feed-routes.ts +23 -19
  676. package/src/runtime/routes/home-state-routes.ts +8 -40
  677. package/src/runtime/routes/host-app-control-routes.ts +5 -0
  678. package/src/runtime/routes/host-bash-routes.ts +5 -0
  679. package/src/runtime/routes/host-browser-routes.ts +13 -0
  680. package/src/runtime/routes/host-cu-routes.ts +5 -0
  681. package/src/runtime/routes/host-file-routes.ts +26 -6
  682. package/src/runtime/routes/host-transfer-routes.ts +13 -2
  683. package/src/runtime/routes/http-adapter.ts +1 -2
  684. package/src/runtime/routes/identity-intro-cache.ts +17 -6
  685. package/src/runtime/routes/identity-routes.ts +12 -2
  686. package/src/runtime/routes/image-generation-routes.ts +5 -0
  687. package/src/runtime/routes/inbound-message-handler.ts +15 -11
  688. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
  689. package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
  690. package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
  691. package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
  692. package/src/runtime/routes/inference-send-routes.ts +11 -11
  693. package/src/runtime/routes/integrations/a2a.ts +30 -7
  694. package/src/runtime/routes/integrations/slack/channel.ts +19 -3
  695. package/src/runtime/routes/integrations/slack/share.ts +9 -2
  696. package/src/runtime/routes/integrations/telegram.ts +28 -9
  697. package/src/runtime/routes/integrations/twilio.ts +35 -7
  698. package/src/runtime/routes/integrations/vercel.ts +3 -3
  699. package/src/runtime/routes/internal-oauth-routes.ts +5 -0
  700. package/src/runtime/routes/internal-twilio-routes.ts +13 -0
  701. package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
  702. package/src/runtime/routes/log-export-routes.ts +28 -10
  703. package/src/runtime/routes/mcp-auth-routes.ts +25 -0
  704. package/src/runtime/routes/memory-item-routes.ts +21 -10
  705. package/src/runtime/routes/memory-v2-routes.ts +90 -36
  706. package/src/runtime/routes/memory-v3-routes.ts +273 -407
  707. package/src/runtime/routes/migration-rollback-routes.ts +5 -1
  708. package/src/runtime/routes/migration-routes.ts +29 -0
  709. package/src/runtime/routes/notification-routes.ts +17 -1
  710. package/src/runtime/routes/oauth-apps.ts +33 -11
  711. package/src/runtime/routes/oauth-commands-routes.ts +37 -14
  712. package/src/runtime/routes/oauth-connect-routes.ts +9 -0
  713. package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
  714. package/src/runtime/routes/oauth-providers.ts +35 -10
  715. package/src/runtime/routes/platform-routes.ts +21 -0
  716. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +3 -2
  717. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
  718. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
  719. package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
  720. package/src/runtime/routes/playground/force-compact.ts +1 -1
  721. package/src/runtime/routes/playground/helpers.ts +0 -1
  722. package/src/runtime/routes/playground/inject-failures.ts +13 -8
  723. package/src/runtime/routes/playground/reset-circuit.ts +14 -9
  724. package/src/runtime/routes/playground/seed-conversation.ts +1 -1
  725. package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
  726. package/src/runtime/routes/playground/state.ts +4 -3
  727. package/src/runtime/routes/plugins-routes.ts +22 -19
  728. package/src/runtime/routes/profiler-routes.ts +17 -4
  729. package/src/runtime/routes/ps-routes.ts +5 -0
  730. package/src/runtime/routes/publish-routes.ts +13 -3
  731. package/src/runtime/routes/question-routes.ts +5 -0
  732. package/src/runtime/routes/recording-routes.ts +25 -12
  733. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  734. package/src/runtime/routes/sanity-routes.ts +9 -2
  735. package/src/runtime/routes/schedule-routes.ts +137 -47
  736. package/src/runtime/routes/secret-routes.ts +17 -4
  737. package/src/runtime/routes/sequence-routes.ts +33 -0
  738. package/src/runtime/routes/settings-routes.ts +65 -19
  739. package/src/runtime/routes/skills-routes.ts +133 -69
  740. package/src/runtime/routes/slack-channel-routes.ts +5 -0
  741. package/src/runtime/routes/stt-routes.ts +13 -6
  742. package/src/runtime/routes/subagents-routes.ts +24 -18
  743. package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
  744. package/src/runtime/routes/surface-action-routes.ts +9 -0
  745. package/src/runtime/routes/surface-content-routes.ts +10 -2
  746. package/src/runtime/routes/task-routes.ts +37 -0
  747. package/src/runtime/routes/telemetry-routes.ts +9 -0
  748. package/src/runtime/routes/trace-event-routes.ts +42 -1
  749. package/src/runtime/routes/trust-rules-routes.ts +5 -0
  750. package/src/runtime/routes/tts-routes.ts +13 -6
  751. package/src/runtime/routes/types.ts +17 -8
  752. package/src/runtime/routes/ui-request-routes.ts +5 -0
  753. package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
  754. package/src/runtime/routes/usage-routes.ts +71 -3
  755. package/src/runtime/routes/user-routes-cli.ts +9 -0
  756. package/src/runtime/routes/user-routes.ts +5 -1
  757. package/src/runtime/routes/wake-conversation-routes.ts +5 -0
  758. package/src/runtime/routes/watcher-routes.ts +21 -0
  759. package/src/runtime/routes/webhook-routes.ts +9 -0
  760. package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
  761. package/src/runtime/routes/work-items-routes.ts +47 -19
  762. package/src/runtime/routes/workspace-commit-routes.ts +5 -0
  763. package/src/runtime/routes/workspace-routes.test.ts +42 -0
  764. package/src/runtime/routes/workspace-routes.ts +120 -9
  765. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -4
  766. package/src/runtime/services/analyze-conversation.ts +3 -6
  767. package/src/runtime/services/conversation-serializer.ts +24 -2
  768. package/src/runtime/sync/resource-sync-events.ts +16 -2
  769. package/src/runtime/sync/sync-publisher.ts +2 -2
  770. package/src/schedule/run-script.ts +28 -3
  771. package/src/schedule/schedule-store.ts +8 -0
  772. package/src/schedule/scheduler.ts +3 -1
  773. package/src/signals/user-message.ts +5 -8
  774. package/src/skills/catalog-files.ts +4 -1
  775. package/src/skills/clawhub-files.ts +2 -0
  776. package/src/skills/skillssh-files.ts +2 -0
  777. package/src/subagent/manager.ts +3 -6
  778. package/src/telemetry/types.ts +26 -0
  779. package/src/telemetry/usage-telemetry-reporter.test.ts +138 -1
  780. package/src/telemetry/usage-telemetry-reporter.ts +31 -0
  781. package/src/tools/acp/spawn.test.ts +88 -38
  782. package/src/tools/apps/definitions.ts +8 -4
  783. package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
  784. package/src/tools/ask-question/ask-question-tool.ts +85 -90
  785. package/src/tools/computer-use/definitions.ts +28 -24
  786. package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
  787. package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
  788. package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
  789. package/src/tools/credentials/vault.ts +112 -111
  790. package/src/tools/execution-target.ts +1 -1
  791. package/src/tools/execution-timeout.ts +3 -4
  792. package/src/tools/filesystem/edit.ts +45 -42
  793. package/src/tools/filesystem/list.ts +33 -30
  794. package/src/tools/filesystem/read.ts +54 -35
  795. package/src/tools/filesystem/write.ts +34 -31
  796. package/src/tools/host-filesystem/edit.ts +44 -42
  797. package/src/tools/host-filesystem/read.ts +49 -35
  798. package/src/tools/host-filesystem/transfer.ts +121 -108
  799. package/src/tools/host-filesystem/write.ts +33 -31
  800. package/src/tools/host-terminal/host-shell.ts +50 -48
  801. package/src/tools/memory/register.ts +23 -24
  802. package/src/tools/network/web-fetch.ts +49 -46
  803. package/src/tools/network/web-search.ts +16 -13
  804. package/src/tools/registry.ts +39 -16
  805. package/src/tools/schedule/create.ts +11 -0
  806. package/src/tools/schedule/update.ts +16 -0
  807. package/src/tools/shared/filesystem/audio-read.ts +122 -0
  808. package/src/tools/shared/filesystem/image-read.ts +1 -1
  809. package/src/tools/skills/execute.ts +34 -31
  810. package/src/tools/skills/load.ts +29 -23
  811. package/src/tools/subagent/notify-parent.ts +35 -32
  812. package/src/tools/system/avatar-generator.ts +13 -22
  813. package/src/tools/system/request-permission.ts +30 -27
  814. package/src/tools/terminal/shell.ts +190 -61
  815. package/src/tools/tool-defaults.ts +20 -9
  816. package/src/tools/tool-manifest.ts +4 -4
  817. package/src/tools/types.ts +74 -23
  818. package/src/tools/ui-surface/definitions.ts +69 -9
  819. package/src/usage/types.ts +10 -0
  820. package/src/util/errors.ts +2 -2
  821. package/src/util/map-limit.ts +27 -0
  822. package/src/util/platform.ts +15 -12
  823. package/src/work-items/work-item-runner.ts +7 -2
  824. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
  825. package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
  826. package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
  827. package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
  828. package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
  829. package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
  830. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
  831. package/src/workspace/migrations/registry.ts +6 -0
  832. package/src/workspace/provider-commit-message-generator.ts +15 -17
  833. package/tsconfig.json +4 -1
  834. package/src/__tests__/history-repair-pipeline.test.ts +0 -396
  835. package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
  836. package/src/cli/commands/memory-v3-render.ts +0 -491
  837. package/src/daemon/message-types/disk-pressure.ts +0 -9
  838. package/src/email/feature-gate.ts +0 -23
  839. package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
  840. package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
  841. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
  842. package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
  843. package/src/memory/v3/__tests__/edges.test.ts +0 -706
  844. package/src/memory/v3/__tests__/filter.test.ts +0 -560
  845. package/src/memory/v3/__tests__/gate.test.ts +0 -637
  846. package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
  847. package/src/memory/v3/__tests__/loop.test.ts +0 -775
  848. package/src/memory/v3/__tests__/retriever.test.ts +0 -226
  849. package/src/memory/v3/__tests__/scouts.test.ts +0 -489
  850. package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
  851. package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
  852. package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
  853. package/src/memory/v3/__tests__/traversal.test.ts +0 -508
  854. package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
  855. package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
  856. package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
  857. package/src/memory/v3/__tests__/validate.test.ts +0 -277
  858. package/src/memory/v3/auto-edges.ts +0 -223
  859. package/src/memory/v3/coactivation-store.ts +0 -124
  860. package/src/memory/v3/consolidation-job.ts +0 -323
  861. package/src/memory/v3/coretrieval-seed.ts +0 -240
  862. package/src/memory/v3/edge-learning-job.ts +0 -160
  863. package/src/memory/v3/edges.ts +0 -286
  864. package/src/memory/v3/filter.ts +0 -286
  865. package/src/memory/v3/gate.ts +0 -349
  866. package/src/memory/v3/index-composition.ts +0 -126
  867. package/src/memory/v3/llm-capture.ts +0 -46
  868. package/src/memory/v3/loop.ts +0 -430
  869. package/src/memory/v3/maintenance.ts +0 -144
  870. package/src/memory/v3/prompt-context.ts +0 -33
  871. package/src/memory/v3/prompts/consolidation.ts +0 -458
  872. package/src/memory/v3/prompts/system-prompts.ts +0 -196
  873. package/src/memory/v3/retriever.ts +0 -33
  874. package/src/memory/v3/scouts.ts +0 -431
  875. package/src/memory/v3/shadow-diff.ts +0 -287
  876. package/src/memory/v3/shadow-middleware.ts +0 -347
  877. package/src/memory/v3/traversal.ts +0 -211
  878. package/src/memory/v3/tree-index.ts +0 -237
  879. package/src/memory/v3/tree-store.ts +0 -394
  880. package/src/memory/v3/tree-walk.ts +0 -356
  881. package/src/memory/v3/validate.ts +0 -323
  882. package/src/plugins/defaults/circuit-breaker.ts +0 -141
  883. package/src/plugins/defaults/compaction.ts +0 -141
  884. package/src/plugins/defaults/empty-response.ts +0 -124
  885. package/src/plugins/defaults/history-repair.ts +0 -83
  886. package/src/plugins/defaults/persistence.ts +0 -146
  887. package/src/plugins/defaults/title-generate.ts +0 -90
  888. package/src/plugins/defaults/token-estimate.ts +0 -101
  889. package/src/plugins/defaults/tool-error.ts +0 -119
  890. package/src/plugins/defaults/tool-result-truncate.ts +0 -84
  891. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
@@ -17,7 +17,7 @@ import type {
17
17
  AgentLoop,
18
18
  AgentLoopExitReason,
19
19
  CheckpointDecision,
20
- CheckpointInfo,
20
+ MidLoopCompaction,
21
21
  } from "../agent/loop.js";
22
22
  import { createAssistantMessage } from "../agent/message-types.js";
23
23
  import type {
@@ -26,6 +26,7 @@ import type {
26
26
  TurnChannelContext,
27
27
  TurnInterfaceContext,
28
28
  } from "../channels/types.js";
29
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
29
30
  import {
30
31
  contextWindowConfigFromEffective,
31
32
  type EffectiveContextWindow,
@@ -95,28 +96,24 @@ import {
95
96
  import type { PermissionPrompter } from "../permissions/prompter.js";
96
97
  import { HOOKS } from "../plugin-api/constants.js";
97
98
  import type { UserPromptSubmitContext } from "../plugin-api/types.js";
98
- import { defaultCompactionTerminal } from "../plugins/defaults/compaction.js";
99
- import { defaultHistoryRepairTerminal } from "../plugins/defaults/history-repair.js";
99
+ import { defaultCompactionTerminal } from "../plugins/defaults/compaction/terminal.js";
100
+ import { deepRepairHistory } from "../plugins/defaults/history-repair/terminal.js";
100
101
  import {
101
102
  asDefaultGraphPayload,
102
103
  type DefaultMemoryRetrievalDeps,
103
104
  type GraphMemoryPayload,
104
105
  runDefaultMemoryRetrieval,
105
- } from "../plugins/defaults/memory-retrieval.js";
106
- import { defaultPersistenceTerminal } from "../plugins/defaults/persistence.js";
107
- import { defaultTitleGenerateTerminal } from "../plugins/defaults/title-generate.js";
108
- import { defaultTokenEstimateTerminal } from "../plugins/defaults/token-estimate.js";
106
+ } from "../plugins/defaults/memory-retrieval/register.js";
107
+ import { defaultPersistenceTerminal } from "../plugins/defaults/persistence/terminal.js";
108
+ import { defaultTitleGenerateTerminal } from "../plugins/defaults/title-generate/terminal.js";
109
+ import { defaultTokenEstimateTerminal } from "../plugins/defaults/token-estimate/terminal.js";
109
110
  import { DEFAULT_TIMEOUTS, runHook, runPipeline } from "../plugins/pipeline.js";
110
111
  import { getMiddlewaresFor } from "../plugins/registry.js";
111
112
  import type {
112
- CircuitBreakerArgs,
113
- CircuitBreakerResult,
114
113
  CompactionArgs,
115
114
  CompactionResult,
116
115
  EstimateArgs,
117
116
  EstimateResult,
118
- HistoryRepairArgs,
119
- HistoryRepairResult,
120
117
  MemoryArgs,
121
118
  MemoryResult,
122
119
  OverflowReduceArgs,
@@ -127,11 +124,6 @@ import type {
127
124
  TurnContext as PluginTurnContext,
128
125
  } from "../plugins/types.js";
129
126
  import { PluginExecutionError, PluginTimeoutError } from "../plugins/types.js";
130
- import {
131
- hasProactiveArtifactCompleted,
132
- runProactiveArtifactJob,
133
- tryClaimProactiveArtifactTrigger,
134
- } from "../proactive-artifact/index.js";
135
127
  import type {
136
128
  ContentBlock,
137
129
  Message,
@@ -210,7 +202,6 @@ import {
210
202
  } from "./date-context.js";
211
203
  import { getDiskPressureStatus } from "./disk-pressure-guard.js";
212
204
  import { classifyDiskPressureTurnPolicy } from "./disk-pressure-policy.js";
213
- import { deepRepairHistory } from "./history-repair.js";
214
205
  import type {
215
206
  DynamicPageSurfaceData,
216
207
  ServerMessage,
@@ -276,154 +267,6 @@ function formatDiskPressureBlockedMessage(): string {
276
267
  return "Storage is critically low, so background processes are paused and remote messages are ignored until the guardian frees enough space. Remote senders should try again later.";
277
268
  }
278
269
 
279
- // ── Compaction circuit-breaker pipeline helpers ─────────────────────
280
- //
281
- // The circuit-breaker behavior (3 consecutive summary-LLM failures trips a
282
- // 1-hour cooldown) is now implemented by the `circuitBreaker` plugin
283
- // pipeline. The default plugin (`plugins/defaults/circuit-breaker.ts`)
284
- // replicates the legacy threshold/cooldown constants and event-emission
285
- // semantics exactly — it operates on the `consecutiveCompactionFailures` /
286
- // `compactionCircuitOpenUntil` fields the conversation still owns so the
287
- // dev-only playground routes (`POST /playground/reset-compaction-circuit`,
288
- // `POST /playground/inject-compaction-failures`) continue to read and
289
- // mutate those fields directly.
290
- //
291
- // The helpers below build the pipeline inputs and invoke the runner. They
292
- // are the sole entry points the rest of the daemon uses to query or update
293
- // the compaction circuit.
294
-
295
- /** Circuit-breaker key for a specific conversation's compaction pipeline. */
296
- function compactionCircuitKey(conversationId: string): string {
297
- return `compaction:${conversationId}`;
298
- }
299
-
300
- /**
301
- * Build the minimal {@link TurnContext} the pipeline runner requires. Called
302
- * both from inside the agent loop (where turn identifiers are available) and
303
- * from non-turn invocations like `Conversation.forceCompact` (which falls
304
- * back to stable placeholders so the runner's log records still carry the
305
- * conversation identifier).
306
- */
307
- function buildCircuitTurnContext(ctx: {
308
- readonly conversationId: string;
309
- currentRequestId?: string;
310
- currentTurnTrustContext?: TrustContext;
311
- trustContext?: TrustContext;
312
- turnCount: number;
313
- }): PluginTurnContext {
314
- const trust: TrustContext =
315
- ctx.currentTurnTrustContext ?? ctx.trustContext ?? FALLBACK_TURN_TRUST;
316
- return {
317
- requestId: ctx.currentRequestId ?? "circuit-breaker",
318
- conversationId: ctx.conversationId,
319
- turnIndex: ctx.turnCount,
320
- trust,
321
- };
322
- }
323
-
324
- /**
325
- * Run the `circuitBreaker` pipeline for the compaction circuit on this
326
- * conversation. When `outcome` is provided, state is updated (and transition
327
- * events emit via `onEvent`); when omitted the call is query-only.
328
- *
329
- * Returns the post-call decision from the pipeline. Callers gate auto-paths
330
- * on `!result.open` and admit forced paths regardless of the decision.
331
- */
332
- async function runCompactionCircuitPipeline(
333
- ctx: {
334
- readonly conversationId: string;
335
- consecutiveCompactionFailures: number;
336
- compactionCircuitOpenUntil: number | null;
337
- currentRequestId?: string;
338
- currentTurnTrustContext?: TrustContext;
339
- trustContext?: TrustContext;
340
- turnCount: number;
341
- },
342
- args: {
343
- outcome?: "success" | "failure";
344
- onEvent?: (msg: ServerMessage) => void;
345
- },
346
- ): Promise<CircuitBreakerResult> {
347
- const turnContext = buildCircuitTurnContext(ctx);
348
- return runPipeline<CircuitBreakerArgs, CircuitBreakerResult>(
349
- "circuitBreaker",
350
- getMiddlewaresFor("circuitBreaker"),
351
- async (terminalArgs) => {
352
- // No plugin in the chain produced a decision. This should be
353
- // unreachable in production because the default plugin registers a
354
- // `circuitBreaker` middleware that always returns a decision, but we
355
- // defensively derive the state here so test setups that intentionally
356
- // omit the default plugin still get a sensible response.
357
- const openUntil = terminalArgs.state.compactionCircuitOpenUntil;
358
- const now = Date.now();
359
- if (openUntil !== null && now < openUntil) {
360
- return { open: true, cooldownRemainingMs: openUntil - now };
361
- }
362
- return { open: false };
363
- },
364
- {
365
- key: compactionCircuitKey(ctx.conversationId),
366
- // Pass the ctx directly as the mutable state container. The
367
- // `CircuitBreakerArgs.state` shape deliberately matches the subset of
368
- // fields the conversation owns so plugins mutate the same object the
369
- // playground routes read and write.
370
- state: ctx,
371
- ...(args.outcome !== undefined ? { outcome: args.outcome } : {}),
372
- ...(args.onEvent ? { onEvent: args.onEvent } : {}),
373
- },
374
- turnContext,
375
- DEFAULT_TIMEOUTS.circuitBreaker,
376
- );
377
- }
378
-
379
- /**
380
- * Query-only: is the compaction circuit breaker currently open for this
381
- * conversation? Thin wrapper around {@link runCompactionCircuitPipeline}
382
- * with no outcome. Async because the pipeline runner is async, but the
383
- * default plugin resolves synchronously on its microtask.
384
- */
385
- async function isCompactionCircuitOpen(ctx: {
386
- readonly conversationId: string;
387
- consecutiveCompactionFailures: number;
388
- compactionCircuitOpenUntil: number | null;
389
- currentRequestId?: string;
390
- currentTurnTrustContext?: TrustContext;
391
- trustContext?: TrustContext;
392
- turnCount: number;
393
- }): Promise<boolean> {
394
- const decision = await runCompactionCircuitPipeline(ctx, {});
395
- return decision.open;
396
- }
397
-
398
- /**
399
- * Update the compaction circuit breaker with the outcome of a `maybeCompact`
400
- * call and emit any transition event. A `summaryFailed` value of `undefined`
401
- * means the summary LLM never ran (early return) — callers must guard with
402
- * `summaryFailed !== undefined` before invoking this helper so early-return
403
- * paths don't silently reset the 3-strike counter.
404
- *
405
- * The default plugin handles threshold-based tripping and cooldown reset;
406
- * see `plugins/defaults/circuit-breaker.ts` for the canonical semantics.
407
- */
408
- export async function trackCompactionOutcome(
409
- ctx: {
410
- readonly conversationId: string;
411
- consecutiveCompactionFailures: number;
412
- compactionCircuitOpenUntil: number | null;
413
- currentRequestId?: string;
414
- currentTurnTrustContext?: TrustContext;
415
- trustContext?: TrustContext;
416
- turnCount: number;
417
- },
418
- summaryFailed: boolean,
419
- onEvent: (msg: ServerMessage) => void,
420
- ): Promise<void> {
421
- await runCompactionCircuitPipeline(ctx, {
422
- outcome: summaryFailed ? "failure" : "success",
423
- onEvent,
424
- });
425
- }
426
-
427
270
  // ── Plugin pipeline helpers ──────────────────────────────────────────
428
271
  //
429
272
  // Canonical {@link PluginTurnContext} builder threaded into every
@@ -484,6 +327,28 @@ function buildPluginTurnContext(
484
327
 
485
328
  // ── Context Interface ────────────────────────────────────────────────
486
329
 
330
+ /**
331
+ * Per-surface entry tracked on the current turn. Inline shape kept stable so
332
+ * routes and persistence helpers can consume it via a named import instead of
333
+ * `infer`-extracting from {@link AgentLoopConversationContext}.
334
+ */
335
+ export interface AssistantSurface {
336
+ surfaceId: string;
337
+ surfaceType: SurfaceType;
338
+ title?: string;
339
+ data: SurfaceData;
340
+ actions?: Array<{
341
+ id: string;
342
+ label: string;
343
+ style?: string;
344
+ data?: Record<string, unknown>;
345
+ }>;
346
+ display?: string;
347
+ persistent?: boolean;
348
+ /** Id of the tool call that produced this surface (the `ui_show` proxy tool). Persisted so app previews can gate on the tool result's arrival rather than whole-turn streaming state. */
349
+ toolCallId?: string;
350
+ }
351
+
487
352
  export interface AgentLoopConversationContext {
488
353
  readonly conversationId: string;
489
354
  messages: Message[];
@@ -507,10 +372,6 @@ export interface AgentLoopConversationContext {
507
372
  * happened just before this turn).
508
373
  */
509
374
  pendingPostCompactReinject: boolean;
510
- /** Tracks consecutive compaction failures (summary LLM call threw). */
511
- consecutiveCompactionFailures: number;
512
- /** Timestamp (ms since epoch) until which the circuit breaker is open. */
513
- compactionCircuitOpenUntil: number | null;
514
375
 
515
376
  readonly graphMemory: ConversationGraphMemory;
516
377
 
@@ -533,20 +394,7 @@ export interface AgentLoopConversationContext {
533
394
  pendingSurfaceActions: Map<string, { surfaceType: SurfaceType }>;
534
395
  surfaceActionRequestIds: Set<string>;
535
396
  approvedViaPromptThisTurn?: boolean;
536
- currentTurnSurfaces: Array<{
537
- surfaceId: string;
538
- surfaceType: SurfaceType;
539
- title?: string;
540
- data: SurfaceData;
541
- actions?: Array<{
542
- id: string;
543
- label: string;
544
- style?: string;
545
- data?: Record<string, unknown>;
546
- }>;
547
- display?: string;
548
- persistent?: boolean;
549
- }>;
397
+ currentTurnSurfaces: AssistantSurface[];
550
398
 
551
399
  workingDir: string;
552
400
  workspaceTopLevelContext: string | null;
@@ -624,9 +472,11 @@ export interface AgentLoopConversationContext {
624
472
  | "message_complete"
625
473
  | "generation_cancelled"
626
474
  | "error_terminal",
627
- anchor?: "assistant_turn" | "user_turn" | "global",
628
- requestId?: string,
629
- statusText?: string,
475
+ options?: {
476
+ anchor?: "assistant_turn" | "user_turn" | "global";
477
+ requestId?: string;
478
+ statusText?: string;
479
+ },
630
480
  ): void;
631
481
  emitConfirmationStateChanged(
632
482
  params: ConfirmationStateChanged extends {
@@ -644,7 +494,6 @@ export interface AgentLoopConversationContext {
644
494
  onConfirmationOutcome?: (
645
495
  requestId: string,
646
496
  state: string,
647
- toolName?: string,
648
497
  toolUseId?: string,
649
498
  ) => void;
650
499
 
@@ -859,6 +708,25 @@ export async function runAgentLoopImpl(
859
708
  preflightBudget: Math.floor(providerMaxTokens * (1 - safetyMargin)),
860
709
  };
861
710
  };
711
+ /**
712
+ * The agent loop's window into the orchestrator's current effective
713
+ * context window. The loop reads `maxInputTokens` for tool-result
714
+ * truncation and `overflowRecovery` for its mid-loop budget gate, applying
715
+ * the long-history safety-margin bump itself off its own running history.
716
+ * Resolved fresh on each access so a mid-turn profile change is reflected.
717
+ */
718
+ const resolveContextWindow = (): {
719
+ maxInputTokens: number;
720
+ overflowRecovery: { enabled: boolean; safetyMarginRatio: number };
721
+ } => {
722
+ refreshCurrentProfileState();
723
+ const { enabled, safetyMarginRatio } =
724
+ currentEffectiveContextWindow.overflowRecovery;
725
+ return {
726
+ maxInputTokens: currentEffectiveContextWindow.maxInputTokens,
727
+ overflowRecovery: { enabled, safetyMarginRatio },
728
+ };
729
+ };
862
730
 
863
731
  // Initial value for `createToolExecutor` to read into
864
732
  // `ToolContext.overrideProfile`. `resolveCurrentOverrideProfile` refreshes
@@ -968,7 +836,10 @@ export async function runAgentLoopImpl(
968
836
  { reason: diskPressureDecision.reason },
969
837
  "Blocked turn during disk pressure cleanup mode",
970
838
  );
971
- ctx.emitActivityState("idle", "error_terminal", "global", reqId);
839
+ ctx.emitActivityState("idle", "error_terminal", {
840
+ anchor: "global",
841
+ requestId: reqId,
842
+ });
972
843
  ctx.traceEmitter.emit("request_error", message, {
973
844
  requestId: reqId,
974
845
  status: "error",
@@ -1220,14 +1091,12 @@ export async function runAgentLoopImpl(
1220
1091
  );
1221
1092
  // Skip auto-compaction while the circuit breaker is open. Force paths
1222
1093
  // and user-initiated /compact bypass this check.
1223
- const autoCompactAllowed = !(await isCompactionCircuitOpen(ctx));
1094
+ const autoCompactAllowed =
1095
+ !(await ctx.agentLoop.compactionCircuit.isOpen(ctx));
1224
1096
  if (compactCheck.needed && autoCompactAllowed) {
1225
- ctx.emitActivityState(
1226
- "thinking",
1227
- "context_compacting",
1228
- "assistant_turn",
1229
- reqId,
1230
- );
1097
+ ctx.emitActivityState("thinking", "context_compacting", {
1098
+ requestId: reqId,
1099
+ });
1231
1100
  }
1232
1101
  const compactionOptions = {
1233
1102
  lastCompactedAt: ctx.contextCompactedAt ?? undefined,
@@ -1235,6 +1104,7 @@ export async function runAgentLoopImpl(
1235
1104
  conversationOriginChannel:
1236
1105
  getConversationOriginChannel(ctx.conversationId) ?? undefined,
1237
1106
  overrideProfile: resolveCurrentOverrideProfile() ?? null,
1107
+ actorTrustClass: ctx.trustContext?.trustClass,
1238
1108
  };
1239
1109
  let compacted: Awaited<
1240
1110
  ReturnType<typeof ctx.contextWindowManager.maybeCompact>
@@ -1267,7 +1137,11 @@ export async function runAgentLoopImpl(
1267
1137
  { err, phase: "start-of-turn-compaction" },
1268
1138
  "Compaction pipeline timed out — skipping compaction this turn",
1269
1139
  );
1270
- await trackCompactionOutcome(ctx, true, onEvent);
1140
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
1141
+ ctx,
1142
+ true,
1143
+ onEvent,
1144
+ );
1271
1145
  compacted = null;
1272
1146
  } else {
1273
1147
  throw err;
@@ -1280,7 +1154,11 @@ export async function runAgentLoopImpl(
1280
1154
  // path) — treating those as "successful" compactions would silently reset
1281
1155
  // the 3-strike counter and break the invariant.
1282
1156
  if (compacted && compacted.summaryFailed !== undefined) {
1283
- await trackCompactionOutcome(ctx, compacted.summaryFailed, onEvent);
1157
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
1158
+ ctx,
1159
+ compacted.summaryFailed,
1160
+ onEvent,
1161
+ );
1284
1162
  }
1285
1163
  if (compacted?.compacted) {
1286
1164
  await applySuccessfulCompaction(
@@ -1295,12 +1173,7 @@ export async function runAgentLoopImpl(
1295
1173
 
1296
1174
  // Register confirmation outcome tracker so the agent loop can link
1297
1175
  // confirmation decisions to tool_use_ids for persistence.
1298
- ctx.onConfirmationOutcome = (
1299
- requestId,
1300
- confirmationState,
1301
- toolName,
1302
- toolUseId,
1303
- ) => {
1176
+ ctx.onConfirmationOutcome = (requestId, confirmationState, toolUseId) => {
1304
1177
  if (confirmationState === "pending") {
1305
1178
  // Use the toolUseId passed from the prompter (which knows which tool
1306
1179
  // requested confirmation) instead of the ambient state.currentToolUseId,
@@ -1317,7 +1190,7 @@ export async function runAgentLoopImpl(
1317
1190
  const resolvedId =
1318
1191
  state.requestIdToToolUseId.get(requestId) ?? toolUseId;
1319
1192
  if (resolvedId) {
1320
- const name = state.toolUseIdToName.get(resolvedId) ?? toolName ?? "";
1193
+ const name = state.toolUseIdToName.get(resolvedId) ?? "";
1321
1194
  // Build a friendly label from the tool name
1322
1195
  const label =
1323
1196
  TOOL_FRIENDLY_LABEL[name] ??
@@ -1760,8 +1633,21 @@ export async function runAgentLoopImpl(
1760
1633
  // to the reduced `ctx.messages`.
1761
1634
  let reducerCompacted = compactedThisTurn;
1762
1635
 
1636
+ // memory-v3-live: route the turn's `<memory>` block to the v3 injector.
1637
+ // When on, runtime assembly suppresses v2's `<memory>` injection (only
1638
+ // when the v3 injector actually produced a block — otherwise v2 stays as a
1639
+ // fallback) and the provider anchors its long-TTL cache breakpoint on the
1640
+ // most recent STABLE user message, since the latest user message now
1641
+ // carries the volatile per-turn memory block. Flag off → bit-for-bit
1642
+ // identical to today's v2 path.
1643
+ const memoryV3Live = isAssistantFeatureFlagEnabled(
1644
+ "memory-v3-live",
1645
+ getConfig(),
1646
+ );
1647
+
1763
1648
  // Shared injection options — reused whenever we need to re-inject after reduction.
1764
1649
  const injectionOpts = {
1650
+ suppressV2MemoryForV3: memoryV3Live,
1765
1651
  diskPressureContext,
1766
1652
  activeSurface,
1767
1653
  activeDocuments,
@@ -1983,6 +1869,7 @@ export async function runAgentLoopImpl(
1983
1869
  options: {
1984
1870
  ...(opts ?? {}),
1985
1871
  overrideProfile: resolveCurrentOverrideProfile() ?? null,
1872
+ actorTrustClass: ctx.trustContext?.trustClass,
1986
1873
  },
1987
1874
  },
1988
1875
  buildPluginTurnContext(ctx, reqId),
@@ -1996,7 +1883,11 @@ export async function runAgentLoopImpl(
1996
1883
  { err, phase: "overflow-reducer-forced-compaction" },
1997
1884
  "Compaction pipeline timed out — falling through to next reducer tier",
1998
1885
  );
1999
- await trackCompactionOutcome(ctx, true, onEvent);
1886
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
1887
+ ctx,
1888
+ true,
1889
+ onEvent,
1890
+ );
2000
1891
  return {
2001
1892
  messages: msgs,
2002
1893
  compacted: false,
@@ -2018,12 +1909,9 @@ export async function runAgentLoopImpl(
2018
1909
  }
2019
1910
  },
2020
1911
  emitActivityState: () => {
2021
- ctx.emitActivityState(
2022
- "thinking",
2023
- "context_compacting",
2024
- "assistant_turn",
2025
- reqId,
2026
- );
1912
+ ctx.emitActivityState("thinking", "context_compacting", {
1913
+ requestId: reqId,
1914
+ });
2027
1915
  },
2028
1916
  onCompactionResult: async (result, compactedBasis) => {
2029
1917
  // Track circuit-breaker state whenever the reducer invoked
@@ -2036,7 +1924,11 @@ export async function runAgentLoopImpl(
2036
1924
  // truncation-only path, etc.) that shouldn't influence the
2037
1925
  // breaker.
2038
1926
  if (result.summaryFailed !== undefined) {
2039
- await trackCompactionOutcome(ctx, result.summaryFailed, onEvent);
1927
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
1928
+ ctx,
1929
+ result.summaryFailed,
1930
+ onEvent,
1931
+ );
2040
1932
  }
2041
1933
  if (result.compacted) {
2042
1934
  await applySuccessfulCompaction(result, compactedBasis);
@@ -2130,55 +2022,7 @@ export async function runAgentLoopImpl(
2130
2022
  }
2131
2023
  }
2132
2024
 
2133
- // Pre-run repair — routed through the `historyRepair` plugin pipeline so
2134
- // plugins can observe or override repair behavior. The default plugin's
2135
- // middleware is a passthrough; the actual repair runs in the terminal
2136
- // (`defaultHistoryRepairTerminal`).
2137
2025
  let preRepairMessages = runMessages;
2138
- let preRunRepair: HistoryRepairResult | null = null;
2139
- try {
2140
- preRunRepair = await runPipeline<HistoryRepairArgs, HistoryRepairResult>(
2141
- "historyRepair",
2142
- getMiddlewaresFor("historyRepair"),
2143
- async (args) => defaultHistoryRepairTerminal(args),
2144
- { history: runMessages, provider: ctx.provider.name },
2145
- buildPluginTurnContext(ctx, reqId),
2146
- DEFAULT_TIMEOUTS.historyRepair,
2147
- );
2148
- } catch (err) {
2149
- if (err instanceof PluginTimeoutError) {
2150
- // Pipeline exceeded its budget — likely a misbehaving third-party
2151
- // middleware. Degrade gracefully by proceeding with the un-repaired
2152
- // history rather than turn-fatal-erroring; un-repaired history is
2153
- // strictly better than no turn at all, and the provider call itself
2154
- // will still error visibly if the drift is unrecoverable.
2155
- rlog.warn(
2156
- { err, phase: "pre_run" },
2157
- "historyRepair pipeline timed out — proceeding with un-repaired history",
2158
- );
2159
- } else {
2160
- throw err;
2161
- }
2162
- }
2163
- if (preRunRepair !== null) {
2164
- // Always adopt the pipeline's output history — a user `historyRepair`
2165
- // middleware may rewrite `messages` (e.g. provider-specific
2166
- // normalization) without incrementing any of the built-in repair
2167
- // counters. Gating the assignment on `stats` would silently discard
2168
- // those edits and send the un-rewritten history to the provider.
2169
- runMessages = preRunRepair.messages;
2170
- if (
2171
- preRunRepair.stats.assistantToolResultsMigrated > 0 ||
2172
- preRunRepair.stats.missingToolResultsInserted > 0 ||
2173
- preRunRepair.stats.orphanToolResultsDowngraded > 0 ||
2174
- preRunRepair.stats.consecutiveSameRoleMerged > 0
2175
- ) {
2176
- rlog.warn(
2177
- { phase: "pre_run", ...preRunRepair.stats },
2178
- "Repaired runtime history before provider call",
2179
- );
2180
- }
2181
- }
2182
2026
 
2183
2027
  // Replace historical web_search_tool_result blocks with text summaries.
2184
2028
  // The opaque `encrypted_content` tokens Anthropic attaches to each result
@@ -2195,12 +2039,12 @@ export async function runAgentLoopImpl(
2195
2039
  }
2196
2040
 
2197
2041
  // user-prompt-submit hook: plugins may transform `runMessages` right
2198
- // before the agent loop receives them. Fires once per user turn at
2199
- // the primary `agentLoop.run` only — the re-entry / retry calls
2200
- // further down in this function do not refire it (they're not new
2201
- // user submissions). Plugins may mutate `ctx.latestMessages` in place
2202
- // OR return a new context with a fresh array; `runHook` forwards
2203
- // whichever the chain settles on. Order is plugin registration order.
2042
+ // before the agent loop receives them. Fires once per user turn at the
2043
+ // primary `agentLoop.run` only — the re-entry / retry calls further down
2044
+ // in this function do not refire it (they're not new user submissions).
2045
+ // Plugins may mutate `ctx.latestMessages` in place OR return a new
2046
+ // context with a fresh array; `runHook` forwards whichever the chain
2047
+ // settles on. Order is plugin registration order.
2204
2048
  //
2205
2049
  // Fires BEFORE `preRunHistoryLength` is captured so the boundary
2206
2050
  // between pre-existing and hook-emitted messages — consumed by the
@@ -2211,6 +2055,7 @@ export async function runAgentLoopImpl(
2211
2055
  conversationId: ctx.conversationId,
2212
2056
  originalMessages: ctx.messages,
2213
2057
  latestMessages: runMessages,
2058
+ logger: rlog,
2214
2059
  };
2215
2060
  const finalUserPromptCtx = await runHook(
2216
2061
  HOOKS.USER_PROMPT_SUBMIT,
@@ -2234,41 +2079,16 @@ export async function runAgentLoopImpl(
2234
2079
  turnChannelContext: capturedTurnChannelContext,
2235
2080
  turnInterfaceContext: capturedTurnInterfaceContext,
2236
2081
  };
2237
- const eventHandler = (event: AgentEvent) =>
2082
+ const eventHandler = (event: AgentEvent): Promise<void> =>
2238
2083
  dispatchAgentEvent(state, deps, event);
2239
2084
  emitTerminalExit = async (reason: AgentLoopExitReason): Promise<void> => {
2240
2085
  await eventHandler({ type: "agent_loop_exit", reason });
2241
2086
  };
2242
2087
 
2243
- const onCheckpoint = async (
2244
- checkpoint: CheckpointInfo,
2245
- ): Promise<CheckpointDecision> => {
2246
- state.currentTurnToolNames = [];
2247
-
2088
+ const onCheckpoint = async (): Promise<CheckpointDecision> => {
2248
2089
  if (ctx.canHandoffAtCheckpoint()) {
2249
- yieldedForHandoff = true;
2250
- pendingCheckpointYield = "handoff";
2251
- return "yield";
2090
+ return "handoff";
2252
2091
  }
2253
-
2254
- // Mid-loop token budget check: estimate current context size and
2255
- // yield if we're approaching the preflight budget. This lets the
2256
- // conversation-agent-loop run compaction before the provider rejects.
2257
- if (overflowRecovery.enabled) {
2258
- const midLoopThreshold =
2259
- resolveCurrentContextBudget().preflightBudget * 0.85;
2260
- const estimated = await runTokenEstimatePipeline(checkpoint.history);
2261
- if (estimated > midLoopThreshold) {
2262
- rlog.warn(
2263
- { phase: "mid-loop", estimated, threshold: midLoopThreshold },
2264
- "Token estimate approaching budget — yielding for compaction",
2265
- );
2266
- yieldedForBudget = true;
2267
- pendingCheckpointYield = "budget";
2268
- return "yield";
2269
- }
2270
- }
2271
-
2272
2092
  return "continue";
2273
2093
  };
2274
2094
 
@@ -2284,19 +2104,120 @@ export async function runAgentLoopImpl(
2284
2104
  // and overwrites `turnIndex` with its own tool-use iteration counter.
2285
2105
  const loopTurnCtx = buildPluginTurnContext(ctx, reqId);
2286
2106
 
2287
- let updatedHistory = await ctx.agentLoop.run(
2288
- runMessages,
2289
- eventHandler,
2290
- abortController.signal,
2291
- reqId,
2292
- onCheckpoint,
2293
- turnCallSite,
2294
- loopTurnCtx,
2295
- turnOverrideProfile,
2296
- resolveCurrentMaxInputTokens(),
2297
- resolveCurrentOverrideProfile,
2298
- resolveCurrentMaxInputTokens,
2299
- );
2107
+ // Hooks for the loop-owned mid-loop compaction. The agent loop owns the
2108
+ // trigger (its budget gate), the `compaction` pipeline call, the result
2109
+ // interpretation (circuit-breaker bookkeeping + the exhaustion decision),
2110
+ // and the inline continue; these callbacks bridge the durable / injection
2111
+ // state the loop is intentionally blind to. Durable persistence and
2112
+ // re-injection stay orchestrator-supplied for now.
2113
+ const midLoopCompaction: MidLoopCompaction = {
2114
+ prepare: (history) => {
2115
+ // Strip injected context so the compactor summarizes the raw
2116
+ // persistent messages, and commit the stripped set to durable state.
2117
+ const rawHistory = stripInjectionsForCompaction(history);
2118
+ ctx.messages = rawHistory;
2119
+ markHistoryStrippedBestEffort(ctx.conversationId, Date.now(), rlog);
2120
+ return {
2121
+ rawHistory,
2122
+ options: {
2123
+ lastCompactedAt: ctx.contextCompactedAt ?? undefined,
2124
+ force: true,
2125
+ targetInputTokensOverride:
2126
+ resolveCurrentContextBudget().preflightBudget,
2127
+ conversationOriginChannel:
2128
+ getConversationOriginChannel(ctx.conversationId) ?? undefined,
2129
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2130
+ actorTrustClass: ctx.trustContext?.trustClass,
2131
+ },
2132
+ };
2133
+ },
2134
+ applyResult: async (result, rawHistory) => {
2135
+ await applySuccessfulCompaction(result, rawHistory);
2136
+ reducerCompacted = true;
2137
+ shouldInjectWorkspace = true;
2138
+ },
2139
+ reinject: async () => {
2140
+ // stripInjectionsForCompaction() unconditionally removed the existing
2141
+ // NOW.md block, so re-inject the current content regardless of whether
2142
+ // compaction actually ran.
2143
+ const injection = await applyRuntimeInjections(ctx.messages, {
2144
+ ...injectionOpts,
2145
+ pkbContext: currentPkbContent,
2146
+ memoryV2Static: currentMemoryV2Static,
2147
+ nowScratchpad: currentNowContent,
2148
+ workspaceTopLevelContext: shouldInjectWorkspace
2149
+ ? ctx.workspaceTopLevelContext
2150
+ : null,
2151
+ // Suppress the chronological-transcript snapshot once the reducer
2152
+ // has collapsed `ctx.messages`; the captured snapshot reflects the
2153
+ // full persisted transcript and would overwrite compaction.
2154
+ slackChronologicalMessages: reducerCompacted
2155
+ ? null
2156
+ : injectionOpts.slackChronologicalMessages,
2157
+ mode: currentInjectionMode,
2158
+ turnContext: buildPluginTurnContext(ctx, reqId),
2159
+ });
2160
+ runMessages = injection.messages;
2161
+ if (isTrustedActor && currentInjectionMode !== "minimal") {
2162
+ ctx.graphMemory.retrackCachedNodes();
2163
+ }
2164
+ const midLoopCompactStrip =
2165
+ stripHistoricalWebSearchResults(runMessages);
2166
+ if (midLoopCompactStrip.stats.blocksStripped > 0) {
2167
+ rlog.info(
2168
+ { phase: "mid-loop-compact", ...midLoopCompactStrip.stats },
2169
+ "Converted historical web_search_tool_result blocks to text summaries",
2170
+ );
2171
+ runMessages = midLoopCompactStrip.messages;
2172
+ }
2173
+ preRepairMessages = runMessages;
2174
+ preRunHistoryLength = runMessages.length;
2175
+ return runMessages;
2176
+ },
2177
+ };
2178
+
2179
+ /**
2180
+ * Shared closure: runs the agent loop with the orchestrator's turn
2181
+ * context and maps the loop's returned checkpoint pause-reason into the
2182
+ * orchestrator's yield bookkeeping. Returns the updated history so call
2183
+ * sites consume it exactly as before. Pass `compaction` only for the
2184
+ * primary run, where the loop compacts in place when its budget gate
2185
+ * trips; reruns omit it and keep yielding for budget.
2186
+ */
2187
+ const runAgentLoop = async (
2188
+ msgs: Message[],
2189
+ compaction?: MidLoopCompaction,
2190
+ ): Promise<Message[]> => {
2191
+ const { history, exitReason } = await ctx.agentLoop.run(
2192
+ msgs,
2193
+ eventHandler,
2194
+ {
2195
+ signal: abortController.signal,
2196
+ requestId: reqId,
2197
+ onCheckpoint,
2198
+ callSite: turnCallSite,
2199
+ turnContext: loopTurnCtx,
2200
+ overrideProfile: turnOverrideProfile,
2201
+ resolveOverrideProfile: resolveCurrentOverrideProfile,
2202
+ resolveContextWindow,
2203
+ compaction,
2204
+ // memory-v3-live: the latest user message carries the volatile v3
2205
+ // `<memory>` block, so anchor the provider's long-TTL cache breakpoint
2206
+ // on the most recent stable message instead.
2207
+ mutableLatestUserMessage: memoryV3Live,
2208
+ },
2209
+ );
2210
+ if (exitReason === "handoff") {
2211
+ yieldedForHandoff = true;
2212
+ pendingCheckpointYield = "handoff";
2213
+ } else if (exitReason === "budget") {
2214
+ yieldedForBudget = true;
2215
+ pendingCheckpointYield = "budget";
2216
+ }
2217
+ return history;
2218
+ };
2219
+
2220
+ let updatedHistory = await runAgentLoop(runMessages, midLoopCompaction);
2300
2221
 
2301
2222
  rlog.info(
2302
2223
  { resultMessageCount: updatedHistory.length },
@@ -2308,167 +2229,16 @@ export async function runAgentLoopImpl(
2308
2229
  pendingCheckpointYield = null;
2309
2230
  }
2310
2231
 
2311
- // ── Proactive mid-loop compaction ───────────────────────────────
2312
- // When the agent loop yielded because the token budget check in
2313
- // onCheckpoint detected approaching limits, run compaction on the
2314
- // accumulated history and re-enter the agent loop. This is distinct
2315
- // from the reactive convergence loop below that fires after a
2316
- // provider rejection — here we compact *before* hitting the limit.
2317
- let midLoopCompactAttempts = 0;
2318
- while (
2319
- yieldedForBudget &&
2320
- midLoopCompactAttempts <
2321
- resolveCurrentContextBudget().overflowRecovery.maxAttempts &&
2322
- !state.contextTooLargeDetected &&
2323
- !abortController.signal.aborted
2324
- ) {
2325
- midLoopCompactAttempts++;
2326
- yieldedForBudget = false;
2327
- pendingCheckpointYield = null;
2328
-
2329
- rlog.info(
2330
- { phase: "mid-loop-compact" },
2331
- "Running compaction after checkpoint yield",
2332
- );
2333
-
2334
- // Strip injected context from updated history before compacting,
2335
- // so we compact the "raw" persistent messages.
2336
- const rawHistory = stripInjectionsForCompaction(updatedHistory);
2337
- ctx.messages = rawHistory;
2338
- markHistoryStrippedBestEffort(ctx.conversationId, Date.now(), rlog);
2339
-
2340
- ctx.emitActivityState(
2341
- "thinking",
2342
- "context_compacting",
2343
- "assistant_turn",
2344
- reqId,
2345
- "Compacting context",
2346
- );
2347
- let midLoopCompact: Awaited<
2348
- ReturnType<typeof ctx.contextWindowManager.maybeCompact>
2349
- >;
2350
- try {
2351
- midLoopCompact = (await runPipeline<CompactionArgs, CompactionResult>(
2352
- "compaction",
2353
- getMiddlewaresFor("compaction"),
2354
- (args) =>
2355
- defaultCompactionTerminal(args, buildPluginTurnContext(ctx, reqId)),
2356
- {
2357
- messages: ctx.messages,
2358
- signal: abortController.signal,
2359
- options: {
2360
- lastCompactedAt: ctx.contextCompactedAt ?? undefined,
2361
- force: true,
2362
- targetInputTokensOverride:
2363
- resolveCurrentContextBudget().preflightBudget,
2364
- conversationOriginChannel:
2365
- getConversationOriginChannel(ctx.conversationId) ?? undefined,
2366
- overrideProfile: resolveCurrentOverrideProfile() ?? null,
2367
- },
2368
- },
2369
- buildPluginTurnContext(ctx, reqId),
2370
- DEFAULT_TIMEOUTS.compaction,
2371
- )) as Awaited<ReturnType<typeof ctx.contextWindowManager.maybeCompact>>;
2372
- } catch (err) {
2373
- if (err instanceof PluginTimeoutError) {
2374
- // Mid-loop compaction timed out. Record the failure for the
2375
- // circuit breaker and escalate to the convergence loop's more
2376
- // aggressive reducer tiers (tool-result truncation, media
2377
- // stubbing, injection downgrade) by flipping the overflow flag
2378
- // and breaking out of the mid-loop retry. The existing
2379
- // "exhausted all attempts" block further down handles the
2380
- // escalation.
2381
- rlog.warn(
2382
- { err, phase: "mid-loop-compact" },
2383
- "Compaction pipeline timed out — escalating to convergence loop",
2384
- );
2385
- await trackCompactionOutcome(ctx, true, onEvent);
2386
- state.contextTooLargeDetected = true;
2387
- break;
2388
- }
2389
- throw err;
2390
- }
2391
- // `force: true` bypasses the cooldown/threshold gates but early returns
2392
- // for "no eligible messages" / "insufficient messages" still leave
2393
- // `summaryFailed` undefined. Only track when the summary LLM actually ran.
2394
- if (midLoopCompact.summaryFailed !== undefined) {
2395
- await trackCompactionOutcome(
2396
- ctx,
2397
- midLoopCompact.summaryFailed,
2398
- onEvent,
2399
- );
2400
- }
2401
- if (midLoopCompact.compacted) {
2402
- await applySuccessfulCompaction(midLoopCompact, rawHistory);
2403
- reducerCompacted = true;
2404
- shouldInjectWorkspace = true;
2405
- }
2406
-
2407
- // Re-inject runtime context and re-enter the agent loop.
2408
- // stripInjectionsForCompaction() unconditionally removed the existing
2409
- // NOW.md block from ctx.messages above, so we must always re-inject
2410
- // the current content regardless of whether compaction actually ran.
2411
- const injection = await applyRuntimeInjections(ctx.messages, {
2412
- ...injectionOpts,
2413
- pkbContext: currentPkbContent,
2414
- memoryV2Static: currentMemoryV2Static,
2415
- nowScratchpad: currentNowContent,
2416
- workspaceTopLevelContext: shouldInjectWorkspace
2417
- ? ctx.workspaceTopLevelContext
2418
- : null,
2419
- // Suppress the chronological-transcript snapshot once the reducer
2420
- // has collapsed `ctx.messages`; the captured snapshot reflects the
2421
- // full persisted transcript and would overwrite compaction.
2422
- slackChronologicalMessages: reducerCompacted
2423
- ? null
2424
- : injectionOpts.slackChronologicalMessages,
2425
- mode: currentInjectionMode,
2426
- turnContext: buildPluginTurnContext(ctx, reqId),
2427
- });
2428
- runMessages = injection.messages;
2429
- if (isTrustedActor && currentInjectionMode !== "minimal") {
2430
- ctx.graphMemory.retrackCachedNodes();
2431
- }
2432
- const midLoopCompactStrip = stripHistoricalWebSearchResults(runMessages);
2433
- if (midLoopCompactStrip.stats.blocksStripped > 0) {
2434
- rlog.info(
2435
- { phase: "mid-loop-compact", ...midLoopCompactStrip.stats },
2436
- "Converted historical web_search_tool_result blocks to text summaries",
2437
- );
2438
- runMessages = midLoopCompactStrip.messages;
2439
- }
2440
- preRepairMessages = runMessages;
2441
- preRunHistoryLength = runMessages.length;
2442
-
2443
- updatedHistory = await ctx.agentLoop.run(
2444
- runMessages,
2445
- eventHandler,
2446
- abortController.signal,
2447
- reqId,
2448
- onCheckpoint,
2449
- turnCallSite,
2450
- loopTurnCtx,
2451
- turnOverrideProfile,
2452
- resolveCurrentMaxInputTokens(),
2453
- resolveCurrentOverrideProfile,
2454
- resolveCurrentMaxInputTokens,
2455
- );
2456
- }
2457
-
2458
- // If mid-loop compaction exhausted all attempts but the agent loop
2459
- // still yielded (yieldedForBudget is true), the turn is incomplete.
2460
- // Escalate to the convergence loop's more aggressive reducer tiers
2461
- // (tool-result truncation, media stubbing, injection downgrade)
2462
- // instead of silently treating an incomplete turn as done.
2232
+ // The loop compacts in place when its budget gate trips and only yields
2233
+ // `exitReason = "budget"` when that inline compaction timed out or
2234
+ // exhausted its retry budget (the `reinject` hook has already restored
2235
+ // runtime context for the productive case). Escalate to the convergence
2236
+ // loop's more aggressive reducer tiers so a half-finished turn doesn't
2237
+ // reach the user.
2463
2238
  if (yieldedForBudget && !abortController.signal.aborted) {
2464
2239
  rlog.warn(
2465
- {
2466
- phase: "mid-loop-compact",
2467
- midLoopCompactAttempts,
2468
- maxAttempts:
2469
- resolveCurrentContextBudget().overflowRecovery.maxAttempts,
2470
- },
2471
- "Mid-loop compaction exhausted all attempts — escalating to convergence loop",
2240
+ { phase: "mid-loop-compact" },
2241
+ "Inline compaction could not get under budget — escalating to convergence loop",
2472
2242
  );
2473
2243
  state.contextTooLargeDetected = true;
2474
2244
  }
@@ -2482,15 +2252,15 @@ export async function runAgentLoopImpl(
2482
2252
  { phase: "retry" },
2483
2253
  "Provider ordering error detected, attempting one-shot deep-repair retry",
2484
2254
  );
2485
- // Design note: deep-repair intentionally bypasses the `historyRepair`
2486
- // plugin pipeline. Deep-repair is a recovery-only path triggered by a
2487
- // provider ordering error it must be deterministic and unaffected by
2488
- // user middleware that might have caused (or be unable to recover from)
2489
- // the original drift. Plugins can already observe / override the
2490
- // pre-run repair via the `historyRepair` pipeline above; widening that
2491
- // surface to deep-repair is intentionally deferred until there's a
2492
- // concrete plugin-level use case. Do not route this call through
2493
- // `runPipeline` without first revisiting that contract.
2255
+ // Design note: deep-repair intentionally stays a direct call rather
2256
+ // than running through the `user-prompt-submit` hook chain. Deep-repair
2257
+ // is a recovery-only path triggered by a provider ordering error — it
2258
+ // must be deterministic and unaffected by user hooks that might have
2259
+ // caused (or be unable to recover from) the original drift. Plugins can
2260
+ // already observe / transform the pre-run repair via the
2261
+ // `user-prompt-submit` hook (the default history-repair plugin runs
2262
+ // `repairHistory` there); widening that surface to deep-repair is
2263
+ // intentionally deferred until there's a concrete plugin-level use case.
2494
2264
  const retryRepair = deepRepairHistory(runMessages);
2495
2265
  runMessages = retryRepair.messages;
2496
2266
  const retryStrip = stripHistoricalWebSearchResults(runMessages);
@@ -2500,19 +2270,7 @@ export async function runAgentLoopImpl(
2500
2270
  state.orderingErrorDetected = false;
2501
2271
  state.deferredOrderingError = null;
2502
2272
 
2503
- updatedHistory = await ctx.agentLoop.run(
2504
- runMessages,
2505
- eventHandler,
2506
- abortController.signal,
2507
- reqId,
2508
- onCheckpoint,
2509
- turnCallSite,
2510
- loopTurnCtx,
2511
- turnOverrideProfile,
2512
- resolveCurrentMaxInputTokens(),
2513
- resolveCurrentOverrideProfile,
2514
- resolveCurrentMaxInputTokens,
2515
- );
2273
+ updatedHistory = await runAgentLoop(runMessages);
2516
2274
 
2517
2275
  if (state.orderingErrorDetected) {
2518
2276
  rlog.error(
@@ -2571,19 +2329,7 @@ export async function runAgentLoopImpl(
2571
2329
  };
2572
2330
  });
2573
2331
  runMessages = ctx.messages;
2574
- updatedHistory = await ctx.agentLoop.run(
2575
- runMessages,
2576
- eventHandler,
2577
- abortController.signal,
2578
- reqId,
2579
- onCheckpoint,
2580
- turnCallSite,
2581
- loopTurnCtx,
2582
- turnOverrideProfile,
2583
- resolveCurrentMaxInputTokens(),
2584
- resolveCurrentOverrideProfile,
2585
- resolveCurrentMaxInputTokens,
2586
- );
2332
+ updatedHistory = await runAgentLoop(runMessages);
2587
2333
  if (state.imageTooLargeDetected) {
2588
2334
  rlog.error(
2589
2335
  { phase: "image-recovery" },
@@ -2703,7 +2449,7 @@ export async function runAgentLoopImpl(
2703
2449
  "Emergency mid-turn compaction succeeded — bypassing reducer tiers",
2704
2450
  );
2705
2451
  if (emergencyResult.summaryFailed !== undefined) {
2706
- await trackCompactionOutcome(
2452
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
2707
2453
  ctx,
2708
2454
  emergencyResult.summaryFailed,
2709
2455
  onEvent,
@@ -2744,12 +2490,9 @@ export async function runAgentLoopImpl(
2744
2490
  "Context too large — applying next reducer tier",
2745
2491
  );
2746
2492
 
2747
- ctx.emitActivityState(
2748
- "thinking",
2749
- "context_compacting",
2750
- "assistant_turn",
2751
- reqId,
2752
- );
2493
+ ctx.emitActivityState("thinking", "context_compacting", {
2494
+ requestId: reqId,
2495
+ });
2753
2496
  const convergenceCompactionBasis = ctx.messages;
2754
2497
  const step = await reduceContextOverflow(
2755
2498
  convergenceCompactionBasis,
@@ -2765,6 +2508,7 @@ export async function runAgentLoopImpl(
2765
2508
  ctx.contextWindowManager.maybeCompact(msgs, signal!, {
2766
2509
  ...(opts ?? {}),
2767
2510
  overrideProfile: resolveCurrentOverrideProfile() ?? null,
2511
+ actorTrustClass: ctx.trustContext?.trustClass,
2768
2512
  }),
2769
2513
  abortController.signal,
2770
2514
  );
@@ -2781,7 +2525,7 @@ export async function runAgentLoopImpl(
2781
2525
  step.compactionResult &&
2782
2526
  step.compactionResult.summaryFailed !== undefined
2783
2527
  ) {
2784
- await trackCompactionOutcome(
2528
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
2785
2529
  ctx,
2786
2530
  step.compactionResult.summaryFailed,
2787
2531
  onEvent,
@@ -2831,19 +2575,7 @@ export async function runAgentLoopImpl(
2831
2575
  state.contextTooLargeDetected = false;
2832
2576
  yieldedForBudget = false;
2833
2577
 
2834
- updatedHistory = await ctx.agentLoop.run(
2835
- runMessages,
2836
- eventHandler,
2837
- abortController.signal,
2838
- reqId,
2839
- onCheckpoint,
2840
- turnCallSite,
2841
- loopTurnCtx,
2842
- turnOverrideProfile,
2843
- resolveCurrentMaxInputTokens(),
2844
- resolveCurrentOverrideProfile,
2845
- resolveCurrentMaxInputTokens,
2846
- );
2578
+ updatedHistory = await runAgentLoop(runMessages);
2847
2579
 
2848
2580
  // If the rerun still yields at checkpoint, the turn is still
2849
2581
  // incomplete — continue reducing through the remaining tiers
@@ -2884,12 +2616,9 @@ export async function runAgentLoopImpl(
2884
2616
 
2885
2617
  if (action === "auto_compress_latest_turn") {
2886
2618
  // Auto-compress without asking — users opt out via the "drop" policy.
2887
- ctx.emitActivityState(
2888
- "thinking",
2889
- "context_compacting",
2890
- "assistant_turn",
2891
- reqId,
2892
- );
2619
+ ctx.emitActivityState("thinking", "context_compacting", {
2620
+ requestId: reqId,
2621
+ });
2893
2622
  let emergencyCompact: Awaited<
2894
2623
  ReturnType<typeof ctx.contextWindowManager.maybeCompact>
2895
2624
  > | null = null;
@@ -2931,7 +2660,11 @@ export async function runAgentLoopImpl(
2931
2660
  { err, phase: "emergency-compaction" },
2932
2661
  "Emergency compaction pipeline timed out — continuing with overflow fallback",
2933
2662
  );
2934
- await trackCompactionOutcome(ctx, true, onEvent);
2663
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
2664
+ ctx,
2665
+ true,
2666
+ onEvent,
2667
+ );
2935
2668
  emergencyCompact = null;
2936
2669
  } else {
2937
2670
  throw err;
@@ -2943,7 +2676,7 @@ export async function runAgentLoopImpl(
2943
2676
  emergencyCompact &&
2944
2677
  emergencyCompact.summaryFailed !== undefined
2945
2678
  ) {
2946
- await trackCompactionOutcome(
2679
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
2947
2680
  ctx,
2948
2681
  emergencyCompact.summaryFailed,
2949
2682
  onEvent,
@@ -2987,19 +2720,7 @@ export async function runAgentLoopImpl(
2987
2720
  preRunHistoryLength = runMessages.length;
2988
2721
  state.contextTooLargeDetected = false;
2989
2722
 
2990
- updatedHistory = await ctx.agentLoop.run(
2991
- runMessages,
2992
- eventHandler,
2993
- abortController.signal,
2994
- reqId,
2995
- onCheckpoint,
2996
- turnCallSite,
2997
- loopTurnCtx,
2998
- turnOverrideProfile,
2999
- resolveCurrentMaxInputTokens(),
3000
- resolveCurrentOverrideProfile,
3001
- resolveCurrentMaxInputTokens,
3002
- );
2723
+ updatedHistory = await runAgentLoop(runMessages);
3003
2724
  }
3004
2725
  // action === "fail_gracefully" falls through to the final error below
3005
2726
  }
@@ -3389,7 +3110,10 @@ export async function runAgentLoopImpl(
3389
3110
  // so the client can re-enable the UI without delay.
3390
3111
  if (abortController.signal.aborted) {
3391
3112
  syncLastAssistantMessageToDisk();
3392
- ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3113
+ ctx.emitActivityState("idle", "generation_cancelled", {
3114
+ anchor: "global",
3115
+ requestId: reqId,
3116
+ });
3393
3117
  ctx.traceEmitter.emit(
3394
3118
  "generation_cancelled",
3395
3119
  "Generation cancelled by user",
@@ -3433,7 +3157,10 @@ export async function runAgentLoopImpl(
3433
3157
  await emitTerminalExit?.("aborted_after_checkpoint");
3434
3158
  pendingCheckpointYield = null;
3435
3159
  }
3436
- ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3160
+ ctx.emitActivityState("idle", "generation_cancelled", {
3161
+ anchor: "global",
3162
+ requestId: reqId,
3163
+ });
3437
3164
  ctx.traceEmitter.emit(
3438
3165
  "generation_cancelled",
3439
3166
  "Generation cancelled by user",
@@ -3474,7 +3201,10 @@ export async function runAgentLoopImpl(
3474
3201
  });
3475
3202
  publishLoopMessagesChanged();
3476
3203
  } else {
3477
- ctx.emitActivityState("idle", "message_complete", "global", reqId);
3204
+ ctx.emitActivityState("idle", "message_complete", {
3205
+ anchor: "global",
3206
+ requestId: reqId,
3207
+ });
3478
3208
  ctx.traceEmitter.emit(
3479
3209
  "message_complete",
3480
3210
  "Message processing complete",
@@ -3497,42 +3227,6 @@ export async function runAgentLoopImpl(
3497
3227
  : {}),
3498
3228
  });
3499
3229
  publishLoopMessagesChanged();
3500
-
3501
- // Proactive artifact: fire once when the processed turn was the 4th user message.
3502
- // Only trigger for real user-authored turns (not subagent/system messages).
3503
- {
3504
- const paConv = getConversation(ctx.conversationId);
3505
- if (
3506
- paConv &&
3507
- paConv.conversationType === "standard" &&
3508
- options?.isUserMessage
3509
- ) {
3510
- void (async () => {
3511
- try {
3512
- if (hasProactiveArtifactCompleted()) return;
3513
- const userMsg = getMessageById(
3514
- userMessageId,
3515
- ctx.conversationId,
3516
- );
3517
- if (!userMsg) return;
3518
- if (!tryClaimProactiveArtifactTrigger(userMsg.createdAt))
3519
- return;
3520
- await runProactiveArtifactJob({
3521
- conversationId: ctx.conversationId,
3522
- userMessageCutoff: userMsg.createdAt,
3523
- assistantMessageId: state.lastAssistantMessageId,
3524
- suppressAppBuild: state.appBuildToolUsedThisRun,
3525
- broadcastMessage,
3526
- });
3527
- } catch (err) {
3528
- log.warn(
3529
- { err, conversationId: ctx.conversationId },
3530
- "Proactive artifact trigger failed",
3531
- );
3532
- }
3533
- })();
3534
- }
3535
- }
3536
3230
  }
3537
3231
  }
3538
3232
 
@@ -3569,7 +3263,10 @@ export async function runAgentLoopImpl(
3569
3263
  await emitTerminalExit?.("aborted_after_checkpoint");
3570
3264
  pendingCheckpointYield = null;
3571
3265
  }
3572
- ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3266
+ ctx.emitActivityState("idle", "generation_cancelled", {
3267
+ anchor: "global",
3268
+ requestId: reqId,
3269
+ });
3573
3270
  rlog.info("Generation cancelled by user");
3574
3271
  ctx.traceEmitter.emit(
3575
3272
  "generation_cancelled",
@@ -3585,7 +3282,10 @@ export async function runAgentLoopImpl(
3585
3282
  });
3586
3283
  publishLoopMessagesChanged();
3587
3284
  } else {
3588
- ctx.emitActivityState("idle", "error_terminal", "global", reqId);
3285
+ ctx.emitActivityState("idle", "error_terminal", {
3286
+ anchor: "global",
3287
+ requestId: reqId,
3288
+ });
3589
3289
  const message = err instanceof Error ? err.message : String(err);
3590
3290
  const errorClass = err instanceof Error ? err.constructor.name : "Error";
3591
3291
  rlog.error({ err }, "Conversation processing error");