@vellumai/assistant 0.8.5 → 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 (1171) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/Dockerfile +1 -0
  4. package/bun.lock +11 -2
  5. package/bunfig.toml +6 -1
  6. package/docker-entrypoint.sh +8 -6
  7. package/docs/credential-execution-service.md +6 -6
  8. package/docs/plugins.md +67 -31
  9. package/examples/plugins/echo/register.ts +4 -7
  10. package/knip.json +1 -0
  11. package/node_modules/@vellumai/environments/bun.lock +24 -0
  12. package/node_modules/@vellumai/environments/package.json +18 -0
  13. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
  14. package/node_modules/@vellumai/environments/src/index.ts +11 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
  16. package/node_modules/@vellumai/environments/src/types.ts +70 -0
  17. package/node_modules/@vellumai/environments/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
  19. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +15 -17
  20. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +10 -3
  21. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  22. package/openapi.yaml +5585 -469
  23. package/package.json +7 -3
  24. package/scripts/generate-openapi.ts +20 -13
  25. package/src/__tests__/actor-token-service.test.ts +3 -2
  26. package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
  27. package/src/__tests__/agent-loop-exit-reason.test.ts +336 -42
  28. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
  29. package/src/__tests__/agent-loop-override-profile.test.ts +21 -33
  30. package/src/__tests__/agent-loop-provider-error-recording.test.ts +6 -4
  31. package/src/__tests__/agent-loop-thinking.test.ts +17 -12
  32. package/src/__tests__/agent-loop.test.ts +207 -341
  33. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +5 -2
  34. package/src/__tests__/agent-wake-override-profile.test.ts +23 -40
  35. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  36. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  37. package/src/__tests__/anthropic-provider.test.ts +201 -55
  38. package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
  39. package/src/__tests__/app-control-flow.test.ts +5 -0
  40. package/src/__tests__/approval-cascade.test.ts +5 -11
  41. package/src/__tests__/approval-routes-http.test.ts +13 -15
  42. package/src/__tests__/assert-not-live-db.ts +79 -0
  43. package/src/__tests__/assistant-event.test.ts +15 -0
  44. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -27
  45. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  46. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  47. package/src/__tests__/avatar-e2e.test.ts +7 -37
  48. package/src/__tests__/avatar-generator.test.ts +12 -42
  49. package/src/__tests__/avatar-identity-sync.test.ts +28 -3
  50. package/src/__tests__/background-shell-bash.test.ts +3 -7
  51. package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
  52. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  53. package/src/__tests__/btw-routes.test.ts +10 -14
  54. package/src/__tests__/call-controller.test.ts +3 -2
  55. package/src/__tests__/call-pointer-messages.test.ts +5 -3
  56. package/src/__tests__/call-site-routing-provider.test.ts +22 -40
  57. package/src/__tests__/catalog-files.test.ts +1 -0
  58. package/src/__tests__/channel-approval-routes.test.ts +51 -22
  59. package/src/__tests__/channel-approvals.test.ts +3 -1
  60. package/src/__tests__/channel-guardian.test.ts +3 -2
  61. package/src/__tests__/channel-invite-transport.test.ts +1 -5
  62. package/src/__tests__/channel-readiness-routes.test.ts +0 -4
  63. package/src/__tests__/channel-readiness-slack-remote.test.ts +170 -0
  64. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  65. package/src/__tests__/channel-retry-sweep.test.ts +388 -79
  66. package/src/__tests__/checker.test.ts +12 -12
  67. package/src/__tests__/circuit-breaker-pipeline.test.ts +3 -3
  68. package/src/__tests__/clawhub-files.test.ts +1 -0
  69. package/src/__tests__/compaction-events.test.ts +6 -17
  70. package/src/__tests__/compaction-pipeline.test.ts +1 -1
  71. package/src/__tests__/compaction-timeout-recovery.test.ts +37 -48
  72. package/src/__tests__/compaction-trail-store.test.ts +186 -0
  73. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  74. package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
  75. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  76. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  77. package/src/__tests__/computer-use-tools.test.ts +14 -16
  78. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  79. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  80. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  81. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  82. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  83. package/src/__tests__/config-schema.test.ts +10 -10
  84. package/src/__tests__/config-watcher.test.ts +28 -0
  85. package/src/__tests__/connection-model-compat.test.ts +83 -0
  86. package/src/__tests__/contacts-tools.test.ts +3 -2
  87. package/src/__tests__/context-search-agent-runner.test.ts +6 -3
  88. package/src/__tests__/context-token-estimator.test.ts +56 -0
  89. package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
  90. package/src/__tests__/conversation-abort-tool-results.test.ts +19 -7
  91. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +4 -2
  92. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  93. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -27
  94. package/src/__tests__/conversation-agent-loop-overflow.test.ts +464 -90
  95. package/src/__tests__/conversation-agent-loop.test.ts +1069 -64
  96. package/src/__tests__/conversation-analysis-routes.test.ts +2 -3
  97. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  98. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -1
  99. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  100. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  101. package/src/__tests__/conversation-clear-safety.test.ts +20 -10
  102. package/src/__tests__/conversation-confirmation-signals.test.ts +16 -45
  103. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  104. package/src/__tests__/conversation-disk-view.test.ts +10 -17
  105. package/src/__tests__/conversation-error.test.ts +30 -0
  106. package/src/__tests__/conversation-fork-crud.test.ts +132 -157
  107. package/src/__tests__/conversation-fork-route.test.ts +19 -16
  108. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  109. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  110. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  111. package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
  112. package/src/__tests__/conversation-lifecycle.test.ts +4 -2
  113. package/src/__tests__/conversation-list-source.test.ts +3 -2
  114. package/src/__tests__/conversation-load-history-repair.test.ts +5 -3
  115. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -1
  116. package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
  117. package/src/__tests__/conversation-pairing.test.ts +87 -4
  118. package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
  119. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +30 -7
  120. package/src/__tests__/conversation-process-callsite.test.ts +28 -30
  121. package/src/__tests__/conversation-provider-retry-repair.test.ts +58 -44
  122. package/src/__tests__/conversation-queue.test.ts +603 -455
  123. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -20
  124. package/src/__tests__/conversation-routes-guardian-reply.test.ts +35 -10
  125. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -4
  126. package/src/__tests__/conversation-runtime-assembly.test.ts +98 -22
  127. package/src/__tests__/conversation-runtime-workspace.test.ts +19 -1
  128. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  129. package/src/__tests__/conversation-slash-queue.test.ts +120 -62
  130. package/src/__tests__/conversation-slash-unknown.test.ts +18 -15
  131. package/src/__tests__/conversation-speed-override.test.ts +9 -22
  132. package/src/__tests__/conversation-stream-state.test.ts +484 -0
  133. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +52 -15
  134. package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
  135. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  136. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  137. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  138. package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
  139. package/src/__tests__/conversation-surfaces-table-action.test.ts +13 -32
  140. package/src/__tests__/conversation-sync-tags.test.ts +128 -12
  141. package/src/__tests__/conversation-title-service.test.ts +1 -0
  142. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +53 -11
  143. package/src/__tests__/conversation-unread-route.test.ts +14 -2
  144. package/src/__tests__/conversation-usage.test.ts +1 -2
  145. package/src/__tests__/conversation-wipe.test.ts +1 -1
  146. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
  147. package/src/__tests__/conversation-workspace-injection.test.ts +53 -22
  148. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +32 -7
  149. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  150. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  151. package/src/__tests__/credential-execution-client.test.ts +72 -1
  152. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  153. package/src/__tests__/credential-execution-tools.test.ts +1 -2
  154. package/src/__tests__/credential-health-service.test.ts +252 -3
  155. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  156. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  157. package/src/__tests__/credential-vault.test.ts +5 -5
  158. package/src/__tests__/cross-provider-web-search.test.ts +61 -3
  159. package/src/__tests__/cu-unified-flow.test.ts +26 -1
  160. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  161. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  162. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  163. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  164. package/src/__tests__/db-schedule-syntax-migration.test.ts +11 -0
  165. package/src/__tests__/db-test-helpers.ts +58 -0
  166. package/src/__tests__/disk-pressure-guard.test.ts +119 -36
  167. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  168. package/src/__tests__/disk-pressure-routes.test.ts +9 -35
  169. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  170. package/src/__tests__/dm-persistence.test.ts +33 -42
  171. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  172. package/src/__tests__/document-find-replace.test.ts +3 -2
  173. package/src/__tests__/document-tool-security.test.ts +81 -2
  174. package/src/__tests__/dynamic-page-surface.test.ts +68 -0
  175. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  176. package/src/__tests__/edit-propagation.test.ts +1 -2
  177. package/src/__tests__/empty-response-pipeline.test.ts +127 -5
  178. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  179. package/src/__tests__/encrypted-store.test.ts +11 -9
  180. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  181. package/src/__tests__/filing-service.test.ts +3 -2
  182. package/src/__tests__/first-greeting.test.ts +103 -12
  183. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  184. package/src/__tests__/gemini-inline-media.test.ts +78 -0
  185. package/src/__tests__/gemini-provider.test.ts +375 -26
  186. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  187. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  188. package/src/__tests__/guardian-routing-state.test.ts +60 -71
  189. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  190. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +10 -7
  191. package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
  192. package/src/__tests__/heartbeat-service.test.ts +3 -1
  193. package/src/__tests__/helpers/mock-logger.ts +26 -0
  194. package/src/__tests__/history-repair-hook.test.ts +161 -0
  195. package/src/__tests__/history-repair-observability.test.ts +1 -1
  196. package/src/__tests__/history-repair.test.ts +2 -1
  197. package/src/__tests__/host-app-control-proxy.test.ts +2 -0
  198. package/src/__tests__/host-bash-routes.test.ts +1 -0
  199. package/src/__tests__/host-cu-proxy.test.ts +2 -0
  200. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  201. package/src/__tests__/host-file-edit-tool.test.ts +4 -2
  202. package/src/__tests__/host-file-proxy.test.ts +31 -0
  203. package/src/__tests__/host-file-read-tool.test.ts +4 -2
  204. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  205. package/src/__tests__/host-file-write-tool.test.ts +9 -3
  206. package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
  207. package/src/__tests__/host-shell-tool.test.ts +11 -5
  208. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  209. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  210. package/src/__tests__/http-user-message-parity.test.ts +31 -9
  211. package/src/__tests__/identity-intro-cache.test.ts +154 -22
  212. package/src/__tests__/inbound-slack-persistence.test.ts +51 -74
  213. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  214. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  215. package/src/__tests__/injector-background-turn.test.ts +1 -1
  216. package/src/__tests__/injector-chain.test.ts +1 -1
  217. package/src/__tests__/injector-disk-pressure.test.ts +4 -18
  218. package/src/__tests__/injector-document-comments.test.ts +1 -1
  219. package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
  220. package/src/__tests__/injector-v3-suppression.test.ts +220 -0
  221. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  222. package/src/__tests__/list-messages-attachments.test.ts +7 -8
  223. package/src/__tests__/list-messages-hidden-metadata.test.ts +93 -11
  224. package/src/__tests__/list-messages-page-latest.test.ts +0 -1
  225. package/src/__tests__/list-messages-tool-merge.test.ts +36 -6
  226. package/src/__tests__/llm-call-pipeline.test.ts +21 -15
  227. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  228. package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
  229. package/src/__tests__/llm-resolver.test.ts +346 -39
  230. package/src/__tests__/llm-schema.test.ts +1 -1
  231. package/src/__tests__/llm-usage-store.test.ts +45 -0
  232. package/src/__tests__/log-export-routes.test.ts +59 -0
  233. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
  234. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  235. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  236. package/src/__tests__/mcp-auth-routes.test.ts +15 -10
  237. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  238. package/src/__tests__/mcp-health-check.test.ts +18 -13
  239. package/src/__tests__/memory-retrieval-pipeline.test.ts +1 -1
  240. package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
  241. package/src/__tests__/messaging-send-tool.test.ts +9 -4
  242. package/src/__tests__/migration-export-http.test.ts +12 -12
  243. package/src/__tests__/migration-import-commit-http.test.ts +8 -8
  244. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  245. package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
  246. package/src/__tests__/migration-validate-http.test.ts +3 -3
  247. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  248. package/src/__tests__/model-intents.test.ts +3 -3
  249. package/src/__tests__/native-web-search.test.ts +44 -22
  250. package/src/__tests__/notification-decision-identity.test.ts +9 -18
  251. package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
  252. package/src/__tests__/notification-deep-link.test.ts +62 -0
  253. package/src/__tests__/oauth-commands-routes.test.ts +38 -1
  254. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  255. package/src/__tests__/oauth-store.test.ts +3 -2
  256. package/src/__tests__/onboarding-template-contract.test.ts +13 -2
  257. package/src/__tests__/openai-provider.test.ts +74 -79
  258. package/src/__tests__/openai-responses-provider.test.ts +90 -86
  259. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  260. package/src/__tests__/outbound-slack-persistence.test.ts +48 -2
  261. package/src/__tests__/overflow-reduce-pipeline.test.ts +2 -4
  262. package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
  263. package/src/__tests__/persistence-pipeline.test.ts +154 -27
  264. package/src/__tests__/persistence-secret-redaction.test.ts +85 -13
  265. package/src/__tests__/pipeline-runner.test.ts +2 -3
  266. package/src/__tests__/plugin-bootstrap.test.ts +60 -36
  267. package/src/__tests__/plugin-route-contribution.test.ts +6 -16
  268. package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
  269. package/src/__tests__/plugin-tool-contribution.test.ts +51 -64
  270. package/src/__tests__/plugin-types.test.ts +7 -14
  271. package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
  272. package/src/__tests__/process-message-background-slack.test.ts +38 -32
  273. package/src/__tests__/process-message-display-content.test.ts +49 -64
  274. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  275. package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
  276. package/src/__tests__/provider-error-scenarios.test.ts +7 -6
  277. package/src/__tests__/provider-platform-proxy-integration.test.ts +215 -8
  278. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  279. package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
  280. package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
  281. package/src/__tests__/provider-usage-tracking.test.ts +0 -6
  282. package/src/__tests__/ratelimit.test.ts +9 -4
  283. package/src/__tests__/recording-handler.test.ts +1 -0
  284. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  285. package/src/__tests__/registry.test.ts +82 -76
  286. package/src/__tests__/relay-server.test.ts +30 -23
  287. package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
  288. package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
  289. package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
  290. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  291. package/src/__tests__/runtime-events-sse-reconnect.test.ts +353 -0
  292. package/src/__tests__/schedule-routes.test.ts +80 -10
  293. package/src/__tests__/schedule-store.test.ts +83 -1
  294. package/src/__tests__/schedule-tools.test.ts +125 -0
  295. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  296. package/src/__tests__/secret-ingress-http.test.ts +7 -3
  297. package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
  298. package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
  299. package/src/__tests__/secret-response-routing.test.ts +13 -11
  300. package/src/__tests__/secure-keys.test.ts +3 -3
  301. package/src/__tests__/send-endpoint-busy.test.ts +83 -43
  302. package/src/__tests__/server-history-render.test.ts +4 -1
  303. package/src/__tests__/shell-observability.test.ts +249 -0
  304. package/src/__tests__/skill-feature-flags-integration.test.ts +19 -21
  305. package/src/__tests__/skill-feature-flags.test.ts +20 -22
  306. package/src/__tests__/skill-load-feature-flag.test.ts +15 -15
  307. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  308. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  309. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  310. package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
  311. package/src/__tests__/skillssh-files.test.ts +1 -0
  312. package/src/__tests__/slack-channel-config.test.ts +3 -3
  313. package/src/__tests__/starter-task-flow.test.ts +6 -6
  314. package/src/__tests__/strip-memory-injections.test.ts +102 -14
  315. package/src/__tests__/subagent-call-site-routing.test.ts +13 -5
  316. package/src/__tests__/subagent-disposal.test.ts +27 -8
  317. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  318. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  319. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  320. package/src/__tests__/subagent-notify-parent.test.ts +5 -4
  321. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  322. package/src/__tests__/subagent-tools.test.ts +2 -1
  323. package/src/__tests__/suggestion-routes.test.ts +4 -3
  324. package/src/__tests__/sync-message-contract.test.ts +19 -16
  325. package/src/__tests__/system-prompt.test.ts +92 -0
  326. package/src/__tests__/terminal-tools.test.ts +3 -24
  327. package/src/__tests__/test-preload-verifier.ts +68 -0
  328. package/src/__tests__/test-preload.ts +32 -39
  329. package/src/__tests__/thread-backfill.test.ts +4 -9
  330. package/src/__tests__/title-generate-pipeline.test.ts +1 -1
  331. package/src/__tests__/token-estimate-pipeline.test.ts +2 -4
  332. package/src/__tests__/tool-error-pipeline.test.ts +2 -2
  333. package/src/__tests__/tool-execute-pipeline.test.ts +1 -1
  334. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  335. package/src/__tests__/tool-executor.test.ts +55 -10
  336. package/src/__tests__/tool-preview-lifecycle.test.ts +14 -11
  337. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  338. package/src/__tests__/tool-result-truncate-pipeline.test.ts +9 -12
  339. package/src/__tests__/tool-result-truncation.test.ts +3 -1
  340. package/src/__tests__/tools-audio-read.test.ts +113 -0
  341. package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
  342. package/src/__tests__/turn-events-store.test.ts +11 -7
  343. package/src/__tests__/twilio-routes.test.ts +3 -2
  344. package/src/__tests__/validate-input.test.ts +381 -0
  345. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  346. package/src/__tests__/voice-scoped-grant-consumer.test.ts +10 -7
  347. package/src/__tests__/voice-session-bridge.test.ts +50 -35
  348. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  349. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  350. package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
  351. package/src/acp/prepare-agent-env.ts +52 -11
  352. package/src/acp/session-manager.ts +5 -6
  353. package/src/agent/compaction-circuit.ts +140 -0
  354. package/src/agent/loop.ts +489 -85
  355. package/src/api/README.md +126 -2
  356. package/src/api/constants/call-sites.ts +27 -0
  357. package/src/api/constants/tool-execution.ts +21 -0
  358. package/src/api/events/assistant-activity-state.ts +75 -0
  359. package/src/api/events/assistant-outbound-attachment.ts +49 -0
  360. package/src/api/events/assistant-text-delta.ts +30 -0
  361. package/src/api/events/assistant-turn-start.ts +31 -0
  362. package/src/api/events/avatar-updated.ts +24 -0
  363. package/src/api/events/compaction-circuit-closed.ts +26 -0
  364. package/src/api/events/compaction-circuit-open.ts +28 -0
  365. package/src/api/events/confirmation-request.ts +114 -0
  366. package/src/api/events/contact-request.ts +33 -0
  367. package/src/api/events/conversation-error.ts +77 -0
  368. package/src/api/events/conversation-list-invalidated.ts +38 -0
  369. package/src/api/events/conversation-title-updated.ts +24 -0
  370. package/src/api/events/disk-pressure-status-changed.ts +61 -0
  371. package/src/api/events/document-comment-created.ts +44 -0
  372. package/src/api/events/document-comment-deleted.ts +22 -0
  373. package/src/api/events/document-comment-reopened.ts +23 -0
  374. package/src/api/events/document-comment-resolved.ts +25 -0
  375. package/src/api/events/document-editor-update.ts +27 -0
  376. package/src/api/events/error.ts +32 -0
  377. package/src/api/events/generation-cancelled.ts +22 -0
  378. package/src/api/events/generation-handoff.ts +39 -0
  379. package/src/api/events/home-feed-updated.ts +26 -0
  380. package/src/api/events/identity-changed.ts +32 -0
  381. package/src/api/events/interaction-resolved.ts +50 -0
  382. package/src/api/events/message-complete.ts +40 -0
  383. package/src/api/events/message-dequeued.ts +21 -0
  384. package/src/api/events/message-queued-deleted.ts +23 -0
  385. package/src/api/events/message-queued.ts +22 -0
  386. package/src/api/events/message-request-complete.ts +29 -0
  387. package/src/api/events/navigate-settings.ts +20 -0
  388. package/src/api/events/notification-intent.ts +33 -0
  389. package/src/api/events/open-url.ts +28 -0
  390. package/src/api/events/question-request.ts +67 -0
  391. package/src/{events → api/events}/relationship-state-updated.ts +6 -8
  392. package/src/api/events/secret-request.ts +42 -0
  393. package/src/api/events/subagent-event.ts +79 -0
  394. package/src/api/events/subagent-spawned.ts +40 -0
  395. package/src/api/events/subagent-status-changed.ts +65 -0
  396. package/src/api/events/sync-changed.ts +29 -0
  397. package/src/api/events/tool-result.ts +129 -0
  398. package/src/api/events/tool-use-start.ts +30 -0
  399. package/src/api/events/turn-profile-auto-routed.ts +28 -0
  400. package/src/api/events/ui-surface-complete.ts +30 -0
  401. package/src/api/events/ui-surface-dismiss.ts +22 -0
  402. package/src/api/events/ui-surface-show.ts +67 -0
  403. package/src/api/events/ui-surface-update.ts +26 -0
  404. package/src/api/events/usage-update.ts +34 -0
  405. package/src/api/events/user-message-echo.ts +35 -0
  406. package/src/api/index.ts +482 -3
  407. package/src/api/requests/dictation.ts +45 -0
  408. package/src/api/responses/disk-pressure-status.ts +26 -0
  409. package/src/api/responses/home.ts +217 -0
  410. package/src/api/responses/llm-context-response.ts +41 -0
  411. package/src/api/responses/llm-request-log-entry.ts +93 -0
  412. package/src/api/responses/memory-recall-log.ts +65 -0
  413. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  414. package/src/api/responses/memory-v3-selection-log.ts +50 -0
  415. package/src/api/responses/subagent-detail.ts +48 -0
  416. package/src/approvals/guardian-decision-primitive.ts +7 -15
  417. package/src/approvals/guardian-request-resolvers.ts +6 -9
  418. package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
  419. package/src/avatar/__tests__/avatar-store.test.ts +193 -0
  420. package/src/avatar/avatar-manifest.ts +195 -0
  421. package/src/avatar/avatar-store.ts +113 -0
  422. package/src/avatar/traits-png-sync.ts +8 -2
  423. package/src/background-wake/background-wake-routes.test.ts +687 -52
  424. package/src/background-wake/next-wake.test.ts +31 -1
  425. package/src/background-wake/next-wake.ts +4 -1
  426. package/src/background-wake/platform-client.test.ts +308 -0
  427. package/src/background-wake/platform-client.ts +167 -0
  428. package/src/background-wake/publisher.ts +91 -0
  429. package/src/background-wake/runtime-registry.ts +2 -2
  430. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  431. package/src/calls/call-conversation-messages.ts +6 -4
  432. package/src/calls/guardian-action-sweep.ts +6 -4
  433. package/src/calls/guardian-dispatch.ts +1 -0
  434. package/src/calls/relay-server.ts +12 -8
  435. package/src/calls/voice-session-bridge.ts +17 -31
  436. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  437. package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
  438. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  439. package/src/cli/commands/avatar.ts +17 -11
  440. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  441. package/src/cli/commands/channels/index.ts +229 -0
  442. package/src/cli/commands/conversations.ts +15 -1
  443. package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
  444. package/src/cli/commands/db/__tests__/status.test.ts +253 -0
  445. package/src/cli/commands/db/format.ts +48 -0
  446. package/src/cli/commands/db/index.ts +29 -0
  447. package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
  448. package/src/cli/commands/db/repair-step-integrity.ts +146 -0
  449. package/src/cli/commands/db/repair-steps.ts +164 -0
  450. package/src/cli/commands/db/repair.ts +141 -0
  451. package/src/cli/commands/db/status.ts +366 -0
  452. package/src/cli/commands/memory-v3.ts +168 -203
  453. package/src/cli/commands/notifications.ts +365 -55
  454. package/src/cli/lib/cli-colors.ts +24 -6
  455. package/src/cli/lib/open-browser.ts +7 -2
  456. package/src/cli/program.ts +6 -5
  457. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  458. package/src/config/assistant-feature-flags.ts +25 -44
  459. package/src/config/bundled-skills/app-builder/SKILL.md +14 -3
  460. package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
  461. package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
  462. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
  463. package/src/config/bundled-skills/schedule/SKILL.md +2 -2
  464. package/src/config/bundled-skills/schedule/TOOLS.json +10 -2
  465. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  466. package/src/config/call-site-defaults.ts +3 -8
  467. package/src/config/feature-flag-cache.ts +86 -0
  468. package/src/config/feature-flag-registry.json +42 -26
  469. package/src/config/llm-context-resolution.ts +10 -1
  470. package/src/config/llm-resolver.ts +121 -15
  471. package/src/config/loader.ts +4 -5
  472. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -211
  473. package/src/config/schemas/call-site-catalog.ts +8 -15
  474. package/src/config/schemas/heartbeat.ts +1 -1
  475. package/src/config/schemas/llm.ts +92 -4
  476. package/src/config/schemas/memory-lifecycle.ts +24 -0
  477. package/src/config/schemas/memory-v2.ts +0 -227
  478. package/src/config/schemas/memory-v3.ts +39 -0
  479. package/src/config/schemas/memory.ts +6 -1
  480. package/src/config/schemas/services.ts +6 -2
  481. package/src/config/schemas/timeouts.ts +3 -1
  482. package/src/config/seed-inference-profiles.ts +36 -16
  483. package/src/context/compactor.ts +54 -31
  484. package/src/context/token-estimator.ts +29 -5
  485. package/src/context/tool-result-truncation.ts +1 -43
  486. package/src/context/window-manager.ts +138 -20
  487. package/src/credential-execution/executable-discovery.ts +40 -0
  488. package/src/credential-execution/process-manager.ts +6 -2
  489. package/src/credential-health/credential-health-service.ts +125 -40
  490. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  491. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +15 -17
  492. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  493. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  494. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  495. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  496. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  497. package/src/daemon/__tests__/web-search-status-text.test.ts +11 -6
  498. package/src/daemon/approval-generators.ts +4 -4
  499. package/src/daemon/config-watcher.ts +7 -1
  500. package/src/daemon/conversation-agent-loop-handlers.ts +613 -155
  501. package/src/daemon/conversation-agent-loop.ts +409 -605
  502. package/src/daemon/conversation-error.ts +40 -12
  503. package/src/daemon/conversation-history.ts +22 -6
  504. package/src/daemon/conversation-launch.ts +4 -8
  505. package/src/daemon/conversation-lifecycle.ts +10 -38
  506. package/src/daemon/conversation-messaging.ts +83 -44
  507. package/src/daemon/conversation-notifiers.ts +7 -5
  508. package/src/daemon/conversation-process.ts +174 -116
  509. package/src/daemon/conversation-runtime-assembly.ts +76 -30
  510. package/src/daemon/conversation-skill-tools.ts +14 -30
  511. package/src/daemon/conversation-store.ts +6 -5
  512. package/src/daemon/conversation-surfaces.ts +124 -103
  513. package/src/daemon/conversation-tool-setup.ts +36 -48
  514. package/src/daemon/conversation.ts +111 -166
  515. package/src/daemon/daemon-control.ts +1 -1
  516. package/src/daemon/daemon-skill-host.ts +7 -4
  517. package/src/daemon/disk-pressure-guard.ts +54 -50
  518. package/src/daemon/external-plugins-bootstrap.ts +46 -24
  519. package/src/daemon/first-greeting.ts +53 -13
  520. package/src/daemon/guardian-action-generators.ts +2 -2
  521. package/src/daemon/handlers/conversations.ts +6 -22
  522. package/src/daemon/handlers/shared.ts +10 -1
  523. package/src/daemon/handlers/skills.ts +15 -14
  524. package/src/daemon/host-app-control-proxy.ts +54 -1
  525. package/src/daemon/host-cu-proxy.ts +46 -22
  526. package/src/daemon/host-file-proxy.ts +25 -1
  527. package/src/daemon/host-proxy-preactivation.ts +25 -6
  528. package/src/daemon/lifecycle.ts +40 -67
  529. package/src/daemon/mcp-reload-service.ts +1 -1
  530. package/src/daemon/meet-manifest-loader.ts +10 -17
  531. package/src/daemon/message-protocol.ts +2 -3
  532. package/src/daemon/message-provenance.ts +49 -0
  533. package/src/daemon/message-types/contacts.ts +3 -20
  534. package/src/daemon/message-types/conversations.ts +25 -125
  535. package/src/daemon/message-types/document-comments.ts +8 -44
  536. package/src/daemon/message-types/documents.ts +3 -9
  537. package/src/daemon/message-types/home.ts +5 -18
  538. package/src/daemon/message-types/integrations.ts +4 -13
  539. package/src/daemon/message-types/messages.ts +47 -377
  540. package/src/daemon/message-types/notifications.ts +2 -32
  541. package/src/daemon/message-types/settings.ts +3 -8
  542. package/src/daemon/message-types/skills.ts +2 -0
  543. package/src/daemon/message-types/subagents.ts +6 -0
  544. package/src/daemon/message-types/surfaces.ts +2 -0
  545. package/src/daemon/message-types/sync.ts +12 -25
  546. package/src/daemon/message-types/workspace.ts +3 -11
  547. package/src/daemon/process-message.ts +58 -55
  548. package/src/daemon/providers-setup.ts +1 -1
  549. package/src/daemon/server.ts +28 -0
  550. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  551. package/src/daemon/tool-setup-types.ts +0 -6
  552. package/src/daemon/tool-side-effects.ts +10 -7
  553. package/src/daemon/trust-context.ts +13 -0
  554. package/src/daemon/wake-target-adapter.ts +21 -1
  555. package/src/documents/document-store.ts +38 -0
  556. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  557. package/src/heartbeat/__tests__/heartbeat-service.test.ts +31 -0
  558. package/src/heartbeat/heartbeat-run-store.ts +31 -0
  559. package/src/heartbeat/heartbeat-service.ts +79 -0
  560. package/src/home/__tests__/feed-writer.test.ts +161 -0
  561. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  562. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  563. package/src/home/feature-gate.ts +22 -0
  564. package/src/home/feed-types.ts +36 -221
  565. package/src/home/feed-writer.ts +146 -7
  566. package/src/home/suggested-prompts.ts +27 -145
  567. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  568. package/src/ipc/__tests__/email-ipc.test.ts +0 -9
  569. package/src/ipc/gateway-client.test.ts +4 -1
  570. package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
  571. package/src/ipc/routes/route-adapter.ts +45 -6
  572. package/src/ipc/skill-routes/__tests__/memory.test.ts +19 -9
  573. package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
  574. package/src/ipc/skill-routes/__tests__/registries.test.ts +59 -20
  575. package/src/ipc/skill-routes/memory.ts +27 -13
  576. package/src/ipc/skill-routes/providers.ts +5 -6
  577. package/src/ipc/skill-routes/registries.ts +39 -88
  578. package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
  579. package/src/memory/__tests__/conversation-queries.test.ts +192 -8
  580. package/src/memory/__tests__/db-maintenance.test.ts +128 -0
  581. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  582. package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
  583. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  584. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  585. package/src/memory/__tests__/memory-retrospective-job.test.ts +11 -6
  586. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  587. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  588. package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
  589. package/src/memory/context-search/agent-runner.ts +2 -4
  590. package/src/memory/conversation-attention-store.ts +17 -3
  591. package/src/memory/conversation-crud.ts +386 -115
  592. package/src/memory/conversation-queries.ts +78 -22
  593. package/src/memory/db-connection.ts +29 -19
  594. package/src/memory/db-init.ts +12 -0
  595. package/src/memory/db-maintenance.ts +18 -2
  596. package/src/memory/db-singleton.ts +77 -0
  597. package/src/memory/delivery-channels.ts +82 -0
  598. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  599. package/src/memory/graph/consolidation.ts +8 -11
  600. package/src/memory/graph/conversation-graph-memory.ts +41 -8
  601. package/src/memory/graph/extraction.ts +6 -9
  602. package/src/memory/graph/narrative.ts +2 -2
  603. package/src/memory/graph/pattern-scan.ts +2 -2
  604. package/src/memory/graph/retriever.test.ts +3 -3
  605. package/src/memory/graph/retriever.ts +20 -26
  606. package/src/memory/graph/tools.ts +4 -4
  607. package/src/memory/job-handlers/conversation-starters.ts +32 -32
  608. package/src/memory/job-handlers/embedding.test.ts +3 -2
  609. package/src/memory/job-handlers/summarization.ts +1 -2
  610. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  611. package/src/memory/jobs-store.ts +3 -1
  612. package/src/memory/jobs-worker.ts +63 -40
  613. package/src/memory/llm-request-log-source-clickhouse.ts +55 -1
  614. package/src/memory/llm-request-log-source-local.ts +13 -0
  615. package/src/memory/llm-request-log-source.ts +21 -6
  616. package/src/memory/llm-request-log-store.ts +147 -3
  617. package/src/memory/llm-usage-store.ts +10 -0
  618. package/src/memory/memory-marker.ts +17 -0
  619. package/src/memory/memory-retrospective-job.ts +6 -2
  620. package/src/memory/memory-v2-activation-log-store.ts +13 -1
  621. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  622. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  623. package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
  624. package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
  625. package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
  626. package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
  627. package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
  628. package/src/memory/migrations/index.ts +6 -0
  629. package/src/memory/schema/conversations.ts +9 -1
  630. package/src/memory/schema/inference.ts +0 -1
  631. package/src/memory/schema/infrastructure.ts +11 -0
  632. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  633. package/src/memory/v2/__tests__/consolidation-job.test.ts +124 -0
  634. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  635. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  636. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  637. package/src/memory/v2/__tests__/migration.test.ts +11 -3
  638. package/src/memory/v2/__tests__/page-index.test.ts +37 -1
  639. package/src/memory/v2/__tests__/router.test.ts +14 -4
  640. package/src/memory/v2/__tests__/sweep-job.test.ts +9 -5
  641. package/src/memory/v2/backfill-jobs.ts +6 -0
  642. package/src/memory/v2/consolidation-job.ts +89 -9
  643. package/src/memory/v2/harness/metrics.ts +5 -1
  644. package/src/memory/v2/harness/replay-input.ts +19 -3
  645. package/src/memory/v2/harness/runner.ts +6 -0
  646. package/src/memory/v2/harness/trace.ts +6 -0
  647. package/src/memory/v2/migration.ts +5 -3
  648. package/src/memory/v2/page-index.ts +11 -0
  649. package/src/memory/v2/router.ts +8 -11
  650. package/src/memory/v2/sweep-job.ts +8 -11
  651. package/src/memory/v2/types.ts +1 -0
  652. package/src/memory/v3/__tests__/assign.test.ts +242 -0
  653. package/src/memory/v3/__tests__/capabilities.test.ts +118 -0
  654. package/src/memory/v3/__tests__/core.test.ts +39 -0
  655. package/src/memory/v3/__tests__/fixtures/eval-turns.json +36 -0
  656. package/src/memory/v3/__tests__/fixtures/live-turns.json +37 -0
  657. package/src/memory/v3/__tests__/health.test.ts +203 -0
  658. package/src/memory/v3/__tests__/live-integration.test.ts +330 -0
  659. package/src/memory/v3/__tests__/maintain-job.test.ts +288 -0
  660. package/src/memory/v3/__tests__/needle.test.ts +107 -0
  661. package/src/memory/v3/__tests__/orchestrate.test.ts +400 -0
  662. package/src/memory/v3/__tests__/reconcile.test.ts +274 -0
  663. package/src/memory/v3/__tests__/render-injection.test.ts +61 -0
  664. package/src/memory/v3/__tests__/router.test.ts +260 -0
  665. package/src/memory/v3/__tests__/selection-log-store.test.ts +179 -0
  666. package/src/memory/v3/__tests__/selector.test.ts +404 -0
  667. package/src/memory/v3/__tests__/shadow-plugin.test.ts +414 -0
  668. package/src/memory/v3/__tests__/snapshot.test.ts +168 -0
  669. package/src/memory/v3/__tests__/tree.test.ts +192 -0
  670. package/src/memory/v3/__tests__/types.test.ts +54 -0
  671. package/src/memory/v3/__tests__/working-set-eviction.test.ts +106 -0
  672. package/src/memory/v3/__tests__/working-set-skeleton.test.ts +44 -0
  673. package/src/memory/v3/assign.ts +268 -0
  674. package/src/memory/v3/capabilities.ts +124 -0
  675. package/src/memory/v3/core.ts +26 -0
  676. package/src/memory/v3/data/README.md +84 -0
  677. package/src/memory/v3/data/assignments.json +5 -0
  678. package/src/memory/v3/data/core.json +1 -0
  679. package/src/memory/v3/data/leaves/domain-a/topic-x.md +9 -0
  680. package/src/memory/v3/data/leaves/domain-a/topic-y.md +9 -0
  681. package/src/memory/v3/data/leaves/domain-b/topic-z.md +9 -0
  682. package/src/memory/v3/health.ts +0 -0
  683. package/src/memory/v3/maintain-job.ts +314 -0
  684. package/src/memory/v3/needle.ts +115 -0
  685. package/src/memory/v3/orchestrate.ts +114 -0
  686. package/src/memory/v3/page-content.ts +34 -0
  687. package/src/memory/v3/provider-blocks.ts +16 -0
  688. package/src/memory/v3/reconcile.ts +523 -0
  689. package/src/memory/v3/render-injection.ts +32 -0
  690. package/src/memory/v3/router.ts +184 -0
  691. package/src/memory/v3/selection-log-store.ts +84 -0
  692. package/src/memory/v3/selector.ts +211 -0
  693. package/src/memory/v3/shadow-plugin.ts +379 -0
  694. package/src/memory/v3/snapshot.ts +209 -0
  695. package/src/memory/v3/tree.ts +174 -0
  696. package/src/memory/v3/types.ts +46 -60
  697. package/src/memory/v3/working-set.ts +88 -0
  698. package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
  699. package/src/messaging/providers/slack/render-transcript.ts +2 -2
  700. package/src/messaging/style-analyzer.ts +8 -11
  701. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  702. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  703. package/src/notifications/adapters/slack.ts +45 -11
  704. package/src/notifications/broadcaster.ts +114 -63
  705. package/src/notifications/conversation-pairing.ts +30 -8
  706. package/src/notifications/decision-engine.ts +10 -13
  707. package/src/notifications/decisions-store.ts +32 -1
  708. package/src/notifications/deliveries-store.ts +45 -0
  709. package/src/notifications/edit-notification.ts +201 -0
  710. package/src/notifications/emit-signal.ts +11 -1
  711. package/src/notifications/preference-extractor.ts +11 -14
  712. package/src/notifications/signal.ts +10 -0
  713. package/src/notifications/types.ts +37 -0
  714. package/src/oauth/byo-connection.test.ts +67 -3
  715. package/src/oauth/byo-connection.ts +32 -5
  716. package/src/oauth/connect-orchestrator.ts +9 -0
  717. package/src/oauth/connection-resolver.test.ts +76 -0
  718. package/src/oauth/connection-resolver.ts +49 -10
  719. package/src/oauth/manual-token-connection.ts +51 -3
  720. package/src/oauth/seed-providers.ts +3 -0
  721. package/src/permissions/approval-policy.test.ts +19 -5
  722. package/src/permissions/approval-policy.ts +14 -3
  723. package/src/permissions/checker.ts +21 -8
  724. package/src/permissions/prompter.ts +42 -36
  725. package/src/permissions/question-prompter.test.ts +35 -26
  726. package/src/permissions/question-prompter.ts +6 -10
  727. package/src/platform/client.test.ts +24 -1
  728. package/src/platform/client.ts +8 -0
  729. package/src/platform/feature-gate.ts +15 -0
  730. package/src/plugin-api/index.ts +2 -0
  731. package/src/plugin-api/types.ts +25 -3
  732. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
  733. package/src/plugins/defaults/circuit-breaker/package.json +15 -0
  734. package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
  735. package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
  736. package/src/plugins/defaults/compaction/package.json +15 -0
  737. package/src/plugins/defaults/compaction/register.ts +35 -0
  738. package/src/plugins/defaults/compaction/terminal.ts +73 -0
  739. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +22 -0
  740. package/src/plugins/defaults/empty-response/package.json +15 -0
  741. package/src/plugins/defaults/empty-response/register.ts +28 -0
  742. package/src/plugins/defaults/empty-response/terminal.ts +106 -0
  743. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
  744. package/src/plugins/defaults/history-repair/package.json +15 -0
  745. package/src/plugins/defaults/history-repair/register.ts +24 -0
  746. package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
  747. package/src/plugins/defaults/index.ts +29 -40
  748. package/src/plugins/defaults/injectors/package.json +15 -0
  749. package/src/plugins/defaults/{injectors.ts → injectors/register.ts} +16 -46
  750. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +17 -0
  751. package/src/plugins/defaults/llm-call/package.json +15 -0
  752. package/src/plugins/defaults/{llm-call.ts → llm-call/register.ts} +6 -38
  753. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +17 -0
  754. package/src/plugins/defaults/memory-retrieval/package.json +15 -0
  755. package/src/plugins/defaults/{memory-retrieval.ts → memory-retrieval/register.ts} +10 -48
  756. package/src/plugins/defaults/{overflow-reduce.ts → overflow-reduce/middlewares/overflowReduce.ts} +18 -77
  757. package/src/plugins/defaults/overflow-reduce/package.json +15 -0
  758. package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
  759. package/src/plugins/defaults/persistence/middlewares/persistence.ts +19 -0
  760. package/src/plugins/defaults/persistence/package.json +15 -0
  761. package/src/plugins/defaults/persistence/register.ts +38 -0
  762. package/src/plugins/defaults/persistence/terminal.ts +83 -0
  763. package/src/plugins/defaults/title-generate/package.json +15 -0
  764. package/src/plugins/defaults/title-generate/register.ts +35 -0
  765. package/src/plugins/defaults/title-generate/terminal.ts +31 -0
  766. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +23 -0
  767. package/src/plugins/defaults/token-estimate/package.json +15 -0
  768. package/src/plugins/defaults/token-estimate/register.ts +34 -0
  769. package/src/plugins/defaults/token-estimate/terminal.ts +40 -0
  770. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +21 -0
  771. package/src/plugins/defaults/tool-error/package.json +15 -0
  772. package/src/plugins/defaults/tool-error/register.ts +35 -0
  773. package/src/plugins/defaults/tool-error/terminal.ts +47 -0
  774. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +23 -0
  775. package/src/plugins/defaults/tool-execute/package.json +15 -0
  776. package/src/plugins/defaults/{tool-execute.ts → tool-execute/register.ts} +8 -46
  777. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +23 -0
  778. package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
  779. package/src/plugins/defaults/tool-result-truncate/register.ts +35 -0
  780. package/src/plugins/defaults/tool-result-truncate/terminal.ts +113 -0
  781. package/src/plugins/defaults/tool-result-truncate/types.ts +22 -0
  782. package/src/plugins/external-plugin-loader.ts +2 -2
  783. package/src/plugins/pipeline.ts +0 -12
  784. package/src/plugins/types.ts +107 -102
  785. package/src/plugins/user-loader.ts +4 -3
  786. package/src/proactive-artifact/aux-message-injector.ts +0 -1
  787. package/src/proactive-artifact/job.test.ts +21 -8
  788. package/src/proactive-artifact/job.ts +3 -1
  789. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  790. package/src/prompts/sections.ts +20 -7
  791. package/src/prompts/system-prompt.ts +38 -40
  792. package/src/prompts/template-detection.ts +10 -4
  793. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
  794. package/src/prompts/templates/BOOTSTRAP.md +10 -10
  795. package/src/prompts/templates/IDENTITY.md +0 -2
  796. package/src/prompts/templates/system-sections.ts +6 -0
  797. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  798. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  799. package/src/providers/__tests__/retry-callsite.test.ts +25 -25
  800. package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
  801. package/src/providers/anthropic/client.ts +24 -5
  802. package/src/providers/call-site-routing.ts +34 -18
  803. package/src/providers/connection-model-compat.ts +23 -0
  804. package/src/providers/connection-resolution.ts +39 -20
  805. package/src/providers/fireworks/client.ts +1 -0
  806. package/src/providers/gemini/client.ts +176 -37
  807. package/src/providers/gemini/inline-media.ts +74 -0
  808. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  809. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  810. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  811. package/src/providers/inference/auth.ts +0 -8
  812. package/src/providers/inference/connections.ts +3 -66
  813. package/src/providers/inference/resolve-auth.ts +2 -3
  814. package/src/providers/model-catalog.ts +35 -1
  815. package/src/providers/model-intents.ts +3 -3
  816. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  817. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -7
  818. package/src/providers/openai/chat-completions-provider.ts +111 -16
  819. package/src/providers/openai/codex-models.ts +2 -0
  820. package/src/providers/openai/responses-provider.ts +54 -57
  821. package/src/providers/openrouter/client.ts +14 -14
  822. package/src/providers/provider-send-message.ts +23 -14
  823. package/src/providers/ratelimit.ts +1 -9
  824. package/src/providers/registry.ts +48 -8
  825. package/src/providers/retry.ts +16 -9
  826. package/src/providers/search-provider-catalog.ts +17 -9
  827. package/src/providers/types.ts +20 -2
  828. package/src/providers/usage-tracking.ts +1 -9
  829. package/src/runtime/__tests__/agent-wake.test.ts +132 -26
  830. package/src/runtime/__tests__/background-job-runner.test.ts +2 -3
  831. package/src/runtime/access-request-helper.ts +1 -0
  832. package/src/runtime/agent-wake.ts +93 -18
  833. package/src/runtime/assistant-event-hub.ts +2 -2
  834. package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
  835. package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
  836. package/src/runtime/auth/route-policy.ts +42 -1069
  837. package/src/runtime/background-job-runner.ts +1 -4
  838. package/src/runtime/btw-sidechain.ts +3 -1
  839. package/src/runtime/channel-approvals.ts +3 -14
  840. package/src/runtime/channel-invite-transport.ts +5 -6
  841. package/src/runtime/channel-readiness-service.ts +70 -5
  842. package/src/runtime/channel-reply-delivery.ts +23 -0
  843. package/src/runtime/channel-retry-sweep.ts +59 -30
  844. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  845. package/src/runtime/conversation-stream-state.ts +294 -0
  846. package/src/runtime/http-router.ts +19 -22
  847. package/src/runtime/http-types.ts +12 -6
  848. package/src/runtime/invite-instruction-generator.ts +3 -3
  849. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  850. package/src/runtime/pending-interactions.ts +2 -2
  851. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
  852. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  853. package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
  854. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +436 -0
  855. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
  856. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  857. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  858. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  859. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +26 -72
  860. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +58 -5
  861. package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
  862. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  863. package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
  864. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
  865. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  866. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  867. package/src/runtime/routes/__tests__/tts-routes.test.ts +3 -3
  868. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  869. package/src/runtime/routes/acp-routes.test.ts +97 -75
  870. package/src/runtime/routes/acp-routes.ts +29 -6
  871. package/src/runtime/routes/app-management-routes.ts +208 -28
  872. package/src/runtime/routes/app-routes.ts +25 -5
  873. package/src/runtime/routes/approval-routes.ts +16 -4
  874. package/src/runtime/routes/attachment-routes.ts +25 -1
  875. package/src/runtime/routes/audio-routes.ts +1 -0
  876. package/src/runtime/routes/audit-routes.ts +5 -0
  877. package/src/runtime/routes/auth-routes.ts +5 -0
  878. package/src/runtime/routes/avatar-routes.ts +238 -59
  879. package/src/runtime/routes/background-tool-routes.ts +9 -0
  880. package/src/runtime/routes/background-wake-routes.ts +201 -23
  881. package/src/runtime/routes/backup-routes.ts +45 -0
  882. package/src/runtime/routes/bookmark-routes.ts +13 -0
  883. package/src/runtime/routes/brain-graph-routes.ts +9 -0
  884. package/src/runtime/routes/browser-routes.ts +5 -0
  885. package/src/runtime/routes/browser-tabs-routes.ts +5 -0
  886. package/src/runtime/routes/btw-routes.ts +9 -5
  887. package/src/runtime/routes/cache-routes.ts +13 -0
  888. package/src/runtime/routes/call-routes.ts +21 -10
  889. package/src/runtime/routes/channel-availability-routes.ts +5 -1
  890. package/src/runtime/routes/channel-readiness-routes.ts +37 -4
  891. package/src/runtime/routes/channel-route-definitions.ts +21 -0
  892. package/src/runtime/routes/channel-verification-routes.ts +21 -0
  893. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
  894. package/src/runtime/routes/client-routes.ts +9 -0
  895. package/src/runtime/routes/consolidation-routes.ts +13 -5
  896. package/src/runtime/routes/contact-prompt-routes.ts +9 -0
  897. package/src/runtime/routes/contact-routes.ts +90 -23
  898. package/src/runtime/routes/content-source-routes.ts +5 -1
  899. package/src/runtime/routes/conversation-analysis-routes.ts +11 -1
  900. package/src/runtime/routes/conversation-attention-routes.ts +5 -0
  901. package/src/runtime/routes/conversation-cli-routes.ts +54 -7
  902. package/src/runtime/routes/conversation-compaction-routes.ts +292 -0
  903. package/src/runtime/routes/conversation-list-routes.ts +225 -9
  904. package/src/runtime/routes/conversation-management-routes.ts +96 -28
  905. package/src/runtime/routes/conversation-query-routes.ts +148 -51
  906. package/src/runtime/routes/conversation-routes.ts +259 -158
  907. package/src/runtime/routes/conversation-starter-routes.ts +22 -13
  908. package/src/runtime/routes/conversations-import-routes.ts +25 -7
  909. package/src/runtime/routes/credential-prompt-routes.ts +5 -0
  910. package/src/runtime/routes/credential-routes.ts +25 -6
  911. package/src/runtime/routes/debug-bash-routes.ts +5 -0
  912. package/src/runtime/routes/debug-routes.ts +11 -2
  913. package/src/runtime/routes/defer-routes.ts +13 -0
  914. package/src/runtime/routes/diagnostics-routes.ts +37 -46
  915. package/src/runtime/routes/disk-pressure-routes.ts +17 -31
  916. package/src/runtime/routes/document-comments-routes.ts +46 -27
  917. package/src/runtime/routes/documents-routes.ts +31 -11
  918. package/src/runtime/routes/domain-routes.ts +61 -28
  919. package/src/runtime/routes/email-routes.ts +33 -0
  920. package/src/runtime/routes/events-routes.ts +114 -9
  921. package/src/runtime/routes/filing-routes.ts +9 -4
  922. package/src/runtime/routes/gateway-log-routes.ts +5 -0
  923. package/src/runtime/routes/global-search-routes.ts +53 -50
  924. package/src/runtime/routes/group-routes.ts +32 -5
  925. package/src/runtime/routes/guardian-action-routes.ts +9 -0
  926. package/src/runtime/routes/guardian-approval-interception.ts +0 -31
  927. package/src/runtime/routes/heartbeat-routes.ts +25 -9
  928. package/src/runtime/routes/home-feed-routes.ts +149 -16
  929. package/src/runtime/routes/home-state-routes.ts +8 -40
  930. package/src/runtime/routes/host-app-control-routes.ts +5 -0
  931. package/src/runtime/routes/host-bash-routes.ts +5 -0
  932. package/src/runtime/routes/host-browser-routes.ts +13 -0
  933. package/src/runtime/routes/host-cu-routes.ts +5 -0
  934. package/src/runtime/routes/host-file-routes.ts +26 -6
  935. package/src/runtime/routes/host-transfer-routes.ts +13 -2
  936. package/src/runtime/routes/http-adapter.ts +1 -2
  937. package/src/runtime/routes/identity-intro-cache.ts +72 -16
  938. package/src/runtime/routes/identity-routes.ts +42 -11
  939. package/src/runtime/routes/image-generation-routes.ts +5 -0
  940. package/src/runtime/routes/inbound-message-handler.ts +15 -11
  941. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +524 -12
  942. package/src/runtime/routes/inbound-stages/background-dispatch.ts +72 -27
  943. package/src/runtime/routes/index.ts +2 -0
  944. package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
  945. package/src/runtime/routes/inference-provider-connection-routes.ts +26 -31
  946. package/src/runtime/routes/inference-send-routes.ts +11 -11
  947. package/src/runtime/routes/integrations/a2a.ts +30 -7
  948. package/src/runtime/routes/integrations/slack/channel.ts +19 -3
  949. package/src/runtime/routes/integrations/slack/share.ts +9 -2
  950. package/src/runtime/routes/integrations/telegram.ts +28 -9
  951. package/src/runtime/routes/integrations/twilio.ts +35 -7
  952. package/src/runtime/routes/integrations/vercel.ts +18 -3
  953. package/src/runtime/routes/internal-oauth-routes.ts +5 -0
  954. package/src/runtime/routes/internal-twilio-routes.ts +13 -0
  955. package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
  956. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  957. package/src/runtime/routes/log-export-routes.ts +28 -10
  958. package/src/runtime/routes/mcp-auth-routes.ts +25 -0
  959. package/src/runtime/routes/memory-item-routes.ts +21 -10
  960. package/src/runtime/routes/memory-v2-routes.ts +90 -36
  961. package/src/runtime/routes/memory-v3-routes.ts +283 -259
  962. package/src/runtime/routes/migration-rollback-routes.ts +5 -1
  963. package/src/runtime/routes/migration-routes.ts +49 -13
  964. package/src/runtime/routes/notification-routes.ts +80 -2
  965. package/src/runtime/routes/oauth-apps.ts +33 -11
  966. package/src/runtime/routes/oauth-commands-routes.ts +43 -15
  967. package/src/runtime/routes/oauth-connect-routes.ts +9 -0
  968. package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
  969. package/src/runtime/routes/oauth-providers.ts +35 -10
  970. package/src/runtime/routes/platform-routes.ts +21 -0
  971. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +3 -2
  972. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
  973. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
  974. package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
  975. package/src/runtime/routes/playground/force-compact.ts +1 -1
  976. package/src/runtime/routes/playground/helpers.ts +0 -1
  977. package/src/runtime/routes/playground/inject-failures.ts +13 -8
  978. package/src/runtime/routes/playground/reset-circuit.ts +14 -9
  979. package/src/runtime/routes/playground/seed-conversation.ts +1 -1
  980. package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
  981. package/src/runtime/routes/playground/state.ts +4 -3
  982. package/src/runtime/routes/plugins-routes.ts +22 -19
  983. package/src/runtime/routes/profiler-routes.ts +17 -4
  984. package/src/runtime/routes/ps-routes.ts +5 -0
  985. package/src/runtime/routes/publish-routes.ts +13 -3
  986. package/src/runtime/routes/question-routes.ts +5 -0
  987. package/src/runtime/routes/recording-routes.ts +25 -12
  988. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  989. package/src/runtime/routes/sanity-routes.ts +9 -2
  990. package/src/runtime/routes/schedule-routes.ts +137 -47
  991. package/src/runtime/routes/secret-routes.ts +17 -4
  992. package/src/runtime/routes/sequence-routes.ts +33 -0
  993. package/src/runtime/routes/settings-routes.ts +65 -19
  994. package/src/runtime/routes/skills-routes.ts +133 -69
  995. package/src/runtime/routes/slack-channel-routes.ts +5 -0
  996. package/src/runtime/routes/stt-routes.ts +13 -6
  997. package/src/runtime/routes/subagents-routes.ts +24 -18
  998. package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
  999. package/src/runtime/routes/surface-action-routes.ts +10 -38
  1000. package/src/runtime/routes/surface-content-routes.ts +21 -6
  1001. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  1002. package/src/runtime/routes/task-routes.ts +37 -0
  1003. package/src/runtime/routes/telemetry-routes.ts +9 -0
  1004. package/src/runtime/routes/trace-event-routes.ts +42 -1
  1005. package/src/runtime/routes/trust-rules-routes.ts +5 -0
  1006. package/src/runtime/routes/tts-routes.ts +13 -6
  1007. package/src/runtime/routes/types.ts +17 -8
  1008. package/src/runtime/routes/ui-request-routes.ts +5 -0
  1009. package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
  1010. package/src/runtime/routes/usage-routes.ts +71 -3
  1011. package/src/runtime/routes/user-routes-cli.ts +9 -0
  1012. package/src/runtime/routes/user-routes.ts +5 -1
  1013. package/src/runtime/routes/wake-conversation-routes.ts +5 -0
  1014. package/src/runtime/routes/watcher-routes.ts +21 -0
  1015. package/src/runtime/routes/webhook-routes.ts +9 -0
  1016. package/src/runtime/routes/wipe-conversation-routes.ts +8 -0
  1017. package/src/runtime/routes/work-items-routes.ts +47 -19
  1018. package/src/runtime/routes/workspace-commit-routes.ts +5 -0
  1019. package/src/runtime/routes/workspace-routes.test.ts +42 -0
  1020. package/src/runtime/routes/workspace-routes.ts +120 -9
  1021. package/src/runtime/services/__tests__/analyze-conversation.test.ts +4 -4
  1022. package/src/runtime/services/analyze-conversation.ts +3 -6
  1023. package/src/runtime/services/conversation-serializer.ts +24 -2
  1024. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  1025. package/src/runtime/sync/resource-sync-events.ts +17 -3
  1026. package/src/runtime/sync/sync-publisher.ts +2 -2
  1027. package/src/runtime/tool-grant-request-helper.ts +1 -0
  1028. package/src/schedule/run-script.ts +28 -3
  1029. package/src/schedule/schedule-store.ts +16 -1
  1030. package/src/schedule/scheduler.ts +114 -16
  1031. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  1032. package/src/security/encrypted-store.ts +7 -16
  1033. package/src/security/store-path-override.ts +61 -0
  1034. package/src/signals/user-message.ts +10 -16
  1035. package/src/skills/catalog-files.ts +4 -1
  1036. package/src/skills/clawhub-files.ts +2 -0
  1037. package/src/skills/skillssh-files.ts +2 -0
  1038. package/src/skills/validate-input.ts +177 -0
  1039. package/src/subagent/manager.ts +16 -19
  1040. package/src/subagent/types.ts +6 -0
  1041. package/src/tasks/tool-sanitizer.ts +2 -2
  1042. package/src/telemetry/types.ts +26 -0
  1043. package/src/telemetry/usage-telemetry-reporter.test.ts +138 -1
  1044. package/src/telemetry/usage-telemetry-reporter.ts +31 -0
  1045. package/src/tools/acp/spawn.test.ts +88 -38
  1046. package/src/tools/apps/definitions.ts +42 -24
  1047. package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
  1048. package/src/tools/ask-question/ask-question-tool.ts +85 -90
  1049. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  1050. package/src/tools/computer-use/definitions.ts +295 -289
  1051. package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
  1052. package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
  1053. package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
  1054. package/src/tools/credentials/vault.ts +112 -111
  1055. package/src/tools/document/document-tool.ts +131 -8
  1056. package/src/tools/execution-target.ts +3 -6
  1057. package/src/tools/execution-timeout.ts +3 -4
  1058. package/src/tools/executor.ts +18 -55
  1059. package/src/tools/filesystem/edit.ts +45 -42
  1060. package/src/tools/filesystem/list.ts +33 -30
  1061. package/src/tools/filesystem/read.ts +54 -35
  1062. package/src/tools/filesystem/write.ts +34 -31
  1063. package/src/tools/host-filesystem/edit.test.ts +1 -0
  1064. package/src/tools/host-filesystem/edit.ts +44 -42
  1065. package/src/tools/host-filesystem/read.test.ts +1 -0
  1066. package/src/tools/host-filesystem/read.ts +49 -35
  1067. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  1068. package/src/tools/host-filesystem/transfer.ts +121 -108
  1069. package/src/tools/host-filesystem/write.test.ts +1 -0
  1070. package/src/tools/host-filesystem/write.ts +33 -31
  1071. package/src/tools/host-terminal/host-shell.ts +50 -48
  1072. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  1073. package/src/tools/memory/register.ts +23 -24
  1074. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  1075. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  1076. package/src/tools/network/managed-search-proxy.ts +183 -0
  1077. package/src/tools/network/web-fetch.ts +49 -46
  1078. package/src/tools/network/web-search.ts +215 -57
  1079. package/src/tools/policy-context.ts +3 -1
  1080. package/src/tools/registry.ts +184 -118
  1081. package/src/tools/schedule/create.ts +12 -1
  1082. package/src/tools/schedule/update.ts +16 -0
  1083. package/src/tools/shared/filesystem/audio-read.ts +122 -0
  1084. package/src/tools/shared/filesystem/image-read.ts +1 -1
  1085. package/src/tools/skills/execute.ts +34 -31
  1086. package/src/tools/skills/load.ts +29 -23
  1087. package/src/tools/skills/skill-tool-factory.ts +17 -36
  1088. package/src/tools/subagent/notify-parent.ts +35 -32
  1089. package/src/tools/subagent/spawn.ts +3 -0
  1090. package/src/tools/system/avatar-generator.ts +13 -22
  1091. package/src/tools/system/request-permission.ts +30 -27
  1092. package/src/tools/terminal/shell.ts +190 -61
  1093. package/src/tools/tool-approval-handler.ts +10 -4
  1094. package/src/tools/tool-defaults.ts +20 -9
  1095. package/src/tools/tool-manifest.ts +4 -4
  1096. package/src/tools/tool-name-aliases.ts +72 -14
  1097. package/src/tools/types.ts +86 -33
  1098. package/src/tools/ui-surface/definitions.ts +166 -94
  1099. package/src/types/onboarding-context.ts +6 -0
  1100. package/src/usage/attribution.ts +32 -1
  1101. package/src/usage/types.ts +10 -0
  1102. package/src/util/browser.ts +7 -2
  1103. package/src/util/errors.ts +2 -2
  1104. package/src/util/map-limit.ts +27 -0
  1105. package/src/util/platform.ts +15 -12
  1106. package/src/work-items/work-item-runner.ts +7 -2
  1107. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
  1108. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  1109. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  1110. package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
  1111. package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
  1112. package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
  1113. package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
  1114. package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
  1115. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
  1116. package/src/workspace/migrations/registry.ts +10 -0
  1117. package/src/workspace/provider-commit-message-generator.ts +15 -17
  1118. package/tsconfig.json +4 -1
  1119. package/src/__tests__/history-repair-pipeline.test.ts +0 -396
  1120. package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
  1121. package/src/cli/commands/memory-v3-render.ts +0 -344
  1122. package/src/daemon/message-types/disk-pressure.ts +0 -9
  1123. package/src/email/feature-gate.ts +0 -23
  1124. package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
  1125. package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -468
  1126. package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
  1127. package/src/memory/v3/__tests__/edges.test.ts +0 -563
  1128. package/src/memory/v3/__tests__/filter.test.ts +0 -512
  1129. package/src/memory/v3/__tests__/gate.test.ts +0 -574
  1130. package/src/memory/v3/__tests__/index-composition.test.ts +0 -233
  1131. package/src/memory/v3/__tests__/loop.test.ts +0 -530
  1132. package/src/memory/v3/__tests__/retriever.test.ts +0 -226
  1133. package/src/memory/v3/__tests__/scouts.test.ts +0 -440
  1134. package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -312
  1135. package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
  1136. package/src/memory/v3/__tests__/traversal.test.ts +0 -469
  1137. package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
  1138. package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
  1139. package/src/memory/v3/__tests__/tree-walk.test.ts +0 -707
  1140. package/src/memory/v3/__tests__/validate.test.ts +0 -245
  1141. package/src/memory/v3/auto-edges.ts +0 -223
  1142. package/src/memory/v3/coactivation-store.ts +0 -124
  1143. package/src/memory/v3/consolidation-job.ts +0 -323
  1144. package/src/memory/v3/edge-learning-job.ts +0 -160
  1145. package/src/memory/v3/edges.ts +0 -249
  1146. package/src/memory/v3/filter.ts +0 -281
  1147. package/src/memory/v3/gate.ts +0 -334
  1148. package/src/memory/v3/index-composition.ts +0 -113
  1149. package/src/memory/v3/llm-capture.ts +0 -46
  1150. package/src/memory/v3/loop.ts +0 -382
  1151. package/src/memory/v3/maintenance.ts +0 -144
  1152. package/src/memory/v3/prompt-context.ts +0 -33
  1153. package/src/memory/v3/prompts/consolidation.ts +0 -458
  1154. package/src/memory/v3/prompts/system-prompts.ts +0 -196
  1155. package/src/memory/v3/retriever.ts +0 -33
  1156. package/src/memory/v3/scouts.ts +0 -420
  1157. package/src/memory/v3/shadow-middleware.ts +0 -305
  1158. package/src/memory/v3/traversal.ts +0 -206
  1159. package/src/memory/v3/tree-index.ts +0 -237
  1160. package/src/memory/v3/tree-store.ts +0 -394
  1161. package/src/memory/v3/tree-walk.ts +0 -351
  1162. package/src/memory/v3/validate.ts +0 -300
  1163. package/src/plugins/defaults/circuit-breaker.ts +0 -141
  1164. package/src/plugins/defaults/compaction.ts +0 -141
  1165. package/src/plugins/defaults/empty-response.ts +0 -124
  1166. package/src/plugins/defaults/history-repair.ts +0 -83
  1167. package/src/plugins/defaults/persistence.ts +0 -127
  1168. package/src/plugins/defaults/title-generate.ts +0 -90
  1169. package/src/plugins/defaults/token-estimate.ts +0 -101
  1170. package/src/plugins/defaults/tool-error.ts +0 -119
  1171. package/src/plugins/defaults/tool-result-truncate.ts +0 -84
@@ -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,
@@ -59,6 +60,7 @@ import { commitAppTurnChanges } from "../memory/app-git-service.js";
59
60
  import { getApp, listAppFiles, resolveAppDir } from "../memory/app-store.js";
60
61
  import { enqueueAutoAnalysisOnCompaction } from "../memory/auto-analysis-enqueue.js";
61
62
  import {
63
+ deleteMessageById,
62
64
  getConversation,
63
65
  getConversationOriginChannel,
64
66
  getConversationOriginInterface,
@@ -79,7 +81,10 @@ import {
79
81
  } from "../memory/conversation-title-service.js";
80
82
  import { isBackgroundConversationType } from "../memory/conversation-types.js";
81
83
  import type { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
82
- import { backfillMessageIdOnLogs } from "../memory/llm-request-log-store.js";
84
+ import {
85
+ backfillMessageIdOnLogs,
86
+ recordSyntheticAgentErrorMessageLog,
87
+ } from "../memory/llm-request-log-store.js";
83
88
  import { recordMemoryRecallLog } from "../memory/memory-recall-log-store.js";
84
89
  import { enqueueMemoryRetrospectiveOnCompaction } from "../memory/memory-retrospective-enqueue.js";
85
90
  import { PKB_WORKSPACE_SCOPE } from "../memory/pkb/types.js";
@@ -91,28 +96,24 @@ import {
91
96
  import type { PermissionPrompter } from "../permissions/prompter.js";
92
97
  import { HOOKS } from "../plugin-api/constants.js";
93
98
  import type { UserPromptSubmitContext } from "../plugin-api/types.js";
94
- import { defaultCompactionTerminal } from "../plugins/defaults/compaction.js";
95
- 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";
96
101
  import {
97
102
  asDefaultGraphPayload,
98
103
  type DefaultMemoryRetrievalDeps,
99
104
  type GraphMemoryPayload,
100
105
  runDefaultMemoryRetrieval,
101
- } from "../plugins/defaults/memory-retrieval.js";
102
- import { defaultPersistenceTerminal } from "../plugins/defaults/persistence.js";
103
- import { defaultTitleGenerateTerminal } from "../plugins/defaults/title-generate.js";
104
- 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";
105
110
  import { DEFAULT_TIMEOUTS, runHook, runPipeline } from "../plugins/pipeline.js";
106
111
  import { getMiddlewaresFor } from "../plugins/registry.js";
107
112
  import type {
108
- CircuitBreakerArgs,
109
- CircuitBreakerResult,
110
113
  CompactionArgs,
111
114
  CompactionResult,
112
115
  EstimateArgs,
113
116
  EstimateResult,
114
- HistoryRepairArgs,
115
- HistoryRepairResult,
116
117
  MemoryArgs,
117
118
  MemoryResult,
118
119
  OverflowReduceArgs,
@@ -123,11 +124,6 @@ import type {
123
124
  TurnContext as PluginTurnContext,
124
125
  } from "../plugins/types.js";
125
126
  import { PluginExecutionError, PluginTimeoutError } from "../plugins/types.js";
126
- import {
127
- hasProactiveArtifactCompleted,
128
- runProactiveArtifactJob,
129
- tryClaimProactiveArtifactTrigger,
130
- } from "../proactive-artifact/index.js";
131
127
  import type {
132
128
  ContentBlock,
133
129
  Message,
@@ -206,7 +202,6 @@ import {
206
202
  } from "./date-context.js";
207
203
  import { getDiskPressureStatus } from "./disk-pressure-guard.js";
208
204
  import { classifyDiskPressureTurnPolicy } from "./disk-pressure-policy.js";
209
- import { deepRepairHistory } from "./history-repair.js";
210
205
  import type {
211
206
  DynamicPageSurfaceData,
212
207
  ServerMessage,
@@ -216,16 +211,37 @@ import type {
216
211
  } from "./message-protocol.js";
217
212
  import type { MemoryRecalled } from "./message-types/memory.js";
218
213
  import type { ConfirmationStateChanged } from "./message-types/messages.js";
219
- import {
220
- conversationMetadataSyncTag,
221
- SYNC_TAGS,
222
- } from "./message-types/sync.js";
214
+ import { conversationMetadataSyncTag } from "./message-types/sync.js";
223
215
  import { parseActualTokensFromError } from "./parse-actual-tokens-from-error.js";
224
216
  import type { TraceEmitter } from "./trace-emitter.js";
225
217
  import type { TrustContext } from "./trust-context.js";
226
218
  import { stripHistoricalWebSearchResults } from "./web-search-history.js";
227
219
 
228
220
  const log = getLogger("conversation-agent-loop");
221
+
222
+ /**
223
+ * Best-effort persistence of the history-stripped marker after an
224
+ * injection-strip event (compaction / overflow recovery). The marker is a
225
+ * durability hint, not turn-critical state — a transient SQLite write failure
226
+ * (SQLITE_BUSY, disk-full, read-only FS) must not abort the turn. Logs a
227
+ * warning and continues on failure, preserving the long-standing non-fatal
228
+ * contract for this metadata write.
229
+ */
230
+ function markHistoryStrippedBestEffort(
231
+ conversationId: string,
232
+ strippedAt: number,
233
+ logger: ReturnType<typeof getLogger>,
234
+ ): void {
235
+ try {
236
+ setConversationHistoryStrippedAt(conversationId, strippedAt);
237
+ } catch (err) {
238
+ logger.warn(
239
+ { err },
240
+ "Failed to persist history-stripped marker after compaction strip (non-fatal)",
241
+ );
242
+ }
243
+ }
244
+
229
245
  const DISK_PRESSURE_ERROR_CODE = "DISK_SPACE_CRITICAL" as const;
230
246
  const DISK_PRESSURE_ERROR_CATEGORY = "disk_pressure";
231
247
 
@@ -251,154 +267,6 @@ function formatDiskPressureBlockedMessage(): string {
251
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.";
252
268
  }
253
269
 
254
- // ── Compaction circuit-breaker pipeline helpers ─────────────────────
255
- //
256
- // The circuit-breaker behavior (3 consecutive summary-LLM failures trips a
257
- // 1-hour cooldown) is now implemented by the `circuitBreaker` plugin
258
- // pipeline. The default plugin (`plugins/defaults/circuit-breaker.ts`)
259
- // replicates the legacy threshold/cooldown constants and event-emission
260
- // semantics exactly — it operates on the `consecutiveCompactionFailures` /
261
- // `compactionCircuitOpenUntil` fields the conversation still owns so the
262
- // dev-only playground routes (`POST /playground/reset-compaction-circuit`,
263
- // `POST /playground/inject-compaction-failures`) continue to read and
264
- // mutate those fields directly.
265
- //
266
- // The helpers below build the pipeline inputs and invoke the runner. They
267
- // are the sole entry points the rest of the daemon uses to query or update
268
- // the compaction circuit.
269
-
270
- /** Circuit-breaker key for a specific conversation's compaction pipeline. */
271
- function compactionCircuitKey(conversationId: string): string {
272
- return `compaction:${conversationId}`;
273
- }
274
-
275
- /**
276
- * Build the minimal {@link TurnContext} the pipeline runner requires. Called
277
- * both from inside the agent loop (where turn identifiers are available) and
278
- * from non-turn invocations like `Conversation.forceCompact` (which falls
279
- * back to stable placeholders so the runner's log records still carry the
280
- * conversation identifier).
281
- */
282
- function buildCircuitTurnContext(ctx: {
283
- readonly conversationId: string;
284
- currentRequestId?: string;
285
- currentTurnTrustContext?: TrustContext;
286
- trustContext?: TrustContext;
287
- turnCount: number;
288
- }): PluginTurnContext {
289
- const trust: TrustContext =
290
- ctx.currentTurnTrustContext ?? ctx.trustContext ?? FALLBACK_TURN_TRUST;
291
- return {
292
- requestId: ctx.currentRequestId ?? "circuit-breaker",
293
- conversationId: ctx.conversationId,
294
- turnIndex: ctx.turnCount,
295
- trust,
296
- };
297
- }
298
-
299
- /**
300
- * Run the `circuitBreaker` pipeline for the compaction circuit on this
301
- * conversation. When `outcome` is provided, state is updated (and transition
302
- * events emit via `onEvent`); when omitted the call is query-only.
303
- *
304
- * Returns the post-call decision from the pipeline. Callers gate auto-paths
305
- * on `!result.open` and admit forced paths regardless of the decision.
306
- */
307
- async function runCompactionCircuitPipeline(
308
- ctx: {
309
- readonly conversationId: string;
310
- consecutiveCompactionFailures: number;
311
- compactionCircuitOpenUntil: number | null;
312
- currentRequestId?: string;
313
- currentTurnTrustContext?: TrustContext;
314
- trustContext?: TrustContext;
315
- turnCount: number;
316
- },
317
- args: {
318
- outcome?: "success" | "failure";
319
- onEvent?: (msg: ServerMessage) => void;
320
- },
321
- ): Promise<CircuitBreakerResult> {
322
- const turnContext = buildCircuitTurnContext(ctx);
323
- return runPipeline<CircuitBreakerArgs, CircuitBreakerResult>(
324
- "circuitBreaker",
325
- getMiddlewaresFor("circuitBreaker"),
326
- async (terminalArgs) => {
327
- // No plugin in the chain produced a decision. This should be
328
- // unreachable in production because the default plugin registers a
329
- // `circuitBreaker` middleware that always returns a decision, but we
330
- // defensively derive the state here so test setups that intentionally
331
- // omit the default plugin still get a sensible response.
332
- const openUntil = terminalArgs.state.compactionCircuitOpenUntil;
333
- const now = Date.now();
334
- if (openUntil !== null && now < openUntil) {
335
- return { open: true, cooldownRemainingMs: openUntil - now };
336
- }
337
- return { open: false };
338
- },
339
- {
340
- key: compactionCircuitKey(ctx.conversationId),
341
- // Pass the ctx directly as the mutable state container. The
342
- // `CircuitBreakerArgs.state` shape deliberately matches the subset of
343
- // fields the conversation owns so plugins mutate the same object the
344
- // playground routes read and write.
345
- state: ctx,
346
- ...(args.outcome !== undefined ? { outcome: args.outcome } : {}),
347
- ...(args.onEvent ? { onEvent: args.onEvent } : {}),
348
- },
349
- turnContext,
350
- DEFAULT_TIMEOUTS.circuitBreaker,
351
- );
352
- }
353
-
354
- /**
355
- * Query-only: is the compaction circuit breaker currently open for this
356
- * conversation? Thin wrapper around {@link runCompactionCircuitPipeline}
357
- * with no outcome. Async because the pipeline runner is async, but the
358
- * default plugin resolves synchronously on its microtask.
359
- */
360
- async function isCompactionCircuitOpen(ctx: {
361
- readonly conversationId: string;
362
- consecutiveCompactionFailures: number;
363
- compactionCircuitOpenUntil: number | null;
364
- currentRequestId?: string;
365
- currentTurnTrustContext?: TrustContext;
366
- trustContext?: TrustContext;
367
- turnCount: number;
368
- }): Promise<boolean> {
369
- const decision = await runCompactionCircuitPipeline(ctx, {});
370
- return decision.open;
371
- }
372
-
373
- /**
374
- * Update the compaction circuit breaker with the outcome of a `maybeCompact`
375
- * call and emit any transition event. A `summaryFailed` value of `undefined`
376
- * means the summary LLM never ran (early return) — callers must guard with
377
- * `summaryFailed !== undefined` before invoking this helper so early-return
378
- * paths don't silently reset the 3-strike counter.
379
- *
380
- * The default plugin handles threshold-based tripping and cooldown reset;
381
- * see `plugins/defaults/circuit-breaker.ts` for the canonical semantics.
382
- */
383
- export async function trackCompactionOutcome(
384
- ctx: {
385
- readonly conversationId: string;
386
- consecutiveCompactionFailures: number;
387
- compactionCircuitOpenUntil: number | null;
388
- currentRequestId?: string;
389
- currentTurnTrustContext?: TrustContext;
390
- trustContext?: TrustContext;
391
- turnCount: number;
392
- },
393
- summaryFailed: boolean,
394
- onEvent: (msg: ServerMessage) => void,
395
- ): Promise<void> {
396
- await runCompactionCircuitPipeline(ctx, {
397
- outcome: summaryFailed ? "failure" : "success",
398
- onEvent,
399
- });
400
- }
401
-
402
270
  // ── Plugin pipeline helpers ──────────────────────────────────────────
403
271
  //
404
272
  // Canonical {@link PluginTurnContext} builder threaded into every
@@ -459,6 +327,28 @@ function buildPluginTurnContext(
459
327
 
460
328
  // ── Context Interface ────────────────────────────────────────────────
461
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
+
462
352
  export interface AgentLoopConversationContext {
463
353
  readonly conversationId: string;
464
354
  messages: Message[];
@@ -482,10 +372,6 @@ export interface AgentLoopConversationContext {
482
372
  * happened just before this turn).
483
373
  */
484
374
  pendingPostCompactReinject: boolean;
485
- /** Tracks consecutive compaction failures (summary LLM call threw). */
486
- consecutiveCompactionFailures: number;
487
- /** Timestamp (ms since epoch) until which the circuit breaker is open. */
488
- compactionCircuitOpenUntil: number | null;
489
375
 
490
376
  readonly graphMemory: ConversationGraphMemory;
491
377
 
@@ -508,15 +394,7 @@ export interface AgentLoopConversationContext {
508
394
  pendingSurfaceActions: Map<string, { surfaceType: SurfaceType }>;
509
395
  surfaceActionRequestIds: Set<string>;
510
396
  approvedViaPromptThisTurn?: boolean;
511
- currentTurnSurfaces: Array<{
512
- surfaceId: string;
513
- surfaceType: SurfaceType;
514
- title?: string;
515
- data: SurfaceData;
516
- actions?: Array<{ id: string; label: string; style?: string }>;
517
- display?: string;
518
- persistent?: boolean;
519
- }>;
397
+ currentTurnSurfaces: AssistantSurface[];
520
398
 
521
399
  workingDir: string;
522
400
  workspaceTopLevelContext: string | null;
@@ -541,12 +419,6 @@ export interface AgentLoopConversationContext {
541
419
  * turn start.
542
420
  */
543
421
  toolRoutedProfile?: string;
544
- /**
545
- * True when the user has explicitly selected an inference profile for this
546
- * conversation (via the composer profile picker). When set, tool-based
547
- * auto-routing is suppressed — the user's explicit choice takes precedence.
548
- */
549
- hasExplicitProfileOverride?: boolean;
550
422
  commandIntent?: { type: string; payload?: string; languageCode?: string };
551
423
  trustContext?: TrustContext;
552
424
  /** Task-run scope for the current turn. Cleared at turn end so queued/drained turns don't inherit it. */
@@ -600,9 +472,11 @@ export interface AgentLoopConversationContext {
600
472
  | "message_complete"
601
473
  | "generation_cancelled"
602
474
  | "error_terminal",
603
- anchor?: "assistant_turn" | "user_turn" | "global",
604
- requestId?: string,
605
- statusText?: string,
475
+ options?: {
476
+ anchor?: "assistant_turn" | "user_turn" | "global";
477
+ requestId?: string;
478
+ statusText?: string;
479
+ },
606
480
  ): void;
607
481
  emitConfirmationStateChanged(
608
482
  params: ConfirmationStateChanged extends {
@@ -620,7 +494,6 @@ export interface AgentLoopConversationContext {
620
494
  onConfirmationOutcome?: (
621
495
  requestId: string,
622
496
  state: string,
623
- toolName?: string,
624
497
  toolUseId?: string,
625
498
  ) => void;
626
499
 
@@ -724,8 +597,6 @@ export async function runAgentLoopImpl(
724
597
  options?.overrideProfile ??
725
598
  getConversationOverrideProfileFromRow(turnStartConversation);
726
599
 
727
- ctx.hasExplicitProfileOverride = !!userExplicitOverride;
728
-
729
600
  const config = getConfig();
730
601
 
731
602
  // Tool-based auto-routing: the switch_inference_profile tool lets the model
@@ -746,12 +617,14 @@ export async function runAgentLoopImpl(
746
617
  llm: config.llm,
747
618
  callSite: turnCallSite,
748
619
  overrideProfile: turnOverrideProfile ?? undefined,
620
+ selectionSeed: ctx.conversationId,
749
621
  });
750
622
  let currentEffectiveContextWindow: EffectiveContextWindow =
751
623
  effectiveContextWindow;
752
624
  let currentContextWindowConfig = contextWindowConfigFromEffective(
753
625
  resolveCallSiteConfig(turnCallSite, config.llm, {
754
626
  overrideProfile: turnOverrideProfile ?? undefined,
627
+ selectionSeed: ctx.conversationId,
755
628
  }).contextWindow,
756
629
  currentEffectiveContextWindow,
757
630
  );
@@ -770,10 +643,12 @@ export async function runAgentLoopImpl(
770
643
  llm: config.llm,
771
644
  callSite: turnCallSite,
772
645
  overrideProfile: currentOverrideProfile,
646
+ selectionSeed: ctx.conversationId,
773
647
  });
774
648
  currentContextWindowConfig = contextWindowConfigFromEffective(
775
649
  resolveCallSiteConfig(turnCallSite, config.llm, {
776
650
  overrideProfile: currentOverrideProfile,
651
+ selectionSeed: ctx.conversationId,
777
652
  }).contextWindow,
778
653
  currentEffectiveContextWindow,
779
654
  );
@@ -833,6 +708,25 @@ export async function runAgentLoopImpl(
833
708
  preflightBudget: Math.floor(providerMaxTokens * (1 - safetyMargin)),
834
709
  };
835
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
+ };
836
730
 
837
731
  // Initial value for `createToolExecutor` to read into
838
732
  // `ToolContext.overrideProfile`. `resolveCurrentOverrideProfile` refreshes
@@ -942,7 +836,10 @@ export async function runAgentLoopImpl(
942
836
  { reason: diskPressureDecision.reason },
943
837
  "Blocked turn during disk pressure cleanup mode",
944
838
  );
945
- ctx.emitActivityState("idle", "error_terminal", "global", reqId);
839
+ ctx.emitActivityState("idle", "error_terminal", {
840
+ anchor: "global",
841
+ requestId: reqId,
842
+ });
946
843
  ctx.traceEmitter.emit("request_error", message, {
947
844
  requestId: reqId,
948
845
  status: "error",
@@ -1028,10 +925,7 @@ export async function runAgentLoopImpl(
1028
925
  });
1029
926
  onEvent({
1030
927
  type: "sync_changed",
1031
- tags: [
1032
- SYNC_TAGS.conversationsList,
1033
- conversationMetadataSyncTag(ctx.conversationId),
1034
- ],
928
+ tags: [conversationMetadataSyncTag(ctx.conversationId)],
1035
929
  });
1036
930
  },
1037
931
  };
@@ -1197,14 +1091,12 @@ export async function runAgentLoopImpl(
1197
1091
  );
1198
1092
  // Skip auto-compaction while the circuit breaker is open. Force paths
1199
1093
  // and user-initiated /compact bypass this check.
1200
- const autoCompactAllowed = !(await isCompactionCircuitOpen(ctx));
1094
+ const autoCompactAllowed =
1095
+ !(await ctx.agentLoop.compactionCircuit.isOpen(ctx));
1201
1096
  if (compactCheck.needed && autoCompactAllowed) {
1202
- ctx.emitActivityState(
1203
- "thinking",
1204
- "context_compacting",
1205
- "assistant_turn",
1206
- reqId,
1207
- );
1097
+ ctx.emitActivityState("thinking", "context_compacting", {
1098
+ requestId: reqId,
1099
+ });
1208
1100
  }
1209
1101
  const compactionOptions = {
1210
1102
  lastCompactedAt: ctx.contextCompactedAt ?? undefined,
@@ -1212,6 +1104,7 @@ export async function runAgentLoopImpl(
1212
1104
  conversationOriginChannel:
1213
1105
  getConversationOriginChannel(ctx.conversationId) ?? undefined,
1214
1106
  overrideProfile: resolveCurrentOverrideProfile() ?? null,
1107
+ actorTrustClass: ctx.trustContext?.trustClass,
1215
1108
  };
1216
1109
  let compacted: Awaited<
1217
1110
  ReturnType<typeof ctx.contextWindowManager.maybeCompact>
@@ -1244,7 +1137,11 @@ export async function runAgentLoopImpl(
1244
1137
  { err, phase: "start-of-turn-compaction" },
1245
1138
  "Compaction pipeline timed out — skipping compaction this turn",
1246
1139
  );
1247
- await trackCompactionOutcome(ctx, true, onEvent);
1140
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
1141
+ ctx,
1142
+ true,
1143
+ onEvent,
1144
+ );
1248
1145
  compacted = null;
1249
1146
  } else {
1250
1147
  throw err;
@@ -1257,7 +1154,11 @@ export async function runAgentLoopImpl(
1257
1154
  // path) — treating those as "successful" compactions would silently reset
1258
1155
  // the 3-strike counter and break the invariant.
1259
1156
  if (compacted && compacted.summaryFailed !== undefined) {
1260
- await trackCompactionOutcome(ctx, compacted.summaryFailed, onEvent);
1157
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
1158
+ ctx,
1159
+ compacted.summaryFailed,
1160
+ onEvent,
1161
+ );
1261
1162
  }
1262
1163
  if (compacted?.compacted) {
1263
1164
  await applySuccessfulCompaction(
@@ -1272,12 +1173,7 @@ export async function runAgentLoopImpl(
1272
1173
 
1273
1174
  // Register confirmation outcome tracker so the agent loop can link
1274
1175
  // confirmation decisions to tool_use_ids for persistence.
1275
- ctx.onConfirmationOutcome = (
1276
- requestId,
1277
- confirmationState,
1278
- toolName,
1279
- toolUseId,
1280
- ) => {
1176
+ ctx.onConfirmationOutcome = (requestId, confirmationState, toolUseId) => {
1281
1177
  if (confirmationState === "pending") {
1282
1178
  // Use the toolUseId passed from the prompter (which knows which tool
1283
1179
  // requested confirmation) instead of the ambient state.currentToolUseId,
@@ -1294,7 +1190,7 @@ export async function runAgentLoopImpl(
1294
1190
  const resolvedId =
1295
1191
  state.requestIdToToolUseId.get(requestId) ?? toolUseId;
1296
1192
  if (resolvedId) {
1297
- const name = state.toolUseIdToName.get(resolvedId) ?? toolName ?? "";
1193
+ const name = state.toolUseIdToName.get(resolvedId) ?? "";
1298
1194
  // Build a friendly label from the tool name
1299
1195
  const label =
1300
1196
  TOOL_FRIENDLY_LABEL[name] ??
@@ -1737,8 +1633,21 @@ export async function runAgentLoopImpl(
1737
1633
  // to the reduced `ctx.messages`.
1738
1634
  let reducerCompacted = compactedThisTurn;
1739
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
+
1740
1648
  // Shared injection options — reused whenever we need to re-inject after reduction.
1741
1649
  const injectionOpts = {
1650
+ suppressV2MemoryForV3: memoryV3Live,
1742
1651
  diskPressureContext,
1743
1652
  activeSurface,
1744
1653
  activeDocuments,
@@ -1960,6 +1869,7 @@ export async function runAgentLoopImpl(
1960
1869
  options: {
1961
1870
  ...(opts ?? {}),
1962
1871
  overrideProfile: resolveCurrentOverrideProfile() ?? null,
1872
+ actorTrustClass: ctx.trustContext?.trustClass,
1963
1873
  },
1964
1874
  },
1965
1875
  buildPluginTurnContext(ctx, reqId),
@@ -1973,7 +1883,11 @@ export async function runAgentLoopImpl(
1973
1883
  { err, phase: "overflow-reducer-forced-compaction" },
1974
1884
  "Compaction pipeline timed out — falling through to next reducer tier",
1975
1885
  );
1976
- await trackCompactionOutcome(ctx, true, onEvent);
1886
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
1887
+ ctx,
1888
+ true,
1889
+ onEvent,
1890
+ );
1977
1891
  return {
1978
1892
  messages: msgs,
1979
1893
  compacted: false,
@@ -1995,12 +1909,9 @@ export async function runAgentLoopImpl(
1995
1909
  }
1996
1910
  },
1997
1911
  emitActivityState: () => {
1998
- ctx.emitActivityState(
1999
- "thinking",
2000
- "context_compacting",
2001
- "assistant_turn",
2002
- reqId,
2003
- );
1912
+ ctx.emitActivityState("thinking", "context_compacting", {
1913
+ requestId: reqId,
1914
+ });
2004
1915
  },
2005
1916
  onCompactionResult: async (result, compactedBasis) => {
2006
1917
  // Track circuit-breaker state whenever the reducer invoked
@@ -2013,7 +1924,11 @@ export async function runAgentLoopImpl(
2013
1924
  // truncation-only path, etc.) that shouldn't influence the
2014
1925
  // breaker.
2015
1926
  if (result.summaryFailed !== undefined) {
2016
- await trackCompactionOutcome(ctx, result.summaryFailed, onEvent);
1927
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
1928
+ ctx,
1929
+ result.summaryFailed,
1930
+ onEvent,
1931
+ );
2017
1932
  }
2018
1933
  if (result.compacted) {
2019
1934
  await applySuccessfulCompaction(result, compactedBasis);
@@ -2107,55 +2022,7 @@ export async function runAgentLoopImpl(
2107
2022
  }
2108
2023
  }
2109
2024
 
2110
- // Pre-run repair — routed through the `historyRepair` plugin pipeline so
2111
- // plugins can observe or override repair behavior. The default plugin's
2112
- // middleware is a passthrough; the actual repair runs in the terminal
2113
- // (`defaultHistoryRepairTerminal`).
2114
2025
  let preRepairMessages = runMessages;
2115
- let preRunRepair: HistoryRepairResult | null = null;
2116
- try {
2117
- preRunRepair = await runPipeline<HistoryRepairArgs, HistoryRepairResult>(
2118
- "historyRepair",
2119
- getMiddlewaresFor("historyRepair"),
2120
- async (args) => defaultHistoryRepairTerminal(args),
2121
- { history: runMessages, provider: ctx.provider.name },
2122
- buildPluginTurnContext(ctx, reqId),
2123
- DEFAULT_TIMEOUTS.historyRepair,
2124
- );
2125
- } catch (err) {
2126
- if (err instanceof PluginTimeoutError) {
2127
- // Pipeline exceeded its budget — likely a misbehaving third-party
2128
- // middleware. Degrade gracefully by proceeding with the un-repaired
2129
- // history rather than turn-fatal-erroring; un-repaired history is
2130
- // strictly better than no turn at all, and the provider call itself
2131
- // will still error visibly if the drift is unrecoverable.
2132
- rlog.warn(
2133
- { err, phase: "pre_run" },
2134
- "historyRepair pipeline timed out — proceeding with un-repaired history",
2135
- );
2136
- } else {
2137
- throw err;
2138
- }
2139
- }
2140
- if (preRunRepair !== null) {
2141
- // Always adopt the pipeline's output history — a user `historyRepair`
2142
- // middleware may rewrite `messages` (e.g. provider-specific
2143
- // normalization) without incrementing any of the built-in repair
2144
- // counters. Gating the assignment on `stats` would silently discard
2145
- // those edits and send the un-rewritten history to the provider.
2146
- runMessages = preRunRepair.messages;
2147
- if (
2148
- preRunRepair.stats.assistantToolResultsMigrated > 0 ||
2149
- preRunRepair.stats.missingToolResultsInserted > 0 ||
2150
- preRunRepair.stats.orphanToolResultsDowngraded > 0 ||
2151
- preRunRepair.stats.consecutiveSameRoleMerged > 0
2152
- ) {
2153
- rlog.warn(
2154
- { phase: "pre_run", ...preRunRepair.stats },
2155
- "Repaired runtime history before provider call",
2156
- );
2157
- }
2158
- }
2159
2026
 
2160
2027
  // Replace historical web_search_tool_result blocks with text summaries.
2161
2028
  // The opaque `encrypted_content` tokens Anthropic attaches to each result
@@ -2172,12 +2039,12 @@ export async function runAgentLoopImpl(
2172
2039
  }
2173
2040
 
2174
2041
  // user-prompt-submit hook: plugins may transform `runMessages` right
2175
- // before the agent loop receives them. Fires once per user turn at
2176
- // the primary `agentLoop.run` only — the re-entry / retry calls
2177
- // further down in this function do not refire it (they're not new
2178
- // user submissions). Plugins may mutate `ctx.latestMessages` in place
2179
- // OR return a new context with a fresh array; `runHook` forwards
2180
- // 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.
2181
2048
  //
2182
2049
  // Fires BEFORE `preRunHistoryLength` is captured so the boundary
2183
2050
  // between pre-existing and hook-emitted messages — consumed by the
@@ -2188,6 +2055,7 @@ export async function runAgentLoopImpl(
2188
2055
  conversationId: ctx.conversationId,
2189
2056
  originalMessages: ctx.messages,
2190
2057
  latestMessages: runMessages,
2058
+ logger: rlog,
2191
2059
  };
2192
2060
  const finalUserPromptCtx = await runHook(
2193
2061
  HOOKS.USER_PROMPT_SUBMIT,
@@ -2211,41 +2079,16 @@ export async function runAgentLoopImpl(
2211
2079
  turnChannelContext: capturedTurnChannelContext,
2212
2080
  turnInterfaceContext: capturedTurnInterfaceContext,
2213
2081
  };
2214
- const eventHandler = (event: AgentEvent) =>
2082
+ const eventHandler = (event: AgentEvent): Promise<void> =>
2215
2083
  dispatchAgentEvent(state, deps, event);
2216
2084
  emitTerminalExit = async (reason: AgentLoopExitReason): Promise<void> => {
2217
2085
  await eventHandler({ type: "agent_loop_exit", reason });
2218
2086
  };
2219
2087
 
2220
- const onCheckpoint = async (
2221
- checkpoint: CheckpointInfo,
2222
- ): Promise<CheckpointDecision> => {
2223
- state.currentTurnToolNames = [];
2224
-
2088
+ const onCheckpoint = async (): Promise<CheckpointDecision> => {
2225
2089
  if (ctx.canHandoffAtCheckpoint()) {
2226
- yieldedForHandoff = true;
2227
- pendingCheckpointYield = "handoff";
2228
- return "yield";
2090
+ return "handoff";
2229
2091
  }
2230
-
2231
- // Mid-loop token budget check: estimate current context size and
2232
- // yield if we're approaching the preflight budget. This lets the
2233
- // conversation-agent-loop run compaction before the provider rejects.
2234
- if (overflowRecovery.enabled) {
2235
- const midLoopThreshold =
2236
- resolveCurrentContextBudget().preflightBudget * 0.85;
2237
- const estimated = await runTokenEstimatePipeline(checkpoint.history);
2238
- if (estimated > midLoopThreshold) {
2239
- rlog.warn(
2240
- { phase: "mid-loop", estimated, threshold: midLoopThreshold },
2241
- "Token estimate approaching budget — yielding for compaction",
2242
- );
2243
- yieldedForBudget = true;
2244
- pendingCheckpointYield = "budget";
2245
- return "yield";
2246
- }
2247
- }
2248
-
2249
2092
  return "continue";
2250
2093
  };
2251
2094
 
@@ -2261,19 +2104,120 @@ export async function runAgentLoopImpl(
2261
2104
  // and overwrites `turnIndex` with its own tool-use iteration counter.
2262
2105
  const loopTurnCtx = buildPluginTurnContext(ctx, reqId);
2263
2106
 
2264
- let updatedHistory = await ctx.agentLoop.run(
2265
- runMessages,
2266
- eventHandler,
2267
- abortController.signal,
2268
- reqId,
2269
- onCheckpoint,
2270
- turnCallSite,
2271
- loopTurnCtx,
2272
- turnOverrideProfile,
2273
- resolveCurrentMaxInputTokens(),
2274
- resolveCurrentOverrideProfile,
2275
- resolveCurrentMaxInputTokens,
2276
- );
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);
2277
2221
 
2278
2222
  rlog.info(
2279
2223
  { resultMessageCount: updatedHistory.length },
@@ -2285,167 +2229,16 @@ export async function runAgentLoopImpl(
2285
2229
  pendingCheckpointYield = null;
2286
2230
  }
2287
2231
 
2288
- // ── Proactive mid-loop compaction ───────────────────────────────
2289
- // When the agent loop yielded because the token budget check in
2290
- // onCheckpoint detected approaching limits, run compaction on the
2291
- // accumulated history and re-enter the agent loop. This is distinct
2292
- // from the reactive convergence loop below that fires after a
2293
- // provider rejection — here we compact *before* hitting the limit.
2294
- let midLoopCompactAttempts = 0;
2295
- while (
2296
- yieldedForBudget &&
2297
- midLoopCompactAttempts <
2298
- resolveCurrentContextBudget().overflowRecovery.maxAttempts &&
2299
- !state.contextTooLargeDetected &&
2300
- !abortController.signal.aborted
2301
- ) {
2302
- midLoopCompactAttempts++;
2303
- yieldedForBudget = false;
2304
- pendingCheckpointYield = null;
2305
-
2306
- rlog.info(
2307
- { phase: "mid-loop-compact" },
2308
- "Running compaction after checkpoint yield",
2309
- );
2310
-
2311
- // Strip injected context from updated history before compacting,
2312
- // so we compact the "raw" persistent messages.
2313
- const rawHistory = stripInjectionsForCompaction(updatedHistory);
2314
- ctx.messages = rawHistory;
2315
- setConversationHistoryStrippedAt(ctx.conversationId, Date.now());
2316
-
2317
- ctx.emitActivityState(
2318
- "thinking",
2319
- "context_compacting",
2320
- "assistant_turn",
2321
- reqId,
2322
- "Compacting context",
2323
- );
2324
- let midLoopCompact: Awaited<
2325
- ReturnType<typeof ctx.contextWindowManager.maybeCompact>
2326
- >;
2327
- try {
2328
- midLoopCompact = (await runPipeline<CompactionArgs, CompactionResult>(
2329
- "compaction",
2330
- getMiddlewaresFor("compaction"),
2331
- (args) =>
2332
- defaultCompactionTerminal(args, buildPluginTurnContext(ctx, reqId)),
2333
- {
2334
- messages: ctx.messages,
2335
- signal: abortController.signal,
2336
- options: {
2337
- lastCompactedAt: ctx.contextCompactedAt ?? undefined,
2338
- force: true,
2339
- targetInputTokensOverride:
2340
- resolveCurrentContextBudget().preflightBudget,
2341
- conversationOriginChannel:
2342
- getConversationOriginChannel(ctx.conversationId) ?? undefined,
2343
- overrideProfile: resolveCurrentOverrideProfile() ?? null,
2344
- },
2345
- },
2346
- buildPluginTurnContext(ctx, reqId),
2347
- DEFAULT_TIMEOUTS.compaction,
2348
- )) as Awaited<ReturnType<typeof ctx.contextWindowManager.maybeCompact>>;
2349
- } catch (err) {
2350
- if (err instanceof PluginTimeoutError) {
2351
- // Mid-loop compaction timed out. Record the failure for the
2352
- // circuit breaker and escalate to the convergence loop's more
2353
- // aggressive reducer tiers (tool-result truncation, media
2354
- // stubbing, injection downgrade) by flipping the overflow flag
2355
- // and breaking out of the mid-loop retry. The existing
2356
- // "exhausted all attempts" block further down handles the
2357
- // escalation.
2358
- rlog.warn(
2359
- { err, phase: "mid-loop-compact" },
2360
- "Compaction pipeline timed out — escalating to convergence loop",
2361
- );
2362
- await trackCompactionOutcome(ctx, true, onEvent);
2363
- state.contextTooLargeDetected = true;
2364
- break;
2365
- }
2366
- throw err;
2367
- }
2368
- // `force: true` bypasses the cooldown/threshold gates but early returns
2369
- // for "no eligible messages" / "insufficient messages" still leave
2370
- // `summaryFailed` undefined. Only track when the summary LLM actually ran.
2371
- if (midLoopCompact.summaryFailed !== undefined) {
2372
- await trackCompactionOutcome(
2373
- ctx,
2374
- midLoopCompact.summaryFailed,
2375
- onEvent,
2376
- );
2377
- }
2378
- if (midLoopCompact.compacted) {
2379
- await applySuccessfulCompaction(midLoopCompact, rawHistory);
2380
- reducerCompacted = true;
2381
- shouldInjectWorkspace = true;
2382
- }
2383
-
2384
- // Re-inject runtime context and re-enter the agent loop.
2385
- // stripInjectionsForCompaction() unconditionally removed the existing
2386
- // NOW.md block from ctx.messages above, so we must always re-inject
2387
- // the current content regardless of whether compaction actually ran.
2388
- const injection = await applyRuntimeInjections(ctx.messages, {
2389
- ...injectionOpts,
2390
- pkbContext: currentPkbContent,
2391
- memoryV2Static: currentMemoryV2Static,
2392
- nowScratchpad: currentNowContent,
2393
- workspaceTopLevelContext: shouldInjectWorkspace
2394
- ? ctx.workspaceTopLevelContext
2395
- : null,
2396
- // Suppress the chronological-transcript snapshot once the reducer
2397
- // has collapsed `ctx.messages`; the captured snapshot reflects the
2398
- // full persisted transcript and would overwrite compaction.
2399
- slackChronologicalMessages: reducerCompacted
2400
- ? null
2401
- : injectionOpts.slackChronologicalMessages,
2402
- mode: currentInjectionMode,
2403
- turnContext: buildPluginTurnContext(ctx, reqId),
2404
- });
2405
- runMessages = injection.messages;
2406
- if (isTrustedActor && currentInjectionMode !== "minimal") {
2407
- ctx.graphMemory.retrackCachedNodes();
2408
- }
2409
- const midLoopCompactStrip = stripHistoricalWebSearchResults(runMessages);
2410
- if (midLoopCompactStrip.stats.blocksStripped > 0) {
2411
- rlog.info(
2412
- { phase: "mid-loop-compact", ...midLoopCompactStrip.stats },
2413
- "Converted historical web_search_tool_result blocks to text summaries",
2414
- );
2415
- runMessages = midLoopCompactStrip.messages;
2416
- }
2417
- preRepairMessages = runMessages;
2418
- preRunHistoryLength = runMessages.length;
2419
-
2420
- updatedHistory = await ctx.agentLoop.run(
2421
- runMessages,
2422
- eventHandler,
2423
- abortController.signal,
2424
- reqId,
2425
- onCheckpoint,
2426
- turnCallSite,
2427
- loopTurnCtx,
2428
- turnOverrideProfile,
2429
- resolveCurrentMaxInputTokens(),
2430
- resolveCurrentOverrideProfile,
2431
- resolveCurrentMaxInputTokens,
2432
- );
2433
- }
2434
-
2435
- // If mid-loop compaction exhausted all attempts but the agent loop
2436
- // still yielded (yieldedForBudget is true), the turn is incomplete.
2437
- // Escalate to the convergence loop's more aggressive reducer tiers
2438
- // (tool-result truncation, media stubbing, injection downgrade)
2439
- // 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.
2440
2238
  if (yieldedForBudget && !abortController.signal.aborted) {
2441
2239
  rlog.warn(
2442
- {
2443
- phase: "mid-loop-compact",
2444
- midLoopCompactAttempts,
2445
- maxAttempts:
2446
- resolveCurrentContextBudget().overflowRecovery.maxAttempts,
2447
- },
2448
- "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",
2449
2242
  );
2450
2243
  state.contextTooLargeDetected = true;
2451
2244
  }
@@ -2459,15 +2252,15 @@ export async function runAgentLoopImpl(
2459
2252
  { phase: "retry" },
2460
2253
  "Provider ordering error detected, attempting one-shot deep-repair retry",
2461
2254
  );
2462
- // Design note: deep-repair intentionally bypasses the `historyRepair`
2463
- // plugin pipeline. Deep-repair is a recovery-only path triggered by a
2464
- // provider ordering error it must be deterministic and unaffected by
2465
- // user middleware that might have caused (or be unable to recover from)
2466
- // the original drift. Plugins can already observe / override the
2467
- // pre-run repair via the `historyRepair` pipeline above; widening that
2468
- // surface to deep-repair is intentionally deferred until there's a
2469
- // concrete plugin-level use case. Do not route this call through
2470
- // `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.
2471
2264
  const retryRepair = deepRepairHistory(runMessages);
2472
2265
  runMessages = retryRepair.messages;
2473
2266
  const retryStrip = stripHistoricalWebSearchResults(runMessages);
@@ -2477,19 +2270,7 @@ export async function runAgentLoopImpl(
2477
2270
  state.orderingErrorDetected = false;
2478
2271
  state.deferredOrderingError = null;
2479
2272
 
2480
- updatedHistory = await ctx.agentLoop.run(
2481
- runMessages,
2482
- eventHandler,
2483
- abortController.signal,
2484
- reqId,
2485
- onCheckpoint,
2486
- turnCallSite,
2487
- loopTurnCtx,
2488
- turnOverrideProfile,
2489
- resolveCurrentMaxInputTokens(),
2490
- resolveCurrentOverrideProfile,
2491
- resolveCurrentMaxInputTokens,
2492
- );
2273
+ updatedHistory = await runAgentLoop(runMessages);
2493
2274
 
2494
2275
  if (state.orderingErrorDetected) {
2495
2276
  rlog.error(
@@ -2548,19 +2329,7 @@ export async function runAgentLoopImpl(
2548
2329
  };
2549
2330
  });
2550
2331
  runMessages = ctx.messages;
2551
- updatedHistory = await ctx.agentLoop.run(
2552
- runMessages,
2553
- eventHandler,
2554
- abortController.signal,
2555
- reqId,
2556
- onCheckpoint,
2557
- turnCallSite,
2558
- loopTurnCtx,
2559
- turnOverrideProfile,
2560
- resolveCurrentMaxInputTokens(),
2561
- resolveCurrentOverrideProfile,
2562
- resolveCurrentMaxInputTokens,
2563
- );
2332
+ updatedHistory = await runAgentLoop(runMessages);
2564
2333
  if (state.imageTooLargeDetected) {
2565
2334
  rlog.error(
2566
2335
  { phase: "image-recovery" },
@@ -2596,7 +2365,7 @@ export async function runAgentLoopImpl(
2596
2365
 
2597
2366
  if (updatedHistory.length > preRunHistoryLength) {
2598
2367
  ctx.messages = stripInjectionsForCompaction(updatedHistory);
2599
- setConversationHistoryStrippedAt(ctx.conversationId, Date.now());
2368
+ markHistoryStrippedBestEffort(ctx.conversationId, Date.now(), rlog);
2600
2369
  convergenceStripped = true;
2601
2370
  preRepairMessages = updatedHistory;
2602
2371
  preRunHistoryLength = updatedHistory.length;
@@ -2680,7 +2449,7 @@ export async function runAgentLoopImpl(
2680
2449
  "Emergency mid-turn compaction succeeded — bypassing reducer tiers",
2681
2450
  );
2682
2451
  if (emergencyResult.summaryFailed !== undefined) {
2683
- await trackCompactionOutcome(
2452
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
2684
2453
  ctx,
2685
2454
  emergencyResult.summaryFailed,
2686
2455
  onEvent,
@@ -2721,12 +2490,9 @@ export async function runAgentLoopImpl(
2721
2490
  "Context too large — applying next reducer tier",
2722
2491
  );
2723
2492
 
2724
- ctx.emitActivityState(
2725
- "thinking",
2726
- "context_compacting",
2727
- "assistant_turn",
2728
- reqId,
2729
- );
2493
+ ctx.emitActivityState("thinking", "context_compacting", {
2494
+ requestId: reqId,
2495
+ });
2730
2496
  const convergenceCompactionBasis = ctx.messages;
2731
2497
  const step = await reduceContextOverflow(
2732
2498
  convergenceCompactionBasis,
@@ -2742,6 +2508,7 @@ export async function runAgentLoopImpl(
2742
2508
  ctx.contextWindowManager.maybeCompact(msgs, signal!, {
2743
2509
  ...(opts ?? {}),
2744
2510
  overrideProfile: resolveCurrentOverrideProfile() ?? null,
2511
+ actorTrustClass: ctx.trustContext?.trustClass,
2745
2512
  }),
2746
2513
  abortController.signal,
2747
2514
  );
@@ -2758,7 +2525,7 @@ export async function runAgentLoopImpl(
2758
2525
  step.compactionResult &&
2759
2526
  step.compactionResult.summaryFailed !== undefined
2760
2527
  ) {
2761
- await trackCompactionOutcome(
2528
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
2762
2529
  ctx,
2763
2530
  step.compactionResult.summaryFailed,
2764
2531
  onEvent,
@@ -2808,19 +2575,7 @@ export async function runAgentLoopImpl(
2808
2575
  state.contextTooLargeDetected = false;
2809
2576
  yieldedForBudget = false;
2810
2577
 
2811
- updatedHistory = await ctx.agentLoop.run(
2812
- runMessages,
2813
- eventHandler,
2814
- abortController.signal,
2815
- reqId,
2816
- onCheckpoint,
2817
- turnCallSite,
2818
- loopTurnCtx,
2819
- turnOverrideProfile,
2820
- resolveCurrentMaxInputTokens(),
2821
- resolveCurrentOverrideProfile,
2822
- resolveCurrentMaxInputTokens,
2823
- );
2578
+ updatedHistory = await runAgentLoop(runMessages);
2824
2579
 
2825
2580
  // If the rerun still yields at checkpoint, the turn is still
2826
2581
  // incomplete — continue reducing through the remaining tiers
@@ -2841,7 +2596,7 @@ export async function runAgentLoopImpl(
2841
2596
  // pre-rerun messages.
2842
2597
  if (updatedHistory.length > preRunHistoryLength) {
2843
2598
  ctx.messages = stripInjectionsForCompaction(updatedHistory);
2844
- setConversationHistoryStrippedAt(ctx.conversationId, Date.now());
2599
+ markHistoryStrippedBestEffort(ctx.conversationId, Date.now(), rlog);
2845
2600
  convergenceStripped = true;
2846
2601
  preRepairMessages = updatedHistory;
2847
2602
  preRunHistoryLength = updatedHistory.length;
@@ -2861,12 +2616,9 @@ export async function runAgentLoopImpl(
2861
2616
 
2862
2617
  if (action === "auto_compress_latest_turn") {
2863
2618
  // Auto-compress without asking — users opt out via the "drop" policy.
2864
- ctx.emitActivityState(
2865
- "thinking",
2866
- "context_compacting",
2867
- "assistant_turn",
2868
- reqId,
2869
- );
2619
+ ctx.emitActivityState("thinking", "context_compacting", {
2620
+ requestId: reqId,
2621
+ });
2870
2622
  let emergencyCompact: Awaited<
2871
2623
  ReturnType<typeof ctx.contextWindowManager.maybeCompact>
2872
2624
  > | null = null;
@@ -2908,7 +2660,11 @@ export async function runAgentLoopImpl(
2908
2660
  { err, phase: "emergency-compaction" },
2909
2661
  "Emergency compaction pipeline timed out — continuing with overflow fallback",
2910
2662
  );
2911
- await trackCompactionOutcome(ctx, true, onEvent);
2663
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
2664
+ ctx,
2665
+ true,
2666
+ onEvent,
2667
+ );
2912
2668
  emergencyCompact = null;
2913
2669
  } else {
2914
2670
  throw err;
@@ -2920,7 +2676,7 @@ export async function runAgentLoopImpl(
2920
2676
  emergencyCompact &&
2921
2677
  emergencyCompact.summaryFailed !== undefined
2922
2678
  ) {
2923
- await trackCompactionOutcome(
2679
+ await ctx.agentLoop.compactionCircuit.recordOutcome(
2924
2680
  ctx,
2925
2681
  emergencyCompact.summaryFailed,
2926
2682
  onEvent,
@@ -2964,19 +2720,7 @@ export async function runAgentLoopImpl(
2964
2720
  preRunHistoryLength = runMessages.length;
2965
2721
  state.contextTooLargeDetected = false;
2966
2722
 
2967
- updatedHistory = await ctx.agentLoop.run(
2968
- runMessages,
2969
- eventHandler,
2970
- abortController.signal,
2971
- reqId,
2972
- onCheckpoint,
2973
- turnCallSite,
2974
- loopTurnCtx,
2975
- turnOverrideProfile,
2976
- resolveCurrentMaxInputTokens(),
2977
- resolveCurrentOverrideProfile,
2978
- resolveCurrentMaxInputTokens,
2979
- );
2723
+ updatedHistory = await runAgentLoop(runMessages);
2980
2724
  }
2981
2725
  // action === "fail_gracefully" falls through to the final error below
2982
2726
  }
@@ -3110,8 +2854,12 @@ export async function runAgentLoopImpl(
3110
2854
  assistantMessageInterface:
3111
2855
  capturedTurnInterfaceContext.assistantMessageInterface,
3112
2856
  };
2857
+ let yieldNoticePersistedId: string | null = null;
3113
2858
  try {
3114
- await runPipeline<PersistArgs, PersistResult>(
2859
+ const yieldPersistResult = (await runPipeline<
2860
+ PersistArgs,
2861
+ PersistResult
2862
+ >(
3115
2863
  "persistence",
3116
2864
  getMiddlewaresFor("persistence"),
3117
2865
  defaultPersistenceTerminal,
@@ -3124,7 +2872,8 @@ export async function runAgentLoopImpl(
3124
2872
  },
3125
2873
  buildPluginTurnContext(ctx, reqId),
3126
2874
  DEFAULT_TIMEOUTS.persistence,
3127
- );
2875
+ )) as PersistAddResult;
2876
+ yieldNoticePersistedId = yieldPersistResult.message.id;
3128
2877
  } catch (err) {
3129
2878
  // Non-fatal — a DB hiccup must not escalate a budget-yield exit into
3130
2879
  // a turn-level throw. The live SSE event was already emitted, so the
@@ -3134,6 +2883,48 @@ export async function runAgentLoopImpl(
3134
2883
  "Failed to persist budget_yield_unrecovered notice (non-fatal)",
3135
2884
  );
3136
2885
  }
2886
+ // Record a synthetic `llm_request_logs` row for the yield so the
2887
+ // inspector's call rail surfaces a clickable, distinctly-rendered
2888
+ // entry for the failure itself. Without this row, the loop yields
2889
+ // silently — the user sees the notice in chat but the inspector
2890
+ // call list ends at the last actual LLM call with no way to scope
2891
+ // the "what compactions led to this failure?" question to the
2892
+ // yield event.
2893
+ //
2894
+ // Recorded *before* emitTerminalExit so the synthetic row exists
2895
+ // by the time the dispatcher's post-loop hook runs. The row
2896
+ // already carries `agent_loop_exit_reason` at insert time, so
2897
+ // `setAgentLoopExitReasonOnLatestLog`'s IS NULL guard skips it
2898
+ // and stamps the prior real mainAgent call instead — preserving
2899
+ // the existing "latest LLM call carries the exit reason"
2900
+ // invariant other consumers depend on.
2901
+ //
2902
+ // `preparedRequest` snapshots the best-known LLM request state
2903
+ // at yield time — `updatedHistory` (the conversation state the
2904
+ // next call would have been built from) plus the input-token
2905
+ // budget that just failed. Mirrors the role of `request_payload`
2906
+ // on real LLM-call rows; the notice text lives on
2907
+ // `response_payload`.
2908
+ if (yieldNoticePersistedId !== null && budgetYieldClassification) {
2909
+ try {
2910
+ recordSyntheticAgentErrorMessageLog({
2911
+ conversationId: ctx.conversationId,
2912
+ messageId: yieldNoticePersistedId,
2913
+ exitReason: "budget_yield_unrecovered",
2914
+ noticeText: budgetYieldClassification.userMessage,
2915
+ preparedRequest: {
2916
+ messages: updatedHistory,
2917
+ maxInputTokensBudget: resolveCurrentMaxInputTokens() ?? null,
2918
+ },
2919
+ createdAt: Date.now(),
2920
+ });
2921
+ } catch (err) {
2922
+ rlog.warn(
2923
+ { err },
2924
+ "Failed to record budget_yield_unrecovered synthetic call log (non-fatal)",
2925
+ );
2926
+ }
2927
+ }
3137
2928
  await emitTerminalExit?.("budget_yield_unrecovered");
3138
2929
  }
3139
2930
 
@@ -3154,6 +2945,34 @@ export async function runAgentLoopImpl(
3154
2945
  !abortController.signal.aborted &&
3155
2946
  !yieldedForHandoff
3156
2947
  ) {
2948
+ // Drop any reservation stranded by the failed LLM call before
2949
+ // inserting the synthetic error message. The B3 pre-allocation
2950
+ // path reserves an empty assistant row at `llm_call_started`;
2951
+ // when the call exits through the provider-error branch (no
2952
+ // `message_complete`), `assistantRowAwaitingFinalization` stays
2953
+ // true. Without this delete the transcript would carry both the
2954
+ // empty reserved row AND the error message — and downstream sync
2955
+ // (`syncLastAssistantMessageToDisk`) would mis-target the empty
2956
+ // row. After delete we set `lastAssistantMessageId` to the new
2957
+ // error row's id so the post-loop emission paths still point at
2958
+ // a real message.
2959
+ if (
2960
+ state.assistantRowAwaitingFinalization &&
2961
+ state.lastAssistantMessageId
2962
+ ) {
2963
+ // Direct `deleteMessageById` (not via the `persistence` pipeline):
2964
+ // see the same rationale on the matching cleanup in
2965
+ // `handleLlmCallStarted` — an unfinalized reservation has no
2966
+ // observable history for plugins.
2967
+ try {
2968
+ deleteMessageById(state.lastAssistantMessageId);
2969
+ } catch (err) {
2970
+ rlog.warn(
2971
+ { err, messageId: state.lastAssistantMessageId },
2972
+ "Failed to clean up stranded reserved assistant row on provider-error path (non-fatal)",
2973
+ );
2974
+ }
2975
+ }
3157
2976
  const errChannelMeta = {
3158
2977
  ...provenanceFromTrustContext(ctx.trustContext),
3159
2978
  userMessageChannel: capturedTurnChannelContext.userMessageChannel,
@@ -3181,6 +3000,15 @@ export async function runAgentLoopImpl(
3181
3000
  DEFAULT_TIMEOUTS.persistence,
3182
3001
  )) as PersistAddResult;
3183
3002
  persistedErrorAssistantMessage = true;
3003
+ // Repoint `lastAssistantMessageId` at the synthetic error row so the
3004
+ // post-loop sync, attachment resolution, and `message_complete`/
3005
+ // `generation_handoff` emissions all reference a real, persisted
3006
+ // message id. The previous reservation (if any) was already deleted
3007
+ // above. Mark finalization complete so the next LLM call in this run
3008
+ // (or a downstream handler) doesn't try to clean up an id that
3009
+ // already corresponds to a finalized row.
3010
+ state.lastAssistantMessageId = errorPersistResult.message.id;
3011
+ state.assistantRowAwaitingFinalization = false;
3184
3012
  newMessages.push(errorAssistantMessage);
3185
3013
  // Pipe the just-assigned message id into any orphaned LLM request log
3186
3014
  // row(s) for this turn. The success path links rows via
@@ -3282,7 +3110,10 @@ export async function runAgentLoopImpl(
3282
3110
  // so the client can re-enable the UI without delay.
3283
3111
  if (abortController.signal.aborted) {
3284
3112
  syncLastAssistantMessageToDisk();
3285
- ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3113
+ ctx.emitActivityState("idle", "generation_cancelled", {
3114
+ anchor: "global",
3115
+ requestId: reqId,
3116
+ });
3286
3117
  ctx.traceEmitter.emit(
3287
3118
  "generation_cancelled",
3288
3119
  "Generation cancelled by user",
@@ -3326,7 +3157,10 @@ export async function runAgentLoopImpl(
3326
3157
  await emitTerminalExit?.("aborted_after_checkpoint");
3327
3158
  pendingCheckpointYield = null;
3328
3159
  }
3329
- ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3160
+ ctx.emitActivityState("idle", "generation_cancelled", {
3161
+ anchor: "global",
3162
+ requestId: reqId,
3163
+ });
3330
3164
  ctx.traceEmitter.emit(
3331
3165
  "generation_cancelled",
3332
3166
  "Generation cancelled by user",
@@ -3367,7 +3201,10 @@ export async function runAgentLoopImpl(
3367
3201
  });
3368
3202
  publishLoopMessagesChanged();
3369
3203
  } else {
3370
- ctx.emitActivityState("idle", "message_complete", "global", reqId);
3204
+ ctx.emitActivityState("idle", "message_complete", {
3205
+ anchor: "global",
3206
+ requestId: reqId,
3207
+ });
3371
3208
  ctx.traceEmitter.emit(
3372
3209
  "message_complete",
3373
3210
  "Message processing complete",
@@ -3390,42 +3227,6 @@ export async function runAgentLoopImpl(
3390
3227
  : {}),
3391
3228
  });
3392
3229
  publishLoopMessagesChanged();
3393
-
3394
- // Proactive artifact: fire once when the processed turn was the 4th user message.
3395
- // Only trigger for real user-authored turns (not subagent/system messages).
3396
- {
3397
- const paConv = getConversation(ctx.conversationId);
3398
- if (
3399
- paConv &&
3400
- paConv.conversationType === "standard" &&
3401
- options?.isUserMessage
3402
- ) {
3403
- void (async () => {
3404
- try {
3405
- if (hasProactiveArtifactCompleted()) return;
3406
- const userMsg = getMessageById(
3407
- userMessageId,
3408
- ctx.conversationId,
3409
- );
3410
- if (!userMsg) return;
3411
- if (!tryClaimProactiveArtifactTrigger(userMsg.createdAt))
3412
- return;
3413
- await runProactiveArtifactJob({
3414
- conversationId: ctx.conversationId,
3415
- userMessageCutoff: userMsg.createdAt,
3416
- assistantMessageId: state.lastAssistantMessageId,
3417
- suppressAppBuild: state.appBuildToolUsedThisRun,
3418
- broadcastMessage,
3419
- });
3420
- } catch (err) {
3421
- log.warn(
3422
- { err, conversationId: ctx.conversationId },
3423
- "Proactive artifact trigger failed",
3424
- );
3425
- }
3426
- })();
3427
- }
3428
- }
3429
3230
  }
3430
3231
  }
3431
3232
 
@@ -3446,10 +3247,7 @@ export async function runAgentLoopImpl(
3446
3247
  });
3447
3248
  onEvent({
3448
3249
  type: "sync_changed",
3449
- tags: [
3450
- SYNC_TAGS.conversationsList,
3451
- conversationMetadataSyncTag(ctx.conversationId),
3452
- ],
3250
+ tags: [conversationMetadataSyncTag(ctx.conversationId)],
3453
3251
  });
3454
3252
  },
3455
3253
  signal: abortController.signal,
@@ -3465,7 +3263,10 @@ export async function runAgentLoopImpl(
3465
3263
  await emitTerminalExit?.("aborted_after_checkpoint");
3466
3264
  pendingCheckpointYield = null;
3467
3265
  }
3468
- ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3266
+ ctx.emitActivityState("idle", "generation_cancelled", {
3267
+ anchor: "global",
3268
+ requestId: reqId,
3269
+ });
3469
3270
  rlog.info("Generation cancelled by user");
3470
3271
  ctx.traceEmitter.emit(
3471
3272
  "generation_cancelled",
@@ -3481,7 +3282,10 @@ export async function runAgentLoopImpl(
3481
3282
  });
3482
3283
  publishLoopMessagesChanged();
3483
3284
  } else {
3484
- ctx.emitActivityState("idle", "error_terminal", "global", reqId);
3285
+ ctx.emitActivityState("idle", "error_terminal", {
3286
+ anchor: "global",
3287
+ requestId: reqId,
3288
+ });
3485
3289
  const message = err instanceof Error ? err.message : String(err);
3486
3290
  const errorClass = err instanceof Error ? err.constructor.name : "Error";
3487
3291
  rlog.error({ err }, "Conversation processing error");
@@ -3694,7 +3498,7 @@ export async function applyCompactionResult(
3694
3498
  result.summaryText,
3695
3499
  ctx.contextCompactedMessageCount,
3696
3500
  );
3697
- setConversationHistoryStrippedAt(ctx.conversationId, compactedAt);
3501
+ markHistoryStrippedBestEffort(ctx.conversationId, compactedAt, log);
3698
3502
  if (options.slackContextCompactionWatermarkTs) {
3699
3503
  updateConversationSlackContextWatermark(
3700
3504
  ctx.conversationId,