@vellumai/assistant 0.4.49 → 0.4.51

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 (353) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/integrations.md +2 -2
  4. package/docs/architecture/keychain-broker.md +6 -6
  5. package/docs/architecture/memory.md +180 -119
  6. package/knip.json +32 -0
  7. package/package.json +3 -2
  8. package/src/__tests__/agent-loop.test.ts +3 -1
  9. package/src/__tests__/anthropic-provider.test.ts +114 -23
  10. package/src/__tests__/approval-cascade.test.ts +1 -15
  11. package/src/__tests__/approval-routes-http.test.ts +2 -0
  12. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  13. package/src/__tests__/btw-routes.test.ts +61 -5
  14. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  15. package/src/__tests__/checker.test.ts +13 -0
  16. package/src/__tests__/config-schema.test.ts +1 -68
  17. package/src/__tests__/config-watcher.test.ts +8 -0
  18. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  19. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  20. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  21. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  22. package/src/__tests__/credential-security-invariants.test.ts +8 -7
  23. package/src/__tests__/credential-vault-unit.test.ts +23 -18
  24. package/src/__tests__/credential-vault.test.ts +30 -18
  25. package/src/__tests__/credentials-cli.test.ts +257 -82
  26. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  27. package/src/__tests__/date-context.test.ts +93 -77
  28. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  29. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  30. package/src/__tests__/history-repair.test.ts +245 -0
  31. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  32. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  33. package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
  34. package/src/__tests__/integration-status.test.ts +31 -30
  35. package/src/__tests__/invite-redemption-service.test.ts +166 -13
  36. package/src/__tests__/invite-routes-http.test.ts +166 -5
  37. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  38. package/src/__tests__/list-messages-attachments.test.ts +193 -0
  39. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  40. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  41. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  42. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  43. package/src/__tests__/memory-regressions.test.ts +477 -2841
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  45. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  46. package/src/__tests__/mime-builder.test.ts +28 -0
  47. package/src/__tests__/native-web-search.test.ts +1 -0
  48. package/src/__tests__/oauth-cli.test.ts +824 -31
  49. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  50. package/src/__tests__/oauth-store.test.ts +363 -17
  51. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  52. package/src/__tests__/registry.test.ts +0 -1
  53. package/src/__tests__/relay-server.test.ts +55 -1
  54. package/src/__tests__/schedule-tools.test.ts +32 -0
  55. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  56. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  57. package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
  58. package/src/__tests__/secure-keys.test.ts +78 -18
  59. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  60. package/src/__tests__/server-history-render.test.ts +2 -2
  61. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  62. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  63. package/src/__tests__/session-agent-loop.test.ts +19 -15
  64. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  65. package/src/__tests__/session-error.test.ts +124 -2
  66. package/src/__tests__/session-history-web-search.test.ts +918 -0
  67. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  68. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  69. package/src/__tests__/session-queue.test.ts +37 -27
  70. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  71. package/src/__tests__/session-slash-known.test.ts +1 -15
  72. package/src/__tests__/session-slash-queue.test.ts +1 -15
  73. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  74. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  75. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  76. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  77. package/src/__tests__/skills-install-extract.test.ts +93 -0
  78. package/src/__tests__/skills.test.ts +2 -2
  79. package/src/__tests__/skillssh-registry.test.ts +451 -0
  80. package/src/__tests__/slack-channel-config.test.ts +10 -8
  81. package/src/__tests__/trust-store.test.ts +15 -0
  82. package/src/__tests__/twilio-config.test.ts +11 -10
  83. package/src/__tests__/twilio-provider.test.ts +9 -4
  84. package/src/__tests__/voice-invite-redemption.test.ts +85 -5
  85. package/src/agent/ax-tree-compaction.test.ts +51 -0
  86. package/src/agent/loop.ts +39 -12
  87. package/src/approvals/AGENTS.md +1 -1
  88. package/src/approvals/guardian-request-resolvers.ts +14 -2
  89. package/src/bundler/compiler-tools.ts +66 -2
  90. package/src/calls/call-domain.ts +134 -3
  91. package/src/calls/call-store.ts +6 -0
  92. package/src/calls/relay-server.ts +44 -6
  93. package/src/calls/relay-setup-router.ts +17 -1
  94. package/src/calls/twilio-config.ts +5 -4
  95. package/src/calls/twilio-provider.ts +14 -9
  96. package/src/calls/twilio-rest.ts +10 -7
  97. package/src/calls/types.ts +3 -1
  98. package/src/cli/commands/config.ts +14 -9
  99. package/src/cli/commands/contacts.ts +3 -0
  100. package/src/cli/commands/credentials.ts +170 -174
  101. package/src/cli/commands/doctor.ts +11 -8
  102. package/src/cli/commands/keys.ts +9 -9
  103. package/src/cli/commands/mcp.ts +46 -59
  104. package/src/cli/commands/memory.ts +16 -165
  105. package/src/cli/commands/oauth/apps.ts +68 -10
  106. package/src/cli/commands/oauth/connections.ts +475 -105
  107. package/src/cli/commands/oauth/index.ts +3 -3
  108. package/src/cli/commands/oauth/providers.ts +18 -4
  109. package/src/cli/commands/sessions.ts +5 -2
  110. package/src/cli/commands/skills.ts +173 -1
  111. package/src/cli/http-client.ts +0 -20
  112. package/src/cli/main-screen.tsx +2 -2
  113. package/src/cli/program.ts +5 -6
  114. package/src/cli.ts +20 -22
  115. package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
  116. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  117. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  118. package/src/config/bundled-skills/contacts/SKILL.md +35 -11
  119. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  120. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  121. package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
  122. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
  123. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
  124. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
  125. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
  126. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
  127. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
  128. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
  129. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
  130. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
  131. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
  132. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
  133. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
  134. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
  135. package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
  136. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
  137. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
  138. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
  139. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
  140. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
  141. package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
  142. package/src/config/bundled-skills/messaging/SKILL.md +1 -1
  143. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
  144. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  145. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
  146. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
  148. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
  149. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
  150. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
  151. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
  152. package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
  153. package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
  154. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
  155. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
  156. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
  157. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
  158. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
  159. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
  160. package/src/config/bundled-tool-registry.ts +2 -5
  161. package/src/config/loader.ts +6 -42
  162. package/src/config/schema.ts +1 -12
  163. package/src/config/schemas/memory-lifecycle.ts +0 -9
  164. package/src/config/schemas/memory-processing.ts +0 -180
  165. package/src/config/schemas/memory-retrieval.ts +32 -104
  166. package/src/config/schemas/memory.ts +0 -10
  167. package/src/config/types.ts +0 -4
  168. package/src/contacts/contact-store.ts +39 -2
  169. package/src/contacts/contacts-write.ts +9 -0
  170. package/src/context/window-manager.ts +4 -1
  171. package/src/daemon/config-watcher.ts +55 -2
  172. package/src/daemon/daemon-control.ts +1 -1
  173. package/src/daemon/date-context.ts +114 -31
  174. package/src/daemon/handlers/config-ingress.ts +2 -2
  175. package/src/daemon/handlers/config-slack-channel.ts +59 -39
  176. package/src/daemon/handlers/config-telegram.ts +23 -14
  177. package/src/daemon/handlers/session-history.ts +1 -358
  178. package/src/daemon/handlers/sessions.ts +18 -13
  179. package/src/daemon/handlers/shared.ts +3 -17
  180. package/src/daemon/handlers/skills.ts +20 -1
  181. package/src/daemon/history-repair.ts +72 -8
  182. package/src/daemon/host-cu-proxy.ts +55 -26
  183. package/src/daemon/lifecycle.ts +39 -4
  184. package/src/daemon/mcp-reload-service.ts +2 -2
  185. package/src/daemon/message-types/computer-use.ts +1 -12
  186. package/src/daemon/message-types/memory.ts +4 -16
  187. package/src/daemon/message-types/messages.ts +1 -0
  188. package/src/daemon/message-types/sessions.ts +4 -42
  189. package/src/daemon/server.ts +6 -1
  190. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  191. package/src/daemon/session-agent-loop.ts +334 -48
  192. package/src/daemon/session-error.ts +89 -6
  193. package/src/daemon/session-history.ts +17 -7
  194. package/src/daemon/session-media-retry.ts +6 -2
  195. package/src/daemon/session-memory.ts +69 -149
  196. package/src/daemon/session-process.ts +10 -1
  197. package/src/daemon/session-runtime-assembly.ts +49 -19
  198. package/src/daemon/session-slash.ts +3 -5
  199. package/src/daemon/session-surfaces.ts +4 -1
  200. package/src/daemon/session-tool-setup.ts +7 -1
  201. package/src/daemon/session.ts +12 -2
  202. package/src/email/providers/index.ts +2 -2
  203. package/src/instrument.ts +61 -1
  204. package/src/media/avatar-router.ts +1 -1
  205. package/src/memory/admin.ts +2 -191
  206. package/src/memory/canonical-guardian-store.ts +38 -2
  207. package/src/memory/conversation-crud.ts +0 -33
  208. package/src/memory/conversation-queries.ts +25 -83
  209. package/src/memory/db-init.ts +32 -0
  210. package/src/memory/embedding-backend.ts +84 -8
  211. package/src/memory/embedding-types.ts +9 -1
  212. package/src/memory/indexer.ts +7 -46
  213. package/src/memory/invite-store.ts +19 -0
  214. package/src/memory/items-extractor.ts +274 -76
  215. package/src/memory/job-handlers/backfill.ts +2 -127
  216. package/src/memory/job-handlers/cleanup.ts +2 -16
  217. package/src/memory/job-handlers/extraction.ts +2 -138
  218. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  219. package/src/memory/job-handlers/summarization.ts +3 -148
  220. package/src/memory/job-utils.ts +21 -59
  221. package/src/memory/jobs-store.ts +1 -159
  222. package/src/memory/jobs-worker.ts +9 -52
  223. package/src/memory/migrations/104-core-indexes.ts +3 -3
  224. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  225. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  226. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  227. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  228. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  229. package/src/memory/migrations/154-drop-fts.ts +20 -0
  230. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  231. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  232. package/src/memory/migrations/157-invite-contact-id.ts +104 -0
  233. package/src/memory/migrations/index.ts +8 -0
  234. package/src/memory/migrations/registry.ts +6 -0
  235. package/src/memory/qdrant-client.ts +148 -51
  236. package/src/memory/raw-query.ts +1 -1
  237. package/src/memory/retriever.test.ts +294 -273
  238. package/src/memory/retriever.ts +421 -645
  239. package/src/memory/schema/calls.ts +2 -0
  240. package/src/memory/schema/contacts.ts +1 -0
  241. package/src/memory/schema/memory-core.ts +3 -48
  242. package/src/memory/schema/oauth.ts +2 -0
  243. package/src/memory/search/formatting.ts +263 -176
  244. package/src/memory/search/lexical.ts +1 -254
  245. package/src/memory/search/ranking.ts +0 -455
  246. package/src/memory/search/semantic.ts +100 -14
  247. package/src/memory/search/staleness.ts +47 -0
  248. package/src/memory/search/tier-classifier.ts +21 -0
  249. package/src/memory/search/types.ts +15 -77
  250. package/src/memory/task-memory-cleanup.ts +4 -6
  251. package/src/messaging/provider.ts +1 -1
  252. package/src/messaging/providers/gmail/adapter.ts +1 -1
  253. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  254. package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
  255. package/src/messaging/providers/whatsapp/adapter.ts +13 -9
  256. package/src/messaging/registry.ts +9 -5
  257. package/src/oauth/byo-connection.test.ts +40 -25
  258. package/src/oauth/connect-orchestrator.ts +4 -10
  259. package/src/oauth/connection-resolver.ts +20 -6
  260. package/src/oauth/manual-token-connection.ts +5 -5
  261. package/src/oauth/oauth-store.ts +183 -31
  262. package/src/oauth/platform-connection.test.ts +1 -1
  263. package/src/oauth/provider-behaviors.ts +503 -4
  264. package/src/oauth/seed-providers.ts +214 -8
  265. package/src/oauth/token-persistence.ts +31 -16
  266. package/src/permissions/defaults.ts +1 -0
  267. package/src/permissions/trust-store.ts +23 -1
  268. package/src/playbooks/playbook-compiler.ts +1 -1
  269. package/src/prompts/system-prompt.ts +18 -2
  270. package/src/providers/anthropic/client.ts +56 -126
  271. package/src/providers/types.ts +7 -1
  272. package/src/runtime/AGENTS.md +9 -0
  273. package/src/runtime/auth/route-policy.ts +6 -3
  274. package/src/runtime/channel-readiness-service.ts +48 -40
  275. package/src/runtime/guardian-reply-router.ts +24 -22
  276. package/src/runtime/http-server.ts +2 -2
  277. package/src/runtime/http-types.ts +2 -0
  278. package/src/runtime/invite-redemption-service.ts +72 -12
  279. package/src/runtime/invite-service.ts +43 -0
  280. package/src/runtime/middleware/twilio-validation.ts +1 -1
  281. package/src/runtime/pending-interactions.ts +2 -2
  282. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  283. package/src/runtime/routes/btw-routes.ts +10 -5
  284. package/src/runtime/routes/conversation-routes.ts +56 -11
  285. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  286. package/src/runtime/routes/integrations/slack/channel.ts +2 -2
  287. package/src/runtime/routes/integrations/telegram.ts +2 -2
  288. package/src/runtime/routes/integrations/twilio.ts +17 -17
  289. package/src/runtime/routes/invite-routes.ts +29 -4
  290. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  291. package/src/runtime/routes/memory-item-routes.ts +503 -0
  292. package/src/runtime/routes/secret-routes.ts +17 -0
  293. package/src/runtime/routes/session-management-routes.ts +3 -3
  294. package/src/runtime/routes/settings-routes.ts +3 -3
  295. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  296. package/src/runtime/routes/workspace-routes.ts +9 -4
  297. package/src/runtime/routes/workspace-utils.ts +8 -2
  298. package/src/schedule/integration-status.ts +26 -19
  299. package/src/security/keychain-broker-client.ts +17 -4
  300. package/src/security/oauth2.ts +6 -7
  301. package/src/security/secure-keys.ts +44 -19
  302. package/src/security/token-manager.ts +46 -39
  303. package/src/services/vercel-deploy.ts +0 -24
  304. package/src/signals/confirm.ts +78 -0
  305. package/src/signals/mcp-reload.ts +18 -0
  306. package/src/skills/catalog-install.ts +74 -18
  307. package/src/skills/skillssh-registry.ts +503 -0
  308. package/src/tools/assets/search.ts +5 -1
  309. package/src/tools/computer-use/definitions.ts +0 -10
  310. package/src/tools/computer-use/registry.ts +1 -1
  311. package/src/tools/credentials/vault.ts +22 -7
  312. package/src/tools/memory/definitions.ts +4 -13
  313. package/src/tools/memory/handlers.test.ts +83 -103
  314. package/src/tools/memory/handlers.ts +50 -85
  315. package/src/tools/network/script-proxy/session-manager.ts +8 -8
  316. package/src/tools/schedule/create.ts +10 -3
  317. package/src/tools/schedule/update.ts +8 -1
  318. package/src/tools/skills/load.ts +25 -2
  319. package/src/watcher/provider-types.ts +1 -1
  320. package/src/watcher/providers/github.ts +1 -1
  321. package/src/watcher/providers/gmail.ts +3 -3
  322. package/src/watcher/providers/google-calendar.ts +3 -3
  323. package/src/watcher/providers/linear.ts +1 -1
  324. package/src/__tests__/clarification-resolver.test.ts +0 -193
  325. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  326. package/src/__tests__/conflict-policy.test.ts +0 -269
  327. package/src/__tests__/conflict-store.test.ts +0 -372
  328. package/src/__tests__/contradiction-checker.test.ts +0 -361
  329. package/src/__tests__/entity-extractor.test.ts +0 -211
  330. package/src/__tests__/entity-search.test.ts +0 -1117
  331. package/src/__tests__/profile-compiler.test.ts +0 -392
  332. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  333. package/src/__tests__/session-profile-injection.test.ts +0 -557
  334. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  335. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  336. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  337. package/src/daemon/session-conflict-gate.ts +0 -167
  338. package/src/daemon/session-dynamic-profile.ts +0 -77
  339. package/src/memory/clarification-resolver.ts +0 -417
  340. package/src/memory/conflict-intent.ts +0 -205
  341. package/src/memory/conflict-policy.ts +0 -127
  342. package/src/memory/conflict-store.ts +0 -410
  343. package/src/memory/contradiction-checker.ts +0 -508
  344. package/src/memory/entity-extractor.ts +0 -535
  345. package/src/memory/format-recall.ts +0 -47
  346. package/src/memory/fts-reconciler.ts +0 -165
  347. package/src/memory/job-handlers/conflict.ts +0 -200
  348. package/src/memory/profile-compiler.ts +0 -195
  349. package/src/memory/recall-cache.ts +0 -117
  350. package/src/memory/search/entity.ts +0 -535
  351. package/src/memory/search/query-expansion.test.ts +0 -70
  352. package/src/memory/search/query-expansion.ts +0 -118
  353. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -13,6 +13,7 @@ import type {
13
13
  AgentEvent,
14
14
  AgentLoop,
15
15
  CheckpointDecision,
16
+ CheckpointInfo,
16
17
  } from "../agent/loop.js";
17
18
  import { createAssistantMessage } from "../agent/message-types.js";
18
19
  import type {
@@ -26,6 +27,10 @@ import { estimatePromptTokens } from "../context/token-estimator.js";
26
27
  import type { ContextWindowManager } from "../context/window-manager.js";
27
28
  import type { ToolProfiler } from "../events/tool-profiling-listener.js";
28
29
  import { getHookManager } from "../hooks/manager.js";
30
+ import {
31
+ clearSentrySessionContext,
32
+ setSentrySessionContext,
33
+ } from "../instrument.js";
29
34
  import { commitAppTurnChanges } from "../memory/app-git-service.js";
30
35
  import { getApp, listAppFiles } from "../memory/app-store.js";
31
36
  import {
@@ -68,7 +73,7 @@ import {
68
73
  } from "./context-overflow-reducer.js";
69
74
  import {
70
75
  buildTemporalContext,
71
- extractUserTimeZoneFromDynamicProfile,
76
+ extractUserTimeZoneFromRecall,
72
77
  } from "./date-context.js";
73
78
  import { deepRepairHistory, repairHistory } from "./history-repair.js";
74
79
  import type {
@@ -88,8 +93,6 @@ import {
88
93
  formatAttachmentWarnings,
89
94
  resolveAssistantAttachments,
90
95
  } from "./session-attachments.js";
91
- import type { ConflictGate } from "./session-conflict-gate.js";
92
- import { stripDynamicProfileMessages } from "./session-dynamic-profile.js";
93
96
  import {
94
97
  buildSessionErrorMessage,
95
98
  classifySessionError,
@@ -122,6 +125,40 @@ import type { TraceEmitter } from "./trace-emitter.js";
122
125
 
123
126
  const log = getLogger("session-agent-loop");
124
127
 
128
+ /**
129
+ * Parse the actual token count reported by the provider in a context-too-large
130
+ * error message. Providers typically include the prompt size, e.g.:
131
+ * "prompt is too long: 242201 tokens > 200000 maximum"
132
+ * "too many input tokens: 242201 > 200000"
133
+ *
134
+ * Returns the actual token count or null if it cannot be parsed.
135
+ */
136
+ function parseActualTokensFromError(
137
+ errorMessage: string | null,
138
+ ): number | null {
139
+ if (!errorMessage) return null;
140
+
141
+ // Match patterns like "242201 tokens > 200000" or "242201 > 200000 maximum"
142
+ const match = errorMessage.match(
143
+ /(\d[\d,]*)\s*tokens?\s*[>≥]|:\s*(\d[\d,]*)\s*[>≥]/i,
144
+ );
145
+ if (match) {
146
+ const raw = (match[1] || match[2]).replace(/,/g, "");
147
+ const parsed = parseInt(raw, 10);
148
+ if (!isNaN(parsed) && parsed > 0) return parsed;
149
+ }
150
+
151
+ // Fallback: match "too many input tokens: N > M"
152
+ const fallback = errorMessage.match(/(\d[\d,]*)\s*[>≥]\s*\d/);
153
+ if (fallback) {
154
+ const raw = fallback[1].replace(/,/g, "");
155
+ const parsed = parseInt(raw, 10);
156
+ if (!isNaN(parsed) && parsed > 0) return parsed;
157
+ }
158
+
159
+ return null;
160
+ }
161
+
125
162
  /** Title-cased friendly labels for tool names, used in confirmation chips. */
126
163
  const TOOL_FRIENDLY_LABEL: Record<string, string> = {
127
164
  bash: "Run Command",
@@ -165,7 +202,6 @@ export interface AgentLoopSessionContext {
165
202
  contextCompactedMessageCount: number;
166
203
  contextCompactedAt: number | null;
167
204
 
168
- readonly conflictGate: ConflictGate;
169
205
  readonly memoryPolicy: { scopeId: string; includeDefaultFallback: boolean };
170
206
 
171
207
  currentActiveSurfaceId?: string;
@@ -231,6 +267,7 @@ export interface AgentLoopSessionContext {
231
267
  | "tool_result_received"
232
268
  | "confirmation_requested"
233
269
  | "confirmation_resolved"
270
+ | "context_compacting"
234
271
  | "message_complete"
235
272
  | "generation_cancelled"
236
273
  | "error_terminal",
@@ -350,6 +387,18 @@ export async function runAgentLoopImpl(
350
387
  ctx.profiler.startRequest();
351
388
  let turnStarted = false;
352
389
 
390
+ // Populate Sentry scope with session-specific tags so any exception
391
+ // captured during this turn (e.g. inside agent/loop.ts) can be
392
+ // filtered by conversation, assistant, or user in the dashboard.
393
+ setSentrySessionContext({
394
+ assistantId: ctx.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
395
+ conversationId: ctx.conversationId,
396
+ messageCount: ctx.messages.length,
397
+ userIdentifier:
398
+ ctx.trustContext?.guardianPrincipalId ??
399
+ ctx.trustContext?.requesterExternalUserId,
400
+ });
401
+
353
402
  try {
354
403
  // Auto-complete stale interactive surfaces from previous turns.
355
404
  // Only dismiss when the user sends a new message (not a surface action
@@ -432,10 +481,9 @@ export async function runAgentLoopImpl(
432
481
  if (compactCheck.needed) {
433
482
  ctx.emitActivityState(
434
483
  "thinking",
435
- "thinking_delta",
484
+ "context_compacting",
436
485
  "assistant_turn",
437
486
  reqId,
438
- "Compacting context",
439
487
  );
440
488
  }
441
489
  const compacted = await ctx.contextWindowManager.maybeCompact(
@@ -528,12 +576,9 @@ export async function runAgentLoopImpl(
528
576
  messages: ctx.messages,
529
577
  systemPrompt: ctx.systemPrompt,
530
578
  provider: ctx.provider,
531
- conflictGate: ctx.conflictGate,
532
579
  scopeId: ctx.memoryPolicy.scopeId,
533
580
  includeDefaultFallback: ctx.memoryPolicy.includeDefaultFallback,
534
581
  trustClass: resolveTrustClass(ctx.trustContext),
535
- isInteractive:
536
- options?.isInteractive ?? (!ctx.hasNoClient && !ctx.headlessLock),
537
582
  },
538
583
  content,
539
584
  userMessageId,
@@ -541,7 +586,7 @@ export async function runAgentLoopImpl(
541
586
  onEvent,
542
587
  );
543
588
 
544
- const { recall, dynamicProfile, recallInjectionStrategy } = memoryResult;
589
+ const { recall } = memoryResult;
545
590
  runMessages = memoryResult.runMessages;
546
591
 
547
592
  // Build active surface context
@@ -574,16 +619,16 @@ export async function runAgentLoopImpl(
574
619
 
575
620
  // Compute fresh temporal context each turn for date grounding.
576
621
  // Absolute "now" is always anchored to assistant host clock, while local
577
- // date semantics prefer configured user timezone, then profile memory.
622
+ // date semantics prefer configured user timezone, then recalled memory.
578
623
  const hostTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
579
- const userTimeZone = extractUserTimeZoneFromDynamicProfile(
580
- dynamicProfile.text,
581
- );
582
624
  const configuredUserTimeZone = getConfig().ui.userTimezone ?? null;
625
+ const recalledUserTimeZone = extractUserTimeZoneFromRecall(
626
+ recall.injectedText,
627
+ );
583
628
  const temporalContext = buildTemporalContext({
584
629
  hostTimeZone,
585
630
  configuredUserTimeZone,
586
- userTimeZone,
631
+ userTimeZone: recalledUserTimeZone,
587
632
  });
588
633
 
589
634
  // Use the channel/interface context captured at the top of this function
@@ -656,7 +701,12 @@ export async function runAgentLoopImpl(
656
701
  const config = getConfig();
657
702
  const overflowRecovery = config.contextWindow.overflowRecovery;
658
703
  const providerMaxTokens = config.contextWindow.maxInputTokens;
659
- const safetyMargin = overflowRecovery.safetyMarginRatio;
704
+ // Widen safety margin for large conversations where estimation error
705
+ // compounds across many messages with tool results.
706
+ const baseSafetyMargin = overflowRecovery.safetyMarginRatio;
707
+ const messageCount = ctx.messages.length;
708
+ const safetyMargin =
709
+ messageCount > 50 ? Math.max(baseSafetyMargin, 0.15) : baseSafetyMargin;
660
710
  const preflightBudget = Math.floor(providerMaxTokens * (1 - safetyMargin));
661
711
  let reducerState: ReducerState | undefined;
662
712
 
@@ -686,10 +736,9 @@ export async function runAgentLoopImpl(
686
736
  preflightAttempts++;
687
737
  ctx.emitActivityState(
688
738
  "thinking",
689
- "thinking_delta",
739
+ "context_compacting",
690
740
  "assistant_turn",
691
741
  reqId,
692
- "Compacting context",
693
742
  );
694
743
  const step = await reduceContextOverflow(
695
744
  ctx.messages,
@@ -790,7 +839,9 @@ export async function runAgentLoopImpl(
790
839
  const eventHandler = (event: AgentEvent) =>
791
840
  dispatchAgentEvent(state, deps, event);
792
841
 
793
- const onCheckpoint = (): CheckpointDecision => {
842
+ let yieldedForBudget = false;
843
+
844
+ const onCheckpoint = (checkpoint: CheckpointInfo): CheckpointDecision => {
794
845
  const turnTools = state.currentTurnToolNames;
795
846
  state.currentTurnToolNames = [];
796
847
 
@@ -803,6 +854,27 @@ export async function runAgentLoopImpl(
803
854
  return "yield";
804
855
  }
805
856
  }
857
+
858
+ // Mid-loop token budget check: estimate current context size and
859
+ // yield if we're approaching the preflight budget. This lets the
860
+ // session-agent-loop run compaction before the provider rejects.
861
+ if (overflowRecovery.enabled) {
862
+ const midLoopThreshold = preflightBudget * 0.85;
863
+ const estimated = estimatePromptTokens(
864
+ checkpoint.history,
865
+ ctx.systemPrompt,
866
+ { providerName: ctx.provider.name },
867
+ );
868
+ if (estimated > midLoopThreshold) {
869
+ rlog.warn(
870
+ { phase: "mid-loop", estimated, threshold: midLoopThreshold },
871
+ "Token estimate approaching budget — yielding for compaction",
872
+ );
873
+ yieldedForBudget = true;
874
+ return "yield";
875
+ }
876
+ }
877
+
806
878
  return "continue";
807
879
  };
808
880
 
@@ -818,6 +890,109 @@ export async function runAgentLoopImpl(
818
890
  onCheckpoint,
819
891
  );
820
892
 
893
+ // ── Proactive mid-loop compaction ───────────────────────────────
894
+ // When the agent loop yielded because the token budget check in
895
+ // onCheckpoint detected approaching limits, run compaction on the
896
+ // accumulated history and re-enter the agent loop. This is distinct
897
+ // from the reactive convergence loop below that fires after a
898
+ // provider rejection — here we compact *before* hitting the limit.
899
+ let midLoopCompactAttempts = 0;
900
+ while (
901
+ yieldedForBudget &&
902
+ midLoopCompactAttempts < overflowRecovery.maxAttempts &&
903
+ !state.contextTooLargeDetected &&
904
+ !abortController.signal.aborted
905
+ ) {
906
+ midLoopCompactAttempts++;
907
+ yieldedForBudget = false;
908
+
909
+ rlog.info(
910
+ { phase: "mid-loop-compact" },
911
+ "Running compaction after checkpoint yield",
912
+ );
913
+
914
+ // Strip injected context from updated history before compacting,
915
+ // so we compact the "raw" persistent messages.
916
+ const rawHistory = stripInjectedContext(updatedHistory, {
917
+ stripRecall: (msgs) =>
918
+ stripMemoryRecallMessages(
919
+ msgs,
920
+ recall.injectedText,
921
+ "separate_context_message",
922
+ ),
923
+ });
924
+ ctx.messages = rawHistory;
925
+
926
+ ctx.emitActivityState(
927
+ "thinking",
928
+ "context_compacting",
929
+ "assistant_turn",
930
+ reqId,
931
+ "Compacting context",
932
+ );
933
+ const midLoopCompact = await ctx.contextWindowManager.maybeCompact(
934
+ ctx.messages,
935
+ abortController.signal,
936
+ {
937
+ lastCompactedAt: ctx.contextCompactedAt ?? undefined,
938
+ force: true,
939
+ targetInputTokensOverride: preflightBudget,
940
+ },
941
+ );
942
+ if (midLoopCompact.compacted) {
943
+ ctx.messages = midLoopCompact.messages;
944
+ ctx.contextCompactedMessageCount +=
945
+ midLoopCompact.compactedPersistedMessages;
946
+ ctx.contextCompactedAt = Date.now();
947
+ updateConversationContextWindow(
948
+ ctx.conversationId,
949
+ midLoopCompact.summaryText,
950
+ ctx.contextCompactedMessageCount,
951
+ );
952
+ onEvent({
953
+ type: "context_compacted",
954
+ previousEstimatedInputTokens:
955
+ midLoopCompact.previousEstimatedInputTokens,
956
+ estimatedInputTokens: midLoopCompact.estimatedInputTokens,
957
+ maxInputTokens: midLoopCompact.maxInputTokens,
958
+ thresholdTokens: midLoopCompact.thresholdTokens,
959
+ compactedMessages: midLoopCompact.compactedMessages,
960
+ summaryCalls: midLoopCompact.summaryCalls,
961
+ summaryInputTokens: midLoopCompact.summaryInputTokens,
962
+ summaryOutputTokens: midLoopCompact.summaryOutputTokens,
963
+ summaryModel: midLoopCompact.summaryModel,
964
+ });
965
+ emitUsage(
966
+ ctx,
967
+ midLoopCompact.summaryInputTokens,
968
+ midLoopCompact.summaryOutputTokens,
969
+ midLoopCompact.summaryModel,
970
+ onEvent,
971
+ "context_compactor",
972
+ reqId,
973
+ midLoopCompact.summaryCacheCreationInputTokens ?? 0,
974
+ midLoopCompact.summaryCacheReadInputTokens ?? 0,
975
+ collapseRawResponses(midLoopCompact.summaryRawResponses),
976
+ );
977
+ }
978
+
979
+ // Re-inject runtime context and re-enter the agent loop
980
+ runMessages = applyRuntimeInjections(ctx.messages, {
981
+ ...injectionOpts,
982
+ mode: currentInjectionMode,
983
+ });
984
+ preRepairMessages = runMessages;
985
+ preRunHistoryLength = runMessages.length;
986
+
987
+ updatedHistory = await ctx.agentLoop.run(
988
+ runMessages,
989
+ eventHandler,
990
+ abortController.signal,
991
+ reqId,
992
+ onCheckpoint,
993
+ );
994
+ }
995
+
821
996
  // One-shot ordering error retry
822
997
  if (
823
998
  state.orderingErrorDetected &&
@@ -855,14 +1030,58 @@ export async function runAgentLoopImpl(
855
1030
  // reducer tiers (forced compaction, tool-result truncation, media
856
1031
  // stubbing, injection downgrade) with optional approval gating for
857
1032
  // interactive latest-turn compression.
858
- if (
859
- state.contextTooLargeDetected &&
860
- updatedHistory.length === preRunHistoryLength
861
- ) {
1033
+ //
1034
+ // When progress was made (agent added messages before hitting the
1035
+ // limit), incorporate those new messages into ctx.messages so the
1036
+ // convergence loop operates on the full (larger) history.
1037
+ if (state.contextTooLargeDetected) {
1038
+ if (updatedHistory.length > preRunHistoryLength) {
1039
+ ctx.messages = stripInjectedContext(updatedHistory, {
1040
+ stripRecall: (msgs) =>
1041
+ stripMemoryRecallMessages(
1042
+ msgs,
1043
+ recall.injectedText,
1044
+ "separate_context_message",
1045
+ ),
1046
+ });
1047
+ preRepairMessages = updatedHistory;
1048
+ }
862
1049
  if (!reducerState) {
863
1050
  reducerState = createInitialReducerState();
864
1051
  }
865
1052
 
1053
+ // When the provider reveals the actual token count in its error
1054
+ // message (e.g. "242201 tokens > 200000"), use it to correct the
1055
+ // compaction target. The estimator may significantly underestimate
1056
+ // (e.g. estimated 185k but actual was 242k), so using the
1057
+ // uncorrected preflightBudget would still be too high.
1058
+ const actualTokens = parseActualTokensFromError(
1059
+ state.contextTooLargeErrorMessage,
1060
+ );
1061
+ const estimatedTokensAtOverflow = estimatePromptTokens(
1062
+ ctx.messages,
1063
+ ctx.systemPrompt,
1064
+ { providerName: ctx.provider.name },
1065
+ );
1066
+ let correctedTarget = preflightBudget;
1067
+ if (actualTokens && estimatedTokensAtOverflow > 0) {
1068
+ const estimationErrorRatio = actualTokens / estimatedTokensAtOverflow;
1069
+ if (estimationErrorRatio > 1.0) {
1070
+ correctedTarget = Math.floor(preflightBudget / estimationErrorRatio);
1071
+ rlog.warn(
1072
+ {
1073
+ phase: "convergence",
1074
+ actualTokens,
1075
+ estimatedTokens: estimatedTokensAtOverflow,
1076
+ estimationErrorRatio: estimationErrorRatio.toFixed(2),
1077
+ preflightBudget,
1078
+ correctedTarget,
1079
+ },
1080
+ "Adjusting compaction target based on observed estimation error",
1081
+ );
1082
+ }
1083
+ }
1084
+
866
1085
  let convergenceAttempts = 0;
867
1086
  const maxAttempts = overflowRecovery.maxAttempts;
868
1087
 
@@ -883,10 +1102,9 @@ export async function runAgentLoopImpl(
883
1102
 
884
1103
  ctx.emitActivityState(
885
1104
  "thinking",
886
- "thinking_delta",
1105
+ "context_compacting",
887
1106
  "assistant_turn",
888
1107
  reqId,
889
- "Compacting context",
890
1108
  );
891
1109
  const step = await reduceContextOverflow(
892
1110
  ctx.messages,
@@ -894,7 +1112,7 @@ export async function runAgentLoopImpl(
894
1112
  providerName: ctx.provider.name,
895
1113
  systemPrompt: ctx.systemPrompt,
896
1114
  contextWindow: config.contextWindow,
897
- targetTokens: preflightBudget,
1115
+ targetTokens: correctedTarget,
898
1116
  },
899
1117
  reducerState,
900
1118
  (msgs, signal, opts) =>
@@ -906,6 +1124,12 @@ export async function runAgentLoopImpl(
906
1124
  ctx.messages = step.messages;
907
1125
  currentInjectionMode = step.state.injectionMode;
908
1126
 
1127
+ // If the reducer is now exhausted without compacting, break out
1128
+ // so the overflow policy path can attempt emergency compaction.
1129
+ if (reducerState.exhausted && !step.compactionResult?.compacted) {
1130
+ break;
1131
+ }
1132
+
909
1133
  if (step.compactionResult?.compacted) {
910
1134
  ctx.contextCompactedMessageCount +=
911
1135
  step.compactionResult.compactedPersistedMessages;
@@ -959,6 +1183,75 @@ export async function runAgentLoopImpl(
959
1183
  );
960
1184
  }
961
1185
 
1186
+ // When all reducer tiers are exhausted but the context is still too
1187
+ // large, attempt one last emergency compaction before consulting the
1188
+ // overflow policy. This covers the case where progress was made
1189
+ // (messages grew) and the normal tiers couldn't compact enough.
1190
+ if (state.contextTooLargeDetected && reducerState.exhausted) {
1191
+ const emergencyCompact = await ctx.contextWindowManager.maybeCompact(
1192
+ ctx.messages,
1193
+ abortController.signal,
1194
+ {
1195
+ lastCompactedAt: ctx.contextCompactedAt ?? undefined,
1196
+ force: true,
1197
+ minKeepRecentUserTurns: 0,
1198
+ targetInputTokensOverride: correctedTarget,
1199
+ },
1200
+ );
1201
+ if (emergencyCompact.compacted) {
1202
+ ctx.messages = emergencyCompact.messages;
1203
+ ctx.contextCompactedMessageCount +=
1204
+ emergencyCompact.compactedPersistedMessages;
1205
+ ctx.contextCompactedAt = Date.now();
1206
+ updateConversationContextWindow(
1207
+ ctx.conversationId,
1208
+ emergencyCompact.summaryText,
1209
+ ctx.contextCompactedMessageCount,
1210
+ );
1211
+ onEvent({
1212
+ type: "context_compacted",
1213
+ previousEstimatedInputTokens:
1214
+ emergencyCompact.previousEstimatedInputTokens,
1215
+ estimatedInputTokens: emergencyCompact.estimatedInputTokens,
1216
+ maxInputTokens: emergencyCompact.maxInputTokens,
1217
+ thresholdTokens: emergencyCompact.thresholdTokens,
1218
+ compactedMessages: emergencyCompact.compactedMessages,
1219
+ summaryCalls: emergencyCompact.summaryCalls,
1220
+ summaryInputTokens: emergencyCompact.summaryInputTokens,
1221
+ summaryOutputTokens: emergencyCompact.summaryOutputTokens,
1222
+ summaryModel: emergencyCompact.summaryModel,
1223
+ });
1224
+ emitUsage(
1225
+ ctx,
1226
+ emergencyCompact.summaryInputTokens,
1227
+ emergencyCompact.summaryOutputTokens,
1228
+ emergencyCompact.summaryModel,
1229
+ onEvent,
1230
+ "context_compactor",
1231
+ reqId,
1232
+ emergencyCompact.summaryCacheCreationInputTokens ?? 0,
1233
+ emergencyCompact.summaryCacheReadInputTokens ?? 0,
1234
+ collapseRawResponses(emergencyCompact.summaryRawResponses),
1235
+ );
1236
+
1237
+ runMessages = applyRuntimeInjections(ctx.messages, {
1238
+ ...injectionOpts,
1239
+ mode: currentInjectionMode,
1240
+ });
1241
+ preRepairMessages = runMessages;
1242
+ preRunHistoryLength = runMessages.length;
1243
+ state.contextTooLargeDetected = false;
1244
+
1245
+ updatedHistory = await ctx.agentLoop.run(
1246
+ runMessages,
1247
+ eventHandler,
1248
+ abortController.signal,
1249
+ reqId,
1250
+ onCheckpoint,
1251
+ );
1252
+ }
1253
+ }
1254
+
962
1255
  // All reducer tiers exhausted but provider still rejects —
963
1256
  // consult the overflow policy for latest-turn compression.
964
1257
  if (state.contextTooLargeDetected) {
@@ -982,7 +1275,7 @@ export async function runAgentLoopImpl(
982
1275
  lastCompactedAt: ctx.contextCompactedAt ?? undefined,
983
1276
  force: true,
984
1277
  minKeepRecentUserTurns: 0,
985
- targetInputTokensOverride: preflightBudget,
1278
+ targetInputTokensOverride: correctedTarget,
986
1279
  },
987
1280
  );
988
1281
  if (emergencyCompact.compacted) {
@@ -1075,10 +1368,9 @@ export async function runAgentLoopImpl(
1075
1368
  // Non-interactive — auto-compress without asking
1076
1369
  ctx.emitActivityState(
1077
1370
  "thinking",
1078
- "thinking_delta",
1371
+ "context_compacting",
1079
1372
  "assistant_turn",
1080
1373
  reqId,
1081
- "Compacting context",
1082
1374
  );
1083
1375
  const emergencyCompact = await ctx.contextWindowManager.maybeCompact(
1084
1376
  ctx.messages,
@@ -1087,7 +1379,7 @@ export async function runAgentLoopImpl(
1087
1379
  lastCompactedAt: ctx.contextCompactedAt ?? undefined,
1088
1380
  force: true,
1089
1381
  minKeepRecentUserTurns: 0,
1090
- targetInputTokensOverride: preflightBudget,
1382
+ targetInputTokensOverride: correctedTarget,
1091
1383
  },
1092
1384
  );
1093
1385
  if (emergencyCompact.compacted) {
@@ -1154,19 +1446,6 @@ export async function runAgentLoopImpl(
1154
1446
  );
1155
1447
  onEvent(buildSessionErrorMessage(ctx.conversationId, classified));
1156
1448
  }
1157
- } else if (state.contextTooLargeDetected) {
1158
- // Progress was made (updatedHistory grew), so the retry path above was
1159
- // skipped. Surface the error so clients are not left with a silent failure.
1160
- rlog.warn(
1161
- { phase: "post_run" },
1162
- "Context too large after progress — surfacing error without retry",
1163
- );
1164
- const classified = classifySessionError(
1165
- new Error("context_length_exceeded"),
1166
- { phase: "agent_loop" },
1167
- );
1168
- onEvent(buildSessionErrorMessage(ctx.conversationId, classified));
1169
- state.providerErrorUserMessage = classified.userMessage;
1170
1449
  }
1171
1450
 
1172
1451
  if (state.deferredOrderingError) {
@@ -1280,10 +1559,8 @@ export async function runAgentLoopImpl(
1280
1559
  stripMemoryRecallMessages(
1281
1560
  msgs,
1282
1561
  recall.injectedText,
1283
- recallInjectionStrategy,
1562
+ "separate_context_message",
1284
1563
  ),
1285
- stripDynamicProfile: (msgs) =>
1286
- stripDynamicProfileMessages(msgs, dynamicProfile.text),
1287
1564
  });
1288
1565
 
1289
1566
  emitUsage(
@@ -1451,12 +1728,17 @@ export async function runAgentLoopImpl(
1451
1728
  const message = err instanceof Error ? err.message : String(err);
1452
1729
  const errorClass = err instanceof Error ? err.constructor.name : "Error";
1453
1730
  rlog.error({ err }, "Session processing error");
1731
+ const classified = classifySessionError(err, errorCtx);
1454
1732
  ctx.traceEmitter.emit("request_error", truncate(message, 200, ""), {
1455
1733
  requestId: reqId,
1456
1734
  status: "error",
1457
- attributes: { errorClass, message: truncate(message, 500, "") },
1735
+ attributes: {
1736
+ errorClass,
1737
+ message: truncate(message, 500, ""),
1738
+ errorCategory: classified.errorCategory,
1739
+ errorCode: classified.code,
1740
+ },
1458
1741
  });
1459
- const classified = classifySessionError(err, errorCtx);
1460
1742
  onEvent({ type: "error", message: classified.userMessage });
1461
1743
  onEvent(buildSessionErrorMessage(ctx.conversationId, classified));
1462
1744
  void getHookManager().trigger("on-error", {
@@ -1515,6 +1797,10 @@ export async function runAgentLoopImpl(
1515
1797
  }
1516
1798
 
1517
1799
  ctx.drainQueue(yieldedForHandoff ? "checkpoint_handoff" : "loop_complete");
1800
+
1801
+ // Clear session tags so they don't leak into unrelated error captures
1802
+ // (e.g. unhandledRejection from a different async chain).
1803
+ clearSentrySessionContext();
1518
1804
  }
1519
1805
  }
1520
1806