cartethyia 2.7.4 → 2.7.6

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 (595) hide show
  1. package/.env.example +5 -0
  2. package/README.md +18 -6
  3. package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
  5. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
  6. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
  7. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
  8. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
  17. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
  19. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
  20. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
  37. package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
  38. package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
  40. package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
  41. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
  42. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
  43. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
  44. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
  45. package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
  46. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  47. package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
  48. package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
  49. package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
  50. package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
  51. package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
  52. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  53. package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
  54. package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
  55. package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
  56. package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
  57. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  58. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  59. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  60. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  61. package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
  62. package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
  63. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
  64. package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
  65. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
  66. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
  67. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
  68. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  69. package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
  70. package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
  71. package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
  72. package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
  73. package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
  74. package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
  75. package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
  76. package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
  78. package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
  79. package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
  80. package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
  81. package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
  82. package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
  83. package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
  84. package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
  85. package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
  86. package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
  87. package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
  88. package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
  89. package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
  90. package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
  91. package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
  92. package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
  93. package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
  94. package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
  95. package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
  96. package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
  97. package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
  98. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  99. package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
  100. package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
  101. package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
  102. package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
  103. package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
  104. package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
  105. package/dist/dashboard/assets/providers/zai.webp +0 -0
  106. package/dist/dashboard/assets/providers/zcode.webp +0 -0
  107. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
  108. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
  109. package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
  110. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  111. package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
  112. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  113. package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
  114. package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
  115. package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
  116. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  117. package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
  118. package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
  119. package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
  120. package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
  121. package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
  122. package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
  123. package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
  124. package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
  125. package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
  126. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  127. package/dist/dashboard/console-backgrounds.jpg +0 -0
  128. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  129. package/dist/dashboard/index.html +2 -2
  130. package/dist/dashboard/index.html.gz +0 -0
  131. package/dist/dashboard/precompressed-assets.json +60 -59
  132. package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
  133. package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
  134. package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
  135. package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
  136. package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
  137. package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
  138. package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
  139. package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
  140. package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
  141. package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
  142. package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
  143. package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
  144. package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
  145. package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
  146. package/package.json +4 -6
  147. package/scripts/cli/update.ts +15 -41
  148. package/src/boot/app.ts +58 -2
  149. package/src/boot/dependencies.ts +794 -732
  150. package/src/boot/lifecycle.ts +11 -12
  151. package/src/config/cooldowns.ts +34 -55
  152. package/src/config/network.ts +1 -1
  153. package/src/config/package-paths.ts +1 -1
  154. package/src/config/providers.ts +19 -14
  155. package/src/config/resources.ts +0 -5
  156. package/src/config/runtime.ts +2 -5
  157. package/src/config/spec.ts +33 -6
  158. package/src/config/usage.ts +27 -0
  159. package/src/console/api-keys/ban-constants.ts +38 -0
  160. package/src/console/api-keys/contracts.ts +71 -62
  161. package/src/console/api-keys/routes.ts +56 -24
  162. package/src/console/auth/access.ts +4 -4
  163. package/src/console/auth/account-reset.ts +1 -1
  164. package/src/console/auth/audit-recorder.ts +1 -1
  165. package/src/console/auth/lockout.ts +7 -3
  166. package/src/console/auth/row-coercion.ts +57 -12
  167. package/src/console/auth/session-resolver.ts +1 -1
  168. package/src/console/auth/session-service.ts +8 -2
  169. package/src/console/auth/session.ts +37 -31
  170. package/src/console/auth/setup-service.ts +4 -3
  171. package/src/console/auth/user-store.ts +1 -1
  172. package/src/console/backup/9router-map.ts +0 -3
  173. package/src/console/backup/contracts.ts +9 -9
  174. package/src/console/backup/export-endpoint.ts +5 -6
  175. package/src/console/backup/nine-router.ts +1 -7
  176. package/src/console/backup/routes.ts +29 -33
  177. package/src/console/backup/service.ts +468 -468
  178. package/src/console/backup/store.ts +47 -29
  179. package/src/console/backup/validate.ts +11 -1
  180. package/src/console/cli-tools/contracts.ts +43 -299
  181. package/src/console/cli-tools/fs-ops.ts +29 -0
  182. package/src/console/cli-tools/injectors/claude.ts +3 -3
  183. package/src/console/cli-tools/injectors/codex.ts +181 -181
  184. package/src/console/cli-tools/injectors/driver.ts +150 -247
  185. package/src/console/cli-tools/injectors/kilo.ts +146 -21
  186. package/src/console/cli-tools/injectors/opencode.ts +176 -153
  187. package/src/console/cli-tools/routes.ts +7 -1
  188. package/src/console/cli-tools/service.ts +8 -7
  189. package/src/console/cli-tools/store.ts +2 -2
  190. package/src/console/console-router.ts +32 -0
  191. package/src/console/dashboard-assets.ts +9 -9
  192. package/src/console/domain-registration.ts +16 -2
  193. package/src/console/inferhub/client.ts +0 -7
  194. package/src/console/inferhub/key-store.ts +1 -1
  195. package/src/console/inferhub/normalize.ts +2 -2
  196. package/src/console/inferhub/routes.ts +1 -1
  197. package/src/console/inferhub/types.ts +1 -1
  198. package/src/console/observability/contracts.ts +7 -1
  199. package/src/console/observability/live.ts +31 -18
  200. package/src/console/observability/logs.ts +1 -1
  201. package/src/console/observability/sse.ts +62 -8
  202. package/src/console/observability/store.ts +23 -4
  203. package/src/console/providers/catalog/catalog-projections.ts +1 -1
  204. package/src/console/providers/catalog/contracts.ts +39 -0
  205. package/src/console/providers/catalog/model-operations.ts +1 -1
  206. package/src/console/providers/catalog/provider-operations.ts +95 -17
  207. package/src/console/providers/catalog/public-model-store.ts +0 -0
  208. package/src/console/providers/catalog/routes.ts +27 -3
  209. package/src/console/providers/catalog/store.ts +147 -8
  210. package/src/console/providers/detail/routes.ts +1 -1
  211. package/src/console/providers/oauth/callback-listener.ts +100 -18
  212. package/src/console/providers/oauth/routes.ts +92 -13
  213. package/src/console/quota/account-quota-global.ts +41 -3
  214. package/src/console/quota/account-quota-shared.ts +10 -3
  215. package/src/console/quota/account-quota-tenant.ts +259 -66
  216. package/src/console/quota/daily-growth-job.ts +308 -0
  217. package/src/console/quota/native-identity.ts +2 -2
  218. package/src/console/quota/quota-view.ts +1 -1
  219. package/src/console/remote-support/routes.ts +1 -1
  220. package/src/console/remote-support/service.ts +2 -2
  221. package/src/console/routing/model/contracts.ts +1 -1
  222. package/src/console/routing/pools/contracts.ts +2 -2
  223. package/src/console/routing/pools/import-parse.ts +3 -3
  224. package/src/console/routing/pools/relay-deploy.ts +3 -3
  225. package/src/console/settings/contracts.ts +29 -2
  226. package/src/console/settings/store.ts +20 -0
  227. package/src/console/share/share-router.ts +100 -32
  228. package/src/console/share/share-stats.ts +11 -13
  229. package/src/console/share/share-usage.ts +1 -1
  230. package/src/console/shared/errors.ts +2 -2
  231. package/src/console/shared/object.ts +18 -0
  232. package/src/console/shared/read-through-cache.ts +4 -4
  233. package/src/console/shared/transaction.ts +14 -0
  234. package/src/console/system-prompt/contracts.ts +89 -12
  235. package/src/console/system-prompt/routes.ts +98 -45
  236. package/src/console/updates/apply-update.ts +244 -0
  237. package/src/console/updates/routes.ts +252 -97
  238. package/src/main.ts +11 -5
  239. package/src/network/http2-bidi.ts +1 -1
  240. package/src/network/ip-utils.ts +298 -0
  241. package/src/network/outbound-fetch.ts +1 -1
  242. package/src/network/pool/agent.ts +7 -6
  243. package/src/network/pool/byte-accounting.ts +1 -1
  244. package/src/network/pool/resolver.ts +16 -12
  245. package/src/network/pool/selector.ts +150 -136
  246. package/src/network/pool-health-machine.ts +1 -1
  247. package/src/network/rate-limit-verdict.ts +1 -1
  248. package/src/network/retry.ts +1 -1
  249. package/src/network/ssrf.ts +20 -71
  250. package/src/observability/event-log.ts +9 -2
  251. package/src/observability/event-query.ts +61 -291
  252. package/src/observability/log-sanitize.ts +1 -1
  253. package/src/observability/metrics.ts +4 -4
  254. package/src/observability/payload-capture.ts +6 -5
  255. package/src/observability/payload-store.ts +1 -1
  256. package/src/observability/runtime-metrics.ts +1 -1
  257. package/src/observability/telemetry-buffer.ts +19 -3
  258. package/src/observability/telemetry-reconciliation.ts +3 -3
  259. package/src/observability/token-speed.ts +1 -1
  260. package/src/persistence/connection-url.ts +1 -1
  261. package/src/persistence/db-handle.ts +1 -1
  262. package/src/persistence/db-pg.ts +2 -2
  263. package/src/persistence/db-sqlite.ts +3 -3
  264. package/src/persistence/dialect.ts +5 -1
  265. package/src/persistence/readiness.ts +1 -1
  266. package/src/persistence/redis.ts +1 -1
  267. package/src/persistence/schema.ts +121 -40
  268. package/src/persistence/shared-cache.ts +2 -2
  269. package/src/persistence/sort-order.ts +1 -1
  270. package/src/persistence/stores/api-key-store.ts +29 -2
  271. package/src/persistence/stores/share-store.ts +20 -8
  272. package/src/persistence/system-prompt-store.ts +63 -18
  273. package/src/persistence/tenant-preferences.ts +1 -1
  274. package/src/persistence/ttl-cache.ts +3 -3
  275. package/src/persistence/usage-flush.ts +7 -2
  276. package/src/protocol/canonical-model.ts +1 -1
  277. package/src/protocol/codecs.ts +6 -10
  278. package/src/protocol/dsml.ts +8 -125
  279. package/src/protocol/image-clamp.ts +2 -2
  280. package/src/protocol/reasoning-text.ts +1 -1
  281. package/src/protocol/reasoning.ts +7 -52
  282. package/src/protocol/registry.ts +50 -15
  283. package/src/protocol/request/chat.ts +46 -19
  284. package/src/protocol/request/codex.ts +1 -1
  285. package/src/protocol/request/messages.ts +46 -14
  286. package/src/protocol/request/responses.ts +19 -10
  287. package/src/protocol/response/chat.ts +25 -10
  288. package/src/protocol/response/codex.ts +1 -1
  289. package/src/protocol/response/messages.ts +1 -1
  290. package/src/protocol/response/omp-tool-call-markup.ts +1 -1
  291. package/src/protocol/response/responses.ts +1 -1
  292. package/src/protocol/sse-decoder.ts +50 -1
  293. package/src/protocol/stop-reason.ts +2 -2
  294. package/src/protocol/think-split.ts +347 -0
  295. package/src/protocol/translation/attachment-integrity.ts +3 -3
  296. package/src/protocol/translation/cache-controls.ts +8 -20
  297. package/src/protocol/translation/capabilities.ts +6 -6
  298. package/src/protocol/translation/dialect/deepseek.ts +10 -0
  299. package/src/protocol/translation/dialect/inband.ts +39 -0
  300. package/src/protocol/translation/dialect/model-thinking.ts +89 -0
  301. package/src/protocol/translation/dialect/qwen.ts +114 -0
  302. package/src/protocol/translation/dialect/resolver.ts +83 -0
  303. package/src/protocol/translation/dialect/rules.ts +93 -0
  304. package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
  305. package/src/protocol/translation/dialect/types.ts +58 -0
  306. package/src/protocol/translation/quirks.ts +18 -4
  307. package/src/protocol/translation/thinking.ts +12 -252
  308. package/src/protocol/translation/tool-id.ts +3 -3
  309. package/src/protocol/translation/tool-repair.ts +1 -1
  310. package/src/protocol/upstream/post-json.ts +6 -1
  311. package/src/providers/adapters/compatible-adapter.ts +71 -4
  312. package/src/providers/authentication/jwt-validator.ts +0 -5
  313. package/src/providers/authentication/oauth-client.ts +254 -253
  314. package/src/providers/authentication/oauth-flow-store.ts +42 -7
  315. package/src/providers/bundled-discovery-providers.ts +6 -30
  316. package/src/providers/configured-openai-providers.ts +82 -97
  317. package/src/providers/default-registry.ts +35 -37
  318. package/src/providers/discovery/catalog/catalog-format.ts +13 -2
  319. package/src/providers/discovery/discovery-types.ts +0 -1
  320. package/src/providers/discovery/openai-model-discovery.ts +2 -2
  321. package/src/providers/discovery/probe-phases.ts +48 -13
  322. package/src/providers/discovery/probing-service.ts +6 -8
  323. package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
  324. package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
  325. package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
  326. package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
  327. package/src/providers/integrations/antigravity/antigravity.ts +22 -13
  328. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
  329. package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
  330. package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
  331. package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
  332. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
  333. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
  334. package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
  335. package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
  336. package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
  337. package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
  338. package/src/providers/integrations/claude/claude-cch.ts +148 -148
  339. package/src/providers/integrations/claude/fallback-credit.ts +3 -7
  340. package/src/providers/integrations/cline/cline.ts +1 -1
  341. package/src/providers/integrations/codex/codex-device-code.ts +101 -101
  342. package/src/providers/integrations/codex/codex-oauth.ts +0 -1
  343. package/src/providers/integrations/commandcode/commandcode.ts +75 -115
  344. package/src/providers/integrations/commandcode/quota.ts +131 -51
  345. package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
  346. package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
  347. package/src/providers/integrations/cursor/cursor-request.ts +0 -87
  348. package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
  349. package/src/providers/integrations/cursor/cursor.ts +35 -3
  350. package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
  351. package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
  352. package/src/providers/integrations/factory/factory.ts +9 -9
  353. package/src/providers/integrations/github/github-discovery.ts +70 -12
  354. package/src/providers/integrations/github/github-oauth.ts +184 -14
  355. package/src/providers/integrations/github/github.ts +134 -26
  356. package/src/providers/integrations/grok/grok-oauth.ts +261 -261
  357. package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
  358. package/src/providers/integrations/grok/grok-quota.ts +16 -7
  359. package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
  360. package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
  361. package/src/providers/integrations/grok/grok.ts +86 -36
  362. package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
  363. package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
  364. package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
  365. package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
  366. package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
  367. package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
  368. package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
  369. package/src/providers/integrations/kiro/kiro-request.ts +8 -11
  370. package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
  371. package/src/providers/integrations/kiro/kiro.ts +80 -68
  372. package/src/providers/integrations/muse/muse-oauth.ts +0 -1
  373. package/src/providers/integrations/muse/muse-quota.ts +0 -4
  374. package/src/providers/integrations/opencode/fingerprint.ts +7 -5
  375. package/src/providers/integrations/opencode/provider.ts +17 -24
  376. package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
  377. package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
  378. package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
  379. package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
  380. package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
  381. package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
  382. package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
  383. package/src/providers/integrations/qoder/qoder.ts +422 -71
  384. package/src/providers/integrations/shared/connect.ts +0 -54
  385. package/src/providers/integrations/shared/hash.ts +15 -0
  386. package/src/providers/integrations/shared/session-cache.ts +136 -0
  387. package/src/providers/integrations/trae/trae-cache.ts +7 -87
  388. package/src/providers/integrations/trae/trae-catalog.ts +1 -6
  389. package/src/providers/integrations/trae/trae-protocol.ts +17 -8
  390. package/src/providers/integrations/trae/trae-realm.ts +0 -7
  391. package/src/providers/integrations/trae/trae.ts +51 -8
  392. package/src/providers/integrations/xai/xai-oauth.ts +5 -5
  393. package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
  394. package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
  395. package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
  396. package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
  397. package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
  398. package/src/providers/operations/account-error-classification.ts +198 -133
  399. package/src/providers/operations/account-health-service.ts +31 -39
  400. package/src/providers/operations/account-reset-service.ts +58 -1
  401. package/src/providers/operations/byok-wire-profile.ts +19 -15
  402. package/src/providers/operations/cli-platform.ts +31 -5
  403. package/src/providers/operations/client-version-resolver.ts +4 -6
  404. package/src/providers/operations/client-versions.ts +10 -7
  405. package/src/providers/operations/provider-catalog-seeder.ts +2 -0
  406. package/src/providers/operations/provider-catalog-service.ts +18 -2
  407. package/src/providers/operations/provider-credential-service.ts +19 -2
  408. package/src/providers/provider-contracts.ts +18 -7
  409. package/src/providers/provider-metadata.ts +17 -2
  410. package/src/providers/provider-model-definition.ts +22 -1
  411. package/src/providers/provider-probe.ts +4 -2
  412. package/src/providers/provider-usage.ts +50 -19
  413. package/src/providers/quota/cache.ts +29 -0
  414. package/src/providers/quota/quota-contracts.ts +4 -0
  415. package/src/providers/quota/refresh.ts +27 -4
  416. package/src/providers/quota/snapshot.ts +94 -0
  417. package/src/providers/search/search-provider.ts +1 -3
  418. package/src/providers/search/search-providers.ts +2 -1
  419. package/src/security/abuse.ts +4 -4
  420. package/src/security/admission/service.ts +13 -6
  421. package/src/security/admission/ttl.ts +61 -61
  422. package/src/security/api-key-auth.ts +17 -3
  423. package/src/security/client-router-fingerprint.ts +4 -4
  424. package/src/security/credential-codec.ts +3 -3
  425. package/src/security/csrf.ts +2 -2
  426. package/src/security/ip-boundary.ts +16 -189
  427. package/src/security/model-access-rule.ts +1 -1
  428. package/src/security/outbound-headers.ts +18 -0
  429. package/src/shared/invoke-guard.ts +47 -0
  430. package/src/transport/contracts.ts +13 -4
  431. package/src/transport/dispatch/attempt-finalize.ts +70 -22
  432. package/src/transport/dispatch/attempt-loop.ts +22 -0
  433. package/src/transport/dispatch/capture-policy.ts +34 -37
  434. package/src/transport/dispatch/empty-stream.ts +23 -20
  435. package/src/transport/dispatch/generation.ts +25 -45
  436. package/src/transport/dispatch/native-shared.ts +127 -0
  437. package/src/transport/dispatch/proxy-request.ts +3 -1
  438. package/src/transport/dispatch/responses-compact.ts +25 -46
  439. package/src/transport/dispatch/streaming/encoder.ts +18 -72
  440. package/src/transport/dispatch/streaming/finalize.ts +2 -2
  441. package/src/transport/dispatch/streaming/stream.ts +7 -3
  442. package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
  443. package/src/transport/dispatch/systemone.ts +20 -48
  444. package/src/transport/dispatch/token-count.ts +41 -38
  445. package/src/transport/dispatch/websearch.ts +19 -45
  446. package/src/transport/drain-endpoint.ts +1 -1
  447. package/src/transport/failure-policy.ts +21 -1
  448. package/src/transport/guards/auth-guard.ts +0 -6
  449. package/src/transport/guards/body-policy.ts +3 -3
  450. package/src/transport/guards/error-lifecycle.ts +4 -0
  451. package/src/transport/guards/headroom-guard.ts +1 -1
  452. package/src/transport/guards/pipeline.ts +1 -1
  453. package/src/transport/guards/request-context.ts +12 -17
  454. package/src/transport/request/preparer.ts +286 -7
  455. package/src/transport/request/rtk/compress-request.ts +3 -3
  456. package/src/transport/request/rtk/rtk-constants.ts +2 -2
  457. package/src/transport/request/rtk/rtk-filters.ts +8 -8
  458. package/src/transport/request/state.ts +7 -13
  459. package/src/transport/request/system-prompt-injection.ts +74 -11
  460. package/src/transport/request/tenant-preferences.ts +22 -0
  461. package/src/transport/routing/candidate-index.ts +2 -2
  462. package/src/transport/routing/round-robin.ts +1 -1
  463. package/src/transport/routing/route-catalog.ts +17 -18
  464. package/src/transport/routing/route-model.ts +126 -33
  465. package/src/transport/routing/router.ts +159 -37
  466. package/src/transport/shutdown-notice.ts +2 -2
  467. package/src/transport/surface/adapters.ts +3 -3
  468. package/src/transport/surface/chat/encode.ts +7 -7
  469. package/src/transport/surface/chat/parse.ts +7 -7
  470. package/src/transport/surface/completion.ts +2 -2
  471. package/src/transport/surface/content-parts.ts +1 -1
  472. package/src/transport/surface/dialects.ts +2 -2
  473. package/src/transport/surface/messages/parse.ts +3 -3
  474. package/src/transport/surface/reasoning-summary.ts +9 -14
  475. package/src/transport/surface/responses/adapter.ts +1 -1
  476. package/src/transport/surface/responses/errors.ts +0 -9
  477. package/src/transport/vision-handoff-cache.ts +58 -0
  478. package/src/transport/vision-handoff.ts +431 -0
  479. package/src/workers/account-identity-sweep.ts +2 -2
  480. package/src/workers/buddy-growth-tasks.ts +392 -346
  481. package/src/workers/daily-checkin.ts +102 -49
  482. package/src/workers/maintenance-lease.ts +2 -35
  483. package/src/workers/oauth-refresh-sweep.ts +8 -1
  484. package/src/workers/quota-refresh-sweep.ts +120 -82
  485. package/src/workers/scheduler.ts +67 -4
  486. package/src/workers/sweep-runner.ts +24 -5
  487. package/src/workers/zcode-gift-claim.ts +108 -0
  488. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
  489. package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
  490. package/dist/dashboard/assets/BannedPage-ChG5svFb.js +0 -1
  491. package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
  492. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
  493. package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
  494. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
  495. package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
  496. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
  497. package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
  498. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
  499. package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
  500. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
  501. package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
  502. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
  503. package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
  504. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
  505. package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
  506. package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
  507. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
  508. package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
  509. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
  510. package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
  511. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
  512. package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
  513. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
  514. package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
  515. package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
  516. package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
  517. package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
  518. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
  519. package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
  520. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
  521. package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
  522. package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
  523. package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
  524. package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
  525. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
  526. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  527. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
  528. package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
  529. package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
  530. package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
  531. package/dist/dashboard/assets/app-BayTTy01.js +0 -9
  532. package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
  533. package/dist/dashboard/assets/app-a159k0ub.css +0 -2
  534. package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
  535. package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
  536. package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
  537. package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
  538. package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
  539. package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
  540. package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
  541. package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
  542. package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
  543. package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
  544. package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
  545. package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
  546. package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
  547. package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
  548. package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
  549. package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
  550. package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
  551. package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
  552. package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
  553. package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
  554. package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
  555. package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
  556. package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
  557. package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
  558. package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
  559. package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
  560. package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
  561. package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
  562. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  563. package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
  564. package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
  565. package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
  566. package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
  567. package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
  568. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  569. package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
  570. package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
  571. package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
  572. package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
  573. package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
  574. package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
  575. package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
  576. package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
  577. package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
  578. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
  579. package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
  580. package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
  581. package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
  582. package/src/console/cli-tools/injectors/cline.ts +0 -110
  583. package/src/console/cli-tools/injectors/copilot.ts +0 -108
  584. package/src/console/cli-tools/injectors/cowork.ts +0 -64
  585. package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
  586. package/src/console/cli-tools/injectors/droid.ts +0 -96
  587. package/src/console/cli-tools/injectors/grok-build.ts +0 -70
  588. package/src/console/cli-tools/injectors/hermes.ts +0 -101
  589. package/src/console/cli-tools/injectors/jcode.ts +0 -115
  590. package/src/console/cli-tools/injectors/openclaw.ts +0 -161
  591. package/src/network/response-headers.ts +0 -39
  592. package/src/protocol/response/dsml.ts +0 -38
  593. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
  594. /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
  595. /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
@@ -7,7 +7,7 @@ import { createHash, randomBytes, randomInt } from "node:crypto";
7
7
  import { and, desc, eq, isNull, or, sql, type SQL } from "drizzle-orm";
8
8
  import type { CartethyiaDatabase } from "../db-handle";
9
9
  import { lockRows } from "../dialect";
10
- import { apiKeys, shareLinks, sharePopups, type ShareLinkKind, type ApiKeyModelAccessMode } from "../schema";
10
+ import { apiKeys, shareLinks, sharePopups, type ShareLinkKind, type ApiKeyModelAccessMode, type ApiKeyConcurrencyScope } from "../schema";
11
11
 
12
12
  /** Hashes a share bearer token for storage and lookup. */
13
13
  /**
@@ -38,7 +38,7 @@ function addressMatches(address: string): SQL {
38
38
  }
39
39
 
40
40
  /** Newly created share-link metadata. */
41
- export interface ShareLinkRecord {
41
+ interface ShareLinkRecord {
42
42
  readonly id: string;
43
43
  readonly apiKeyId: string;
44
44
  readonly kind: ShareLinkKind;
@@ -61,7 +61,7 @@ export interface ShareLinkSummary {
61
61
  }
62
62
 
63
63
  /** A stable link's encrypted token, or null when it predates token retention. */
64
- export interface ShareLinkToken {
64
+ interface ShareLinkToken {
65
65
  readonly id: string;
66
66
  readonly apiKeyId: string;
67
67
  readonly kind: ShareLinkKind;
@@ -86,6 +86,7 @@ export interface ShareLinkPolicy {
86
86
  readonly monthlyTokenLimit: number | null;
87
87
  readonly lifetimeTokenBudget: number | null;
88
88
  readonly maxConcurrentRequests: number | null;
89
+ readonly concurrencyScope: ApiKeyConcurrencyScope;
89
90
  readonly modelAccessMode: ApiKeyModelAccessMode | null;
90
91
  readonly modelList: readonly string[] | null;
91
92
  readonly modelPrefix: string | null;
@@ -102,13 +103,13 @@ export interface ShareLinkPolicy {
102
103
  }
103
104
 
104
105
  /** Public template fields authorized for a valid enrollment-link lookup. */
105
- export interface ShareApiKeyRow extends ShareLinkPolicy {
106
+ interface ShareApiKeyRow extends ShareLinkPolicy {
106
107
  readonly active: boolean;
107
108
  readonly createdAt: string;
108
109
  }
109
110
 
110
111
  /** A personal key revealed by its handoff link. */
111
- export interface ShareHandoffRow extends ShareLinkPolicy {
112
+ interface ShareHandoffRow extends ShareLinkPolicy {
112
113
  /** Encrypted personal credential; null when the row predates secret storage. */
113
114
  readonly keyEncrypted: Buffer | null;
114
115
  }
@@ -118,7 +119,7 @@ export interface ShareHandoffRow extends ShareLinkPolicy {
118
119
  * public lookup answers with a discriminated result instead of the caller
119
120
  * guessing which endpoint to try.
120
121
  */
121
- export type ShareLinkResolution =
122
+ type ShareLinkResolution =
122
123
  | { readonly kind: "enroll"; readonly key: ShareApiKeyRow }
123
124
  | { readonly kind: "handoff"; readonly key: ShareHandoffRow };
124
125
 
@@ -136,7 +137,7 @@ export type ShareLinkOutcome =
136
137
  | { readonly ok: true; readonly resolution: ShareLinkResolution }
137
138
  | { readonly ok: false; readonly refusal: ShareLinkRefusal };
138
139
 
139
- export interface SharedApiKeyMaterial {
140
+ interface SharedApiKeyMaterial {
140
141
  readonly keyHash: string;
141
142
  readonly keyPrefix: string;
142
143
  readonly clientIp: string;
@@ -145,7 +146,7 @@ export interface SharedApiKeyMaterial {
145
146
  readonly nameHint?: string;
146
147
  }
147
148
 
148
- export type SharedApiKeyIssueResult =
149
+ type SharedApiKeyIssueResult =
149
150
  | {
150
151
  readonly kind: "issued";
151
152
  readonly apiKeyId: string;
@@ -185,6 +186,7 @@ function mapShareRow(key: ApiKeyRow, link: ShareLinkRow, popup?: PopupRow): Shar
185
186
  monthlyTokenLimit: key.monthlyTokenLimit,
186
187
  lifetimeTokenBudget: key.lifetimeTokenBudget,
187
188
  maxConcurrentRequests: key.maxConcurrentRequests,
189
+ concurrencyScope: key.concurrencyScope ?? "family",
188
190
  modelAccessMode: key.modelAccessMode as ApiKeyModelAccessMode | null,
189
191
  modelList: key.modelList as readonly string[] | null,
190
192
  modelPrefix: key.modelPrefix,
@@ -213,6 +215,7 @@ function mapHandoffRow(key: ApiKeyRow, link: ShareLinkRow, popup?: PopupRow): Sh
213
215
  monthlyTokenLimit: key.monthlyTokenLimit,
214
216
  lifetimeTokenBudget: key.lifetimeTokenBudget,
215
217
  maxConcurrentRequests: key.maxConcurrentRequests,
218
+ concurrencyScope: key.concurrencyScope ?? "family",
216
219
  modelAccessMode: key.modelAccessMode as ApiKeyModelAccessMode | null,
217
220
  modelList: key.modelList as readonly string[] | null,
218
221
  modelPrefix: key.modelPrefix,
@@ -557,6 +560,12 @@ export class DrizzleShareLinkStore implements ShareLinkStore {
557
560
  // Nullable policy columns stay NULL; `model_access_mode` takes its
558
561
  // NOT NULL default ('whitelist'). Keep in sync with migration 0029 and
559
562
  // `normalizeShareChildPolicy` in `src/console/backup/store.ts`.
563
+ //
564
+ // Exception: a template with `concurrencyScope: "per_key"` lets each
565
+ // recipient carry its own concurrency cap, so the cap is copied to the
566
+ // child at issue (the child's admission identity is its own id, not the
567
+ // parent's). The `family` default keeps child caps NULL — they inherit
568
+ // the parent's cap through auth.
560
569
  const [child] = await tx
561
570
  .insert(apiKeys)
562
571
  .values({
@@ -571,6 +580,9 @@ export class DrizzleShareLinkStore implements ShareLinkStore {
571
580
  keyPrefix: material.keyPrefix,
572
581
  keyEncrypted: null,
573
582
  lifetimeTokensConsumed: 0,
583
+ ...(parent.concurrencyScope === "per_key"
584
+ ? { concurrencyScope: "per_key" as const, maxConcurrentRequests: parent.maxConcurrentRequests }
585
+ : {}),
574
586
  })
575
587
  .returning({ id: apiKeys.id, tenantId: apiKeys.tenantId, createdAt: apiKeys.createdAt });
576
588
  if (!child) throw new Error("shared API-key insert returned no row");
@@ -7,15 +7,20 @@ import { eq } from "drizzle-orm";
7
7
  import type { CartethyiaDatabase } from "./db-handle";
8
8
  import { tenantSystemPrompts } from "./schema";
9
9
  import type {
10
- ProviderPromptEntry,
11
- PromptSetting,
10
+ ScopePromptRef,
11
+ SystemPromptPreset,
12
12
  TenantSystemPrompts,
13
13
  } from "../console/system-prompt/contracts";
14
14
  import {
15
15
  EMPTY_TENANT_SYSTEM_PROMPTS,
16
+ SYSTEM_PROMPT_ALIAS_LIMIT,
17
+ SYSTEM_PROMPT_COMBO_LIMIT,
16
18
  SYSTEM_PROMPT_MODEL_LIMIT,
19
+ SYSTEM_PROMPT_PRESET_LIMIT,
17
20
  SYSTEM_PROMPT_PROVIDER_LIMIT,
18
21
  isPromptSetting,
22
+ isScopePromptRef,
23
+ isSystemPromptPreset,
19
24
  parseSystemPromptModelKey,
20
25
  } from "../console/system-prompt/contracts";
21
26
  import { bumpSettingsRevision } from "./settings-revision";
@@ -23,6 +28,24 @@ import { bumpSettingsRevision } from "./settings-revision";
23
28
  /** The stored row, normalized. Absent row reads as the empty configuration. */
24
29
  export type StoredTenantSystemPrompts = TenantSystemPrompts;
25
30
 
31
+ /**
32
+ * Normalizes one scope reference value. Accepts the new `{ kind, ... }` shape
33
+ * and the legacy per-provider entry shape (`{ followGlobal, setting? }`) so a
34
+ * hand-edited or pre-migration row stays valid. Invalid values become
35
+ * `undefined` (the caller drops the key).
36
+ */
37
+ function normalizeScopeRef(value: unknown, legacyProviderEntry?: boolean): ScopePromptRef | undefined {
38
+ if (isScopePromptRef(value)) return value;
39
+ if (legacyProviderEntry && typeof value === "object" && value !== null) {
40
+ const candidate = value as { followGlobal?: unknown; setting?: unknown };
41
+ if (candidate.followGlobal === true) return { kind: "global" };
42
+ if (candidate.followGlobal === false && isPromptSetting(candidate.setting)) {
43
+ return { kind: "custom", setting: candidate.setting };
44
+ }
45
+ }
46
+ return undefined;
47
+ }
48
+
26
49
  /**
27
50
  * Reads the tenant's stored configuration, defensively normalized: a
28
51
  * hand-edited or legacy row must not poison the dispatch path, so malformed
@@ -35,8 +58,11 @@ export async function readTenantSystemPrompts(
35
58
  const rows = await db
36
59
  .select({
37
60
  global: tenantSystemPrompts.global,
61
+ presets: tenantSystemPrompts.presets,
38
62
  providers: tenantSystemPrompts.providers,
39
63
  models: tenantSystemPrompts.models,
64
+ combos: tenantSystemPrompts.combos,
65
+ aliases: tenantSystemPrompts.aliases,
40
66
  })
41
67
  .from(tenantSystemPrompts)
42
68
  .where(eq(tenantSystemPrompts.tenantId, tenantId))
@@ -44,30 +70,43 @@ export async function readTenantSystemPrompts(
44
70
  const row = rows[0];
45
71
  if (row === undefined) return EMPTY_TENANT_SYSTEM_PROMPTS;
46
72
 
47
- const providers: Record<string, ProviderPromptEntry> = {};
48
- for (const [providerId, entry] of Object.entries(row.providers ?? {})) {
49
- if (typeof entry !== "object" || entry === null) continue;
50
- const candidate = entry as Partial<ProviderPromptEntry>;
51
- if (typeof candidate.followGlobal !== "boolean") continue;
52
- if (candidate.followGlobal === false) {
53
- if (!isPromptSetting(candidate.setting)) continue;
54
- providers[providerId] = { followGlobal: false, setting: candidate.setting };
55
- } else {
56
- providers[providerId] = { followGlobal: true };
57
- }
73
+ const presets: Record<string, SystemPromptPreset> = {};
74
+ for (const [id, preset] of Object.entries(row.presets ?? {})) {
75
+ if (isSystemPromptPreset(preset) && preset.id === id) presets[id] = preset;
58
76
  }
59
77
 
60
- const models: Record<string, PromptSetting> = {};
61
- for (const [key, setting] of Object.entries(row.models ?? {})) {
78
+ const providers: Record<string, ScopePromptRef> = {};
79
+ for (const [providerId, value] of Object.entries(row.providers ?? {})) {
80
+ const ref = normalizeScopeRef(value, true);
81
+ if (ref !== undefined) providers[providerId] = ref;
82
+ }
83
+
84
+ const models: Record<string, ScopePromptRef> = {};
85
+ for (const [key, value] of Object.entries(row.models ?? {})) {
62
86
  if (parseSystemPromptModelKey(key) === null) continue;
63
- if (!isPromptSetting(setting)) continue;
64
- models[key] = setting;
87
+ const ref = normalizeScopeRef(value);
88
+ if (ref !== undefined) models[key] = ref;
89
+ }
90
+
91
+ const combos: Record<string, ScopePromptRef> = {};
92
+ for (const [name, value] of Object.entries(row.combos ?? {})) {
93
+ const ref = normalizeScopeRef(value);
94
+ if (ref !== undefined) combos[name] = ref;
95
+ }
96
+
97
+ const aliases: Record<string, ScopePromptRef> = {};
98
+ for (const [name, value] of Object.entries(row.aliases ?? {})) {
99
+ const ref = normalizeScopeRef(value);
100
+ if (ref !== undefined) aliases[name] = ref;
65
101
  }
66
102
 
67
103
  return {
68
- global: row.global !== null && isPromptSetting(row.global) ? row.global : null,
104
+ global: row.global !== null && isScopePromptRef(row.global) ? row.global : null,
105
+ presets,
69
106
  providers,
70
107
  models,
108
+ combos,
109
+ aliases,
71
110
  };
72
111
  }
73
112
 
@@ -79,12 +118,18 @@ export async function writeTenantSystemPrompts(
79
118
  ): Promise<void> {
80
119
  // Keep the row bounded even though the API already enforces the caps: a
81
120
  // direct store caller must not be able to plant an unbounded map.
121
+ const presets = Object.entries(next.presets).slice(0, SYSTEM_PROMPT_PRESET_LIMIT);
82
122
  const providers = Object.entries(next.providers).slice(0, SYSTEM_PROMPT_PROVIDER_LIMIT);
83
123
  const models = Object.entries(next.models).slice(0, SYSTEM_PROMPT_MODEL_LIMIT);
124
+ const combos = Object.entries(next.combos).slice(0, SYSTEM_PROMPT_COMBO_LIMIT);
125
+ const aliases = Object.entries(next.aliases).slice(0, SYSTEM_PROMPT_ALIAS_LIMIT);
84
126
  const values = {
85
127
  global: next.global,
128
+ presets: Object.fromEntries(presets),
86
129
  providers: Object.fromEntries(providers),
87
130
  models: Object.fromEntries(models),
131
+ combos: Object.fromEntries(combos),
132
+ aliases: Object.fromEntries(aliases),
88
133
  updatedAt: new Date(),
89
134
  };
90
135
  await db
@@ -29,7 +29,7 @@ export class DrizzlePreferencesReader implements PreferencesReader {
29
29
  }
30
30
  }
31
31
 
32
- export interface CachedPreferencesReaderOptions {
32
+ interface CachedPreferencesReaderOptions {
33
33
  readonly ttlMs?: number;
34
34
  readonly maxEntries?: number;
35
35
  readonly revision?: () => number;
@@ -7,13 +7,13 @@ import { dedupeRequest } from "../network/deduplication";
7
7
  * - Revision-aware key invalidation
8
8
  */
9
9
 
10
- export interface TtlCacheOptions {
10
+ interface TtlCacheOptions {
11
11
  readonly ttlMs?: number;
12
12
  readonly maxEntries?: number;
13
13
  readonly now?: () => number;
14
14
  }
15
15
 
16
- export interface TtlCacheEntry<T> {
16
+ interface TtlCacheEntry<T> {
17
17
  readonly value: T;
18
18
  readonly at: number;
19
19
  }
@@ -134,7 +134,7 @@ export class TtlCache<T> {
134
134
  }
135
135
 
136
136
  /** One TTL-sized cache inside a {@link TtlCacheFamily}. */
137
- export interface TtlCacheFamilyOptions {
137
+ interface TtlCacheFamilyOptions {
138
138
  /** TTL applied when a caller does not pass its own. */
139
139
  readonly defaultTtlMs: number;
140
140
  /** Entry cap per TTL-sized cache. */
@@ -65,14 +65,19 @@ async function writeLifetimeDeltas(
65
65
  // text and the join against a uuid key fails. SQLite stores the key as text
66
66
  // and rejects `UPDATE ... FROM (VALUES ...)` outright, so the same rows are
67
67
  // a SELECT with named columns instead.
68
- const source = sqliteDialect()
68
+ const sqlite = sqliteDialect();
69
+ const source = sqlite
69
70
  ? `(${rows.map((row) => `SELECT '${row.apiKeyId}' AS id, ${row.delta} AS delta`).join(" UNION ALL ")})`
70
71
  : `(VALUES ${rows.map((row) => `('${row.apiKeyId}'::uuid, ${row.delta}::bigint)`).join(", ")}) AS v(id, delta)`;
72
+ // PostgreSQL already carries its alias in `source`; only the bare SQLite
73
+ // subquery needs the trailing alias appended. Emitting both (`AS v(id, delta) v`)
74
+ // is a syntax error on Postgres, so the flush silently never ran.
75
+ const from = sqlite ? `${source} v` : source;
71
76
  await db.execute(sql.raw(`
72
77
  UPDATE "api_keys"
73
78
  SET "lifetime_tokens_consumed" =
74
79
  COALESCE("api_keys"."lifetime_tokens_consumed", 0) + v.delta
75
- FROM ${source} v
80
+ FROM ${from}
76
81
  WHERE "api_keys"."id" = v.id
77
82
  `));
78
83
  }
@@ -614,7 +614,7 @@ export function hasEncryptedReasoning(parts: readonly ContentPart[]): boolean {
614
614
  * concurrent preflights would be an aliasing hazard; the memoized summary and
615
615
  * its extension set are treated as read-only.)
616
616
  */
617
- export interface PartSummary {
617
+ interface PartSummary {
618
618
  readonly hasImage: boolean;
619
619
  readonly hasDocument: boolean;
620
620
  readonly hasAudio: boolean;
@@ -89,7 +89,7 @@ export function hash32(input: string): number {
89
89
  return hash;
90
90
  }
91
91
 
92
- export const CLAUDE_TOOL_PREFIX = "_";
92
+ const CLAUDE_TOOL_PREFIX = "_";
93
93
 
94
94
  export type ClaudeWireObject = Record<string, unknown>;
95
95
 
@@ -408,13 +408,9 @@ export function isClaudeBillingHeaderText(text: string): boolean {
408
408
  // Well-formed UTF-16 coercion
409
409
 
410
410
  export function toWellFormedString(value: string): string {
411
- const maybe = value as unknown as { toWellFormed?: () => string };
412
- if (typeof maybe.toWellFormed === "function") return maybe.toWellFormed();
413
- // Fallback for runtimes without String.prototype.toWellFormed (replace lone surrogates)
414
- return value.replace(
415
- /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,
416
- "\uFFFD",
417
- );
411
+ // String.prototype.toWellFormed is ES2024; guaranteed by Bun ≥1.4, the only
412
+ // supported runtime.
413
+ return value.toWellFormed();
418
414
  }
419
415
 
420
416
  export function toWellFormedDeep(value: unknown): unknown {
@@ -873,7 +869,7 @@ export const BARE_ROOT_ENDPOINTS: Partial<Record<WireFamily, string>> = {
873
869
  };
874
870
 
875
871
  /** An image part's provider-neutral source, resolved from any supported origin shape. */
876
- export interface ResolvedImageSource {
872
+ interface ResolvedImageSource {
877
873
  /** URL or `data:` URI. Mutually usable with `fileId`. */
878
874
  url?: string;
879
875
  /** Files API reference (Chat/Responses `file_id`). */
@@ -883,7 +879,7 @@ export interface ResolvedImageSource {
883
879
  }
884
880
 
885
881
  /** An RFC 2397 base64 `data:` URL split into the media type and bytes it carries. */
886
- export interface DataUrlPayload {
882
+ interface DataUrlPayload {
887
883
  /** The declared media type (the `image/png` of `data:image/png;base64,…`). */
888
884
  readonly mediaType: string;
889
885
  /** The base64 payload, with the `data:` prefix and media type removed. */
@@ -3,28 +3,18 @@
3
3
  *
4
4
  * DeepSeek-family models occasionally write their tool calls as XML-like DSML
5
5
  * markup inside the plain-text content stream instead of the structured
6
- * `tool_calls` field. Two upstream families in this gateway see it:
6
+ * `tool_calls` field. The gateway sees it from one upstream family:
7
7
  *
8
8
  * - CodeBuddy/WorkBuddy emit strict blocks (`<|DSML| calls>…</|DSML| calls>`)
9
9
  * that must be parsed exactly, with declared-tool allowlisting.
10
- * - Qoder emits looser blocks (`<[^>]*DSML[^>]*calls>…`) that tolerate any
11
- * intermediate characters in the tag, and its parameter values are plain
12
- * strings.
13
- *
14
- * The two share the same recovery idea, so the mechanics live here once and
15
- * each caller picks its dialect through options. The defaults reproduce the
16
- * CodeBuddy dialect exactly (`app/dsml.py` in codebuddy2api); Qoder passes
17
- * `fuzzy: true` and its id factory, mirroring `qoder_proxy.py`.
18
10
  */
19
- import { randomUUID } from "node:crypto";
20
-
21
11
  export interface DsmlToolCall {
22
12
  readonly id: string;
23
13
  readonly name: string;
24
14
  readonly arguments: string;
25
15
  }
26
16
 
27
- export interface DsmlRecovery {
17
+ interface DsmlRecovery {
28
18
  readonly calls: readonly DsmlToolCall[];
29
19
  readonly clean: string;
30
20
  }
@@ -34,19 +24,15 @@ export interface DsmlParseOptions {
34
24
  readonly separators?: readonly string[];
35
25
  /** Block names a strict opener may name. */
36
26
  readonly blockNames?: readonly string[];
37
- /** When true, match the Qoder fuzzy form (`<[^>]*DSML[^>]*…`). */
38
- readonly fuzzy?: boolean;
39
- /** How parameter values decode: `json` (CodeBuddy) or `string` (Qoder). */
27
+ /** How parameter values decode: `json` (CodeBuddy) or `string`. */
40
28
  readonly params?: "json" | "string";
41
- /** Call id factory. Defaults to sequential for strict, uuid for fuzzy. */
29
+ /** Call id factory. Defaults to sequential. */
42
30
  readonly id?: (call: { readonly name: string }, index: number, seq: number) => string;
43
31
  /** Non-empty allowlist makes the strict dialect reject undeclared names. */
44
32
  readonly allowedNames?: ReadonlySet<string>;
45
33
  /**
46
34
  * When true, JSON stringifies argument objects with recursively sorted keys,
47
- * matching the reference bridge (`json.dumps(..., sort_keys=True)`). The
48
- * strict dialect defaults to true; the fuzzy dialect never sorts, matching
49
- * its reference's `json.dumps(params)`.
35
+ * matching the reference bridge (`json.dumps(..., sort_keys=True)`).
50
36
  */
51
37
  readonly sortKeys?: boolean;
52
38
  /**
@@ -63,7 +49,7 @@ const DEFAULT_MAX_BLOCK_BYTES = 256 * 1024;
63
49
  const TOOL_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,63}$/;
64
50
 
65
51
  /** Escapes regex metacharacters so a `|` separator is literal, not alternation. */
66
- export function escapeRegExp(value: string): string {
52
+ function escapeRegExp(value: string): string {
67
53
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68
54
  }
69
55
 
@@ -75,11 +61,7 @@ function sequentialId(_call: { readonly name: string }, _index: number, seq: num
75
61
  return `call_dsml_${seq}_${_call.name}`;
76
62
  }
77
63
 
78
- function uuidId(_call: { readonly name: string }, _index: number, _seq: number): string {
79
- return `call_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
80
- }
81
-
82
- export interface DsmlParseResult {
64
+ interface DsmlParseResult {
83
65
  readonly text: string;
84
66
  readonly calls: readonly DsmlToolCall[];
85
67
  }
@@ -257,72 +239,11 @@ function normalizeForSort(value: unknown): unknown {
257
239
  return out;
258
240
  }
259
241
 
260
- /** The Qoder fuzzy block scanner, mirroring the reference bridge's regexes. */
261
- const FUZZY_CALLS_RE = /<[^>]*DSML[^>]*calls>([\s\S]*?)<\/[^>]*DSML[^>]*calls>/;
262
- const FUZZY_INVOKE_RE = /<[^>]*DSML[^>]*invoke\s+name=["']([^"']+)["']>([\s\S]*?)<\/[^>]*invoke>/g;
263
- const FUZZY_PARAM_RE = /<[^>]*DSML[^>]*parameter\s+name=["']([^"']+)["'][^>]*>([\s\S]*?)<\/[^>]*parameter>/g;
264
-
265
- function parseFuzzyDsml(text: string, options: DsmlParseOptions): DsmlRecovery {
266
- const id = options.id ?? uuidId;
267
- if (text.length === 0 || !text.includes("DSML")) return { calls: [], clean: text };
268
-
269
- const callsMatch = FUZZY_CALLS_RE.exec(text);
270
- let block: string;
271
- let clean: string;
272
-
273
- if (callsMatch) {
274
- block = callsMatch[1] ?? "";
275
- const before = text.slice(0, callsMatch.index).trimEnd();
276
- const after = text.slice(callsMatch.index + callsMatch[0].length).trimStart();
277
- clean = (before + " " + after).trim();
278
- } else {
279
- // No <calls> wrapper (issue #51): find bare <invoke> blocks in the whole
280
- // text, then cut the prose off at the last `<` before the first invoke —
281
- // exclusive of the invoke's own tag start, exactly like the reference's
282
- // `text.rfind("<", 0, first_index)`.
283
- FUZZY_INVOKE_RE.lastIndex = 0;
284
- const first = FUZZY_INVOKE_RE.exec(text);
285
- if (!first) return { calls: [], clean: text };
286
- block = text;
287
- const cut = text.lastIndexOf("<", first.index - 1);
288
- clean = text.slice(0, cut >= 0 ? cut : first.index).trim();
289
- }
290
-
291
- const calls: DsmlToolCall[] = [];
292
- FUZZY_INVOKE_RE.lastIndex = 0;
293
- let inv: RegExpExecArray | null;
294
- let seq = 0;
295
- while ((inv = FUZZY_INVOKE_RE.exec(block)) !== null) {
296
- const params: Record<string, string> = {};
297
- FUZZY_PARAM_RE.lastIndex = 0;
298
- let par: RegExpExecArray | null;
299
- while ((par = FUZZY_PARAM_RE.exec(inv[2] ?? "")) !== null) {
300
- params[par[1] ?? ""] = (par[2] ?? "").trim();
301
- }
302
- const call = { name: inv[1] ?? "", arguments: JSON.stringify(params) };
303
- calls.push({ ...call, id: id(call, calls.length, seq++) });
304
- }
305
-
306
- return { calls, clean };
307
- }
308
-
309
242
  /**
310
- * Recovers DSML tool calls from one plain-text string using the Qoder fuzzy
311
- * dialect. Always returns `{ calls, clean }` — no-DSML input returns the text
312
- * unchanged with no calls.
313
- */
314
- export function parseFuzzyDsmlToolCalls(text: string): DsmlRecovery {
315
- return parseFuzzyDsml(text, {});
316
- }
317
-
318
- /**
319
- * Recovers DSML tool calls from one plain-text string. The dialect is chosen
320
- * by `options.fuzzy`; each dialect keeps its exact public shape:
243
+ * Recovers DSML tool calls from one plain-text string.
321
244
  *
322
245
  * - strict (CodeBuddy): returns `{ text, calls }`, with `text` the clean
323
246
  * prose; `undefined` when nothing was recovered.
324
- * - fuzzy (Qoder): returns `{ clean, calls }`, with no-DSML input returning
325
- * `{ calls: [], clean: text }`.
326
247
  *
327
248
  * Callers that need the streaming strict parser use {@link DsmlStreamParser}.
328
249
  */
@@ -330,7 +251,6 @@ export function parseDsmlToolCalls(
330
251
  text: string,
331
252
  options: DsmlParseOptions = {},
332
253
  ): DsmlRecovery | DsmlParseResult | undefined {
333
- if (options.fuzzy === true) return parseFuzzyDsml(text, options);
334
254
  if (!text.includes("DSML")) return undefined;
335
255
  const parser = new DsmlStreamParser(options);
336
256
  const first = parser.push(text);
@@ -338,40 +258,3 @@ export function parseDsmlToolCalls(
338
258
  const recovered = [...first.calls, ...last.calls];
339
259
  return recovered.length === 0 ? undefined : { text: `${first.text}${last.text}`.trim(), calls: recovered };
340
260
  }
341
-
342
- /**
343
- * The holdable tail of a content chunk: a short suffix that looks like the
344
- * start of a DSML tag and may finish in the next frame. Empty when there is
345
- * nothing worth holding. Mirrors `_dsml_tail_hold` in the reference bridge.
346
- */
347
- export function dsmlTailHold(text: string): string {
348
- const at = text.lastIndexOf("<");
349
- if (at < 0 || text.length - at > 24) return "";
350
- const tail = text.slice(at);
351
- // A complete tag already — nothing to wait for.
352
- if (tail.includes(">")) return "";
353
- const body = tail.replace(/\uFF5C/g, "|").toUpperCase().slice(1).replace(/^\//, "");
354
- if (body.length === 0) return tail; // bare "<" or "</"
355
- if (!body.startsWith("|")) return "";
356
- const rest = body.slice(1);
357
- if (rest.length === 0) return tail;
358
- if ("DSML".startsWith(rest) || rest.startsWith("DSML")) return tail;
359
- return "";
360
- }
361
-
362
- const DSML_CLOSE_TAG_RE = /<\/(?:[^>]*DSML[^>]*|parameter|invoke|calls|tool_calls|function_calls)\s*>\s*$/i;
363
-
364
- /**
365
- * Removes leftover DSML closing tags from the tail of a text. Anchored to the
366
- * end and looped, so several trailing tags come off one per pass; a closing
367
- * tag in the middle of a text is not residue and is preserved.
368
- */
369
- export function stripDsmlCloseTags(text: string): string {
370
- let previous: string | null = null;
371
- let out = text.trimEnd();
372
- while (previous !== out) {
373
- previous = out;
374
- out = out.replace(DSML_CLOSE_TAG_RE, "").trimEnd();
375
- }
376
- return out;
377
- }
@@ -69,7 +69,7 @@ export interface ImageClampInput {
69
69
  readonly maxBytesOverride?: number;
70
70
  }
71
71
 
72
- export interface ImageClampDecision {
72
+ interface ImageClampDecision {
73
73
  readonly action: ImageClampAction;
74
74
  readonly reason: ImageClampReason;
75
75
  /** Longest edge allowed for this image, given how many the request carries. */
@@ -96,7 +96,7 @@ export function base64DecodedByteLength(data: string): number {
96
96
  }
97
97
 
98
98
  /** The per-image longest-edge ceiling for a request carrying `imageCount` images. */
99
- export function imageClampMaxDimensionPx(imageCount: number): number {
99
+ function imageClampMaxDimensionPx(imageCount: number): number {
100
100
  return imageCount > IMAGE_CLAMP_MANY_IMAGE_COUNT
101
101
  ? IMAGE_CLAMP_MANY_IMAGE_MAX_DIMENSION_PX
102
102
  : IMAGE_CLAMP_MAX_DIMENSION_PX;
@@ -1,6 +1,6 @@
1
1
  /** The reasoning-shaped part this reads. Structural, so any reasoning part —
2
2
  * including a `ContentPart` narrowed to `kind: "reasoning"` — is accepted. */
3
- export interface ReasoningTextSource {
3
+ interface ReasoningTextSource {
4
4
  readonly payload?: unknown;
5
5
  readonly summary?: string;
6
6
  readonly opaque?: boolean;
@@ -1,6 +1,7 @@
1
1
  import type { CanonicalRequest, ReasoningEffort, ReasoningIntent } from "./canonical-model";
2
2
  import { budgetToLevel } from "./translation/thinking";
3
3
  import { isRecord } from "./codecs";
4
+ import { DEEPSEEK_DEFAULT_EFFORT } from "./translation/dialect/thinking-ladder";
4
5
 
5
6
  /**
6
7
  * The effective reasoning effort for a canonical intent, deriving one from an
@@ -47,7 +48,7 @@ export function applyDeepSeekReasoning(payload: Record<string, unknown>): void {
47
48
  if (thinking === undefined) payload["thinking"] = { type: "enabled" };
48
49
  else if (type.length === 0) thinking["type"] = "enabled";
49
50
  if (typeof payload["reasoning_effort"] !== "string" || payload["reasoning_effort"].length === 0)
50
- payload["reasoning_effort"] = "high";
51
+ payload["reasoning_effort"] = DEEPSEEK_DEFAULT_EFFORT;
51
52
  }
52
53
 
53
54
  /**
@@ -151,59 +152,13 @@ export function geminiThinkingOutputFloor(reasoning: CanonicalRequest["reasoning
151
152
 
152
153
  /**
153
154
  * Minimum output budget for a caller whose requested effort the ladder could
154
- * not serve as asked.
155
+ * not serve as asked. See the module doc for why both clamp directions need it.
156
+ * Only ever raises, and only to the model's own catalog limit.
155
157
  *
156
- * A wire ladder can only serve levels the upstream accepts, so a request for
157
- * `low` may be clamped up to whatever that model offers — `hy4-preview-f`
158
- * serves `high` and nothing else — and a request for `max` may be clamped down
159
- * to it. Either way the caller sized `max_tokens` for the effort it asked for,
160
- * and effort is not free: the applied level spends its budget on reasoning
161
- * before it writes the answer, so an answer that fit in 2048 tokens of visible
162
- * text no longer fits once the reasoning share is taken out of the same 2048.
163
- *
164
- * Both directions need the floor, because the mismatch is what costs tokens,
165
- * not its sign. A caller who asked for `max` with a 2048-token answer in mind
166
- * is owed the same headroom as one who asked for `low`: the applied level is
167
- * `high` either way, and `high` reasons out of that 2048 regardless.
168
- *
169
- * The symptom this removes is a truncated answer that an agent retries from
170
- * the top — reading as a loop ("let me do / let me go / let me do it") rather
171
- * than as a short limit.
172
- *
173
- * Only ever raises, and only to the model's own catalog limit: a caller whose
174
- * effort was served exactly as asked keeps its number, and no provider is
175
- * asked for more output than it declares.
158
+ * Re-exported from `translation/dialect/model-thinking` (the canonical helper);
159
+ * this shim keeps existing importers (e.g. buddy-chat-shared) stable.
176
160
  */
177
- export function clampedEffortOutputFloor(
178
- requested: string | undefined,
179
- applied: string | undefined,
180
- modelOutputLimit: number | undefined,
181
- ): number | undefined {
182
- if (requested === undefined || applied === undefined) return undefined;
183
- const from = BUDDY_LEVEL_RANK[requested.trim().toLowerCase()];
184
- const to = BUDDY_LEVEL_RANK[applied.trim().toLowerCase()];
185
- if (from === undefined || to === undefined || from === to) return undefined;
186
- if (modelOutputLimit === undefined || !Number.isFinite(modelOutputLimit) || modelOutputLimit <= 0)
187
- return undefined;
188
- // Leave headroom instead of claiming the whole catalog limit: the caller's
189
- // number was a real preference about answer size, and the goal is to stop
190
- // truncating, not to spend the model's ceiling on every turn.
191
- return Math.max(CLAMPED_EFFORT_MIN_OUTPUT_TOKENS, Math.floor(modelOutputLimit / 2));
192
- }
193
-
194
- const CLAMPED_EFFORT_MIN_OUTPUT_TOKENS = 16_384;
195
-
196
- const BUDDY_LEVEL_RANK: Readonly<Record<string, number>> = {
197
- off: 0,
198
- none: 0,
199
- instant: 1,
200
- minimal: 2,
201
- low: 3,
202
- medium: 4,
203
- high: 5,
204
- xhigh: 6,
205
- max: 7,
206
- };
161
+ export { outputFloorFor as clampedEffortOutputFloor } from "./translation/dialect/model-thinking";
207
162
 
208
163
  /** Builds the Anthropic thinking block from canonical reasoning intent. */
209
164
  export function buildAnthropicThinkingPayload(reasoning: ReasoningIntent): Record<string, unknown> {