@omnicross/core 0.1.5 → 0.1.7

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 (231) hide show
  1. package/dist/ApiConverter.cjs +2 -2
  2. package/dist/ApiConverter.js +1 -1
  3. package/dist/{CompletionService-DdWaAxq0.d.ts → CompletionService-C3BX0Ovy.d.ts} +4 -3
  4. package/dist/{CompletionService-D3a47vdU.d.cts → CompletionService-kNA17cfI.d.cts} +4 -3
  5. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +8 -6
  6. package/dist/auth/GeminiCodeAssistProjectResolver.js +6 -4
  7. package/dist/{chunk-3Z5BQZFM.cjs → chunk-235MXAGX.cjs} +3 -1
  8. package/dist/{chunk-HRRYG2AX.js → chunk-23S5OPIO.js} +10 -1
  9. package/dist/chunk-25GXJCEZ.cjs +40 -0
  10. package/dist/chunk-2E42SAW3.cjs +323 -0
  11. package/dist/chunk-2JYRIADS.js +40 -0
  12. package/dist/chunk-2Z4XM3H5.cjs +44 -0
  13. package/dist/{chunk-BODMAL6G.js → chunk-32O35CIF.js} +271 -91
  14. package/dist/chunk-3SCOLIPT.cjs +63 -0
  15. package/dist/{chunk-SYQF7OIR.js → chunk-3WD4HTR2.js} +2 -0
  16. package/dist/{chunk-CGKLE6IW.cjs → chunk-4BB343BI.cjs} +1489 -520
  17. package/dist/{chunk-VACWOIRE.cjs → chunk-4VZX5F4T.cjs} +39 -15
  18. package/dist/{chunk-5ZQBEOMD.js → chunk-5NJLQ6T6.js} +46 -12
  19. package/dist/chunk-5RSZYQJH.cjs +62 -0
  20. package/dist/chunk-6QQG7YG3.cjs +153 -0
  21. package/dist/{chunk-IFXJ2BG7.js → chunk-7C7DET6S.js} +51 -6
  22. package/dist/{chunk-IK3KE3AU.js → chunk-DE2SRBFZ.js} +1 -1
  23. package/dist/{chunk-FH2P4OCD.js → chunk-E3COOKUP.js} +1240 -271
  24. package/dist/{chunk-UHGVBFU3.js → chunk-EBLFCNPY.js} +38 -14
  25. package/dist/chunk-FU7EAR73.js +62 -0
  26. package/dist/chunk-FXR6N3SR.js +323 -0
  27. package/dist/{chunk-SUEGJ75D.cjs → chunk-GDIXXKF2.cjs} +46 -12
  28. package/dist/chunk-GFIZ6LQI.js +170 -0
  29. package/dist/chunk-GL6XM527.js +61 -0
  30. package/dist/chunk-HVBBDBS3.cjs +350 -0
  31. package/dist/{chunk-46ZIFWYG.cjs → chunk-IIRPUVPH.cjs} +7 -1
  32. package/dist/chunk-J3QQ6JS5.cjs +170 -0
  33. package/dist/{chunk-JWVTXXPX.cjs → chunk-KBM2W6UY.cjs} +8 -4
  34. package/dist/chunk-LB6EBS6Y.js +69 -0
  35. package/dist/{chunk-IWQAX3CS.js → chunk-LVUCAWMK.js} +1 -1
  36. package/dist/chunk-MKF46GNZ.js +350 -0
  37. package/dist/{chunk-HYN75H6G.cjs → chunk-MUHWHQVM.cjs} +1 -1
  38. package/dist/chunk-MYYR3HZL.js +44 -0
  39. package/dist/{chunk-MFZ5437A.js → chunk-N5T4M4GU.js} +1 -1
  40. package/dist/{chunk-5ENKBSWO.js → chunk-N7OOJI5V.js} +88 -8
  41. package/dist/chunk-NKVZ6Z3Y.cjs +61 -0
  42. package/dist/chunk-OF3BECU2.cjs +69 -0
  43. package/dist/{chunk-SNYBEXLB.cjs → chunk-OJGQKHN5.cjs} +3 -1
  44. package/dist/{chunk-DDMRUL76.cjs → chunk-OKMJLEXX.cjs} +5 -5
  45. package/dist/chunk-OPRS4IQZ.js +153 -0
  46. package/dist/chunk-OTZC4TNH.cjs +79 -0
  47. package/dist/{chunk-3Q6Z6JTO.cjs → chunk-OVQBNVSV.cjs} +287 -107
  48. package/dist/{chunk-4XMYPW46.cjs → chunk-OXHNSZ53.cjs} +84 -44
  49. package/dist/{chunk-C6OV7GCC.js → chunk-PEGTIPB3.js} +52 -12
  50. package/dist/chunk-PO3XB56Z.js +79 -0
  51. package/dist/{chunk-D3TDKWRP.js → chunk-RJ5ZGNI3.js} +54 -5
  52. package/dist/{chunk-PG7PS2NN.cjs → chunk-RJUONLUQ.cjs} +59 -10
  53. package/dist/{chunk-BPKCU575.js → chunk-SOO562NM.js} +3 -1
  54. package/dist/{chunk-6QZGDLL4.js → chunk-TOVPMMHM.js} +5 -1
  55. package/dist/{chunk-K52THW2C.cjs → chunk-V4TXMEJS.cjs} +5 -5
  56. package/dist/{chunk-G2UTNSGU.js → chunk-WNKWAEUR.js} +7 -1
  57. package/dist/{chunk-UNWKG6EI.cjs → chunk-XPIOJQS2.cjs} +51 -6
  58. package/dist/chunk-XXFYLL7R.js +63 -0
  59. package/dist/{chunk-KFI44N2R.cjs → chunk-YNJNTCSQ.cjs} +10 -1
  60. package/dist/{chunk-VQY5W4YW.cjs → chunk-YWL7YZWS.cjs} +90 -10
  61. package/dist/completion/ApiKeyPoolService.cjs +12 -0
  62. package/dist/completion/ApiKeyPoolService.d.cts +6 -0
  63. package/dist/completion/ApiKeyPoolService.d.ts +6 -0
  64. package/dist/completion/ApiKeyPoolService.js +12 -0
  65. package/dist/completion/CompletionService.cjs +30 -24
  66. package/dist/completion/CompletionService.d.cts +3 -1
  67. package/dist/completion/CompletionService.d.ts +3 -1
  68. package/dist/completion/CompletionService.js +29 -23
  69. package/dist/completion.cjs +32 -26
  70. package/dist/completion.d.cts +3 -1
  71. package/dist/completion.d.ts +3 -1
  72. package/dist/completion.js +31 -25
  73. package/dist/index.cjs +56 -29
  74. package/dist/index.d.cts +8 -8
  75. package/dist/index.d.ts +8 -8
  76. package/dist/index.js +61 -34
  77. package/dist/{keyPolicy-Cp0FntZl.d.cts → keyPolicy-zdWTu3CQ.d.cts} +1 -1
  78. package/dist/{keyPolicy-Cp0FntZl.d.ts → keyPolicy-zdWTu3CQ.d.ts} +1 -1
  79. package/dist/outbound-api/auditCapture.cjs +2 -2
  80. package/dist/outbound-api/auditCapture.d.cts +3 -4
  81. package/dist/outbound-api/auditCapture.d.ts +3 -4
  82. package/dist/outbound-api/auditCapture.js +1 -1
  83. package/dist/outbound-api/quotaWarn.d.cts +1 -1
  84. package/dist/outbound-api/quotaWarn.d.ts +1 -1
  85. package/dist/outbound-api/routeResolver.cjs +2 -2
  86. package/dist/outbound-api/routeResolver.d.cts +5 -3
  87. package/dist/outbound-api/routeResolver.d.ts +5 -3
  88. package/dist/outbound-api/routeResolver.js +1 -1
  89. package/dist/outbound-api/subscriptionRegistryPort.d.cts +2 -0
  90. package/dist/outbound-api/subscriptionRegistryPort.d.ts +2 -0
  91. package/dist/outbound-api/types.d.cts +4 -2
  92. package/dist/outbound-api/types.d.ts +4 -2
  93. package/dist/outbound-api.cjs +52 -25
  94. package/dist/outbound-api.d.cts +131 -87
  95. package/dist/outbound-api.d.ts +131 -87
  96. package/dist/outbound-api.js +55 -28
  97. package/dist/pipeline/AccountAllowanceScheduling.cjs +15 -0
  98. package/dist/pipeline/AccountAllowanceScheduling.d.cts +91 -0
  99. package/dist/pipeline/AccountAllowanceScheduling.d.ts +91 -0
  100. package/dist/pipeline/AccountAllowanceScheduling.js +15 -0
  101. package/dist/pipeline/AccountAllowanceStore.cjs +20 -0
  102. package/dist/pipeline/AccountAllowanceStore.d.cts +77 -0
  103. package/dist/pipeline/AccountAllowanceStore.d.ts +77 -0
  104. package/dist/pipeline/AccountAllowanceStore.js +20 -0
  105. package/dist/pipeline/AccountRouteActivity.cjs +10 -0
  106. package/dist/pipeline/AccountRouteActivity.d.cts +61 -0
  107. package/dist/pipeline/AccountRouteActivity.d.ts +61 -0
  108. package/dist/pipeline/AccountRouteActivity.js +10 -0
  109. package/dist/pipeline/AuthSource.d.cts +11 -2
  110. package/dist/pipeline/AuthSource.d.ts +11 -2
  111. package/dist/pipeline/BoundAccountSelectionError.cjs +10 -0
  112. package/dist/pipeline/BoundAccountSelectionError.d.cts +28 -0
  113. package/dist/pipeline/BoundAccountSelectionError.d.ts +28 -0
  114. package/dist/pipeline/BoundAccountSelectionError.js +10 -0
  115. package/dist/pipeline/LlmConfigProviderAuth.d.cts +1 -0
  116. package/dist/pipeline/LlmConfigProviderAuth.d.ts +1 -0
  117. package/dist/pipeline/ServerOverloadCounter.cjs +16 -0
  118. package/dist/pipeline/ServerOverloadCounter.d.cts +77 -0
  119. package/dist/pipeline/ServerOverloadCounter.d.ts +77 -0
  120. package/dist/pipeline/ServerOverloadCounter.js +16 -0
  121. package/dist/pipeline/SubscriptionAccountHealth.cjs +6 -2
  122. package/dist/pipeline/SubscriptionAccountHealth.d.cts +65 -8
  123. package/dist/pipeline/SubscriptionAccountHealth.d.ts +65 -8
  124. package/dist/pipeline/SubscriptionAccountHealth.js +5 -1
  125. package/dist/pipeline/SubscriptionAuthSource.cjs +5 -2
  126. package/dist/pipeline/SubscriptionAuthSource.d.cts +1 -0
  127. package/dist/pipeline/SubscriptionAuthSource.d.ts +1 -0
  128. package/dist/pipeline/SubscriptionAuthSource.js +4 -1
  129. package/dist/pipeline/SubscriptionAuthStrategy.d.cts +8 -3
  130. package/dist/pipeline/SubscriptionAuthStrategy.d.ts +8 -3
  131. package/dist/pipeline/executeProviderCall.cjs +2 -2
  132. package/dist/pipeline/executeProviderCall.d.cts +8 -3
  133. package/dist/pipeline/executeProviderCall.d.ts +8 -3
  134. package/dist/pipeline/executeProviderCall.js +1 -1
  135. package/dist/pipeline/resolveProviderChain.d.cts +2 -3
  136. package/dist/pipeline/resolveProviderChain.d.ts +2 -3
  137. package/dist/pipeline/resolveSubscriptionChain.d.cts +1 -0
  138. package/dist/pipeline/resolveSubscriptionChain.d.ts +1 -0
  139. package/dist/pipeline/upstreamFetch.cjs +5 -3
  140. package/dist/pipeline/upstreamFetch.d.cts +35 -6
  141. package/dist/pipeline/upstreamFetch.d.ts +35 -6
  142. package/dist/pipeline/upstreamFetch.js +4 -2
  143. package/dist/pipeline/upstreamTrace.cjs +4 -2
  144. package/dist/pipeline/upstreamTrace.d.cts +22 -8
  145. package/dist/pipeline/upstreamTrace.d.ts +22 -8
  146. package/dist/pipeline/upstreamTrace.js +3 -1
  147. package/dist/ports/pricing-store.d.cts +4 -3
  148. package/dist/ports/pricing-store.d.ts +4 -3
  149. package/dist/ports.d.cts +4 -3
  150. package/dist/ports.d.ts +4 -3
  151. package/dist/provider-proxy/ProviderProxy.cjs +30 -24
  152. package/dist/provider-proxy/ProviderProxy.d.cts +2 -0
  153. package/dist/provider-proxy/ProviderProxy.d.ts +2 -0
  154. package/dist/provider-proxy/ProviderProxy.js +29 -23
  155. package/dist/provider-proxy/identity/codexCliHeaders.cjs +10 -0
  156. package/dist/provider-proxy/identity/codexCliHeaders.d.cts +45 -0
  157. package/dist/provider-proxy/identity/codexCliHeaders.d.ts +45 -0
  158. package/dist/provider-proxy/identity/codexCliHeaders.js +10 -0
  159. package/dist/provider-proxy/ingress/providerProxyShared.cjs +32 -24
  160. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +44 -5
  161. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +44 -5
  162. package/dist/provider-proxy/ingress/providerProxyShared.js +31 -23
  163. package/dist/provider-proxy/matchText.cjs +4 -2
  164. package/dist/provider-proxy/matchText.d.cts +39 -6
  165. package/dist/provider-proxy/matchText.d.ts +39 -6
  166. package/dist/provider-proxy/matchText.js +3 -1
  167. package/dist/provider-proxy/types.d.cts +35 -2
  168. package/dist/provider-proxy/types.d.ts +35 -2
  169. package/dist/provider-proxy.cjs +30 -24
  170. package/dist/provider-proxy.d.cts +2 -0
  171. package/dist/provider-proxy.d.ts +2 -0
  172. package/dist/provider-proxy.js +29 -23
  173. package/dist/{routeResolver-C4T7yClx.d.ts → routeResolver-BxOfunbm.d.ts} +1 -1
  174. package/dist/{routeResolver-CB3tvJKy.d.cts → routeResolver-D5OzD0iM.d.cts} +1 -1
  175. package/dist/transformer/TransformerChainExecutor.cjs +2 -2
  176. package/dist/transformer/TransformerChainExecutor.js +1 -1
  177. package/dist/transformer/transformers/AnthropicTransformer.cjs +3 -2
  178. package/dist/transformer/transformers/AnthropicTransformer.js +2 -1
  179. package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +3 -3
  180. package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +2 -2
  181. package/dist/transformer/transformers/GeminiTransformer.cjs +3 -3
  182. package/dist/transformer/transformers/GeminiTransformer.js +2 -2
  183. package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +3 -2
  184. package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +1 -1
  185. package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +1 -1
  186. package/dist/transformer/transformers/OpenAIResponseTransformer.js +2 -1
  187. package/dist/transformer/transformers/OpenAITransformer.cjs +6 -0
  188. package/dist/transformer/transformers/OpenAITransformer.d.cts +68 -0
  189. package/dist/transformer/transformers/OpenAITransformer.d.ts +68 -0
  190. package/dist/transformer/transformers/OpenAITransformer.js +6 -0
  191. package/dist/transformer/transformers.cjs +9 -14
  192. package/dist/transformer/transformers.d.cts +12 -48
  193. package/dist/transformer/transformers.d.ts +12 -48
  194. package/dist/transformer/transformers.js +11 -16
  195. package/dist/transformer/types.d.cts +17 -0
  196. package/dist/transformer/types.d.ts +17 -0
  197. package/dist/transformer.cjs +12 -12
  198. package/dist/transformer.d.cts +1 -2
  199. package/dist/transformer.d.ts +1 -2
  200. package/dist/transformer.js +12 -12
  201. package/dist/{types-lgPYQuZS.d.ts → types-CqZD77rS.d.ts} +173 -7
  202. package/dist/{types-B8Arpxn0.d.cts → types-gjJ5lJ5n.d.cts} +173 -7
  203. package/dist/usage/pricing-engine.cjs +2 -2
  204. package/dist/usage/pricing-engine.d.cts +29 -1
  205. package/dist/usage/pricing-engine.d.ts +29 -1
  206. package/dist/usage/pricing-engine.js +1 -1
  207. package/dist/usage/usage-recorder.cjs +2 -2
  208. package/dist/usage/usage-recorder.d.cts +5 -1
  209. package/dist/usage/usage-recorder.d.ts +5 -1
  210. package/dist/usage/usage-recorder.js +1 -1
  211. package/dist/usage.cjs +3 -3
  212. package/dist/usage.js +2 -2
  213. package/package.json +1 -1
  214. package/dist/chunk-7FEBOPJO.cjs +0 -50
  215. package/dist/chunk-DWKQZ2SK.cjs +0 -254
  216. package/dist/chunk-IAXWICZU.js +0 -69
  217. package/dist/chunk-IB3FDI4O.js +0 -50
  218. package/dist/chunk-OQWKZCXP.js +0 -242
  219. package/dist/chunk-QWBWGODS.cjs +0 -69
  220. package/dist/chunk-YMFAJFQT.cjs +0 -173
  221. package/dist/chunk-ZCIQ3L3R.cjs +0 -242
  222. package/dist/chunk-ZJZAOKI7.js +0 -254
  223. package/dist/chunk-ZLX3KGXX.js +0 -173
  224. package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +0 -6
  225. package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +0 -51
  226. package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +0 -51
  227. package/dist/transformer/transformers/OpenCodeGoTransformer.js +0 -6
  228. package/dist/transformer/transformers/ReasoningTransformer.cjs +0 -6
  229. package/dist/transformer/transformers/ReasoningTransformer.d.cts +0 -47
  230. package/dist/transformer/transformers/ReasoningTransformer.d.ts +0 -47
  231. package/dist/transformer/transformers/ReasoningTransformer.js +0 -6
@@ -0,0 +1,44 @@
1
+ import {
2
+ AnthropicTransformer
3
+ } from "./chunk-PEGTIPB3.js";
4
+ import {
5
+ GeminiTransformer
6
+ } from "./chunk-LVUCAWMK.js";
7
+ import {
8
+ OpenAIResponseTransformer
9
+ } from "./chunk-32O35CIF.js";
10
+ import {
11
+ OpenAITransformer
12
+ } from "./chunk-LB6EBS6Y.js";
13
+ import {
14
+ GeminiCodeAssistTransformer
15
+ } from "./chunk-N5T4M4GU.js";
16
+
17
+ // src/transformer/transformers/index.ts
18
+ var BuiltinTransformers = {
19
+ OpenAITransformer,
20
+ GeminiTransformer,
21
+ GeminiCodeAssistTransformer,
22
+ AnthropicTransformer,
23
+ OpenAIResponseTransformer
24
+ };
25
+ function getBuiltinTransformers() {
26
+ return BuiltinTransformers;
27
+ }
28
+ var BUILTIN_TRANSFORMER_NAMES = [
29
+ "openai",
30
+ "gemini",
31
+ "gemini-code-assist",
32
+ "anthropic",
33
+ "openai-response"
34
+ ];
35
+ async function registerBuiltinTransformers(service) {
36
+ await service.initialize(getBuiltinTransformers());
37
+ }
38
+
39
+ export {
40
+ BuiltinTransformers,
41
+ getBuiltinTransformers,
42
+ BUILTIN_TRANSFORMER_NAMES,
43
+ registerBuiltinTransformers
44
+ };
@@ -3,7 +3,7 @@ import {
3
3
  transformRequestOut,
4
4
  transformResponseIn,
5
5
  transformResponseOut
6
- } from "./chunk-6QZGDLL4.js";
6
+ } from "./chunk-TOVPMMHM.js";
7
7
 
8
8
  // src/transformer/transformers/GeminiCodeAssistTransformer.ts
9
9
  var DEFAULT_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
@@ -3,10 +3,12 @@ var OVERLOAD_TTL_MS = 10 * 6e4;
3
3
  var OVERLOAD_ENABLED_DEFAULT = true;
4
4
  var AUTH_ERROR_TTL_MS = 30 * 6e4;
5
5
  var SERVER_ERROR_TTL_MS = 5 * 6e4;
6
+ var MAX_QUOTA_COOLDOWN_MS = 7 * 24 * 60 * 6e4;
6
7
  var BAN_BODY_MARKERS = [
7
8
  "this organization has been disabled",
8
9
  "oauth authentication is currently not allowed"
9
10
  ];
11
+ var ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT = 200;
10
12
  function readHeader(headers, name) {
11
13
  if (typeof headers.get === "function") {
12
14
  return headers.get(name);
@@ -37,13 +39,14 @@ function isBanBody(bodyText) {
37
39
  return BAN_BODY_MARKERS.some((marker) => lower.includes(marker));
38
40
  }
39
41
  function isRecordEmpty(record) {
40
- return record.rateLimitEndAt === void 0 && record.overloadUntil === void 0 && record.tempUnavailableUntil === void 0 && !record.blocked;
42
+ return record.rateLimitEndAt === void 0 && record.overloadUntil === void 0 && record.tempUnavailableUntil === void 0 && record.quotaExhaustedUntil === void 0 && !record.blocked;
41
43
  }
42
44
  var KEY_SEP = "\0";
43
45
  var SubscriptionAccountHealth = class {
44
46
  records = /* @__PURE__ */ new Map();
45
47
  listeners = /* @__PURE__ */ new Set();
46
48
  anomalyListeners = /* @__PURE__ */ new Set();
49
+ diagnostics = [];
47
50
  now;
48
51
  overloadEnabled;
49
52
  overloadTtlMs;
@@ -71,8 +74,8 @@ var SubscriptionAccountHealth = class {
71
74
  }
72
75
  /**
73
76
  * Whether an account may be scheduled RIGHT NOW. `blocked` → false; any timer
74
- * still in the future → false. Expired timers are lazily deleted on read (CRS
75
- * lazy-clear parity) so an elapsed cooldown restores the account WITHOUT a
77
+ * still in the future → false. Expired timers are lazily deleted on read so
78
+ * an elapsed cooldown restores the account WITHOUT a
76
79
  * timer — but the recovery SIGNAL is the sweeper's job (`sweepRecoveries`), so
77
80
  * this read never emits (correctness is independent of the tick).
78
81
  */
@@ -86,7 +89,7 @@ var SubscriptionAccountHealth = class {
86
89
  return !active;
87
90
  }
88
91
  /**
89
- * The single marking entry point (design D3). Faithful to CRS status semantics:
92
+ * The single marking entry point (design D3):
90
93
  * - 429 + authoritative reset → `rateLimitEndAt`; a non-claude 429 may use
91
94
  * `retryAfterSeconds`; a bare 429 (no resolvable reset) is NOT marked;
92
95
  * - 529 → `overloadUntil` (gated by `overloadEnabled`);
@@ -141,9 +144,54 @@ var SubscriptionAccountHealth = class {
141
144
  }
142
145
  if (isRecordEmpty(record)) this.records.delete(key);
143
146
  else this.records.set(key, record);
144
- if (recovered) this.emit({ providerId, accountId, at: now, kind: "rateLimitRecovery" });
147
+ if (recovered) {
148
+ const recovery = {
149
+ providerId,
150
+ accountId,
151
+ at: now,
152
+ kind: "rateLimitRecovery"
153
+ };
154
+ this.recordDiagnostic({ kind: "health-recovery", providerId, accountId, at: now });
155
+ this.emit(recovery);
156
+ }
145
157
  if (anomalyState && !wasUnhealthyBefore) {
146
- this.emitAnomaly({ providerId, accountId, at: now, state: anomalyState });
158
+ const anomaly = { providerId, accountId, at: now, state: anomalyState };
159
+ this.recordDiagnostic({ kind: "health-anomaly", ...anomaly });
160
+ this.emitAnomaly(anomaly);
161
+ }
162
+ }
163
+ /**
164
+ * Mark an account quota/usage-exhausted until `untilEpochMs` (the Codex weekly
165
+ * usage-limit wall). Unlike the status-driven timers set by
166
+ * {@link recordUpstreamOutcome}, this mark is NOT cleared by a 2xx: in-flight
167
+ * sessions on the same account keep succeeding after the window is exhausted,
168
+ * so a traffic-driven clear would re-strand every NEW session onto this
169
+ * account. It excludes the account from NEW-session selection until the
170
+ * deadline elapses (existing sessions are unaffected — they already hold their
171
+ * token). Self-heals lazily on read and via {@link sweepRecoveries}.
172
+ * `untilEpochMs` is clamped to {@link MAX_QUOTA_COOLDOWN_MS}; an already-elapsed
173
+ * deadline is a no-op. Emits a `quota_exhausted` anomaly on the healthy→
174
+ * unhealthy edge.
175
+ */
176
+ markQuotaExhausted(providerId, accountId, untilEpochMs, now = this.now()) {
177
+ if (!Number.isFinite(untilEpochMs)) return;
178
+ const clamped = Math.min(untilEpochMs, now + MAX_QUOTA_COOLDOWN_MS);
179
+ if (clamped <= now) return;
180
+ const key = this.key(providerId, accountId);
181
+ const preExisting = this.records.get(key);
182
+ const record = preExisting ?? {};
183
+ const wasUnhealthyBefore = preExisting ? this.isUnhealthyAt(preExisting, now) : false;
184
+ record.quotaExhaustedUntil = clamped;
185
+ this.records.set(key, record);
186
+ if (!wasUnhealthyBefore) {
187
+ const anomaly = {
188
+ providerId,
189
+ accountId,
190
+ at: now,
191
+ state: "quota_exhausted"
192
+ };
193
+ this.recordDiagnostic({ kind: "health-anomaly", ...anomaly });
194
+ this.emitAnomaly(anomaly);
147
195
  }
148
196
  }
149
197
  /** Whether a record is unhealthy at `now`: blocked, or any timer still future. */
@@ -152,6 +200,7 @@ var SubscriptionAccountHealth = class {
152
200
  if (record.rateLimitEndAt !== void 0 && now < record.rateLimitEndAt) return true;
153
201
  if (record.overloadUntil !== void 0 && now < record.overloadUntil) return true;
154
202
  if (record.tempUnavailableUntil !== void 0 && now < record.tempUnavailableUntil) return true;
203
+ if (record.quotaExhaustedUntil !== void 0 && now < record.quotaExhaustedUntil) return true;
155
204
  return false;
156
205
  }
157
206
  /**
@@ -188,7 +237,7 @@ var SubscriptionAccountHealth = class {
188
237
  const recovered = [];
189
238
  for (const [key, record] of this.records) {
190
239
  if (record.blocked) continue;
191
- const hadTimer = record.rateLimitEndAt !== void 0 || record.overloadUntil !== void 0 || record.tempUnavailableUntil !== void 0;
240
+ const hadTimer = record.rateLimitEndAt !== void 0 || record.overloadUntil !== void 0 || record.tempUnavailableUntil !== void 0 || record.quotaExhaustedUntil !== void 0;
192
241
  const active = this.clearExpired(record, now);
193
242
  if (hadTimer && !active && isRecordEmpty(record)) {
194
243
  const [pid, aid] = this.parseKey(key);
@@ -196,7 +245,15 @@ var SubscriptionAccountHealth = class {
196
245
  this.records.delete(key);
197
246
  }
198
247
  }
199
- for (const event of recovered) this.emit(event);
248
+ for (const event of recovered) {
249
+ this.recordDiagnostic({
250
+ kind: "health-recovery",
251
+ providerId: event.providerId,
252
+ accountId: event.accountId,
253
+ at: event.at
254
+ });
255
+ this.emit(event);
256
+ }
200
257
  return recovered;
201
258
  }
202
259
  /** Admin-facing coarse status + cooldown-until for one account (secret-free). */
@@ -204,6 +261,9 @@ var SubscriptionAccountHealth = class {
204
261
  const record = this.records.get(this.key(providerId, accountId));
205
262
  if (!record) return { state: "healthy" };
206
263
  if (record.blocked) return { state: "blocked" };
264
+ if (record.quotaExhaustedUntil !== void 0 && now < record.quotaExhaustedUntil) {
265
+ return { state: "quota_exhausted", cooldownUntil: record.quotaExhaustedUntil };
266
+ }
207
267
  if (record.rateLimitEndAt !== void 0 && now < record.rateLimitEndAt) {
208
268
  return { state: "rate_limited", cooldownUntil: record.rateLimitEndAt };
209
269
  }
@@ -215,6 +275,14 @@ var SubscriptionAccountHealth = class {
215
275
  }
216
276
  return { state: "healthy" };
217
277
  }
278
+ /** Return newest-first, bounded, secret-free health edge diagnostics. */
279
+ getDiagnostics(filter = {}) {
280
+ const requested = typeof filter.limit === "number" && Number.isFinite(filter.limit) ? Math.trunc(filter.limit) : ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT;
281
+ const limit = Math.min(ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT, Math.max(1, requested));
282
+ return this.diagnostics.filter(
283
+ (event) => (!filter.providerId || event.providerId === filter.providerId) && (!filter.accountId || event.accountId === filter.accountId)
284
+ ).slice(-limit).reverse().map((event) => ({ ...event }));
285
+ }
218
286
  /**
219
287
  * Register a recovery listener (the OQ3 seam #5 webhooks + #8 health-cron
220
288
  * consume). Returns an unsubscribe function. Two edges fire it:
@@ -257,6 +325,12 @@ var SubscriptionAccountHealth = class {
257
325
  }
258
326
  }
259
327
  }
328
+ recordDiagnostic(event) {
329
+ this.diagnostics.push({ ...event });
330
+ if (this.diagnostics.length > ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT) {
331
+ this.diagnostics.splice(0, this.diagnostics.length - ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT);
332
+ }
333
+ }
260
334
  /**
261
335
  * Delete every EXPIRED timer field on the record in place; return whether any
262
336
  * timer is STILL active (in the future). `blocked` is not a timer and is left
@@ -276,6 +350,10 @@ var SubscriptionAccountHealth = class {
276
350
  if (now >= record.tempUnavailableUntil) delete record.tempUnavailableUntil;
277
351
  else active = true;
278
352
  }
353
+ if (record.quotaExhaustedUntil !== void 0) {
354
+ if (now >= record.quotaExhaustedUntil) delete record.quotaExhaustedUntil;
355
+ else active = true;
356
+ }
279
357
  return active;
280
358
  }
281
359
  key(providerId, accountId) {
@@ -303,6 +381,8 @@ export {
303
381
  OVERLOAD_ENABLED_DEFAULT,
304
382
  AUTH_ERROR_TTL_MS,
305
383
  SERVER_ERROR_TTL_MS,
384
+ MAX_QUOTA_COOLDOWN_MS,
385
+ ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT,
306
386
  resolveResetSeconds,
307
387
  SubscriptionAccountHealth,
308
388
  getSharedAccountHealth,
@@ -0,0 +1,61 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/pipeline/AccountRouteActivity.ts
2
+ var ACCOUNT_ROUTE_ACTIVITY_LIMIT = 300;
3
+ function boundedLimit(value) {
4
+ if (!Number.isFinite(value)) return 100;
5
+ return Math.max(1, Math.min(ACCOUNT_ROUTE_ACTIVITY_LIMIT, Math.trunc(value)));
6
+ }
7
+ var AccountRouteActivityStore = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }
8
+ __init() {this.records = []}
9
+ __init2() {this.sequence = 0}
10
+ record(input) {
11
+ const ts = _nullishCoalesce(input.ts, () => ( Date.now()));
12
+ const sessionKey = _optionalChain([input, 'access', _ => _.sessionKey, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]) || void 0;
13
+ const previous = sessionKey ? this.records.find((record2) => record2.providerId === input.providerId && record2.endpoint === input.endpoint && record2.sessionKey === sessionKey) : void 0;
14
+ const affinity = !sessionKey ? "untracked" : !previous ? "new" : previous.accountId === input.accountId ? "sticky" : "switched";
15
+ const record = {
16
+ ...input,
17
+ id: `${ts.toString(36)}-${(this.sequence++).toString(36)}`,
18
+ ts,
19
+ sessionKey,
20
+ affinity,
21
+ ...affinity === "switched" && previous ? { previousAccountId: previous.accountId } : {}
22
+ };
23
+ this.records.unshift(record);
24
+ if (this.records.length > ACCOUNT_ROUTE_ACTIVITY_LIMIT) {
25
+ this.records.length = ACCOUNT_ROUTE_ACTIVITY_LIMIT;
26
+ }
27
+ return { ...record };
28
+ }
29
+ list(query = {}) {
30
+ const providerId = _optionalChain([query, 'access', _4 => _4.providerId, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]);
31
+ const accountId = _optionalChain([query, 'access', _7 => _7.accountId, 'optionalAccess', _8 => _8.trim, 'call', _9 => _9()]);
32
+ const sessionKey = _optionalChain([query, 'access', _10 => _10.sessionKey, 'optionalAccess', _11 => _11.trim, 'call', _12 => _12()]);
33
+ return this.records.filter((record) => !providerId || record.providerId === providerId).filter((record) => !accountId || record.accountId === accountId).filter((record) => !sessionKey || record.sessionKey === sessionKey).slice(0, boundedLimit(query.limit)).map((record) => ({ ...record }));
34
+ }
35
+ clear() {
36
+ this.records.length = 0;
37
+ this.sequence = 0;
38
+ }
39
+ /**
40
+ * Backfill a field on an already-recorded entry. Used to annotate a record
41
+ * AFTER the upstream body streams — e.g. a `200` whose SSE body carries a
42
+ * `response.failed` overload event is only detectable mid-relay, long after
43
+ * `record()` stamped the 200 status. `patch` is intentionally narrow: only
44
+ * post-hoc-observable fields belong here, never the routing-decision fields.
45
+ * No-op when the id has already aged out of the bounded ring.
46
+ */
47
+ amend(id, patch) {
48
+ const record = this.records.find((r) => r.id === id);
49
+ if (record) Object.assign(record, patch);
50
+ }
51
+ }, _class);
52
+ var sharedAccountRouteActivity = new AccountRouteActivityStore();
53
+ function getSharedAccountRouteActivity() {
54
+ return sharedAccountRouteActivity;
55
+ }
56
+
57
+
58
+
59
+
60
+
61
+ exports.ACCOUNT_ROUTE_ACTIVITY_LIMIT = ACCOUNT_ROUTE_ACTIVITY_LIMIT; exports.AccountRouteActivityStore = AccountRouteActivityStore; exports.getSharedAccountRouteActivity = getSharedAccountRouteActivity;
@@ -0,0 +1,69 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/transformer/transformers/OpenAITransformer.ts
2
+ var FOREIGN_MESSAGE_KEYS = ["cache_control", "thinking"];
3
+ var FOREIGN_BODY_KEYS = ["meta", "reasoning", "thinking"];
4
+ function normalizeContent(content) {
5
+ if (!Array.isArray(content)) return content;
6
+ const cleaned = content.map((block) => {
7
+ if (block && typeof block === "object" && "cache_control" in block) {
8
+ const { cache_control: _dropped, ...rest } = block;
9
+ return rest;
10
+ }
11
+ return block;
12
+ });
13
+ if (cleaned.length === 1) {
14
+ const only = cleaned[0];
15
+ if (_optionalChain([only, 'optionalAccess', _ => _.type]) === "text" && typeof only.text === "string") return only.text;
16
+ }
17
+ return cleaned;
18
+ }
19
+ var OpenAITransformer = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }
20
+ static __initStatic() {this.TransformerName = "openai"}
21
+ __init() {this.name = "openai"}
22
+ __init2() {this.endPoint = "/v1/chat/completions"}
23
+
24
+ /**
25
+ * Unified → OpenAI Chat Completions.
26
+ */
27
+ async transformRequestIn(request, _provider, _context) {
28
+ const source = request;
29
+ const out = {};
30
+ for (const key of Object.keys(source)) {
31
+ if (FOREIGN_BODY_KEYS.includes(key)) continue;
32
+ out[key] = source[key];
33
+ }
34
+ if (Array.isArray(request.messages)) {
35
+ out.messages = request.messages.map((message) => {
36
+ const src = message;
37
+ const encoded = {};
38
+ for (const key of Object.keys(src)) {
39
+ if (FOREIGN_MESSAGE_KEYS.includes(key)) continue;
40
+ encoded[key] = src[key];
41
+ }
42
+ if (message.content !== void 0) {
43
+ encoded.content = normalizeContent(message.content);
44
+ }
45
+ const thinkingContent = _optionalChain([message, 'access', _2 => _2.thinking, 'optionalAccess', _3 => _3.content]);
46
+ if (typeof thinkingContent === "string" && thinkingContent.length > 0) {
47
+ encoded.reasoning_content = thinkingContent;
48
+ }
49
+ return encoded;
50
+ });
51
+ }
52
+ const effort = _optionalChain([request, 'access', _4 => _4.reasoning, 'optionalAccess', _5 => _5.effort]);
53
+ if (typeof effort === "string" && effort !== "none") {
54
+ out.reasoning_effort = effort;
55
+ }
56
+ return out;
57
+ }
58
+ /**
59
+ * OpenAI Chat Completions → Unified. Identity — see the module JSDoc for why
60
+ * `reasoning_content` is NOT rewritten here.
61
+ */
62
+ async transformResponseOut(response, _context) {
63
+ return response;
64
+ }
65
+ }, _class.__initStatic(), _class);
66
+
67
+
68
+
69
+ exports.OpenAITransformer = OpenAITransformer;
@@ -78,7 +78,9 @@ var UsageRecorder = class {
78
78
  costSavedByCacheUsd,
79
79
  rawUsage: input.rawUsage != null ? safeStringify(input.rawUsage) : null,
80
80
  runId: _nullishCoalesce(input.runId, () => ( null)),
81
- eventId: _nullishCoalesce(input.eventId, () => ( null))
81
+ eventId: _nullishCoalesce(input.eventId, () => ( null)),
82
+ ...input.cacheKeySource !== void 0 ? { cacheKeySource: input.cacheKeySource } : {},
83
+ ...input.cacheKeyInjected !== void 0 ? { cacheKeyInjected: input.cacheKeyInjected } : {}
82
84
  };
83
85
  try {
84
86
  return await this.store.insert(row);
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkJWVTXXPXcjs = require('./chunk-JWVTXXPX.cjs');
6
+ var _chunkKBM2W6UYcjs = require('./chunk-KBM2W6UY.cjs');
7
7
 
8
8
  // src/transformer/transformers/GeminiCodeAssistTransformer.ts
9
9
  var DEFAULT_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
@@ -117,7 +117,7 @@ var GeminiCodeAssistTransformer = (_class = class {constructor() { _class.protot
117
117
  * openaiResponsesIngress). `undefined` is the valid fresh free-tier value.
118
118
  */
119
119
  async transformRequestIn(request, provider, _context) {
120
- const inner = _chunkJWVTXXPXcjs.buildRequestBody.call(void 0, request);
120
+ const inner = _chunkKBM2W6UYcjs.buildRequestBody.call(void 0, request);
121
121
  const envelope = {
122
122
  model: request.model,
123
123
  project: provider.geminiProject,
@@ -147,7 +147,7 @@ var GeminiCodeAssistTransformer = (_class = class {constructor() { _class.protot
147
147
  if (inner && typeof inner === "object" && !("model" in inner) && "model" in r) {
148
148
  inner.model = r.model;
149
149
  }
150
- return _chunkJWVTXXPXcjs.transformRequestOut.call(void 0, inner);
150
+ return _chunkKBM2W6UYcjs.transformRequestOut.call(void 0, inner);
151
151
  }
152
152
  /**
153
153
  * Code Assist response → OpenAI-compatible. PEEL the `.response` envelope from
@@ -155,7 +155,7 @@ var GeminiCodeAssistTransformer = (_class = class {constructor() { _class.protot
155
155
  */
156
156
  async transformResponseOut(response, _context) {
157
157
  const unwrapped = await unwrapCodeAssistResponse(response);
158
- return _chunkJWVTXXPXcjs.transformResponseOut.call(void 0, unwrapped, this.name, this.logger);
158
+ return _chunkKBM2W6UYcjs.transformResponseOut.call(void 0, unwrapped, this.name, this.logger);
159
159
  }
160
160
  /**
161
161
  * OpenAI-compatible response → Code Assist (endpoint-encode parity). Re-wraps
@@ -164,7 +164,7 @@ var GeminiCodeAssistTransformer = (_class = class {constructor() { _class.protot
164
164
  * `transformResponseOut`'s peel.
165
165
  */
166
166
  async transformResponseIn(response, _context) {
167
- const geminiResponse = await _chunkJWVTXXPXcjs.transformResponseIn.call(void 0, response, this.logger);
167
+ const geminiResponse = await _chunkKBM2W6UYcjs.transformResponseIn.call(void 0, response, this.logger);
168
168
  const contentType = _nullishCoalesce(geminiResponse.headers.get("Content-Type"), () => ( ""));
169
169
  if (contentType.includes("text/event-stream")) {
170
170
  return geminiResponse;
@@ -0,0 +1,153 @@
1
+ import {
2
+ getSharedAccountAllowanceStore
3
+ } from "./chunk-MKF46GNZ.js";
4
+
5
+ // src/pipeline/AccountAllowanceScheduling.ts
6
+ var DEFAULT_POLICY = {
7
+ enabled: false,
8
+ demoteAtPercent: 80,
9
+ pauseAtPercent: 98,
10
+ priorityPenalty: 100
11
+ };
12
+ var HISTORY_LIMIT = 200;
13
+ var AccountAllowanceExhaustedError = class extends Error {
14
+ constructor(providerId, resumeAt) {
15
+ super(
16
+ resumeAt ? `All ${providerId} subscription accounts are paused by the allowance policy until ${resumeAt}` : `All ${providerId} subscription accounts are paused by the allowance policy`
17
+ );
18
+ this.providerId = providerId;
19
+ this.resumeAt = resumeAt;
20
+ this.name = "AccountAllowanceExhaustedError";
21
+ }
22
+ providerId;
23
+ resumeAt;
24
+ code = "account_allowance_exhausted";
25
+ status = 429;
26
+ };
27
+ function isAccountAllowanceExhaustedError(error) {
28
+ if (error instanceof AccountAllowanceExhaustedError) return true;
29
+ if (!error || typeof error !== "object") return false;
30
+ const value = error;
31
+ return value.code === "account_allowance_exhausted" && value.status === 429;
32
+ }
33
+ function freshWorstWindow(snapshot) {
34
+ return snapshot.windows.filter((window) => window.state === "fresh" && typeof window.usedPercent === "number").sort((left, right) => (right.usedPercent ?? -1) - (left.usedPercent ?? -1))[0];
35
+ }
36
+ var AccountAllowanceScheduling = class {
37
+ constructor(store = getSharedAccountAllowanceStore(), now = Date.now) {
38
+ this.store = store;
39
+ this.now = now;
40
+ }
41
+ store;
42
+ now;
43
+ config = { ...DEFAULT_POLICY };
44
+ history = [];
45
+ lastRecorded = /* @__PURE__ */ new Map();
46
+ configure(config) {
47
+ this.config = config ? { ...config } : { ...DEFAULT_POLICY };
48
+ }
49
+ getConfig() {
50
+ return { ...this.config };
51
+ }
52
+ evaluate(providerId, accountId, basePriority, now = this.now()) {
53
+ const decision = this.decide(providerId, accountId, basePriority, now);
54
+ this.recordApplied(decision);
55
+ return decision;
56
+ }
57
+ /**
58
+ * Project the current policy result without recording an applied decision.
59
+ * Admin/read-model callers use this so polling an account list never creates
60
+ * synthetic demotion/pause history entries.
61
+ */
62
+ preview(providerId, accountId, basePriority, now = this.now()) {
63
+ return this.decide(providerId, accountId, basePriority, now);
64
+ }
65
+ decide(providerId, accountId, basePriority, now) {
66
+ const decidedAt = new Date(now).toISOString();
67
+ const base = {
68
+ providerId,
69
+ accountId,
70
+ basePriority,
71
+ effectivePriority: basePriority,
72
+ schedulable: true,
73
+ decidedAt
74
+ };
75
+ if (!this.config.enabled) return { ...base, action: "ignore", reason: "policy-disabled" };
76
+ if (providerId !== "claude" && providerId !== "codex") {
77
+ return { ...base, action: "ignore", reason: "provider-unsupported" };
78
+ }
79
+ const snapshot = this.store.get(providerId, accountId, now);
80
+ if (!snapshot) return { ...base, action: "ignore", reason: "snapshot-missing" };
81
+ const worst = freshWorstWindow(snapshot);
82
+ if (!worst || worst.usedPercent === null) {
83
+ return { ...base, action: "ignore", reason: "snapshot-not-fresh", observedAt: snapshot.observedAt };
84
+ }
85
+ const resumeAt = worst.resetsAt ?? snapshot.expiresAt;
86
+ const resumeMs = resumeAt ? Date.parse(resumeAt) : Number.NaN;
87
+ const canPauseUntilKnownDeadline = Number.isFinite(resumeMs) && resumeMs > now;
88
+ let decision;
89
+ if (worst.usedPercent >= this.config.pauseAtPercent && canPauseUntilKnownDeadline) {
90
+ decision = {
91
+ ...base,
92
+ action: "pause",
93
+ reason: "pause-threshold",
94
+ schedulable: false,
95
+ usedPercent: worst.usedPercent,
96
+ observedAt: snapshot.observedAt,
97
+ resumeAt
98
+ };
99
+ } else if (worst.usedPercent >= this.config.demoteAtPercent) {
100
+ decision = {
101
+ ...base,
102
+ action: "demote",
103
+ reason: "demote-threshold",
104
+ effectivePriority: basePriority + this.config.priorityPenalty,
105
+ usedPercent: worst.usedPercent,
106
+ observedAt: snapshot.observedAt,
107
+ resumeAt: canPauseUntilKnownDeadline ? resumeAt : void 0
108
+ };
109
+ } else {
110
+ decision = {
111
+ ...base,
112
+ action: "normal",
113
+ reason: "below-threshold",
114
+ usedPercent: worst.usedPercent,
115
+ observedAt: snapshot.observedAt,
116
+ resumeAt: canPauseUntilKnownDeadline ? resumeAt : void 0
117
+ };
118
+ }
119
+ return decision;
120
+ }
121
+ getHistory() {
122
+ return this.history.map((decision) => ({ ...decision }));
123
+ }
124
+ clearHistory() {
125
+ this.history.length = 0;
126
+ this.lastRecorded.clear();
127
+ }
128
+ recordApplied(decision) {
129
+ if (decision.action !== "demote" && decision.action !== "pause") return;
130
+ const key = `${decision.providerId}\0${decision.accountId}`;
131
+ const signature = `${decision.action}\0${decision.observedAt ?? ""}\0${decision.usedPercent ?? ""}`;
132
+ if (this.lastRecorded.get(key) === signature) return;
133
+ this.lastRecorded.set(key, signature);
134
+ this.history.push({ ...decision });
135
+ if (this.history.length > HISTORY_LIMIT) this.history.splice(0, this.history.length - HISTORY_LIMIT);
136
+ }
137
+ };
138
+ var sharedScheduling = null;
139
+ function getSharedAccountAllowanceScheduling() {
140
+ if (!sharedScheduling) sharedScheduling = new AccountAllowanceScheduling();
141
+ return sharedScheduling;
142
+ }
143
+ function __resetSharedAccountAllowanceSchedulingForTests() {
144
+ sharedScheduling = null;
145
+ }
146
+
147
+ export {
148
+ AccountAllowanceExhaustedError,
149
+ isAccountAllowanceExhaustedError,
150
+ AccountAllowanceScheduling,
151
+ getSharedAccountAllowanceScheduling,
152
+ __resetSharedAccountAllowanceSchedulingForTests
153
+ };
@@ -0,0 +1,79 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/pipeline/ServerOverloadCounter.ts
2
+ var OVERLOAD_RECENT_LIMIT = 64;
3
+ var OVERLOAD_ENTRY_TTL_MS = 24 * 60 * 6e4;
4
+ function entryKey(providerId, accountId, endpoint) {
5
+ return `${providerId}\0${accountId}\0${endpoint}`;
6
+ }
7
+ var ServerOverloadCounterStore = (_class = class {constructor() { _class.prototype.__init.call(this); }
8
+ __init() {this.entries = /* @__PURE__ */ new Map()}
9
+ /**
10
+ * Record one overload event. Creates the entry on first sight, then bumps
11
+ * `count`, `lastTs`, and prepends the event to the bounded `recent` ring.
12
+ */
13
+ recordOverload(input, now = Date.now()) {
14
+ const key = entryKey(input.providerId, input.accountId, input.endpoint);
15
+ const existing = this.entries.get(key);
16
+ if (existing) {
17
+ existing.count += 1;
18
+ existing.lastTs = now;
19
+ existing.recent.unshift(now);
20
+ if (existing.recent.length > OVERLOAD_RECENT_LIMIT) {
21
+ existing.recent.length = OVERLOAD_RECENT_LIMIT;
22
+ }
23
+ return { ...existing, recent: [...existing.recent] };
24
+ }
25
+ const entry = {
26
+ providerId: input.providerId,
27
+ accountId: input.accountId,
28
+ endpoint: input.endpoint,
29
+ count: 1,
30
+ firstTs: now,
31
+ lastTs: now,
32
+ recent: [now]
33
+ };
34
+ this.entries.set(key, entry);
35
+ return { ...entry, recent: [...entry.recent] };
36
+ }
37
+ /**
38
+ * Snapshot all (optionally filtered) entries, pruning stale ones first. Returns
39
+ * copies — callers (admin API → JSON) never see the live mutable entries.
40
+ */
41
+ list(query = {}, now = Date.now()) {
42
+ const providerId = _optionalChain([query, 'access', _ => _.providerId, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]);
43
+ const accountId = _optionalChain([query, 'access', _4 => _4.accountId, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]);
44
+ const out = [];
45
+ for (const [key, entry] of this.entries) {
46
+ if (now - entry.lastTs > OVERLOAD_ENTRY_TTL_MS) {
47
+ this.entries.delete(key);
48
+ continue;
49
+ }
50
+ if (providerId && entry.providerId !== providerId) continue;
51
+ if (accountId && entry.accountId !== accountId) continue;
52
+ out.push({ ...entry, recent: [...entry.recent] });
53
+ }
54
+ return out;
55
+ }
56
+ clear() {
57
+ this.entries.clear();
58
+ }
59
+ }, _class);
60
+ var sharedStore = null;
61
+ function getSharedOverloadCounter() {
62
+ if (!sharedStore) sharedStore = new ServerOverloadCounterStore();
63
+ return sharedStore;
64
+ }
65
+ function setSharedOverloadCounter(instance) {
66
+ sharedStore = instance;
67
+ }
68
+ function __resetSharedOverloadCounterForTests() {
69
+ sharedStore = null;
70
+ }
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ exports.OVERLOAD_RECENT_LIMIT = OVERLOAD_RECENT_LIMIT; exports.OVERLOAD_ENTRY_TTL_MS = OVERLOAD_ENTRY_TTL_MS; exports.ServerOverloadCounterStore = ServerOverloadCounterStore; exports.getSharedOverloadCounter = getSharedOverloadCounter; exports.setSharedOverloadCounter = setSharedOverloadCounter; exports.__resetSharedOverloadCounterForTests = __resetSharedOverloadCounterForTests;