@vellumai/assistant 0.8.3 → 0.8.5

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 (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -15,6 +15,7 @@ import { optimizeImageForTransport } from "../agent/image-optimize.js";
15
15
  import type {
16
16
  AgentEvent,
17
17
  AgentLoop,
18
+ AgentLoopExitReason,
18
19
  CheckpointDecision,
19
20
  CheckpointInfo,
20
21
  } from "../agent/loop.js";
@@ -27,9 +28,13 @@ import type {
27
28
  } from "../channels/types.js";
28
29
  import {
29
30
  contextWindowConfigFromEffective,
31
+ type EffectiveContextWindow,
30
32
  resolveEffectiveContextWindow,
31
33
  } from "../config/llm-context-resolution.js";
32
- import { resolveCallSiteConfig } from "../config/llm-resolver.js";
34
+ import {
35
+ resolveCallSiteConfig,
36
+ resolveDefaultProfileKey,
37
+ } from "../config/llm-resolver.js";
33
38
  import { getConfig } from "../config/loader.js";
34
39
  import type { LLMCallSite } from "../config/schemas/llm.js";
35
40
  import type { ContextWindowConfig } from "../config/types.js";
@@ -54,7 +59,6 @@ import { commitAppTurnChanges } from "../memory/app-git-service.js";
54
59
  import { getApp, listAppFiles, resolveAppDir } from "../memory/app-store.js";
55
60
  import { enqueueAutoAnalysisOnCompaction } from "../memory/auto-analysis-enqueue.js";
56
61
  import {
57
- clearStrippedInjectionMetadataForConversation,
58
62
  getConversation,
59
63
  getConversationOriginChannel,
60
64
  getConversationOriginInterface,
@@ -62,6 +66,8 @@ import {
62
66
  getLastUserTimestampBefore,
63
67
  getMessageById,
64
68
  provenanceFromTrustContext,
69
+ setConversationHistoryStrippedAt,
70
+ setLastNotifiedInferenceProfile,
65
71
  updateConversationContextWindow,
66
72
  updateConversationSlackContextWatermark,
67
73
  } from "../memory/conversation-crud.js";
@@ -156,13 +162,13 @@ import {
156
162
  createEventHandlerState,
157
163
  dispatchAgentEvent,
158
164
  type EventHandlerDeps,
159
- getClientDisplayMessageId,
160
165
  } from "./conversation-agent-loop-handlers.js";
161
166
  import {
162
167
  approveHostAttachmentRead,
163
168
  resolveAssistantAttachments,
164
169
  } from "./conversation-attachments.js";
165
170
  import {
171
+ budgetYieldUnrecoveredClassification,
166
172
  buildConversationErrorMessage,
167
173
  classifyConversationError,
168
174
  isUserCancellation,
@@ -521,14 +527,26 @@ export interface AgentLoopConversationContext {
521
527
  /** Per-turn snapshot of channelCapabilities, frozen at message-processing start. */
522
528
  currentTurnChannelCapabilities?: ChannelCapabilities;
523
529
  /**
524
- * Per-turn snapshot of the resolved inference-profile override. Read by
530
+ * Current inference-profile override for this turn. Read by
525
531
  * `createToolExecutor` so `ToolContext.overrideProfile` carries the same
526
- * profile the agent loop is sending to the provider. Without this, a tool
527
- * that spawns nested subagents (e.g. `subagent_spawn`) cannot recover the
528
- * override from a row read because the in-flight subagent's own row never
529
- * had `inferenceProfile` set.
532
+ * profile the agent loop is sending to the provider. Refreshed between
533
+ * model calls so an explicitly confirmed profile session opened mid-turn
534
+ * is inherited by later tool executions and nested subagents.
530
535
  */
531
536
  currentTurnOverrideProfile?: string;
537
+ /**
538
+ * Set by the `switch_inference_profile` tool when the model self-selects a
539
+ * different profile mid-turn. Read by `readCurrentOverrideProfile` in the
540
+ * agent loop so the next LLM call uses the switched profile. Reset at
541
+ * turn start.
542
+ */
543
+ 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;
532
550
  commandIntent?: { type: string; payload?: string; languageCode?: string };
533
551
  trustContext?: TrustContext;
534
552
  /** Task-run scope for the current turn. Cleared at turn end so queued/drained turns don't inherit it. */
@@ -536,7 +554,6 @@ export interface AgentLoopConversationContext {
536
554
  assistantId?: string;
537
555
  voiceCallControlPrompt?: string;
538
556
  transportHints?: string[];
539
- slackRuntimeContextNotice?: string;
540
557
  clientTimezone?: string;
541
558
 
542
559
  readonly coreToolNames: Set<string>;
@@ -670,6 +687,18 @@ export async function runAgentLoopImpl(
670
687
  requestId: reqId,
671
688
  });
672
689
  let yieldedForHandoff = false;
690
+ let yieldedForBudget = false;
691
+ let pendingCheckpointYield: "budget" | "handoff" | null = null;
692
+ // Captured when the auto_compress_latest_turn rerun yields at the mid-loop
693
+ // budget checkpoint. SSE emission happens immediately at the detection site;
694
+ // assistant-row persistence is deferred until after the pendingToolResults
695
+ // flush so we don't orphan tool_use/tool_result pairs in the durable history.
696
+ let budgetYieldClassification: ReturnType<
697
+ typeof budgetYieldUnrecoveredClassification
698
+ > | null = null;
699
+ let emitTerminalExit:
700
+ | ((reason: AgentLoopExitReason) => Promise<void>)
701
+ | null = null;
673
702
 
674
703
  // Default user-initiated turns to the `mainAgent` call site. Other
675
704
  // invocation contexts (heartbeat, filing, analyze, etc.) pass their own
@@ -691,31 +720,124 @@ export async function runAgentLoopImpl(
691
720
  // spawned subagent's background conversation) wins over the row read
692
721
  // so the agent loop's own background-skip rule doesn't zero out an
693
722
  // explicitly inherited override.
694
- const turnOverrideProfile =
723
+ const userExplicitOverride =
695
724
  options?.overrideProfile ??
696
725
  getConversationOverrideProfileFromRow(turnStartConversation);
697
726
 
727
+ ctx.hasExplicitProfileOverride = !!userExplicitOverride;
728
+
698
729
  const config = getConfig();
730
+
731
+ // Tool-based auto-routing: the switch_inference_profile tool lets the model
732
+ // self-select a different profile mid-turn. Reset the per-turn slot so a
733
+ // stale selection from a previous turn doesn't leak forward.
734
+ ctx.toolRoutedProfile = undefined;
735
+
736
+ const turnOverrideProfile = userExplicitOverride;
737
+
738
+ const readCurrentOverrideProfile = (): string | undefined =>
739
+ options?.overrideProfile ??
740
+ getConversationOverrideProfileFromRow(
741
+ getConversation(ctx.conversationId),
742
+ ) ??
743
+ ctx.toolRoutedProfile;
744
+
699
745
  const effectiveContextWindow = resolveEffectiveContextWindow({
700
746
  llm: config.llm,
701
747
  callSite: turnCallSite,
702
748
  overrideProfile: turnOverrideProfile ?? undefined,
703
749
  });
704
- const turnContextWindowConfig = contextWindowConfigFromEffective(
750
+ let currentEffectiveContextWindow: EffectiveContextWindow =
751
+ effectiveContextWindow;
752
+ let currentContextWindowConfig = contextWindowConfigFromEffective(
705
753
  resolveCallSiteConfig(turnCallSite, config.llm, {
706
754
  overrideProfile: turnOverrideProfile ?? undefined,
707
755
  }).contextWindow,
708
- effectiveContextWindow,
756
+ currentEffectiveContextWindow,
709
757
  );
710
- (
758
+ const contextWindowManager =
711
759
  ctx.contextWindowManager as ContextWindowManager & {
712
760
  updateConfig?: (config: ContextWindowConfig) => void;
761
+ };
762
+ contextWindowManager.updateConfig?.(currentContextWindowConfig);
763
+
764
+ let appliedOverrideProfile = turnOverrideProfile;
765
+ let emittedToolRoutedProfile: string | undefined;
766
+ const refreshCurrentProfileState = (): string | undefined => {
767
+ const currentOverrideProfile = readCurrentOverrideProfile();
768
+ if (currentOverrideProfile !== appliedOverrideProfile) {
769
+ currentEffectiveContextWindow = resolveEffectiveContextWindow({
770
+ llm: config.llm,
771
+ callSite: turnCallSite,
772
+ overrideProfile: currentOverrideProfile,
773
+ });
774
+ currentContextWindowConfig = contextWindowConfigFromEffective(
775
+ resolveCallSiteConfig(turnCallSite, config.llm, {
776
+ overrideProfile: currentOverrideProfile,
777
+ }).contextWindow,
778
+ currentEffectiveContextWindow,
779
+ );
780
+ contextWindowManager.updateConfig?.(currentContextWindowConfig);
781
+ appliedOverrideProfile = currentOverrideProfile;
782
+ rlog.info(
783
+ { overrideProfile: currentOverrideProfile ?? null },
784
+ "Turn inference profile changed mid-loop",
785
+ );
786
+ }
787
+
788
+ // Emit turn_profile_auto_routed when the tool-based router selects a
789
+ // new profile. Deduplicated so the event fires at most once per profile.
790
+ if (
791
+ ctx.toolRoutedProfile &&
792
+ ctx.toolRoutedProfile !== emittedToolRoutedProfile
793
+ ) {
794
+ emittedToolRoutedProfile = ctx.toolRoutedProfile;
795
+ const profileEntry = config.llm.profiles?.[ctx.toolRoutedProfile];
796
+ const label = profileEntry?.label ?? ctx.toolRoutedProfile;
797
+ broadcastMessage({
798
+ type: "turn_profile_auto_routed",
799
+ conversationId: ctx.conversationId,
800
+ profile: ctx.toolRoutedProfile,
801
+ profileLabel: label,
802
+ });
713
803
  }
714
- ).updateConfig?.(turnContextWindowConfig);
715
804
 
716
- // Snapshot for `createToolExecutor` to read into `ToolContext.overrideProfile`
717
- // — see field doc on `AgentLoopConversationContext` for why the tool needs
718
- // it (nested subagent spawns can't recover the override from a row read).
805
+ ctx.currentTurnOverrideProfile = currentOverrideProfile;
806
+ return currentOverrideProfile;
807
+ };
808
+ const resolveCurrentOverrideProfile = (): string | undefined =>
809
+ refreshCurrentProfileState();
810
+ const resolveCurrentMaxInputTokens = (): number => {
811
+ refreshCurrentProfileState();
812
+ return currentEffectiveContextWindow.maxInputTokens;
813
+ };
814
+ const resolveCurrentContextWindowConfig = (): ContextWindowConfig => {
815
+ refreshCurrentProfileState();
816
+ return currentContextWindowConfig;
817
+ };
818
+ const resolveCurrentContextBudget = (): {
819
+ overflowRecovery: EffectiveContextWindow["overflowRecovery"];
820
+ providerMaxTokens: number;
821
+ preflightBudget: number;
822
+ } => {
823
+ refreshCurrentProfileState();
824
+ const overflowRecovery = currentEffectiveContextWindow.overflowRecovery;
825
+ const providerMaxTokens = currentEffectiveContextWindow.maxInputTokens;
826
+ const baseSafetyMargin = overflowRecovery.safetyMarginRatio;
827
+ const messageCount = ctx.messages.length;
828
+ const safetyMargin =
829
+ messageCount > 50 ? Math.max(baseSafetyMargin, 0.15) : baseSafetyMargin;
830
+ return {
831
+ overflowRecovery,
832
+ providerMaxTokens,
833
+ preflightBudget: Math.floor(providerMaxTokens * (1 - safetyMargin)),
834
+ };
835
+ };
836
+
837
+ // Initial value for `createToolExecutor` to read into
838
+ // `ToolContext.overrideProfile`. `resolveCurrentOverrideProfile` refreshes
839
+ // this between model calls so a confirmed profile session opened by a tool
840
+ // applies to later tool executions and nested subagents in the same turn.
719
841
  ctx.currentTurnOverrideProfile = turnOverrideProfile;
720
842
 
721
843
  // Capture the turn channel context *before* any awaits so a second
@@ -1029,6 +1151,7 @@ export async function runAgentLoopImpl(
1029
1151
  {
1030
1152
  message: result.messages[0]!,
1031
1153
  sourceChannelTs: null,
1154
+ tagLineProvenance: "none",
1032
1155
  },
1033
1156
  ...retainedRenderedMessages,
1034
1157
  ],
@@ -1088,7 +1211,7 @@ export async function runAgentLoopImpl(
1088
1211
  precomputedEstimate: compactCheck.estimatedTokens,
1089
1212
  conversationOriginChannel:
1090
1213
  getConversationOriginChannel(ctx.conversationId) ?? undefined,
1091
- overrideProfile: turnOverrideProfile ?? null,
1214
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
1092
1215
  };
1093
1216
  let compacted: Awaited<
1094
1217
  ReturnType<typeof ctx.contextWindowManager.maybeCompact>
@@ -1453,6 +1576,26 @@ export async function runAgentLoopImpl(
1453
1576
  }
1454
1577
  }
1455
1578
 
1579
+ // Resolve the effective profile key for this turn and detect changes.
1580
+ // Only inject model_profile into the turn context when the profile
1581
+ // changed since the last turn (or on the first turn of a conversation)
1582
+ // to avoid per-turn token cost.
1583
+ const effectiveProfileKey =
1584
+ turnOverrideProfile ??
1585
+ config.llm.activeProfile ??
1586
+ resolveDefaultProfileKey("mainAgent", config.llm);
1587
+ const lastNotified = turnStartConversation?.lastNotifiedInferenceProfile;
1588
+ let modelProfileStr: string | null = null;
1589
+ if (effectiveProfileKey != null && effectiveProfileKey !== lastNotified) {
1590
+ const profileEntry = config.llm.profiles?.[effectiveProfileKey];
1591
+ const resolved = resolveCallSiteConfig(turnCallSite, config.llm, {
1592
+ overrideProfile: turnOverrideProfile ?? undefined,
1593
+ });
1594
+ const label = profileEntry?.label ?? effectiveProfileKey;
1595
+ modelProfileStr = resolved.model ? `${label} (${resolved.model})` : label;
1596
+ setLastNotifiedInferenceProfile(ctx.conversationId, effectiveProfileKey);
1597
+ }
1598
+
1456
1599
  const baseTurnContext = {
1457
1600
  timestamp,
1458
1601
  interfaceName,
@@ -1461,6 +1604,7 @@ export async function runAgentLoopImpl(
1461
1604
  clientTimezone: timezoneContext.clientTimezone,
1462
1605
  detectedTimezone: timezoneContext.detectedTimezone,
1463
1606
  timeSinceLastMessage,
1607
+ modelProfile: modelProfileStr,
1464
1608
  };
1465
1609
  const unifiedTurnContextStr = buildUnifiedTurnContextBlock(
1466
1610
  isGuardian
@@ -1506,7 +1650,7 @@ export async function runAgentLoopImpl(
1506
1650
  // V2 static memory block (essentials/threads/recent/buffer).
1507
1651
  // `currentMemoryV2Static` is the trust-gated content reused by every
1508
1652
  // re-injection path — it stays non-null on non-full-mode turns so
1509
- // that mid-turn reducer compaction (which strips the prior `<memory>`
1653
+ // that mid-turn reducer compaction (which strips the prior `<info>`
1510
1654
  // block) can restore the freshest content. `memoryV2Static` is the
1511
1655
  // first-turn / post-compaction cadence-gated value for initial
1512
1656
  // injection only. `readMemoryV2StaticContent` self-gates on the v2
@@ -1617,7 +1761,6 @@ export async function runAgentLoopImpl(
1617
1761
  nowScratchpad,
1618
1762
  voiceCallControlPrompt: ctx.voiceCallControlPrompt ?? null,
1619
1763
  transportHints: ctx.transportHints ?? null,
1620
- slackRuntimeContextNotice: ctx.slackRuntimeContextNotice ?? null,
1621
1764
  isNonInteractive: !isInteractiveResolved,
1622
1765
  isBackgroundConversation: isBackgroundConversationType(
1623
1766
  turnStartConversation?.conversationType,
@@ -1704,15 +1847,9 @@ export async function runAgentLoopImpl(
1704
1847
  // After runtime injections are applied, estimate the prompt token count
1705
1848
  // and proactively invoke the reducer if already above budget. This avoids
1706
1849
  // a wasted provider round-trip that would just fail with context_too_large.
1707
- const overflowRecovery = effectiveContextWindow.overflowRecovery;
1708
- const providerMaxTokens = effectiveContextWindow.maxInputTokens;
1709
- // Widen safety margin for large conversations where estimation error
1710
- // compounds across many messages with tool results.
1711
- const baseSafetyMargin = overflowRecovery.safetyMarginRatio;
1712
- const messageCount = ctx.messages.length;
1713
- const safetyMargin =
1714
- messageCount > 50 ? Math.max(baseSafetyMargin, 0.15) : baseSafetyMargin;
1715
- const preflightBudget = Math.floor(providerMaxTokens * (1 - safetyMargin));
1850
+ const initialContextBudget = resolveCurrentContextBudget();
1851
+ const overflowRecovery = initialContextBudget.overflowRecovery;
1852
+ const preflightBudget = initialContextBudget.preflightBudget;
1716
1853
  let reducerState: ReducerState | undefined;
1717
1854
 
1718
1855
  const toolTokenBudget = ctx.agentLoop.getToolTokenBudget(runMessages);
@@ -1789,10 +1926,10 @@ export async function runAgentLoopImpl(
1789
1926
  runMessages,
1790
1927
  systemPrompt: ctx.systemPrompt,
1791
1928
  providerName: estimationProviderName,
1792
- contextWindow: turnContextWindowConfig,
1929
+ contextWindow: resolveCurrentContextWindowConfig(),
1793
1930
  preflightBudget,
1794
1931
  toolTokenBudget,
1795
- maxAttempts: overflowRecovery.maxAttempts,
1932
+ maxAttempts: resolveCurrentContextBudget().overflowRecovery.maxAttempts,
1796
1933
  abortSignal: abortController.signal,
1797
1934
  compactFn: async (msgs, signal, opts) => {
1798
1935
  // Route the reducer's forced-compaction tier through the
@@ -1822,7 +1959,7 @@ export async function runAgentLoopImpl(
1822
1959
  signal,
1823
1960
  options: {
1824
1961
  ...(opts ?? {}),
1825
- overrideProfile: turnOverrideProfile ?? null,
1962
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
1826
1963
  },
1827
1964
  },
1828
1965
  buildPluginTurnContext(ctx, reqId),
@@ -2076,8 +2213,9 @@ export async function runAgentLoopImpl(
2076
2213
  };
2077
2214
  const eventHandler = (event: AgentEvent) =>
2078
2215
  dispatchAgentEvent(state, deps, event);
2079
-
2080
- let yieldedForBudget = false;
2216
+ emitTerminalExit = async (reason: AgentLoopExitReason): Promise<void> => {
2217
+ await eventHandler({ type: "agent_loop_exit", reason });
2218
+ };
2081
2219
 
2082
2220
  const onCheckpoint = async (
2083
2221
  checkpoint: CheckpointInfo,
@@ -2086,6 +2224,7 @@ export async function runAgentLoopImpl(
2086
2224
 
2087
2225
  if (ctx.canHandoffAtCheckpoint()) {
2088
2226
  yieldedForHandoff = true;
2227
+ pendingCheckpointYield = "handoff";
2089
2228
  return "yield";
2090
2229
  }
2091
2230
 
@@ -2093,7 +2232,8 @@ export async function runAgentLoopImpl(
2093
2232
  // yield if we're approaching the preflight budget. This lets the
2094
2233
  // conversation-agent-loop run compaction before the provider rejects.
2095
2234
  if (overflowRecovery.enabled) {
2096
- const midLoopThreshold = preflightBudget * 0.85;
2235
+ const midLoopThreshold =
2236
+ resolveCurrentContextBudget().preflightBudget * 0.85;
2097
2237
  const estimated = await runTokenEstimatePipeline(checkpoint.history);
2098
2238
  if (estimated > midLoopThreshold) {
2099
2239
  rlog.warn(
@@ -2101,6 +2241,7 @@ export async function runAgentLoopImpl(
2101
2241
  "Token estimate approaching budget — yielding for compaction",
2102
2242
  );
2103
2243
  yieldedForBudget = true;
2244
+ pendingCheckpointYield = "budget";
2104
2245
  return "yield";
2105
2246
  }
2106
2247
  }
@@ -2129,7 +2270,9 @@ export async function runAgentLoopImpl(
2129
2270
  turnCallSite,
2130
2271
  loopTurnCtx,
2131
2272
  turnOverrideProfile,
2132
- effectiveContextWindow.maxInputTokens,
2273
+ resolveCurrentMaxInputTokens(),
2274
+ resolveCurrentOverrideProfile,
2275
+ resolveCurrentMaxInputTokens,
2133
2276
  );
2134
2277
 
2135
2278
  rlog.info(
@@ -2137,6 +2280,11 @@ export async function runAgentLoopImpl(
2137
2280
  "Agent loop run completed",
2138
2281
  );
2139
2282
 
2283
+ if (yieldedForHandoff) {
2284
+ await emitTerminalExit?.("checkpoint_handoff");
2285
+ pendingCheckpointYield = null;
2286
+ }
2287
+
2140
2288
  // ── Proactive mid-loop compaction ───────────────────────────────
2141
2289
  // When the agent loop yielded because the token budget check in
2142
2290
  // onCheckpoint detected approaching limits, run compaction on the
@@ -2146,12 +2294,14 @@ export async function runAgentLoopImpl(
2146
2294
  let midLoopCompactAttempts = 0;
2147
2295
  while (
2148
2296
  yieldedForBudget &&
2149
- midLoopCompactAttempts < overflowRecovery.maxAttempts &&
2297
+ midLoopCompactAttempts <
2298
+ resolveCurrentContextBudget().overflowRecovery.maxAttempts &&
2150
2299
  !state.contextTooLargeDetected &&
2151
2300
  !abortController.signal.aborted
2152
2301
  ) {
2153
2302
  midLoopCompactAttempts++;
2154
2303
  yieldedForBudget = false;
2304
+ pendingCheckpointYield = null;
2155
2305
 
2156
2306
  rlog.info(
2157
2307
  { phase: "mid-loop-compact" },
@@ -2162,14 +2312,7 @@ export async function runAgentLoopImpl(
2162
2312
  // so we compact the "raw" persistent messages.
2163
2313
  const rawHistory = stripInjectionsForCompaction(updatedHistory);
2164
2314
  ctx.messages = rawHistory;
2165
- try {
2166
- clearStrippedInjectionMetadataForConversation(ctx.conversationId);
2167
- } catch (err) {
2168
- rlog.warn(
2169
- { err },
2170
- "Failed to clear stripped-injection metadata after compaction strip (non-fatal)",
2171
- );
2172
- }
2315
+ setConversationHistoryStrippedAt(ctx.conversationId, Date.now());
2173
2316
 
2174
2317
  ctx.emitActivityState(
2175
2318
  "thinking",
@@ -2193,10 +2336,11 @@ export async function runAgentLoopImpl(
2193
2336
  options: {
2194
2337
  lastCompactedAt: ctx.contextCompactedAt ?? undefined,
2195
2338
  force: true,
2196
- targetInputTokensOverride: preflightBudget,
2339
+ targetInputTokensOverride:
2340
+ resolveCurrentContextBudget().preflightBudget,
2197
2341
  conversationOriginChannel:
2198
2342
  getConversationOriginChannel(ctx.conversationId) ?? undefined,
2199
- overrideProfile: turnOverrideProfile ?? null,
2343
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2200
2344
  },
2201
2345
  },
2202
2346
  buildPluginTurnContext(ctx, reqId),
@@ -2282,7 +2426,9 @@ export async function runAgentLoopImpl(
2282
2426
  turnCallSite,
2283
2427
  loopTurnCtx,
2284
2428
  turnOverrideProfile,
2285
- effectiveContextWindow.maxInputTokens,
2429
+ resolveCurrentMaxInputTokens(),
2430
+ resolveCurrentOverrideProfile,
2431
+ resolveCurrentMaxInputTokens,
2286
2432
  );
2287
2433
  }
2288
2434
 
@@ -2296,7 +2442,8 @@ export async function runAgentLoopImpl(
2296
2442
  {
2297
2443
  phase: "mid-loop-compact",
2298
2444
  midLoopCompactAttempts,
2299
- maxAttempts: overflowRecovery.maxAttempts,
2445
+ maxAttempts:
2446
+ resolveCurrentContextBudget().overflowRecovery.maxAttempts,
2300
2447
  },
2301
2448
  "Mid-loop compaction exhausted all attempts — escalating to convergence loop",
2302
2449
  );
@@ -2339,7 +2486,9 @@ export async function runAgentLoopImpl(
2339
2486
  turnCallSite,
2340
2487
  loopTurnCtx,
2341
2488
  turnOverrideProfile,
2342
- effectiveContextWindow.maxInputTokens,
2489
+ resolveCurrentMaxInputTokens(),
2490
+ resolveCurrentOverrideProfile,
2491
+ resolveCurrentMaxInputTokens,
2343
2492
  );
2344
2493
 
2345
2494
  if (state.orderingErrorDetected) {
@@ -2408,7 +2557,9 @@ export async function runAgentLoopImpl(
2408
2557
  turnCallSite,
2409
2558
  loopTurnCtx,
2410
2559
  turnOverrideProfile,
2411
- effectiveContextWindow.maxInputTokens,
2560
+ resolveCurrentMaxInputTokens(),
2561
+ resolveCurrentOverrideProfile,
2562
+ resolveCurrentMaxInputTokens,
2412
2563
  );
2413
2564
  if (state.imageTooLargeDetected) {
2414
2565
  rlog.error(
@@ -2445,14 +2596,7 @@ export async function runAgentLoopImpl(
2445
2596
 
2446
2597
  if (updatedHistory.length > preRunHistoryLength) {
2447
2598
  ctx.messages = stripInjectionsForCompaction(updatedHistory);
2448
- try {
2449
- clearStrippedInjectionMetadataForConversation(ctx.conversationId);
2450
- } catch (err) {
2451
- rlog.warn(
2452
- { err },
2453
- "Failed to clear stripped-injection metadata after compaction strip (non-fatal)",
2454
- );
2455
- }
2599
+ setConversationHistoryStrippedAt(ctx.conversationId, Date.now());
2456
2600
  convergenceStripped = true;
2457
2601
  preRepairMessages = updatedHistory;
2458
2602
  preRunHistoryLength = updatedHistory.length;
@@ -2479,18 +2623,21 @@ export async function runAgentLoopImpl(
2479
2623
  toolTokenBudget,
2480
2624
  },
2481
2625
  );
2482
- let correctedTarget = preflightBudget;
2626
+ const convergenceBudget = resolveCurrentContextBudget();
2627
+ let correctedTarget = convergenceBudget.preflightBudget;
2483
2628
  if (actualTokens && estimatedTokensAtOverflow > 0) {
2484
2629
  const estimationErrorRatio = actualTokens / estimatedTokensAtOverflow;
2485
2630
  if (estimationErrorRatio > 1.0) {
2486
- correctedTarget = Math.floor(preflightBudget / estimationErrorRatio);
2631
+ correctedTarget = Math.floor(
2632
+ convergenceBudget.preflightBudget / estimationErrorRatio,
2633
+ );
2487
2634
  rlog.warn(
2488
2635
  {
2489
2636
  phase: "convergence",
2490
2637
  actualTokens,
2491
2638
  estimatedTokens: estimatedTokensAtOverflow,
2492
2639
  estimationErrorRatio: estimationErrorRatio.toFixed(2),
2493
- preflightBudget,
2640
+ preflightBudget: convergenceBudget.preflightBudget,
2494
2641
  correctedTarget,
2495
2642
  },
2496
2643
  "Adjusting compaction target based on observed estimation error",
@@ -2515,11 +2662,11 @@ export async function runAgentLoopImpl(
2515
2662
  systemPrompt: ctx.systemPrompt,
2516
2663
  tools: undefined,
2517
2664
  compaction: emergencyConfig,
2518
- maxInputTokens: effectiveContextWindow.maxInputTokens,
2665
+ maxInputTokens: resolveCurrentMaxInputTokens(),
2519
2666
  previousEstimatedInputTokens: estimatedTokensAtOverflow,
2520
2667
  force: true,
2521
2668
  signal: abortController.signal,
2522
- overrideProfile: turnOverrideProfile ?? null,
2669
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2523
2670
  nonPersistedPrefixCount:
2524
2671
  ctx.contextWindowManager.nonPersistedPrefixCount,
2525
2672
  });
@@ -2557,7 +2704,7 @@ export async function runAgentLoopImpl(
2557
2704
  }
2558
2705
 
2559
2706
  let convergenceAttempts = 0;
2560
- const maxAttempts = overflowRecovery.maxAttempts;
2707
+ const maxAttempts = convergenceBudget.overflowRecovery.maxAttempts;
2561
2708
 
2562
2709
  while (
2563
2710
  state.contextTooLargeDetected &&
@@ -2586,7 +2733,7 @@ export async function runAgentLoopImpl(
2586
2733
  {
2587
2734
  providerName: estimationProviderName,
2588
2735
  systemPrompt: ctx.systemPrompt,
2589
- contextWindow: turnContextWindowConfig,
2736
+ contextWindow: resolveCurrentContextWindowConfig(),
2590
2737
  targetTokens: correctedTarget,
2591
2738
  toolTokenBudget,
2592
2739
  },
@@ -2594,7 +2741,7 @@ export async function runAgentLoopImpl(
2594
2741
  (msgs, signal, opts) =>
2595
2742
  ctx.contextWindowManager.maybeCompact(msgs, signal!, {
2596
2743
  ...(opts ?? {}),
2597
- overrideProfile: turnOverrideProfile ?? null,
2744
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2598
2745
  }),
2599
2746
  abortController.signal,
2600
2747
  );
@@ -2670,7 +2817,9 @@ export async function runAgentLoopImpl(
2670
2817
  turnCallSite,
2671
2818
  loopTurnCtx,
2672
2819
  turnOverrideProfile,
2673
- effectiveContextWindow.maxInputTokens,
2820
+ resolveCurrentMaxInputTokens(),
2821
+ resolveCurrentOverrideProfile,
2822
+ resolveCurrentMaxInputTokens,
2674
2823
  );
2675
2824
 
2676
2825
  // If the rerun still yields at checkpoint, the turn is still
@@ -2692,14 +2841,7 @@ export async function runAgentLoopImpl(
2692
2841
  // pre-rerun messages.
2693
2842
  if (updatedHistory.length > preRunHistoryLength) {
2694
2843
  ctx.messages = stripInjectionsForCompaction(updatedHistory);
2695
- try {
2696
- clearStrippedInjectionMetadataForConversation(ctx.conversationId);
2697
- } catch (err) {
2698
- rlog.warn(
2699
- { err },
2700
- "Failed to clear stripped-injection metadata after compaction strip (non-fatal)",
2701
- );
2702
- }
2844
+ setConversationHistoryStrippedAt(ctx.conversationId, Date.now());
2703
2845
  convergenceStripped = true;
2704
2846
  preRepairMessages = updatedHistory;
2705
2847
  preRunHistoryLength = updatedHistory.length;
@@ -2748,7 +2890,7 @@ export async function runAgentLoopImpl(
2748
2890
  force: true,
2749
2891
  minKeepRecentUserTurns: 0,
2750
2892
  targetInputTokensOverride: correctedTarget,
2751
- overrideProfile: turnOverrideProfile ?? null,
2893
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2752
2894
  },
2753
2895
  },
2754
2896
  buildPluginTurnContext(ctx, reqId),
@@ -2831,7 +2973,9 @@ export async function runAgentLoopImpl(
2831
2973
  turnCallSite,
2832
2974
  loopTurnCtx,
2833
2975
  turnOverrideProfile,
2834
- effectiveContextWindow.maxInputTokens,
2976
+ resolveCurrentMaxInputTokens(),
2977
+ resolveCurrentOverrideProfile,
2978
+ resolveCurrentMaxInputTokens,
2835
2979
  );
2836
2980
  }
2837
2981
  // action === "fail_gracefully" falls through to the final error below
@@ -2843,7 +2987,35 @@ export async function runAgentLoopImpl(
2843
2987
  new Error("context_length_exceeded"),
2844
2988
  { phase: "agent_loop" },
2845
2989
  );
2990
+ await emitTerminalExit?.("context_too_large");
2991
+ pendingCheckpointYield = null;
2846
2992
  onEvent(buildConversationErrorMessage(ctx.conversationId, classified));
2993
+ } else if (yieldedForBudget && !abortController.signal.aborted) {
2994
+ // The auto_compress_latest_turn rerun (action === "auto_compress_latest_turn"
2995
+ // above) reset `contextTooLargeDetected` to false before its final
2996
+ // `agentLoop.run`, so the context-too-large branch above won't fire
2997
+ // even when that rerun yields at the mid-loop budget checkpoint with
2998
+ // no further recovery layer to re-enter. Without surfacing this here,
2999
+ // the turn terminates silently — the inspector sees `agent_loop_exit_reason
3000
+ // = NULL` and the user sees no message at all (just a "ghost" turn).
3001
+ //
3002
+ // Unlike provider-error persistence at L3091 — which only fires when
3003
+ // the loop produced NO assistant output — budget_yield_unrecovered
3004
+ // typically yields AFTER one or more successful tool-use iterations,
3005
+ // so `hasAssistantResponse` is true and that path would skip us. We
3006
+ // capture the classification here so the live SSE event fires
3007
+ // immediately, and persist a dedicated notice row below — after the
3008
+ // pendingToolResults flush — so the transcript reads as: tool-use →
3009
+ // tool results → "I couldn't fit the next step…" notice. Persisting
3010
+ // earlier would orphan an assistant(tool_use) from its user(tool_result),
3011
+ // breaking provider adjacency on replay.
3012
+ budgetYieldClassification = budgetYieldUnrecoveredClassification();
3013
+ onEvent(
3014
+ buildConversationErrorMessage(
3015
+ ctx.conversationId,
3016
+ budgetYieldClassification,
3017
+ ),
3018
+ );
2847
3019
  }
2848
3020
  }
2849
3021
 
@@ -2919,6 +3091,52 @@ export async function runAgentLoopImpl(
2919
3091
  state.pendingToolResults.clear();
2920
3092
  }
2921
3093
 
3094
+ // Persist the budget_yield_unrecovered notice now that any pending
3095
+ // tool_results have flushed. The SSE event already fired upstream; this
3096
+ // makes the row durable in the right position: tool-use → tool-results →
3097
+ // notice. Doing it earlier (e.g. at the detection site) would land the
3098
+ // assistant row between a tool_use and its tool_result and break provider
3099
+ // adjacency on replay.
3100
+ if (budgetYieldClassification && !abortController.signal.aborted) {
3101
+ const yieldNoticeMessage = createAssistantMessage(
3102
+ budgetYieldClassification.userMessage,
3103
+ );
3104
+ const yieldNoticeMetadata = {
3105
+ ...provenanceFromTrustContext(ctx.trustContext),
3106
+ userMessageChannel: capturedTurnChannelContext.userMessageChannel,
3107
+ assistantMessageChannel:
3108
+ capturedTurnChannelContext.assistantMessageChannel,
3109
+ userMessageInterface: capturedTurnInterfaceContext.userMessageInterface,
3110
+ assistantMessageInterface:
3111
+ capturedTurnInterfaceContext.assistantMessageInterface,
3112
+ };
3113
+ try {
3114
+ await runPipeline<PersistArgs, PersistResult>(
3115
+ "persistence",
3116
+ getMiddlewaresFor("persistence"),
3117
+ defaultPersistenceTerminal,
3118
+ {
3119
+ op: "add",
3120
+ conversationId: ctx.conversationId,
3121
+ role: "assistant",
3122
+ content: JSON.stringify(yieldNoticeMessage.content),
3123
+ metadata: yieldNoticeMetadata,
3124
+ },
3125
+ buildPluginTurnContext(ctx, reqId),
3126
+ DEFAULT_TIMEOUTS.persistence,
3127
+ );
3128
+ } catch (err) {
3129
+ // Non-fatal — a DB hiccup must not escalate a budget-yield exit into
3130
+ // a turn-level throw. The live SSE event was already emitted, so the
3131
+ // user still sees the notice this turn even if the durable row missed.
3132
+ rlog.warn(
3133
+ { err },
3134
+ "Failed to persist budget_yield_unrecovered notice (non-fatal)",
3135
+ );
3136
+ }
3137
+ await emitTerminalExit?.("budget_yield_unrecovered");
3138
+ }
3139
+
2922
3140
  // Reconstruct history
2923
3141
  const newMessages = updatedHistory.slice(preRunHistoryLength).map((msg) => {
2924
3142
  if (msg.role !== "assistant") return msg;
@@ -3041,11 +3259,11 @@ export async function runAgentLoopImpl(
3041
3259
  state.exchangeLlmCallCount,
3042
3260
  {
3043
3261
  tokens: state.lastCallInputTokens,
3044
- maxTokens: effectiveContextWindow.maxInputTokens,
3262
+ maxTokens: resolveCurrentMaxInputTokens(),
3045
3263
  },
3046
3264
  {
3047
3265
  callSite: turnCallSite,
3048
- overrideProfile: turnOverrideProfile ?? null,
3266
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
3049
3267
  },
3050
3268
  );
3051
3269
 
@@ -3101,10 +3319,13 @@ export async function runAgentLoopImpl(
3101
3319
  ctx.lastAssistantAttachments = assistantAttachments;
3102
3320
  ctx.lastAttachmentWarnings = attachmentResult.directiveWarnings;
3103
3321
  syncLastAssistantMessageToDisk();
3104
- const clientDisplayMessageId = getClientDisplayMessageId(state);
3105
3322
 
3106
3323
  // Re-check: the user may have cancelled during attachment resolution
3107
3324
  if (abortController.signal.aborted) {
3325
+ if (pendingCheckpointYield === "budget") {
3326
+ await emitTerminalExit?.("aborted_after_checkpoint");
3327
+ pendingCheckpointYield = null;
3328
+ }
3108
3329
  ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3109
3330
  ctx.traceEmitter.emit(
3110
3331
  "generation_cancelled",
@@ -3143,9 +3364,6 @@ export async function runAgentLoopImpl(
3143
3364
  ...(state.lastAssistantMessageId
3144
3365
  ? { messageId: state.lastAssistantMessageId }
3145
3366
  : {}),
3146
- ...(clientDisplayMessageId
3147
- ? { displayMessageId: clientDisplayMessageId }
3148
- : {}),
3149
3367
  });
3150
3368
  publishLoopMessagesChanged();
3151
3369
  } else {
@@ -3170,9 +3388,6 @@ export async function runAgentLoopImpl(
3170
3388
  ...(state.lastAssistantMessageId
3171
3389
  ? { messageId: state.lastAssistantMessageId }
3172
3390
  : {}),
3173
- ...(clientDisplayMessageId
3174
- ? { displayMessageId: clientDisplayMessageId }
3175
- : {}),
3176
3391
  });
3177
3392
  publishLoopMessagesChanged();
3178
3393
 
@@ -3246,6 +3461,10 @@ export async function runAgentLoopImpl(
3246
3461
  aborted: abortController.signal.aborted,
3247
3462
  };
3248
3463
  if (isUserCancellation(err, errorCtx)) {
3464
+ if (pendingCheckpointYield === "budget") {
3465
+ await emitTerminalExit?.("aborted_after_checkpoint");
3466
+ pendingCheckpointYield = null;
3467
+ }
3249
3468
  ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3250
3469
  rlog.info("Generation cancelled by user");
3251
3470
  ctx.traceEmitter.emit(
@@ -3338,7 +3557,6 @@ export async function runAgentLoopImpl(
3338
3557
  ctx.diskPressureCleanupModeActive = false;
3339
3558
  ctx.preactivatedSkillIds = undefined;
3340
3559
  ctx.currentTurnOverrideProfile = undefined;
3341
- ctx.slackRuntimeContextNotice = undefined;
3342
3560
  // Channel command intents (e.g. Telegram /start) are single-turn metadata.
3343
3561
  // Clear at turn end so they never leak into subsequent unrelated messages.
3344
3562
  ctx.commandIntent = undefined;
@@ -3476,6 +3694,7 @@ export async function applyCompactionResult(
3476
3694
  result.summaryText,
3477
3695
  ctx.contextCompactedMessageCount,
3478
3696
  );
3697
+ setConversationHistoryStrippedAt(ctx.conversationId, compactedAt);
3479
3698
  if (options.slackContextCompactionWatermarkTs) {
3480
3699
  updateConversationSlackContextWatermark(
3481
3700
  ctx.conversationId,