@vellumai/assistant 0.8.3 → 0.8.4

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 (342) hide show
  1. package/docker-entrypoint.sh +0 -1
  2. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  3. package/openapi.yaml +610 -16
  4. package/package.json +1 -1
  5. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  6. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  7. package/src/__tests__/agent-loop.test.ts +88 -3
  8. package/src/__tests__/anthropic-provider.test.ts +272 -0
  9. package/src/__tests__/approval-cascade.test.ts +1 -1
  10. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  11. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  12. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  13. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  14. package/src/__tests__/compaction-events.test.ts +1 -1
  15. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  16. package/src/__tests__/config-watcher.test.ts +1 -1
  17. package/src/__tests__/context-token-estimator.test.ts +91 -1
  18. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  19. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
  20. package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
  21. package/src/__tests__/conversation-agent-loop.test.ts +25 -7
  22. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  23. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  24. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  25. package/src/__tests__/conversation-fork-crud.test.ts +161 -0
  26. package/src/__tests__/conversation-lifecycle.test.ts +1 -1
  27. package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
  28. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  29. package/src/__tests__/conversation-pairing.test.ts +2 -2
  30. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  31. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
  32. package/src/__tests__/conversation-queue.test.ts +1 -1
  33. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  34. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  35. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  36. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  37. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  38. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  39. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  40. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
  41. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  42. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  43. package/src/__tests__/credential-security-invariants.test.ts +6 -0
  44. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  45. package/src/__tests__/dm-backfill.test.ts +64 -0
  46. package/src/__tests__/dm-persistence.test.ts +33 -0
  47. package/src/__tests__/document-find-replace.test.ts +501 -0
  48. package/src/__tests__/first-greeting.test.ts +23 -2
  49. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  50. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  51. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  52. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  53. package/src/__tests__/host-file-proxy.test.ts +8 -1
  54. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  55. package/src/__tests__/identity-routes.test.ts +57 -0
  56. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  57. package/src/__tests__/injector-chain.test.ts +2 -0
  58. package/src/__tests__/injector-document-comments.test.ts +378 -0
  59. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  60. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  61. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  62. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  63. package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
  64. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  65. package/src/__tests__/llm-resolver.test.ts +85 -1
  66. package/src/__tests__/log-export-routes.test.ts +99 -2
  67. package/src/__tests__/message-queue-steer.test.ts +114 -0
  68. package/src/__tests__/openai-provider.test.ts +105 -0
  69. package/src/__tests__/openai-responses-provider.test.ts +4 -4
  70. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  71. package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
  72. package/src/__tests__/platform.test.ts +0 -3
  73. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  74. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  75. package/src/__tests__/process-message-display-content.test.ts +21 -16
  76. package/src/__tests__/server-history-render.test.ts +83 -4
  77. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  78. package/src/__tests__/system-prompt.test.ts +51 -28
  79. package/src/__tests__/terminal-tools.test.ts +11 -1
  80. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  81. package/src/__tests__/thread-backfill.test.ts +370 -22
  82. package/src/__tests__/tool-executor.test.ts +90 -1
  83. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  84. package/src/__tests__/twilio-routes.test.ts +1 -1
  85. package/src/__tests__/web-fetch.test.ts +2 -2
  86. package/src/__tests__/workspace-git-service.test.ts +88 -5
  87. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  88. package/src/agent/attachments.ts +1 -0
  89. package/src/agent/loop.ts +57 -20
  90. package/src/background-wake/next-wake.test.ts +289 -0
  91. package/src/background-wake/next-wake.ts +172 -0
  92. package/src/browser/operations.ts +15 -0
  93. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  94. package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
  95. package/src/cli/commands/conversations.ts +128 -1
  96. package/src/cli/commands/inference-providers.ts +147 -1
  97. package/src/cli/commands/memory-v2.ts +308 -0
  98. package/src/cli/commands/notifications.ts +24 -2
  99. package/src/cli/utils/conversation-id.ts +17 -5
  100. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  101. package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
  102. package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
  103. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  104. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  105. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  106. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  107. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  108. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  109. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  110. package/src/config/bundled-tool-registry.ts +22 -12
  111. package/src/config/call-site-defaults.ts +19 -0
  112. package/src/config/feature-flag-registry.json +99 -3
  113. package/src/config/llm-resolver.ts +16 -2
  114. package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
  115. package/src/config/schemas/call-site-catalog.ts +21 -0
  116. package/src/config/schemas/llm.ts +3 -0
  117. package/src/config/schemas/memory-v2.ts +48 -1
  118. package/src/context/compactor.ts +8 -1
  119. package/src/context/token-estimator.ts +47 -4
  120. package/src/context/window-manager.ts +25 -0
  121. package/src/credential-health/credential-health-service.ts +34 -19
  122. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  123. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  124. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  125. package/src/daemon/conversation-agent-loop-handlers.ts +153 -23
  126. package/src/daemon/conversation-agent-loop.ts +223 -54
  127. package/src/daemon/conversation-lifecycle.ts +142 -116
  128. package/src/daemon/conversation-messaging.ts +3 -0
  129. package/src/daemon/conversation-process.ts +273 -0
  130. package/src/daemon/conversation-queue-manager.ts +14 -0
  131. package/src/daemon/conversation-runtime-assembly.ts +135 -75
  132. package/src/daemon/conversation-slash.ts +37 -5
  133. package/src/daemon/conversation-surfaces.ts +45 -2
  134. package/src/daemon/conversation-tool-setup.ts +7 -0
  135. package/src/daemon/conversation.ts +42 -5
  136. package/src/daemon/first-greeting.ts +10 -0
  137. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  138. package/src/daemon/handlers/config-a2a.ts +160 -0
  139. package/src/daemon/handlers/config-model.test.ts +1 -0
  140. package/src/daemon/handlers/conversations.ts +79 -0
  141. package/src/daemon/handlers/shared.ts +92 -29
  142. package/src/daemon/host-bash-proxy.ts +1 -1
  143. package/src/daemon/host-cu-proxy.ts +1 -1
  144. package/src/daemon/host-file-proxy.ts +1 -1
  145. package/src/daemon/host-transfer-proxy.ts +1 -1
  146. package/src/daemon/lifecycle.ts +18 -4
  147. package/src/daemon/message-protocol.ts +4 -0
  148. package/src/daemon/message-types/conversations.ts +8 -0
  149. package/src/daemon/message-types/document-comments.ts +50 -0
  150. package/src/daemon/message-types/messages.ts +68 -1
  151. package/src/daemon/message-types/surfaces.ts +3 -1
  152. package/src/daemon/message-types/web-activity.ts +57 -0
  153. package/src/daemon/plugin-source-watcher.ts +135 -3
  154. package/src/daemon/process-message.ts +69 -12
  155. package/src/daemon/query-complexity-router.ts +75 -0
  156. package/src/daemon/trust-context.ts +6 -0
  157. package/src/documents/document-comments-store.test.ts +338 -0
  158. package/src/documents/document-comments-store.ts +237 -0
  159. package/src/documents/document-store.ts +202 -0
  160. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  161. package/src/heartbeat/heartbeat-service.ts +1 -0
  162. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  163. package/src/home/feed-types.ts +6 -1
  164. package/src/home/home-content-refresh.ts +52 -0
  165. package/src/home/home-greeting-cache.ts +69 -0
  166. package/src/home/home-greeting.ts +94 -0
  167. package/src/home/suggested-prompts.ts +177 -9
  168. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  169. package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
  170. package/src/memory/conversation-crud.ts +133 -43
  171. package/src/memory/db-init.ts +16 -0
  172. package/src/memory/delivery-crud.ts +41 -0
  173. package/src/memory/delivery-status.ts +141 -15
  174. package/src/memory/external-conversation-store.ts +32 -1
  175. package/src/memory/jobs-worker.ts +21 -1
  176. package/src/memory/memory-retrospective-constants.ts +28 -0
  177. package/src/memory/memory-retrospective-enqueue.ts +3 -2
  178. package/src/memory/memory-retrospective-job.ts +408 -18
  179. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  180. package/src/memory/memory-v2-activation-log-store.ts +26 -8
  181. package/src/memory/migrations/100-core-tables.ts +1 -0
  182. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  183. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  184. package/src/memory/migrations/253-document-comments.ts +47 -0
  185. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  186. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  187. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  188. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  189. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  190. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  191. package/src/memory/migrations/index.ts +17 -0
  192. package/src/memory/migrations/registry.ts +25 -0
  193. package/src/memory/onboarding-events-store.ts +7 -0
  194. package/src/memory/schema/calls.ts +1 -0
  195. package/src/memory/schema/conversations.ts +3 -0
  196. package/src/memory/schema/infrastructure.ts +1 -0
  197. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  198. package/src/memory/v2/__tests__/injection.test.ts +31 -14
  199. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  200. package/src/memory/v2/__tests__/router.test.ts +489 -1
  201. package/src/memory/v2/consolidation-job.ts +14 -0
  202. package/src/memory/v2/injection-events.ts +101 -0
  203. package/src/memory/v2/injection.ts +21 -10
  204. package/src/memory/v2/page-index.ts +209 -7
  205. package/src/memory/v2/page-store.ts +18 -0
  206. package/src/memory/v2/router.ts +209 -55
  207. package/src/messaging/providers/index.ts +7 -1
  208. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  209. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  210. package/src/messaging/providers/slack/adapter.ts +178 -25
  211. package/src/messaging/providers/slack/api.test.ts +54 -0
  212. package/src/messaging/providers/slack/api.ts +119 -3
  213. package/src/messaging/providers/slack/client.ts +12 -0
  214. package/src/messaging/providers/slack/deep-link.ts +20 -1
  215. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  216. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  217. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  218. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  219. package/src/messaging/providers/slack/send.test.ts +77 -0
  220. package/src/messaging/providers/slack/send.ts +8 -2
  221. package/src/messaging/providers/slack/types.ts +14 -0
  222. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  223. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  224. package/src/notifications/conversation-seed-composer.ts +14 -2
  225. package/src/notifications/deferred-emit.ts +135 -0
  226. package/src/notifications/emit-signal.ts +9 -1
  227. package/src/notifications/home-feed-side-effect.ts +60 -30
  228. package/src/oauth/connect-orchestrator.ts +3 -0
  229. package/src/oauth/credential-token-resolver.ts +2 -0
  230. package/src/oauth/manual-token-connection.ts +19 -0
  231. package/src/oauth/oauth-store.ts +12 -0
  232. package/src/oauth/seed-providers.ts +22 -0
  233. package/src/permissions/prompter.ts +5 -2
  234. package/src/permissions/secret-prompter.ts +4 -1
  235. package/src/plugins/defaults/injectors.ts +82 -9
  236. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  237. package/src/prompts/normalize-onboarding.ts +40 -0
  238. package/src/prompts/sections.ts +32 -14
  239. package/src/prompts/system-prompt.ts +105 -68
  240. package/src/prompts/template-detection.ts +37 -0
  241. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  242. package/src/prompts/templates/BOOTSTRAP.md +8 -0
  243. package/src/prompts/templates/VOICE.md +3 -0
  244. package/src/prompts/templates/system-sections.ts +53 -3
  245. package/src/providers/anthropic/client.ts +132 -5
  246. package/src/providers/fireworks/client.ts +20 -2
  247. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  248. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  249. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  250. package/src/providers/inference/adapter-factory.ts +15 -1
  251. package/src/providers/inference/auth.ts +3 -3
  252. package/src/providers/inference/codex-token-refresh.ts +128 -0
  253. package/src/providers/inference/resolve-auth.ts +49 -6
  254. package/src/providers/model-catalog.ts +48 -1
  255. package/src/providers/openai/chat-completions-provider.ts +57 -20
  256. package/src/providers/openai/responses-provider.ts +9 -3
  257. package/src/providers/openrouter/client.ts +5 -1
  258. package/src/providers/types.ts +25 -0
  259. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  260. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  261. package/src/runtime/agent-wake.ts +151 -56
  262. package/src/runtime/auth/route-policy.ts +7 -3
  263. package/src/runtime/background-job-runner.ts +26 -0
  264. package/src/runtime/channel-reply-delivery.ts +182 -47
  265. package/src/runtime/channel-retry-sweep.ts +141 -16
  266. package/src/runtime/http-types.ts +7 -4
  267. package/src/runtime/pending-interactions.ts +51 -8
  268. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  269. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
  270. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  271. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
  272. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  273. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  274. package/src/runtime/routes/approval-routes.ts +4 -1
  275. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  276. package/src/runtime/routes/content-source-routes.ts +78 -0
  277. package/src/runtime/routes/conversation-cli-routes.ts +146 -1
  278. package/src/runtime/routes/conversation-query-routes.ts +60 -1
  279. package/src/runtime/routes/conversation-routes.ts +281 -76
  280. package/src/runtime/routes/document-comments-routes.ts +287 -0
  281. package/src/runtime/routes/documents-routes.ts +33 -0
  282. package/src/runtime/routes/home-feed-routes.ts +6 -3
  283. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  284. package/src/runtime/routes/host-browser-routes.ts +8 -1
  285. package/src/runtime/routes/identity-routes.ts +21 -0
  286. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  287. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  288. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  289. package/src/runtime/routes/index.ts +12 -4
  290. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  291. package/src/runtime/routes/integrations/a2a.ts +60 -1
  292. package/src/runtime/routes/log-export-routes.ts +39 -0
  293. package/src/runtime/routes/memory-v2-routes.ts +217 -0
  294. package/src/runtime/routes/notification-routes.ts +19 -2
  295. package/src/runtime/routes/question-routes.ts +4 -1
  296. package/src/runtime/routes/sanity-routes.ts +159 -0
  297. package/src/runtime/routes/slack-channel-routes.ts +187 -0
  298. package/src/runtime/services/conversation-serializer.ts +30 -4
  299. package/src/schedule/integration-status.ts +3 -1
  300. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  301. package/src/security/oauth2-device-code.ts +307 -0
  302. package/src/security/oauth2.ts +26 -9
  303. package/src/security/secure-keys.ts +5 -0
  304. package/src/skills/catalog-install.ts +6 -2
  305. package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
  306. package/src/tools/browser/browser-execution.ts +93 -0
  307. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  308. package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
  309. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
  310. package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
  311. package/src/tools/browser/cdp-client/factory.ts +87 -3
  312. package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
  313. package/src/tools/browser/cdp-client/types.ts +36 -0
  314. package/src/tools/browser/pinned-tabs.ts +90 -0
  315. package/src/tools/document/document-comment-tool.test.ts +379 -0
  316. package/src/tools/document/document-comment-tool.ts +156 -0
  317. package/src/tools/document/document-tool.ts +128 -2
  318. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  319. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  320. package/src/tools/network/domain-normalize.ts +17 -0
  321. package/src/tools/network/web-fetch.ts +213 -64
  322. package/src/tools/network/web-search.ts +191 -66
  323. package/src/tools/terminal/safe-env.ts +3 -2
  324. package/src/tools/tool-approval-handler.ts +19 -12
  325. package/src/tools/types.ts +4 -0
  326. package/src/tools/ui-surface/definitions.ts +3 -1
  327. package/src/types/onboarding-context.ts +4 -0
  328. package/src/util/__tests__/favicon.test.ts +84 -0
  329. package/src/util/favicon.ts +40 -0
  330. package/src/util/platform.ts +0 -5
  331. package/src/workspace/git-service.ts +75 -4
  332. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  333. package/src/workspace/migrations/registry.ts +2 -0
  334. package/src/config/bundled-skills/document/SKILL.md +0 -54
  335. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  336. package/src/daemon/seed-files.ts +0 -18
  337. package/src/runtime/routes/interface-routes.ts +0 -43
  338. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  339. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  340. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  341. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  342. /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";
@@ -25,11 +26,16 @@ import type {
25
26
  TurnChannelContext,
26
27
  TurnInterfaceContext,
27
28
  } from "../channels/types.js";
29
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
28
30
  import {
29
31
  contextWindowConfigFromEffective,
32
+ type EffectiveContextWindow,
30
33
  resolveEffectiveContextWindow,
31
34
  } from "../config/llm-context-resolution.js";
32
- import { resolveCallSiteConfig } from "../config/llm-resolver.js";
35
+ import {
36
+ resolveCallSiteConfig,
37
+ resolveDefaultProfileKey,
38
+ } from "../config/llm-resolver.js";
33
39
  import { getConfig } from "../config/loader.js";
34
40
  import type { LLMCallSite } from "../config/schemas/llm.js";
35
41
  import type { ContextWindowConfig } from "../config/types.js";
@@ -62,6 +68,7 @@ import {
62
68
  getLastUserTimestampBefore,
63
69
  getMessageById,
64
70
  provenanceFromTrustContext,
71
+ setLastNotifiedInferenceProfile,
65
72
  updateConversationContextWindow,
66
73
  updateConversationSlackContextWatermark,
67
74
  } from "../memory/conversation-crud.js";
@@ -215,6 +222,10 @@ import {
215
222
  SYNC_TAGS,
216
223
  } from "./message-types/sync.js";
217
224
  import { parseActualTokensFromError } from "./parse-actual-tokens-from-error.js";
225
+ import {
226
+ classifyQueryComplexity,
227
+ complexityTierToProfileKey,
228
+ } from "./query-complexity-router.js";
218
229
  import type { TraceEmitter } from "./trace-emitter.js";
219
230
  import type { TrustContext } from "./trust-context.js";
220
231
  import { stripHistoricalWebSearchResults } from "./web-search-history.js";
@@ -521,12 +532,11 @@ export interface AgentLoopConversationContext {
521
532
  /** Per-turn snapshot of channelCapabilities, frozen at message-processing start. */
522
533
  currentTurnChannelCapabilities?: ChannelCapabilities;
523
534
  /**
524
- * Per-turn snapshot of the resolved inference-profile override. Read by
535
+ * Current inference-profile override for this turn. Read by
525
536
  * `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.
537
+ * profile the agent loop is sending to the provider. Refreshed between
538
+ * model calls so an explicitly confirmed profile session opened mid-turn
539
+ * is inherited by later tool executions and nested subagents.
530
540
  */
531
541
  currentTurnOverrideProfile?: string;
532
542
  commandIntent?: { type: string; payload?: string; languageCode?: string };
@@ -536,7 +546,6 @@ export interface AgentLoopConversationContext {
536
546
  assistantId?: string;
537
547
  voiceCallControlPrompt?: string;
538
548
  transportHints?: string[];
539
- slackRuntimeContextNotice?: string;
540
549
  clientTimezone?: string;
541
550
 
542
551
  readonly coreToolNames: Set<string>;
@@ -670,6 +679,11 @@ export async function runAgentLoopImpl(
670
679
  requestId: reqId,
671
680
  });
672
681
  let yieldedForHandoff = false;
682
+ let yieldedForBudget = false;
683
+ let pendingCheckpointYield: "budget" | "handoff" | null = null;
684
+ let emitTerminalExit:
685
+ | ((reason: AgentLoopExitReason) => Promise<void>)
686
+ | null = null;
673
687
 
674
688
  // Default user-initiated turns to the `mainAgent` call site. Other
675
689
  // invocation contexts (heartbeat, filing, analyze, etc.) pass their own
@@ -691,31 +705,134 @@ export async function runAgentLoopImpl(
691
705
  // spawned subagent's background conversation) wins over the row read
692
706
  // so the agent loop's own background-skip rule doesn't zero out an
693
707
  // explicitly inherited override.
694
- const turnOverrideProfile =
708
+ const userExplicitOverride =
695
709
  options?.overrideProfile ??
696
710
  getConversationOverrideProfileFromRow(turnStartConversation);
697
711
 
698
712
  const config = getConfig();
713
+
714
+ // Query complexity routing: when no explicit user override is set and the
715
+ // feature flag is enabled, classify the query and route to the appropriate
716
+ // profile for this turn. The override is ephemeral (not persisted).
717
+ let turnOverrideProfile = userExplicitOverride;
718
+ if (
719
+ !userExplicitOverride &&
720
+ turnCallSite === "mainAgent" &&
721
+ isAssistantFeatureFlagEnabled("query-complexity-routing", config)
722
+ ) {
723
+ const tier = await classifyQueryComplexity(content);
724
+ if (tier && tier !== "balanced") {
725
+ const routedProfile = complexityTierToProfileKey(tier);
726
+ if (config.llm.profiles?.[routedProfile]) {
727
+ turnOverrideProfile = routedProfile;
728
+ }
729
+ }
730
+ }
731
+
732
+ // Notify clients when the auto-router selected a non-default profile.
733
+ if (turnOverrideProfile && turnOverrideProfile !== userExplicitOverride) {
734
+ const profileEntry = config.llm.profiles?.[turnOverrideProfile];
735
+ const label = profileEntry?.label ?? turnOverrideProfile;
736
+ broadcastMessage({
737
+ type: "turn_profile_auto_routed",
738
+ conversationId: ctx.conversationId,
739
+ profile: turnOverrideProfile,
740
+ profileLabel: label,
741
+ });
742
+ }
743
+
744
+ // Only use the complexity-routed profile as a fallback — not the initial
745
+ // explicit override. If a mid-turn session expiry clears the conversation
746
+ // override, the old behavior (return undefined → revert to workspace
747
+ // defaults) must be preserved for non-routed turns.
748
+ const complexityRoutedProfile =
749
+ turnOverrideProfile !== userExplicitOverride
750
+ ? turnOverrideProfile
751
+ : undefined;
752
+ const readCurrentOverrideProfile = (): string | undefined =>
753
+ options?.overrideProfile ??
754
+ getConversationOverrideProfileFromRow(
755
+ getConversation(ctx.conversationId),
756
+ ) ??
757
+ complexityRoutedProfile;
758
+
699
759
  const effectiveContextWindow = resolveEffectiveContextWindow({
700
760
  llm: config.llm,
701
761
  callSite: turnCallSite,
702
762
  overrideProfile: turnOverrideProfile ?? undefined,
703
763
  });
704
- const turnContextWindowConfig = contextWindowConfigFromEffective(
764
+ let currentEffectiveContextWindow: EffectiveContextWindow =
765
+ effectiveContextWindow;
766
+ let currentContextWindowConfig = contextWindowConfigFromEffective(
705
767
  resolveCallSiteConfig(turnCallSite, config.llm, {
706
768
  overrideProfile: turnOverrideProfile ?? undefined,
707
769
  }).contextWindow,
708
- effectiveContextWindow,
770
+ currentEffectiveContextWindow,
709
771
  );
710
- (
772
+ const contextWindowManager =
711
773
  ctx.contextWindowManager as ContextWindowManager & {
712
774
  updateConfig?: (config: ContextWindowConfig) => void;
775
+ };
776
+ contextWindowManager.updateConfig?.(currentContextWindowConfig);
777
+
778
+ let appliedOverrideProfile = turnOverrideProfile;
779
+ const refreshCurrentProfileState = (): string | undefined => {
780
+ const currentOverrideProfile = readCurrentOverrideProfile();
781
+ if (currentOverrideProfile !== appliedOverrideProfile) {
782
+ currentEffectiveContextWindow = resolveEffectiveContextWindow({
783
+ llm: config.llm,
784
+ callSite: turnCallSite,
785
+ overrideProfile: currentOverrideProfile,
786
+ });
787
+ currentContextWindowConfig = contextWindowConfigFromEffective(
788
+ resolveCallSiteConfig(turnCallSite, config.llm, {
789
+ overrideProfile: currentOverrideProfile,
790
+ }).contextWindow,
791
+ currentEffectiveContextWindow,
792
+ );
793
+ contextWindowManager.updateConfig?.(currentContextWindowConfig);
794
+ appliedOverrideProfile = currentOverrideProfile;
795
+ rlog.info(
796
+ { overrideProfile: currentOverrideProfile ?? null },
797
+ "Turn inference profile changed mid-loop",
798
+ );
713
799
  }
714
- ).updateConfig?.(turnContextWindowConfig);
800
+ ctx.currentTurnOverrideProfile = currentOverrideProfile;
801
+ return currentOverrideProfile;
802
+ };
803
+ const resolveCurrentOverrideProfile = (): string | undefined =>
804
+ refreshCurrentProfileState();
805
+ const resolveCurrentMaxInputTokens = (): number => {
806
+ refreshCurrentProfileState();
807
+ return currentEffectiveContextWindow.maxInputTokens;
808
+ };
809
+ const resolveCurrentContextWindowConfig = (): ContextWindowConfig => {
810
+ refreshCurrentProfileState();
811
+ return currentContextWindowConfig;
812
+ };
813
+ const resolveCurrentContextBudget = (): {
814
+ overflowRecovery: EffectiveContextWindow["overflowRecovery"];
815
+ providerMaxTokens: number;
816
+ preflightBudget: number;
817
+ } => {
818
+ refreshCurrentProfileState();
819
+ const overflowRecovery = currentEffectiveContextWindow.overflowRecovery;
820
+ const providerMaxTokens = currentEffectiveContextWindow.maxInputTokens;
821
+ const baseSafetyMargin = overflowRecovery.safetyMarginRatio;
822
+ const messageCount = ctx.messages.length;
823
+ const safetyMargin =
824
+ messageCount > 50 ? Math.max(baseSafetyMargin, 0.15) : baseSafetyMargin;
825
+ return {
826
+ overflowRecovery,
827
+ providerMaxTokens,
828
+ preflightBudget: Math.floor(providerMaxTokens * (1 - safetyMargin)),
829
+ };
830
+ };
715
831
 
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).
832
+ // Initial value for `createToolExecutor` to read into
833
+ // `ToolContext.overrideProfile`. `resolveCurrentOverrideProfile` refreshes
834
+ // this between model calls so a confirmed profile session opened by a tool
835
+ // applies to later tool executions and nested subagents in the same turn.
719
836
  ctx.currentTurnOverrideProfile = turnOverrideProfile;
720
837
 
721
838
  // Capture the turn channel context *before* any awaits so a second
@@ -1029,6 +1146,7 @@ export async function runAgentLoopImpl(
1029
1146
  {
1030
1147
  message: result.messages[0]!,
1031
1148
  sourceChannelTs: null,
1149
+ tagLineProvenance: "none",
1032
1150
  },
1033
1151
  ...retainedRenderedMessages,
1034
1152
  ],
@@ -1088,7 +1206,7 @@ export async function runAgentLoopImpl(
1088
1206
  precomputedEstimate: compactCheck.estimatedTokens,
1089
1207
  conversationOriginChannel:
1090
1208
  getConversationOriginChannel(ctx.conversationId) ?? undefined,
1091
- overrideProfile: turnOverrideProfile ?? null,
1209
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
1092
1210
  };
1093
1211
  let compacted: Awaited<
1094
1212
  ReturnType<typeof ctx.contextWindowManager.maybeCompact>
@@ -1453,6 +1571,26 @@ export async function runAgentLoopImpl(
1453
1571
  }
1454
1572
  }
1455
1573
 
1574
+ // Resolve the effective profile key for this turn and detect changes.
1575
+ // Only inject model_profile into the turn context when the profile
1576
+ // changed since the last turn (or on the first turn of a conversation)
1577
+ // to avoid per-turn token cost.
1578
+ const effectiveProfileKey =
1579
+ turnOverrideProfile ??
1580
+ config.llm.activeProfile ??
1581
+ resolveDefaultProfileKey("mainAgent", config.llm);
1582
+ const lastNotified = turnStartConversation?.lastNotifiedInferenceProfile;
1583
+ let modelProfileStr: string | null = null;
1584
+ if (effectiveProfileKey != null && effectiveProfileKey !== lastNotified) {
1585
+ const profileEntry = config.llm.profiles?.[effectiveProfileKey];
1586
+ const resolved = resolveCallSiteConfig(turnCallSite, config.llm, {
1587
+ overrideProfile: turnOverrideProfile ?? undefined,
1588
+ });
1589
+ const label = profileEntry?.label ?? effectiveProfileKey;
1590
+ modelProfileStr = resolved.model ? `${label} (${resolved.model})` : label;
1591
+ setLastNotifiedInferenceProfile(ctx.conversationId, effectiveProfileKey);
1592
+ }
1593
+
1456
1594
  const baseTurnContext = {
1457
1595
  timestamp,
1458
1596
  interfaceName,
@@ -1461,6 +1599,7 @@ export async function runAgentLoopImpl(
1461
1599
  clientTimezone: timezoneContext.clientTimezone,
1462
1600
  detectedTimezone: timezoneContext.detectedTimezone,
1463
1601
  timeSinceLastMessage,
1602
+ modelProfile: modelProfileStr,
1464
1603
  };
1465
1604
  const unifiedTurnContextStr = buildUnifiedTurnContextBlock(
1466
1605
  isGuardian
@@ -1617,7 +1756,6 @@ export async function runAgentLoopImpl(
1617
1756
  nowScratchpad,
1618
1757
  voiceCallControlPrompt: ctx.voiceCallControlPrompt ?? null,
1619
1758
  transportHints: ctx.transportHints ?? null,
1620
- slackRuntimeContextNotice: ctx.slackRuntimeContextNotice ?? null,
1621
1759
  isNonInteractive: !isInteractiveResolved,
1622
1760
  isBackgroundConversation: isBackgroundConversationType(
1623
1761
  turnStartConversation?.conversationType,
@@ -1704,15 +1842,9 @@ export async function runAgentLoopImpl(
1704
1842
  // After runtime injections are applied, estimate the prompt token count
1705
1843
  // and proactively invoke the reducer if already above budget. This avoids
1706
1844
  // 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));
1845
+ const initialContextBudget = resolveCurrentContextBudget();
1846
+ const overflowRecovery = initialContextBudget.overflowRecovery;
1847
+ const preflightBudget = initialContextBudget.preflightBudget;
1716
1848
  let reducerState: ReducerState | undefined;
1717
1849
 
1718
1850
  const toolTokenBudget = ctx.agentLoop.getToolTokenBudget(runMessages);
@@ -1789,10 +1921,10 @@ export async function runAgentLoopImpl(
1789
1921
  runMessages,
1790
1922
  systemPrompt: ctx.systemPrompt,
1791
1923
  providerName: estimationProviderName,
1792
- contextWindow: turnContextWindowConfig,
1924
+ contextWindow: resolveCurrentContextWindowConfig(),
1793
1925
  preflightBudget,
1794
1926
  toolTokenBudget,
1795
- maxAttempts: overflowRecovery.maxAttempts,
1927
+ maxAttempts: resolveCurrentContextBudget().overflowRecovery.maxAttempts,
1796
1928
  abortSignal: abortController.signal,
1797
1929
  compactFn: async (msgs, signal, opts) => {
1798
1930
  // Route the reducer's forced-compaction tier through the
@@ -1822,7 +1954,7 @@ export async function runAgentLoopImpl(
1822
1954
  signal,
1823
1955
  options: {
1824
1956
  ...(opts ?? {}),
1825
- overrideProfile: turnOverrideProfile ?? null,
1957
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
1826
1958
  },
1827
1959
  },
1828
1960
  buildPluginTurnContext(ctx, reqId),
@@ -2076,8 +2208,9 @@ export async function runAgentLoopImpl(
2076
2208
  };
2077
2209
  const eventHandler = (event: AgentEvent) =>
2078
2210
  dispatchAgentEvent(state, deps, event);
2079
-
2080
- let yieldedForBudget = false;
2211
+ emitTerminalExit = async (reason: AgentLoopExitReason): Promise<void> => {
2212
+ await eventHandler({ type: "agent_loop_exit", reason });
2213
+ };
2081
2214
 
2082
2215
  const onCheckpoint = async (
2083
2216
  checkpoint: CheckpointInfo,
@@ -2086,6 +2219,7 @@ export async function runAgentLoopImpl(
2086
2219
 
2087
2220
  if (ctx.canHandoffAtCheckpoint()) {
2088
2221
  yieldedForHandoff = true;
2222
+ pendingCheckpointYield = "handoff";
2089
2223
  return "yield";
2090
2224
  }
2091
2225
 
@@ -2093,7 +2227,8 @@ export async function runAgentLoopImpl(
2093
2227
  // yield if we're approaching the preflight budget. This lets the
2094
2228
  // conversation-agent-loop run compaction before the provider rejects.
2095
2229
  if (overflowRecovery.enabled) {
2096
- const midLoopThreshold = preflightBudget * 0.85;
2230
+ const midLoopThreshold =
2231
+ resolveCurrentContextBudget().preflightBudget * 0.85;
2097
2232
  const estimated = await runTokenEstimatePipeline(checkpoint.history);
2098
2233
  if (estimated > midLoopThreshold) {
2099
2234
  rlog.warn(
@@ -2101,6 +2236,7 @@ export async function runAgentLoopImpl(
2101
2236
  "Token estimate approaching budget — yielding for compaction",
2102
2237
  );
2103
2238
  yieldedForBudget = true;
2239
+ pendingCheckpointYield = "budget";
2104
2240
  return "yield";
2105
2241
  }
2106
2242
  }
@@ -2129,7 +2265,9 @@ export async function runAgentLoopImpl(
2129
2265
  turnCallSite,
2130
2266
  loopTurnCtx,
2131
2267
  turnOverrideProfile,
2132
- effectiveContextWindow.maxInputTokens,
2268
+ resolveCurrentMaxInputTokens(),
2269
+ resolveCurrentOverrideProfile,
2270
+ resolveCurrentMaxInputTokens,
2133
2271
  );
2134
2272
 
2135
2273
  rlog.info(
@@ -2137,6 +2275,11 @@ export async function runAgentLoopImpl(
2137
2275
  "Agent loop run completed",
2138
2276
  );
2139
2277
 
2278
+ if (yieldedForHandoff) {
2279
+ await emitTerminalExit?.("checkpoint_handoff");
2280
+ pendingCheckpointYield = null;
2281
+ }
2282
+
2140
2283
  // ── Proactive mid-loop compaction ───────────────────────────────
2141
2284
  // When the agent loop yielded because the token budget check in
2142
2285
  // onCheckpoint detected approaching limits, run compaction on the
@@ -2146,12 +2289,14 @@ export async function runAgentLoopImpl(
2146
2289
  let midLoopCompactAttempts = 0;
2147
2290
  while (
2148
2291
  yieldedForBudget &&
2149
- midLoopCompactAttempts < overflowRecovery.maxAttempts &&
2292
+ midLoopCompactAttempts <
2293
+ resolveCurrentContextBudget().overflowRecovery.maxAttempts &&
2150
2294
  !state.contextTooLargeDetected &&
2151
2295
  !abortController.signal.aborted
2152
2296
  ) {
2153
2297
  midLoopCompactAttempts++;
2154
2298
  yieldedForBudget = false;
2299
+ pendingCheckpointYield = null;
2155
2300
 
2156
2301
  rlog.info(
2157
2302
  { phase: "mid-loop-compact" },
@@ -2193,10 +2338,11 @@ export async function runAgentLoopImpl(
2193
2338
  options: {
2194
2339
  lastCompactedAt: ctx.contextCompactedAt ?? undefined,
2195
2340
  force: true,
2196
- targetInputTokensOverride: preflightBudget,
2341
+ targetInputTokensOverride:
2342
+ resolveCurrentContextBudget().preflightBudget,
2197
2343
  conversationOriginChannel:
2198
2344
  getConversationOriginChannel(ctx.conversationId) ?? undefined,
2199
- overrideProfile: turnOverrideProfile ?? null,
2345
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2200
2346
  },
2201
2347
  },
2202
2348
  buildPluginTurnContext(ctx, reqId),
@@ -2282,7 +2428,9 @@ export async function runAgentLoopImpl(
2282
2428
  turnCallSite,
2283
2429
  loopTurnCtx,
2284
2430
  turnOverrideProfile,
2285
- effectiveContextWindow.maxInputTokens,
2431
+ resolveCurrentMaxInputTokens(),
2432
+ resolveCurrentOverrideProfile,
2433
+ resolveCurrentMaxInputTokens,
2286
2434
  );
2287
2435
  }
2288
2436
 
@@ -2296,7 +2444,8 @@ export async function runAgentLoopImpl(
2296
2444
  {
2297
2445
  phase: "mid-loop-compact",
2298
2446
  midLoopCompactAttempts,
2299
- maxAttempts: overflowRecovery.maxAttempts,
2447
+ maxAttempts:
2448
+ resolveCurrentContextBudget().overflowRecovery.maxAttempts,
2300
2449
  },
2301
2450
  "Mid-loop compaction exhausted all attempts — escalating to convergence loop",
2302
2451
  );
@@ -2339,7 +2488,9 @@ export async function runAgentLoopImpl(
2339
2488
  turnCallSite,
2340
2489
  loopTurnCtx,
2341
2490
  turnOverrideProfile,
2342
- effectiveContextWindow.maxInputTokens,
2491
+ resolveCurrentMaxInputTokens(),
2492
+ resolveCurrentOverrideProfile,
2493
+ resolveCurrentMaxInputTokens,
2343
2494
  );
2344
2495
 
2345
2496
  if (state.orderingErrorDetected) {
@@ -2408,7 +2559,9 @@ export async function runAgentLoopImpl(
2408
2559
  turnCallSite,
2409
2560
  loopTurnCtx,
2410
2561
  turnOverrideProfile,
2411
- effectiveContextWindow.maxInputTokens,
2562
+ resolveCurrentMaxInputTokens(),
2563
+ resolveCurrentOverrideProfile,
2564
+ resolveCurrentMaxInputTokens,
2412
2565
  );
2413
2566
  if (state.imageTooLargeDetected) {
2414
2567
  rlog.error(
@@ -2479,18 +2632,21 @@ export async function runAgentLoopImpl(
2479
2632
  toolTokenBudget,
2480
2633
  },
2481
2634
  );
2482
- let correctedTarget = preflightBudget;
2635
+ const convergenceBudget = resolveCurrentContextBudget();
2636
+ let correctedTarget = convergenceBudget.preflightBudget;
2483
2637
  if (actualTokens && estimatedTokensAtOverflow > 0) {
2484
2638
  const estimationErrorRatio = actualTokens / estimatedTokensAtOverflow;
2485
2639
  if (estimationErrorRatio > 1.0) {
2486
- correctedTarget = Math.floor(preflightBudget / estimationErrorRatio);
2640
+ correctedTarget = Math.floor(
2641
+ convergenceBudget.preflightBudget / estimationErrorRatio,
2642
+ );
2487
2643
  rlog.warn(
2488
2644
  {
2489
2645
  phase: "convergence",
2490
2646
  actualTokens,
2491
2647
  estimatedTokens: estimatedTokensAtOverflow,
2492
2648
  estimationErrorRatio: estimationErrorRatio.toFixed(2),
2493
- preflightBudget,
2649
+ preflightBudget: convergenceBudget.preflightBudget,
2494
2650
  correctedTarget,
2495
2651
  },
2496
2652
  "Adjusting compaction target based on observed estimation error",
@@ -2515,11 +2671,11 @@ export async function runAgentLoopImpl(
2515
2671
  systemPrompt: ctx.systemPrompt,
2516
2672
  tools: undefined,
2517
2673
  compaction: emergencyConfig,
2518
- maxInputTokens: effectiveContextWindow.maxInputTokens,
2674
+ maxInputTokens: resolveCurrentMaxInputTokens(),
2519
2675
  previousEstimatedInputTokens: estimatedTokensAtOverflow,
2520
2676
  force: true,
2521
2677
  signal: abortController.signal,
2522
- overrideProfile: turnOverrideProfile ?? null,
2678
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2523
2679
  nonPersistedPrefixCount:
2524
2680
  ctx.contextWindowManager.nonPersistedPrefixCount,
2525
2681
  });
@@ -2557,7 +2713,7 @@ export async function runAgentLoopImpl(
2557
2713
  }
2558
2714
 
2559
2715
  let convergenceAttempts = 0;
2560
- const maxAttempts = overflowRecovery.maxAttempts;
2716
+ const maxAttempts = convergenceBudget.overflowRecovery.maxAttempts;
2561
2717
 
2562
2718
  while (
2563
2719
  state.contextTooLargeDetected &&
@@ -2586,7 +2742,7 @@ export async function runAgentLoopImpl(
2586
2742
  {
2587
2743
  providerName: estimationProviderName,
2588
2744
  systemPrompt: ctx.systemPrompt,
2589
- contextWindow: turnContextWindowConfig,
2745
+ contextWindow: resolveCurrentContextWindowConfig(),
2590
2746
  targetTokens: correctedTarget,
2591
2747
  toolTokenBudget,
2592
2748
  },
@@ -2594,7 +2750,7 @@ export async function runAgentLoopImpl(
2594
2750
  (msgs, signal, opts) =>
2595
2751
  ctx.contextWindowManager.maybeCompact(msgs, signal!, {
2596
2752
  ...(opts ?? {}),
2597
- overrideProfile: turnOverrideProfile ?? null,
2753
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2598
2754
  }),
2599
2755
  abortController.signal,
2600
2756
  );
@@ -2670,7 +2826,9 @@ export async function runAgentLoopImpl(
2670
2826
  turnCallSite,
2671
2827
  loopTurnCtx,
2672
2828
  turnOverrideProfile,
2673
- effectiveContextWindow.maxInputTokens,
2829
+ resolveCurrentMaxInputTokens(),
2830
+ resolveCurrentOverrideProfile,
2831
+ resolveCurrentMaxInputTokens,
2674
2832
  );
2675
2833
 
2676
2834
  // If the rerun still yields at checkpoint, the turn is still
@@ -2748,7 +2906,7 @@ export async function runAgentLoopImpl(
2748
2906
  force: true,
2749
2907
  minKeepRecentUserTurns: 0,
2750
2908
  targetInputTokensOverride: correctedTarget,
2751
- overrideProfile: turnOverrideProfile ?? null,
2909
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
2752
2910
  },
2753
2911
  },
2754
2912
  buildPluginTurnContext(ctx, reqId),
@@ -2831,7 +2989,9 @@ export async function runAgentLoopImpl(
2831
2989
  turnCallSite,
2832
2990
  loopTurnCtx,
2833
2991
  turnOverrideProfile,
2834
- effectiveContextWindow.maxInputTokens,
2992
+ resolveCurrentMaxInputTokens(),
2993
+ resolveCurrentOverrideProfile,
2994
+ resolveCurrentMaxInputTokens,
2835
2995
  );
2836
2996
  }
2837
2997
  // action === "fail_gracefully" falls through to the final error below
@@ -2843,6 +3003,8 @@ export async function runAgentLoopImpl(
2843
3003
  new Error("context_length_exceeded"),
2844
3004
  { phase: "agent_loop" },
2845
3005
  );
3006
+ await emitTerminalExit?.("context_too_large");
3007
+ pendingCheckpointYield = null;
2846
3008
  onEvent(buildConversationErrorMessage(ctx.conversationId, classified));
2847
3009
  }
2848
3010
  }
@@ -3041,11 +3203,11 @@ export async function runAgentLoopImpl(
3041
3203
  state.exchangeLlmCallCount,
3042
3204
  {
3043
3205
  tokens: state.lastCallInputTokens,
3044
- maxTokens: effectiveContextWindow.maxInputTokens,
3206
+ maxTokens: resolveCurrentMaxInputTokens(),
3045
3207
  },
3046
3208
  {
3047
3209
  callSite: turnCallSite,
3048
- overrideProfile: turnOverrideProfile ?? null,
3210
+ overrideProfile: resolveCurrentOverrideProfile() ?? null,
3049
3211
  },
3050
3212
  );
3051
3213
 
@@ -3105,6 +3267,10 @@ export async function runAgentLoopImpl(
3105
3267
 
3106
3268
  // Re-check: the user may have cancelled during attachment resolution
3107
3269
  if (abortController.signal.aborted) {
3270
+ if (pendingCheckpointYield === "budget") {
3271
+ await emitTerminalExit?.("aborted_after_checkpoint");
3272
+ pendingCheckpointYield = null;
3273
+ }
3108
3274
  ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3109
3275
  ctx.traceEmitter.emit(
3110
3276
  "generation_cancelled",
@@ -3246,6 +3412,10 @@ export async function runAgentLoopImpl(
3246
3412
  aborted: abortController.signal.aborted,
3247
3413
  };
3248
3414
  if (isUserCancellation(err, errorCtx)) {
3415
+ if (pendingCheckpointYield === "budget") {
3416
+ await emitTerminalExit?.("aborted_after_checkpoint");
3417
+ pendingCheckpointYield = null;
3418
+ }
3249
3419
  ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
3250
3420
  rlog.info("Generation cancelled by user");
3251
3421
  ctx.traceEmitter.emit(
@@ -3338,7 +3508,6 @@ export async function runAgentLoopImpl(
3338
3508
  ctx.diskPressureCleanupModeActive = false;
3339
3509
  ctx.preactivatedSkillIds = undefined;
3340
3510
  ctx.currentTurnOverrideProfile = undefined;
3341
- ctx.slackRuntimeContextNotice = undefined;
3342
3511
  // Channel command intents (e.g. Telegram /start) are single-turn metadata.
3343
3512
  // Clear at turn end so they never leak into subsequent unrelated messages.
3344
3513
  ctx.commandIntent = undefined;