@omnicross/core 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ApiConverter.cjs +2 -2
- package/dist/ApiConverter.js +1 -1
- package/dist/{CompletionService-DdWaAxq0.d.ts → CompletionService-C3BX0Ovy.d.ts} +4 -3
- package/dist/{CompletionService-D3a47vdU.d.cts → CompletionService-kNA17cfI.d.cts} +4 -3
- package/dist/auth/GeminiCodeAssistProjectResolver.cjs +8 -6
- package/dist/auth/GeminiCodeAssistProjectResolver.js +6 -4
- package/dist/{chunk-3Z5BQZFM.cjs → chunk-235MXAGX.cjs} +3 -1
- package/dist/{chunk-HRRYG2AX.js → chunk-23S5OPIO.js} +10 -1
- package/dist/chunk-25GXJCEZ.cjs +40 -0
- package/dist/chunk-2E42SAW3.cjs +323 -0
- package/dist/chunk-2JYRIADS.js +40 -0
- package/dist/chunk-2Z4XM3H5.cjs +44 -0
- package/dist/{chunk-BODMAL6G.js → chunk-32O35CIF.js} +271 -91
- package/dist/chunk-3SCOLIPT.cjs +63 -0
- package/dist/{chunk-SYQF7OIR.js → chunk-3WD4HTR2.js} +2 -0
- package/dist/{chunk-CGKLE6IW.cjs → chunk-4BB343BI.cjs} +1489 -520
- package/dist/{chunk-VACWOIRE.cjs → chunk-4VZX5F4T.cjs} +39 -15
- package/dist/{chunk-5ZQBEOMD.js → chunk-5NJLQ6T6.js} +46 -12
- package/dist/chunk-5RSZYQJH.cjs +62 -0
- package/dist/chunk-6QQG7YG3.cjs +153 -0
- package/dist/{chunk-IFXJ2BG7.js → chunk-7C7DET6S.js} +51 -6
- package/dist/{chunk-IK3KE3AU.js → chunk-DE2SRBFZ.js} +1 -1
- package/dist/{chunk-FH2P4OCD.js → chunk-E3COOKUP.js} +1240 -271
- package/dist/{chunk-UHGVBFU3.js → chunk-EBLFCNPY.js} +38 -14
- package/dist/chunk-FU7EAR73.js +62 -0
- package/dist/chunk-FXR6N3SR.js +323 -0
- package/dist/{chunk-SUEGJ75D.cjs → chunk-GDIXXKF2.cjs} +46 -12
- package/dist/chunk-GFIZ6LQI.js +170 -0
- package/dist/chunk-GL6XM527.js +61 -0
- package/dist/chunk-HVBBDBS3.cjs +350 -0
- package/dist/{chunk-46ZIFWYG.cjs → chunk-IIRPUVPH.cjs} +7 -1
- package/dist/chunk-J3QQ6JS5.cjs +170 -0
- package/dist/{chunk-JWVTXXPX.cjs → chunk-KBM2W6UY.cjs} +8 -4
- package/dist/chunk-LB6EBS6Y.js +69 -0
- package/dist/{chunk-IWQAX3CS.js → chunk-LVUCAWMK.js} +1 -1
- package/dist/chunk-MKF46GNZ.js +350 -0
- package/dist/{chunk-HYN75H6G.cjs → chunk-MUHWHQVM.cjs} +1 -1
- package/dist/chunk-MYYR3HZL.js +44 -0
- package/dist/{chunk-MFZ5437A.js → chunk-N5T4M4GU.js} +1 -1
- package/dist/{chunk-5ENKBSWO.js → chunk-N7OOJI5V.js} +88 -8
- package/dist/chunk-NKVZ6Z3Y.cjs +61 -0
- package/dist/chunk-OF3BECU2.cjs +69 -0
- package/dist/{chunk-SNYBEXLB.cjs → chunk-OJGQKHN5.cjs} +3 -1
- package/dist/{chunk-DDMRUL76.cjs → chunk-OKMJLEXX.cjs} +5 -5
- package/dist/chunk-OPRS4IQZ.js +153 -0
- package/dist/chunk-OTZC4TNH.cjs +79 -0
- package/dist/{chunk-3Q6Z6JTO.cjs → chunk-OVQBNVSV.cjs} +287 -107
- package/dist/{chunk-4XMYPW46.cjs → chunk-OXHNSZ53.cjs} +84 -44
- package/dist/{chunk-C6OV7GCC.js → chunk-PEGTIPB3.js} +52 -12
- package/dist/chunk-PO3XB56Z.js +79 -0
- package/dist/{chunk-D3TDKWRP.js → chunk-RJ5ZGNI3.js} +54 -5
- package/dist/{chunk-PG7PS2NN.cjs → chunk-RJUONLUQ.cjs} +59 -10
- package/dist/{chunk-BPKCU575.js → chunk-SOO562NM.js} +3 -1
- package/dist/{chunk-6QZGDLL4.js → chunk-TOVPMMHM.js} +5 -1
- package/dist/{chunk-K52THW2C.cjs → chunk-V4TXMEJS.cjs} +5 -5
- package/dist/{chunk-G2UTNSGU.js → chunk-WNKWAEUR.js} +7 -1
- package/dist/{chunk-UNWKG6EI.cjs → chunk-XPIOJQS2.cjs} +51 -6
- package/dist/chunk-XXFYLL7R.js +63 -0
- package/dist/{chunk-KFI44N2R.cjs → chunk-YNJNTCSQ.cjs} +10 -1
- package/dist/{chunk-VQY5W4YW.cjs → chunk-YWL7YZWS.cjs} +90 -10
- package/dist/completion/ApiKeyPoolService.cjs +12 -0
- package/dist/completion/ApiKeyPoolService.d.cts +6 -0
- package/dist/completion/ApiKeyPoolService.d.ts +6 -0
- package/dist/completion/ApiKeyPoolService.js +12 -0
- package/dist/completion/CompletionService.cjs +30 -24
- package/dist/completion/CompletionService.d.cts +3 -1
- package/dist/completion/CompletionService.d.ts +3 -1
- package/dist/completion/CompletionService.js +29 -23
- package/dist/completion.cjs +32 -26
- package/dist/completion.d.cts +3 -1
- package/dist/completion.d.ts +3 -1
- package/dist/completion.js +31 -25
- package/dist/index.cjs +56 -29
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +61 -34
- package/dist/{keyPolicy-Cp0FntZl.d.cts → keyPolicy-zdWTu3CQ.d.cts} +1 -1
- package/dist/{keyPolicy-Cp0FntZl.d.ts → keyPolicy-zdWTu3CQ.d.ts} +1 -1
- package/dist/outbound-api/auditCapture.cjs +2 -2
- package/dist/outbound-api/auditCapture.d.cts +3 -4
- package/dist/outbound-api/auditCapture.d.ts +3 -4
- package/dist/outbound-api/auditCapture.js +1 -1
- package/dist/outbound-api/quotaWarn.d.cts +1 -1
- package/dist/outbound-api/quotaWarn.d.ts +1 -1
- package/dist/outbound-api/routeResolver.cjs +2 -2
- package/dist/outbound-api/routeResolver.d.cts +5 -3
- package/dist/outbound-api/routeResolver.d.ts +5 -3
- package/dist/outbound-api/routeResolver.js +1 -1
- package/dist/outbound-api/subscriptionRegistryPort.d.cts +2 -0
- package/dist/outbound-api/subscriptionRegistryPort.d.ts +2 -0
- package/dist/outbound-api/types.d.cts +4 -2
- package/dist/outbound-api/types.d.ts +4 -2
- package/dist/outbound-api.cjs +52 -25
- package/dist/outbound-api.d.cts +131 -87
- package/dist/outbound-api.d.ts +131 -87
- package/dist/outbound-api.js +55 -28
- package/dist/pipeline/AccountAllowanceScheduling.cjs +15 -0
- package/dist/pipeline/AccountAllowanceScheduling.d.cts +91 -0
- package/dist/pipeline/AccountAllowanceScheduling.d.ts +91 -0
- package/dist/pipeline/AccountAllowanceScheduling.js +15 -0
- package/dist/pipeline/AccountAllowanceStore.cjs +20 -0
- package/dist/pipeline/AccountAllowanceStore.d.cts +77 -0
- package/dist/pipeline/AccountAllowanceStore.d.ts +77 -0
- package/dist/pipeline/AccountAllowanceStore.js +20 -0
- package/dist/pipeline/AccountRouteActivity.cjs +10 -0
- package/dist/pipeline/AccountRouteActivity.d.cts +61 -0
- package/dist/pipeline/AccountRouteActivity.d.ts +61 -0
- package/dist/pipeline/AccountRouteActivity.js +10 -0
- package/dist/pipeline/AuthSource.d.cts +11 -2
- package/dist/pipeline/AuthSource.d.ts +11 -2
- package/dist/pipeline/BoundAccountSelectionError.cjs +10 -0
- package/dist/pipeline/BoundAccountSelectionError.d.cts +28 -0
- package/dist/pipeline/BoundAccountSelectionError.d.ts +28 -0
- package/dist/pipeline/BoundAccountSelectionError.js +10 -0
- package/dist/pipeline/LlmConfigProviderAuth.d.cts +1 -0
- package/dist/pipeline/LlmConfigProviderAuth.d.ts +1 -0
- package/dist/pipeline/ServerOverloadCounter.cjs +16 -0
- package/dist/pipeline/ServerOverloadCounter.d.cts +77 -0
- package/dist/pipeline/ServerOverloadCounter.d.ts +77 -0
- package/dist/pipeline/ServerOverloadCounter.js +16 -0
- package/dist/pipeline/SubscriptionAccountHealth.cjs +6 -2
- package/dist/pipeline/SubscriptionAccountHealth.d.cts +65 -8
- package/dist/pipeline/SubscriptionAccountHealth.d.ts +65 -8
- package/dist/pipeline/SubscriptionAccountHealth.js +5 -1
- package/dist/pipeline/SubscriptionAuthSource.cjs +5 -2
- package/dist/pipeline/SubscriptionAuthSource.d.cts +1 -0
- package/dist/pipeline/SubscriptionAuthSource.d.ts +1 -0
- package/dist/pipeline/SubscriptionAuthSource.js +4 -1
- package/dist/pipeline/SubscriptionAuthStrategy.d.cts +8 -3
- package/dist/pipeline/SubscriptionAuthStrategy.d.ts +8 -3
- package/dist/pipeline/executeProviderCall.cjs +2 -2
- package/dist/pipeline/executeProviderCall.d.cts +8 -3
- package/dist/pipeline/executeProviderCall.d.ts +8 -3
- package/dist/pipeline/executeProviderCall.js +1 -1
- package/dist/pipeline/resolveProviderChain.d.cts +2 -3
- package/dist/pipeline/resolveProviderChain.d.ts +2 -3
- package/dist/pipeline/resolveSubscriptionChain.d.cts +1 -0
- package/dist/pipeline/resolveSubscriptionChain.d.ts +1 -0
- package/dist/pipeline/upstreamFetch.cjs +5 -3
- package/dist/pipeline/upstreamFetch.d.cts +35 -6
- package/dist/pipeline/upstreamFetch.d.ts +35 -6
- package/dist/pipeline/upstreamFetch.js +4 -2
- package/dist/pipeline/upstreamTrace.cjs +4 -2
- package/dist/pipeline/upstreamTrace.d.cts +22 -8
- package/dist/pipeline/upstreamTrace.d.ts +22 -8
- package/dist/pipeline/upstreamTrace.js +3 -1
- package/dist/ports/pricing-store.d.cts +4 -3
- package/dist/ports/pricing-store.d.ts +4 -3
- package/dist/ports.d.cts +4 -3
- package/dist/ports.d.ts +4 -3
- package/dist/provider-proxy/ProviderProxy.cjs +30 -24
- package/dist/provider-proxy/ProviderProxy.d.cts +2 -0
- package/dist/provider-proxy/ProviderProxy.d.ts +2 -0
- package/dist/provider-proxy/ProviderProxy.js +29 -23
- package/dist/provider-proxy/identity/codexCliHeaders.cjs +10 -0
- package/dist/provider-proxy/identity/codexCliHeaders.d.cts +45 -0
- package/dist/provider-proxy/identity/codexCliHeaders.d.ts +45 -0
- package/dist/provider-proxy/identity/codexCliHeaders.js +10 -0
- package/dist/provider-proxy/ingress/providerProxyShared.cjs +32 -24
- package/dist/provider-proxy/ingress/providerProxyShared.d.cts +44 -5
- package/dist/provider-proxy/ingress/providerProxyShared.d.ts +44 -5
- package/dist/provider-proxy/ingress/providerProxyShared.js +31 -23
- package/dist/provider-proxy/matchText.cjs +4 -2
- package/dist/provider-proxy/matchText.d.cts +39 -6
- package/dist/provider-proxy/matchText.d.ts +39 -6
- package/dist/provider-proxy/matchText.js +3 -1
- package/dist/provider-proxy/types.d.cts +35 -2
- package/dist/provider-proxy/types.d.ts +35 -2
- package/dist/provider-proxy.cjs +30 -24
- package/dist/provider-proxy.d.cts +2 -0
- package/dist/provider-proxy.d.ts +2 -0
- package/dist/provider-proxy.js +29 -23
- package/dist/{routeResolver-C4T7yClx.d.ts → routeResolver-BxOfunbm.d.ts} +1 -1
- package/dist/{routeResolver-CB3tvJKy.d.cts → routeResolver-D5OzD0iM.d.cts} +1 -1
- package/dist/transformer/TransformerChainExecutor.cjs +2 -2
- package/dist/transformer/TransformerChainExecutor.js +1 -1
- package/dist/transformer/transformers/AnthropicTransformer.cjs +3 -2
- package/dist/transformer/transformers/AnthropicTransformer.js +2 -1
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +3 -3
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +2 -2
- package/dist/transformer/transformers/GeminiTransformer.cjs +3 -3
- package/dist/transformer/transformers/GeminiTransformer.js +2 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +3 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +1 -1
- package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +1 -1
- package/dist/transformer/transformers/OpenAIResponseTransformer.js +2 -1
- package/dist/transformer/transformers/OpenAITransformer.cjs +6 -0
- package/dist/transformer/transformers/OpenAITransformer.d.cts +68 -0
- package/dist/transformer/transformers/OpenAITransformer.d.ts +68 -0
- package/dist/transformer/transformers/OpenAITransformer.js +6 -0
- package/dist/transformer/transformers.cjs +9 -14
- package/dist/transformer/transformers.d.cts +12 -48
- package/dist/transformer/transformers.d.ts +12 -48
- package/dist/transformer/transformers.js +11 -16
- package/dist/transformer/types.d.cts +17 -0
- package/dist/transformer/types.d.ts +17 -0
- package/dist/transformer.cjs +12 -12
- package/dist/transformer.d.cts +1 -2
- package/dist/transformer.d.ts +1 -2
- package/dist/transformer.js +12 -12
- package/dist/{types-lgPYQuZS.d.ts → types-CqZD77rS.d.ts} +173 -7
- package/dist/{types-B8Arpxn0.d.cts → types-gjJ5lJ5n.d.cts} +173 -7
- package/dist/usage/pricing-engine.cjs +2 -2
- package/dist/usage/pricing-engine.d.cts +29 -1
- package/dist/usage/pricing-engine.d.ts +29 -1
- package/dist/usage/pricing-engine.js +1 -1
- package/dist/usage/usage-recorder.cjs +2 -2
- package/dist/usage/usage-recorder.d.cts +5 -1
- package/dist/usage/usage-recorder.d.ts +5 -1
- package/dist/usage/usage-recorder.js +1 -1
- package/dist/usage.cjs +3 -3
- package/dist/usage.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-7FEBOPJO.cjs +0 -50
- package/dist/chunk-DWKQZ2SK.cjs +0 -254
- package/dist/chunk-IAXWICZU.js +0 -69
- package/dist/chunk-IB3FDI4O.js +0 -50
- package/dist/chunk-OQWKZCXP.js +0 -242
- package/dist/chunk-QWBWGODS.cjs +0 -69
- package/dist/chunk-YMFAJFQT.cjs +0 -173
- package/dist/chunk-ZCIQ3L3R.cjs +0 -242
- package/dist/chunk-ZJZAOKI7.js +0 -254
- package/dist/chunk-ZLX3KGXX.js +0 -173
- package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +0 -6
- package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +0 -51
- package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +0 -51
- package/dist/transformer/transformers/OpenCodeGoTransformer.js +0 -6
- package/dist/transformer/transformers/ReasoningTransformer.cjs +0 -6
- package/dist/transformer/transformers/ReasoningTransformer.d.cts +0 -47
- package/dist/transformer/transformers/ReasoningTransformer.d.ts +0 -47
- package/dist/transformer/transformers/ReasoningTransformer.js +0 -6
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret-free failure contract for a strict outbound subscription-account
|
|
3
|
+
* binding. The selector throws this value only when an endpoint explicitly (or
|
|
4
|
+
* by the migrated default) requires its bound account to serve the request.
|
|
5
|
+
*/
|
|
6
|
+
/** Persisted policy for an endpoint that binds a subscription account. */
|
|
7
|
+
type BoundAccountFallbackPolicy = 'strict' | 'pool';
|
|
8
|
+
/** Bounded reasons a strict bound account can refuse a request. */
|
|
9
|
+
type BoundAccountSelectionFailureReason = 'unavailable' | 'not-found' | 'disabled' | 'unhealthy' | 'model-incompatible' | 'allowance-paused' | 'empty-token';
|
|
10
|
+
/** Return the fixed public message for a validated failure reason. */
|
|
11
|
+
declare function boundAccountSelectionMessage(reason: BoundAccountSelectionFailureReason): string;
|
|
12
|
+
/**
|
|
13
|
+
* Stable, non-secret error thrown by strict account selection. Account ids and
|
|
14
|
+
* token material are intentionally absent from both the message and the wire
|
|
15
|
+
* projection; the reason is a small fixed vocabulary.
|
|
16
|
+
*/
|
|
17
|
+
declare class BoundAccountSelectionError extends Error {
|
|
18
|
+
readonly providerId: string;
|
|
19
|
+
readonly reason: BoundAccountSelectionFailureReason;
|
|
20
|
+
readonly resumeAt?: string | undefined;
|
|
21
|
+
readonly code: "bound_account_unavailable";
|
|
22
|
+
readonly status: 429 | 503;
|
|
23
|
+
constructor(providerId: string, reason: BoundAccountSelectionFailureReason, resumeAt?: string | undefined);
|
|
24
|
+
}
|
|
25
|
+
/** Structural guard that survives package/bundle boundaries. */
|
|
26
|
+
declare function isBoundAccountSelectionError(error: unknown): error is BoundAccountSelectionError;
|
|
27
|
+
|
|
28
|
+
export { type BoundAccountFallbackPolicy, BoundAccountSelectionError, type BoundAccountSelectionFailureReason, boundAccountSelectionMessage, isBoundAccountSelectionError };
|
|
@@ -2,6 +2,7 @@ import { LLMProvider } from '@omnicross/contracts/llm-config';
|
|
|
2
2
|
import { ApiKeyPoolService } from '../completion/ApiKeyPoolService.cjs';
|
|
3
3
|
import { AuthSource, AuthApplyHints, AuthResultOutcome } from './AuthSource.cjs';
|
|
4
4
|
import '../logger-4GvQNzhE.cjs';
|
|
5
|
+
import './BoundAccountSelectionError.cjs';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* LlmConfigProviderAuth — `AuthSource` for LLM-config provider rows.
|
|
@@ -2,6 +2,7 @@ import { LLMProvider } from '@omnicross/contracts/llm-config';
|
|
|
2
2
|
import { ApiKeyPoolService } from '../completion/ApiKeyPoolService.js';
|
|
3
3
|
import { AuthSource, AuthApplyHints, AuthResultOutcome } from './AuthSource.js';
|
|
4
4
|
import '../logger-4GvQNzhE.js';
|
|
5
|
+
import './BoundAccountSelectionError.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* LlmConfigProviderAuth — `AuthSource` for LLM-config provider rows.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
var _chunkOTZC4TNHcjs = require('../chunk-OTZC4TNH.cjs');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
exports.OVERLOAD_ENTRY_TTL_MS = _chunkOTZC4TNHcjs.OVERLOAD_ENTRY_TTL_MS; exports.OVERLOAD_RECENT_LIMIT = _chunkOTZC4TNHcjs.OVERLOAD_RECENT_LIMIT; exports.ServerOverloadCounterStore = _chunkOTZC4TNHcjs.ServerOverloadCounterStore; exports.__resetSharedOverloadCounterForTests = _chunkOTZC4TNHcjs.__resetSharedOverloadCounterForTests; exports.getSharedOverloadCounter = _chunkOTZC4TNHcjs.getSharedOverloadCounter; exports.setSharedOverloadCounter = _chunkOTZC4TNHcjs.setSharedOverloadCounter;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { AccountRouteEndpoint } from './AccountRouteActivity.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ServerOverloadCounter — a bounded, metadata-only tally of Codex server-
|
|
5
|
+
* overload events (`response.failed` with `error.code` `server_is_overloaded` /
|
|
6
|
+
* `slow_down`) per provider+account+endpoint.
|
|
7
|
+
*
|
|
8
|
+
* WHY a dedicated store (not derived from {@link AccountRouteActivity}): those
|
|
9
|
+
* 200-status overloads are invisible at the route-activity HTTP-status layer, and
|
|
10
|
+
* the route-activity ring (300 rows) can be pushed out by normal traffic before
|
|
11
|
+
* an operator notices. This counter ACCUMULATES across the ring and keeps a
|
|
12
|
+
* bounded recent-timestamp ring for a trend sparkline, so "how often is account X
|
|
13
|
+
* being overloaded, and is it worsening" stays answerable.
|
|
14
|
+
*
|
|
15
|
+
* Overload is account-INDEPENDENT server capacity (retrying on another account is
|
|
16
|
+
* pointless), so — unlike {@link SubscriptionAccountHealth} — this store only
|
|
17
|
+
* OBSERVES; it never gates scheduling. It carries no secrets, only counts + epoch
|
|
18
|
+
* timestamps. In-memory (cleared on restart), same as the other pipeline stores.
|
|
19
|
+
*
|
|
20
|
+
* Shaped like `SubscriptionAccountHealth`: an in-memory `Map`, an injectable
|
|
21
|
+
* `now` clock, no external deps, opaque `providerId\0accountId\0endpoint` keys.
|
|
22
|
+
*
|
|
23
|
+
* @module pipeline/ServerOverloadCounter
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Bounded recent-event ring per entry — the sparkline's data source. */
|
|
27
|
+
declare const OVERLOAD_RECENT_LIMIT = 64;
|
|
28
|
+
/**
|
|
29
|
+
* Entries with no event in this window are pruned on read, so the map cannot
|
|
30
|
+
* grow unbounded across a long-lived process with many transient accounts.
|
|
31
|
+
*/
|
|
32
|
+
declare const OVERLOAD_ENTRY_TTL_MS: number;
|
|
33
|
+
interface OverloadRecordInput {
|
|
34
|
+
providerId: string;
|
|
35
|
+
accountId: string;
|
|
36
|
+
endpoint: AccountRouteEndpoint;
|
|
37
|
+
/** Epoch-ms of the event. Omit to use the clock default. */
|
|
38
|
+
now?: number;
|
|
39
|
+
}
|
|
40
|
+
interface OverloadCounterEntry {
|
|
41
|
+
providerId: string;
|
|
42
|
+
accountId: string;
|
|
43
|
+
endpoint: AccountRouteEndpoint;
|
|
44
|
+
/** Lifetime count of overload events for this key (since first observed). */
|
|
45
|
+
count: number;
|
|
46
|
+
firstTs: number;
|
|
47
|
+
lastTs: number;
|
|
48
|
+
/** Most-recent-first epoch-ms events, capped at {@link OVERLOAD_RECENT_LIMIT}. */
|
|
49
|
+
recent: number[];
|
|
50
|
+
}
|
|
51
|
+
interface OverloadCounterQuery {
|
|
52
|
+
providerId?: string;
|
|
53
|
+
accountId?: string;
|
|
54
|
+
}
|
|
55
|
+
declare class ServerOverloadCounterStore {
|
|
56
|
+
private readonly entries;
|
|
57
|
+
/**
|
|
58
|
+
* Record one overload event. Creates the entry on first sight, then bumps
|
|
59
|
+
* `count`, `lastTs`, and prepends the event to the bounded `recent` ring.
|
|
60
|
+
*/
|
|
61
|
+
recordOverload(input: OverloadRecordInput, now?: number): OverloadCounterEntry;
|
|
62
|
+
/**
|
|
63
|
+
* Snapshot all (optionally filtered) entries, pruning stale ones first. Returns
|
|
64
|
+
* copies — callers (admin API → JSON) never see the live mutable entries.
|
|
65
|
+
*/
|
|
66
|
+
list(query?: OverloadCounterQuery, now?: number): OverloadCounterEntry[];
|
|
67
|
+
clear(): void;
|
|
68
|
+
}
|
|
69
|
+
declare function getSharedOverloadCounter(): ServerOverloadCounterStore;
|
|
70
|
+
/**
|
|
71
|
+
* TEST SEAM — swap the singleton (e.g. with a fresh instance). Mirrors the
|
|
72
|
+
* `setShared…`/`__reset…ForTests` discipline of the sibling pipeline stores.
|
|
73
|
+
*/
|
|
74
|
+
declare function setSharedOverloadCounter(instance: ServerOverloadCounterStore): void;
|
|
75
|
+
declare function __resetSharedOverloadCounterForTests(): void;
|
|
76
|
+
|
|
77
|
+
export { OVERLOAD_ENTRY_TTL_MS, OVERLOAD_RECENT_LIMIT, type OverloadCounterEntry, type OverloadCounterQuery, type OverloadRecordInput, ServerOverloadCounterStore, __resetSharedOverloadCounterForTests, getSharedOverloadCounter, setSharedOverloadCounter };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { AccountRouteEndpoint } from './AccountRouteActivity.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ServerOverloadCounter — a bounded, metadata-only tally of Codex server-
|
|
5
|
+
* overload events (`response.failed` with `error.code` `server_is_overloaded` /
|
|
6
|
+
* `slow_down`) per provider+account+endpoint.
|
|
7
|
+
*
|
|
8
|
+
* WHY a dedicated store (not derived from {@link AccountRouteActivity}): those
|
|
9
|
+
* 200-status overloads are invisible at the route-activity HTTP-status layer, and
|
|
10
|
+
* the route-activity ring (300 rows) can be pushed out by normal traffic before
|
|
11
|
+
* an operator notices. This counter ACCUMULATES across the ring and keeps a
|
|
12
|
+
* bounded recent-timestamp ring for a trend sparkline, so "how often is account X
|
|
13
|
+
* being overloaded, and is it worsening" stays answerable.
|
|
14
|
+
*
|
|
15
|
+
* Overload is account-INDEPENDENT server capacity (retrying on another account is
|
|
16
|
+
* pointless), so — unlike {@link SubscriptionAccountHealth} — this store only
|
|
17
|
+
* OBSERVES; it never gates scheduling. It carries no secrets, only counts + epoch
|
|
18
|
+
* timestamps. In-memory (cleared on restart), same as the other pipeline stores.
|
|
19
|
+
*
|
|
20
|
+
* Shaped like `SubscriptionAccountHealth`: an in-memory `Map`, an injectable
|
|
21
|
+
* `now` clock, no external deps, opaque `providerId\0accountId\0endpoint` keys.
|
|
22
|
+
*
|
|
23
|
+
* @module pipeline/ServerOverloadCounter
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Bounded recent-event ring per entry — the sparkline's data source. */
|
|
27
|
+
declare const OVERLOAD_RECENT_LIMIT = 64;
|
|
28
|
+
/**
|
|
29
|
+
* Entries with no event in this window are pruned on read, so the map cannot
|
|
30
|
+
* grow unbounded across a long-lived process with many transient accounts.
|
|
31
|
+
*/
|
|
32
|
+
declare const OVERLOAD_ENTRY_TTL_MS: number;
|
|
33
|
+
interface OverloadRecordInput {
|
|
34
|
+
providerId: string;
|
|
35
|
+
accountId: string;
|
|
36
|
+
endpoint: AccountRouteEndpoint;
|
|
37
|
+
/** Epoch-ms of the event. Omit to use the clock default. */
|
|
38
|
+
now?: number;
|
|
39
|
+
}
|
|
40
|
+
interface OverloadCounterEntry {
|
|
41
|
+
providerId: string;
|
|
42
|
+
accountId: string;
|
|
43
|
+
endpoint: AccountRouteEndpoint;
|
|
44
|
+
/** Lifetime count of overload events for this key (since first observed). */
|
|
45
|
+
count: number;
|
|
46
|
+
firstTs: number;
|
|
47
|
+
lastTs: number;
|
|
48
|
+
/** Most-recent-first epoch-ms events, capped at {@link OVERLOAD_RECENT_LIMIT}. */
|
|
49
|
+
recent: number[];
|
|
50
|
+
}
|
|
51
|
+
interface OverloadCounterQuery {
|
|
52
|
+
providerId?: string;
|
|
53
|
+
accountId?: string;
|
|
54
|
+
}
|
|
55
|
+
declare class ServerOverloadCounterStore {
|
|
56
|
+
private readonly entries;
|
|
57
|
+
/**
|
|
58
|
+
* Record one overload event. Creates the entry on first sight, then bumps
|
|
59
|
+
* `count`, `lastTs`, and prepends the event to the bounded `recent` ring.
|
|
60
|
+
*/
|
|
61
|
+
recordOverload(input: OverloadRecordInput, now?: number): OverloadCounterEntry;
|
|
62
|
+
/**
|
|
63
|
+
* Snapshot all (optionally filtered) entries, pruning stale ones first. Returns
|
|
64
|
+
* copies — callers (admin API → JSON) never see the live mutable entries.
|
|
65
|
+
*/
|
|
66
|
+
list(query?: OverloadCounterQuery, now?: number): OverloadCounterEntry[];
|
|
67
|
+
clear(): void;
|
|
68
|
+
}
|
|
69
|
+
declare function getSharedOverloadCounter(): ServerOverloadCounterStore;
|
|
70
|
+
/**
|
|
71
|
+
* TEST SEAM — swap the singleton (e.g. with a fresh instance). Mirrors the
|
|
72
|
+
* `setShared…`/`__reset…ForTests` discipline of the sibling pipeline stores.
|
|
73
|
+
*/
|
|
74
|
+
declare function setSharedOverloadCounter(instance: ServerOverloadCounterStore): void;
|
|
75
|
+
declare function __resetSharedOverloadCounterForTests(): void;
|
|
76
|
+
|
|
77
|
+
export { OVERLOAD_ENTRY_TTL_MS, OVERLOAD_RECENT_LIMIT, type OverloadCounterEntry, type OverloadCounterQuery, type OverloadRecordInput, ServerOverloadCounterStore, __resetSharedOverloadCounterForTests, getSharedOverloadCounter, setSharedOverloadCounter };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OVERLOAD_ENTRY_TTL_MS,
|
|
3
|
+
OVERLOAD_RECENT_LIMIT,
|
|
4
|
+
ServerOverloadCounterStore,
|
|
5
|
+
__resetSharedOverloadCounterForTests,
|
|
6
|
+
getSharedOverloadCounter,
|
|
7
|
+
setSharedOverloadCounter
|
|
8
|
+
} from "../chunk-PO3XB56Z.js";
|
|
9
|
+
export {
|
|
10
|
+
OVERLOAD_ENTRY_TTL_MS,
|
|
11
|
+
OVERLOAD_RECENT_LIMIT,
|
|
12
|
+
ServerOverloadCounterStore,
|
|
13
|
+
__resetSharedOverloadCounterForTests,
|
|
14
|
+
getSharedOverloadCounter,
|
|
15
|
+
setSharedOverloadCounter
|
|
16
|
+
};
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
var _chunkVQY5W4YWcjs = require('../chunk-VQY5W4YW.cjs');
|
|
12
11
|
|
|
13
12
|
|
|
13
|
+
var _chunkYWL7YZWScjs = require('../chunk-YWL7YZWS.cjs');
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
@@ -19,4 +19,8 @@ var _chunkVQY5W4YWcjs = require('../chunk-VQY5W4YW.cjs');
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
exports.ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT = _chunkYWL7YZWScjs.ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT; exports.AUTH_ERROR_TTL_MS = _chunkYWL7YZWScjs.AUTH_ERROR_TTL_MS; exports.MAX_QUOTA_COOLDOWN_MS = _chunkYWL7YZWScjs.MAX_QUOTA_COOLDOWN_MS; exports.OVERLOAD_ENABLED_DEFAULT = _chunkYWL7YZWScjs.OVERLOAD_ENABLED_DEFAULT; exports.OVERLOAD_TTL_MS = _chunkYWL7YZWScjs.OVERLOAD_TTL_MS; exports.SERVER_ERROR_TTL_MS = _chunkYWL7YZWScjs.SERVER_ERROR_TTL_MS; exports.SubscriptionAccountHealth = _chunkYWL7YZWScjs.SubscriptionAccountHealth; exports.__resetSharedAccountHealthForTests = _chunkYWL7YZWScjs.__resetSharedAccountHealthForTests; exports.getSharedAccountHealth = _chunkYWL7YZWScjs.getSharedAccountHealth; exports.resolveResetSeconds = _chunkYWL7YZWScjs.resolveResetSeconds; exports.setSharedAccountHealth = _chunkYWL7YZWScjs.setSharedAccountHealth;
|
|
@@ -27,10 +27,16 @@
|
|
|
27
27
|
declare const OVERLOAD_TTL_MS: number;
|
|
28
28
|
/** 529 overload cooldown enabled by default (LEAD OQ1). */
|
|
29
29
|
declare const OVERLOAD_ENABLED_DEFAULT = true;
|
|
30
|
-
/** Transient cooldown for a final
|
|
30
|
+
/** Transient cooldown for a final 401 or non-ban 403 response. */
|
|
31
31
|
declare const AUTH_ERROR_TTL_MS: number;
|
|
32
|
-
/** Transient cooldown for a 5xx
|
|
32
|
+
/** Transient cooldown for a 5xx response or network failure. */
|
|
33
33
|
declare const SERVER_ERROR_TTL_MS: number;
|
|
34
|
+
/**
|
|
35
|
+
* Hard cap on a quota-exhaustion cooldown (the weekly usage window is ~1 week).
|
|
36
|
+
* A parsed/supplied deadline farther than this into the future is clamped, so a
|
|
37
|
+
* bogus parse can never strand an account indefinitely.
|
|
38
|
+
*/
|
|
39
|
+
declare const MAX_QUOTA_COOLDOWN_MS: number;
|
|
34
40
|
/** Per-account health record (all timers are epoch ms). Absent fields ⇒ healthy. */
|
|
35
41
|
interface HealthRecord {
|
|
36
42
|
/** From a 429 authoritative reset header — rate-limited until this instant. */
|
|
@@ -39,11 +45,20 @@ interface HealthRecord {
|
|
|
39
45
|
overloadUntil?: number;
|
|
40
46
|
/** From a final-401 / plain-403 / 5xx / thrown — transient cooldown until this. */
|
|
41
47
|
tempUnavailableUntil?: number;
|
|
48
|
+
/**
|
|
49
|
+
* From a detected quota/usage-limit error (e.g. Codex weekly window exhausted).
|
|
50
|
+
* Intentionally NOT cleared by a 2xx: while the account's credits/window are
|
|
51
|
+
* exhausted, in-flight sessions on the SAME account keep returning 2xx (Codex
|
|
52
|
+
* does not kill them mid-session), which would otherwise immediately heal a
|
|
53
|
+
* transient/rate-limit mark and re-strand every NEW session onto this account.
|
|
54
|
+
* Self-heals only when the deadline elapses.
|
|
55
|
+
*/
|
|
56
|
+
quotaExhaustedUntil?: number;
|
|
42
57
|
/** From a 403-ban — permanent, self-heal never clears it. */
|
|
43
58
|
blocked?: boolean;
|
|
44
59
|
}
|
|
45
60
|
/** The coarse health state surfaced to the admin accounts view (secret-free). */
|
|
46
|
-
type AccountHealthState = 'healthy' | 'rate_limited' | 'overloaded' | 'transient' | 'blocked';
|
|
61
|
+
type AccountHealthState = 'healthy' | 'rate_limited' | 'overloaded' | 'transient' | 'quota_exhausted' | 'blocked';
|
|
47
62
|
/** Admin-facing status projection for one account. */
|
|
48
63
|
interface AccountHealthStatus {
|
|
49
64
|
state: AccountHealthState;
|
|
@@ -68,7 +83,7 @@ interface AccountRecoveryEvent {
|
|
|
68
83
|
/** A recovery listener (fire-and-forget; the emitter never awaits it). */
|
|
69
84
|
type AccountRecoveryListener = (event: AccountRecoveryEvent) => void;
|
|
70
85
|
/** The coarse anomaly state an account transitioned into (secret-free). */
|
|
71
|
-
type AccountAnomalyState = 'blocked' | 'unauthorized' | 'rate_limited' | 'overloaded';
|
|
86
|
+
type AccountAnomalyState = 'blocked' | 'unauthorized' | 'rate_limited' | 'overloaded' | 'quota_exhausted';
|
|
72
87
|
/**
|
|
73
88
|
* The anomaly signal emitted when a HEALTHY account transitions to unhealthy —
|
|
74
89
|
* the additive union #5 (webhooks) consumes, which this tracker's frozen `kind`
|
|
@@ -89,6 +104,30 @@ interface AccountAnomalyEvent {
|
|
|
89
104
|
}
|
|
90
105
|
/** An anomaly listener (fire-and-forget; the emitter never awaits it). */
|
|
91
106
|
type AccountAnomalyListener = (event: AccountAnomalyEvent) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Admin-readable projection of the existing health event stream. It is kept in
|
|
109
|
+
* the tracker itself so webhooks and diagnostics consume the same edge events;
|
|
110
|
+
* this is not a second marking/event mechanism. Only opaque ids, timestamps,
|
|
111
|
+
* and coarse states are retained.
|
|
112
|
+
*/
|
|
113
|
+
type AccountHealthDiagnostic = {
|
|
114
|
+
kind: 'health-anomaly';
|
|
115
|
+
providerId: string;
|
|
116
|
+
accountId: string;
|
|
117
|
+
at: number;
|
|
118
|
+
state: AccountAnomalyState;
|
|
119
|
+
} | {
|
|
120
|
+
kind: 'health-recovery';
|
|
121
|
+
providerId: string;
|
|
122
|
+
accountId: string;
|
|
123
|
+
at: number;
|
|
124
|
+
};
|
|
125
|
+
interface AccountHealthDiagnosticFilter {
|
|
126
|
+
providerId?: string;
|
|
127
|
+
accountId?: string;
|
|
128
|
+
limit?: number;
|
|
129
|
+
}
|
|
130
|
+
declare const ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT = 200;
|
|
92
131
|
/** The inputs one upstream attempt contributes to health marking (design D3). */
|
|
93
132
|
interface RecordUpstreamOutcomeInput {
|
|
94
133
|
/** The final HTTP status, or `null` for a thrown / network failure. */
|
|
@@ -134,6 +173,7 @@ declare class SubscriptionAccountHealth {
|
|
|
134
173
|
private readonly records;
|
|
135
174
|
private readonly listeners;
|
|
136
175
|
private readonly anomalyListeners;
|
|
176
|
+
private readonly diagnostics;
|
|
137
177
|
private readonly now;
|
|
138
178
|
private overloadEnabled;
|
|
139
179
|
private overloadTtlMs;
|
|
@@ -150,14 +190,14 @@ declare class SubscriptionAccountHealth {
|
|
|
150
190
|
configure(opts: Pick<SubscriptionAccountHealthOptions, 'overloadEnabled' | 'overloadTtlMs' | 'authErrorTtlMs' | 'serverErrorTtlMs'>): void;
|
|
151
191
|
/**
|
|
152
192
|
* Whether an account may be scheduled RIGHT NOW. `blocked` → false; any timer
|
|
153
|
-
* still in the future → false. Expired timers are lazily deleted on read
|
|
154
|
-
*
|
|
193
|
+
* still in the future → false. Expired timers are lazily deleted on read so
|
|
194
|
+
* an elapsed cooldown restores the account WITHOUT a
|
|
155
195
|
* timer — but the recovery SIGNAL is the sweeper's job (`sweepRecoveries`), so
|
|
156
196
|
* this read never emits (correctness is independent of the tick).
|
|
157
197
|
*/
|
|
158
198
|
isSchedulable(providerId: string, accountId: string, now?: number): boolean;
|
|
159
199
|
/**
|
|
160
|
-
* The single marking entry point (design D3)
|
|
200
|
+
* The single marking entry point (design D3):
|
|
161
201
|
* - 429 + authoritative reset → `rateLimitEndAt`; a non-claude 429 may use
|
|
162
202
|
* `retryAfterSeconds`; a bare 429 (no resolvable reset) is NOT marked;
|
|
163
203
|
* - 529 → `overloadUntil` (gated by `overloadEnabled`);
|
|
@@ -167,6 +207,20 @@ declare class SubscriptionAccountHealth {
|
|
|
167
207
|
* - 2xx → clear rate-limit + transient state (keep `blocked`).
|
|
168
208
|
*/
|
|
169
209
|
recordUpstreamOutcome(providerId: string, accountId: string, input: RecordUpstreamOutcomeInput): void;
|
|
210
|
+
/**
|
|
211
|
+
* Mark an account quota/usage-exhausted until `untilEpochMs` (the Codex weekly
|
|
212
|
+
* usage-limit wall). Unlike the status-driven timers set by
|
|
213
|
+
* {@link recordUpstreamOutcome}, this mark is NOT cleared by a 2xx: in-flight
|
|
214
|
+
* sessions on the same account keep succeeding after the window is exhausted,
|
|
215
|
+
* so a traffic-driven clear would re-strand every NEW session onto this
|
|
216
|
+
* account. It excludes the account from NEW-session selection until the
|
|
217
|
+
* deadline elapses (existing sessions are unaffected — they already hold their
|
|
218
|
+
* token). Self-heals lazily on read and via {@link sweepRecoveries}.
|
|
219
|
+
* `untilEpochMs` is clamped to {@link MAX_QUOTA_COOLDOWN_MS}; an already-elapsed
|
|
220
|
+
* deadline is a no-op. Emits a `quota_exhausted` anomaly on the healthy→
|
|
221
|
+
* unhealthy edge.
|
|
222
|
+
*/
|
|
223
|
+
markQuotaExhausted(providerId: string, accountId: string, untilEpochMs: number, now?: number): void;
|
|
170
224
|
/** Whether a record is unhealthy at `now`: blocked, or any timer still future. */
|
|
171
225
|
private isUnhealthyAt;
|
|
172
226
|
/**
|
|
@@ -194,6 +248,8 @@ declare class SubscriptionAccountHealth {
|
|
|
194
248
|
sweepRecoveries(now?: number): AccountRecoveryEvent[];
|
|
195
249
|
/** Admin-facing coarse status + cooldown-until for one account (secret-free). */
|
|
196
250
|
getStatus(providerId: string, accountId: string, now?: number): AccountHealthStatus;
|
|
251
|
+
/** Return newest-first, bounded, secret-free health edge diagnostics. */
|
|
252
|
+
getDiagnostics(filter?: AccountHealthDiagnosticFilter): AccountHealthDiagnostic[];
|
|
197
253
|
/**
|
|
198
254
|
* Register a recovery listener (the OQ3 seam #5 webhooks + #8 health-cron
|
|
199
255
|
* consume). Returns an unsubscribe function. Two edges fire it:
|
|
@@ -216,6 +272,7 @@ declare class SubscriptionAccountHealth {
|
|
|
216
272
|
*/
|
|
217
273
|
onAnomaly(listener: AccountAnomalyListener): () => void;
|
|
218
274
|
private emitAnomaly;
|
|
275
|
+
private recordDiagnostic;
|
|
219
276
|
/**
|
|
220
277
|
* Delete every EXPIRED timer field on the record in place; return whether any
|
|
221
278
|
* timer is STILL active (in the future). `blocked` is not a timer and is left
|
|
@@ -232,4 +289,4 @@ declare function setSharedAccountHealth(instance: SubscriptionAccountHealth): vo
|
|
|
232
289
|
/** Reset the shared singleton (tests / teardown only). */
|
|
233
290
|
declare function __resetSharedAccountHealthForTests(): void;
|
|
234
291
|
|
|
235
|
-
export { AUTH_ERROR_TTL_MS, type AccountAnomalyEvent, type AccountAnomalyListener, type AccountAnomalyState, type AccountHealthState, type AccountHealthStatus, type AccountRecoveryEvent, type AccountRecoveryListener, type HeadersLike, type HealthRecord, OVERLOAD_ENABLED_DEFAULT, OVERLOAD_TTL_MS, type RecordUpstreamOutcomeInput, SERVER_ERROR_TTL_MS, SubscriptionAccountHealth, type SubscriptionAccountHealthOptions, __resetSharedAccountHealthForTests, getSharedAccountHealth, resolveResetSeconds, setSharedAccountHealth };
|
|
292
|
+
export { ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT, AUTH_ERROR_TTL_MS, type AccountAnomalyEvent, type AccountAnomalyListener, type AccountAnomalyState, type AccountHealthDiagnostic, type AccountHealthDiagnosticFilter, type AccountHealthState, type AccountHealthStatus, type AccountRecoveryEvent, type AccountRecoveryListener, type HeadersLike, type HealthRecord, MAX_QUOTA_COOLDOWN_MS, OVERLOAD_ENABLED_DEFAULT, OVERLOAD_TTL_MS, type RecordUpstreamOutcomeInput, SERVER_ERROR_TTL_MS, SubscriptionAccountHealth, type SubscriptionAccountHealthOptions, __resetSharedAccountHealthForTests, getSharedAccountHealth, resolveResetSeconds, setSharedAccountHealth };
|
|
@@ -27,10 +27,16 @@
|
|
|
27
27
|
declare const OVERLOAD_TTL_MS: number;
|
|
28
28
|
/** 529 overload cooldown enabled by default (LEAD OQ1). */
|
|
29
29
|
declare const OVERLOAD_ENABLED_DEFAULT = true;
|
|
30
|
-
/** Transient cooldown for a final
|
|
30
|
+
/** Transient cooldown for a final 401 or non-ban 403 response. */
|
|
31
31
|
declare const AUTH_ERROR_TTL_MS: number;
|
|
32
|
-
/** Transient cooldown for a 5xx
|
|
32
|
+
/** Transient cooldown for a 5xx response or network failure. */
|
|
33
33
|
declare const SERVER_ERROR_TTL_MS: number;
|
|
34
|
+
/**
|
|
35
|
+
* Hard cap on a quota-exhaustion cooldown (the weekly usage window is ~1 week).
|
|
36
|
+
* A parsed/supplied deadline farther than this into the future is clamped, so a
|
|
37
|
+
* bogus parse can never strand an account indefinitely.
|
|
38
|
+
*/
|
|
39
|
+
declare const MAX_QUOTA_COOLDOWN_MS: number;
|
|
34
40
|
/** Per-account health record (all timers are epoch ms). Absent fields ⇒ healthy. */
|
|
35
41
|
interface HealthRecord {
|
|
36
42
|
/** From a 429 authoritative reset header — rate-limited until this instant. */
|
|
@@ -39,11 +45,20 @@ interface HealthRecord {
|
|
|
39
45
|
overloadUntil?: number;
|
|
40
46
|
/** From a final-401 / plain-403 / 5xx / thrown — transient cooldown until this. */
|
|
41
47
|
tempUnavailableUntil?: number;
|
|
48
|
+
/**
|
|
49
|
+
* From a detected quota/usage-limit error (e.g. Codex weekly window exhausted).
|
|
50
|
+
* Intentionally NOT cleared by a 2xx: while the account's credits/window are
|
|
51
|
+
* exhausted, in-flight sessions on the SAME account keep returning 2xx (Codex
|
|
52
|
+
* does not kill them mid-session), which would otherwise immediately heal a
|
|
53
|
+
* transient/rate-limit mark and re-strand every NEW session onto this account.
|
|
54
|
+
* Self-heals only when the deadline elapses.
|
|
55
|
+
*/
|
|
56
|
+
quotaExhaustedUntil?: number;
|
|
42
57
|
/** From a 403-ban — permanent, self-heal never clears it. */
|
|
43
58
|
blocked?: boolean;
|
|
44
59
|
}
|
|
45
60
|
/** The coarse health state surfaced to the admin accounts view (secret-free). */
|
|
46
|
-
type AccountHealthState = 'healthy' | 'rate_limited' | 'overloaded' | 'transient' | 'blocked';
|
|
61
|
+
type AccountHealthState = 'healthy' | 'rate_limited' | 'overloaded' | 'transient' | 'quota_exhausted' | 'blocked';
|
|
47
62
|
/** Admin-facing status projection for one account. */
|
|
48
63
|
interface AccountHealthStatus {
|
|
49
64
|
state: AccountHealthState;
|
|
@@ -68,7 +83,7 @@ interface AccountRecoveryEvent {
|
|
|
68
83
|
/** A recovery listener (fire-and-forget; the emitter never awaits it). */
|
|
69
84
|
type AccountRecoveryListener = (event: AccountRecoveryEvent) => void;
|
|
70
85
|
/** The coarse anomaly state an account transitioned into (secret-free). */
|
|
71
|
-
type AccountAnomalyState = 'blocked' | 'unauthorized' | 'rate_limited' | 'overloaded';
|
|
86
|
+
type AccountAnomalyState = 'blocked' | 'unauthorized' | 'rate_limited' | 'overloaded' | 'quota_exhausted';
|
|
72
87
|
/**
|
|
73
88
|
* The anomaly signal emitted when a HEALTHY account transitions to unhealthy —
|
|
74
89
|
* the additive union #5 (webhooks) consumes, which this tracker's frozen `kind`
|
|
@@ -89,6 +104,30 @@ interface AccountAnomalyEvent {
|
|
|
89
104
|
}
|
|
90
105
|
/** An anomaly listener (fire-and-forget; the emitter never awaits it). */
|
|
91
106
|
type AccountAnomalyListener = (event: AccountAnomalyEvent) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Admin-readable projection of the existing health event stream. It is kept in
|
|
109
|
+
* the tracker itself so webhooks and diagnostics consume the same edge events;
|
|
110
|
+
* this is not a second marking/event mechanism. Only opaque ids, timestamps,
|
|
111
|
+
* and coarse states are retained.
|
|
112
|
+
*/
|
|
113
|
+
type AccountHealthDiagnostic = {
|
|
114
|
+
kind: 'health-anomaly';
|
|
115
|
+
providerId: string;
|
|
116
|
+
accountId: string;
|
|
117
|
+
at: number;
|
|
118
|
+
state: AccountAnomalyState;
|
|
119
|
+
} | {
|
|
120
|
+
kind: 'health-recovery';
|
|
121
|
+
providerId: string;
|
|
122
|
+
accountId: string;
|
|
123
|
+
at: number;
|
|
124
|
+
};
|
|
125
|
+
interface AccountHealthDiagnosticFilter {
|
|
126
|
+
providerId?: string;
|
|
127
|
+
accountId?: string;
|
|
128
|
+
limit?: number;
|
|
129
|
+
}
|
|
130
|
+
declare const ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT = 200;
|
|
92
131
|
/** The inputs one upstream attempt contributes to health marking (design D3). */
|
|
93
132
|
interface RecordUpstreamOutcomeInput {
|
|
94
133
|
/** The final HTTP status, or `null` for a thrown / network failure. */
|
|
@@ -134,6 +173,7 @@ declare class SubscriptionAccountHealth {
|
|
|
134
173
|
private readonly records;
|
|
135
174
|
private readonly listeners;
|
|
136
175
|
private readonly anomalyListeners;
|
|
176
|
+
private readonly diagnostics;
|
|
137
177
|
private readonly now;
|
|
138
178
|
private overloadEnabled;
|
|
139
179
|
private overloadTtlMs;
|
|
@@ -150,14 +190,14 @@ declare class SubscriptionAccountHealth {
|
|
|
150
190
|
configure(opts: Pick<SubscriptionAccountHealthOptions, 'overloadEnabled' | 'overloadTtlMs' | 'authErrorTtlMs' | 'serverErrorTtlMs'>): void;
|
|
151
191
|
/**
|
|
152
192
|
* Whether an account may be scheduled RIGHT NOW. `blocked` → false; any timer
|
|
153
|
-
* still in the future → false. Expired timers are lazily deleted on read
|
|
154
|
-
*
|
|
193
|
+
* still in the future → false. Expired timers are lazily deleted on read so
|
|
194
|
+
* an elapsed cooldown restores the account WITHOUT a
|
|
155
195
|
* timer — but the recovery SIGNAL is the sweeper's job (`sweepRecoveries`), so
|
|
156
196
|
* this read never emits (correctness is independent of the tick).
|
|
157
197
|
*/
|
|
158
198
|
isSchedulable(providerId: string, accountId: string, now?: number): boolean;
|
|
159
199
|
/**
|
|
160
|
-
* The single marking entry point (design D3)
|
|
200
|
+
* The single marking entry point (design D3):
|
|
161
201
|
* - 429 + authoritative reset → `rateLimitEndAt`; a non-claude 429 may use
|
|
162
202
|
* `retryAfterSeconds`; a bare 429 (no resolvable reset) is NOT marked;
|
|
163
203
|
* - 529 → `overloadUntil` (gated by `overloadEnabled`);
|
|
@@ -167,6 +207,20 @@ declare class SubscriptionAccountHealth {
|
|
|
167
207
|
* - 2xx → clear rate-limit + transient state (keep `blocked`).
|
|
168
208
|
*/
|
|
169
209
|
recordUpstreamOutcome(providerId: string, accountId: string, input: RecordUpstreamOutcomeInput): void;
|
|
210
|
+
/**
|
|
211
|
+
* Mark an account quota/usage-exhausted until `untilEpochMs` (the Codex weekly
|
|
212
|
+
* usage-limit wall). Unlike the status-driven timers set by
|
|
213
|
+
* {@link recordUpstreamOutcome}, this mark is NOT cleared by a 2xx: in-flight
|
|
214
|
+
* sessions on the same account keep succeeding after the window is exhausted,
|
|
215
|
+
* so a traffic-driven clear would re-strand every NEW session onto this
|
|
216
|
+
* account. It excludes the account from NEW-session selection until the
|
|
217
|
+
* deadline elapses (existing sessions are unaffected — they already hold their
|
|
218
|
+
* token). Self-heals lazily on read and via {@link sweepRecoveries}.
|
|
219
|
+
* `untilEpochMs` is clamped to {@link MAX_QUOTA_COOLDOWN_MS}; an already-elapsed
|
|
220
|
+
* deadline is a no-op. Emits a `quota_exhausted` anomaly on the healthy→
|
|
221
|
+
* unhealthy edge.
|
|
222
|
+
*/
|
|
223
|
+
markQuotaExhausted(providerId: string, accountId: string, untilEpochMs: number, now?: number): void;
|
|
170
224
|
/** Whether a record is unhealthy at `now`: blocked, or any timer still future. */
|
|
171
225
|
private isUnhealthyAt;
|
|
172
226
|
/**
|
|
@@ -194,6 +248,8 @@ declare class SubscriptionAccountHealth {
|
|
|
194
248
|
sweepRecoveries(now?: number): AccountRecoveryEvent[];
|
|
195
249
|
/** Admin-facing coarse status + cooldown-until for one account (secret-free). */
|
|
196
250
|
getStatus(providerId: string, accountId: string, now?: number): AccountHealthStatus;
|
|
251
|
+
/** Return newest-first, bounded, secret-free health edge diagnostics. */
|
|
252
|
+
getDiagnostics(filter?: AccountHealthDiagnosticFilter): AccountHealthDiagnostic[];
|
|
197
253
|
/**
|
|
198
254
|
* Register a recovery listener (the OQ3 seam #5 webhooks + #8 health-cron
|
|
199
255
|
* consume). Returns an unsubscribe function. Two edges fire it:
|
|
@@ -216,6 +272,7 @@ declare class SubscriptionAccountHealth {
|
|
|
216
272
|
*/
|
|
217
273
|
onAnomaly(listener: AccountAnomalyListener): () => void;
|
|
218
274
|
private emitAnomaly;
|
|
275
|
+
private recordDiagnostic;
|
|
219
276
|
/**
|
|
220
277
|
* Delete every EXPIRED timer field on the record in place; return whether any
|
|
221
278
|
* timer is STILL active (in the future). `blocked` is not a timer and is left
|
|
@@ -232,4 +289,4 @@ declare function setSharedAccountHealth(instance: SubscriptionAccountHealth): vo
|
|
|
232
289
|
/** Reset the shared singleton (tests / teardown only). */
|
|
233
290
|
declare function __resetSharedAccountHealthForTests(): void;
|
|
234
291
|
|
|
235
|
-
export { AUTH_ERROR_TTL_MS, type AccountAnomalyEvent, type AccountAnomalyListener, type AccountAnomalyState, type AccountHealthState, type AccountHealthStatus, type AccountRecoveryEvent, type AccountRecoveryListener, type HeadersLike, type HealthRecord, OVERLOAD_ENABLED_DEFAULT, OVERLOAD_TTL_MS, type RecordUpstreamOutcomeInput, SERVER_ERROR_TTL_MS, SubscriptionAccountHealth, type SubscriptionAccountHealthOptions, __resetSharedAccountHealthForTests, getSharedAccountHealth, resolveResetSeconds, setSharedAccountHealth };
|
|
292
|
+
export { ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT, AUTH_ERROR_TTL_MS, type AccountAnomalyEvent, type AccountAnomalyListener, type AccountAnomalyState, type AccountHealthDiagnostic, type AccountHealthDiagnosticFilter, type AccountHealthState, type AccountHealthStatus, type AccountRecoveryEvent, type AccountRecoveryListener, type HeadersLike, type HealthRecord, MAX_QUOTA_COOLDOWN_MS, OVERLOAD_ENABLED_DEFAULT, OVERLOAD_TTL_MS, type RecordUpstreamOutcomeInput, SERVER_ERROR_TTL_MS, SubscriptionAccountHealth, type SubscriptionAccountHealthOptions, __resetSharedAccountHealthForTests, getSharedAccountHealth, resolveResetSeconds, setSharedAccountHealth };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT,
|
|
2
3
|
AUTH_ERROR_TTL_MS,
|
|
4
|
+
MAX_QUOTA_COOLDOWN_MS,
|
|
3
5
|
OVERLOAD_ENABLED_DEFAULT,
|
|
4
6
|
OVERLOAD_TTL_MS,
|
|
5
7
|
SERVER_ERROR_TTL_MS,
|
|
@@ -8,9 +10,11 @@ import {
|
|
|
8
10
|
getSharedAccountHealth,
|
|
9
11
|
resolveResetSeconds,
|
|
10
12
|
setSharedAccountHealth
|
|
11
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-N7OOJI5V.js";
|
|
12
14
|
export {
|
|
15
|
+
ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT,
|
|
13
16
|
AUTH_ERROR_TTL_MS,
|
|
17
|
+
MAX_QUOTA_COOLDOWN_MS,
|
|
14
18
|
OVERLOAD_ENABLED_DEFAULT,
|
|
15
19
|
OVERLOAD_TTL_MS,
|
|
16
20
|
SERVER_ERROR_TTL_MS,
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkYNJNTCSQcjs = require('../chunk-YNJNTCSQ.cjs');
|
|
5
5
|
require('../chunk-SVNDB62D.cjs');
|
|
6
|
+
require('../chunk-6QQG7YG3.cjs');
|
|
7
|
+
require('../chunk-25GXJCEZ.cjs');
|
|
8
|
+
require('../chunk-HVBBDBS3.cjs');
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
|
|
9
|
-
exports.SubscriptionAuthSource =
|
|
12
|
+
exports.SubscriptionAuthSource = _chunkYNJNTCSQcjs.SubscriptionAuthSource; exports.stripAuthHeaders = _chunkYNJNTCSQcjs.stripAuthHeaders;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenCodeGoTokenConfig, OpenCodeGoScenario, OpenCodeGoModelEntry } from '@omnicross/contracts/subscription-types';
|
|
2
2
|
import { AuthSource, AuthApplyHints } from './AuthSource.cjs';
|
|
3
3
|
import { AuthStrategy } from './SubscriptionAuthStrategy.cjs';
|
|
4
|
+
import './BoundAccountSelectionError.cjs';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* SubscriptionAuthSource — `AuthSource` wrapping a subscription `AuthStrategy`.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenCodeGoTokenConfig, OpenCodeGoScenario, OpenCodeGoModelEntry } from '@omnicross/contracts/subscription-types';
|
|
2
2
|
import { AuthSource, AuthApplyHints } from './AuthSource.js';
|
|
3
3
|
import { AuthStrategy } from './SubscriptionAuthStrategy.js';
|
|
4
|
+
import './BoundAccountSelectionError.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* SubscriptionAuthSource — `AuthSource` wrapping a subscription `AuthStrategy`.
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SubscriptionAuthSource,
|
|
3
3
|
stripAuthHeaders
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-23S5OPIO.js";
|
|
5
5
|
import "../chunk-AVYJ4YBN.js";
|
|
6
|
+
import "../chunk-OPRS4IQZ.js";
|
|
7
|
+
import "../chunk-2JYRIADS.js";
|
|
8
|
+
import "../chunk-MKF46GNZ.js";
|
|
6
9
|
export {
|
|
7
10
|
SubscriptionAuthSource,
|
|
8
11
|
stripAuthHeaders
|