@vellumai/assistant 0.6.4 → 0.6.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 (717) hide show
  1. package/.prettierignore +5 -0
  2. package/ARCHITECTURE.md +32 -36
  3. package/Dockerfile +12 -0
  4. package/README.md +3 -4
  5. package/bun.lock +8 -3
  6. package/docs/architecture/integrations.md +1 -20
  7. package/docs/architecture/security.md +16 -16
  8. package/docs/error-handling.md +111 -0
  9. package/docs/skills.md +10 -10
  10. package/docs/stt-provider-onboarding.md +2 -1
  11. package/knip.json +9 -2
  12. package/node_modules/@vellumai/ces-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  14. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  15. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  16. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  17. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  18. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  19. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  20. package/openapi.yaml +123 -11
  21. package/package.json +6 -3
  22. package/scripts/generate-openapi.ts +50 -11
  23. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  24. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  25. package/src/__tests__/agent-loop.test.ts +112 -1
  26. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  27. package/src/__tests__/anthropic-provider.test.ts +171 -2
  28. package/src/__tests__/approval-cascade.test.ts +31 -10
  29. package/src/__tests__/approval-routes-http.test.ts +134 -10
  30. package/src/__tests__/assistant-attachments.test.ts +44 -0
  31. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  32. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  33. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  34. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  35. package/src/__tests__/browser-skill-endstate.test.ts +51 -182
  36. package/src/__tests__/btw-routes.test.ts +47 -1
  37. package/src/__tests__/call-controller.test.ts +1 -2
  38. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  39. package/src/__tests__/catalog-cache.test.ts +27 -4
  40. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  41. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  42. package/src/__tests__/checker.test.ts +428 -501
  43. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  44. package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
  45. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  46. package/src/__tests__/config-analysis.test.ts +11 -28
  47. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  48. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  49. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  50. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  51. package/src/__tests__/config-schema.test.ts +427 -114
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/contact-store-user-file.test.ts +72 -73
  54. package/src/__tests__/contacts-write.test.ts +4 -4
  55. package/src/__tests__/context-token-estimator.test.ts +191 -1
  56. package/src/__tests__/context-window-manager.test.ts +530 -2
  57. package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
  58. package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
  59. package/src/__tests__/conversation-agent-loop.test.ts +412 -82
  60. package/src/__tests__/conversation-attachments.test.ts +1 -1
  61. package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
  62. package/src/__tests__/conversation-error.test.ts +37 -6
  63. package/src/__tests__/conversation-history-web-search.test.ts +6 -0
  64. package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
  65. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  66. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  67. package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
  68. package/src/__tests__/conversation-process-callsite.test.ts +306 -0
  69. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
  70. package/src/__tests__/conversation-queue.test.ts +41 -26
  71. package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
  75. package/src/__tests__/conversation-skill-tools.test.ts +12 -146
  76. package/src/__tests__/conversation-slash-queue.test.ts +34 -19
  77. package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
  78. package/src/__tests__/conversation-speed-override.test.ts +30 -11
  79. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  80. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  81. package/src/__tests__/conversation-title-service.test.ts +2 -2
  82. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
  83. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  84. package/src/__tests__/conversation-usage.test.ts +3 -1
  85. package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
  86. package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
  87. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
  88. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  89. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  90. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  91. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  92. package/src/__tests__/credential-vault-unit.test.ts +135 -19
  93. package/src/__tests__/credentials-cli.test.ts +1 -9
  94. package/src/__tests__/cross-provider-web-search.test.ts +84 -0
  95. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  96. package/src/__tests__/delete-propagation.test.ts +437 -0
  97. package/src/__tests__/dm-backfill.test.ts +417 -0
  98. package/src/__tests__/dm-persistence.test.ts +227 -0
  99. package/src/__tests__/edit-propagation.test.ts +280 -0
  100. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  101. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  102. package/src/__tests__/estimator-calibration.test.ts +213 -0
  103. package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
  104. package/src/__tests__/file-write-tool.test.ts +151 -1
  105. package/src/__tests__/filing-service.test.ts +255 -0
  106. package/src/__tests__/gemini-provider.test.ts +0 -3
  107. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  108. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  109. package/src/__tests__/heartbeat-service.test.ts +96 -15
  110. package/src/__tests__/host-shell-tool.test.ts +124 -18
  111. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  112. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  113. package/src/__tests__/intent-routing.test.ts +1 -40
  114. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  115. package/src/__tests__/llm-context-normalization.test.ts +121 -0
  116. package/src/__tests__/llm-resolver.test.ts +214 -0
  117. package/src/__tests__/llm-schema.test.ts +223 -0
  118. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  119. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  120. package/src/__tests__/migration-import-from-url.test.ts +684 -0
  121. package/src/__tests__/model-intents.test.ts +9 -83
  122. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  123. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  124. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  125. package/src/__tests__/oauth-store.test.ts +10 -7
  126. package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
  127. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  128. package/src/__tests__/openai-provider.test.ts +7 -0
  129. package/src/__tests__/openai-responses-provider.test.ts +396 -0
  130. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  131. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  132. package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
  133. package/src/__tests__/permission-mode.test.ts +16 -0
  134. package/src/__tests__/permission-types.test.ts +0 -1
  135. package/src/__tests__/persona-resolver.test.ts +13 -13
  136. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  137. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  138. package/src/__tests__/pricing.test.ts +50 -3
  139. package/src/__tests__/profiler-routes.test.ts +1 -1
  140. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  141. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  143. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  144. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  145. package/src/__tests__/proxy-approval-callback.test.ts +0 -1
  146. package/src/__tests__/reaction-persistence.test.ts +560 -0
  147. package/src/__tests__/relay-server.test.ts +1 -1
  148. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  149. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  150. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  151. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  152. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  153. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  154. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  156. package/src/__tests__/secret-scanner-executor.test.ts +1 -1
  157. package/src/__tests__/send-endpoint-busy.test.ts +29 -1
  158. package/src/__tests__/server-history-render.test.ts +31 -0
  159. package/src/__tests__/shell-parser-property.test.ts +13 -13
  160. package/src/__tests__/skill-cache-store.test.ts +182 -0
  161. package/src/__tests__/skills.test.ts +19 -33
  162. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  163. package/src/__tests__/slack-skill.test.ts +3 -8
  164. package/src/__tests__/starter-bundle.test.ts +35 -0
  165. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  166. package/src/__tests__/suggestion-routes.test.ts +160 -3
  167. package/src/__tests__/system-prompt.test.ts +22 -35
  168. package/src/__tests__/task-runner.test.ts +3 -1
  169. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  170. package/src/__tests__/terminal-tools.test.ts +8 -0
  171. package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
  172. package/src/__tests__/thread-backfill.test.ts +941 -0
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  175. package/src/__tests__/tool-executor.test.ts +60 -94
  176. package/src/__tests__/trust-store.test.ts +442 -109
  177. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  178. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  179. package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
  180. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  181. package/src/__tests__/volume-security-guard.test.ts +3 -2
  182. package/src/__tests__/web-search-history.test.ts +337 -0
  183. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  184. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  185. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  186. package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
  187. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  188. package/src/__tests__/workspace-policy.test.ts +1 -13
  189. package/src/acp/client-handler.ts +1 -2
  190. package/src/agent/loop.ts +209 -17
  191. package/src/avatar/resvg-lazy.test.ts +136 -0
  192. package/src/avatar/resvg-lazy.ts +82 -9
  193. package/src/avatar/traits-png-sync.ts +21 -1
  194. package/src/browser/__tests__/operations.test.ts +163 -0
  195. package/src/browser/identifiers.ts +51 -0
  196. package/src/browser/operations.ts +660 -0
  197. package/src/browser/types.ts +81 -0
  198. package/src/calls/guardian-question-copy.ts +2 -2
  199. package/src/calls/telephony-stt-routing.ts +1 -1
  200. package/src/calls/voice-session-bridge.ts +1 -0
  201. package/src/cli/AGENTS.md +1 -1
  202. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  203. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  204. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  205. package/src/cli/commands/__tests__/email-list.test.ts +6 -0
  206. package/src/cli/commands/__tests__/email-send.test.ts +93 -1
  207. package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
  208. package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
  209. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  210. package/src/cli/commands/__tests__/task.test.ts +913 -0
  211. package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
  212. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  213. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  214. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  215. package/src/cli/commands/attachment.ts +182 -0
  216. package/src/cli/commands/browser.ts +350 -0
  217. package/src/cli/commands/cache.ts +341 -0
  218. package/src/cli/commands/completions.ts +0 -3
  219. package/src/cli/commands/config.ts +6 -6
  220. package/src/cli/commands/conversations-import.ts +347 -0
  221. package/src/cli/commands/conversations.ts +14 -1
  222. package/src/cli/commands/email.ts +234 -194
  223. package/src/cli/commands/image-generation.ts +300 -0
  224. package/src/cli/commands/inference.ts +200 -0
  225. package/src/cli/commands/memory.ts +127 -17
  226. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  227. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
  228. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
  229. package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
  230. package/src/cli/commands/stt.ts +339 -0
  231. package/src/cli/commands/task.ts +795 -0
  232. package/src/cli/commands/trust.ts +50 -19
  233. package/src/cli/commands/tts.ts +273 -0
  234. package/src/cli/commands/ui.ts +670 -0
  235. package/src/cli/commands/watchers.ts +509 -0
  236. package/src/cli/lib/daemon-credential-client.ts +0 -19
  237. package/src/cli/program.ts +23 -4
  238. package/src/cli.ts +0 -37
  239. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
  240. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  241. package/src/config/bundled-skills/messaging/SKILL.md +2 -2
  242. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  243. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
  244. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  245. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  246. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
  247. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
  248. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  249. package/src/config/bundled-tool-registry.ts +0 -175
  250. package/src/config/env.ts +7 -2
  251. package/src/config/feature-flag-registry.json +25 -9
  252. package/src/config/llm-resolver.ts +128 -0
  253. package/src/config/loader.ts +194 -10
  254. package/src/config/raw-config-utils.ts +30 -2
  255. package/src/config/sanitize-for-transfer.ts +35 -0
  256. package/src/config/schema.ts +30 -41
  257. package/src/config/schemas/analysis.ts +3 -22
  258. package/src/config/schemas/calls.ts +0 -4
  259. package/src/config/schemas/filing.ts +2 -7
  260. package/src/config/schemas/heartbeat.ts +0 -5
  261. package/src/config/schemas/inference.ts +3 -23
  262. package/src/config/schemas/llm.ts +318 -0
  263. package/src/config/schemas/memory-processing.ts +1 -9
  264. package/src/config/schemas/notifications.ts +4 -11
  265. package/src/config/schemas/platform.ts +3 -9
  266. package/src/config/schemas/security.ts +33 -0
  267. package/src/config/schemas/services.ts +9 -4
  268. package/src/config/schemas/stt.ts +1 -0
  269. package/src/config/schemas/tts.ts +53 -0
  270. package/src/config/schemas/updates.ts +1 -1
  271. package/src/config/schemas/workspace-git.ts +3 -40
  272. package/src/config/skills.ts +2 -2
  273. package/src/context/__tests__/compact-prompt.test.ts +45 -0
  274. package/src/context/__tests__/microcompact.test.ts +805 -0
  275. package/src/context/estimator-calibration.ts +136 -0
  276. package/src/context/microcompact.ts +443 -0
  277. package/src/context/prompts/compact.md +12 -0
  278. package/src/context/token-estimator.ts +61 -3
  279. package/src/context/window-manager.ts +229 -25
  280. package/src/credential-execution/approval-bridge.ts +0 -1
  281. package/src/credential-execution/executable-discovery.ts +19 -8
  282. package/src/credential-execution/process-manager.test.ts +109 -0
  283. package/src/credential-execution/process-manager.ts +65 -2
  284. package/src/daemon/approval-generators.ts +29 -4
  285. package/src/daemon/assistant-attachments.ts +24 -13
  286. package/src/daemon/classifier.ts +2 -2
  287. package/src/daemon/config-watcher.ts +0 -1
  288. package/src/daemon/context-overflow-reducer.ts +4 -1
  289. package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
  290. package/src/daemon/conversation-agent-loop.ts +462 -80
  291. package/src/daemon/conversation-attachments.ts +2 -6
  292. package/src/daemon/conversation-error.ts +36 -1
  293. package/src/daemon/conversation-lifecycle.ts +30 -6
  294. package/src/daemon/conversation-messaging.ts +73 -4
  295. package/src/daemon/conversation-process.ts +10 -4
  296. package/src/daemon/conversation-queue-manager.ts +3 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +760 -29
  298. package/src/daemon/conversation-slash.ts +2 -2
  299. package/src/daemon/conversation-surfaces.ts +389 -1
  300. package/src/daemon/conversation-tool-setup.ts +10 -5
  301. package/src/daemon/conversation-usage.ts +1 -1
  302. package/src/daemon/conversation.ts +118 -30
  303. package/src/daemon/external-skills-bootstrap.ts +41 -0
  304. package/src/daemon/guardian-action-generators.ts +34 -14
  305. package/src/daemon/handlers/config-model.test.ts +86 -0
  306. package/src/daemon/handlers/config-model.ts +54 -12
  307. package/src/daemon/handlers/conversations.ts +9 -2
  308. package/src/daemon/handlers/shared.ts +39 -11
  309. package/src/daemon/handlers/skills.ts +2 -2
  310. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  311. package/src/daemon/lifecycle.ts +76 -14
  312. package/src/daemon/message-types/conversations.ts +14 -0
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/trust.ts +0 -2
  315. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  316. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  317. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  318. package/src/daemon/pkb-context-tracker.ts +125 -0
  319. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  320. package/src/daemon/pkb-reminder-builder.ts +31 -0
  321. package/src/daemon/providers-setup.ts +6 -0
  322. package/src/daemon/server.ts +117 -9
  323. package/src/daemon/tool-side-effects.ts +0 -9
  324. package/src/daemon/watch-handler.ts +4 -4
  325. package/src/daemon/web-search-history.ts +126 -0
  326. package/src/events/domain-events.ts +0 -1
  327. package/src/filing/filing-service.ts +9 -10
  328. package/src/heartbeat/heartbeat-service.ts +76 -28
  329. package/src/home/__tests__/feed-scheduler.test.ts +39 -11
  330. package/src/home/__tests__/rollup-producer.test.ts +44 -0
  331. package/src/home/assistant-feed-authoring.ts +4 -0
  332. package/src/home/emit-feed-event.ts +4 -0
  333. package/src/home/feed-scheduler.ts +20 -4
  334. package/src/home/feed-types.ts +56 -2
  335. package/src/home/relationship-state-writer.ts +2 -2
  336. package/src/home/rollup-producer.ts +34 -5
  337. package/src/home/suggested-prompts.ts +101 -0
  338. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  339. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  340. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  341. package/src/ipc/__tests__/socket-path.test.ts +73 -0
  342. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  343. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  344. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  345. package/src/ipc/cli-client.ts +2 -1
  346. package/src/ipc/cli-server.ts +26 -8
  347. package/src/ipc/gateway-client.ts +4 -4
  348. package/src/ipc/routes/attachment.ts +114 -0
  349. package/src/ipc/routes/browser-context.ts +61 -0
  350. package/src/ipc/routes/browser.ts +96 -0
  351. package/src/ipc/routes/cache.ts +96 -0
  352. package/src/ipc/routes/index.ts +17 -1
  353. package/src/ipc/routes/task-queue.ts +226 -0
  354. package/src/ipc/routes/task.ts +173 -0
  355. package/src/ipc/routes/ui-request.ts +50 -0
  356. package/src/ipc/routes/watcher.ts +203 -0
  357. package/src/ipc/socket-path.ts +100 -0
  358. package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
  359. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  360. package/src/memory/admin.ts +18 -0
  361. package/src/memory/conversation-analyze-job.ts +14 -13
  362. package/src/memory/conversation-attention-store.ts +13 -6
  363. package/src/memory/conversation-crud.ts +103 -3
  364. package/src/memory/conversation-group-migration.ts +38 -6
  365. package/src/memory/conversation-title-service.ts +7 -4
  366. package/src/memory/db-init.ts +2 -0
  367. package/src/memory/embedding-backend.ts +1 -1
  368. package/src/memory/graph/compaction.ts +299 -0
  369. package/src/memory/graph/consolidation.ts +4 -4
  370. package/src/memory/graph/conversation-graph-memory.ts +89 -29
  371. package/src/memory/graph/extraction.test.ts +272 -2
  372. package/src/memory/graph/extraction.ts +173 -51
  373. package/src/memory/graph/graph-search.test.ts +92 -0
  374. package/src/memory/graph/graph-search.ts +4 -1
  375. package/src/memory/graph/narrative.ts +2 -2
  376. package/src/memory/graph/pattern-scan.ts +2 -2
  377. package/src/memory/graph/retriever.test.ts +459 -0
  378. package/src/memory/graph/retriever.ts +230 -48
  379. package/src/memory/graph/store.ts +41 -0
  380. package/src/memory/graph/tool-handlers.ts +27 -0
  381. package/src/memory/graph/tools.ts +6 -1
  382. package/src/memory/indexer.ts +5 -5
  383. package/src/memory/job-handlers/conversation-starters.ts +23 -20
  384. package/src/memory/job-handlers/summarization.ts +2 -2
  385. package/src/memory/job-utils.ts +7 -1
  386. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  387. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  388. package/src/memory/jobs-store.ts +44 -3
  389. package/src/memory/jobs-worker.ts +4 -0
  390. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  391. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
  392. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  393. package/src/memory/migrations/index.ts +1 -0
  394. package/src/memory/pkb/pkb-index.test.ts +368 -0
  395. package/src/memory/pkb/pkb-index.ts +255 -0
  396. package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
  397. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  398. package/src/memory/pkb/pkb-search.test.ts +438 -0
  399. package/src/memory/pkb/pkb-search.ts +137 -0
  400. package/src/memory/pkb/types.ts +53 -0
  401. package/src/memory/qdrant-client.ts +122 -1
  402. package/src/memory/slack-thread-store.ts +37 -0
  403. package/src/messaging/providers/gmail/adapter.ts +6 -16
  404. package/src/messaging/providers/gmail/client.ts +22 -0
  405. package/src/messaging/providers/gmail/types.ts +7 -0
  406. package/src/messaging/providers/slack/adapter.ts +14 -2
  407. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  408. package/src/messaging/providers/slack/backfill.ts +101 -0
  409. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  410. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  411. package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
  412. package/src/messaging/providers/slack/render-transcript.ts +443 -0
  413. package/src/messaging/style-analyzer.ts +5 -2
  414. package/src/notifications/README.md +9 -5
  415. package/src/notifications/decision-engine.ts +3 -9
  416. package/src/notifications/preference-extractor.ts +2 -6
  417. package/src/oauth/oauth-store.ts +1 -0
  418. package/src/oauth/platform-connection.test.ts +47 -0
  419. package/src/oauth/platform-connection.ts +15 -5
  420. package/src/oauth/seed-providers.ts +4 -2
  421. package/src/permissions/approval-policy.test.ts +948 -0
  422. package/src/permissions/approval-policy.ts +257 -0
  423. package/src/permissions/bash-risk-classifier.test.ts +1208 -0
  424. package/src/permissions/bash-risk-classifier.ts +707 -0
  425. package/src/permissions/checker.ts +217 -708
  426. package/src/permissions/command-registry.test.ts +535 -0
  427. package/src/permissions/command-registry.ts +825 -0
  428. package/src/permissions/defaults.ts +26 -78
  429. package/src/permissions/file-risk-classifier.test.ts +535 -0
  430. package/src/permissions/file-risk-classifier.ts +274 -0
  431. package/src/permissions/risk-types.ts +205 -0
  432. package/src/permissions/secret-prompter.ts +53 -2
  433. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  434. package/src/permissions/skill-risk-classifier.ts +214 -0
  435. package/src/permissions/trust-client.ts +52 -25
  436. package/src/permissions/trust-store-interface.ts +1 -6
  437. package/src/permissions/trust-store.ts +161 -62
  438. package/src/permissions/types.ts +23 -14
  439. package/src/permissions/web-risk-classifier.test.ts +170 -0
  440. package/src/permissions/web-risk-classifier.ts +89 -0
  441. package/src/permissions/workspace-policy.ts +1 -16
  442. package/src/platform/client.ts +19 -1
  443. package/src/prompts/persona-resolver.ts +3 -3
  444. package/src/prompts/system-prompt.ts +19 -20
  445. package/src/prompts/templates/SOUL.md +2 -2
  446. package/src/prompts/update-bulletin-job.ts +190 -0
  447. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  448. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  449. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  450. package/src/providers/anthropic/client.ts +183 -14
  451. package/src/providers/call-site-routing.ts +71 -0
  452. package/src/providers/gemini/client.ts +65 -2
  453. package/src/providers/managed-proxy/constants.ts +2 -1
  454. package/src/providers/model-catalog.ts +501 -33
  455. package/src/providers/model-intents.ts +4 -4
  456. package/src/providers/openai/chat-completions-provider.ts +57 -1
  457. package/src/providers/openai/responses-provider.ts +86 -9
  458. package/src/providers/openrouter/client.ts +76 -9
  459. package/src/providers/provider-env-vars.ts +56 -0
  460. package/src/providers/provider-send-message.ts +22 -5
  461. package/src/providers/ratelimit.ts +4 -0
  462. package/src/providers/registry.ts +19 -8
  463. package/src/providers/retry.ts +174 -39
  464. package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
  465. package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
  466. package/src/providers/speech-to-text/provider-catalog.ts +17 -0
  467. package/src/providers/speech-to-text/resolve.ts +7 -0
  468. package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
  469. package/src/providers/speech-to-text/xai-realtime.ts +796 -0
  470. package/src/providers/speech-to-text/xai.test.ts +155 -0
  471. package/src/providers/speech-to-text/xai.ts +97 -0
  472. package/src/providers/types.ts +93 -3
  473. package/src/runtime/AGENTS.md +2 -2
  474. package/src/runtime/__tests__/agent-wake.test.ts +43 -2
  475. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  476. package/src/runtime/agent-wake.ts +63 -22
  477. package/src/runtime/auth/route-policy.ts +4 -0
  478. package/src/runtime/btw-sidechain.ts +13 -3
  479. package/src/runtime/channel-reply-delivery.ts +106 -2
  480. package/src/runtime/decision-token.ts +116 -0
  481. package/src/runtime/gateway-client.ts +2 -2
  482. package/src/runtime/http-router.ts +32 -0
  483. package/src/runtime/http-server.ts +52 -1
  484. package/src/runtime/http-types.ts +23 -1
  485. package/src/runtime/interactive-ui.ts +362 -0
  486. package/src/runtime/invite-instruction-generator.ts +2 -2
  487. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  488. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  489. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  490. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  491. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  492. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  493. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  494. package/src/runtime/migrations/vbundle-importer.ts +154 -9
  495. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  496. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  497. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  498. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  499. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  500. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
  501. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
  502. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  503. package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
  504. package/src/runtime/routes/approval-routes.ts +12 -17
  505. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  506. package/src/runtime/routes/avatar-routes.ts +20 -4
  507. package/src/runtime/routes/btw-routes.ts +1 -4
  508. package/src/runtime/routes/conversation-management-routes.ts +20 -2
  509. package/src/runtime/routes/conversation-routes.ts +133 -27
  510. package/src/runtime/routes/debug-routes.ts +1 -1
  511. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  512. package/src/runtime/routes/events-routes.ts +16 -0
  513. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  514. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  515. package/src/runtime/routes/home-feed-routes.ts +120 -2
  516. package/src/runtime/routes/inbound-message-handler.ts +912 -2
  517. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  518. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  519. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  520. package/src/runtime/routes/integrations/slack/channel.ts +25 -3
  521. package/src/runtime/routes/llm-context-normalization.ts +23 -1
  522. package/src/runtime/routes/migration-routes.ts +720 -124
  523. package/src/runtime/routes/settings-routes.ts +4 -2
  524. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  525. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  526. package/src/runtime/routes/work-items-routes.ts +3 -2
  527. package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
  528. package/src/runtime/services/analyze-conversation.ts +12 -16
  529. package/src/runtime/skill-route-registry.ts +28 -6
  530. package/src/schedule/scheduler.ts +8 -0
  531. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  532. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  533. package/src/security/oauth2.ts +98 -35
  534. package/src/security/secure-keys.ts +7 -8
  535. package/src/security/token-manager.ts +27 -13
  536. package/src/security/untrusted-content.ts +102 -0
  537. package/src/skills/catalog-cache.ts +26 -7
  538. package/src/skills/catalog-install.ts +31 -3
  539. package/src/skills/skill-cache-store.ts +97 -0
  540. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
  541. package/src/stt/daemon-batch-transcriber.ts +33 -0
  542. package/src/stt/stt-stream-session.ts +8 -1
  543. package/src/stt/types.ts +5 -1
  544. package/src/subagent/manager.ts +41 -13
  545. package/src/tasks/ephemeral-permissions.ts +9 -4
  546. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  547. package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
  548. package/src/tools/browser/browser-execution.ts +65 -38
  549. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
  550. package/src/tools/credentials/tool-policy.ts +39 -5
  551. package/src/tools/credentials/vault.ts +9 -4
  552. package/src/tools/executor.ts +4 -0
  553. package/src/tools/filesystem/write.ts +52 -0
  554. package/src/tools/host-terminal/host-shell.ts +45 -5
  555. package/src/tools/memory/register.test.ts +185 -0
  556. package/src/tools/memory/register.ts +3 -1
  557. package/src/tools/network/web-fetch.ts +20 -10
  558. package/src/tools/network/web-search.ts +19 -4
  559. package/src/tools/permission-checker.ts +36 -15
  560. package/src/tools/policy-context.ts +25 -8
  561. package/src/tools/registry.ts +55 -3
  562. package/src/tools/side-effects.ts +0 -11
  563. package/src/tools/skills/execute.ts +2 -2
  564. package/src/tools/skills/sandbox-runner.ts +5 -2
  565. package/src/tools/terminal/backends/native.ts +51 -2
  566. package/src/tools/terminal/safe-env.ts +3 -2
  567. package/src/tools/terminal/shell.ts +1 -0
  568. package/src/tools/tool-manifest.ts +6 -21
  569. package/src/tools/types.ts +12 -3
  570. package/src/tools/verification-control-plane-policy.ts +1 -1
  571. package/src/tts/__tests__/provider-adapters.test.ts +240 -13
  572. package/src/tts/provider-catalog.ts +18 -0
  573. package/src/tts/providers/index.ts +2 -0
  574. package/src/tts/providers/xai-provider.ts +224 -0
  575. package/src/tts/types.ts +46 -0
  576. package/src/types/tar-stream.d.ts +66 -0
  577. package/src/util/json.ts +17 -0
  578. package/src/util/platform.ts +2 -2
  579. package/src/util/pricing.ts +15 -5
  580. package/src/watcher/engine.ts +1 -1
  581. package/src/watcher/providers/google-calendar.ts +134 -8
  582. package/src/watcher/providers/outlook-calendar.ts +42 -2
  583. package/src/workspace/git-service.ts +23 -4
  584. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  585. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  586. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  587. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
  588. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  589. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  590. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  591. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  592. package/src/workspace/migrations/AGENTS.md +1 -1
  593. package/src/workspace/migrations/registry.ts +16 -0
  594. package/src/workspace/provider-commit-message-generator.ts +19 -38
  595. package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
  596. package/src/__tests__/gmail-archive-gate.test.ts +0 -246
  597. package/src/__tests__/gmail-preferences.test.ts +0 -117
  598. package/src/__tests__/outlook-attachments.test.ts +0 -301
  599. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  600. package/src/__tests__/outlook-categories.test.ts +0 -212
  601. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  602. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  603. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  604. package/src/__tests__/outlook-trash.test.ts +0 -77
  605. package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
  606. package/src/__tests__/update-bulletin-format.test.ts +0 -181
  607. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  608. package/src/__tests__/update-bulletin.test.ts +0 -478
  609. package/src/__tests__/update-template-contract.test.ts +0 -29
  610. package/src/cli/commands/doctor.ts +0 -341
  611. package/src/config/bundled-skills/browser/SKILL.md +0 -88
  612. package/src/config/bundled-skills/browser/TOOLS.json +0 -516
  613. package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
  614. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  615. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  616. package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
  617. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  618. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  619. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  620. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  621. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  622. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  623. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  624. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  625. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  626. package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
  627. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  628. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
  629. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  630. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  631. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  632. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  633. package/src/config/bundled-skills/gmail/SKILL.md +0 -221
  634. package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
  635. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
  636. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  637. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  638. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  639. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  640. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  641. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  642. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
  643. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
  644. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
  645. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  646. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
  647. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  648. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  649. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  650. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  651. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  652. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  653. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  654. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  655. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  656. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  657. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  658. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  659. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  660. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  661. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  662. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  663. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  664. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  665. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  666. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  667. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  668. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  669. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  670. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  671. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  672. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  673. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  674. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  675. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  676. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  677. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  678. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  679. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  680. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  681. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  682. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  683. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  684. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  685. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  686. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  687. package/src/config/bundled-skills/slack/SKILL.md +0 -108
  688. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  689. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  690. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  691. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  692. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  693. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  694. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  695. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  696. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  697. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  698. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  699. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  700. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  701. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  702. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  703. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  704. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  705. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  706. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  707. package/src/prompts/templates/UPDATES.md +0 -50
  708. package/src/prompts/update-bulletin-format.ts +0 -85
  709. package/src/prompts/update-bulletin-state.ts +0 -58
  710. package/src/prompts/update-bulletin-template-path.ts +0 -13
  711. package/src/prompts/update-bulletin.ts +0 -139
  712. package/src/shared/provider-env-vars.ts +0 -19
  713. package/src/tools/watcher/create.ts +0 -86
  714. package/src/tools/watcher/delete.ts +0 -36
  715. package/src/tools/watcher/digest.ts +0 -54
  716. package/src/tools/watcher/list.ts +0 -83
  717. package/src/tools/watcher/update.ts +0 -71
@@ -7,6 +7,8 @@
7
7
  * runAgentLoop method here via the AgentLoopConversationContext interface.
8
8
  */
9
9
 
10
+ import { join } from "node:path";
11
+
10
12
  import { v4 as uuid } from "uuid";
11
13
 
12
14
  import type {
@@ -24,11 +26,15 @@ import type {
24
26
  } from "../channels/types.js";
25
27
  import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
26
28
  import { getConfig } from "../config/loader.js";
29
+ import type { LLMCallSite } from "../config/schemas/llm.js";
27
30
  import {
28
31
  derefToolResultReReads,
29
32
  postTurnTruncateToolResults,
30
33
  } from "../context/post-turn-tool-result-truncation.js";
31
- import { estimatePromptTokens } from "../context/token-estimator.js";
34
+ import {
35
+ estimatePromptTokens,
36
+ getCalibrationProviderKey,
37
+ } from "../context/token-estimator.js";
32
38
  import type { ContextWindowManager } from "../context/window-manager.js";
33
39
  import type { ToolProfiler } from "../events/tool-profiling-listener.js";
34
40
  import { writeRelationshipState } from "../home/relationship-state-writer.js";
@@ -42,6 +48,7 @@ import { getApp, listAppFiles, resolveAppDir } from "../memory/app-store.js";
42
48
  import { enqueueAutoAnalysisOnCompaction } from "../memory/auto-analysis-enqueue.js";
43
49
  import {
44
50
  addMessage,
51
+ clearPkbSystemReminderMetadataForConversation,
45
52
  deleteMessageById,
46
53
  getConversation,
47
54
  getConversationOriginChannel,
@@ -63,6 +70,7 @@ import {
63
70
  } from "../memory/conversation-title-service.js";
64
71
  import type { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
65
72
  import { recordMemoryRecallLog } from "../memory/memory-recall-log-store.js";
73
+ import { PKB_WORKSPACE_SCOPE } from "../memory/pkb/types.js";
66
74
  import type { PermissionPrompter } from "../permissions/prompter.js";
67
75
  import type { ContentBlock, Message } from "../providers/types.js";
68
76
  import type { Provider } from "../providers/types.js";
@@ -71,6 +79,7 @@ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
71
79
  import { getSubagentManager } from "../subagent/index.js";
72
80
  import type { UsageActor } from "../usage/actors.js";
73
81
  import { getLogger } from "../util/logger.js";
82
+ import { getWorkspaceDir } from "../util/platform.js";
74
83
  import { timeAgo } from "../util/time.js";
75
84
  import { truncate } from "../util/truncate.js";
76
85
  import { getWorkspaceGitService } from "../workspace/git-service.js";
@@ -115,8 +124,11 @@ import {
115
124
  buildSubagentStatusBlock,
116
125
  buildUnifiedTurnContextBlock,
117
126
  findLastInjectedNowContent,
127
+ getPkbAutoInjectList,
118
128
  inboundActorContextFromTrust,
119
129
  inboundActorContextFromTrustContext,
130
+ loadSlackActiveThreadFocusBlock,
131
+ loadSlackChronologicalMessages,
120
132
  readNowScratchpad,
121
133
  readPkbContext,
122
134
  stripInjectionsForCompaction,
@@ -135,44 +147,12 @@ import type {
135
147
  UsageStats,
136
148
  } from "./message-protocol.js";
137
149
  import type { MemoryRecalled } from "./message-types/memory.js";
150
+ import { parseActualTokensFromError } from "./parse-actual-tokens-from-error.js";
138
151
  import type { TraceEmitter } from "./trace-emitter.js";
152
+ import { stripHistoricalWebSearchResults } from "./web-search-history.js";
139
153
 
140
154
  const log = getLogger("conversation-agent-loop");
141
155
 
142
- /**
143
- * Parse the actual token count reported by the provider in a context-too-large
144
- * error message. Providers typically include the prompt size, e.g.:
145
- * "prompt is too long: 242201 tokens > 200000 maximum"
146
- * "too many input tokens: 242201 > 200000"
147
- *
148
- * Returns the actual token count or null if it cannot be parsed.
149
- */
150
- export function parseActualTokensFromError(
151
- errorMessage: string | null,
152
- ): number | null {
153
- if (!errorMessage) return null;
154
-
155
- // Match patterns like "242201 tokens > 200000" or "242201 > 200000 maximum"
156
- const match = errorMessage.match(
157
- /(\d[\d,]*)\s*tokens?\s*[>≥]|:\s*(\d[\d,]*)\s*[>≥]/i,
158
- );
159
- if (match) {
160
- const raw = (match[1] || match[2]).replace(/,/g, "");
161
- const parsed = parseInt(raw, 10);
162
- if (!isNaN(parsed) && parsed > 0) return parsed;
163
- }
164
-
165
- // Fallback: match "too many input tokens: N > M"
166
- const fallback = errorMessage.match(/(\d[\d,]*)\s*[>≥]\s*\d/);
167
- if (fallback) {
168
- const raw = fallback[1].replace(/,/g, "");
169
- const parsed = parseInt(raw, 10);
170
- if (!isNaN(parsed) && parsed > 0) return parsed;
171
- }
172
-
173
- return null;
174
- }
175
-
176
156
  /** Title-cased friendly labels for tool names, used in confirmation chips. */
177
157
  const TOOL_FRIENDLY_LABEL: Record<string, string> = {
178
158
  bash: "Run Command",
@@ -181,12 +161,6 @@ const TOOL_FRIENDLY_LABEL: Record<string, string> = {
181
161
  file_read: "Read File",
182
162
  file_write: "Write File",
183
163
  file_edit: "Edit File",
184
- browser_navigate: "Browser",
185
- browser_click: "Browser",
186
- browser_type: "Browser",
187
- browser_screenshot: "Browser",
188
- browser_scroll: "Browser",
189
- browser_wait: "Browser",
190
164
  app_create: "Create App",
191
165
  app_refresh: "Refresh App",
192
166
  skill_load: "Load Skill",
@@ -197,6 +171,79 @@ type GitServiceInitializer = {
197
171
  ensureInitialized(): Promise<void>;
198
172
  };
199
173
 
174
+ // ── Compaction circuit-breaker constants ────────────────────────────
175
+ //
176
+ // The circuit opens after `COMPACTION_CIRCUIT_FAILURE_THRESHOLD` consecutive
177
+ // summary-LLM failures and stays open for `COMPACTION_CIRCUIT_COOLDOWN_MS`
178
+ // before auto-compaction is allowed to retry. User-initiated compaction
179
+ // (`force: true`) bypasses the breaker regardless of its state.
180
+ const COMPACTION_CIRCUIT_FAILURE_THRESHOLD = 3;
181
+ const COMPACTION_CIRCUIT_COOLDOWN_MS = 60 * 60 * 1000; // 1 hour
182
+
183
+ /**
184
+ * Check whether the compaction circuit breaker is currently open for the
185
+ * given context. The breaker auto-closes once `compactionCircuitOpenUntil`
186
+ * has elapsed.
187
+ */
188
+ export function isCompactionCircuitOpen(ctx: {
189
+ compactionCircuitOpenUntil: number | null;
190
+ }): boolean {
191
+ return (
192
+ ctx.compactionCircuitOpenUntil !== null &&
193
+ Date.now() < ctx.compactionCircuitOpenUntil
194
+ );
195
+ }
196
+
197
+ /**
198
+ * Track the outcome of a `maybeCompact()` call against the circuit breaker.
199
+ *
200
+ * - When the summary LLM call failed (local fallback covered the result),
201
+ * increment the consecutive-failure counter. If the counter reaches the
202
+ * threshold, open the circuit for the cooldown window and emit
203
+ * `compaction_circuit_open` so clients can surface a notice.
204
+ * - When the call did not fail, reset the counter and clear any open circuit.
205
+ *
206
+ * This is called by every `maybeCompact()` site (including forced ones),
207
+ * because a run of three failures is a provider-health signal regardless of
208
+ * whether the caller bypassed the breaker.
209
+ */
210
+ export function trackCompactionOutcome(
211
+ ctx: {
212
+ consecutiveCompactionFailures: number;
213
+ compactionCircuitOpenUntil: number | null;
214
+ },
215
+ summaryFailed: boolean | undefined,
216
+ onEvent: (msg: ServerMessage) => void,
217
+ ): void {
218
+ if (summaryFailed) {
219
+ ctx.consecutiveCompactionFailures += 1;
220
+ // Treat a stale/expired open-until timestamp the same as null so a new
221
+ // 3-strike window can re-open the circuit after the prior cooldown
222
+ // elapses. Without this the second trip would no-op because
223
+ // `compactionCircuitOpenUntil` remains set to a past timestamp even
224
+ // though `isCompactionCircuitOpen()` correctly reports closed.
225
+ const circuitDormant =
226
+ ctx.compactionCircuitOpenUntil === null ||
227
+ Date.now() >= ctx.compactionCircuitOpenUntil;
228
+ if (
229
+ ctx.consecutiveCompactionFailures >=
230
+ COMPACTION_CIRCUIT_FAILURE_THRESHOLD &&
231
+ circuitDormant
232
+ ) {
233
+ const openUntil = Date.now() + COMPACTION_CIRCUIT_COOLDOWN_MS;
234
+ ctx.compactionCircuitOpenUntil = openUntil;
235
+ onEvent({
236
+ type: "compaction_circuit_open",
237
+ reason: "3_consecutive_failures",
238
+ openUntil,
239
+ });
240
+ }
241
+ } else {
242
+ ctx.consecutiveCompactionFailures = 0;
243
+ ctx.compactionCircuitOpenUntil = null;
244
+ }
245
+ }
246
+
200
247
  // ── Context Interface ────────────────────────────────────────────────
201
248
 
202
249
  export interface AgentLoopConversationContext {
@@ -213,6 +260,10 @@ export interface AgentLoopConversationContext {
213
260
  readonly contextWindowManager: ContextWindowManager;
214
261
  contextCompactedMessageCount: number;
215
262
  contextCompactedAt: number | null;
263
+ /** Tracks consecutive compaction failures (summary LLM call threw). */
264
+ consecutiveCompactionFailures: number;
265
+ /** Timestamp (ms since epoch) until which the circuit breaker is open. */
266
+ compactionCircuitOpenUntil: number | null;
216
267
 
217
268
  readonly memoryPolicy: { scopeId: string; includeDefaultFallback: boolean };
218
269
  readonly graphMemory: ConversationGraphMemory;
@@ -235,6 +286,7 @@ export interface AgentLoopConversationContext {
235
286
  >;
236
287
  pendingSurfaceActions: Map<string, { surfaceType: SurfaceType }>;
237
288
  surfaceActionRequestIds: Set<string>;
289
+ approvedViaPromptThisTurn?: boolean;
238
290
  currentTurnSurfaces: Array<{
239
291
  surfaceId: string;
240
292
  surfaceType: SurfaceType;
@@ -356,6 +408,13 @@ export async function runAgentLoopImpl(
356
408
  isInteractive?: boolean;
357
409
  isUserMessage?: boolean;
358
410
  titleText?: string;
411
+ /**
412
+ * LLM call-site identifier threaded into the per-call provider config.
413
+ * Adapter callers (heartbeat, filing, scheduler, etc.) pass their own
414
+ * call-site id so the resolver picks `llm.callSites.<id>`. When unset,
415
+ * the agent loop defaults to `'mainAgent'` for user-initiated turns.
416
+ */
417
+ callSite?: LLMCallSite;
359
418
  },
360
419
  ): Promise<void> {
361
420
  if (!ctx.abortController) {
@@ -379,6 +438,13 @@ export async function runAgentLoopImpl(
379
438
  });
380
439
  let yieldedForHandoff = false;
381
440
 
441
+ // Default user-initiated turns to the `mainAgent` call site. Other
442
+ // invocation contexts (heartbeat, filing, analyze, etc.) pass their own
443
+ // `callSite`. The provider layer resolves provider/model/maxTokens via
444
+ // `resolveCallSiteConfig`, picking up any user overrides under
445
+ // `llm.callSites.mainAgent` (falling back to `llm.default` when absent).
446
+ const turnCallSite: LLMCallSite = options?.callSite ?? "mainAgent";
447
+
382
448
  // Capture the turn channel context *before* any awaits so a second
383
449
  // message from a different channel can't overwrite it mid-flight.
384
450
  // When context is unavailable (e.g. regenerate after daemon restart),
@@ -524,7 +590,10 @@ export async function runAgentLoopImpl(
524
590
  let compactedThisTurn = false;
525
591
 
526
592
  const compactCheck = ctx.contextWindowManager.shouldCompact(ctx.messages);
527
- if (compactCheck.needed) {
593
+ // Skip auto-compaction while the circuit breaker is open. Force paths
594
+ // and user-initiated /compact bypass this check.
595
+ const autoCompactAllowed = !isCompactionCircuitOpen(ctx);
596
+ if (compactCheck.needed && autoCompactAllowed) {
528
597
  ctx.emitActivityState(
529
598
  "thinking",
530
599
  "context_compacting",
@@ -532,15 +601,27 @@ export async function runAgentLoopImpl(
532
601
  reqId,
533
602
  );
534
603
  }
535
- const compacted = await ctx.contextWindowManager.maybeCompact(
536
- ctx.messages,
537
- abortController.signal,
538
- {
539
- lastCompactedAt: ctx.contextCompactedAt ?? undefined,
540
- precomputedEstimate: compactCheck.estimatedTokens,
541
- },
542
- );
543
- if (compacted.compacted) {
604
+ const compacted = autoCompactAllowed
605
+ ? await ctx.contextWindowManager.maybeCompact(
606
+ ctx.messages,
607
+ abortController.signal,
608
+ {
609
+ lastCompactedAt: ctx.contextCompactedAt ?? undefined,
610
+ precomputedEstimate: compactCheck.estimatedTokens,
611
+ conversationOriginChannel:
612
+ getConversationOriginChannel(ctx.conversationId) ?? undefined,
613
+ },
614
+ )
615
+ : null;
616
+ // Only track circuit-breaker state when a summary LLM call actually ran.
617
+ // `summaryFailed` is `undefined` on early returns (compaction disabled,
618
+ // below threshold, cooldown active, no eligible messages, truncation-only
619
+ // path) — treating those as "successful" compactions would silently reset
620
+ // the 3-strike counter and break the invariant.
621
+ if (compacted && compacted.summaryFailed !== undefined) {
622
+ trackCompactionOutcome(ctx, compacted.summaryFailed, onEvent);
623
+ }
624
+ if (compacted?.compacted) {
544
625
  ctx.messages = compacted.messages;
545
626
  ctx.contextCompactedMessageCount += compacted.compactedPersistedMessages;
546
627
  ctx.contextCompactedAt = Date.now();
@@ -631,7 +712,12 @@ export async function runAgentLoopImpl(
631
712
  let runMessages = ctx.messages;
632
713
 
633
714
  // Memory graph retrieval — dispatches to context-load / per-turn based on
634
- // conversation state.
715
+ // conversation state. Keep the query vector around so the PKB reminder
716
+ // can reuse it for relevance-hint search (see `applyRuntimeInjections`).
717
+ let pkbQueryVector: number[] | undefined;
718
+ let pkbSparseVector:
719
+ | import("../memory/qdrant-client.js").QdrantSparseVector
720
+ | undefined;
635
721
  const isTrustedActor = resolveTrustClass(ctx.trustContext) === "guardian";
636
722
  if (isTrustedActor) {
637
723
  const graphResult = await ctx.graphMemory.prepareMemory(
@@ -641,6 +727,22 @@ export async function runAgentLoopImpl(
641
727
  onEvent,
642
728
  );
643
729
  runMessages = graphResult.runMessages;
730
+ // Select dense+sparse as a matched pair so RRF fusion combines two
731
+ // signals aligned to the same query text:
732
+ // 1. Context-load with a user query: user-query dense + user-query
733
+ // sparse — the cleanest pairing.
734
+ // 2. Otherwise (context-load without a user query, or per-turn):
735
+ // whatever `queryVector` / `sparseVector` the retriever produced,
736
+ // which are themselves co-aligned (both summary-derived in
737
+ // context-load, both user-last-message-derived in per-turn).
738
+ // Never pair a user-query dense with a summary-aligned sparse.
739
+ if (graphResult.userQueryVector) {
740
+ pkbQueryVector = graphResult.userQueryVector;
741
+ pkbSparseVector = graphResult.userQuerySparseVector;
742
+ } else {
743
+ pkbQueryVector = graphResult.queryVector;
744
+ pkbSparseVector = graphResult.sparseVector;
745
+ }
644
746
 
645
747
  // Persist the injected block text in message metadata so it survives
646
748
  // conversation reloads (eviction, restart, fork). loadFromDb re-injects
@@ -839,6 +941,21 @@ export async function runAgentLoopImpl(
839
941
  const pkbContext = shouldInjectNowAndPkb ? currentPkbContent : null;
840
942
  const pkbActive = currentPkbContent !== null;
841
943
 
944
+ // PKB relevance-hint inputs. Resolved once per turn and reused across
945
+ // re-injections so post-compaction rebuilds pick up fresh hints against
946
+ // the updated conversation history.
947
+ const pkbRoot = pkbActive ? join(getWorkspaceDir(), "pkb") : undefined;
948
+ const pkbAutoInjectList = pkbRoot
949
+ ? getPkbAutoInjectList(pkbRoot)
950
+ : undefined;
951
+ // Pass `ctx` directly — `PkbContextConversation` is structural and
952
+ // `getInContextPkbPaths` re-reads `conversation.messages` on each call,
953
+ // so post-compaction re-injects see the updated history.
954
+ const pkbConversation = pkbActive ? ctx : undefined;
955
+ // PKB points live under a single workspace sentinel scope, not the
956
+ // conversation's memoryPolicy.scopeId. See `PKB_WORKSPACE_SCOPE` for why.
957
+ const pkbScopeId = pkbActive ? PKB_WORKSPACE_SCOPE : undefined;
958
+
842
959
  // Subagent status injection — gives the parent LLM visibility into active/completed children.
843
960
  // Skipped when this conversation IS a subagent (no nesting) or has no children.
844
961
  const subagentStatusBlock = ctx.isSubagent
@@ -847,6 +964,43 @@ export async function runAgentLoopImpl(
847
964
  getSubagentManager().getChildrenOf(ctx.conversationId),
848
965
  );
849
966
 
967
+ // For any Slack conversation (channels and DMs alike), build a
968
+ // chronological transcript from the persisted message rows so the
969
+ // model sees one channel-wide view instead of the gateway's per-turn
970
+ // hints. DMs render as a flat sequence (no thread tags), channels
971
+ // include sibling threads.
972
+ const isSlackConversation = ctx.channelCapabilities?.channel === "slack";
973
+ const slackChronologicalMessages = isSlackConversation
974
+ ? loadSlackChronologicalMessages(
975
+ ctx.conversationId,
976
+ ctx.channelCapabilities!,
977
+ { trustClass: ctx.trustContext?.trustClass },
978
+ )
979
+ : null;
980
+
981
+ // Active-thread focus block: when the inbound user message belongs to
982
+ // a Slack thread, append a non-persisted `<active_thread>` tail block
983
+ // to the final user turn listing the thread's parent + replies. Helps
984
+ // the model orient when the channel transcript is long and
985
+ // interleaved. Replays strip the block via RUNTIME_INJECTION_PREFIXES.
986
+ // DMs short-circuit to null inside `loadSlackActiveThreadFocusBlock`
987
+ // since DMs do not have threads.
988
+ const slackActiveThreadFocusBlock = isSlackConversation
989
+ ? loadSlackActiveThreadFocusBlock(
990
+ ctx.conversationId,
991
+ ctx.channelCapabilities!,
992
+ { trustClass: ctx.trustContext?.trustClass },
993
+ )
994
+ : null;
995
+
996
+ // Guards the chronological-transcript override on re-injection after
997
+ // the reducer compacts `ctx.messages`. The captured transcript is the
998
+ // full persisted history; blindly replaying it on every re-inject would
999
+ // overwrite the reducer's compacted messages and undo compaction. Flip
1000
+ // to `true` after any compaction so subsequent re-injections fall back
1001
+ // to the reduced `ctx.messages`.
1002
+ let reducerCompacted = compactedThisTurn;
1003
+
850
1004
  // Shared injection options — reused whenever we need to re-inject after reduction.
851
1005
  const injectionOpts = {
852
1006
  activeSurface,
@@ -858,27 +1012,66 @@ export async function runAgentLoopImpl(
858
1012
  unifiedTurnContext: unifiedTurnContextStr,
859
1013
  pkbContext,
860
1014
  pkbActive,
1015
+ pkbQueryVector,
1016
+ pkbSparseVector,
1017
+ pkbScopeId,
1018
+ pkbConversation,
1019
+ pkbAutoInjectList,
1020
+ pkbRoot,
1021
+ pkbWorkingDir: pkbActive ? ctx.workingDir : undefined,
861
1022
  nowScratchpad,
862
1023
  voiceCallControlPrompt: ctx.voiceCallControlPrompt ?? null,
863
1024
  transportHints: ctx.transportHints ?? null,
864
1025
  isNonInteractive: !isInteractiveResolved,
865
1026
  subagentStatusBlock,
1027
+ slackChronologicalMessages,
1028
+ slackActiveThreadFocusBlock,
866
1029
  } as const;
867
1030
 
868
1031
  let currentInjectionMode: InjectionMode = "full";
869
1032
 
870
- runMessages = applyRuntimeInjections(runMessages, {
1033
+ const injection = await applyRuntimeInjections(runMessages, {
871
1034
  ...injectionOpts,
1035
+ slackChronologicalMessages: reducerCompacted
1036
+ ? null
1037
+ : injectionOpts.slackChronologicalMessages,
872
1038
  mode: currentInjectionMode,
873
1039
  });
1040
+ runMessages = injection.messages;
1041
+
1042
+ // Persist injected blocks in message metadata so they survive conversation
1043
+ // reloads (eviction, restart, fork). loadFromDb re-injects from metadata.
1044
+ // Only the first call site persists — the overflow-recovery re-entry sites
1045
+ // send identical bytes and the tail row may not correspond to
1046
+ // `userMessageId`. Both blocks are written in a single call to avoid
1047
+ // doubling SQLite SELECT+UPDATE work on every turn.
1048
+ if (
1049
+ injection.blocks.unifiedTurnContext ||
1050
+ injection.blocks.pkbSystemReminder
1051
+ ) {
1052
+ try {
1053
+ const metadataUpdates: Record<string, unknown> = {};
1054
+ if (injection.blocks.unifiedTurnContext) {
1055
+ metadataUpdates.turnContextBlock =
1056
+ injection.blocks.unifiedTurnContext;
1057
+ }
1058
+ if (injection.blocks.pkbSystemReminder) {
1059
+ metadataUpdates.pkbSystemReminderBlock =
1060
+ injection.blocks.pkbSystemReminder;
1061
+ }
1062
+ updateMessageMetadata(userMessageId, metadataUpdates);
1063
+ } catch (err) {
1064
+ rlog.warn({ err }, "Failed to persist injection metadata (non-fatal)");
1065
+ }
1066
+ }
874
1067
 
875
1068
  // ── Preflight budget evaluation ──────────────────────────────
876
1069
  // After runtime injections are applied, estimate the prompt token count
877
1070
  // and proactively invoke the reducer if already above budget. This avoids
878
1071
  // a wasted provider round-trip that would just fail with context_too_large.
879
1072
  const config = getConfig();
880
- const overflowRecovery = config.contextWindow.overflowRecovery;
881
- const providerMaxTokens = config.contextWindow.maxInputTokens;
1073
+ const overflowRecovery = config.llm.default.contextWindow.overflowRecovery;
1074
+ const providerMaxTokens = config.llm.default.contextWindow.maxInputTokens;
882
1075
  // Widen safety margin for large conversations where estimation error
883
1076
  // compounds across many messages with tool results.
884
1077
  const baseSafetyMargin = overflowRecovery.safetyMarginRatio;
@@ -889,10 +1082,17 @@ export async function runAgentLoopImpl(
889
1082
  let reducerState: ReducerState | undefined;
890
1083
 
891
1084
  const toolTokenBudget = ctx.agentLoop.getToolTokenBudget(runMessages);
1085
+ // Canonical calibration key used at every `estimatePromptTokens` site in
1086
+ // this function. Matches the key recorded by `handleUsage` for wrapper
1087
+ // providers (OpenRouter routing to Anthropic → key is `"anthropic"`).
1088
+ const estimationProviderName = getCalibrationProviderKey(ctx.provider);
892
1089
  const preflightTokens = estimatePromptTokens(
893
1090
  runMessages,
894
1091
  ctx.systemPrompt,
895
- { providerName: ctx.provider.name, toolTokenBudget },
1092
+ {
1093
+ providerName: estimationProviderName,
1094
+ toolTokenBudget,
1095
+ },
896
1096
  );
897
1097
 
898
1098
  if (overflowRecovery.enabled && preflightTokens > preflightBudget) {
@@ -922,9 +1122,9 @@ export async function runAgentLoopImpl(
922
1122
  const step = await reduceContextOverflow(
923
1123
  ctx.messages,
924
1124
  {
925
- providerName: ctx.provider.name,
1125
+ providerName: estimationProviderName,
926
1126
  systemPrompt: ctx.systemPrompt,
927
- contextWindow: config.contextWindow,
1127
+ contextWindow: config.llm.default.contextWindow,
928
1128
  targetTokens: preflightBudget,
929
1129
  toolTokenBudget,
930
1130
  },
@@ -938,6 +1138,24 @@ export async function runAgentLoopImpl(
938
1138
  ctx.messages = step.messages;
939
1139
  currentInjectionMode = step.state.injectionMode;
940
1140
 
1141
+ // Track circuit-breaker state whenever the reducer invoked compaction.
1142
+ // The reducer's forced_compaction tier uses force:true, so it bypasses
1143
+ // the open-circuit check, but we still want failure tracking to detect
1144
+ // a run of broken summaries and clear the counter on success. Only
1145
+ // track when the summary LLM actually ran — `summaryFailed === undefined`
1146
+ // indicates an early return (no eligible messages, truncation-only
1147
+ // path, etc.) that shouldn't influence the breaker.
1148
+ if (
1149
+ step.compactionResult &&
1150
+ step.compactionResult.summaryFailed !== undefined
1151
+ ) {
1152
+ trackCompactionOutcome(
1153
+ ctx,
1154
+ step.compactionResult.summaryFailed,
1155
+ onEvent,
1156
+ );
1157
+ }
1158
+
941
1159
  if (step.compactionResult?.compacted) {
942
1160
  ctx.contextCompactedMessageCount +=
943
1161
  step.compactionResult.compactedPersistedMessages;
@@ -981,13 +1199,14 @@ export async function runAgentLoopImpl(
981
1199
  step.compactionResult.compactedPersistedMessages,
982
1200
  );
983
1201
  shouldInjectWorkspace = true;
1202
+ reducerCompacted = true;
984
1203
  }
985
1204
 
986
1205
  // Re-inject with potentially downgraded injection mode.
987
1206
  // When compaction ran it strips existing NOW.md / PKB blocks, so we
988
1207
  // must re-inject the current content. Otherwise rely on the deduplicated
989
1208
  // value from injectionOpts to avoid duplicate injection.
990
- runMessages = applyRuntimeInjections(ctx.messages, {
1209
+ const injection = await applyRuntimeInjections(ctx.messages, {
991
1210
  ...injectionOpts,
992
1211
  ...(step.compactionResult?.compacted && {
993
1212
  pkbContext: currentPkbContent,
@@ -998,8 +1217,16 @@ export async function runAgentLoopImpl(
998
1217
  workspaceTopLevelContext: shouldInjectWorkspace
999
1218
  ? ctx.workspaceTopLevelContext
1000
1219
  : null,
1220
+ // Once the reducer has compacted `ctx.messages`, the captured
1221
+ // `slackChronologicalMessages` snapshot (built from the full
1222
+ // persisted transcript) would overwrite the compacted history
1223
+ // and undo compaction. Suppress the override from here on.
1224
+ slackChronologicalMessages: reducerCompacted
1225
+ ? null
1226
+ : injectionOpts.slackChronologicalMessages,
1001
1227
  mode: currentInjectionMode,
1002
1228
  });
1229
+ runMessages = injection.messages;
1003
1230
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1004
1231
  const memResult = ctx.graphMemory.reinjectCachedMemory(runMessages);
1005
1232
  runMessages = memResult.runMessages;
@@ -1011,7 +1238,10 @@ export async function runAgentLoopImpl(
1011
1238
  const postInjectionTokens = estimatePromptTokens(
1012
1239
  runMessages,
1013
1240
  ctx.systemPrompt,
1014
- { providerName: ctx.provider.name, toolTokenBudget },
1241
+ {
1242
+ providerName: estimationProviderName,
1243
+ toolTokenBudget,
1244
+ },
1015
1245
  );
1016
1246
 
1017
1247
  if (postInjectionTokens <= preflightBudget) break;
@@ -1034,6 +1264,20 @@ export async function runAgentLoopImpl(
1034
1264
  runMessages = preRunRepair.messages;
1035
1265
  }
1036
1266
 
1267
+ // Replace historical web_search_tool_result blocks with text summaries.
1268
+ // The opaque `encrypted_content` tokens Anthropic attaches to each result
1269
+ // expire / are route-scoped; replaying a stale token is rejected with
1270
+ // `Invalid encrypted_content in search_result block`. Titles + URLs
1271
+ // preserve enough context for the model on follow-up turns.
1272
+ const webSearchStrip = stripHistoricalWebSearchResults(runMessages);
1273
+ if (webSearchStrip.stats.blocksStripped > 0) {
1274
+ rlog.info(
1275
+ { phase: "pre_run", ...webSearchStrip.stats },
1276
+ "Converted historical web_search_tool_result blocks to text summaries",
1277
+ );
1278
+ runMessages = webSearchStrip.messages;
1279
+ }
1280
+
1037
1281
  let preRunHistoryLength = runMessages.length;
1038
1282
 
1039
1283
  const shouldGenerateTitle = isReplaceableTitle(
@@ -1056,17 +1300,11 @@ export async function runAgentLoopImpl(
1056
1300
  let yieldedForBudget = false;
1057
1301
 
1058
1302
  const onCheckpoint = (checkpoint: CheckpointInfo): CheckpointDecision => {
1059
- const turnTools = state.currentTurnToolNames;
1060
1303
  state.currentTurnToolNames = [];
1061
1304
 
1062
1305
  if (ctx.canHandoffAtCheckpoint()) {
1063
- const inBrowserFlow =
1064
- turnTools.length > 0 &&
1065
- turnTools.every((n) => n.startsWith("browser_"));
1066
- if (!inBrowserFlow) {
1067
- yieldedForHandoff = true;
1068
- return "yield";
1069
- }
1306
+ yieldedForHandoff = true;
1307
+ return "yield";
1070
1308
  }
1071
1309
 
1072
1310
  // Mid-loop token budget check: estimate current context size and
@@ -1077,7 +1315,10 @@ export async function runAgentLoopImpl(
1077
1315
  const estimated = estimatePromptTokens(
1078
1316
  checkpoint.history,
1079
1317
  ctx.systemPrompt,
1080
- { providerName: ctx.provider.name, toolTokenBudget },
1318
+ {
1319
+ providerName: estimationProviderName,
1320
+ toolTokenBudget,
1321
+ },
1081
1322
  );
1082
1323
  if (estimated > midLoopThreshold) {
1083
1324
  rlog.warn(
@@ -1096,12 +1337,20 @@ export async function runAgentLoopImpl(
1096
1337
 
1097
1338
  let denyCompressionMessage: Message | null = null;
1098
1339
 
1340
+ rlog.info({ callSite: turnCallSite }, "Starting agent loop run");
1341
+
1099
1342
  let updatedHistory = await ctx.agentLoop.run(
1100
1343
  runMessages,
1101
1344
  eventHandler,
1102
1345
  abortController.signal,
1103
1346
  reqId,
1104
1347
  onCheckpoint,
1348
+ turnCallSite,
1349
+ );
1350
+
1351
+ rlog.info(
1352
+ { resultMessageCount: updatedHistory.length },
1353
+ "Agent loop run completed",
1105
1354
  );
1106
1355
 
1107
1356
  // ── Proactive mid-loop compaction ───────────────────────────────
@@ -1129,6 +1378,14 @@ export async function runAgentLoopImpl(
1129
1378
  // so we compact the "raw" persistent messages.
1130
1379
  const rawHistory = stripInjectionsForCompaction(updatedHistory);
1131
1380
  ctx.messages = rawHistory;
1381
+ try {
1382
+ clearPkbSystemReminderMetadataForConversation(ctx.conversationId);
1383
+ } catch (err) {
1384
+ rlog.warn(
1385
+ { err },
1386
+ "Failed to clear pkbSystemReminderBlock metadata after compaction strip (non-fatal)",
1387
+ );
1388
+ }
1132
1389
 
1133
1390
  ctx.emitActivityState(
1134
1391
  "thinking",
@@ -1144,10 +1401,19 @@ export async function runAgentLoopImpl(
1144
1401
  lastCompactedAt: ctx.contextCompactedAt ?? undefined,
1145
1402
  force: true,
1146
1403
  targetInputTokensOverride: preflightBudget,
1404
+ conversationOriginChannel:
1405
+ getConversationOriginChannel(ctx.conversationId) ?? undefined,
1147
1406
  },
1148
1407
  );
1408
+ // `force: true` bypasses the cooldown/threshold gates but early returns
1409
+ // for "no eligible messages" / "insufficient messages" still leave
1410
+ // `summaryFailed` undefined. Only track when the summary LLM actually ran.
1411
+ if (midLoopCompact.summaryFailed !== undefined) {
1412
+ trackCompactionOutcome(ctx, midLoopCompact.summaryFailed, onEvent);
1413
+ }
1149
1414
  if (midLoopCompact.compacted) {
1150
1415
  ctx.messages = midLoopCompact.messages;
1416
+ reducerCompacted = true;
1151
1417
  ctx.contextCompactedMessageCount +=
1152
1418
  midLoopCompact.compactedPersistedMessages;
1153
1419
  ctx.contextCompactedAt = Date.now();
@@ -1194,18 +1460,33 @@ export async function runAgentLoopImpl(
1194
1460
  // stripInjectionsForCompaction() unconditionally removed the existing
1195
1461
  // NOW.md block from ctx.messages above, so we must always re-inject
1196
1462
  // the current content regardless of whether compaction actually ran.
1197
- runMessages = applyRuntimeInjections(ctx.messages, {
1463
+ const injection = await applyRuntimeInjections(ctx.messages, {
1198
1464
  ...injectionOpts,
1199
1465
  pkbContext: currentPkbContent,
1200
1466
  nowScratchpad: currentNowContent,
1201
1467
  workspaceTopLevelContext: shouldInjectWorkspace
1202
1468
  ? ctx.workspaceTopLevelContext
1203
1469
  : null,
1470
+ // Suppress the chronological-transcript snapshot once the reducer
1471
+ // has collapsed `ctx.messages`; the captured snapshot reflects the
1472
+ // full persisted transcript and would overwrite compaction.
1473
+ slackChronologicalMessages: reducerCompacted
1474
+ ? null
1475
+ : injectionOpts.slackChronologicalMessages,
1204
1476
  mode: currentInjectionMode,
1205
1477
  });
1478
+ runMessages = injection.messages;
1206
1479
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1207
1480
  ctx.graphMemory.retrackCachedNodes();
1208
1481
  }
1482
+ const midLoopCompactStrip = stripHistoricalWebSearchResults(runMessages);
1483
+ if (midLoopCompactStrip.stats.blocksStripped > 0) {
1484
+ rlog.info(
1485
+ { phase: "mid-loop-compact", ...midLoopCompactStrip.stats },
1486
+ "Converted historical web_search_tool_result blocks to text summaries",
1487
+ );
1488
+ runMessages = midLoopCompactStrip.messages;
1489
+ }
1209
1490
  preRepairMessages = runMessages;
1210
1491
  preRunHistoryLength = runMessages.length;
1211
1492
 
@@ -1215,6 +1496,7 @@ export async function runAgentLoopImpl(
1215
1496
  abortController.signal,
1216
1497
  reqId,
1217
1498
  onCheckpoint,
1499
+ turnCallSite,
1218
1500
  );
1219
1501
  }
1220
1502
 
@@ -1246,7 +1528,9 @@ export async function runAgentLoopImpl(
1246
1528
  );
1247
1529
  const retryRepair = deepRepairHistory(runMessages);
1248
1530
  runMessages = retryRepair.messages;
1249
- preRepairMessages = retryRepair.messages;
1531
+ const retryStrip = stripHistoricalWebSearchResults(runMessages);
1532
+ runMessages = retryStrip.messages;
1533
+ preRepairMessages = runMessages;
1250
1534
  preRunHistoryLength = runMessages.length;
1251
1535
  state.orderingErrorDetected = false;
1252
1536
  state.deferredOrderingError = null;
@@ -1257,6 +1541,7 @@ export async function runAgentLoopImpl(
1257
1541
  abortController.signal,
1258
1542
  reqId,
1259
1543
  onCheckpoint,
1544
+ turnCallSite,
1260
1545
  );
1261
1546
 
1262
1547
  if (state.orderingErrorDetected) {
@@ -1286,6 +1571,14 @@ export async function runAgentLoopImpl(
1286
1571
 
1287
1572
  if (updatedHistory.length > preRunHistoryLength) {
1288
1573
  ctx.messages = stripInjectionsForCompaction(updatedHistory);
1574
+ try {
1575
+ clearPkbSystemReminderMetadataForConversation(ctx.conversationId);
1576
+ } catch (err) {
1577
+ rlog.warn(
1578
+ { err },
1579
+ "Failed to clear pkbSystemReminderBlock metadata after compaction strip (non-fatal)",
1580
+ );
1581
+ }
1289
1582
  convergenceStripped = true;
1290
1583
  preRepairMessages = updatedHistory;
1291
1584
  preRunHistoryLength = updatedHistory.length;
@@ -1298,14 +1591,19 @@ export async function runAgentLoopImpl(
1298
1591
  // message (e.g. "242201 tokens > 200000"), use it to correct the
1299
1592
  // compaction target. The estimator may significantly underestimate
1300
1593
  // (e.g. estimated 185k but actual was 242k), so using the
1301
- // uncorrected preflightBudget would still be too high.
1594
+ // uncorrected preflightBudget would still be too high. Passes the raw
1595
+ // error so ContextOverflowError.actualTokens can short-circuit the
1596
+ // string-regex path for proxy-rewrapped untyped errors.
1302
1597
  const actualTokens = parseActualTokensFromError(
1303
- state.contextTooLargeErrorMessage,
1598
+ state.contextTooLargeError,
1304
1599
  );
1305
1600
  const estimatedTokensAtOverflow = estimatePromptTokens(
1306
1601
  ctx.messages,
1307
1602
  ctx.systemPrompt,
1308
- { providerName: ctx.provider.name, toolTokenBudget },
1603
+ {
1604
+ providerName: estimationProviderName,
1605
+ toolTokenBudget,
1606
+ },
1309
1607
  );
1310
1608
  let correctedTarget = preflightBudget;
1311
1609
  if (actualTokens && estimatedTokensAtOverflow > 0) {
@@ -1353,9 +1651,9 @@ export async function runAgentLoopImpl(
1353
1651
  const step = await reduceContextOverflow(
1354
1652
  ctx.messages,
1355
1653
  {
1356
- providerName: ctx.provider.name,
1654
+ providerName: estimationProviderName,
1357
1655
  systemPrompt: ctx.systemPrompt,
1358
- contextWindow: config.contextWindow,
1656
+ contextWindow: config.llm.default.contextWindow,
1359
1657
  targetTokens: correctedTarget,
1360
1658
  toolTokenBudget,
1361
1659
  },
@@ -1369,6 +1667,21 @@ export async function runAgentLoopImpl(
1369
1667
  ctx.messages = step.messages;
1370
1668
  currentInjectionMode = step.state.injectionMode;
1371
1669
 
1670
+ // See the preflight reducer call above for rationale. Only track when
1671
+ // the summary LLM actually ran — `summaryFailed === undefined`
1672
+ // indicates the reducer's forced compaction took an early-return path
1673
+ // without calling the summary LLM.
1674
+ if (
1675
+ step.compactionResult &&
1676
+ step.compactionResult.summaryFailed !== undefined
1677
+ ) {
1678
+ trackCompactionOutcome(
1679
+ ctx,
1680
+ step.compactionResult.summaryFailed,
1681
+ onEvent,
1682
+ );
1683
+ }
1684
+
1372
1685
  if (step.compactionResult?.compacted) {
1373
1686
  ctx.contextCompactedMessageCount +=
1374
1687
  step.compactionResult.compactedPersistedMessages;
@@ -1412,23 +1725,36 @@ export async function runAgentLoopImpl(
1412
1725
  step.compactionResult.compactedPersistedMessages,
1413
1726
  );
1414
1727
  shouldInjectWorkspace = true;
1728
+ reducerCompacted = true;
1415
1729
  }
1416
1730
 
1417
1731
  // Only re-inject NOW.md when ctx.messages was actually stripped;
1418
1732
  // otherwise the existing NOW.md block is still present and
1419
1733
  // re-injecting would duplicate it.
1420
- runMessages = applyRuntimeInjections(ctx.messages, {
1734
+ const injection = await applyRuntimeInjections(ctx.messages, {
1421
1735
  ...injectionOpts,
1422
1736
  pkbContext: currentPkbContent,
1423
1737
  nowScratchpad: convergenceStripped ? currentNowContent : null,
1424
1738
  workspaceTopLevelContext: shouldInjectWorkspace
1425
1739
  ? ctx.workspaceTopLevelContext
1426
1740
  : null,
1741
+ slackChronologicalMessages: reducerCompacted
1742
+ ? null
1743
+ : injectionOpts.slackChronologicalMessages,
1427
1744
  mode: currentInjectionMode,
1428
1745
  });
1746
+ runMessages = injection.messages;
1429
1747
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1430
1748
  ctx.graphMemory.retrackCachedNodes();
1431
1749
  }
1750
+ const convergenceStrip = stripHistoricalWebSearchResults(runMessages);
1751
+ if (convergenceStrip.stats.blocksStripped > 0) {
1752
+ rlog.info(
1753
+ { phase: "convergence", ...convergenceStrip.stats },
1754
+ "Converted historical web_search_tool_result blocks to text summaries",
1755
+ );
1756
+ runMessages = convergenceStrip.messages;
1757
+ }
1432
1758
  preRepairMessages = runMessages;
1433
1759
  preRunHistoryLength = runMessages.length;
1434
1760
  state.contextTooLargeDetected = false;
@@ -1440,6 +1766,7 @@ export async function runAgentLoopImpl(
1440
1766
  abortController.signal,
1441
1767
  reqId,
1442
1768
  onCheckpoint,
1769
+ turnCallSite,
1443
1770
  );
1444
1771
 
1445
1772
  // If the rerun still yields at checkpoint, the turn is still
@@ -1461,6 +1788,14 @@ export async function runAgentLoopImpl(
1461
1788
  // pre-rerun messages.
1462
1789
  if (updatedHistory.length > preRunHistoryLength) {
1463
1790
  ctx.messages = stripInjectionsForCompaction(updatedHistory);
1791
+ try {
1792
+ clearPkbSystemReminderMetadataForConversation(ctx.conversationId);
1793
+ } catch (err) {
1794
+ rlog.warn(
1795
+ { err },
1796
+ "Failed to clear pkbSystemReminderBlock metadata after compaction strip (non-fatal)",
1797
+ );
1798
+ }
1464
1799
  convergenceStripped = true;
1465
1800
  preRepairMessages = updatedHistory;
1466
1801
  preRunHistoryLength = updatedHistory.length;
@@ -1496,8 +1831,18 @@ export async function runAgentLoopImpl(
1496
1831
  targetInputTokensOverride: correctedTarget,
1497
1832
  },
1498
1833
  );
1834
+ // Only track when the summary LLM actually ran; `force: true`
1835
+ // bypasses the cooldown but not the early-return paths.
1836
+ if (emergencyCompact.summaryFailed !== undefined) {
1837
+ trackCompactionOutcome(
1838
+ ctx,
1839
+ emergencyCompact.summaryFailed,
1840
+ onEvent,
1841
+ );
1842
+ }
1499
1843
  if (emergencyCompact.compacted) {
1500
1844
  ctx.messages = emergencyCompact.messages;
1845
+ reducerCompacted = true;
1501
1846
  ctx.contextCompactedMessageCount +=
1502
1847
  emergencyCompact.compactedPersistedMessages;
1503
1848
  ctx.contextCompactedAt = Date.now();
@@ -1544,18 +1889,30 @@ export async function runAgentLoopImpl(
1544
1889
 
1545
1890
  // Only re-inject NOW.md when ctx.messages was actually stripped;
1546
1891
  // otherwise the existing block is still present.
1547
- runMessages = applyRuntimeInjections(ctx.messages, {
1892
+ const injection = await applyRuntimeInjections(ctx.messages, {
1548
1893
  ...injectionOpts,
1549
1894
  pkbContext: currentPkbContent,
1550
1895
  nowScratchpad: convergenceStripped ? currentNowContent : null,
1551
1896
  workspaceTopLevelContext: shouldInjectWorkspace
1552
1897
  ? ctx.workspaceTopLevelContext
1553
1898
  : null,
1899
+ slackChronologicalMessages: reducerCompacted
1900
+ ? null
1901
+ : injectionOpts.slackChronologicalMessages,
1554
1902
  mode: currentInjectionMode,
1555
1903
  });
1904
+ runMessages = injection.messages;
1556
1905
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1557
1906
  ctx.graphMemory.retrackCachedNodes();
1558
1907
  }
1908
+ const emergencyStrip = stripHistoricalWebSearchResults(runMessages);
1909
+ if (emergencyStrip.stats.blocksStripped > 0) {
1910
+ rlog.info(
1911
+ { phase: "emergency_compact", ...emergencyStrip.stats },
1912
+ "Converted historical web_search_tool_result blocks to text summaries",
1913
+ );
1914
+ runMessages = emergencyStrip.messages;
1915
+ }
1559
1916
  preRepairMessages = runMessages;
1560
1917
  preRunHistoryLength = runMessages.length;
1561
1918
  state.contextTooLargeDetected = false;
@@ -1566,6 +1923,7 @@ export async function runAgentLoopImpl(
1566
1923
  abortController.signal,
1567
1924
  reqId,
1568
1925
  onCheckpoint,
1926
+ turnCallSite,
1569
1927
  );
1570
1928
  } else {
1571
1929
  // User denied compression — emit a graceful assistant explanation
@@ -1619,8 +1977,18 @@ export async function runAgentLoopImpl(
1619
1977
  targetInputTokensOverride: correctedTarget,
1620
1978
  },
1621
1979
  );
1980
+ // Only track when the summary LLM actually ran; `force: true`
1981
+ // bypasses the cooldown but not the early-return paths.
1982
+ if (emergencyCompact.summaryFailed !== undefined) {
1983
+ trackCompactionOutcome(
1984
+ ctx,
1985
+ emergencyCompact.summaryFailed,
1986
+ onEvent,
1987
+ );
1988
+ }
1622
1989
  if (emergencyCompact.compacted) {
1623
1990
  ctx.messages = emergencyCompact.messages;
1991
+ reducerCompacted = true;
1624
1992
  ctx.contextCompactedMessageCount +=
1625
1993
  emergencyCompact.compactedPersistedMessages;
1626
1994
  ctx.contextCompactedAt = Date.now();
@@ -1667,18 +2035,30 @@ export async function runAgentLoopImpl(
1667
2035
 
1668
2036
  // Only re-inject NOW.md when ctx.messages was actually stripped;
1669
2037
  // otherwise the existing block is still present.
1670
- runMessages = applyRuntimeInjections(ctx.messages, {
2038
+ const injection = await applyRuntimeInjections(ctx.messages, {
1671
2039
  ...injectionOpts,
1672
2040
  pkbContext: currentPkbContent,
1673
2041
  nowScratchpad: convergenceStripped ? currentNowContent : null,
1674
2042
  workspaceTopLevelContext: shouldInjectWorkspace
1675
2043
  ? ctx.workspaceTopLevelContext
1676
2044
  : null,
2045
+ slackChronologicalMessages: reducerCompacted
2046
+ ? null
2047
+ : injectionOpts.slackChronologicalMessages,
1677
2048
  mode: currentInjectionMode,
1678
2049
  });
2050
+ runMessages = injection.messages;
1679
2051
  if (isTrustedActor && currentInjectionMode !== "minimal") {
1680
2052
  ctx.graphMemory.retrackCachedNodes();
1681
2053
  }
2054
+ const fallbackStrip = stripHistoricalWebSearchResults(runMessages);
2055
+ if (fallbackStrip.stats.blocksStripped > 0) {
2056
+ rlog.info(
2057
+ { phase: "fail_gracefully_compact", ...fallbackStrip.stats },
2058
+ "Converted historical web_search_tool_result blocks to text summaries",
2059
+ );
2060
+ runMessages = fallbackStrip.messages;
2061
+ }
1682
2062
  preRepairMessages = runMessages;
1683
2063
  preRunHistoryLength = runMessages.length;
1684
2064
  state.contextTooLargeDetected = false;
@@ -1689,6 +2069,7 @@ export async function runAgentLoopImpl(
1689
2069
  abortController.signal,
1690
2070
  reqId,
1691
2071
  onCheckpoint,
2072
+ turnCallSite,
1692
2073
  );
1693
2074
  }
1694
2075
  // action === "fail_gracefully" falls through to the final error below
@@ -1863,7 +2244,7 @@ export async function runAgentLoopImpl(
1863
2244
  state.exchangeLlmCallCount,
1864
2245
  {
1865
2246
  tokens: state.lastCallInputTokens,
1866
- maxTokens: config.contextWindow.maxInputTokens,
2247
+ maxTokens: config.llm.default.contextWindow.maxInputTokens,
1867
2248
  },
1868
2249
  );
1869
2250
 
@@ -2095,6 +2476,7 @@ export async function runAgentLoopImpl(
2095
2476
  ctx.processing = false;
2096
2477
  ctx.onConfirmationOutcome = undefined;
2097
2478
  ctx.surfaceActionRequestIds.delete(ctx.currentRequestId ?? "");
2479
+ ctx.approvedViaPromptThisTurn = false;
2098
2480
  ctx.currentRequestId = undefined;
2099
2481
  ctx.currentActiveSurfaceId = undefined;
2100
2482
  ctx.allowedToolNames = undefined;