@superblocksteam/vite-plugin-file-sync 2.0.158 → 2.0.159-next.1

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 (337) hide show
  1. package/dist/ai-service/agent/middleware.d.ts +1 -2
  2. package/dist/ai-service/agent/middleware.d.ts.map +1 -1
  3. package/dist/ai-service/agent/middleware.js +2 -3
  4. package/dist/ai-service/agent/middleware.js.map +1 -1
  5. package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
  6. package/dist/ai-service/agent/prompts/build-base-system-prompt.js +5 -4
  7. package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
  8. package/dist/ai-service/agent/tool-message-utils.d.ts.map +1 -1
  9. package/dist/ai-service/agent/tool-message-utils.js +16 -1
  10. package/dist/ai-service/agent/tool-message-utils.js.map +1 -1
  11. package/dist/ai-service/agent/tool-result-metrics.d.ts +29 -0
  12. package/dist/ai-service/agent/tool-result-metrics.d.ts.map +1 -0
  13. package/dist/ai-service/agent/tool-result-metrics.js +162 -0
  14. package/dist/ai-service/agent/tool-result-metrics.js.map +1 -0
  15. package/dist/ai-service/agent/tools/apis/test-api.d.ts +50 -1
  16. package/dist/ai-service/agent/tools/apis/test-api.d.ts.map +1 -1
  17. package/dist/ai-service/agent/tools/apis/test-api.js +132 -10
  18. package/dist/ai-service/agent/tools/apis/test-api.js.map +1 -1
  19. package/dist/ai-service/agent/tools/build-delete-file.d.ts +1 -2
  20. package/dist/ai-service/agent/tools/build-delete-file.d.ts.map +1 -1
  21. package/dist/ai-service/agent/tools/build-delete-file.js +2 -0
  22. package/dist/ai-service/agent/tools/build-delete-file.js.map +1 -1
  23. package/dist/ai-service/agent/tools/build-edit-file.d.ts +1 -2
  24. package/dist/ai-service/agent/tools/build-edit-file.d.ts.map +1 -1
  25. package/dist/ai-service/agent/tools/build-edit-file.js +2 -0
  26. package/dist/ai-service/agent/tools/build-edit-file.js.map +1 -1
  27. package/dist/ai-service/agent/tools/build-install-packages.d.ts +1 -0
  28. package/dist/ai-service/agent/tools/build-install-packages.d.ts.map +1 -1
  29. package/dist/ai-service/agent/tools/build-install-packages.js +123 -16
  30. package/dist/ai-service/agent/tools/build-install-packages.js.map +1 -1
  31. package/dist/ai-service/agent/tools/build-lookup-npm-package.d.ts.map +1 -1
  32. package/dist/ai-service/agent/tools/build-lookup-npm-package.js +4 -16
  33. package/dist/ai-service/agent/tools/build-lookup-npm-package.js.map +1 -1
  34. package/dist/ai-service/agent/tools/build-manage-checklist.d.ts +11 -7
  35. package/dist/ai-service/agent/tools/build-manage-checklist.d.ts.map +1 -1
  36. package/dist/ai-service/agent/tools/build-manage-checklist.js +18 -9
  37. package/dist/ai-service/agent/tools/build-manage-checklist.js.map +1 -1
  38. package/dist/ai-service/agent/tools/build-multi-edit-file.d.ts +1 -2
  39. package/dist/ai-service/agent/tools/build-multi-edit-file.d.ts.map +1 -1
  40. package/dist/ai-service/agent/tools/build-multi-edit-file.js +2 -0
  41. package/dist/ai-service/agent/tools/build-multi-edit-file.js.map +1 -1
  42. package/dist/ai-service/agent/tools/build-read-file.d.ts.map +1 -1
  43. package/dist/ai-service/agent/tools/build-read-file.js +31 -18
  44. package/dist/ai-service/agent/tools/build-read-file.js.map +1 -1
  45. package/dist/ai-service/agent/tools/build-rename-file.d.ts +1 -2
  46. package/dist/ai-service/agent/tools/build-rename-file.d.ts.map +1 -1
  47. package/dist/ai-service/agent/tools/build-rename-file.js +2 -0
  48. package/dist/ai-service/agent/tools/build-rename-file.js.map +1 -1
  49. package/dist/ai-service/agent/tools/build-write-file.d.ts +1 -2
  50. package/dist/ai-service/agent/tools/build-write-file.d.ts.map +1 -1
  51. package/dist/ai-service/agent/tools/build-write-file.js +2 -0
  52. package/dist/ai-service/agent/tools/build-write-file.js.map +1 -1
  53. package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts +3 -3
  54. package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts.map +1 -1
  55. package/dist/ai-service/agent/tools/databases/dev-database-tasks.js +5 -7
  56. package/dist/ai-service/agent/tools/databases/dev-database-tasks.js.map +1 -1
  57. package/dist/ai-service/agent/tools/debug-cache.d.ts +16 -14
  58. package/dist/ai-service/agent/tools/debug-cache.d.ts.map +1 -1
  59. package/dist/ai-service/agent/tools/debug-cache.js +200 -42
  60. package/dist/ai-service/agent/tools/debug-cache.js.map +1 -1
  61. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +13 -13
  62. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts.map +1 -1
  63. package/dist/ai-service/agent/tools/integrations/execute-request.js +52 -20
  64. package/dist/ai-service/agent/tools/integrations/execute-request.js.map +1 -1
  65. package/dist/ai-service/agent/tools/integrations/sql-classification.d.ts +7 -1
  66. package/dist/ai-service/agent/tools/integrations/sql-classification.d.ts.map +1 -1
  67. package/dist/ai-service/agent/tools/integrations/sql-classification.js +229 -11
  68. package/dist/ai-service/agent/tools/integrations/sql-classification.js.map +1 -1
  69. package/dist/ai-service/agent/tools/page-edit-guidance.d.ts +2 -0
  70. package/dist/ai-service/agent/tools/page-edit-guidance.d.ts.map +1 -0
  71. package/dist/ai-service/agent/tools/page-edit-guidance.js +11 -0
  72. package/dist/ai-service/agent/tools/page-edit-guidance.js.map +1 -0
  73. package/dist/ai-service/agent/tools/report-security-findings.d.ts +5 -5
  74. package/dist/ai-service/agent/tools.d.ts +32 -0
  75. package/dist/ai-service/agent/tools.d.ts.map +1 -1
  76. package/dist/ai-service/agent/tools.js +54 -33
  77. package/dist/ai-service/agent/tools.js.map +1 -1
  78. package/dist/ai-service/agent/tools2/registry.d.ts +1 -1
  79. package/dist/ai-service/agent/tools2/registry.d.ts.map +1 -1
  80. package/dist/ai-service/agent/tools2/registry.js +21 -4
  81. package/dist/ai-service/agent/tools2/registry.js.map +1 -1
  82. package/dist/ai-service/agent/tools2/tools/end-test-run.d.ts +2 -2
  83. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.d.ts.map +1 -1
  84. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js +20 -11
  85. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js.map +1 -1
  86. package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.d.ts +4 -0
  87. package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.d.ts.map +1 -1
  88. package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.js +28 -8
  89. package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.js.map +1 -1
  90. package/dist/ai-service/agent/tools2/tools/update-test-case-status.d.ts +2 -2
  91. package/dist/ai-service/app-interface/cancellable-exec.d.ts +34 -0
  92. package/dist/ai-service/app-interface/cancellable-exec.d.ts.map +1 -0
  93. package/dist/ai-service/app-interface/cancellable-exec.js +98 -0
  94. package/dist/ai-service/app-interface/cancellable-exec.js.map +1 -0
  95. package/dist/ai-service/app-interface/shell.d.ts +20 -3
  96. package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
  97. package/dist/ai-service/app-interface/shell.js +139 -65
  98. package/dist/ai-service/app-interface/shell.js.map +1 -1
  99. package/dist/ai-service/attachment-upload.d.ts.map +1 -1
  100. package/dist/ai-service/attachment-upload.js +4 -1
  101. package/dist/ai-service/attachment-upload.js.map +1 -1
  102. package/dist/ai-service/attachments/parse-data-url.d.ts +5 -0
  103. package/dist/ai-service/attachments/parse-data-url.d.ts.map +1 -0
  104. package/dist/ai-service/attachments/parse-data-url.js +14 -0
  105. package/dist/ai-service/attachments/parse-data-url.js.map +1 -0
  106. package/dist/ai-service/chat/chat-session-store.d.ts +0 -8
  107. package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
  108. package/dist/ai-service/chat/chat-session-store.js +34 -611
  109. package/dist/ai-service/chat/chat-session-store.js.map +1 -1
  110. package/dist/ai-service/chat/utils.d.ts +2 -6
  111. package/dist/ai-service/chat/utils.d.ts.map +1 -1
  112. package/dist/ai-service/chat/utils.js +2 -95
  113. package/dist/ai-service/chat/utils.js.map +1 -1
  114. package/dist/ai-service/index.d.ts +3 -2
  115. package/dist/ai-service/index.d.ts.map +1 -1
  116. package/dist/ai-service/index.js +138 -48
  117. package/dist/ai-service/index.js.map +1 -1
  118. package/dist/ai-service/integrations/mention-safe-tag-name.d.ts +12 -0
  119. package/dist/ai-service/integrations/mention-safe-tag-name.d.ts.map +1 -0
  120. package/dist/ai-service/integrations/mention-safe-tag-name.js +18 -0
  121. package/dist/ai-service/integrations/mention-safe-tag-name.js.map +1 -0
  122. package/dist/ai-service/integrations/store.d.ts +9 -0
  123. package/dist/ai-service/integrations/store.d.ts.map +1 -1
  124. package/dist/ai-service/integrations/store.js +11 -1
  125. package/dist/ai-service/integrations/store.js.map +1 -1
  126. package/dist/ai-service/llm/client.d.ts.map +1 -1
  127. package/dist/ai-service/llm/client.js +67 -4
  128. package/dist/ai-service/llm/client.js.map +1 -1
  129. package/dist/ai-service/llm/context-v2/adapter.d.ts +1 -0
  130. package/dist/ai-service/llm/context-v2/adapter.d.ts.map +1 -1
  131. package/dist/ai-service/llm/context-v2/adapter.js +3 -0
  132. package/dist/ai-service/llm/context-v2/adapter.js.map +1 -1
  133. package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts +7 -0
  134. package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts.map +1 -1
  135. package/dist/ai-service/llm/context-v2/compaction/client-side.js +28 -6
  136. package/dist/ai-service/llm/context-v2/compaction/client-side.js.map +1 -1
  137. package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts +12 -0
  138. package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts.map +1 -1
  139. package/dist/ai-service/llm/context-v2/compactor.d.ts +8 -0
  140. package/dist/ai-service/llm/context-v2/compactor.d.ts.map +1 -1
  141. package/dist/ai-service/llm/context-v2/compactor.js +15 -3
  142. package/dist/ai-service/llm/context-v2/compactor.js.map +1 -1
  143. package/dist/ai-service/llm/context-v2/config.d.ts.map +1 -1
  144. package/dist/ai-service/llm/context-v2/config.js +1 -0
  145. package/dist/ai-service/llm/context-v2/config.js.map +1 -1
  146. package/dist/ai-service/llm/context-v2/context-metrics.d.ts +44 -1
  147. package/dist/ai-service/llm/context-v2/context-metrics.d.ts.map +1 -1
  148. package/dist/ai-service/llm/context-v2/context-metrics.js +97 -12
  149. package/dist/ai-service/llm/context-v2/context-metrics.js.map +1 -1
  150. package/dist/ai-service/llm/context-v2/context.d.ts +125 -0
  151. package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
  152. package/dist/ai-service/llm/context-v2/context.js +478 -14
  153. package/dist/ai-service/llm/context-v2/context.js.map +1 -1
  154. package/dist/ai-service/llm/context-v2/conversation-context.d.ts +12 -0
  155. package/dist/ai-service/llm/context-v2/conversation-context.d.ts.map +1 -1
  156. package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.d.ts.map +1 -1
  157. package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.js +2 -1
  158. package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.js.map +1 -1
  159. package/dist/ai-service/llm/context-v2/phase2-session-summarizer.js +3 -3
  160. package/dist/ai-service/llm/context-v2/phase2-session-summarizer.js.map +1 -1
  161. package/dist/ai-service/llm/context-v2/token-tracker.d.ts +49 -1
  162. package/dist/ai-service/llm/context-v2/token-tracker.d.ts.map +1 -1
  163. package/dist/ai-service/llm/context-v2/token-tracker.js +119 -0
  164. package/dist/ai-service/llm/context-v2/token-tracker.js.map +1 -1
  165. package/dist/ai-service/llm/context-v2/truncation-fallback.d.ts.map +1 -1
  166. package/dist/ai-service/llm/context-v2/truncation-fallback.js +2 -1
  167. package/dist/ai-service/llm/context-v2/truncation-fallback.js.map +1 -1
  168. package/dist/ai-service/llm/context-v2/types.d.ts +9 -0
  169. package/dist/ai-service/llm/context-v2/types.d.ts.map +1 -1
  170. package/dist/ai-service/llm/context-v2/types.js.map +1 -1
  171. package/dist/ai-service/llm/context-v2/well-formed-text.d.ts +24 -0
  172. package/dist/ai-service/llm/context-v2/well-formed-text.d.ts.map +1 -0
  173. package/dist/ai-service/llm/context-v2/well-formed-text.js +73 -0
  174. package/dist/ai-service/llm/context-v2/well-formed-text.js.map +1 -0
  175. package/dist/ai-service/llm/error.d.ts +22 -1
  176. package/dist/ai-service/llm/error.d.ts.map +1 -1
  177. package/dist/ai-service/llm/error.js +159 -22
  178. package/dist/ai-service/llm/error.js.map +1 -1
  179. package/dist/ai-service/llm/stream/empty-tool-call-tracker.d.ts +4 -3
  180. package/dist/ai-service/llm/stream/empty-tool-call-tracker.d.ts.map +1 -1
  181. package/dist/ai-service/llm/stream/empty-tool-call-tracker.js +5 -4
  182. package/dist/ai-service/llm/stream/empty-tool-call-tracker.js.map +1 -1
  183. package/dist/ai-service/llm/stream/idle-monitor.d.ts.map +1 -1
  184. package/dist/ai-service/llm/stream/idle-monitor.js +5 -0
  185. package/dist/ai-service/llm/stream/idle-monitor.js.map +1 -1
  186. package/dist/ai-service/llm/stream/observers/logging.d.ts +6 -0
  187. package/dist/ai-service/llm/stream/observers/logging.d.ts.map +1 -1
  188. package/dist/ai-service/llm/stream/observers/logging.js +44 -3
  189. package/dist/ai-service/llm/stream/observers/logging.js.map +1 -1
  190. package/dist/ai-service/llm/stream/orchestrator.d.ts.map +1 -1
  191. package/dist/ai-service/llm/stream/orchestrator.js +28 -4
  192. package/dist/ai-service/llm/stream/orchestrator.js.map +1 -1
  193. package/dist/ai-service/llm/stream/retry-engine.d.ts +1 -1
  194. package/dist/ai-service/llm/stream/retry-engine.d.ts.map +1 -1
  195. package/dist/ai-service/llm/stream/retry-engine.js +9 -1
  196. package/dist/ai-service/llm/stream/retry-engine.js.map +1 -1
  197. package/dist/ai-service/llm/stream/session.d.ts +6 -0
  198. package/dist/ai-service/llm/stream/session.d.ts.map +1 -1
  199. package/dist/ai-service/llm/stream/session.js +6 -0
  200. package/dist/ai-service/llm/stream/session.js.map +1 -1
  201. package/dist/ai-service/llm/stream/terminal-reason.d.ts.map +1 -1
  202. package/dist/ai-service/llm/stream/terminal-reason.js +11 -1
  203. package/dist/ai-service/llm/stream/terminal-reason.js.map +1 -1
  204. package/dist/ai-service/llm/stream/wait-state-tracker.d.ts +4 -3
  205. package/dist/ai-service/llm/stream/wait-state-tracker.d.ts.map +1 -1
  206. package/dist/ai-service/llm/stream/wait-state-tracker.js +12 -8
  207. package/dist/ai-service/llm/stream/wait-state-tracker.js.map +1 -1
  208. package/dist/ai-service/policy-agent-metrics.d.ts +2 -1
  209. package/dist/ai-service/policy-agent-metrics.d.ts.map +1 -1
  210. package/dist/ai-service/policy-agent-metrics.js +11 -2
  211. package/dist/ai-service/policy-agent-metrics.js.map +1 -1
  212. package/dist/ai-service/policy-gate-error.d.ts +3 -0
  213. package/dist/ai-service/policy-gate-error.d.ts.map +1 -0
  214. package/dist/ai-service/policy-gate-error.js +33 -0
  215. package/dist/ai-service/policy-gate-error.js.map +1 -0
  216. package/dist/ai-service/resolve-generation-state-for-ui.js +1 -1
  217. package/dist/ai-service/resolve-generation-state-for-ui.js.map +1 -1
  218. package/dist/ai-service/security/secret-scanner-service.d.ts +3 -6
  219. package/dist/ai-service/security/secret-scanner-service.d.ts.map +1 -1
  220. package/dist/ai-service/security/secret-scanner-service.js +229 -97
  221. package/dist/ai-service/security/secret-scanner-service.js.map +1 -1
  222. package/dist/ai-service/security/secret-scanner.d.ts +28 -5
  223. package/dist/ai-service/security/secret-scanner.d.ts.map +1 -1
  224. package/dist/ai-service/security/secret-scanner.js +197 -42
  225. package/dist/ai-service/security/secret-scanner.js.map +1 -1
  226. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts +1 -1
  227. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts.map +1 -1
  228. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js +49 -0
  229. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js.map +1 -1
  230. package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts +1 -1
  231. package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts.map +1 -1
  232. package/dist/ai-service/skills/system/third-party-migration/skill.generated.js +1 -0
  233. package/dist/ai-service/skills/system/third-party-migration/skill.generated.js.map +1 -1
  234. package/dist/ai-service/state-machine/clark-fsm.d.ts +2 -0
  235. package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
  236. package/dist/ai-service/state-machine/clark-fsm.js +1 -1
  237. package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
  238. package/dist/ai-service/state-machine/handlers/agent-planning.d.ts +1 -1
  239. package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
  240. package/dist/ai-service/state-machine/handlers/agent-planning.js +74 -3
  241. package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
  242. package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
  243. package/dist/ai-service/state-machine/handlers/llm-generating.js +1 -11
  244. package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
  245. package/dist/ai-service/state-machine/helpers/policy-gate-usage-metrics.d.ts +3 -2
  246. package/dist/ai-service/state-machine/helpers/policy-gate-usage-metrics.d.ts.map +1 -1
  247. package/dist/ai-service/state-machine/helpers/policy-gate-usage-metrics.js +25 -5
  248. package/dist/ai-service/state-machine/helpers/policy-gate-usage-metrics.js.map +1 -1
  249. package/dist/ai-service/state-machine/helpers/prompt-billing-metrics.d.ts.map +1 -1
  250. package/dist/ai-service/state-machine/helpers/prompt-billing-metrics.js +1 -0
  251. package/dist/ai-service/state-machine/helpers/prompt-billing-metrics.js.map +1 -1
  252. package/dist/ai-service/state-machine/helpers/stable-peer.d.ts +4 -0
  253. package/dist/ai-service/state-machine/helpers/stable-peer.d.ts.map +1 -1
  254. package/dist/ai-service/state-machine/helpers/stable-peer.js +6 -1
  255. package/dist/ai-service/state-machine/helpers/stable-peer.js.map +1 -1
  256. package/dist/ai-service/test-utils/metrics-harness.d.ts +6 -0
  257. package/dist/ai-service/test-utils/metrics-harness.d.ts.map +1 -1
  258. package/dist/ai-service/test-utils/metrics-harness.js.map +1 -1
  259. package/dist/ai-service/transform/api-builder/shared.d.ts.map +1 -1
  260. package/dist/ai-service/transform/api-builder/shared.js +2 -1
  261. package/dist/ai-service/transform/api-builder/shared.js.map +1 -1
  262. package/dist/dev-server-ws-smoke-client.d.ts.map +1 -1
  263. package/dist/dev-server-ws-smoke-client.js +1 -0
  264. package/dist/dev-server-ws-smoke-client.js.map +1 -1
  265. package/dist/file-sync-vite-plugin.d.ts +21 -0
  266. package/dist/file-sync-vite-plugin.d.ts.map +1 -1
  267. package/dist/file-sync-vite-plugin.js +131 -11
  268. package/dist/file-sync-vite-plugin.js.map +1 -1
  269. package/dist/file-system-helpers.d.ts +2 -2
  270. package/dist/file-system-helpers.d.ts.map +1 -1
  271. package/dist/file-system-helpers.js +2 -12
  272. package/dist/file-system-helpers.js.map +1 -1
  273. package/dist/migration-templates/app-fullstack/.gitignore +50 -0
  274. package/dist/parsing/jsx.d.ts +12 -0
  275. package/dist/parsing/jsx.d.ts.map +1 -1
  276. package/dist/parsing/jsx.js +30 -12
  277. package/dist/parsing/jsx.js.map +1 -1
  278. package/dist/parsing/page.d.ts.map +1 -1
  279. package/dist/parsing/page.js +16 -8
  280. package/dist/parsing/page.js.map +1 -1
  281. package/dist/parsing/parse-tsx-module.d.ts +4 -0
  282. package/dist/parsing/parse-tsx-module.d.ts.map +1 -0
  283. package/dist/parsing/parse-tsx-module.js +13 -0
  284. package/dist/parsing/parse-tsx-module.js.map +1 -0
  285. package/dist/plugin-options.d.ts +4 -0
  286. package/dist/plugin-options.d.ts.map +1 -1
  287. package/dist/plugin-options.js.map +1 -1
  288. package/dist/policy-gate-callback-mapper.d.ts +2 -1
  289. package/dist/policy-gate-callback-mapper.d.ts.map +1 -1
  290. package/dist/policy-gate-callback-mapper.js +3 -5
  291. package/dist/policy-gate-callback-mapper.js.map +1 -1
  292. package/dist/policy-gate-run-registry.d.ts +4 -2
  293. package/dist/policy-gate-run-registry.d.ts.map +1 -1
  294. package/dist/policy-gate-run-registry.js +17 -2
  295. package/dist/policy-gate-run-registry.js.map +1 -1
  296. package/dist/policy-gate-runner.d.ts +1 -1
  297. package/dist/policy-gate-runner.d.ts.map +1 -1
  298. package/dist/policy-gate-runner.js +41 -7
  299. package/dist/policy-gate-runner.js.map +1 -1
  300. package/dist/router-parser-metrics.d.ts +2 -0
  301. package/dist/router-parser-metrics.d.ts.map +1 -0
  302. package/dist/router-parser-metrics.js +13 -0
  303. package/dist/router-parser-metrics.js.map +1 -0
  304. package/dist/router-parser.d.ts +6 -1
  305. package/dist/router-parser.d.ts.map +1 -1
  306. package/dist/router-parser.js +72 -17
  307. package/dist/router-parser.js.map +1 -1
  308. package/dist/scan-scope-merge.d.ts +1 -1
  309. package/dist/scan-scope-merge.js +1 -1
  310. package/dist/source-tracker.d.ts.map +1 -1
  311. package/dist/source-tracker.js +2 -6
  312. package/dist/source-tracker.js.map +1 -1
  313. package/dist/sync-service/download.d.ts.map +1 -1
  314. package/dist/sync-service/download.js +10 -2
  315. package/dist/sync-service/download.js.map +1 -1
  316. package/dist/sync-service/index.d.ts +5 -0
  317. package/dist/sync-service/index.d.ts.map +1 -1
  318. package/dist/sync-service/index.js +66 -14
  319. package/dist/sync-service/index.js.map +1 -1
  320. package/dist/sync-service/list-dir.d.ts.map +1 -1
  321. package/dist/sync-service/list-dir.js +8 -4
  322. package/dist/sync-service/list-dir.js.map +1 -1
  323. package/dist/sync-service/snapshot/upload-snapshot.d.ts.map +1 -1
  324. package/dist/sync-service/snapshot/upload-snapshot.js +20 -0
  325. package/dist/sync-service/snapshot/upload-snapshot.js.map +1 -1
  326. package/dist/util/log-sanitizer.d.ts.map +1 -1
  327. package/dist/util/log-sanitizer.js +10 -8
  328. package/dist/util/log-sanitizer.js.map +1 -1
  329. package/dist/util/logger.d.ts +2 -0
  330. package/dist/util/logger.d.ts.map +1 -1
  331. package/dist/util/logger.js +357 -38
  332. package/dist/util/logger.js.map +1 -1
  333. package/dist/util.d.ts +3 -1
  334. package/dist/util.d.ts.map +1 -1
  335. package/dist/util.js +2 -2
  336. package/dist/util.js.map +1 -1
  337. package/package.json +9 -9
@@ -1,17 +1,18 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { performance } from "node:perf_hooks";
3
3
  import { setImmediate } from "node:timers/promises";
4
- import { getPrefixedLogger } from "../../../util/logger.js";
4
+ import { getErrorMeta, getPrefixedLogger } from "../../../util/logger.js";
5
5
  import { LLMObsTracer } from "../../llmobs/tracer.js";
6
6
  import { findBreakpoints } from "./caching.js";
7
7
  import { SERVER_COMPACTION_INTERRUPTED_SUMMARY, SERVER_COMPACTION_SUMMARY, ServerSideCompactionStrategy, } from "./compaction/server-side.js";
8
- import { DEFAULT_CACHING, resolveContextOptionsV2 } from "./config.js";
9
- import { recordContextPersist, recordServerCompaction, recordTurnsSinceLastCompaction, } from "./context-metrics.js";
8
+ import { compactionThresholdTokens, DEFAULT_CACHING, resolveContextOptionsV2, } from "./config.js";
9
+ import { recordContextPersist, recordPreflightCompaction, recordServerCompaction, recordTurnsSinceLastCompaction, } from "./context-metrics.js";
10
10
  import { OperationQueue } from "./operation-queue.js";
11
11
  import { CLIENT_COMPACTION_MARKER } from "./storage/event-types.js";
12
- import { getTotalInputTokens } from "./token-tracker.js";
12
+ import { estimateMessagesTokens, getTotalInputTokens, } from "./token-tracker.js";
13
13
  import { toStoredMessage, toSdkMessages, CACHE_BREAKPOINT_PROVIDER_OPTIONS, normalizeToolMessage, stripUndefinedFromToolOutputs, repairBrokenToolCalls, repairOrphanedToolResults, enforceToolUsePairing, normalizeReasoningProviderOptions, stripUnsignedReasoning, stripTrailingAssistantPrefill, extractTokenUsage, } from "./types.js";
14
14
  import { uniquifyFilePartFilenames } from "./uniquify-filenames.js";
15
+ import { makeJsonSafeValue } from "./well-formed-text.js";
15
16
  const logger = getPrefixedLogger("[context-v2]");
16
17
  /**
17
18
  * Persists slower than this get a warning log with the message count.
@@ -21,6 +22,71 @@ const logger = getPrefixedLogger("[context-v2]");
21
22
  * Raise or remove once per-turn full-file rewrites are addressed.
22
23
  */
23
24
  const SLOW_PERSIST_WARN_MS = 500;
25
+ /**
26
+ * Deadline for the pre-flight oversize compaction pass.
27
+ *
28
+ * Sits just above the summarizer's own per-call budget
29
+ * (`DEFAULT_COMPACTION_SUMMARY_TIMEOUT_MS`, 240s) so the deadline stops firing
30
+ * before the work it is waiting on can even finish.
31
+ *
32
+ * Picked from production rather than from taste. Measured over the four weeks
33
+ * to 2026-09-01, client-side compaction
34
+ * (`superblocks.clark.context.server_compaction.duration_seconds`, filtered to
35
+ * `path:client`) ran 6,690 times with a mean of 36.8s, an hourly-worst-case p99
36
+ * of 238.7s, and a peak of 382.8s. The earlier 120s value sat under the p90 of
37
+ * the hourly maxima, so it abandoned passes that would have succeeded on
38
+ * exactly the largest histories, which are the ones this check exists to
39
+ * rescue. Even 250s does not cover the peak; a pass past it degrades to the
40
+ * provider rejecting the request, reported as `context_too_large`.
41
+ *
42
+ * A longer deadline is only affordable because of the latch. Without
43
+ * `oversizeCompactionExhausted` a doomed pass would be restarted on every
44
+ * prompt, so the wait would be paid per message rather than once per
45
+ * conversation.
46
+ *
47
+ * `compact()` accepts no abort signal, so a pass past its deadline is abandoned
48
+ * rather than cancelled and keeps running (and billing) in the background.
49
+ * `oversizeCompactionSettled` is what keeps a sibling pass out while that
50
+ * orphan is live; raising this value without real cancellation lengthens the
51
+ * orphan window it has to cover.
52
+ */
53
+ export const PREFLIGHT_COMPACTION_TIMEOUT_MS = 250_000;
54
+ /** Thrown by `withDeadline` so a slow pass can be told apart from a broken one. */
55
+ class DeadlineExceededError extends Error {
56
+ }
57
+ /**
58
+ * Resolve `promise`, or reject once `timeoutMs` elapses.
59
+ *
60
+ * Deliberately local rather than reusing `withRpcTimeout`: that helper logs
61
+ * "RPC timed out", which would misdirect anyone triaging a summarizer stall.
62
+ */
63
+ async function withDeadline(promise, timeoutMs) {
64
+ let timeoutId;
65
+ try {
66
+ return await Promise.race([
67
+ promise,
68
+ new Promise((_resolve, reject) => {
69
+ timeoutId = setTimeout(() => reject(new DeadlineExceededError(`compaction did not settle within ${timeoutMs}ms`)), timeoutMs);
70
+ }),
71
+ ]);
72
+ }
73
+ finally {
74
+ if (timeoutId !== undefined) {
75
+ clearTimeout(timeoutId);
76
+ }
77
+ }
78
+ }
79
+ /**
80
+ * Which family a compaction strategy belongs to.
81
+ *
82
+ * Read by instance rather than from a field on the interface: there are exactly
83
+ * two implementations, `ServerSideCompactionStrategy` is already imported here,
84
+ * and a `kind` member would have to be added to every strategy and every test
85
+ * mock to answer the same question.
86
+ */
87
+ function compactionStrategyKind(strategy) {
88
+ return strategy instanceof ServerSideCompactionStrategy ? "server" : "client";
89
+ }
24
90
  /** Messages replayed between explicit event-loop yields in initializeWith. */
25
91
  const REPLAY_YIELD_INTERVAL = 100;
26
92
  /**
@@ -163,6 +229,49 @@ export class ContextV2 {
163
229
  // When disposed, this context may still be referenced by in-flight streams.
164
230
  // Keep it inert so stale generations cannot persist cleared history.
165
231
  isDisposed = false;
232
+ /** Shared by concurrent callers so only one oversize pass ever runs. */
233
+ oversizeCompactionInFlight;
234
+ /**
235
+ * Largest `extraInputTokens` any caller attached to the in-flight pass.
236
+ *
237
+ * Callers sharing a pass can have sized different tool surfaces, and the
238
+ * shared promise would otherwise silently decide on whichever one happened to
239
+ * start it. Max fails safe: under-counting skips a compaction the request
240
+ * needed, over-counting only runs one it could have skipped. Read when the
241
+ * queued callback actually runs, so anything attaching before then counts.
242
+ *
243
+ * Defensive rather than an observed bug: both callers have to be on the SAME
244
+ * ContextV2, and the nested one-shot path uses `context: { mode: "stateless" }`,
245
+ * so it never shares this instance.
246
+ */
247
+ oversizeCompactionExtraTokens = 0;
248
+ /**
249
+ * The summarizer call itself, unbounded by the deadline the caller waits on.
250
+ * `compactIfOversized` holds the in-flight guard until this settles, so an
251
+ * abandoned `compact()` cannot run alongside a fresh pass.
252
+ */
253
+ oversizeCompactionSettled;
254
+ /**
255
+ * Set once a pass has ended with the payload STILL over the threshold, for
256
+ * any reason: the strategy declined, the pass threw, the deadline rejected,
257
+ * or it ran and reclaimed too little. While set, `compactIfOversized` returns
258
+ * immediately and starts no new paid pass.
259
+ *
260
+ * The restore-time version of this check got the same protection for free,
261
+ * because `ContextManagerV2` only invoked it while a pending flag was set and
262
+ * a pass that tried and failed never re-flagged. Moving the check onto every
263
+ * `streamText` dropped that, so a summarizer that hangs would stall every
264
+ * later prompt for the full deadline and stack concurrent billable
265
+ * summarizations over the same history. The provider rejecting an oversized
266
+ * request takes ~130ms; this took two minutes per prompt. Worse than the
267
+ * wedge it exists to fix.
268
+ *
269
+ * Deliberately NOT cleared when a pass succeeds and the payload now fits, so
270
+ * later regrowth can still be rescued. Reset only in `setCompactionStrategy`:
271
+ * a different strategy is the one real change that could produce a different
272
+ * outcome, and New Chat resets it for free by constructing a fresh instance.
273
+ */
274
+ oversizeCompactionExhausted = false;
166
275
  // Persists currently mid-flight. A persist that has already passed the
167
276
  // dispose guard is committed to writing the snapshot, so `dispose()` alone
168
277
  // cannot stop it; `drainPersists()` lets a caller that is about to overwrite
@@ -218,9 +327,23 @@ export class ContextV2 {
218
327
  setCompactionStrategy(strategy) {
219
328
  if (this.compaction === strategy)
220
329
  return;
330
+ const previousKind = compactionStrategyKind(this.compaction);
221
331
  this.compaction = strategy;
222
332
  this.compaction.updateConfig(this.config);
223
333
  this.compaction.setCallbacks(this.compactionCallbacks);
334
+ // Only a change of KIND clears the latch, not any swap.
335
+ //
336
+ // `buildCompactionStrategy` returns a fresh instance every time and the
337
+ // manager calls this on every managed turn, so the reference guard above
338
+ // never short-circuits in production. Clearing on any swap therefore
339
+ // cleared the latch on every single prompt, which made it do nothing and
340
+ // put the per-prompt stall straight back. Kind is the thing that actually
341
+ // changes the answer: the `ai.server-context-management.options` kill
342
+ // switch flipping a latched server-side context over to a client-side one
343
+ // is a genuine second chance, another client instance is not.
344
+ if (compactionStrategyKind(strategy) !== previousKind) {
345
+ this.oversizeCompactionExhausted = false;
346
+ }
224
347
  }
225
348
  setMetricIdentity(identity) {
226
349
  this.metricIdentity = identity;
@@ -669,6 +792,327 @@ export class ContextV2 {
669
792
  }
670
793
  });
671
794
  }
795
+ /**
796
+ * Compact this conversation when the request about to be sent is already too
797
+ * large for the model, BEFORE sending it.
798
+ *
799
+ * A long session can grow its persisted history past the model's input limit
800
+ * without any single live request crossing the compaction threshold: the
801
+ * per-request prompt stays small while storage keeps accumulating. When a
802
+ * dev-server restart or warm-pool recycle then restores that history in full,
803
+ * the first request is rejected by the provider before it reports any usage.
804
+ * Since `afterResponse` is the only compaction trigger and it needs
805
+ * provider-reported usage, compaction can never fire again, every retry
806
+ * replays the same oversized payload, and the app is wedged permanently
807
+ * (ENG-5282).
808
+ *
809
+ * Breaking that cycle needs a check that does not depend on the provider, so
810
+ * the size is estimated from the payload's own content. The estimate only
811
+ * gates the decision. The compaction pipeline, thresholds, and phase
812
+ * selection are the existing ones, so behavior below the threshold is
813
+ * unchanged.
814
+ *
815
+ * Call this from where the request is assembled, after the system prompt is
816
+ * set and the turn is appended: the estimate then covers the array that is
817
+ * actually going on the wire, the UI compaction callbacks are already
818
+ * attached so the user sees the summarization, and a concurrent eviction is
819
+ * handled by the same `isDisposed` guard as the per-response trigger.
820
+ *
821
+ * At most one pass runs at a time, and a pass that ends with the payload
822
+ * still over the threshold latches the context so no later turn starts
823
+ * another paid attempt. See `oversizeCompactionExhausted`.
824
+ *
825
+ * @param extraInputTokens tokens the request carries that are not in the
826
+ * message array, principally the tool-definition schemas. The caller sizes
827
+ * them because it is the layer that holds them; this class stays free of
828
+ * any dependency on the agent tool types. Measured against the five
829
+ * captured production sessions, the estimate reads 0.19x to 1.23x of
830
+ * provider-reported usage, and tool schemas are the largest identified
831
+ * part of that shortfall. Omitting this is exactly today's behavior.
832
+ * Accepts a thunk so the caller's sizing work (schema conversion and
833
+ * serialization of every tool) is skipped entirely when the kill switch is
834
+ * off or the context is latched; a plain number is evaluated by the caller
835
+ * regardless.
836
+ * @returns `true` only when a pass ran and the payload now fits. `false`
837
+ * when it already fit (the common case), when no strategy could act, when
838
+ * the pass failed or timed out, or when it ran and reclaimed too little.
839
+ */
840
+ async compactIfOversized(extraInputTokens = 0) {
841
+ if (this.isDisposed ||
842
+ this.messages.length === 0 ||
843
+ !this.config.preflightCompactionEnabled) {
844
+ return false;
845
+ }
846
+ if (this.oversizeCompactionExhausted) {
847
+ // The metric is anonymous by design (identity is never a label), so this
848
+ // line is what lets oncall find WHICH session is stuck when the `latched`
849
+ // count rises. One line per prompt on a wedged session is the cost.
850
+ logger.warn("Skipping oversize compaction: an earlier pass on this context already ended over the threshold", this.metricIdentity);
851
+ recordPreflightCompaction({
852
+ outcome: "latched",
853
+ durationMs: 0,
854
+ identity: this.metricIdentity,
855
+ });
856
+ return false;
857
+ }
858
+ const extraTokens = typeof extraInputTokens === "function"
859
+ ? extraInputTokens()
860
+ : extraInputTokens;
861
+ // One pass at a time, and concurrent callers share it rather than stacking
862
+ // paid summarizations over the same history. A later caller may have sized
863
+ // a larger tool surface, so the pass decides on the largest figure attached
864
+ // to it rather than whichever caller happened to start it.
865
+ if (this.oversizeCompactionInFlight) {
866
+ this.oversizeCompactionExtraTokens = Math.max(this.oversizeCompactionExtraTokens, extraTokens);
867
+ return this.oversizeCompactionInFlight;
868
+ }
869
+ // An abandoned summarizer from a timed-out pass is still running and still
870
+ // billing. Refuse to start a second one over the same history.
871
+ if (this.oversizeCompactionSettled) {
872
+ logger.warn("Skipping oversize compaction: a previous pass outlived its deadline and is still running", this.metricIdentity);
873
+ recordPreflightCompaction({
874
+ outcome: "skipped",
875
+ durationMs: 0,
876
+ identity: this.metricIdentity,
877
+ });
878
+ return false;
879
+ }
880
+ this.oversizeCompactionExtraTokens = extraTokens;
881
+ const pass = this.queue.enqueue(() => this.runOversizeCompaction(this.oversizeCompactionExtraTokens));
882
+ this.oversizeCompactionInFlight = pass;
883
+ void pass
884
+ .catch(() => undefined)
885
+ .finally(() => {
886
+ this.oversizeCompactionInFlight = undefined;
887
+ });
888
+ return pass;
889
+ }
890
+ async runOversizeCompaction(extraInputTokens) {
891
+ const startedAt = performance.now();
892
+ let outcome = "failed";
893
+ // Set once `this.messages` holds the compacted window. Anything that throws
894
+ // after that point (the event-log append, persist bookkeeping) happened to
895
+ // a pass that succeeded, so it must not latch the context.
896
+ let committed = false;
897
+ try {
898
+ // Estimated inside the queued callback, not before it. An earlier queued
899
+ // pass may already have shrunk the history, and pairing a stale
900
+ // pre-compaction estimate with the new messages would hand the strategy
901
+ // an inconsistent (messages, usage) pair and skew its phase selection.
902
+ //
903
+ // Sizes the wire payload rather than `this.messages`: `buildMessages`
904
+ // prepends the system prompt and strips `_superblocks`, so this is what
905
+ // the provider will actually receive. The estimate can throw on a
906
+ // malformed message (`JSON.stringify` rejects a BigInt), which the
907
+ // surrounding catch absorbs: this method promises never to be the reason
908
+ // a request fails.
909
+ const payload = await this.buildMessages();
910
+ // `extraInputTokens` is the rest of the request the provider charges for
911
+ // and the message array does not carry, principally the tool schemas.
912
+ // Folded in here rather than compared separately so the synthetic usage
913
+ // handed to `afterResponse` is the same total this gate decided on;
914
+ // passing a messages-only figure would make the strategy's own threshold
915
+ // check disagree with ours and decline the pass we just asked for.
916
+ const estimatedTokens = estimateMessagesTokens(payload) + extraInputTokens;
917
+ const threshold = compactionThresholdTokens(this.config);
918
+ if (estimatedTokens <= threshold) {
919
+ outcome = "fit";
920
+ return false;
921
+ }
922
+ logger.warn(`Request payload is oversized: ~${estimatedTokens} estimated tokens > ${threshold} threshold ` +
923
+ `(${this.messages.length} messages); compacting before sending`, this.metricIdentity);
924
+ // Input-only synthetic usage: `computeContextUsed` measures the input
925
+ // window, and a non-zero output count would inflate it.
926
+ //
927
+ // Bounded, because a summarizer that HANGS rather than throws would
928
+ // otherwise block this turn forever. `OperationQueue` is a bare promise
929
+ // chain with no cancellation, so it would also stall every later
930
+ // operation on this context. The deadline is generous: abandoning a
931
+ // compaction that would have worked puts us back at the wedge, so waiting
932
+ // is the cheaper mistake. An abandoned call may still settle in the
933
+ // background; it can no longer hold up the request.
934
+ //
935
+ // `buildMessages()` above primed `_cachedMessages` with the payload this
936
+ // pass is about to replace. `getMessages` serves that cache without
937
+ // joining the queue, so a reader that arrives while the summarizer runs
938
+ // would otherwise be handed the oversized pre-compaction snapshot. Drop
939
+ // it: the reader then queues behind this pass and gets the result. The
940
+ // common fit path returned above with the cache intact, so the request
941
+ // that follows still gets its cache hit.
942
+ this.invalidateMessageCache();
943
+ const settled = this.compaction.afterResponse({
944
+ messages: this.messages,
945
+ usage: {
946
+ inputTokens: estimatedTokens,
947
+ outputTokens: 0,
948
+ totalTokens: estimatedTokens,
949
+ },
950
+ usageIsEstimate: true,
951
+ });
952
+ // Tracked separately from the deadline the caller waits on. `compact()`
953
+ // takes no abort signal, so a pass past its deadline is abandoned rather
954
+ // than cancelled and keeps running and billing; this marker is what stops
955
+ // the queue admitting a sibling pass alongside that orphan. It clears
956
+ // when the SUMMARIZER settles, whether that is before or after the
957
+ // deadline, so the normal path releases it without the caller waiting.
958
+ //
959
+ // The exhausted latch stops a second pass after a timeout on its own, so
960
+ // this covers the one case that clears the latch mid-orphan, a
961
+ // `setCompactionStrategy` swap. It narrows the orphan window rather than
962
+ // closing it; closing it needs an `abortSignal` on `compact()`.
963
+ this.oversizeCompactionSettled = settled;
964
+ // An orphan's eventual outcome is otherwise invisible: the metric already
965
+ // recorded `timed_out` and the request went out without it. These lines
966
+ // are how oncall tells "settled two minutes late" from "still running an
967
+ // hour later".
968
+ let timedOut = false;
969
+ void settled
970
+ .then(() => {
971
+ if (timedOut) {
972
+ logger.warn(`Oversize compaction pass settled ${Math.round(performance.now() - startedAt)}ms after it started, past its deadline; its result was discarded`, this.metricIdentity);
973
+ }
974
+ }, (err) => {
975
+ if (timedOut) {
976
+ logger.warn(`Oversize compaction pass failed after outliving its deadline: ${err}`, this.metricIdentity);
977
+ }
978
+ })
979
+ .finally(() => {
980
+ if (this.oversizeCompactionSettled === settled) {
981
+ this.oversizeCompactionSettled = undefined;
982
+ }
983
+ });
984
+ let result;
985
+ try {
986
+ result = await withDeadline(settled, PREFLIGHT_COMPACTION_TIMEOUT_MS);
987
+ }
988
+ catch (err) {
989
+ if (err instanceof DeadlineExceededError) {
990
+ timedOut = true;
991
+ outcome = "timed_out";
992
+ }
993
+ throw err;
994
+ }
995
+ if (!result.messages) {
996
+ // No strategy could act, or the strategy declined. Either way the
997
+ // provider is left to reject the request, now labelled
998
+ // `context_too_large`.
999
+ this.oversizeCompactionExhausted = true;
1000
+ outcome = "discarded";
1001
+ logger.warn("Oversize compaction produced no changes; payload remains too large to send", this.metricIdentity);
1002
+ return false;
1003
+ }
1004
+ // Measured BEFORE anything is mutated or written, because a compaction is
1005
+ // irreversible once persisted and a pass can come back with the window
1006
+ // essentially unchanged (phase 1 finds nothing prunable and phase 2
1007
+ // declines).
1008
+ //
1009
+ // Three outcomes, not two. The trigger threshold is
1010
+ // `contextLimit - maxOutputTokens - contextBufferTokens`, so 120k on a
1011
+ // 188k model, and tool schemas are irreducible: compaction cannot shrink
1012
+ // them, so `messages_after + extraInputTokens` can sit above the trigger
1013
+ // threshold while being comfortably sendable. Discarding there would
1014
+ // throw away a real shrink and send the original oversized history
1015
+ // instead, which is strictly worse.
1016
+ //
1017
+ // Sendability is measured against `contextLimit - contextBufferTokens`
1018
+ // rather than bare `contextLimit`. `maxOutputTokens` is a generation
1019
+ // reservation, not input the provider charges against the payload, so it
1020
+ // does not belong in a can-this-be-sent bound. The buffer does stay:
1021
+ // against the five captured production sessions this estimator reads
1022
+ // 0.19x to 1.23x of provider-reported usage, so a margin below the hard
1023
+ // limit is doing real work.
1024
+ const candidate = this.markPreCompactionBaselines(result.messages);
1025
+ const remainingTokens = estimateMessagesTokens(await this.buildMessages(candidate)) +
1026
+ extraInputTokens;
1027
+ const sendableLimit = this.config.contextLimit - this.config.contextBufferTokens;
1028
+ if (remainingTokens > sendableLimit) {
1029
+ // Still unsendable. Keep the original history, which at least leaves
1030
+ // the conversation recoverable, and stop paying for the same answer.
1031
+ this.oversizeCompactionExhausted = true;
1032
+ outcome = "discarded";
1033
+ // `compact()` fires onCompactionStart and onCompactionComplete before
1034
+ // it returns, so the user has already been shown "Summarized past
1035
+ // conversation" for work we are about to throw away. Retract it, or the
1036
+ // UI claims a summarization happened while the request goes out with
1037
+ // the original history and gets rejected. Only phase 2 needs this:
1038
+ // `LLMClient.createContext` drops phase-1 callbacks before they render.
1039
+ if (result.phase2MessageId) {
1040
+ this.compactionCallbacks?.onCompactionCancel?.("phase2", result.phase2MessageId);
1041
+ }
1042
+ logger.warn(`Oversize compaction ran but the payload is still unsendable ` +
1043
+ `(~${remainingTokens} estimated tokens > ${sendableLimit} sendable limit, ` +
1044
+ `${estimatedTokens} before); discarding it and leaving the request to the provider`, this.metricIdentity);
1045
+ return false;
1046
+ }
1047
+ // Over the compaction threshold but under the limit, so it will send.
1048
+ // Persist it and do NOT latch: real progress was made, and the normal
1049
+ // usage-driven `afterResponse` can carry on from here with the provider's
1050
+ // own numbers instead of this estimate.
1051
+ const underThreshold = remainingTokens <= threshold;
1052
+ if (!underThreshold) {
1053
+ logger.info(`Oversize compaction reduced the payload to a sendable size but not under the ` +
1054
+ `compaction threshold (~${remainingTokens} estimated tokens, ${estimatedTokens} before, ` +
1055
+ `${threshold} threshold, ${sendableLimit} sendable limit)`, this.metricIdentity);
1056
+ }
1057
+ // Re-checked after an await that can last the full deadline. `dispose()`
1058
+ // marks the instance inert so stale generations cannot resurrect cleared
1059
+ // history, and the disk paths already honour it (`runPersist` and
1060
+ // `appendCompactionWindow` both bail on `isDisposed`), but assigning
1061
+ // `this.messages` here would put the pre-dispose conversation back in
1062
+ // memory on a context New Chat or a checkpoint restore just cleared.
1063
+ if (this.isDisposed) {
1064
+ outcome = "discarded";
1065
+ if (result.phase2MessageId) {
1066
+ this.compactionCallbacks?.onCompactionCancel?.("phase2", result.phase2MessageId);
1067
+ }
1068
+ logger.warn("Discarding an oversize compaction result: the context was disposed while it ran", this.metricIdentity);
1069
+ return false;
1070
+ }
1071
+ this.messages = candidate;
1072
+ committed = true;
1073
+ this.invalidateMessageCache();
1074
+ if (result.skillsNeedReload) {
1075
+ this.skillsNeedReload = true;
1076
+ this.readSystemSkills.clear();
1077
+ }
1078
+ if (result.eventLogAnchor) {
1079
+ await this.appendCompactionWindow(this.messages);
1080
+ }
1081
+ await this.persist("compaction");
1082
+ outcome = underThreshold ? "compacted" : "progressed";
1083
+ return underThreshold;
1084
+ }
1085
+ catch (err) {
1086
+ // Never rethrown, and latched so it is never retried: a summarizer that
1087
+ // threw or blew the deadline would stall every later request for the same
1088
+ // deadline and fail the same way, which is worse than the wedge. Degrade
1089
+ // to letting the provider reject the request, now labelled
1090
+ // `context_too_large`. The estimate itself can also throw here
1091
+ // (`JSON.stringify` rejects a BigInt), and latching that is right too:
1092
+ // a malformed message will still be malformed next turn.
1093
+ //
1094
+ // Not latched once the compacted window is in memory: the pass did its
1095
+ // job and the request goes out with the smaller history. Latching there
1096
+ // would block the next rescue over a bookkeeping failure.
1097
+ if (committed) {
1098
+ logger.error(`Oversize compaction applied but its bookkeeping failed: ${err}`, { ...getErrorMeta(err), ...this.metricIdentity });
1099
+ return false;
1100
+ }
1101
+ this.oversizeCompactionExhausted = true;
1102
+ logger.error(`Failed to compact oversized payload; leaving it to the provider: ${err}`, { ...getErrorMeta(err), ...this.metricIdentity });
1103
+ return false;
1104
+ }
1105
+ finally {
1106
+ // Telemetry only: `outcome` is assigned on every path and nothing here
1107
+ // touches control flow, so a metrics failure cannot change what the
1108
+ // request does.
1109
+ recordPreflightCompaction({
1110
+ outcome,
1111
+ durationMs: performance.now() - startedAt,
1112
+ identity: this.metricIdentity,
1113
+ });
1114
+ }
1115
+ }
672
1116
  async getMessages() {
673
1117
  // Fast path: return cached result when messages haven't changed.
674
1118
  // This avoids re-running 10+ normalization/repair passes and avoids
@@ -677,9 +1121,23 @@ export class ContextV2 {
677
1121
  logger.debug(`getMessages: cache hit (${this._cachedMessages.length} messages)`);
678
1122
  return [...this._cachedMessages];
679
1123
  }
680
- return this.queue.enqueue(async () => {
681
- // Re-check inside queue in case another enqueued op just computed it.
682
- if (this._cachedMessages) {
1124
+ return this.queue.enqueue(() => this.buildMessages());
1125
+ }
1126
+ /**
1127
+ * Build the wire payload. Split out of `getMessages` so a pass already
1128
+ * running inside the operation queue can size the real payload without
1129
+ * re-entering the queue and deadlocking.
1130
+ */
1131
+ async buildMessages(source) {
1132
+ // A `source` means "size this candidate window", not "this is the context
1133
+ // now": the cache is neither read nor written, so a candidate the caller
1134
+ // ends up discarding cannot leak into the next real request. Same function
1135
+ // either way on purpose, so the before and after measurements are the same
1136
+ // shape and a difference between them means a real difference in payload.
1137
+ const isCandidate = source !== undefined;
1138
+ {
1139
+ // Re-check in case another enqueued op just computed it.
1140
+ if (!isCandidate && this._cachedMessages) {
683
1141
  return [...this._cachedMessages];
684
1142
  }
685
1143
  // Strip system-role messages from the conversation array before
@@ -695,7 +1153,7 @@ export class ContextV2 {
695
1153
  // marker through would put a second system-role message in the
696
1154
  // middle of the `messages` array and 400 the request on the
697
1155
  // first post-compaction turn.
698
- const conversationMessages = this.messages.filter((msg) => msg.role !== "system");
1156
+ const conversationMessages = (source ?? this.messages).filter((msg) => msg.role !== "system");
699
1157
  const allMessages = this.systemPrompt
700
1158
  ? [this.systemPrompt, ...conversationMessages]
701
1159
  : [...conversationMessages];
@@ -753,21 +1211,27 @@ export class ContextV2 {
753
1211
  // prefill), so they cannot orphan a tool_use. Cache-breakpoint application
754
1212
  // below mutates providerOptions only and is safe.
755
1213
  const enforced = enforceToolUsePairing(deduped);
1214
+ // Compaction can persist a lone UTF-16 surrogate (emoji split). Bedrock
1215
+ // then rejects the Converse body as invalid JSON. Repair on the wire
1216
+ // only so already-poisoned chats can send without rewriting history.
1217
+ const jsonSafe = makeJsonSafeValue(enforced);
756
1218
  // Apply cache breakpoints (recompute indices after filtering since
757
1219
  // the .filter() step may have removed messages, shifting positions)
758
1220
  const repairedBreakpoints = findBreakpoints({
759
- messages: enforced,
1221
+ messages: jsonSafe,
760
1222
  hasSystemPrompt: this.systemPrompt !== undefined,
761
1223
  config: this.caching,
762
1224
  });
763
1225
  for (const idx of repairedBreakpoints) {
764
- if (enforced[idx]) {
765
- enforced[idx].providerOptions = CACHE_BREAKPOINT_PROVIDER_OPTIONS;
1226
+ if (jsonSafe[idx]) {
1227
+ jsonSafe[idx].providerOptions = CACHE_BREAKPOINT_PROVIDER_OPTIONS;
766
1228
  }
767
1229
  }
768
- this._cachedMessages = enforced;
769
- return [...enforced];
770
- });
1230
+ if (!isCandidate) {
1231
+ this._cachedMessages = jsonSafe;
1232
+ }
1233
+ return [...jsonSafe];
1234
+ }
771
1235
  }
772
1236
  async onCompactionDetected() {
773
1237
  const detectedGeneration = this.activeServerCompactionGeneration ??