@omnicross/subscriptions 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +4 -33
- package/dist/index.cjs +315 -152
- package/dist/index.d.cts +12 -12
- package/dist/index.d.ts +12 -12
- package/dist/index.js +271 -108
- package/dist/oauth.d.cts +1 -5
- package/dist/oauth.d.ts +1 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -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,103 @@ 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(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
|
+
_optionalChain([ctx, 'access', _ => _.reportSelection, 'optionalCall', _2 => _2(preferredId, preferredId === activeAccountId, remapped)]);
|
|
223
|
+
return token;
|
|
224
|
+
}
|
|
225
|
+
function gateByAllowance(accounts, providerId, now) {
|
|
226
|
+
const scheduling = _AccountAllowanceScheduling.getSharedAccountAllowanceScheduling.call(void 0, );
|
|
227
|
+
const evaluatedAt = _nullishCoalesce(now, () => ( Date.now()));
|
|
228
|
+
const eligibleBeforePolicy = accounts.filter((account) => account.schedulable !== false);
|
|
229
|
+
if (eligibleBeforePolicy.length === 0) return accounts;
|
|
230
|
+
const decisions = /* @__PURE__ */ new Map();
|
|
231
|
+
const gated = accounts.map((account) => {
|
|
232
|
+
if (account.schedulable === false) return account;
|
|
233
|
+
const decision = scheduling.evaluate(
|
|
234
|
+
providerId,
|
|
235
|
+
account.id,
|
|
236
|
+
_nullishCoalesce(account.priority, () => ( DEFAULT_ACCOUNT_PRIORITY)),
|
|
237
|
+
evaluatedAt
|
|
238
|
+
);
|
|
239
|
+
decisions.set(account.id, decision);
|
|
240
|
+
return {
|
|
241
|
+
...account,
|
|
242
|
+
priority: decision.effectivePriority,
|
|
243
|
+
schedulable: decision.schedulable
|
|
244
|
+
};
|
|
245
|
+
});
|
|
246
|
+
if (gated.some((account) => account.schedulable !== false)) return gated;
|
|
247
|
+
const paused = eligibleBeforePolicy.map((account) => decisions.get(account.id)).filter((decision) => _optionalChain([decision, 'optionalAccess', _3 => _3.action]) === "pause");
|
|
248
|
+
if (paused.length !== eligibleBeforePolicy.length) return gated;
|
|
249
|
+
const resumeAt = paused.map((decision) => _optionalChain([decision, 'optionalAccess', _4 => _4.resumeAt])).filter((value) => !!value).sort()[0];
|
|
250
|
+
throw new (0, _AccountAllowanceScheduling.AccountAllowanceExhaustedError)(providerId, resumeAt);
|
|
251
|
+
}
|
|
163
252
|
function readSchedulableAccounts(config, providerId) {
|
|
164
253
|
const raw = _nullishCoalesce(config[ACCOUNTS_KEY[providerId]], () => ( []));
|
|
165
254
|
const accounts = raw.map((a) => ({
|
|
166
255
|
id: a.id,
|
|
256
|
+
group: typeof a.group === "string" && a.group.trim() !== "" ? a.group.trim() : providerId,
|
|
167
257
|
priority: a.priority,
|
|
168
258
|
lastUsedAt: a.lastUsedAt,
|
|
169
|
-
createdAt: a.createdAt
|
|
259
|
+
createdAt: a.createdAt,
|
|
260
|
+
// Persisted opt-out is absolute, including a one-account pool. Legacy rows
|
|
261
|
+
// omit the field and therefore remain enabled.
|
|
262
|
+
schedulable: a.enabled !== false
|
|
170
263
|
}));
|
|
171
264
|
const supportedModelsById = new Map(
|
|
172
265
|
raw.map((a) => [a.id, a.supportedModels])
|
|
@@ -184,24 +277,64 @@ function pickByIdTarget(selector, gated, providerId, activeAccountId, sessionKey
|
|
|
184
277
|
return void 0;
|
|
185
278
|
}
|
|
186
279
|
async function resolveSelectedToken(selector, tokens, providerId, sessionKey, activeGetter, ctx) {
|
|
187
|
-
const health = _optionalChain([ctx, 'optionalAccess',
|
|
188
|
-
const report = _optionalChain([ctx, 'optionalAccess',
|
|
189
|
-
const now = _optionalChain([ctx, 'optionalAccess',
|
|
190
|
-
const resolvedModel = _optionalChain([ctx, 'optionalAccess',
|
|
280
|
+
const health = _optionalChain([ctx, 'optionalAccess', _5 => _5.health]);
|
|
281
|
+
const report = _optionalChain([ctx, 'optionalAccess', _6 => _6.reportSelection]);
|
|
282
|
+
const now = _optionalChain([ctx, 'optionalAccess', _7 => _7.now]);
|
|
283
|
+
const resolvedModel = _optionalChain([ctx, 'optionalAccess', _8 => _8.resolvedModel]);
|
|
284
|
+
const preferredId = typeof _optionalChain([ctx, 'optionalAccess', _9 => _9.preferredAccountId]) === "string" && ctx.preferredAccountId.trim() !== "" ? ctx.preferredAccountId.trim() : void 0;
|
|
285
|
+
const preferredGroup = typeof _optionalChain([ctx, 'optionalAccess', _10 => _10.preferredAccountGroup]) === "string" && ctx.preferredAccountGroup.trim() !== "" ? ctx.preferredAccountGroup.trim() : void 0;
|
|
286
|
+
if (preferredId && ctx && ctx.boundAccountFallbackPolicy !== "pool") {
|
|
287
|
+
return resolveStrictPreferredToken(tokens, providerId, preferredId, activeGetter, ctx);
|
|
288
|
+
}
|
|
191
289
|
if (selector && tokens.getAccessTokenForAccount) {
|
|
192
290
|
const config = await tokens.getFullConfig();
|
|
193
291
|
const { accounts, activeAccountId, supportedModelsById } = readSchedulableAccounts(config, providerId);
|
|
194
|
-
const
|
|
195
|
-
|
|
292
|
+
const groupAccounts = preferredGroup ? accounts.filter((account) => account.group === preferredGroup) : accounts;
|
|
293
|
+
if (preferredGroup && groupAccounts.length === 0 && _optionalChain([ctx, 'optionalAccess', _11 => _11.boundAccountFallbackPolicy]) !== "pool") {
|
|
294
|
+
throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "not-found");
|
|
295
|
+
}
|
|
296
|
+
let candidates = groupAccounts.length > 0 ? groupAccounts : accounts;
|
|
297
|
+
let healthAndModelGated = gateSchedulable(
|
|
298
|
+
candidates,
|
|
299
|
+
providerId,
|
|
300
|
+
health,
|
|
301
|
+
now,
|
|
302
|
+
resolvedModel,
|
|
303
|
+
supportedModelsById
|
|
304
|
+
);
|
|
305
|
+
let gated = gateByAllowance(healthAndModelGated, providerId, now);
|
|
306
|
+
let groupHasUsableCredential = gated.some((account) => account.schedulable !== false);
|
|
307
|
+
if (groupHasUsableCredential && preferredGroup && _optionalChain([ctx, 'optionalAccess', _12 => _12.boundAccountFallbackPolicy]) === "pool" && candidates !== accounts) {
|
|
308
|
+
groupHasUsableCredential = false;
|
|
309
|
+
for (const account of gated) {
|
|
310
|
+
if (account.schedulable === false) continue;
|
|
311
|
+
if (await tokens.getAccessTokenForAccount(providerId, account.id)) {
|
|
312
|
+
groupHasUsableCredential = true;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (preferredGroup && _optionalChain([ctx, 'optionalAccess', _13 => _13.boundAccountFallbackPolicy]) === "pool" && candidates !== accounts && !groupHasUsableCredential) {
|
|
318
|
+
candidates = accounts;
|
|
319
|
+
healthAndModelGated = gateSchedulable(
|
|
320
|
+
candidates,
|
|
321
|
+
providerId,
|
|
322
|
+
health,
|
|
323
|
+
now,
|
|
324
|
+
resolvedModel,
|
|
325
|
+
supportedModelsById
|
|
326
|
+
);
|
|
327
|
+
gated = gateByAllowance(healthAndModelGated, providerId, now);
|
|
328
|
+
}
|
|
329
|
+
const poolGated = isPoolGated(candidates, health, resolvedModel) || candidates.some((account) => account.schedulable === false) || preferredGroup !== void 0;
|
|
196
330
|
const remapFor = (id) => remapReportForAccount(supportedModelsById.get(id), resolvedModel);
|
|
197
|
-
const preferredId = _optionalChain([ctx, 'optionalAccess', _5 => _5.preferredAccountId]);
|
|
198
331
|
if (preferredId) {
|
|
199
332
|
const preferred = gated.find((a) => a.id === preferredId);
|
|
200
333
|
if (preferred && preferred.schedulable !== false) {
|
|
201
334
|
const preferredToken = await tokens.getAccessTokenForAccount(providerId, preferredId);
|
|
202
335
|
if (preferredToken) {
|
|
203
336
|
maybeTouchLastUsed(selector, tokens, providerId, preferredId);
|
|
204
|
-
_optionalChain([report, 'optionalCall',
|
|
337
|
+
_optionalChain([report, 'optionalCall', _14 => _14(preferredId, preferredId === activeAccountId, remapFor(preferredId))]);
|
|
205
338
|
return preferredToken;
|
|
206
339
|
}
|
|
207
340
|
}
|
|
@@ -211,23 +344,39 @@ async function resolveSelectedToken(selector, tokens, providerId, sessionKey, ac
|
|
|
211
344
|
const byId = await tokens.getAccessTokenForAccount(providerId, targetId);
|
|
212
345
|
if (byId) {
|
|
213
346
|
maybeTouchLastUsed(selector, tokens, providerId, targetId);
|
|
214
|
-
_optionalChain([report, 'optionalCall',
|
|
347
|
+
_optionalChain([report, 'optionalCall', _15 => _15(targetId, false, remapFor(targetId))]);
|
|
215
348
|
return byId;
|
|
216
349
|
}
|
|
217
350
|
selector.evictAffinity(providerId, targetId);
|
|
218
|
-
_optionalChain([health, 'optionalAccess',
|
|
351
|
+
_optionalChain([health, 'optionalAccess', _16 => _16.recordUpstreamOutcome, 'call', _17 => _17(providerId, targetId, { status: 401, now })]);
|
|
219
352
|
const remaining = gated.filter((a) => a.id !== targetId);
|
|
220
353
|
const retryId = pickByIdTarget(selector, remaining, providerId, activeAccountId, sessionKey, now, poolGated);
|
|
221
354
|
if (retryId !== void 0) {
|
|
222
355
|
const retryToken = await tokens.getAccessTokenForAccount(providerId, retryId);
|
|
223
356
|
if (retryToken) {
|
|
224
357
|
maybeTouchLastUsed(selector, tokens, providerId, retryId);
|
|
225
|
-
_optionalChain([report, 'optionalCall',
|
|
358
|
+
_optionalChain([report, 'optionalCall', _18 => _18(retryId, false, remapFor(retryId))]);
|
|
226
359
|
return retryToken;
|
|
227
360
|
}
|
|
228
361
|
}
|
|
229
362
|
}
|
|
230
|
-
if (activeAccountId)
|
|
363
|
+
if (activeAccountId) {
|
|
364
|
+
if (preferredGroup && _optionalChain([ctx, 'optionalAccess', _19 => _19.boundAccountFallbackPolicy]) !== "pool" && !candidates.some((account) => account.id === activeAccountId)) {
|
|
365
|
+
throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "unavailable");
|
|
366
|
+
}
|
|
367
|
+
const persistedActive = accounts.find((account) => account.id === activeAccountId);
|
|
368
|
+
if (_optionalChain([persistedActive, 'optionalAccess', _20 => _20.schedulable]) === false) return null;
|
|
369
|
+
const activeAllowance = _AccountAllowanceScheduling.getSharedAccountAllowanceScheduling.call(void 0, ).evaluate(
|
|
370
|
+
providerId,
|
|
371
|
+
activeAccountId,
|
|
372
|
+
_nullishCoalesce(_optionalChain([persistedActive, 'optionalAccess', _21 => _21.priority]), () => ( DEFAULT_ACCOUNT_PRIORITY)),
|
|
373
|
+
now
|
|
374
|
+
);
|
|
375
|
+
if (activeAllowance.action === "pause") {
|
|
376
|
+
throw new (0, _AccountAllowanceScheduling.AccountAllowanceExhaustedError)(providerId, activeAllowance.resumeAt);
|
|
377
|
+
}
|
|
378
|
+
_optionalChain([report, 'optionalCall', _22 => _22(activeAccountId, true, remapFor(activeAccountId))]);
|
|
379
|
+
}
|
|
231
380
|
return activeGetter();
|
|
232
381
|
}
|
|
233
382
|
return activeGetter();
|
|
@@ -276,9 +425,16 @@ var OAuthBearerAuthStrategy = (_class2 = class {
|
|
|
276
425
|
this.selector,
|
|
277
426
|
this.tokens,
|
|
278
427
|
this.providerId,
|
|
279
|
-
_optionalChain([hints, 'optionalAccess',
|
|
428
|
+
_optionalChain([hints, 'optionalAccess', _23 => _23.sessionKey]),
|
|
280
429
|
() => this.resolveAccessToken(),
|
|
281
|
-
{
|
|
430
|
+
{
|
|
431
|
+
health: this.health,
|
|
432
|
+
reportSelection: _optionalChain([hints, 'optionalAccess', _24 => _24.reportSelection]),
|
|
433
|
+
resolvedModel: _optionalChain([hints, 'optionalAccess', _25 => _25.resolvedModel]),
|
|
434
|
+
preferredAccountId: _optionalChain([hints, 'optionalAccess', _26 => _26.preferredAccountId]),
|
|
435
|
+
preferredAccountGroup: _optionalChain([hints, 'optionalAccess', _27 => _27.preferredAccountGroup]),
|
|
436
|
+
boundAccountFallbackPolicy: _optionalChain([hints, 'optionalAccess', _28 => _28.boundAccountFallbackPolicy])
|
|
437
|
+
}
|
|
282
438
|
);
|
|
283
439
|
if (!token) {
|
|
284
440
|
return;
|
|
@@ -300,7 +456,7 @@ var OAuthBearerAuthStrategy = (_class2 = class {
|
|
|
300
456
|
async describeStatus() {
|
|
301
457
|
const config = await this.tokens.getFullConfig();
|
|
302
458
|
const entry = this.providerId === "codex" ? config.codex : config.gemini;
|
|
303
|
-
if (!_optionalChain([entry, 'optionalAccess',
|
|
459
|
+
if (!_optionalChain([entry, 'optionalAccess', _29 => _29.accessToken])) {
|
|
304
460
|
return { providerId: this.providerId, ok: false, reason: "missing-credential" };
|
|
305
461
|
}
|
|
306
462
|
if (entry.status === "expired") {
|
|
@@ -317,7 +473,7 @@ var OAuthBearerAuthStrategy = (_class2 = class {
|
|
|
317
473
|
async resolveAccessToken() {
|
|
318
474
|
const config = await this.tokens.getFullConfig();
|
|
319
475
|
const entry = this.providerId === "codex" ? config.codex : config.gemini;
|
|
320
|
-
if (!_optionalChain([entry, 'optionalAccess',
|
|
476
|
+
if (!_optionalChain([entry, 'optionalAccess', _30 => _30.accessToken])) return null;
|
|
321
477
|
const expiresAtMs = entry.expiresAt ? new Date(entry.expiresAt).getTime() : 0;
|
|
322
478
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - REFRESH_LEAD_MS;
|
|
323
479
|
if (expiringSoon && entry.refreshToken) {
|
|
@@ -327,7 +483,7 @@ var OAuthBearerAuthStrategy = (_class2 = class {
|
|
|
327
483
|
if (!refreshed) return null;
|
|
328
484
|
const fresh = await this.tokens.getFullConfig();
|
|
329
485
|
const freshEntry = this.providerId === "codex" ? fresh.codex : fresh.gemini;
|
|
330
|
-
return _nullishCoalesce(_optionalChain([freshEntry, 'optionalAccess',
|
|
486
|
+
return _nullishCoalesce(_optionalChain([freshEntry, 'optionalAccess', _31 => _31.accessToken]), () => ( null));
|
|
331
487
|
}
|
|
332
488
|
if (entry.status === "expired") return null;
|
|
333
489
|
return entry.accessToken;
|
|
@@ -353,9 +509,16 @@ var PassThroughAuthStrategy = (_class3 = class {
|
|
|
353
509
|
this.selector,
|
|
354
510
|
this.tokens,
|
|
355
511
|
"claude",
|
|
356
|
-
_optionalChain([hints, 'optionalAccess',
|
|
512
|
+
_optionalChain([hints, 'optionalAccess', _32 => _32.sessionKey]),
|
|
357
513
|
() => this.tokens.getValidClaudeAccessToken(),
|
|
358
|
-
{
|
|
514
|
+
{
|
|
515
|
+
health: this.health,
|
|
516
|
+
reportSelection: _optionalChain([hints, 'optionalAccess', _33 => _33.reportSelection]),
|
|
517
|
+
resolvedModel: _optionalChain([hints, 'optionalAccess', _34 => _34.resolvedModel]),
|
|
518
|
+
preferredAccountId: _optionalChain([hints, 'optionalAccess', _35 => _35.preferredAccountId]),
|
|
519
|
+
preferredAccountGroup: _optionalChain([hints, 'optionalAccess', _36 => _36.preferredAccountGroup]),
|
|
520
|
+
boundAccountFallbackPolicy: _optionalChain([hints, 'optionalAccess', _37 => _37.boundAccountFallbackPolicy])
|
|
521
|
+
}
|
|
359
522
|
);
|
|
360
523
|
if (!token) return;
|
|
361
524
|
headers["Authorization"] = `Bearer ${token}`;
|
|
@@ -375,7 +538,7 @@ var PassThroughAuthStrategy = (_class3 = class {
|
|
|
375
538
|
async describeStatus() {
|
|
376
539
|
const config = await this.tokens.getFullConfig();
|
|
377
540
|
const claude = config.claude;
|
|
378
|
-
if (!_optionalChain([claude, 'optionalAccess',
|
|
541
|
+
if (!_optionalChain([claude, 'optionalAccess', _38 => _38.accessToken])) {
|
|
379
542
|
return { providerId: "claude", ok: false, reason: "missing-credential" };
|
|
380
543
|
}
|
|
381
544
|
if (claude.status === "expired") {
|
|
@@ -431,15 +594,22 @@ var StaticBearerAuthStrategy = (_class5 = class {
|
|
|
431
594
|
this.selector,
|
|
432
595
|
this.tokens,
|
|
433
596
|
"opencodego",
|
|
434
|
-
_optionalChain([hints, 'optionalAccess',
|
|
597
|
+
_optionalChain([hints, 'optionalAccess', _39 => _39.sessionKey]),
|
|
435
598
|
() => this.tokens.getValidOpenCodeGoApiKey(),
|
|
436
|
-
{
|
|
599
|
+
{
|
|
600
|
+
health: this.health,
|
|
601
|
+
reportSelection: _optionalChain([hints, 'optionalAccess', _40 => _40.reportSelection]),
|
|
602
|
+
resolvedModel: _optionalChain([hints, 'optionalAccess', _41 => _41.resolvedModel]),
|
|
603
|
+
preferredAccountId: _optionalChain([hints, 'optionalAccess', _42 => _42.preferredAccountId]),
|
|
604
|
+
preferredAccountGroup: _optionalChain([hints, 'optionalAccess', _43 => _43.preferredAccountGroup]),
|
|
605
|
+
boundAccountFallbackPolicy: _optionalChain([hints, 'optionalAccess', _44 => _44.boundAccountFallbackPolicy])
|
|
606
|
+
}
|
|
437
607
|
);
|
|
438
608
|
if (!key) {
|
|
439
609
|
return;
|
|
440
610
|
}
|
|
441
611
|
headers["Authorization"] = `Bearer ${key}`;
|
|
442
|
-
if (_optionalChain([hints, 'optionalAccess',
|
|
612
|
+
if (_optionalChain([hints, 'optionalAccess', _45 => _45.upstreamUrl, 'optionalAccess', _46 => _46.includes, 'call', _47 => _47(ANTHROPIC_SHAPE_PATH)])) {
|
|
443
613
|
headers["x-api-key"] = key;
|
|
444
614
|
}
|
|
445
615
|
}
|
|
@@ -449,7 +619,7 @@ var StaticBearerAuthStrategy = (_class5 = class {
|
|
|
449
619
|
async describeStatus() {
|
|
450
620
|
const config = await this.tokens.getFullConfig();
|
|
451
621
|
const oc = config.opencodego;
|
|
452
|
-
if (!_optionalChain([oc, 'optionalAccess',
|
|
622
|
+
if (!_optionalChain([oc, 'optionalAccess', _48 => _48.apiKey])) {
|
|
453
623
|
return { providerId: "opencodego", ok: false, reason: "missing-credential" };
|
|
454
624
|
}
|
|
455
625
|
if (oc.status === "error") {
|
|
@@ -523,31 +693,29 @@ var _GeminiCodeAssistTransformer = require('@omnicross/core/transformer/transfor
|
|
|
523
693
|
|
|
524
694
|
// src/opencodego/CircuitBreaker.ts
|
|
525
695
|
var CircuitBreaker = (_class7 = class {
|
|
526
|
-
__init12() {this.
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
/** `now()` at the last recorded failure — drives the open→half-open elapsed check. */
|
|
534
|
-
__init16() {this.lastFailureTime = 0}
|
|
535
|
-
|
|
536
|
-
|
|
696
|
+
__init12() {this.snapshot = {
|
|
697
|
+
mode: "closed",
|
|
698
|
+
consecutiveFailures: 0,
|
|
699
|
+
openedAt: 0,
|
|
700
|
+
probeAdmissions: 0,
|
|
701
|
+
probeSuccesses: 0
|
|
702
|
+
}}
|
|
537
703
|
|
|
538
704
|
|
|
539
|
-
constructor(opts = {}) {;_class7.prototype.__init12.call(this);
|
|
540
|
-
this.
|
|
541
|
-
|
|
542
|
-
|
|
705
|
+
constructor(opts = {}) {;_class7.prototype.__init12.call(this);
|
|
706
|
+
this.limits = {
|
|
707
|
+
threshold: _nullishCoalesce(opts.threshold, () => ( 3)),
|
|
708
|
+
openMs: _nullishCoalesce(opts.openMs, () => ( 3e4)),
|
|
709
|
+
halfOpenMaxCalls: _nullishCoalesce(opts.halfOpenMaxCalls, () => ( 3))
|
|
710
|
+
};
|
|
543
711
|
this.now = _nullishCoalesce(opts.now, () => ( Date.now));
|
|
544
712
|
}
|
|
545
713
|
/** Current state (diagnostics / tests). */
|
|
546
714
|
getState() {
|
|
547
|
-
return this.
|
|
715
|
+
return this.snapshot.mode;
|
|
548
716
|
}
|
|
549
717
|
/**
|
|
550
|
-
* Admission gate
|
|
718
|
+
* Admission gate. Returns whether a
|
|
551
719
|
* request to this model is allowed RIGHT NOW. Side-effecting BY DESIGN:
|
|
552
720
|
* - `closed` → always admit.
|
|
553
721
|
* - `open` → if `now() - lastFailureTime > openMs`, FLIP to `half-open`,
|
|
@@ -558,74 +726,91 @@ var CircuitBreaker = (_class7 = class {
|
|
|
558
726
|
* recorded outcome resolves the state).
|
|
559
727
|
*/
|
|
560
728
|
allowRequest() {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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;
|
|
729
|
+
if (this.snapshot.mode === "closed") return true;
|
|
730
|
+
if (this.snapshot.mode === "open") {
|
|
731
|
+
const elapsed = this.now() - this.snapshot.openedAt;
|
|
732
|
+
if (elapsed <= this.limits.openMs) return false;
|
|
733
|
+
this.snapshot = {
|
|
734
|
+
...this.snapshot,
|
|
735
|
+
mode: "half-open",
|
|
736
|
+
probeAdmissions: 1,
|
|
737
|
+
probeSuccesses: 0
|
|
738
|
+
};
|
|
739
|
+
return true;
|
|
580
740
|
}
|
|
741
|
+
if (this.snapshot.probeAdmissions >= this.limits.halfOpenMaxCalls) return false;
|
|
742
|
+
this.snapshot = {
|
|
743
|
+
...this.snapshot,
|
|
744
|
+
probeAdmissions: this.snapshot.probeAdmissions + 1
|
|
745
|
+
};
|
|
746
|
+
return true;
|
|
581
747
|
}
|
|
582
748
|
/**
|
|
583
|
-
* Record a successful attempt
|
|
749
|
+
* Record a successful attempt.
|
|
584
750
|
* - `half-open` → increment `successCount`; at `halfOpenMaxCalls` successes,
|
|
585
751
|
* CLOSE the circuit and reset all counters.
|
|
586
752
|
* - `closed` → reset the consecutive `failureCount` (a single good call
|
|
587
753
|
* clears the streak).
|
|
588
754
|
*/
|
|
589
755
|
recordSuccess() {
|
|
590
|
-
if (this.
|
|
591
|
-
|
|
592
|
-
if (this.
|
|
593
|
-
this.
|
|
594
|
-
this.failureCount = 0;
|
|
595
|
-
this.successCount = 0;
|
|
596
|
-
this.halfOpenCalls = 0;
|
|
756
|
+
if (this.snapshot.mode === "open") return;
|
|
757
|
+
if (this.snapshot.mode === "closed") {
|
|
758
|
+
if (this.snapshot.consecutiveFailures !== 0) {
|
|
759
|
+
this.snapshot = { ...this.snapshot, consecutiveFailures: 0 };
|
|
597
760
|
}
|
|
598
761
|
return;
|
|
599
762
|
}
|
|
600
|
-
this.
|
|
763
|
+
const probeSuccesses = this.snapshot.probeSuccesses + 1;
|
|
764
|
+
if (probeSuccesses >= this.limits.halfOpenMaxCalls) {
|
|
765
|
+
this.snapshot = {
|
|
766
|
+
mode: "closed",
|
|
767
|
+
consecutiveFailures: 0,
|
|
768
|
+
openedAt: 0,
|
|
769
|
+
probeAdmissions: 0,
|
|
770
|
+
probeSuccesses: 0
|
|
771
|
+
};
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
this.snapshot = { ...this.snapshot, probeSuccesses };
|
|
601
775
|
}
|
|
602
776
|
/**
|
|
603
|
-
* Record a failed attempt
|
|
777
|
+
* Record a failed attempt.
|
|
604
778
|
* - `half-open` → immediately RE-OPEN (one probe failure is enough); stamp
|
|
605
779
|
* `lastFailureTime`, reset `successCount`.
|
|
606
780
|
* - `closed` → increment the consecutive `failureCount`; at `threshold`,
|
|
607
781
|
* OPEN the circuit. Always stamp `lastFailureTime`.
|
|
608
782
|
*/
|
|
609
783
|
recordFailure() {
|
|
610
|
-
|
|
611
|
-
if (this.
|
|
612
|
-
this.
|
|
613
|
-
|
|
614
|
-
|
|
784
|
+
const openedAt = this.now();
|
|
785
|
+
if (this.snapshot.mode === "half-open") {
|
|
786
|
+
this.snapshot = {
|
|
787
|
+
...this.snapshot,
|
|
788
|
+
mode: "open",
|
|
789
|
+
openedAt,
|
|
790
|
+
probeAdmissions: 0,
|
|
791
|
+
probeSuccesses: 0
|
|
792
|
+
};
|
|
615
793
|
return;
|
|
616
794
|
}
|
|
617
|
-
this.
|
|
618
|
-
|
|
619
|
-
this.
|
|
795
|
+
const consecutiveFailures = this.snapshot.consecutiveFailures + 1;
|
|
796
|
+
this.snapshot = {
|
|
797
|
+
...this.snapshot,
|
|
798
|
+
consecutiveFailures,
|
|
799
|
+
openedAt,
|
|
800
|
+
mode: consecutiveFailures >= this.limits.threshold ? "open" : this.snapshot.mode
|
|
801
|
+
};
|
|
802
|
+
if (this.snapshot.mode === "open") {
|
|
803
|
+
this.snapshot.probeAdmissions = 0;
|
|
804
|
+
this.snapshot.probeSuccesses = 0;
|
|
620
805
|
}
|
|
621
806
|
}
|
|
622
807
|
}, _class7);
|
|
623
808
|
var CircuitBreakerRegistry = (_class8 = class {
|
|
624
|
-
constructor(options = {}) {;_class8.prototype.
|
|
809
|
+
constructor(options = {}) {;_class8.prototype.__init13.call(this);
|
|
625
810
|
this.options = options;
|
|
626
811
|
}
|
|
627
812
|
|
|
628
|
-
|
|
813
|
+
__init13() {this.breakers = /* @__PURE__ */ new Map()}
|
|
629
814
|
/** Get (or lazily create) the breaker for a model id. */
|
|
630
815
|
get(modelId) {
|
|
631
816
|
let breaker = this.breakers.get(modelId);
|
|
@@ -665,8 +850,7 @@ var DEFAULT_OPENCODEGO_MODEL_MAP = {
|
|
|
665
850
|
modelId: "glm-5"
|
|
666
851
|
},
|
|
667
852
|
complex: {
|
|
668
|
-
//
|
|
669
|
-
// drifted to `mimo-v2-pro` (audit D4).
|
|
853
|
+
// Complex tasks use the higher-capability GLM variant by default.
|
|
670
854
|
modelId: "glm-5.1"
|
|
671
855
|
},
|
|
672
856
|
fast: {
|
|
@@ -793,11 +977,11 @@ function resolveOpenCodeGoShape(entry) {
|
|
|
793
977
|
function resolveOpenCodeGoHalf(modelId, config) {
|
|
794
978
|
if (!config) return "go";
|
|
795
979
|
for (const entry of Object.values(_nullishCoalesce(config.modelMap, () => ( {})))) {
|
|
796
|
-
if (_optionalChain([entry, 'optionalAccess',
|
|
980
|
+
if (_optionalChain([entry, 'optionalAccess', _49 => _49.modelId]) === modelId) return _nullishCoalesce(entry.provider, () => ( "go"));
|
|
797
981
|
}
|
|
798
982
|
for (const list of Object.values(_nullishCoalesce(config.fallbacks, () => ( {})))) {
|
|
799
983
|
for (const entry of _nullishCoalesce(list, () => ( []))) {
|
|
800
|
-
if (_optionalChain([entry, 'optionalAccess',
|
|
984
|
+
if (_optionalChain([entry, 'optionalAccess', _50 => _50.modelId]) === modelId) return _nullishCoalesce(entry.provider, () => ( "go"));
|
|
801
985
|
}
|
|
802
986
|
}
|
|
803
987
|
return "go";
|
|
@@ -805,67 +989,44 @@ function resolveOpenCodeGoHalf(modelId, config) {
|
|
|
805
989
|
|
|
806
990
|
// src/opencodego/ScenarioRouter.ts
|
|
807
991
|
var COMPLEX_KEYWORDS = [
|
|
808
|
-
// Architectural
|
|
809
|
-
"architect",
|
|
810
992
|
"architecture",
|
|
811
993
|
"refactor",
|
|
812
994
|
"redesign",
|
|
813
|
-
"complex",
|
|
814
|
-
"difficult",
|
|
815
|
-
"challenging",
|
|
816
995
|
"optimize",
|
|
817
996
|
"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
997
|
"implement",
|
|
827
998
|
"build",
|
|
828
|
-
"create",
|
|
829
|
-
"add feature",
|
|
830
|
-
"write to",
|
|
831
999
|
"edit file",
|
|
832
|
-
"
|
|
1000
|
+
"debug",
|
|
1001
|
+
"migrate",
|
|
1002
|
+
"benchmark"
|
|
833
1003
|
];
|
|
834
1004
|
var THINKING_KEYWORDS = [
|
|
835
1005
|
"think",
|
|
836
|
-
"thinking",
|
|
837
1006
|
"plan",
|
|
838
1007
|
"reason",
|
|
839
|
-
"reasoning",
|
|
840
1008
|
"analyze",
|
|
841
|
-
"
|
|
842
|
-
"
|
|
1009
|
+
"step by step",
|
|
1010
|
+
"evaluate",
|
|
1011
|
+
"compare tradeoffs"
|
|
843
1012
|
];
|
|
844
1013
|
var ANT_THINKING_MARKER = "antThinking";
|
|
845
1014
|
var TOOL_BLOCKERS = [
|
|
846
1015
|
"tool",
|
|
847
1016
|
"function",
|
|
848
|
-
"
|
|
849
|
-
"run command",
|
|
1017
|
+
"command",
|
|
850
1018
|
"write",
|
|
851
1019
|
"edit",
|
|
852
|
-
"create",
|
|
853
1020
|
"delete",
|
|
854
|
-
"remove",
|
|
855
1021
|
"implement",
|
|
856
1022
|
"build",
|
|
857
|
-
"add",
|
|
858
1023
|
"modify"
|
|
859
1024
|
];
|
|
860
1025
|
var BACKGROUND_KEYWORDS = [
|
|
861
1026
|
"list directory",
|
|
862
|
-
"ls -",
|
|
863
|
-
"dir",
|
|
864
1027
|
"show file",
|
|
865
|
-
"
|
|
866
|
-
"cat file",
|
|
1028
|
+
"read file",
|
|
867
1029
|
"what is",
|
|
868
|
-
"what's",
|
|
869
1030
|
"tell me about",
|
|
870
1031
|
"check status",
|
|
871
1032
|
"show status"
|
|
@@ -897,7 +1058,7 @@ function hasBackgroundPattern(loweredSlices) {
|
|
|
897
1058
|
return containsAny(loweredSlices, BACKGROUND_KEYWORDS);
|
|
898
1059
|
}
|
|
899
1060
|
function resolveOpenCodeGoScenario(summary, config) {
|
|
900
|
-
const longContextThreshold = _nullishCoalesce(_optionalChain([config, 'optionalAccess',
|
|
1061
|
+
const longContextThreshold = _nullishCoalesce(_optionalChain([config, 'optionalAccess', _51 => _51.modelMap, 'optionalAccess', _52 => _52.long_context, 'optionalAccess', _53 => _53.contextThreshold]), () => ( DEFAULT_OPENCODEGO_LONG_CONTEXT_THRESHOLD));
|
|
901
1062
|
if (summary.estimatedInputTokens >= longContextThreshold) {
|
|
902
1063
|
return "long_context";
|
|
903
1064
|
}
|
|
@@ -921,7 +1082,7 @@ function opencodegoTransformerNamesForShape(shape) {
|
|
|
921
1082
|
return ["gemini"];
|
|
922
1083
|
case "chat":
|
|
923
1084
|
default:
|
|
924
|
-
return ["
|
|
1085
|
+
return ["openai"];
|
|
925
1086
|
}
|
|
926
1087
|
}
|
|
927
1088
|
function resolveOpenCodeGoTarget(modelId, config) {
|
|
@@ -930,7 +1091,7 @@ function resolveOpenCodeGoTarget(modelId, config) {
|
|
|
930
1091
|
return { half, shape };
|
|
931
1092
|
}
|
|
932
1093
|
var SubscriptionProviderRegistry = (_class9 = class {
|
|
933
|
-
constructor(accounts, tokens) {;_class9.prototype.
|
|
1094
|
+
constructor(accounts, tokens) {;_class9.prototype.__init14.call(this);
|
|
934
1095
|
this.accounts = accounts;
|
|
935
1096
|
this.tokens = tokens;
|
|
936
1097
|
const claude = this.accounts.getStrategy("claude");
|
|
@@ -976,7 +1137,7 @@ var SubscriptionProviderRegistry = (_class9 = class {
|
|
|
976
1137
|
authStrategy: codex,
|
|
977
1138
|
mode: "transformer",
|
|
978
1139
|
// ChatGPT internal endpoint — accepts the OpenAI Responses API
|
|
979
|
-
// format
|
|
1140
|
+
// format and uses the Codex OAuth access token below.
|
|
980
1141
|
// The Codex OAuth access token grants access here; the public
|
|
981
1142
|
// `api.openai.com/v1/responses` endpoint would reject the same token.
|
|
982
1143
|
resolveUpstreamUrl: () => "https://chatgpt.com/backend-api/codex/responses",
|
|
@@ -1022,16 +1183,16 @@ var SubscriptionProviderRegistry = (_class9 = class {
|
|
|
1022
1183
|
// `ocConfig`; the core `/v1/messages` plan builder passes the opaque
|
|
1023
1184
|
// `route.subscriptionConfig`). With NO zen config every resolved model
|
|
1024
1185
|
// is go-half → byte-identical to the prior resolver.
|
|
1025
|
-
// `// UNVERIFIED (no live zen key)`: the
|
|
1026
|
-
//
|
|
1186
|
+
// `// UNVERIFIED (no live zen key)`: the ZEN endpoint hosts and paths
|
|
1187
|
+
// are covered by in-process tests only.
|
|
1027
1188
|
resolveUpstreamUrl: (model, config) => {
|
|
1028
1189
|
const oc = config;
|
|
1029
1190
|
const { half, shape } = resolveOpenCodeGoTarget(model, oc);
|
|
1030
|
-
const override = half === "zen" ? _optionalChain([oc, 'optionalAccess',
|
|
1191
|
+
const override = half === "zen" ? _optionalChain([oc, 'optionalAccess', _54 => _54.zenBaseUrl]) : _optionalChain([oc, 'optionalAccess', _55 => _55.baseUrl]);
|
|
1031
1192
|
return buildOpenCodeGoUrl(half, shape, override);
|
|
1032
1193
|
},
|
|
1033
1194
|
// zen seam (Decision 3): vary the provider transformer chain by resolved
|
|
1034
|
-
// shape (anthropic⇒[] verbatim, chat⇒
|
|
1195
|
+
// shape (anthropic⇒[] verbatim, chat⇒openai, responses⇒openai-response,
|
|
1035
1196
|
// gemini⇒gemini). OPTIONAL on the profile type — only opencodego sets it;
|
|
1036
1197
|
// claude/codex/gemini omit it and fall back to `providerTransformerNames`,
|
|
1037
1198
|
// keeping their routing byte-identical. The static `providerTransformerNames`
|
|
@@ -1041,11 +1202,11 @@ var SubscriptionProviderRegistry = (_class9 = class {
|
|
|
1041
1202
|
const { shape } = resolveOpenCodeGoTarget(model, config);
|
|
1042
1203
|
return opencodegoTransformerNamesForShape(shape);
|
|
1043
1204
|
},
|
|
1044
|
-
providerTransformerNames: ["
|
|
1205
|
+
providerTransformerNames: ["openai"],
|
|
1045
1206
|
modelTransformerNames: [],
|
|
1046
1207
|
modelMapper: (sdkModel, summary, config) => {
|
|
1047
1208
|
const scenario = resolveOpenCodeGoScenario(summary, config);
|
|
1048
|
-
const entry = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess',
|
|
1209
|
+
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
1210
|
if (!entry) {
|
|
1050
1211
|
return { resolvedModel: sdkModel, scenario };
|
|
1051
1212
|
}
|
|
@@ -1055,16 +1216,15 @@ var SubscriptionProviderRegistry = (_class9 = class {
|
|
|
1055
1216
|
// circuit is open. `breaker.allowRequest(modelId)` is the admission
|
|
1056
1217
|
// gate — calling it has the side effect of flipping an `open` model to
|
|
1057
1218
|
// `half-open` once its 30s window elapses AND counting a half-open admit
|
|
1058
|
-
// slot. It MUST therefore be consulted
|
|
1059
|
-
// on the candidate about to be attempted
|
|
1060
|
-
// (`fallback.go` calls `AllowRequest` once, on the model it returns).
|
|
1219
|
+
// slot. It MUST therefore be consulted exactly once per returned model,
|
|
1220
|
+
// on the candidate about to be attempted.
|
|
1061
1221
|
// An early-returning scan (NOT `Array.filter`, which would `allowRequest`
|
|
1062
1222
|
// every candidate and burn the admit slots of half-open models AFTER the
|
|
1063
1223
|
// chosen one — those are never attempted, never recorded, so they would
|
|
1064
1224
|
// wedge permanently in half-open). When NO circuit is open this returns
|
|
1065
1225
|
// the same first non-attempted entry as the prior `!attempted` filter.
|
|
1066
1226
|
nextFallback: (scenario, attempted, config) => {
|
|
1067
|
-
const list = _nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess',
|
|
1227
|
+
const list = _nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess', _60 => _60.fallbacks, 'optionalAccess', _61 => _61[scenario]]), () => ( DEFAULT_OPENCODEGO_FALLBACKS[scenario])), () => ( []));
|
|
1068
1228
|
for (const entry of list) {
|
|
1069
1229
|
if (attempted.includes(entry.modelId)) continue;
|
|
1070
1230
|
if (this.breaker.allowRequest(entry.modelId)) return entry;
|
|
@@ -1091,11 +1251,11 @@ var SubscriptionProviderRegistry = (_class9 = class {
|
|
|
1091
1251
|
* process singleton, built here and captured by the opencodego profile's
|
|
1092
1252
|
* `nextFallback` (consult) + `recordModelOutcome` (record) closures. Because
|
|
1093
1253
|
* the `SubscriptionProviderRegistry` is itself a process singleton (via
|
|
1094
|
-
* `setSubscriptionProviderRegistry`), breaker state persists across requests
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
1254
|
+
* `setSubscriptionProviderRegistry`), breaker state persists across requests.
|
|
1255
|
+
* Constructed with the production thresholds (3 / 30s / 3) and the default
|
|
1256
|
+
* `Date.now` clock.
|
|
1097
1257
|
*/
|
|
1098
|
-
|
|
1258
|
+
__init14() {this.breaker = new CircuitBreakerRegistry()}
|
|
1099
1259
|
/** Returns the dispatch profile for a known subscription provider, or
|
|
1100
1260
|
* `null` for unknown ids (callers must treat null as "fall back to the
|
|
1101
1261
|
* legacy LLM provider DB lookup"). */
|
|
@@ -1121,6 +1281,8 @@ function getSubscriptionProviderRegistry() {
|
|
|
1121
1281
|
}
|
|
1122
1282
|
|
|
1123
1283
|
// src/SubscriptionDispatcher.ts
|
|
1284
|
+
|
|
1285
|
+
|
|
1124
1286
|
var _geminicodeassistresolver = require('@omnicross/core/ports/gemini-code-assist-resolver');
|
|
1125
1287
|
|
|
1126
1288
|
|
|
@@ -1165,7 +1327,7 @@ var SubscriptionDispatcher = class {
|
|
|
1165
1327
|
scenario = mapped.scenario;
|
|
1166
1328
|
req.anthropicBody.model = resolvedModel;
|
|
1167
1329
|
}
|
|
1168
|
-
const upstreamUrl = _optionalChain([this, 'access',
|
|
1330
|
+
const upstreamUrl = _optionalChain([this, 'access', _62 => _62.profile, 'access', _63 => _63.resolveUpstreamUrl, 'optionalCall', _64 => _64(resolvedModel, ocConfig)]);
|
|
1169
1331
|
if (!upstreamUrl) {
|
|
1170
1332
|
throw new Error(`[SubscriptionDispatcher] profile=${this.profile.providerId} missing resolveUpstreamUrl`);
|
|
1171
1333
|
}
|
|
@@ -1197,7 +1359,7 @@ var SubscriptionDispatcher = class {
|
|
|
1197
1359
|
);
|
|
1198
1360
|
try {
|
|
1199
1361
|
const upstream = await this.hooks.fetchWithRetry(upstreamUrl, headers, req.anthropicBody, currentModel);
|
|
1200
|
-
_optionalChain([this, 'access',
|
|
1362
|
+
_optionalChain([this, 'access', _65 => _65.profile, 'access', _66 => _66.recordModelOutcome, 'optionalCall', _67 => _67(currentModel, true)]);
|
|
1201
1363
|
this.markHealth(usedAccountId, 200);
|
|
1202
1364
|
await this.hooks.writeProxyResponse(req.res, upstream, req.isStream, req.reqId);
|
|
1203
1365
|
return;
|
|
@@ -1206,7 +1368,7 @@ var SubscriptionDispatcher = class {
|
|
|
1206
1368
|
if (handled.retryOnce) {
|
|
1207
1369
|
try {
|
|
1208
1370
|
const upstream = await this.hooks.fetchWithRetry(upstreamUrl, handled.headers, req.anthropicBody, currentModel);
|
|
1209
|
-
_optionalChain([this, 'access',
|
|
1371
|
+
_optionalChain([this, 'access', _68 => _68.profile, 'access', _69 => _69.recordModelOutcome, 'optionalCall', _70 => _70(currentModel, true)]);
|
|
1210
1372
|
this.markHealth(usedAccountId, 200);
|
|
1211
1373
|
await this.hooks.writeProxyResponse(req.res, upstream, req.isStream, req.reqId);
|
|
1212
1374
|
return;
|
|
@@ -1216,10 +1378,10 @@ var SubscriptionDispatcher = class {
|
|
|
1216
1378
|
}
|
|
1217
1379
|
}
|
|
1218
1380
|
if (caughtErrorBreakerOutcome(err) === "failure") {
|
|
1219
|
-
_optionalChain([this, 'access',
|
|
1381
|
+
_optionalChain([this, 'access', _71 => _71.profile, 'access', _72 => _72.recordModelOutcome, 'optionalCall', _73 => _73(currentModel, false)]);
|
|
1220
1382
|
}
|
|
1221
1383
|
this.markHealth(usedAccountId, errStatus(err), err);
|
|
1222
|
-
const next = _optionalChain([this, 'access',
|
|
1384
|
+
const next = _optionalChain([this, 'access', _74 => _74.profile, 'access', _75 => _75.nextFallback, 'optionalCall', _76 => _76(scenario, attempted, ocConfig)]);
|
|
1223
1385
|
if (!next || attempted.length >= MAX_FALLBACK_ATTEMPTS_LOCAL) {
|
|
1224
1386
|
throw err;
|
|
1225
1387
|
}
|
|
@@ -1233,7 +1395,7 @@ var SubscriptionDispatcher = class {
|
|
|
1233
1395
|
}
|
|
1234
1396
|
/** Standard subscription transformer chain — Codex/Gemini/OpenCodeGo OpenAI-shape. */
|
|
1235
1397
|
async dispatchTransformerChain(req, upstreamUrl, resolvedModel, scenario, ocConfig, sessionKey) {
|
|
1236
|
-
const providerNames = _nullishCoalesce(_optionalChain([this, 'access',
|
|
1398
|
+
const providerNames = _nullishCoalesce(_optionalChain([this, 'access', _77 => _77.profile, 'access', _78 => _78.resolveProviderTransformerNames, 'optionalCall', _79 => _79(resolvedModel, ocConfig)]), () => ( this.profile.providerTransformerNames));
|
|
1237
1399
|
const providerTransformers = this.resolveTransformers(providerNames);
|
|
1238
1400
|
const modelTransformers = this.resolveTransformers(this.profile.modelTransformerNames);
|
|
1239
1401
|
const transformerProvider = {
|
|
@@ -1274,7 +1436,7 @@ var SubscriptionDispatcher = class {
|
|
|
1274
1436
|
);
|
|
1275
1437
|
try {
|
|
1276
1438
|
const upstream = await this.hooks.fetchWithRetry(fetchUrl, headers, requestBody, currentModel);
|
|
1277
|
-
_optionalChain([this, 'access',
|
|
1439
|
+
_optionalChain([this, 'access', _80 => _80.profile, 'access', _81 => _81.recordModelOutcome, 'optionalCall', _82 => _82(currentModel, true)]);
|
|
1278
1440
|
this.markHealth(usedAccountId, 200);
|
|
1279
1441
|
const finalResponse = await this.hooks.executor.executeResponseChain(
|
|
1280
1442
|
requestBody,
|
|
@@ -1290,7 +1452,7 @@ var SubscriptionDispatcher = class {
|
|
|
1290
1452
|
if (handled.retryOnce) {
|
|
1291
1453
|
try {
|
|
1292
1454
|
const upstream = await this.hooks.fetchWithRetry(fetchUrl, handled.headers, requestBody, currentModel);
|
|
1293
|
-
_optionalChain([this, 'access',
|
|
1455
|
+
_optionalChain([this, 'access', _83 => _83.profile, 'access', _84 => _84.recordModelOutcome, 'optionalCall', _85 => _85(currentModel, true)]);
|
|
1294
1456
|
this.markHealth(usedAccountId, 200);
|
|
1295
1457
|
const finalResponse = await this.hooks.executor.executeResponseChain(
|
|
1296
1458
|
requestBody,
|
|
@@ -1307,10 +1469,10 @@ var SubscriptionDispatcher = class {
|
|
|
1307
1469
|
}
|
|
1308
1470
|
}
|
|
1309
1471
|
if (caughtErrorBreakerOutcome(err) === "failure") {
|
|
1310
|
-
_optionalChain([this, 'access',
|
|
1472
|
+
_optionalChain([this, 'access', _86 => _86.profile, 'access', _87 => _87.recordModelOutcome, 'optionalCall', _88 => _88(currentModel, false)]);
|
|
1311
1473
|
}
|
|
1312
1474
|
this.markHealth(usedAccountId, errStatus(err), err);
|
|
1313
|
-
const next = _optionalChain([this, 'access',
|
|
1475
|
+
const next = _optionalChain([this, 'access', _89 => _89.profile, 'access', _90 => _90.nextFallback, 'optionalCall', _91 => _91(scenario, attempted, ocConfig)]);
|
|
1314
1476
|
if (!next || attempted.length >= MAX_FALLBACK_ATTEMPTS_LOCAL) {
|
|
1315
1477
|
throw err;
|
|
1316
1478
|
}
|
|
@@ -1340,7 +1502,7 @@ var SubscriptionDispatcher = class {
|
|
|
1340
1502
|
return { firstModel: primaryModel, attempted: [] };
|
|
1341
1503
|
}
|
|
1342
1504
|
const skipped = [primaryModel];
|
|
1343
|
-
const firstAdmitting = _optionalChain([this, 'access',
|
|
1505
|
+
const firstAdmitting = _optionalChain([this, 'access', _92 => _92.profile, 'access', _93 => _93.nextFallback, 'optionalCall', _94 => _94(scenario, skipped, ocConfig)]);
|
|
1344
1506
|
if (firstAdmitting) {
|
|
1345
1507
|
console.warn(
|
|
1346
1508
|
`[AgentProxy:subscription] opencodego primary ${primaryModel} circuit open -> first admitting fallback ${firstAdmitting.modelId}`
|
|
@@ -1358,7 +1520,7 @@ var SubscriptionDispatcher = class {
|
|
|
1358
1520
|
* successfully (caller should retry once); otherwise re-throws.
|
|
1359
1521
|
*/
|
|
1360
1522
|
async maybeRetryAfterError(err, headers, req, resolvedModel, sessionKey) {
|
|
1361
|
-
const status = _nullishCoalesce(_optionalChain([err, 'optionalAccess',
|
|
1523
|
+
const status = _nullishCoalesce(_optionalChain([err, 'optionalAccess', _95 => _95.status]), () => ( 0));
|
|
1362
1524
|
if (status !== 401) {
|
|
1363
1525
|
return { retryOnce: false, headers };
|
|
1364
1526
|
}
|
|
@@ -1378,6 +1540,7 @@ var SubscriptionDispatcher = class {
|
|
|
1378
1540
|
try {
|
|
1379
1541
|
await this.profile.authStrategy.applyHeaders(headers, hints);
|
|
1380
1542
|
} catch (err) {
|
|
1543
|
+
if (_AccountAllowanceScheduling.isAccountAllowanceExhaustedError.call(void 0, err) || _BoundAccountSelectionError.isBoundAccountSelectionError.call(void 0, err)) throw err;
|
|
1381
1544
|
console.warn("[AgentProxy:subscription] authStrategy.applyHeaders threw:", _serializeError.serializeError.call(void 0, err));
|
|
1382
1545
|
}
|
|
1383
1546
|
}
|
|
@@ -1480,11 +1643,11 @@ var SubscriptionDispatcher = class {
|
|
|
1480
1643
|
};
|
|
1481
1644
|
var MAX_FALLBACK_ATTEMPTS_LOCAL = 3;
|
|
1482
1645
|
function errStatus(err) {
|
|
1483
|
-
const status = _optionalChain([err, 'optionalAccess',
|
|
1646
|
+
const status = _optionalChain([err, 'optionalAccess', _96 => _96.status]);
|
|
1484
1647
|
return typeof status === "number" ? status : null;
|
|
1485
1648
|
}
|
|
1486
1649
|
function errHeaders(err) {
|
|
1487
|
-
const h = _optionalChain([err, 'optionalAccess',
|
|
1650
|
+
const h = _optionalChain([err, 'optionalAccess', _97 => _97.headers]);
|
|
1488
1651
|
if (!h) return void 0;
|
|
1489
1652
|
if (typeof h.get === "function") return h;
|
|
1490
1653
|
if (typeof h === "object") return h;
|
|
@@ -1492,11 +1655,11 @@ function errHeaders(err) {
|
|
|
1492
1655
|
}
|
|
1493
1656
|
function errBodyText(err) {
|
|
1494
1657
|
const e = err;
|
|
1495
|
-
const raw = typeof _optionalChain([e, 'optionalAccess',
|
|
1496
|
-
return _optionalChain([raw, 'optionalAccess',
|
|
1658
|
+
const raw = typeof _optionalChain([e, 'optionalAccess', _98 => _98.bodyText]) === "string" ? e.bodyText : typeof _optionalChain([e, 'optionalAccess', _99 => _99.body]) === "string" ? e.body : void 0;
|
|
1659
|
+
return _optionalChain([raw, 'optionalAccess', _100 => _100.slice, 'call', _101 => _101(0, 2048)]);
|
|
1497
1660
|
}
|
|
1498
1661
|
function caughtErrorBreakerOutcome(err) {
|
|
1499
|
-
const status = _optionalChain([err, 'optionalAccess',
|
|
1662
|
+
const status = _optionalChain([err, 'optionalAccess', _102 => _102.status]);
|
|
1500
1663
|
if (typeof status !== "number") return "failure";
|
|
1501
1664
|
if (status === 0) return "neutral";
|
|
1502
1665
|
if (status >= 500 || status === 429) return "failure";
|