@omnicross/subscriptions 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.
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkTPW5Q25Ycjs = require('./chunk-TPW5Q25Y.cjs');
5
+ var _chunkPWV5NBO5cjs = require('./chunk-PWV5NBO5.cjs');
6
6
 
7
7
  // src/scheduler/SubscriptionAccountSelector.ts
8
8
  var SESSION_AFFINITY_TTL_MS = 36e5;
@@ -108,6 +108,15 @@ var SubscriptionAccountSelector = (_class = class {constructor() { _class.protot
108
108
  // src/SubscriptionAccountService.ts
109
109
  var _SubscriptionAccountHealth = require('@omnicross/core/pipeline/SubscriptionAccountHealth');
110
110
 
111
+ // src/scheduler/accountSelection.ts
112
+
113
+
114
+
115
+ var _AccountAllowanceScheduling = require('@omnicross/core/pipeline/AccountAllowanceScheduling');
116
+
117
+
118
+ var _BoundAccountSelectionError = require('@omnicross/core/pipeline/BoundAccountSelectionError');
119
+
111
120
  // src/scheduler/accountModelMap.ts
112
121
  function canonicalModelId(value) {
113
122
  const idx = value.indexOf(",");
@@ -154,19 +163,104 @@ function gateSchedulable(accounts, providerId, health, now, resolvedModel, suppo
154
163
  return accounts.map((a) => {
155
164
  const healthOk = health ? health.isSchedulable(providerId, a.id, now) : true;
156
165
  const modelOk = resolvedModel ? accountSupportsModel(supportedModelsById.get(a.id), resolvedModel) : true;
157
- return { ...a, schedulable: healthOk && modelOk };
166
+ return { ...a, schedulable: a.schedulable !== false && healthOk && modelOk };
158
167
  });
159
168
  }
160
169
  function isPoolGated(accounts, health, resolvedModel) {
161
170
  return (health !== void 0 || resolvedModel !== void 0) && accounts.length >= 2;
162
171
  }
172
+ function hasUsableToken(token) {
173
+ return typeof token === "string" && token.trim() !== "";
174
+ }
175
+ async function resolveStrictPreferredToken(selector, tokens, providerId, preferredId, activeGetter, ctx) {
176
+ let config;
177
+ try {
178
+ config = await tokens.getFullConfig();
179
+ } catch (e2) {
180
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "unavailable");
181
+ }
182
+ let accounts;
183
+ let activeAccountId;
184
+ let supportedModelsById;
185
+ try {
186
+ ({ accounts, activeAccountId, supportedModelsById } = readSchedulableAccounts(config, providerId));
187
+ } catch (e3) {
188
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "unavailable");
189
+ }
190
+ const preferred = accounts.find((account) => account.id === preferredId);
191
+ if (!preferred) {
192
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "not-found");
193
+ }
194
+ if (preferred.schedulable === false) {
195
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "disabled");
196
+ }
197
+ if (ctx.health && !ctx.health.isSchedulable(providerId, preferredId, ctx.now)) {
198
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "unhealthy");
199
+ }
200
+ if (ctx.resolvedModel && !accountSupportsModel(supportedModelsById.get(preferredId), ctx.resolvedModel)) {
201
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "model-incompatible");
202
+ }
203
+ const allowance = _AccountAllowanceScheduling.getSharedAccountAllowanceScheduling.call(void 0, ).evaluate(
204
+ providerId,
205
+ preferredId,
206
+ _nullishCoalesce(preferred.priority, () => ( DEFAULT_ACCOUNT_PRIORITY)),
207
+ ctx.now
208
+ );
209
+ if (allowance.action === "pause") {
210
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "allowance-paused", allowance.resumeAt);
211
+ }
212
+ let token = null;
213
+ try {
214
+ token = tokens.getAccessTokenForAccount ? await tokens.getAccessTokenForAccount(providerId, preferredId) : preferredId === activeAccountId ? await activeGetter() : null;
215
+ } catch (e4) {
216
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "unavailable");
217
+ }
218
+ if (!hasUsableToken(token)) {
219
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "empty-token");
220
+ }
221
+ const remapped = remapReportForAccount(supportedModelsById.get(preferredId), ctx.resolvedModel);
222
+ if (selector) maybeTouchLastUsed(selector, tokens, providerId, preferredId);
223
+ _optionalChain([ctx, 'access', _ => _.reportSelection, 'optionalCall', _2 => _2(preferredId, preferredId === activeAccountId, remapped)]);
224
+ return token;
225
+ }
226
+ function gateByAllowance(accounts, providerId, now) {
227
+ const scheduling = _AccountAllowanceScheduling.getSharedAccountAllowanceScheduling.call(void 0, );
228
+ const evaluatedAt = _nullishCoalesce(now, () => ( Date.now()));
229
+ const eligibleBeforePolicy = accounts.filter((account) => account.schedulable !== false);
230
+ if (eligibleBeforePolicy.length === 0) return accounts;
231
+ const decisions = /* @__PURE__ */ new Map();
232
+ const gated = accounts.map((account) => {
233
+ if (account.schedulable === false) return account;
234
+ const decision = scheduling.evaluate(
235
+ providerId,
236
+ account.id,
237
+ _nullishCoalesce(account.priority, () => ( DEFAULT_ACCOUNT_PRIORITY)),
238
+ evaluatedAt
239
+ );
240
+ decisions.set(account.id, decision);
241
+ return {
242
+ ...account,
243
+ priority: decision.effectivePriority,
244
+ schedulable: decision.schedulable
245
+ };
246
+ });
247
+ if (gated.some((account) => account.schedulable !== false)) return gated;
248
+ const paused = eligibleBeforePolicy.map((account) => decisions.get(account.id)).filter((decision) => _optionalChain([decision, 'optionalAccess', _3 => _3.action]) === "pause");
249
+ if (paused.length !== eligibleBeforePolicy.length) return gated;
250
+ const resumeAt = paused.map((decision) => _optionalChain([decision, 'optionalAccess', _4 => _4.resumeAt])).filter((value) => !!value).sort()[0];
251
+ throw new (0, _AccountAllowanceScheduling.AccountAllowanceExhaustedError)(providerId, resumeAt);
252
+ }
163
253
  function readSchedulableAccounts(config, providerId) {
164
254
  const raw = _nullishCoalesce(config[ACCOUNTS_KEY[providerId]], () => ( []));
165
255
  const accounts = raw.map((a) => ({
166
256
  id: a.id,
257
+ group: typeof a.group === "string" && a.group.trim() !== "" ? a.group.trim() : providerId,
167
258
  priority: a.priority,
168
259
  lastUsedAt: a.lastUsedAt,
169
- createdAt: a.createdAt
260
+ createdAt: a.createdAt,
261
+ // Persisted opt-out is absolute, including a one-account pool. Legacy rows
262
+ // omit the field and therefore remain enabled.
263
+ schedulable: a.enabled !== false
170
264
  }));
171
265
  const supportedModelsById = new Map(
172
266
  raw.map((a) => [a.id, a.supportedModels])
@@ -184,24 +278,64 @@ function pickByIdTarget(selector, gated, providerId, activeAccountId, sessionKey
184
278
  return void 0;
185
279
  }
186
280
  async function resolveSelectedToken(selector, tokens, providerId, sessionKey, activeGetter, ctx) {
187
- const health = _optionalChain([ctx, 'optionalAccess', _ => _.health]);
188
- const report = _optionalChain([ctx, 'optionalAccess', _2 => _2.reportSelection]);
189
- const now = _optionalChain([ctx, 'optionalAccess', _3 => _3.now]);
190
- const resolvedModel = _optionalChain([ctx, 'optionalAccess', _4 => _4.resolvedModel]);
281
+ const health = _optionalChain([ctx, 'optionalAccess', _5 => _5.health]);
282
+ const report = _optionalChain([ctx, 'optionalAccess', _6 => _6.reportSelection]);
283
+ const now = _optionalChain([ctx, 'optionalAccess', _7 => _7.now]);
284
+ const resolvedModel = _optionalChain([ctx, 'optionalAccess', _8 => _8.resolvedModel]);
285
+ const preferredId = typeof _optionalChain([ctx, 'optionalAccess', _9 => _9.preferredAccountId]) === "string" && ctx.preferredAccountId.trim() !== "" ? ctx.preferredAccountId.trim() : void 0;
286
+ const preferredGroup = typeof _optionalChain([ctx, 'optionalAccess', _10 => _10.preferredAccountGroup]) === "string" && ctx.preferredAccountGroup.trim() !== "" ? ctx.preferredAccountGroup.trim() : void 0;
287
+ if (preferredId && ctx && ctx.boundAccountFallbackPolicy !== "pool") {
288
+ return resolveStrictPreferredToken(selector, tokens, providerId, preferredId, activeGetter, ctx);
289
+ }
191
290
  if (selector && tokens.getAccessTokenForAccount) {
192
291
  const config = await tokens.getFullConfig();
193
292
  const { accounts, activeAccountId, supportedModelsById } = readSchedulableAccounts(config, providerId);
194
- const gated = gateSchedulable(accounts, providerId, health, now, resolvedModel, supportedModelsById);
195
- const poolGated = isPoolGated(accounts, health, resolvedModel);
293
+ const groupAccounts = preferredGroup ? accounts.filter((account) => account.group === preferredGroup) : accounts;
294
+ if (preferredGroup && groupAccounts.length === 0 && _optionalChain([ctx, 'optionalAccess', _11 => _11.boundAccountFallbackPolicy]) !== "pool") {
295
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "not-found");
296
+ }
297
+ let candidates = groupAccounts.length > 0 ? groupAccounts : accounts;
298
+ let healthAndModelGated = gateSchedulable(
299
+ candidates,
300
+ providerId,
301
+ health,
302
+ now,
303
+ resolvedModel,
304
+ supportedModelsById
305
+ );
306
+ let gated = gateByAllowance(healthAndModelGated, providerId, now);
307
+ let groupHasUsableCredential = gated.some((account) => account.schedulable !== false);
308
+ if (groupHasUsableCredential && preferredGroup && _optionalChain([ctx, 'optionalAccess', _12 => _12.boundAccountFallbackPolicy]) === "pool" && candidates !== accounts) {
309
+ groupHasUsableCredential = false;
310
+ for (const account of gated) {
311
+ if (account.schedulable === false) continue;
312
+ if (await tokens.getAccessTokenForAccount(providerId, account.id)) {
313
+ groupHasUsableCredential = true;
314
+ break;
315
+ }
316
+ }
317
+ }
318
+ if (preferredGroup && _optionalChain([ctx, 'optionalAccess', _13 => _13.boundAccountFallbackPolicy]) === "pool" && candidates !== accounts && !groupHasUsableCredential) {
319
+ candidates = accounts;
320
+ healthAndModelGated = gateSchedulable(
321
+ candidates,
322
+ providerId,
323
+ health,
324
+ now,
325
+ resolvedModel,
326
+ supportedModelsById
327
+ );
328
+ gated = gateByAllowance(healthAndModelGated, providerId, now);
329
+ }
330
+ const poolGated = isPoolGated(candidates, health, resolvedModel) || candidates.some((account) => account.schedulable === false) || preferredGroup !== void 0;
196
331
  const remapFor = (id) => remapReportForAccount(supportedModelsById.get(id), resolvedModel);
197
- const preferredId = _optionalChain([ctx, 'optionalAccess', _5 => _5.preferredAccountId]);
198
332
  if (preferredId) {
199
333
  const preferred = gated.find((a) => a.id === preferredId);
200
334
  if (preferred && preferred.schedulable !== false) {
201
335
  const preferredToken = await tokens.getAccessTokenForAccount(providerId, preferredId);
202
336
  if (preferredToken) {
203
337
  maybeTouchLastUsed(selector, tokens, providerId, preferredId);
204
- _optionalChain([report, 'optionalCall', _6 => _6(preferredId, preferredId === activeAccountId, remapFor(preferredId))]);
338
+ _optionalChain([report, 'optionalCall', _14 => _14(preferredId, preferredId === activeAccountId, remapFor(preferredId))]);
205
339
  return preferredToken;
206
340
  }
207
341
  }
@@ -211,23 +345,44 @@ async function resolveSelectedToken(selector, tokens, providerId, sessionKey, ac
211
345
  const byId = await tokens.getAccessTokenForAccount(providerId, targetId);
212
346
  if (byId) {
213
347
  maybeTouchLastUsed(selector, tokens, providerId, targetId);
214
- _optionalChain([report, 'optionalCall', _7 => _7(targetId, false, remapFor(targetId))]);
348
+ _optionalChain([report, 'optionalCall', _15 => _15(targetId, false, remapFor(targetId))]);
215
349
  return byId;
216
350
  }
217
351
  selector.evictAffinity(providerId, targetId);
218
- _optionalChain([health, 'optionalAccess', _8 => _8.recordUpstreamOutcome, 'call', _9 => _9(providerId, targetId, { status: 401, now })]);
352
+ _optionalChain([health, 'optionalAccess', _16 => _16.recordUpstreamOutcome, 'call', _17 => _17(providerId, targetId, { status: 401, now })]);
219
353
  const remaining = gated.filter((a) => a.id !== targetId);
220
354
  const retryId = pickByIdTarget(selector, remaining, providerId, activeAccountId, sessionKey, now, poolGated);
221
355
  if (retryId !== void 0) {
222
356
  const retryToken = await tokens.getAccessTokenForAccount(providerId, retryId);
223
357
  if (retryToken) {
224
358
  maybeTouchLastUsed(selector, tokens, providerId, retryId);
225
- _optionalChain([report, 'optionalCall', _10 => _10(retryId, false, remapFor(retryId))]);
359
+ _optionalChain([report, 'optionalCall', _18 => _18(retryId, false, remapFor(retryId))]);
226
360
  return retryToken;
227
361
  }
228
362
  }
229
363
  }
230
- if (activeAccountId) _optionalChain([report, 'optionalCall', _11 => _11(activeAccountId, true, remapFor(activeAccountId))]);
364
+ if (activeAccountId) {
365
+ if (preferredGroup && _optionalChain([ctx, 'optionalAccess', _19 => _19.boundAccountFallbackPolicy]) !== "pool" && !candidates.some((account) => account.id === activeAccountId)) {
366
+ throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "unavailable");
367
+ }
368
+ const persistedActive = accounts.find((account) => account.id === activeAccountId);
369
+ if (_optionalChain([persistedActive, 'optionalAccess', _20 => _20.schedulable]) === false) return null;
370
+ const activeAllowance = _AccountAllowanceScheduling.getSharedAccountAllowanceScheduling.call(void 0, ).evaluate(
371
+ providerId,
372
+ activeAccountId,
373
+ _nullishCoalesce(_optionalChain([persistedActive, 'optionalAccess', _21 => _21.priority]), () => ( DEFAULT_ACCOUNT_PRIORITY)),
374
+ now
375
+ );
376
+ if (activeAllowance.action === "pause") {
377
+ throw new (0, _AccountAllowanceScheduling.AccountAllowanceExhaustedError)(providerId, activeAllowance.resumeAt);
378
+ }
379
+ const token = await activeGetter();
380
+ if (hasUsableToken(token)) {
381
+ maybeTouchLastUsed(selector, tokens, providerId, activeAccountId);
382
+ _optionalChain([report, 'optionalCall', _22 => _22(activeAccountId, true, remapFor(activeAccountId))]);
383
+ }
384
+ return token;
385
+ }
231
386
  return activeGetter();
232
387
  }
233
388
  return activeGetter();
@@ -276,9 +431,16 @@ var OAuthBearerAuthStrategy = (_class2 = class {
276
431
  this.selector,
277
432
  this.tokens,
278
433
  this.providerId,
279
- _optionalChain([hints, 'optionalAccess', _12 => _12.sessionKey]),
434
+ _optionalChain([hints, 'optionalAccess', _23 => _23.sessionKey]),
280
435
  () => this.resolveAccessToken(),
281
- { health: this.health, reportSelection: _optionalChain([hints, 'optionalAccess', _13 => _13.reportSelection]), resolvedModel: _optionalChain([hints, 'optionalAccess', _14 => _14.resolvedModel]), preferredAccountId: _optionalChain([hints, 'optionalAccess', _15 => _15.preferredAccountId]) }
436
+ {
437
+ health: this.health,
438
+ reportSelection: _optionalChain([hints, 'optionalAccess', _24 => _24.reportSelection]),
439
+ resolvedModel: _optionalChain([hints, 'optionalAccess', _25 => _25.resolvedModel]),
440
+ preferredAccountId: _optionalChain([hints, 'optionalAccess', _26 => _26.preferredAccountId]),
441
+ preferredAccountGroup: _optionalChain([hints, 'optionalAccess', _27 => _27.preferredAccountGroup]),
442
+ boundAccountFallbackPolicy: _optionalChain([hints, 'optionalAccess', _28 => _28.boundAccountFallbackPolicy])
443
+ }
282
444
  );
283
445
  if (!token) {
284
446
  return;
@@ -300,7 +462,7 @@ var OAuthBearerAuthStrategy = (_class2 = class {
300
462
  async describeStatus() {
301
463
  const config = await this.tokens.getFullConfig();
302
464
  const entry = this.providerId === "codex" ? config.codex : config.gemini;
303
- if (!_optionalChain([entry, 'optionalAccess', _16 => _16.accessToken])) {
465
+ if (!_optionalChain([entry, 'optionalAccess', _29 => _29.accessToken])) {
304
466
  return { providerId: this.providerId, ok: false, reason: "missing-credential" };
305
467
  }
306
468
  if (entry.status === "expired") {
@@ -317,7 +479,7 @@ var OAuthBearerAuthStrategy = (_class2 = class {
317
479
  async resolveAccessToken() {
318
480
  const config = await this.tokens.getFullConfig();
319
481
  const entry = this.providerId === "codex" ? config.codex : config.gemini;
320
- if (!_optionalChain([entry, 'optionalAccess', _17 => _17.accessToken])) return null;
482
+ if (!_optionalChain([entry, 'optionalAccess', _30 => _30.accessToken])) return null;
321
483
  const expiresAtMs = entry.expiresAt ? new Date(entry.expiresAt).getTime() : 0;
322
484
  const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - REFRESH_LEAD_MS;
323
485
  if (expiringSoon && entry.refreshToken) {
@@ -327,7 +489,7 @@ var OAuthBearerAuthStrategy = (_class2 = class {
327
489
  if (!refreshed) return null;
328
490
  const fresh = await this.tokens.getFullConfig();
329
491
  const freshEntry = this.providerId === "codex" ? fresh.codex : fresh.gemini;
330
- return _nullishCoalesce(_optionalChain([freshEntry, 'optionalAccess', _18 => _18.accessToken]), () => ( null));
492
+ return _nullishCoalesce(_optionalChain([freshEntry, 'optionalAccess', _31 => _31.accessToken]), () => ( null));
331
493
  }
332
494
  if (entry.status === "expired") return null;
333
495
  return entry.accessToken;
@@ -353,9 +515,16 @@ var PassThroughAuthStrategy = (_class3 = class {
353
515
  this.selector,
354
516
  this.tokens,
355
517
  "claude",
356
- _optionalChain([hints, 'optionalAccess', _19 => _19.sessionKey]),
518
+ _optionalChain([hints, 'optionalAccess', _32 => _32.sessionKey]),
357
519
  () => this.tokens.getValidClaudeAccessToken(),
358
- { health: this.health, reportSelection: _optionalChain([hints, 'optionalAccess', _20 => _20.reportSelection]), resolvedModel: _optionalChain([hints, 'optionalAccess', _21 => _21.resolvedModel]), preferredAccountId: _optionalChain([hints, 'optionalAccess', _22 => _22.preferredAccountId]) }
520
+ {
521
+ health: this.health,
522
+ reportSelection: _optionalChain([hints, 'optionalAccess', _33 => _33.reportSelection]),
523
+ resolvedModel: _optionalChain([hints, 'optionalAccess', _34 => _34.resolvedModel]),
524
+ preferredAccountId: _optionalChain([hints, 'optionalAccess', _35 => _35.preferredAccountId]),
525
+ preferredAccountGroup: _optionalChain([hints, 'optionalAccess', _36 => _36.preferredAccountGroup]),
526
+ boundAccountFallbackPolicy: _optionalChain([hints, 'optionalAccess', _37 => _37.boundAccountFallbackPolicy])
527
+ }
359
528
  );
360
529
  if (!token) return;
361
530
  headers["Authorization"] = `Bearer ${token}`;
@@ -375,7 +544,7 @@ var PassThroughAuthStrategy = (_class3 = class {
375
544
  async describeStatus() {
376
545
  const config = await this.tokens.getFullConfig();
377
546
  const claude = config.claude;
378
- if (!_optionalChain([claude, 'optionalAccess', _23 => _23.accessToken])) {
547
+ if (!_optionalChain([claude, 'optionalAccess', _38 => _38.accessToken])) {
379
548
  return { providerId: "claude", ok: false, reason: "missing-credential" };
380
549
  }
381
550
  if (claude.status === "expired") {
@@ -431,15 +600,22 @@ var StaticBearerAuthStrategy = (_class5 = class {
431
600
  this.selector,
432
601
  this.tokens,
433
602
  "opencodego",
434
- _optionalChain([hints, 'optionalAccess', _24 => _24.sessionKey]),
603
+ _optionalChain([hints, 'optionalAccess', _39 => _39.sessionKey]),
435
604
  () => this.tokens.getValidOpenCodeGoApiKey(),
436
- { health: this.health, reportSelection: _optionalChain([hints, 'optionalAccess', _25 => _25.reportSelection]), resolvedModel: _optionalChain([hints, 'optionalAccess', _26 => _26.resolvedModel]), preferredAccountId: _optionalChain([hints, 'optionalAccess', _27 => _27.preferredAccountId]) }
605
+ {
606
+ health: this.health,
607
+ reportSelection: _optionalChain([hints, 'optionalAccess', _40 => _40.reportSelection]),
608
+ resolvedModel: _optionalChain([hints, 'optionalAccess', _41 => _41.resolvedModel]),
609
+ preferredAccountId: _optionalChain([hints, 'optionalAccess', _42 => _42.preferredAccountId]),
610
+ preferredAccountGroup: _optionalChain([hints, 'optionalAccess', _43 => _43.preferredAccountGroup]),
611
+ boundAccountFallbackPolicy: _optionalChain([hints, 'optionalAccess', _44 => _44.boundAccountFallbackPolicy])
612
+ }
437
613
  );
438
614
  if (!key) {
439
615
  return;
440
616
  }
441
617
  headers["Authorization"] = `Bearer ${key}`;
442
- if (_optionalChain([hints, 'optionalAccess', _28 => _28.upstreamUrl, 'optionalAccess', _29 => _29.includes, 'call', _30 => _30(ANTHROPIC_SHAPE_PATH)])) {
618
+ if (_optionalChain([hints, 'optionalAccess', _45 => _45.upstreamUrl, 'optionalAccess', _46 => _46.includes, 'call', _47 => _47(ANTHROPIC_SHAPE_PATH)])) {
443
619
  headers["x-api-key"] = key;
444
620
  }
445
621
  }
@@ -449,7 +625,7 @@ var StaticBearerAuthStrategy = (_class5 = class {
449
625
  async describeStatus() {
450
626
  const config = await this.tokens.getFullConfig();
451
627
  const oc = config.opencodego;
452
- if (!_optionalChain([oc, 'optionalAccess', _31 => _31.apiKey])) {
628
+ if (!_optionalChain([oc, 'optionalAccess', _48 => _48.apiKey])) {
453
629
  return { providerId: "opencodego", ok: false, reason: "missing-credential" };
454
630
  }
455
631
  if (oc.status === "error") {
@@ -523,31 +699,29 @@ var _GeminiCodeAssistTransformer = require('@omnicross/core/transformer/transfor
523
699
 
524
700
  // src/opencodego/CircuitBreaker.ts
525
701
  var CircuitBreaker = (_class7 = class {
526
- __init12() {this.state = "closed"}
527
- /** CONSECUTIVE failures while closed (reset by any closed success). */
528
- __init13() {this.failureCount = 0}
529
- /** Successes accumulated in the current half-open probe window. */
530
- __init14() {this.successCount = 0}
531
- /** Test calls admitted in the current half-open window (cap = halfOpenMaxCalls). */
532
- __init15() {this.halfOpenCalls = 0}
533
- /** `now()` at the last recorded failure — drives the open→half-open elapsed check. */
534
- __init16() {this.lastFailureTime = 0}
702
+ __init12() {this.snapshot = {
703
+ mode: "closed",
704
+ consecutiveFailures: 0,
705
+ openedAt: 0,
706
+ probeAdmissions: 0,
707
+ probeSuccesses: 0
708
+ }}
535
709
 
536
710
 
537
-
538
-
539
- constructor(opts = {}) {;_class7.prototype.__init12.call(this);_class7.prototype.__init13.call(this);_class7.prototype.__init14.call(this);_class7.prototype.__init15.call(this);_class7.prototype.__init16.call(this);
540
- this.threshold = _nullishCoalesce(opts.threshold, () => ( 3));
541
- this.openMs = _nullishCoalesce(opts.openMs, () => ( 3e4));
542
- this.halfOpenMaxCalls = _nullishCoalesce(opts.halfOpenMaxCalls, () => ( 3));
711
+ constructor(opts = {}) {;_class7.prototype.__init12.call(this);
712
+ this.limits = {
713
+ threshold: _nullishCoalesce(opts.threshold, () => ( 3)),
714
+ openMs: _nullishCoalesce(opts.openMs, () => ( 3e4)),
715
+ halfOpenMaxCalls: _nullishCoalesce(opts.halfOpenMaxCalls, () => ( 3))
716
+ };
543
717
  this.now = _nullishCoalesce(opts.now, () => ( Date.now));
544
718
  }
545
719
  /** Current state (diagnostics / tests). */
546
720
  getState() {
547
- return this.state;
721
+ return this.snapshot.mode;
548
722
  }
549
723
  /**
550
- * Admission gate (`fallback.go:54-72` `AllowRequest`). Returns whether a
724
+ * Admission gate. Returns whether a
551
725
  * request to this model is allowed RIGHT NOW. Side-effecting BY DESIGN:
552
726
  * - `closed` → always admit.
553
727
  * - `open` → if `now() - lastFailureTime > openMs`, FLIP to `half-open`,
@@ -558,74 +732,91 @@ var CircuitBreaker = (_class7 = class {
558
732
  * recorded outcome resolves the state).
559
733
  */
560
734
  allowRequest() {
561
- switch (this.state) {
562
- case "closed":
563
- return true;
564
- case "open":
565
- if (this.now() - this.lastFailureTime > this.openMs) {
566
- this.state = "half-open";
567
- this.successCount = 0;
568
- this.halfOpenCalls = 1;
569
- return true;
570
- }
571
- return false;
572
- case "half-open":
573
- if (this.halfOpenCalls < this.halfOpenMaxCalls) {
574
- this.halfOpenCalls += 1;
575
- return true;
576
- }
577
- return false;
578
- default:
579
- return true;
735
+ if (this.snapshot.mode === "closed") return true;
736
+ if (this.snapshot.mode === "open") {
737
+ const elapsed = this.now() - this.snapshot.openedAt;
738
+ if (elapsed <= this.limits.openMs) return false;
739
+ this.snapshot = {
740
+ ...this.snapshot,
741
+ mode: "half-open",
742
+ probeAdmissions: 1,
743
+ probeSuccesses: 0
744
+ };
745
+ return true;
580
746
  }
747
+ if (this.snapshot.probeAdmissions >= this.limits.halfOpenMaxCalls) return false;
748
+ this.snapshot = {
749
+ ...this.snapshot,
750
+ probeAdmissions: this.snapshot.probeAdmissions + 1
751
+ };
752
+ return true;
581
753
  }
582
754
  /**
583
- * Record a successful attempt (`fallback.go:75-91` `RecordSuccess`).
755
+ * Record a successful attempt.
584
756
  * - `half-open` → increment `successCount`; at `halfOpenMaxCalls` successes,
585
757
  * CLOSE the circuit and reset all counters.
586
758
  * - `closed` → reset the consecutive `failureCount` (a single good call
587
759
  * clears the streak).
588
760
  */
589
761
  recordSuccess() {
590
- if (this.state === "half-open") {
591
- this.successCount += 1;
592
- if (this.successCount >= this.halfOpenMaxCalls) {
593
- this.state = "closed";
594
- this.failureCount = 0;
595
- this.successCount = 0;
596
- this.halfOpenCalls = 0;
762
+ if (this.snapshot.mode === "open") return;
763
+ if (this.snapshot.mode === "closed") {
764
+ if (this.snapshot.consecutiveFailures !== 0) {
765
+ this.snapshot = { ...this.snapshot, consecutiveFailures: 0 };
597
766
  }
598
767
  return;
599
768
  }
600
- this.failureCount = 0;
769
+ const probeSuccesses = this.snapshot.probeSuccesses + 1;
770
+ if (probeSuccesses >= this.limits.halfOpenMaxCalls) {
771
+ this.snapshot = {
772
+ mode: "closed",
773
+ consecutiveFailures: 0,
774
+ openedAt: 0,
775
+ probeAdmissions: 0,
776
+ probeSuccesses: 0
777
+ };
778
+ return;
779
+ }
780
+ this.snapshot = { ...this.snapshot, probeSuccesses };
601
781
  }
602
782
  /**
603
- * Record a failed attempt (`fallback.go:94-115` `RecordFailure`).
783
+ * Record a failed attempt.
604
784
  * - `half-open` → immediately RE-OPEN (one probe failure is enough); stamp
605
785
  * `lastFailureTime`, reset `successCount`.
606
786
  * - `closed` → increment the consecutive `failureCount`; at `threshold`,
607
787
  * OPEN the circuit. Always stamp `lastFailureTime`.
608
788
  */
609
789
  recordFailure() {
610
- this.lastFailureTime = this.now();
611
- if (this.state === "half-open") {
612
- this.state = "open";
613
- this.successCount = 0;
614
- this.halfOpenCalls = 0;
790
+ const openedAt = this.now();
791
+ if (this.snapshot.mode === "half-open") {
792
+ this.snapshot = {
793
+ ...this.snapshot,
794
+ mode: "open",
795
+ openedAt,
796
+ probeAdmissions: 0,
797
+ probeSuccesses: 0
798
+ };
615
799
  return;
616
800
  }
617
- this.failureCount += 1;
618
- if (this.failureCount >= this.threshold) {
619
- this.state = "open";
801
+ const consecutiveFailures = this.snapshot.consecutiveFailures + 1;
802
+ this.snapshot = {
803
+ ...this.snapshot,
804
+ consecutiveFailures,
805
+ openedAt,
806
+ mode: consecutiveFailures >= this.limits.threshold ? "open" : this.snapshot.mode
807
+ };
808
+ if (this.snapshot.mode === "open") {
809
+ this.snapshot.probeAdmissions = 0;
810
+ this.snapshot.probeSuccesses = 0;
620
811
  }
621
812
  }
622
813
  }, _class7);
623
814
  var CircuitBreakerRegistry = (_class8 = class {
624
- constructor(options = {}) {;_class8.prototype.__init17.call(this);
815
+ constructor(options = {}) {;_class8.prototype.__init13.call(this);
625
816
  this.options = options;
626
817
  }
627
818
 
628
- __init17() {this.breakers = /* @__PURE__ */ new Map()}
819
+ __init13() {this.breakers = /* @__PURE__ */ new Map()}
629
820
  /** Get (or lazily create) the breaker for a model id. */
630
821
  get(modelId) {
631
822
  let breaker = this.breakers.get(modelId);
@@ -665,8 +856,7 @@ var DEFAULT_OPENCODEGO_MODEL_MAP = {
665
856
  modelId: "glm-5"
666
857
  },
667
858
  complex: {
668
- // Reference maps `complex` `glm-5.1` (config.example.json:55-60); was
669
- // drifted to `mimo-v2-pro` (audit D4).
859
+ // Complex tasks use the higher-capability GLM variant by default.
670
860
  modelId: "glm-5.1"
671
861
  },
672
862
  fast: {
@@ -793,11 +983,11 @@ function resolveOpenCodeGoShape(entry) {
793
983
  function resolveOpenCodeGoHalf(modelId, config) {
794
984
  if (!config) return "go";
795
985
  for (const entry of Object.values(_nullishCoalesce(config.modelMap, () => ( {})))) {
796
- if (_optionalChain([entry, 'optionalAccess', _32 => _32.modelId]) === modelId) return _nullishCoalesce(entry.provider, () => ( "go"));
986
+ if (_optionalChain([entry, 'optionalAccess', _49 => _49.modelId]) === modelId) return _nullishCoalesce(entry.provider, () => ( "go"));
797
987
  }
798
988
  for (const list of Object.values(_nullishCoalesce(config.fallbacks, () => ( {})))) {
799
989
  for (const entry of _nullishCoalesce(list, () => ( []))) {
800
- if (_optionalChain([entry, 'optionalAccess', _33 => _33.modelId]) === modelId) return _nullishCoalesce(entry.provider, () => ( "go"));
990
+ if (_optionalChain([entry, 'optionalAccess', _50 => _50.modelId]) === modelId) return _nullishCoalesce(entry.provider, () => ( "go"));
801
991
  }
802
992
  }
803
993
  return "go";
@@ -805,67 +995,44 @@ function resolveOpenCodeGoHalf(modelId, config) {
805
995
 
806
996
  // src/opencodego/ScenarioRouter.ts
807
997
  var COMPLEX_KEYWORDS = [
808
- // Architectural
809
- "architect",
810
998
  "architecture",
811
999
  "refactor",
812
1000
  "redesign",
813
- "complex",
814
- "difficult",
815
- "challenging",
816
1001
  "optimize",
817
1002
  "performance",
818
- "efficiency",
819
- "design pattern",
820
- "best practice",
821
- // Tool-related keywords indicate complex operations
822
- "execute",
823
- "run command",
824
- "bash",
825
- "shell",
826
1003
  "implement",
827
1004
  "build",
828
- "create",
829
- "add feature",
830
- "write to",
831
1005
  "edit file",
832
- "create file"
1006
+ "debug",
1007
+ "migrate",
1008
+ "benchmark"
833
1009
  ];
834
1010
  var THINKING_KEYWORDS = [
835
1011
  "think",
836
- "thinking",
837
1012
  "plan",
838
1013
  "reason",
839
- "reasoning",
840
1014
  "analyze",
841
- "analysis",
842
- "step by step"
1015
+ "step by step",
1016
+ "evaluate",
1017
+ "compare tradeoffs"
843
1018
  ];
844
1019
  var ANT_THINKING_MARKER = "antThinking";
845
1020
  var TOOL_BLOCKERS = [
846
1021
  "tool",
847
1022
  "function",
848
- "execute",
849
- "run command",
1023
+ "command",
850
1024
  "write",
851
1025
  "edit",
852
- "create",
853
1026
  "delete",
854
- "remove",
855
1027
  "implement",
856
1028
  "build",
857
- "add",
858
1029
  "modify"
859
1030
  ];
860
1031
  var BACKGROUND_KEYWORDS = [
861
1032
  "list directory",
862
- "ls -",
863
- "dir",
864
1033
  "show file",
865
- "view file",
866
- "cat file",
1034
+ "read file",
867
1035
  "what is",
868
- "what's",
869
1036
  "tell me about",
870
1037
  "check status",
871
1038
  "show status"
@@ -897,7 +1064,7 @@ function hasBackgroundPattern(loweredSlices) {
897
1064
  return containsAny(loweredSlices, BACKGROUND_KEYWORDS);
898
1065
  }
899
1066
  function resolveOpenCodeGoScenario(summary, config) {
900
- const longContextThreshold = _nullishCoalesce(_optionalChain([config, 'optionalAccess', _34 => _34.modelMap, 'optionalAccess', _35 => _35.long_context, 'optionalAccess', _36 => _36.contextThreshold]), () => ( DEFAULT_OPENCODEGO_LONG_CONTEXT_THRESHOLD));
1067
+ const longContextThreshold = _nullishCoalesce(_optionalChain([config, 'optionalAccess', _51 => _51.modelMap, 'optionalAccess', _52 => _52.long_context, 'optionalAccess', _53 => _53.contextThreshold]), () => ( DEFAULT_OPENCODEGO_LONG_CONTEXT_THRESHOLD));
901
1068
  if (summary.estimatedInputTokens >= longContextThreshold) {
902
1069
  return "long_context";
903
1070
  }
@@ -921,7 +1088,7 @@ function opencodegoTransformerNamesForShape(shape) {
921
1088
  return ["gemini"];
922
1089
  case "chat":
923
1090
  default:
924
- return ["opencodego"];
1091
+ return ["openai"];
925
1092
  }
926
1093
  }
927
1094
  function resolveOpenCodeGoTarget(modelId, config) {
@@ -930,7 +1097,7 @@ function resolveOpenCodeGoTarget(modelId, config) {
930
1097
  return { half, shape };
931
1098
  }
932
1099
  var SubscriptionProviderRegistry = (_class9 = class {
933
- constructor(accounts, tokens) {;_class9.prototype.__init18.call(this);
1100
+ constructor(accounts, tokens) {;_class9.prototype.__init14.call(this);
934
1101
  this.accounts = accounts;
935
1102
  this.tokens = tokens;
936
1103
  const claude = this.accounts.getStrategy("claude");
@@ -976,7 +1143,7 @@ var SubscriptionProviderRegistry = (_class9 = class {
976
1143
  authStrategy: codex,
977
1144
  mode: "transformer",
978
1145
  // ChatGPT internal endpoint — accepts the OpenAI Responses API
979
- // format. Mirrors `_others/claude-relay-service/src/routes/openaiRoutes.js:454`.
1146
+ // format and uses the Codex OAuth access token below.
980
1147
  // The Codex OAuth access token grants access here; the public
981
1148
  // `api.openai.com/v1/responses` endpoint would reject the same token.
982
1149
  resolveUpstreamUrl: () => "https://chatgpt.com/backend-api/codex/responses",
@@ -1022,16 +1189,16 @@ var SubscriptionProviderRegistry = (_class9 = class {
1022
1189
  // `ocConfig`; the core `/v1/messages` plan builder passes the opaque
1023
1190
  // `route.subscriptionConfig`). With NO zen config every resolved model
1024
1191
  // is go-half → byte-identical to the prior resolver.
1025
- // `// UNVERIFIED (no live zen key)`: the zen endpoint hosts/paths are
1026
- // ported from the reference + proven in-process only.
1192
+ // `// UNVERIFIED (no live zen key)`: the ZEN endpoint hosts and paths
1193
+ // are covered by in-process tests only.
1027
1194
  resolveUpstreamUrl: (model, config) => {
1028
1195
  const oc = config;
1029
1196
  const { half, shape } = resolveOpenCodeGoTarget(model, oc);
1030
- const override = half === "zen" ? _optionalChain([oc, 'optionalAccess', _37 => _37.zenBaseUrl]) : _optionalChain([oc, 'optionalAccess', _38 => _38.baseUrl]);
1197
+ const override = half === "zen" ? _optionalChain([oc, 'optionalAccess', _54 => _54.zenBaseUrl]) : _optionalChain([oc, 'optionalAccess', _55 => _55.baseUrl]);
1031
1198
  return buildOpenCodeGoUrl(half, shape, override);
1032
1199
  },
1033
1200
  // zen seam (Decision 3): vary the provider transformer chain by resolved
1034
- // shape (anthropic⇒[] verbatim, chat⇒opencodego, responses⇒openai-response,
1201
+ // shape (anthropic⇒[] verbatim, chat⇒openai, responses⇒openai-response,
1035
1202
  // gemini⇒gemini). OPTIONAL on the profile type — only opencodego sets it;
1036
1203
  // claude/codex/gemini omit it and fall back to `providerTransformerNames`,
1037
1204
  // keeping their routing byte-identical. The static `providerTransformerNames`
@@ -1041,11 +1208,11 @@ var SubscriptionProviderRegistry = (_class9 = class {
1041
1208
  const { shape } = resolveOpenCodeGoTarget(model, config);
1042
1209
  return opencodegoTransformerNamesForShape(shape);
1043
1210
  },
1044
- providerTransformerNames: ["opencodego"],
1211
+ providerTransformerNames: ["openai"],
1045
1212
  modelTransformerNames: [],
1046
1213
  modelMapper: (sdkModel, summary, config) => {
1047
1214
  const scenario = resolveOpenCodeGoScenario(summary, config);
1048
- const entry = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess', _39 => _39.modelMap, 'optionalAccess', _40 => _40[scenario]]), () => ( _optionalChain([config, 'optionalAccess', _41 => _41.modelMap, 'optionalAccess', _42 => _42.default]))), () => ( DEFAULT_OPENCODEGO_MODEL_MAP[scenario])), () => ( DEFAULT_OPENCODEGO_MODEL_MAP.default));
1215
+ const entry = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess', _56 => _56.modelMap, 'optionalAccess', _57 => _57[scenario]]), () => ( _optionalChain([config, 'optionalAccess', _58 => _58.modelMap, 'optionalAccess', _59 => _59.default]))), () => ( DEFAULT_OPENCODEGO_MODEL_MAP[scenario])), () => ( DEFAULT_OPENCODEGO_MODEL_MAP.default));
1049
1216
  if (!entry) {
1050
1217
  return { resolvedModel: sdkModel, scenario };
1051
1218
  }
@@ -1055,16 +1222,15 @@ var SubscriptionProviderRegistry = (_class9 = class {
1055
1222
  // circuit is open. `breaker.allowRequest(modelId)` is the admission
1056
1223
  // gate — calling it has the side effect of flipping an `open` model to
1057
1224
  // `half-open` once its 30s window elapses AND counting a half-open admit
1058
- // slot. It MUST therefore be consulted EXACTLY ONCE per returned model,
1059
- // on the candidate about to be attempted — mirroring the reference
1060
- // (`fallback.go` calls `AllowRequest` once, on the model it returns).
1225
+ // slot. It MUST therefore be consulted exactly once per returned model,
1226
+ // on the candidate about to be attempted.
1061
1227
  // An early-returning scan (NOT `Array.filter`, which would `allowRequest`
1062
1228
  // every candidate and burn the admit slots of half-open models AFTER the
1063
1229
  // chosen one — those are never attempted, never recorded, so they would
1064
1230
  // wedge permanently in half-open). When NO circuit is open this returns
1065
1231
  // the same first non-attempted entry as the prior `!attempted` filter.
1066
1232
  nextFallback: (scenario, attempted, config) => {
1067
- const list = _nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess', _43 => _43.fallbacks, 'optionalAccess', _44 => _44[scenario]]), () => ( DEFAULT_OPENCODEGO_FALLBACKS[scenario])), () => ( []));
1233
+ const list = _nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess', _60 => _60.fallbacks, 'optionalAccess', _61 => _61[scenario]]), () => ( DEFAULT_OPENCODEGO_FALLBACKS[scenario])), () => ( []));
1068
1234
  for (const entry of list) {
1069
1235
  if (attempted.includes(entry.modelId)) continue;
1070
1236
  if (this.breaker.allowRequest(entry.modelId)) return entry;
@@ -1091,11 +1257,11 @@ var SubscriptionProviderRegistry = (_class9 = class {
1091
1257
  * process singleton, built here and captured by the opencodego profile's
1092
1258
  * `nextFallback` (consult) + `recordModelOutcome` (record) closures. Because
1093
1259
  * the `SubscriptionProviderRegistry` is itself a process singleton (via
1094
- * `setSubscriptionProviderRegistry`), breaker state persists across requests
1095
- * exactly the reference's long-lived `FallbackHandler`. Constructed with the
1096
- * default reference thresholds (3 / 30s / 3) and the default `Date.now` clock.
1260
+ * `setSubscriptionProviderRegistry`), breaker state persists across requests.
1261
+ * Constructed with the production thresholds (3 / 30s / 3) and the default
1262
+ * `Date.now` clock.
1097
1263
  */
1098
- __init18() {this.breaker = new CircuitBreakerRegistry()}
1264
+ __init14() {this.breaker = new CircuitBreakerRegistry()}
1099
1265
  /** Returns the dispatch profile for a known subscription provider, or
1100
1266
  * `null` for unknown ids (callers must treat null as "fall back to the
1101
1267
  * legacy LLM provider DB lookup"). */
@@ -1121,6 +1287,8 @@ function getSubscriptionProviderRegistry() {
1121
1287
  }
1122
1288
 
1123
1289
  // src/SubscriptionDispatcher.ts
1290
+
1291
+
1124
1292
  var _geminicodeassistresolver = require('@omnicross/core/ports/gemini-code-assist-resolver');
1125
1293
 
1126
1294
 
@@ -1165,7 +1333,7 @@ var SubscriptionDispatcher = class {
1165
1333
  scenario = mapped.scenario;
1166
1334
  req.anthropicBody.model = resolvedModel;
1167
1335
  }
1168
- const upstreamUrl = _optionalChain([this, 'access', _45 => _45.profile, 'access', _46 => _46.resolveUpstreamUrl, 'optionalCall', _47 => _47(resolvedModel, ocConfig)]);
1336
+ const upstreamUrl = _optionalChain([this, 'access', _62 => _62.profile, 'access', _63 => _63.resolveUpstreamUrl, 'optionalCall', _64 => _64(resolvedModel, ocConfig)]);
1169
1337
  if (!upstreamUrl) {
1170
1338
  throw new Error(`[SubscriptionDispatcher] profile=${this.profile.providerId} missing resolveUpstreamUrl`);
1171
1339
  }
@@ -1197,7 +1365,7 @@ var SubscriptionDispatcher = class {
1197
1365
  );
1198
1366
  try {
1199
1367
  const upstream = await this.hooks.fetchWithRetry(upstreamUrl, headers, req.anthropicBody, currentModel);
1200
- _optionalChain([this, 'access', _48 => _48.profile, 'access', _49 => _49.recordModelOutcome, 'optionalCall', _50 => _50(currentModel, true)]);
1368
+ _optionalChain([this, 'access', _65 => _65.profile, 'access', _66 => _66.recordModelOutcome, 'optionalCall', _67 => _67(currentModel, true)]);
1201
1369
  this.markHealth(usedAccountId, 200);
1202
1370
  await this.hooks.writeProxyResponse(req.res, upstream, req.isStream, req.reqId);
1203
1371
  return;
@@ -1206,7 +1374,7 @@ var SubscriptionDispatcher = class {
1206
1374
  if (handled.retryOnce) {
1207
1375
  try {
1208
1376
  const upstream = await this.hooks.fetchWithRetry(upstreamUrl, handled.headers, req.anthropicBody, currentModel);
1209
- _optionalChain([this, 'access', _51 => _51.profile, 'access', _52 => _52.recordModelOutcome, 'optionalCall', _53 => _53(currentModel, true)]);
1377
+ _optionalChain([this, 'access', _68 => _68.profile, 'access', _69 => _69.recordModelOutcome, 'optionalCall', _70 => _70(currentModel, true)]);
1210
1378
  this.markHealth(usedAccountId, 200);
1211
1379
  await this.hooks.writeProxyResponse(req.res, upstream, req.isStream, req.reqId);
1212
1380
  return;
@@ -1216,10 +1384,10 @@ var SubscriptionDispatcher = class {
1216
1384
  }
1217
1385
  }
1218
1386
  if (caughtErrorBreakerOutcome(err) === "failure") {
1219
- _optionalChain([this, 'access', _54 => _54.profile, 'access', _55 => _55.recordModelOutcome, 'optionalCall', _56 => _56(currentModel, false)]);
1387
+ _optionalChain([this, 'access', _71 => _71.profile, 'access', _72 => _72.recordModelOutcome, 'optionalCall', _73 => _73(currentModel, false)]);
1220
1388
  }
1221
1389
  this.markHealth(usedAccountId, errStatus(err), err);
1222
- const next = _optionalChain([this, 'access', _57 => _57.profile, 'access', _58 => _58.nextFallback, 'optionalCall', _59 => _59(scenario, attempted, ocConfig)]);
1390
+ const next = _optionalChain([this, 'access', _74 => _74.profile, 'access', _75 => _75.nextFallback, 'optionalCall', _76 => _76(scenario, attempted, ocConfig)]);
1223
1391
  if (!next || attempted.length >= MAX_FALLBACK_ATTEMPTS_LOCAL) {
1224
1392
  throw err;
1225
1393
  }
@@ -1233,7 +1401,7 @@ var SubscriptionDispatcher = class {
1233
1401
  }
1234
1402
  /** Standard subscription transformer chain — Codex/Gemini/OpenCodeGo OpenAI-shape. */
1235
1403
  async dispatchTransformerChain(req, upstreamUrl, resolvedModel, scenario, ocConfig, sessionKey) {
1236
- const providerNames = _nullishCoalesce(_optionalChain([this, 'access', _60 => _60.profile, 'access', _61 => _61.resolveProviderTransformerNames, 'optionalCall', _62 => _62(resolvedModel, ocConfig)]), () => ( this.profile.providerTransformerNames));
1404
+ const providerNames = _nullishCoalesce(_optionalChain([this, 'access', _77 => _77.profile, 'access', _78 => _78.resolveProviderTransformerNames, 'optionalCall', _79 => _79(resolvedModel, ocConfig)]), () => ( this.profile.providerTransformerNames));
1237
1405
  const providerTransformers = this.resolveTransformers(providerNames);
1238
1406
  const modelTransformers = this.resolveTransformers(this.profile.modelTransformerNames);
1239
1407
  const transformerProvider = {
@@ -1274,7 +1442,7 @@ var SubscriptionDispatcher = class {
1274
1442
  );
1275
1443
  try {
1276
1444
  const upstream = await this.hooks.fetchWithRetry(fetchUrl, headers, requestBody, currentModel);
1277
- _optionalChain([this, 'access', _63 => _63.profile, 'access', _64 => _64.recordModelOutcome, 'optionalCall', _65 => _65(currentModel, true)]);
1445
+ _optionalChain([this, 'access', _80 => _80.profile, 'access', _81 => _81.recordModelOutcome, 'optionalCall', _82 => _82(currentModel, true)]);
1278
1446
  this.markHealth(usedAccountId, 200);
1279
1447
  const finalResponse = await this.hooks.executor.executeResponseChain(
1280
1448
  requestBody,
@@ -1290,7 +1458,7 @@ var SubscriptionDispatcher = class {
1290
1458
  if (handled.retryOnce) {
1291
1459
  try {
1292
1460
  const upstream = await this.hooks.fetchWithRetry(fetchUrl, handled.headers, requestBody, currentModel);
1293
- _optionalChain([this, 'access', _66 => _66.profile, 'access', _67 => _67.recordModelOutcome, 'optionalCall', _68 => _68(currentModel, true)]);
1461
+ _optionalChain([this, 'access', _83 => _83.profile, 'access', _84 => _84.recordModelOutcome, 'optionalCall', _85 => _85(currentModel, true)]);
1294
1462
  this.markHealth(usedAccountId, 200);
1295
1463
  const finalResponse = await this.hooks.executor.executeResponseChain(
1296
1464
  requestBody,
@@ -1307,10 +1475,10 @@ var SubscriptionDispatcher = class {
1307
1475
  }
1308
1476
  }
1309
1477
  if (caughtErrorBreakerOutcome(err) === "failure") {
1310
- _optionalChain([this, 'access', _69 => _69.profile, 'access', _70 => _70.recordModelOutcome, 'optionalCall', _71 => _71(currentModel, false)]);
1478
+ _optionalChain([this, 'access', _86 => _86.profile, 'access', _87 => _87.recordModelOutcome, 'optionalCall', _88 => _88(currentModel, false)]);
1311
1479
  }
1312
1480
  this.markHealth(usedAccountId, errStatus(err), err);
1313
- const next = _optionalChain([this, 'access', _72 => _72.profile, 'access', _73 => _73.nextFallback, 'optionalCall', _74 => _74(scenario, attempted, ocConfig)]);
1481
+ const next = _optionalChain([this, 'access', _89 => _89.profile, 'access', _90 => _90.nextFallback, 'optionalCall', _91 => _91(scenario, attempted, ocConfig)]);
1314
1482
  if (!next || attempted.length >= MAX_FALLBACK_ATTEMPTS_LOCAL) {
1315
1483
  throw err;
1316
1484
  }
@@ -1340,7 +1508,7 @@ var SubscriptionDispatcher = class {
1340
1508
  return { firstModel: primaryModel, attempted: [] };
1341
1509
  }
1342
1510
  const skipped = [primaryModel];
1343
- const firstAdmitting = _optionalChain([this, 'access', _75 => _75.profile, 'access', _76 => _76.nextFallback, 'optionalCall', _77 => _77(scenario, skipped, ocConfig)]);
1511
+ const firstAdmitting = _optionalChain([this, 'access', _92 => _92.profile, 'access', _93 => _93.nextFallback, 'optionalCall', _94 => _94(scenario, skipped, ocConfig)]);
1344
1512
  if (firstAdmitting) {
1345
1513
  console.warn(
1346
1514
  `[AgentProxy:subscription] opencodego primary ${primaryModel} circuit open -> first admitting fallback ${firstAdmitting.modelId}`
@@ -1358,7 +1526,7 @@ var SubscriptionDispatcher = class {
1358
1526
  * successfully (caller should retry once); otherwise re-throws.
1359
1527
  */
1360
1528
  async maybeRetryAfterError(err, headers, req, resolvedModel, sessionKey) {
1361
- const status = _nullishCoalesce(_optionalChain([err, 'optionalAccess', _78 => _78.status]), () => ( 0));
1529
+ const status = _nullishCoalesce(_optionalChain([err, 'optionalAccess', _95 => _95.status]), () => ( 0));
1362
1530
  if (status !== 401) {
1363
1531
  return { retryOnce: false, headers };
1364
1532
  }
@@ -1378,6 +1546,7 @@ var SubscriptionDispatcher = class {
1378
1546
  try {
1379
1547
  await this.profile.authStrategy.applyHeaders(headers, hints);
1380
1548
  } catch (err) {
1549
+ if (_AccountAllowanceScheduling.isAccountAllowanceExhaustedError.call(void 0, err) || _BoundAccountSelectionError.isBoundAccountSelectionError.call(void 0, err)) throw err;
1381
1550
  console.warn("[AgentProxy:subscription] authStrategy.applyHeaders threw:", _serializeError.serializeError.call(void 0, err));
1382
1551
  }
1383
1552
  }
@@ -1480,11 +1649,11 @@ var SubscriptionDispatcher = class {
1480
1649
  };
1481
1650
  var MAX_FALLBACK_ATTEMPTS_LOCAL = 3;
1482
1651
  function errStatus(err) {
1483
- const status = _optionalChain([err, 'optionalAccess', _79 => _79.status]);
1652
+ const status = _optionalChain([err, 'optionalAccess', _96 => _96.status]);
1484
1653
  return typeof status === "number" ? status : null;
1485
1654
  }
1486
1655
  function errHeaders(err) {
1487
- const h = _optionalChain([err, 'optionalAccess', _80 => _80.headers]);
1656
+ const h = _optionalChain([err, 'optionalAccess', _97 => _97.headers]);
1488
1657
  if (!h) return void 0;
1489
1658
  if (typeof h.get === "function") return h;
1490
1659
  if (typeof h === "object") return h;
@@ -1492,11 +1661,11 @@ function errHeaders(err) {
1492
1661
  }
1493
1662
  function errBodyText(err) {
1494
1663
  const e = err;
1495
- const raw = typeof _optionalChain([e, 'optionalAccess', _81 => _81.bodyText]) === "string" ? e.bodyText : typeof _optionalChain([e, 'optionalAccess', _82 => _82.body]) === "string" ? e.body : void 0;
1496
- return _optionalChain([raw, 'optionalAccess', _83 => _83.slice, 'call', _84 => _84(0, 2048)]);
1664
+ const raw = typeof _optionalChain([e, 'optionalAccess', _98 => _98.bodyText]) === "string" ? e.bodyText : typeof _optionalChain([e, 'optionalAccess', _99 => _99.body]) === "string" ? e.body : void 0;
1665
+ return _optionalChain([raw, 'optionalAccess', _100 => _100.slice, 'call', _101 => _101(0, 2048)]);
1497
1666
  }
1498
1667
  function caughtErrorBreakerOutcome(err) {
1499
- const status = _optionalChain([err, 'optionalAccess', _85 => _85.status]);
1668
+ const status = _optionalChain([err, 'optionalAccess', _102 => _102.status]);
1500
1669
  if (typeof status !== "number") return "failure";
1501
1670
  if (status === 0) return "neutral";
1502
1671
  if (status >= 500 || status === 429) return "failure";
@@ -1534,4 +1703,4 @@ function stripAuthHeaders(headers) {
1534
1703
 
1535
1704
 
1536
1705
 
1537
- exports.DEFAULT_ACCOUNT_PRIORITY = DEFAULT_ACCOUNT_PRIORITY; exports.LAST_USED_PERSIST_THROTTLE_MS = LAST_USED_PERSIST_THROTTLE_MS; exports.SESSION_AFFINITY_TTL_MS = SESSION_AFFINITY_TTL_MS; exports.SubscriptionAccountSelector = SubscriptionAccountSelector; exports.SubscriptionAccountService = SubscriptionAccountService; exports.SubscriptionDispatcher = SubscriptionDispatcher; exports.SubscriptionProviderRegistry = SubscriptionProviderRegistry; exports.claudeOAuth = _chunkTPW5Q25Ycjs.claude_exports; exports.codexOAuth = _chunkTPW5Q25Ycjs.codex_exports; exports.geminiOAuth = _chunkTPW5Q25Ycjs.gemini_exports; exports.getSubscriptionAccountService = getSubscriptionAccountService; exports.getSubscriptionProviderRegistry = getSubscriptionProviderRegistry; exports.setSubscriptionAccountService = setSubscriptionAccountService; exports.setSubscriptionProviderRegistry = setSubscriptionProviderRegistry;
1706
+ exports.DEFAULT_ACCOUNT_PRIORITY = DEFAULT_ACCOUNT_PRIORITY; exports.LAST_USED_PERSIST_THROTTLE_MS = LAST_USED_PERSIST_THROTTLE_MS; exports.SESSION_AFFINITY_TTL_MS = SESSION_AFFINITY_TTL_MS; exports.SubscriptionAccountSelector = SubscriptionAccountSelector; exports.SubscriptionAccountService = SubscriptionAccountService; exports.SubscriptionDispatcher = SubscriptionDispatcher; exports.SubscriptionProviderRegistry = SubscriptionProviderRegistry; exports.claudeOAuth = _chunkPWV5NBO5cjs.claude_exports; exports.codexOAuth = _chunkPWV5NBO5cjs.codex_exports; exports.geminiOAuth = _chunkPWV5NBO5cjs.gemini_exports; exports.getSubscriptionAccountService = getSubscriptionAccountService; exports.getSubscriptionProviderRegistry = getSubscriptionProviderRegistry; exports.setSubscriptionAccountService = setSubscriptionAccountService; exports.setSubscriptionProviderRegistry = setSubscriptionProviderRegistry;