studiograph 1.3.48-next.241 → 1.3.48-next.243

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 (233) hide show
  1. package/dist/agent/agent-pool.d.ts +24 -10
  2. package/dist/agent/agent-pool.js +31 -10
  3. package/dist/agent/agent-pool.js.map +1 -1
  4. package/dist/agent/context-window.d.ts +113 -0
  5. package/dist/agent/context-window.js +459 -0
  6. package/dist/agent/context-window.js.map +1 -0
  7. package/dist/agent/orchestrator.d.ts +7 -0
  8. package/dist/agent/orchestrator.js +71 -0
  9. package/dist/agent/orchestrator.js.map +1 -1
  10. package/dist/agent/skills/entity-schema.md +4 -1
  11. package/dist/agent/tools/graph-tools.js +6 -1
  12. package/dist/agent/tools/graph-tools.js.map +1 -1
  13. package/dist/cli/commands/admin/migrate-assets.d.ts +9 -0
  14. package/dist/cli/commands/admin/migrate-assets.js +49 -1
  15. package/dist/cli/commands/admin/migrate-assets.js.map +1 -1
  16. package/dist/core/artifact-frame-model.d.ts +139 -0
  17. package/dist/core/artifact-frame-model.js +433 -0
  18. package/dist/core/artifact-frame-model.js.map +1 -0
  19. package/dist/core/entity-body-templates.js +1 -1
  20. package/dist/core/entity-body-templates.js.map +1 -1
  21. package/dist/core/font-family.d.ts +62 -0
  22. package/dist/core/font-family.js +84 -0
  23. package/dist/core/font-family.js.map +1 -0
  24. package/dist/core/graph.d.ts +0 -4
  25. package/dist/core/graph.js +7 -13
  26. package/dist/core/graph.js.map +1 -1
  27. package/dist/core/secrets/index.d.ts +2 -2
  28. package/dist/core/secrets/index.js +1 -1
  29. package/dist/core/secrets/index.js.map +1 -1
  30. package/dist/core/secrets/registry.d.ts +65 -16
  31. package/dist/core/secrets/registry.js +96 -28
  32. package/dist/core/secrets/registry.js.map +1 -1
  33. package/dist/core/types.js +5 -3
  34. package/dist/core/types.js.map +1 -1
  35. package/dist/core/validation.js +7 -3
  36. package/dist/core/validation.js.map +1 -1
  37. package/dist/core/workspace-manager.d.ts +12 -0
  38. package/dist/core/workspace-manager.js +19 -0
  39. package/dist/core/workspace-manager.js.map +1 -1
  40. package/dist/mcp/tools.js +37 -0
  41. package/dist/mcp/tools.js.map +1 -1
  42. package/dist/server/index.js +5 -1
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/routes/asset-api.js +65 -179
  45. package/dist/server/routes/asset-api.js.map +1 -1
  46. package/dist/server/routes/chat.js +39 -0
  47. package/dist/server/routes/chat.js.map +1 -1
  48. package/dist/server/routes/graph-api.js +113 -7
  49. package/dist/server/routes/graph-api.js.map +1 -1
  50. package/dist/services/asset-listing.d.ts +24 -35
  51. package/dist/services/asset-listing.js +37 -235
  52. package/dist/services/asset-listing.js.map +1 -1
  53. package/dist/services/asset-move-impact.d.ts +49 -0
  54. package/dist/services/asset-move-impact.js +62 -0
  55. package/dist/services/asset-move-impact.js.map +1 -0
  56. package/dist/services/assets/index.d.ts +2 -22
  57. package/dist/services/assets/index.js +2 -20
  58. package/dist/services/assets/index.js.map +1 -1
  59. package/dist/services/fonts/font-metadata.d.ts +42 -0
  60. package/dist/services/fonts/font-metadata.js +126 -0
  61. package/dist/services/fonts/font-metadata.js.map +1 -0
  62. package/dist/services/fonts/font-skill-service.d.ts +66 -0
  63. package/dist/services/fonts/font-skill-service.js +137 -0
  64. package/dist/services/fonts/font-skill-service.js.map +1 -0
  65. package/dist/services/utility-model-resolver.d.ts +6 -10
  66. package/dist/services/utility-model-resolver.js +11 -17
  67. package/dist/services/utility-model-resolver.js.map +1 -1
  68. package/dist/web/_app/immutable/assets/0.4q9K6_aj.css +2 -0
  69. package/dist/web/_app/immutable/assets/{4.CdxzP04G.css → 4.rAQde9C1.css} +1 -1
  70. package/dist/web/_app/immutable/assets/{6.Bd_F20BM.css → 6.DONJs2Ua.css} +1 -1
  71. package/dist/web/_app/immutable/assets/ArtifactEntityView.DUGIdmZD.css +1 -0
  72. package/dist/web/_app/immutable/assets/{BulkActionsBar.D_-CBnA5.css → BulkActionsBar.CXSM7tO7.css} +1 -1
  73. package/dist/web/_app/immutable/assets/{FolderPicker.Cp46GHe2.css → FolderPicker.BIvdgOqd.css} +1 -1
  74. package/dist/web/_app/immutable/chunks/-P-_45Lq.js +1 -0
  75. package/dist/web/_app/immutable/chunks/{YLpl_oIu.js → 2dmmroBJ.js} +2 -2
  76. package/dist/web/_app/immutable/chunks/39GYFK-92.js +2 -0
  77. package/dist/web/_app/immutable/chunks/{CQl7rD-U2.js → 3NWakot5.js} +1 -1
  78. package/dist/web/_app/immutable/chunks/{Cb8T8f2g2.js → 41TuxWjY2.js} +1 -1
  79. package/dist/web/_app/immutable/chunks/{40u3Fyat.js → 7hwha0Sh.js} +1 -1
  80. package/dist/web/_app/immutable/chunks/{LCa8e6zu.js → 8fpcZDYw.js} +1 -1
  81. package/dist/web/_app/immutable/chunks/94Ryxh_u.js +5 -0
  82. package/dist/web/_app/immutable/chunks/9eAJQZrp.js +2 -0
  83. package/dist/web/_app/immutable/chunks/B5t0eFJ92.js +1 -0
  84. package/dist/web/_app/immutable/chunks/B8Yk5rbB.js +1 -0
  85. package/dist/web/_app/immutable/chunks/{BzaxZ6j12.js → BDAt3t0d.js} +1 -1
  86. package/dist/web/_app/immutable/chunks/{DtWm59NA.js → BJDXKR9Z.js} +1 -1
  87. package/dist/web/_app/immutable/chunks/BKFu0lwN.js +1 -0
  88. package/dist/web/_app/immutable/chunks/BKoDfR5M.js +1 -0
  89. package/dist/web/_app/immutable/chunks/{3mkRcVr2.js → BMH0TYNu.js} +1 -1
  90. package/dist/web/_app/immutable/chunks/BYwcH5OJ.js +8 -0
  91. package/dist/web/_app/immutable/chunks/BZaiSHRW.js +2 -0
  92. package/dist/web/_app/immutable/chunks/B_ZKmxdt.js +2 -0
  93. package/dist/web/_app/immutable/chunks/{BaCR2693.js → BdpPyI9Q.js} +1 -1
  94. package/dist/web/_app/immutable/chunks/BeLBbSLQ.js +1 -0
  95. package/dist/web/_app/immutable/chunks/{2eVtOz9o.js → BetCjBPr.js} +1 -1
  96. package/dist/web/_app/immutable/chunks/{CG2hnd1h.js → Bgvs_OLG.js} +2 -2
  97. package/dist/web/_app/immutable/chunks/{CykwAMMd.js → Blkb3o9A.js} +1 -1
  98. package/dist/web/_app/immutable/chunks/{DV2oc0o_2.js → Blnr7o0u2.js} +1 -1
  99. package/dist/web/_app/immutable/chunks/BqXMLvhL.js +1 -0
  100. package/dist/web/_app/immutable/chunks/{rW2lf3lL.js → BsGGYtBY.js} +3 -3
  101. package/dist/web/_app/immutable/chunks/{okRkqEuc2.js → Bu_5WOFt2.js} +2 -2
  102. package/dist/web/_app/immutable/chunks/{Szr0Rn1A2.js → BudZbwja2.js} +1 -1
  103. package/dist/web/_app/immutable/chunks/BuiYznRn2.js +1 -0
  104. package/dist/web/_app/immutable/chunks/{CUFQMEBn2.js → BxFrEcwX.js} +1 -1
  105. package/dist/web/_app/immutable/chunks/{D1yycc8k.js → By5fEhAV.js} +1 -1
  106. package/dist/web/_app/immutable/chunks/ByDn7_Qm.js +1 -0
  107. package/dist/web/_app/immutable/chunks/C-mgrnTp2.js +1 -0
  108. package/dist/web/_app/immutable/chunks/C17GgqdR.js +2 -0
  109. package/dist/web/_app/immutable/chunks/{B0psjWmv2.js → CAGPIH5O2.js} +1 -1
  110. package/dist/web/_app/immutable/chunks/CIgtLr8b.js +1 -0
  111. package/dist/web/_app/immutable/chunks/{CvLjUdY6.js → CMKOh3Gu.js} +1 -1
  112. package/dist/web/_app/immutable/chunks/{ClfNZrQj2.js → CQ7ssHiz2.js} +1 -1
  113. package/dist/web/_app/immutable/chunks/{B2vfWNjV.js → CQz_RPfB.js} +1 -1
  114. package/dist/web/_app/immutable/chunks/{BFa4tT_F.js → CSYiGoxd.js} +1 -1
  115. package/dist/web/_app/immutable/chunks/CVGnbOuS2.js +2 -0
  116. package/dist/web/_app/immutable/chunks/CbjSpBcK2.js +83 -0
  117. package/dist/web/_app/immutable/chunks/{BsWU-saX.js → Ciqwqq772.js} +1 -1
  118. package/dist/web/_app/immutable/chunks/Cj6oCHku.js +6 -0
  119. package/dist/web/_app/immutable/chunks/CkvGsRCA.js +1 -0
  120. package/dist/web/_app/immutable/chunks/CpUmT5hX2.js +1 -0
  121. package/dist/web/_app/immutable/chunks/Cr7JY6F4.js +1 -0
  122. package/dist/web/_app/immutable/chunks/D-p0AOc9.js +1 -0
  123. package/dist/web/_app/immutable/chunks/{DA1iSzCa.js → D1Jt3dpR.js} +3 -3
  124. package/dist/web/_app/immutable/chunks/D5PyYNZN2.js +1 -0
  125. package/dist/web/_app/immutable/chunks/D62Ewp98.js +1 -0
  126. package/dist/web/_app/immutable/chunks/{CkRPByFB.js → D6T1mkH32.js} +1 -1
  127. package/dist/web/_app/immutable/chunks/D7pxZWUe2.js +1 -0
  128. package/dist/web/_app/immutable/chunks/{DzMudXy8.js → DFRK-opu.js} +1 -1
  129. package/dist/web/_app/immutable/chunks/{C5LRkli5.js → DJ-tF4mo.js} +1 -1
  130. package/dist/web/_app/immutable/chunks/{T0b_sc_32.js → DMpy3mnJ2.js} +3 -3
  131. package/dist/web/_app/immutable/chunks/DRkkJdkV.js +1 -0
  132. package/dist/web/_app/immutable/chunks/DTRR5v8f.js +1 -0
  133. package/dist/web/_app/immutable/chunks/DYdH-va_.js +2 -0
  134. package/dist/web/_app/immutable/chunks/{DNSTIFsE2.js → D_1fVNTS2.js} +1 -1
  135. package/dist/web/_app/immutable/chunks/Db11KpbT.js +1 -0
  136. package/dist/web/_app/immutable/chunks/DcPrzUMJ2.js +1 -0
  137. package/dist/web/_app/immutable/chunks/{CcdOGNoe.js → DeNJFJmW.js} +1 -1
  138. package/dist/web/_app/immutable/chunks/DioG9gEY.js +1 -0
  139. package/dist/web/_app/immutable/chunks/{Jvqy_K4S.js → DjWIE8kE.js} +1 -1
  140. package/dist/web/_app/immutable/chunks/{CxSiMudP.js → Dm3FeQiy.js} +2 -2
  141. package/dist/web/_app/immutable/chunks/{D2hYhhaj.js → DoBrc_MD.js} +1 -1
  142. package/dist/web/_app/immutable/chunks/{BHt0yMqi.js → Dqt3ZQbf.js} +1 -1
  143. package/dist/web/_app/immutable/chunks/{DZyEqtHZ.js → DvDv3PXS.js} +1 -1
  144. package/dist/web/_app/immutable/chunks/DwcahpVZ2.js +2 -0
  145. package/dist/web/_app/immutable/chunks/{o5oBpH0v.js → Dz0NKq7k.js} +1 -1
  146. package/dist/web/_app/immutable/chunks/JPkInEiy2.js +2 -0
  147. package/dist/web/_app/immutable/chunks/OlaBOMTk.js +1 -0
  148. package/dist/web/_app/immutable/chunks/VYOuBRrp2.js +1 -0
  149. package/dist/web/_app/immutable/chunks/WnsexHKM.js +2 -0
  150. package/dist/web/_app/immutable/chunks/_4BKTeRf.js +1 -0
  151. package/dist/web/_app/immutable/chunks/_nxBVnQa.js +1 -0
  152. package/dist/web/_app/immutable/chunks/deOZsxWU.js +1 -0
  153. package/dist/web/_app/immutable/chunks/kkDjm5WC.js +1 -0
  154. package/dist/web/_app/immutable/chunks/{NQ4cw_fx.js → oYsB08kS.js} +1 -1
  155. package/dist/web/_app/immutable/chunks/{Cm2Oo0dQ.js → u78dShfg.js} +1 -1
  156. package/dist/web/_app/immutable/chunks/{DsgQXkPI.js → x2r4Enci.js} +1 -1
  157. package/dist/web/_app/immutable/chunks/{DcpCVN9q.js → yhLA_1G0.js} +1 -1
  158. package/dist/web/_app/immutable/entry/app.CyaMSTD-.js +2 -0
  159. package/dist/web/_app/immutable/entry/start.w51yOSqv.js +1 -0
  160. package/dist/web/_app/immutable/nodes/{0.B6Y_LBk7.js → 0.De1hV4CK.js} +2 -2
  161. package/dist/web/_app/immutable/nodes/{1.D4YkYR_K.js → 1.Byc9lVX0.js} +1 -1
  162. package/dist/web/_app/immutable/nodes/{10.GeQH4voX.js → 10.5O2PtJDQ.js} +1 -1
  163. package/dist/web/_app/immutable/nodes/{11.Dh82pQqJ.js → 11.B5We-hs7.js} +1 -1
  164. package/dist/web/_app/immutable/nodes/{12.B_3NIDKw.js → 12.BJY0oraq.js} +1 -1
  165. package/dist/web/_app/immutable/nodes/{13.CENuKScK.js → 13.3tv4r7-W.js} +1 -1
  166. package/dist/web/_app/immutable/nodes/15.CXxJLwsA.js +1 -0
  167. package/dist/web/_app/immutable/nodes/{16.CQ1g-7fy.js → 16.DAZbcz9r.js} +1 -1
  168. package/dist/web/_app/immutable/nodes/17.DNfdzJKO.js +1 -0
  169. package/dist/web/_app/immutable/nodes/{18.BGK1DT5w.js → 18.CRWiO34_.js} +1 -1
  170. package/dist/web/_app/immutable/nodes/2.D2zEcEUa.js +125 -0
  171. package/dist/web/_app/immutable/nodes/3.DtSKWUk0.js +6 -0
  172. package/dist/web/_app/immutable/nodes/4.C1C-ZltD.js +16 -0
  173. package/dist/web/_app/immutable/nodes/{5.Dg7MyS_D.js → 5.B7l9_B2T.js} +1 -1
  174. package/dist/web/_app/immutable/nodes/6.DT5EDRvz.js +4 -0
  175. package/dist/web/_app/immutable/nodes/{7.DsTgdKJY.js → 7.CQ8iYk-b.js} +1 -1
  176. package/dist/web/_app/immutable/nodes/{8.C2cgEqFX.js → 8.B-B5mqzx.js} +1 -1
  177. package/dist/web/_app/immutable/nodes/{9.DzTYjHLu.js → 9.A8hgLZWF.js} +1 -1
  178. package/dist/web/_app/version.json +1 -1
  179. package/dist/web/index.html +17 -17
  180. package/package.json +3 -1
  181. package/dist/web/_app/immutable/assets/0.CbwnbgGB.css +0 -2
  182. package/dist/web/_app/immutable/assets/ArtifactEntityView.pIdeOQQE.css +0 -1
  183. package/dist/web/_app/immutable/chunks/4nXmhCEf2.js +0 -2
  184. package/dist/web/_app/immutable/chunks/B1jPUcUY.js +0 -2
  185. package/dist/web/_app/immutable/chunks/B8CjE3Zb.js +0 -1
  186. package/dist/web/_app/immutable/chunks/BCC39_oZ.js +0 -1
  187. package/dist/web/_app/immutable/chunks/BL0OfnJm.js +0 -8
  188. package/dist/web/_app/immutable/chunks/BNpak4NV.js +0 -6
  189. package/dist/web/_app/immutable/chunks/BSBfdnem.js +0 -1
  190. package/dist/web/_app/immutable/chunks/BTNc9Y1I.js +0 -1
  191. package/dist/web/_app/immutable/chunks/BbdJq5V-.js +0 -2
  192. package/dist/web/_app/immutable/chunks/BiL0KXYv2.js +0 -1
  193. package/dist/web/_app/immutable/chunks/BielyHqY.js +0 -1
  194. package/dist/web/_app/immutable/chunks/Bl41UZSS.js +0 -1
  195. package/dist/web/_app/immutable/chunks/BljLR4Fb.js +0 -1
  196. package/dist/web/_app/immutable/chunks/BoCumB752.js +0 -1
  197. package/dist/web/_app/immutable/chunks/BqGYAiLE.js +0 -2
  198. package/dist/web/_app/immutable/chunks/CE3Vl5fC.js +0 -2
  199. package/dist/web/_app/immutable/chunks/CIfkrRcg2.js +0 -1
  200. package/dist/web/_app/immutable/chunks/CW-Ov0pM2.js +0 -7
  201. package/dist/web/_app/immutable/chunks/CWReqXbo.js +0 -1
  202. package/dist/web/_app/immutable/chunks/CZgFaimi2.js +0 -83
  203. package/dist/web/_app/immutable/chunks/CmD0Pw4p.js +0 -1
  204. package/dist/web/_app/immutable/chunks/CrwD95qi.js +0 -1
  205. package/dist/web/_app/immutable/chunks/D-3tCwGW2.js +0 -1
  206. package/dist/web/_app/immutable/chunks/D0rBhnqB2.js +0 -1
  207. package/dist/web/_app/immutable/chunks/D7DUV8m62.js +0 -1
  208. package/dist/web/_app/immutable/chunks/D7LxIGlr2.js +0 -1
  209. package/dist/web/_app/immutable/chunks/DAiEwMLj.js +0 -1
  210. package/dist/web/_app/immutable/chunks/DBsoRks82.js +0 -2
  211. package/dist/web/_app/immutable/chunks/DLrTb3Ng2.js +0 -2
  212. package/dist/web/_app/immutable/chunks/DOPRSg8K.js +0 -1
  213. package/dist/web/_app/immutable/chunks/Dk_kSxt2.js +0 -2
  214. package/dist/web/_app/immutable/chunks/Dv03G3ns.js +0 -1
  215. package/dist/web/_app/immutable/chunks/DxiNzbUg2.js +0 -1
  216. package/dist/web/_app/immutable/chunks/PeIamBdH.js +0 -3
  217. package/dist/web/_app/immutable/chunks/Re1mVUuh.js +0 -1
  218. package/dist/web/_app/immutable/chunks/anMAD2SC.js +0 -1
  219. package/dist/web/_app/immutable/chunks/g9Li6c7C.js +0 -2
  220. package/dist/web/_app/immutable/chunks/poXSsDDO.js +0 -1
  221. package/dist/web/_app/immutable/entry/app.DMx34n6u.js +0 -2
  222. package/dist/web/_app/immutable/entry/start.CyYr2Wch.js +0 -1
  223. package/dist/web/_app/immutable/nodes/15.CsSKUe8p.js +0 -1
  224. package/dist/web/_app/immutable/nodes/17.BOHirkyf.js +0 -1
  225. package/dist/web/_app/immutable/nodes/2.z37DNGUZ.js +0 -123
  226. package/dist/web/_app/immutable/nodes/3.CRRrDZcD.js +0 -6
  227. package/dist/web/_app/immutable/nodes/4.BvQ4JVou.js +0 -11
  228. package/dist/web/_app/immutable/nodes/6.BsBJH-kP.js +0 -6
  229. /package/dist/web/_app/immutable/chunks/{CYEYDNwO2.js → CYEYDNwO.js} +0 -0
  230. /package/dist/web/_app/immutable/chunks/{D4uUZ_aS2.js → D4uUZ_aS.js} +0 -0
  231. /package/dist/web/_app/immutable/chunks/{HrvI1J6b.js → HrvI1J6b2.js} +0 -0
  232. /package/dist/web/_app/immutable/chunks/{UZu_W1XF.js → UZu_W1XF2.js} +0 -0
  233. /package/dist/web/_app/immutable/chunks/{hG-QCarB.js → hG-QCarB2.js} +0 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Context-window management for the in-app chat agent.
3
+ *
4
+ * The agent accumulates conversation history in `agent.state.messages`. Large
5
+ * tool results (asset/image payloads from list_assets / read_asset /
6
+ * inspect_artifact, big document or dataset bodies) plus the running turn count
7
+ * make that array grow without bound. Once it exceeds the model's token ceiling
8
+ * the provider rejects the WHOLE request with a hard 400 ("prompt is too long:
9
+ * N tokens > M maximum"), killing the chat.
10
+ *
11
+ * This module is the PROACTIVE half of the fix: a pure, provider-agnostic
12
+ * pass that estimates tokens and, when a request would overflow, trims the
13
+ * transcript down to fit BEFORE it is sent. It is wired into the runtime via
14
+ * pi-agent-core's `transformContext` hook (orchestrator.ts), which runs once
15
+ * before every LLM call and non-destructively produces the message array sent
16
+ * to the provider — the persistent transcript (saved to disk / shown in the UI)
17
+ * is untouched.
18
+ *
19
+ * The DESTRUCTIVE half (agent-pool.ts `compactArtifactHistory`) is the privacy/
20
+ * baseline complement: after each turn it strips images and elides artifact
21
+ * bundles from the PERSISTED transcript, but deliberately keeps large text (so
22
+ * saved chats stay readable). It does not share this module's helpers — the two
23
+ * passes have different jobs (must-not-persist vs live token budget); see that
24
+ * method's own docstring for the split of labor.
25
+ *
26
+ * Nothing here does I/O or model calls — it is deterministic and unit-tested.
27
+ */
28
+ // ── Tunables ────────────────────────────────────────────────────────────────
29
+ /**
30
+ * Characters per token — deliberately the LOW end of the realistic range, which
31
+ * makes the whole module's safety invariant hold:
32
+ *
33
+ * as long as real-chars-per-token ≥ CHARS_PER_TOKEN, our estimate is ≥ the
34
+ * real token count, so clipping/trimming to a token budget guarantees the
35
+ * real prompt stays under that budget (and thus under the model window).
36
+ *
37
+ * The two error directions are asymmetric: under-counting risks a hard provider
38
+ * 400 that kills the whole conversation (the exact failure this module prevents);
39
+ * over-counting only trims slightly earlier than strictly necessary. Measured
40
+ * density by content: English prose ~4, code ~3, and the token-dense JSON that
41
+ * actually overflows — `list_assets` rows full of UUIDs/URLs/filenames — ~2.4
42
+ * (observed live: 1.10M real tokens vs a 0.88M estimate at the old value of 3,
43
+ * a 25% under-count that 400'd a 1M-window request). 2.2 sits just below that
44
+ * worst realistic case, so dense tool results no longer slip the budget; only
45
+ * sub-2.2 content (heavy base64 text, which is rare since images are stripped as
46
+ * blocks) can, and the reactive `isContextOverflow` net (chat.ts) backstops it.
47
+ */
48
+ const CHARS_PER_TOKEN = 2.2;
49
+ /** Per-message structural overhead (role framing, delimiters) in tokens. */
50
+ const PER_MESSAGE_OVERHEAD = 4;
51
+ /** Rough token cost of an image content block. Image tokens are a function of
52
+ * pixel dimensions, not byte size, but we only have the base64 string here, so
53
+ * this is a deliberately rough heuristic with an 800-token floor (downscaled
54
+ * inspect images ≤1024px land ~1.6k; large composer uploads more). Exactness
55
+ * isn't required — images ride only in recent/pinned turns, and the budget
56
+ * margin plus the reactive overflow net absorb the slack. */
57
+ function estimateImageTokens(base64Len) {
58
+ return Math.max(800, Math.ceil(base64Len / 700));
59
+ }
60
+ /** A tool result is "large" once its content estimate crosses this. Tier-1
61
+ * stubbing and the post-turn compaction both use it. ~2k tokens ≈ a screenful
62
+ * of JSON — small results stay inline so the agent doesn't have to re-fetch. */
63
+ export const LARGE_TOOL_RESULT_TOKENS = 2000;
64
+ /** Turns at the tail that are never evicted or stubbed — the live working set
65
+ * the agent needs to answer the current question. A "turn" is a user-message-
66
+ * delimited group (see `groupIntoTurns`). */
67
+ const PINNED_RECENT_TURNS = 3;
68
+ /** Estimate tokens for one message's content (text + images + tool-call args). */
69
+ export function estimateMessageTokens(msg) {
70
+ const m = msg;
71
+ if (!m || typeof m !== 'object')
72
+ return 0;
73
+ let chars = 0;
74
+ let tokens = PER_MESSAGE_OVERHEAD;
75
+ const addContent = (content) => {
76
+ if (typeof content === 'string') {
77
+ chars += content.length;
78
+ return;
79
+ }
80
+ if (!Array.isArray(content))
81
+ return;
82
+ for (const block of content) {
83
+ if (!block || typeof block !== 'object')
84
+ continue;
85
+ if (block.type === 'text' && typeof block.text === 'string')
86
+ chars += block.text.length;
87
+ else if (block.type === 'thinking' && typeof block.thinking === 'string')
88
+ chars += block.thinking.length;
89
+ else if (block.type === 'image' && typeof block.data === 'string')
90
+ tokens += estimateImageTokens(block.data.length);
91
+ else if (block.type === 'toolCall') {
92
+ // Assistant tool-call block: name + serialized arguments.
93
+ if (typeof block.name === 'string')
94
+ chars += block.name.length;
95
+ try {
96
+ chars += JSON.stringify(block.arguments ?? {}).length;
97
+ }
98
+ catch { /* unserializable — skip */ }
99
+ }
100
+ }
101
+ };
102
+ addContent(m.content);
103
+ return tokens + Math.ceil(chars / CHARS_PER_TOKEN);
104
+ }
105
+ /**
106
+ * Estimate the total prompt tokens for a transcript, optionally including the
107
+ * system prompt and tool schemas (both ride in every request).
108
+ */
109
+ export function estimateTokens(messages, systemPrompt, tools) {
110
+ let total = 0;
111
+ for (const msg of messages)
112
+ total += estimateMessageTokens(msg);
113
+ if (systemPrompt)
114
+ total += Math.ceil(systemPrompt.length / CHARS_PER_TOKEN);
115
+ if (tools && tools.length) {
116
+ let schemaChars = 0;
117
+ for (const t of tools) {
118
+ schemaChars += (t.name?.length ?? 0) + (t.description?.length ?? 0);
119
+ try {
120
+ schemaChars += JSON.stringify(t.parameters ?? {}).length;
121
+ }
122
+ catch { /* skip */ }
123
+ }
124
+ total += Math.ceil(schemaChars / CHARS_PER_TOKEN);
125
+ }
126
+ return total;
127
+ }
128
+ // Note on accuracy: an earlier design self-calibrated the estimator against each
129
+ // turn's real `usage.input`. That was removed deliberately — once the manager
130
+ // starts trimming, the persisted transcript and the prompt actually sent diverge,
131
+ // so the learned ratio biases downward exactly when trimming is active (less
132
+ // trimming → overflow), and folding the fixed tool-schema cost into a
133
+ // multiplicative factor double-counted it. The conservative fixed estimator
134
+ // (`CHARS_PER_TOKEN`, see above) plus the `computeBudget` margin and the reactive
135
+ // `isContextOverflow` backstop are simpler and fail safe.
136
+ // ── Large-result detection + stubbing (used by manageContext's Tier 1) ─────
137
+ /** True if a toolResult message's content is large enough to be worth eliding. */
138
+ export function isLargeToolResult(msg) {
139
+ const m = msg;
140
+ if (!m || m.role !== 'toolResult' || !Array.isArray(m.content))
141
+ return false;
142
+ // Any image block makes it large (vision must never persist across turns).
143
+ if (m.content.some((b) => b?.type === 'image'))
144
+ return true;
145
+ return estimateMessageTokens(msg) >= LARGE_TOOL_RESULT_TOKENS;
146
+ }
147
+ /** The stub text that replaces an elided tool result. Names the tool so the
148
+ * agent knows it can call it again to retrieve the content on demand. */
149
+ export function toolResultStub(toolName) {
150
+ const name = toolName || 'a tool';
151
+ return `[A large ${name} result from an earlier step was removed to save context. ` +
152
+ `Call ${name} again if you need its current contents.]`;
153
+ }
154
+ /**
155
+ * Return a pairing-safe stubbed COPY of a toolResult message: content replaced
156
+ * with a one-line stub, `toolCallId`/`toolName`/`role` preserved (so the paired
157
+ * assistant tool-call still resolves and the provider doesn't 400), `details`
158
+ * dropped. Returns the original reference if it is not a large tool result.
159
+ */
160
+ export function stubToolResult(msg) {
161
+ const m = msg;
162
+ if (!isLargeToolResult(msg))
163
+ return msg;
164
+ const copy = { ...m, content: [{ type: 'text', text: toolResultStub(m.toolName) }] };
165
+ delete copy.details;
166
+ return copy;
167
+ }
168
+ /** Compute the usable input-token budget for a model. */
169
+ export function computeBudget(model) {
170
+ // Defensive: a freeform/custom model id resolves to `undefined` via pi-ai's
171
+ // getModel(); without this guard `model.contextWindow` throws, transformContext
172
+ // swallows it, and proactive trimming is silently disabled for the whole
173
+ // session. Treat an unknown model as a conservative 200k window.
174
+ const ctx = model?.contextWindow && model.contextWindow > 0 ? model.contextWindow : 200_000;
175
+ const reserveForOutput = Math.min(model?.maxTokens && model.maxTokens > 0 ? model.maxTokens : 8000, 64_000);
176
+ // 10% cushion (was 6%): absorbs estimator variance for content slightly denser
177
+ // than CHARS_PER_TOKEN, on top of the output reservation.
178
+ const safetyMargin = Math.max(Math.floor(ctx * 0.10), 8000);
179
+ return Math.max(ctx - reserveForOutput - safetyMargin, Math.floor(ctx * 0.25));
180
+ }
181
+ /**
182
+ * Group messages into user-delimited turns. A turn starts at a `user` message
183
+ * and runs up to (but not including) the next `user` message — so an assistant's
184
+ * tool-call blocks and the `toolResult` messages that answer them always live in
185
+ * the SAME turn. That invariant is what makes whole-turn eviction pairing-safe:
186
+ * dropping a turn never orphans a tool_use from its tool_result (the Anthropic
187
+ * 400). Any leading non-user messages (e.g. a loaded session) form turn 0.
188
+ */
189
+ function groupIntoTurns(messages) {
190
+ const turns = [];
191
+ let current = [];
192
+ for (const msg of messages) {
193
+ if (msg?.role === 'user' && current.length > 0) {
194
+ turns.push(current);
195
+ current = [];
196
+ }
197
+ current.push(msg);
198
+ }
199
+ if (current.length > 0)
200
+ turns.push(current);
201
+ return turns;
202
+ }
203
+ /** Short topic snippet from a user message, for the rolling-summary stub. */
204
+ function userTopic(msg) {
205
+ const m = msg;
206
+ if (m?.role !== 'user')
207
+ return null;
208
+ let text = '';
209
+ if (typeof m.content === 'string')
210
+ text = m.content;
211
+ else if (Array.isArray(m.content)) {
212
+ text = m.content.filter((b) => b?.type === 'text').map((b) => b.text).join(' ');
213
+ }
214
+ text = text.replace(/\s+/g, ' ').trim();
215
+ if (!text)
216
+ return null;
217
+ return text.length > 80 ? text.slice(0, 80) + '…' : text;
218
+ }
219
+ /** Rolling-summary text describing the evicted turns. Prepended to the first
220
+ * KEPT user message (not emitted as its own message) so we never produce two
221
+ * consecutive user messages — some providers enforce role alternation. */
222
+ function buildSummaryText(evicted) {
223
+ const msgCount = evicted.reduce((n, t) => n + t.length, 0);
224
+ const topics = evicted
225
+ .flat()
226
+ .map(userTopic)
227
+ .filter((t) => !!t)
228
+ .slice(0, 8);
229
+ const topicLine = topics.length ? ` Earlier topics: ${topics.join('; ')}.` : '';
230
+ return `[${msgCount} earlier message(s) were removed to fit the model's context window.${topicLine} ` +
231
+ `Ask the user or re-run a tool if you need details from before this point.]`;
232
+ }
233
+ /** Return a COPY of `turns` with the summary text prepended to the first user
234
+ * message of the first kept turn (so it reads as scene-setting on the oldest
235
+ * surviving question, with no extra message and no role-alternation break). */
236
+ function prependSummaryToTurns(turns, summary) {
237
+ const flat = turns.flat();
238
+ const idx = flat.findIndex((m) => m?.role === 'user');
239
+ const block = { type: 'text', text: summary };
240
+ if (idx < 0) {
241
+ // No user message survived (unusual) — fall back to a standalone leading note.
242
+ return [{ role: 'user', content: [block], timestamp: 0 }, ...flat];
243
+ }
244
+ const m = flat[idx];
245
+ const content = Array.isArray(m.content)
246
+ ? [block, ...m.content]
247
+ : [block, { type: 'text', text: typeof m.content === 'string' ? m.content : '' }];
248
+ flat[idx] = { ...m, content };
249
+ return flat;
250
+ }
251
+ /**
252
+ * Trim the latest turn's own oversized blocks in place (on copies): drop image
253
+ * blocks and clip very long text blocks, prepending a note. Used only when a
254
+ * single turn alone exceeds budget (Tier 3) so the turn still runs instead of
255
+ * poisoning the session.
256
+ */
257
+ function truncateTurn(turn, targetTokens) {
258
+ const note = '[Some attachments or large contents in this message were truncated because it exceeded the model context window. ' +
259
+ 'Send less at once or split it up if you need the full content processed.]';
260
+ // Cumulative char budget across the WHOLE turn — not per-block. A turn with
261
+ // several individually-small-but-collectively-large text results (e.g. one
262
+ // assistant step returning many tool results) must still come in under the
263
+ // target; a per-block cap would let each pass and leave the turn over budget.
264
+ let remainingChars = Math.max(2000, Math.floor(targetTokens * CHARS_PER_TOKEN));
265
+ const clipText = (text) => {
266
+ if (remainingChars <= 0)
267
+ return '…[truncated]';
268
+ if (text.length <= remainingChars) {
269
+ remainingChars -= text.length;
270
+ return text;
271
+ }
272
+ const slice = text.slice(0, remainingChars) + '\n…[truncated]';
273
+ remainingChars = 0;
274
+ return slice;
275
+ };
276
+ // Strip images and clip text cumulatively on a copy of each message.
277
+ const out = turn.map((msg) => {
278
+ const m = msg;
279
+ if (Array.isArray(m.content)) {
280
+ const content = m.content
281
+ .filter((b) => b?.type !== 'image')
282
+ .map((b) => {
283
+ if (b?.type === 'text' && typeof b.text === 'string')
284
+ return { ...b, text: clipText(b.text) };
285
+ // Elide an oversized assistant tool-call argument payload (e.g. a
286
+ // create_artifact bundle made THIS turn, before the post-turn
287
+ // compaction runs). estimateMessageTokens counts these, so leaving
288
+ // them inline lets Tier 3 finish "truncated" yet still over budget →
289
+ // a provider 400. Replace the args with a marker; keep id/name so the
290
+ // tool_use ↔ tool_result pairing survives. Mirrors agent-pool's
291
+ // `{ _elided: true }` convention for create_artifact bundles.
292
+ if (b?.type === 'toolCall' && b.arguments && b.arguments._elided !== true) {
293
+ let argLen = 0;
294
+ try {
295
+ argLen = JSON.stringify(b.arguments).length;
296
+ }
297
+ catch {
298
+ argLen = 0;
299
+ }
300
+ if (argLen > 1000) {
301
+ remainingChars = Math.max(0, remainingChars - 40);
302
+ return { ...b, arguments: { _elided: true } };
303
+ }
304
+ }
305
+ return b;
306
+ });
307
+ // Never emit an empty content array — the providers 400 on a tool_result
308
+ // (or user message) with no content. An image-only result filtered down to
309
+ // nothing gets a placeholder so the message — and its toolCallId pairing —
310
+ // survives.
311
+ if (content.length === 0)
312
+ content.push({ type: 'text', text: '[content removed to fit context]' });
313
+ return { ...m, content };
314
+ }
315
+ if (typeof m.content === 'string') {
316
+ return { ...m, content: clipText(m.content) };
317
+ }
318
+ return msg;
319
+ });
320
+ // Prepend the note to the first user message of the turn, if any.
321
+ const firstUserIdx = out.findIndex((m) => m?.role === 'user');
322
+ if (firstUserIdx >= 0) {
323
+ const m = out[firstUserIdx];
324
+ const noteBlock = { type: 'text', text: note };
325
+ const content = Array.isArray(m.content)
326
+ ? [noteBlock, ...m.content]
327
+ : [noteBlock, { type: 'text', text: typeof m.content === 'string' ? m.content : '' }];
328
+ out[firstUserIdx] = { ...m, content };
329
+ }
330
+ return out;
331
+ }
332
+ /**
333
+ * Bring a transcript under the model's input-token budget. Non-destructive:
334
+ * returns a NEW array of (possibly stubbed) message copies; the caller's message
335
+ * objects are never mutated. Tiered:
336
+ * 1. stub oversized tool results (oldest first, recent turns pinned)
337
+ * 2. evict oldest whole turns, folding a rolling summary into the oldest kept
338
+ * user message
339
+ * 3. if the latest turn alone still overflows, truncate its oversized blocks
340
+ *
341
+ * Always pairing-safe: tool_use ↔ tool_result groupings are preserved so the
342
+ * provider never 400s on an orphaned tool result. Best-effort under extreme
343
+ * inputs: if even the truncated latest turn can't fit (e.g. enormous tool-call
344
+ * arguments the manager won't drop), it returns the smallest result it can and
345
+ * `stats.estimateAfter` reports the residual — the reactive `isContextOverflow`
346
+ * net (chat.ts) is the final backstop.
347
+ */
348
+ export function manageContext(input) {
349
+ const { messages, systemPrompt, model, tools } = input;
350
+ const budget = computeBudget(model);
351
+ // Fixed per-request overhead (system prompt + tool schemas) — constant across
352
+ // all tiers, so compute once. Per-message costs are cached and kept summed so
353
+ // the whole pass stays O(n) instead of re-estimating the array in every loop.
354
+ const fixedOverhead = estimateTokens([], systemPrompt, tools);
355
+ const msgEst = (msg) => estimateMessageTokens(msg);
356
+ const perMsg = messages.map(msgEst);
357
+ const total = () => fixedOverhead + perMsg.reduce((a, b) => a + b, 0);
358
+ const estimateBefore = total();
359
+ const stats = {
360
+ budget,
361
+ estimateBefore,
362
+ estimateAfter: estimateBefore,
363
+ stubbedResults: 0,
364
+ evictedTurns: 0,
365
+ truncatedLatestTurn: false,
366
+ overBudget: false,
367
+ };
368
+ // Fast path — the overwhelming common case.
369
+ if (estimateBefore <= budget)
370
+ return { messages, stats };
371
+ // Parallel arrays: `working[i]` ↔ `costs[i]`. `running` tracks the live total.
372
+ let working = messages.slice();
373
+ let costs = perMsg.slice();
374
+ let running = estimateBefore;
375
+ // ── Tier 1: stub oversized tool results, oldest first, pinning recent turns ──
376
+ // Recent turns are normally pinned so the agent keeps its live working set
377
+ // inline. EXCEPTION: a single tool result that alone eats a large fraction of
378
+ // the budget (e.g. a 3M-token `list_assets` dump) is stubbed even when pinned —
379
+ // keeping one giant result inline would otherwise force Tier 2 to evict whole
380
+ // turns of surrounding conversation to make room, gutting the agent's task
381
+ // context (observed live: a 7,278-asset list collapsed context to ~48k and the
382
+ // agent lost the thread). The agent re-fetches from the stub on demand.
383
+ const GIANT_RESULT_TOKENS = Math.floor(budget * 0.33);
384
+ const turnsForPin = groupIntoTurns(working);
385
+ const pinnedFrom = Math.max(0, turnsForPin.length - PINNED_RECENT_TURNS);
386
+ const pinnedMsgs = new Set(turnsForPin.slice(pinnedFrom).flat());
387
+ for (let i = 0; i < working.length && running > budget; i++) {
388
+ const msg = working[i];
389
+ // Skip pinned recent messages UNLESS this one is a single giant result.
390
+ if (pinnedMsgs.has(msg) && costs[i] <= GIANT_RESULT_TOKENS)
391
+ continue;
392
+ if (isLargeToolResult(msg)) {
393
+ working[i] = stubToolResult(msg);
394
+ const newCost = msgEst(working[i]);
395
+ running += newCost - costs[i];
396
+ costs[i] = newCost;
397
+ stats.stubbedResults++;
398
+ }
399
+ }
400
+ // ── Tier 2: evict oldest whole turns, keeping the pinned tail ──────────────
401
+ if (running > budget) {
402
+ const turns = groupIntoTurns(working);
403
+ // Cost per turn, aligned with `turns` order (turns partition `working`).
404
+ let offset = 0;
405
+ const turnCosts = turns.map((t) => {
406
+ let c = 0;
407
+ for (let k = 0; k < t.length; k++)
408
+ c += costs[offset + k];
409
+ offset += t.length;
410
+ return c;
411
+ });
412
+ const evicted = [];
413
+ let firstKept = 0; // index of the first turn still kept
414
+ // Evict oldest first, keeping at least the single latest turn (Tier 3 then
415
+ // truncates that turn if it alone still overflows). The PINNED_RECENT_TURNS
416
+ // preference only matters when already under budget — and there we never
417
+ // enter this loop — so the hard floor here is 1, not PINNED_RECENT_TURNS;
418
+ // otherwise a few large recent turns could exceed a small budget with no way
419
+ // to recover.
420
+ while (turns.length - firstKept > 1 && running > budget) {
421
+ evicted.push(turns[firstKept]);
422
+ running -= turnCosts[firstKept];
423
+ firstKept++;
424
+ stats.evictedTurns++;
425
+ }
426
+ if (evicted.length > 0) {
427
+ const keptTurns = turns.slice(firstKept);
428
+ working = prependSummaryToTurns(keptTurns, buildSummaryText(evicted));
429
+ costs = working.map(msgEst);
430
+ running = fixedOverhead + costs.reduce((a, b) => a + b, 0);
431
+ }
432
+ }
433
+ // ── Tier 3: latest turn alone still overflows — truncate its own bulk ──────
434
+ if (running > budget) {
435
+ const turns = groupIntoTurns(working);
436
+ if (turns.length > 0) {
437
+ const lastIdx = turns.length - 1;
438
+ const before = turns.slice(0, lastIdx).flat();
439
+ const beforeTokens = fixedOverhead + before.map(msgEst).reduce((a, b) => a + b, 0);
440
+ // Room left for the latest turn after everything kept before it. Floor at
441
+ // 256 so a large `before` can't push the target so high that truncateTurn's
442
+ // own minimum-keep defeats the truncation entirely.
443
+ const target = Math.max(budget - beforeTokens, 256);
444
+ turns[lastIdx] = truncateTurn(turns[lastIdx], target);
445
+ working = turns.flat();
446
+ costs = working.map(msgEst);
447
+ running = fixedOverhead + costs.reduce((a, b) => a + b, 0);
448
+ stats.truncatedLatestTurn = true;
449
+ }
450
+ }
451
+ stats.estimateAfter = running;
452
+ // Terminal guard: all tiers ran and we're STILL over budget — the irreducible
453
+ // cost (systemPrompt + tool schemas, or one unshrinkable message) exceeds the
454
+ // budget. Nothing more to trim here; flag it so the caller can log it (not a
455
+ // silent failure) and the reactive net handles the likely 400.
456
+ stats.overBudget = running > budget;
457
+ return { messages: working, stats };
458
+ }
459
+ //# sourceMappingURL=context-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-window.js","sourceRoot":"","sources":["../../src/agent/context-window.ts"],"names":[],"mappings":"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"}
@@ -171,6 +171,13 @@ export declare class AgentOrchestrator {
171
171
  provider: string;
172
172
  modelId: string;
173
173
  };
174
+ /**
175
+ * The active chat model's context window (max prompt tokens), or null if it
176
+ * can't be resolved. Used by the chat route's reactive overflow detection
177
+ * (isContextOverflow needs the window to catch silent overflow). Mirrors the
178
+ * model resolution in createAgentInstance, including the Credits gateway.
179
+ */
180
+ getContextWindow(): number | null;
174
181
  /** Can the active chat model accept image input? Drives the composer's
175
182
  * vision-attach affordance (disable + tooltip when false). The Studiograph
176
183
  * Credits gateway is vision-capable (A6); otherwise resolve from the model. */
@@ -38,6 +38,7 @@ import { loadUserConfig, resolveApiKey, resolveProvider, resolveModelId } from '
38
38
  import { supportsVision } from '../services/model-capabilities.js';
39
39
  import { onSecretMutation } from '../core/secrets/index.js';
40
40
  import { AgentPool } from './agent-pool.js';
41
+ import { manageContext } from './context-window.js';
41
42
  import { addInferenceUsage, createCloudInferenceStreamFn, createInferenceUsageSummary, isStudiographCreditsMode, studiographCreditsModel, } from '../services/cloud-inference-billing.js';
42
43
  import { recordAiUsage } from '../services/ai-usage-ledger.js';
43
44
  export class AgentOrchestrator {
@@ -253,8 +254,59 @@ export class AgentOrchestrator {
253
254
  bundledSeen.add(name);
254
255
  dedupedTools.push(t);
255
256
  }
257
+ // Proactive context-window manager. pi-agent-core calls `transformContext`
258
+ // once before every LLM request, non-destructively producing the message
259
+ // array sent to the provider (the persistent transcript in
260
+ // agent.state.messages — saved to disk / shown in the UI — is untouched).
261
+ // We estimate tokens and, when a request would overflow the model's window,
262
+ // stub oversized tool results / evict oldest turns / truncate an over-large
263
+ // latest turn so the request fits — instead of letting the provider 400 the
264
+ // whole conversation. The estimator is deliberately conservative (see
265
+ // context-window.ts); the hook MUST NOT throw (runtime contract), so on any
266
+ // error we fall back to the untrimmed messages.
267
+ const transformContext = async (messages) => {
268
+ try {
269
+ const { messages: out, stats } = manageContext({
270
+ messages,
271
+ systemPrompt,
272
+ model: model,
273
+ // Use the LIVE tool list, not the captured `dedupedTools`: per-user
274
+ // connector + custom tools are appended to `agent.state.tools` by
275
+ // loadCustomToolsAsync AFTER construction and ride in every request,
276
+ // so estimating with the initial set would undercount the schema
277
+ // overhead and let a near-limit prompt skip trimming. `agent` is
278
+ // assigned just below; this closure only runs at request time, by
279
+ // which point it's initialized.
280
+ tools: agent.state.tools,
281
+ });
282
+ // Log whenever we're at/over budget or actually trimmed — rare path,
283
+ // high signal for ops/QA. (Quiet on the common under-budget fast path.)
284
+ if (stats.estimateBefore > stats.budget || stats.stubbedResults || stats.evictedTurns || stats.truncatedLatestTurn) {
285
+ // eslint-disable-next-line no-console
286
+ console.warn(`[ctx-window] before=${stats.estimateBefore} after=${stats.estimateAfter} budget=${stats.budget} ` +
287
+ `msgs=${messages.length} stubbed=${stats.stubbedResults} evicted=${stats.evictedTurns} truncated=${stats.truncatedLatestTurn}` +
288
+ (stats.overBudget ? ' OVER-BUDGET-AFTER-TRIM' : ''));
289
+ }
290
+ // Distinct, loud signal for the irreducible case (systemPrompt + tool
291
+ // schemas alone exceed budget): everything trimmed yet still over. The
292
+ // reactive isContextOverflow net will surface the 400 to the user; this
293
+ // tells ops WHY (too many/large tools, not a runaway transcript).
294
+ if (stats.overBudget) {
295
+ // eslint-disable-next-line no-console
296
+ console.error(`[ctx-window] STILL OVER BUDGET after full trim (after=${stats.estimateAfter} > budget=${stats.budget}). ` +
297
+ `Likely systemPrompt + tool schemas exceed the window (tools=${agent.state.tools.length}). Request may 400.`);
298
+ }
299
+ return out;
300
+ }
301
+ catch (err) {
302
+ // eslint-disable-next-line no-console
303
+ console.error('[ctx-window] manageContext threw — sending UNTRIMMED messages:', err instanceof Error ? err.stack || err.message : err);
304
+ return messages;
305
+ }
306
+ };
256
307
  const agent = new Agent({
257
308
  getApiKey: () => apiKey,
309
+ transformContext,
258
310
  // `thinkingLevel: 'medium'` turns on extended thinking so the runtime
259
311
  // emits thinking_delta events. The chat stream relays those as a
260
312
  // single-line "what the agent is thinking" status (replacing the pulsing
@@ -773,6 +825,25 @@ ${repoList}`;
773
825
  modelId: resolveModelId(userConfig, this.config),
774
826
  };
775
827
  }
828
+ /**
829
+ * The active chat model's context window (max prompt tokens), or null if it
830
+ * can't be resolved. Used by the chat route's reactive overflow detection
831
+ * (isContextOverflow needs the window to catch silent overflow). Mirrors the
832
+ * model resolution in createAgentInstance, including the Credits gateway.
833
+ */
834
+ getContextWindow() {
835
+ try {
836
+ if (isStudiographCreditsMode()) {
837
+ return studiographCreditsModel().contextWindow ?? null;
838
+ }
839
+ const { provider, modelId } = this.getModelSelection();
840
+ const model = getModel(provider, modelId);
841
+ return model?.contextWindow ?? null;
842
+ }
843
+ catch {
844
+ return null;
845
+ }
846
+ }
776
847
  /** Can the active chat model accept image input? Drives the composer's
777
848
  * vision-attach affordance (disable + tooltip when false). The Studiograph
778
849
  * Credits gateway is vision-capable (A6); otherwise resolve from the model. */