@juspay/neurolink 9.92.2 → 9.93.0
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/CHANGELOG.md +12 -0
- package/dist/browser/neurolink.min.js +330 -330
- package/dist/cli/commands/proxy.d.ts +2 -1
- package/dist/cli/commands/proxy.js +157 -27
- package/dist/lib/proxy/proxyActivity.d.ts +2 -2
- package/dist/lib/proxy/proxyActivity.js +49 -9
- package/dist/lib/proxy/proxyLifecycle.d.ts +8 -0
- package/dist/lib/proxy/proxyLifecycle.js +333 -0
- package/dist/lib/proxy/requestLogger.js +6 -0
- package/dist/lib/server/routes/claudeProxyRoutes.d.ts +16 -18
- package/dist/lib/server/routes/claudeProxyRoutes.js +63 -71
- package/dist/lib/types/proxy.d.ts +82 -0
- package/dist/proxy/proxyActivity.d.ts +2 -2
- package/dist/proxy/proxyActivity.js +49 -9
- package/dist/proxy/proxyLifecycle.d.ts +8 -0
- package/dist/proxy/proxyLifecycle.js +332 -0
- package/dist/proxy/requestLogger.js +6 -0
- package/dist/server/routes/claudeProxyRoutes.d.ts +16 -18
- package/dist/server/routes/claudeProxyRoutes.js +63 -71
- package/dist/types/proxy.d.ts +82 -0
- package/package.json +1 -1
|
@@ -53,11 +53,6 @@ const BLOCKED_UPSTREAM_HEADERS = new Set([
|
|
|
53
53
|
let primaryAccountIndex = 0;
|
|
54
54
|
/** Track account count so we can reset primaryAccountIndex when it changes. */
|
|
55
55
|
let lastKnownAccountCount = 0;
|
|
56
|
-
/** Stable account key used by legacy fixed-config route factories. Runtime
|
|
57
|
-
* config providers pass the request generation's primary key explicitly.
|
|
58
|
-
* When undefined, home semantics retain insertion-order behavior. */
|
|
59
|
-
let configuredPrimaryAccountKey;
|
|
60
|
-
let configuredAccountAllowlist;
|
|
61
56
|
const MAX_AUTH_RETRIES = 5;
|
|
62
57
|
const MAX_TRANSIENT_SAME_ACCOUNT_RETRIES = 2;
|
|
63
58
|
const TRANSIENT_SAME_ACCOUNT_RETRY_DELAYS_MS = [250, 1_000];
|
|
@@ -129,7 +124,7 @@ function advancePrimaryIfCurrent(accountKey, enabledCount, primaryAccountKey) {
|
|
|
129
124
|
* no key is configured or the key cannot be matched (account disabled/
|
|
130
125
|
* removed). The resolution is per-request because enabledAccounts membership
|
|
131
126
|
* can shift between requests. */
|
|
132
|
-
function resolveHomeIndex(enabledAccounts, primaryAccountKey
|
|
127
|
+
function resolveHomeIndex(enabledAccounts, primaryAccountKey) {
|
|
133
128
|
if (!primaryAccountKey) {
|
|
134
129
|
return 0;
|
|
135
130
|
}
|
|
@@ -140,7 +135,7 @@ function resolveHomeIndex(enabledAccounts, primaryAccountKey = configuredPrimary
|
|
|
140
135
|
* primaryAccountIndex back to its index so traffic returns to the preferred
|
|
141
136
|
* account once its rate limit window expires. Called at the start of each
|
|
142
137
|
* request. Home is resolved fresh per call via resolveHomeIndex. */
|
|
143
|
-
function maybeResetPrimaryToHome(enabledAccounts, primaryAccountKey
|
|
138
|
+
function maybeResetPrimaryToHome(enabledAccounts, primaryAccountKey) {
|
|
144
139
|
if (enabledAccounts.length <= 1) {
|
|
145
140
|
return;
|
|
146
141
|
}
|
|
@@ -463,28 +458,30 @@ function accountSortMetrics(accountKey, now, sessionSoftLimit, sessionResetToler
|
|
|
463
458
|
}
|
|
464
459
|
/**
|
|
465
460
|
* Order accounts to MAXIMIZE quota utilization (fill-first, smart order):
|
|
466
|
-
* spend the
|
|
467
|
-
*
|
|
461
|
+
* spend the overall weekly allowance that expires SOONEST first, so quota
|
|
462
|
+
* cannot disappear while traffic is consuming a newer weekly window. The 5h
|
|
463
|
+
* window remains an availability boundary: a session at its soft limit is
|
|
464
|
+
* temporarily demoted until that session resets.
|
|
468
465
|
*
|
|
469
466
|
* Priority among usable accounts:
|
|
470
467
|
* 1. no quota data yet — probe first: one request reveals its windows and
|
|
471
468
|
* self-corrects the ordering. (Ranking unknowns last would starve them
|
|
472
469
|
* forever: never picked → never observed → never comparable.)
|
|
473
470
|
* 2. session headroom before session-saturated (>= soft limit or
|
|
474
|
-
* "throttled") —
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
* 5. highest weekly utilization — finish off the one closest to done
|
|
471
|
+
* "throttled") — do not re-hammer an urgent weekly account while its 5h
|
|
472
|
+
* capacity is temporarily unavailable.
|
|
473
|
+
* 3. soonest WEEKLY (7d) reset — consume the oldest overall allowance
|
|
474
|
+
* before it expires.
|
|
475
|
+
* 4. soonest SESSION (5h) reset — decides equal-weekly or fresh-weekly ties,
|
|
476
|
+
* using tolerance buckets for comparator stability.
|
|
477
|
+
* 5. highest weekly utilization — finish off the one closest to done.
|
|
481
478
|
* 6. configured primary account, then insertion order
|
|
482
|
-
*
|
|
483
|
-
*
|
|
479
|
+
* Saturated pairs are ordered by soonest 5h recovery first, then weekly reset,
|
|
480
|
+
* because neither can consume more quota until session capacity returns.
|
|
484
481
|
* Cooling/rejected accounts sort last, soonest-back-to-service first, as
|
|
485
482
|
* last resort.
|
|
486
483
|
*/
|
|
487
|
-
function orderAccountsByQuota(accounts, now, primaryKey
|
|
484
|
+
function orderAccountsByQuota(accounts, now, primaryKey, sessionSoftLimit = getSessionSoftLimit(), sessionResetToleranceMs = getSessionResetToleranceMs()) {
|
|
488
485
|
const metrics = (key) => accountSortMetrics(key, now, sessionSoftLimit, sessionResetToleranceMs);
|
|
489
486
|
return [...accounts].sort((a, b) => {
|
|
490
487
|
const ma = metrics(a.key);
|
|
@@ -503,14 +500,21 @@ function orderAccountsByQuota(accounts, now, primaryKey = configuredPrimaryAccou
|
|
|
503
500
|
if (ma.saturated !== mb.saturated) {
|
|
504
501
|
return ma.saturated ? 1 : -1;
|
|
505
502
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
503
|
+
if (ma.saturated && mb.saturated) {
|
|
504
|
+
if (ma.sessionResetBucket !== mb.sessionResetBucket) {
|
|
505
|
+
return ma.sessionResetBucket - mb.sessionResetBucket;
|
|
506
|
+
}
|
|
507
|
+
if (ma.weeklyReset !== mb.weeklyReset) {
|
|
508
|
+
return ma.weeklyReset - mb.weeklyReset;
|
|
509
|
+
}
|
|
511
510
|
}
|
|
512
|
-
|
|
513
|
-
|
|
511
|
+
else {
|
|
512
|
+
if (ma.weeklyReset !== mb.weeklyReset) {
|
|
513
|
+
return ma.weeklyReset - mb.weeklyReset;
|
|
514
|
+
}
|
|
515
|
+
if (ma.sessionResetBucket !== mb.sessionResetBucket) {
|
|
516
|
+
return ma.sessionResetBucket - mb.sessionResetBucket;
|
|
517
|
+
}
|
|
514
518
|
}
|
|
515
519
|
if (ma.weeklyUsed !== mb.weeklyUsed) {
|
|
516
520
|
return mb.weeklyUsed - ma.weeklyUsed;
|
|
@@ -1538,7 +1542,7 @@ async function handleClaudePassthroughJsonResponse(args) {
|
|
|
1538
1542
|
return responseJson;
|
|
1539
1543
|
}
|
|
1540
1544
|
async function loadClaudeProxyAccounts(args) {
|
|
1541
|
-
const { ctx, body, tracer, requestStartTime, accountStrategy, primaryAccountKey
|
|
1545
|
+
const { ctx, body, tracer, requestStartTime, accountStrategy, primaryAccountKey, accountAllowlist, quotaRoutingEnabled = isQuotaRoutingEnabled(), sessionSoftLimit = getSessionSoftLimit(), sessionResetToleranceMs = getSessionResetToleranceMs(), buildLoggedClaudeError, } = args;
|
|
1542
1546
|
const fs = await import("fs");
|
|
1543
1547
|
const os = await import("os");
|
|
1544
1548
|
const accounts = [];
|
|
@@ -1722,10 +1726,16 @@ async function loadClaudeProxyAccounts(args) {
|
|
|
1722
1726
|
const quotaOrdered = accountStrategy === "fill-first" &&
|
|
1723
1727
|
orderedAccounts.length > 1 &&
|
|
1724
1728
|
quotaRoutingEnabled;
|
|
1729
|
+
if (!quotaOrdered && accountStrategy === "fill-first") {
|
|
1730
|
+
// Apply the request-scoped home before deriving this request's order. A
|
|
1731
|
+
// hot-reloaded primary change must not lag by one request. Round-robin
|
|
1732
|
+
// deliberately skips this reset so its rotating index remains strict.
|
|
1733
|
+
maybeResetPrimaryToHome(enabledAccounts, primaryAccountKey);
|
|
1734
|
+
}
|
|
1725
1735
|
if (quotaOrdered) {
|
|
1726
|
-
// Fill-first with a smart fill order: spend the account whose window
|
|
1727
|
-
// soonest
|
|
1728
|
-
//
|
|
1736
|
+
// Fill-first with a smart fill order: spend the account whose weekly window
|
|
1737
|
+
// expires soonest while temporarily demoting sessions without headroom.
|
|
1738
|
+
// Supersedes the static home/primary index.
|
|
1729
1739
|
orderedAccounts = orderAccountsByQuota(enabledAccounts, Date.now(), primaryAccountKey, sessionSoftLimit, sessionResetToleranceMs);
|
|
1730
1740
|
if (logger.shouldLog("debug")) {
|
|
1731
1741
|
logger.debug(`[proxy] quota-ordered fill sequence: ${orderedAccounts
|
|
@@ -3052,7 +3062,10 @@ async function handleAnthropicAuthRetry(args) {
|
|
|
3052
3062
|
: String(retryFetchErr);
|
|
3053
3063
|
authRetryError = `network error on retry ${authRetry + 1}: ${message}`;
|
|
3054
3064
|
currentLastError = authRetryError;
|
|
3055
|
-
retryLogAttempt(502, "network_error", message, {
|
|
3065
|
+
retryLogAttempt(502, "network_error", message, {
|
|
3066
|
+
retryable: isRetryableNetworkError(retryFetchErr),
|
|
3067
|
+
errorCode: getErrorCode(retryFetchErr) ?? "unknown",
|
|
3068
|
+
});
|
|
3056
3069
|
logger.debug(`[proxy] ${authRetryError}`);
|
|
3057
3070
|
break;
|
|
3058
3071
|
}
|
|
@@ -3427,6 +3440,7 @@ function createAnthropicAttemptLogger(args) {
|
|
|
3427
3440
|
responseTimeMs: Date.now() - requestStart,
|
|
3428
3441
|
...(errorType ? { errorType } : {}),
|
|
3429
3442
|
...(errorMessage ? { errorMessage } : {}),
|
|
3443
|
+
...(extra?.errorCode ? { errorCode: extra.errorCode } : {}),
|
|
3430
3444
|
...(extra?.inputTokens !== undefined
|
|
3431
3445
|
? { inputTokens: extra.inputTokens }
|
|
3432
3446
|
: {}),
|
|
@@ -3641,19 +3655,27 @@ async function fetchAnthropicAccountResponse(args) {
|
|
|
3641
3655
|
});
|
|
3642
3656
|
}
|
|
3643
3657
|
catch (fetchErr) {
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3658
|
+
const retryable = isRetryableNetworkError(fetchErr);
|
|
3659
|
+
// Every dispatched upstream request is an attempt, including terminal
|
|
3660
|
+
// transport failures. Record it once before preserving the throw behavior.
|
|
3647
3661
|
sawNetworkError = true;
|
|
3648
3662
|
recordAttemptError(account.label, account.type, 502);
|
|
3649
3663
|
const errorCode = getErrorCode(fetchErr) ?? "unknown";
|
|
3650
3664
|
const errorMessage = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
|
|
3651
3665
|
lastError = errorMessage;
|
|
3652
|
-
logger.always(`[proxy] fetch error account=${account.label} code=${errorCode} (retryable): ${errorMessage}`);
|
|
3653
|
-
logAttempt(502, "network_error", errorMessage
|
|
3666
|
+
logger.always(`[proxy] fetch error account=${account.label} code=${errorCode} (${retryable ? "retryable" : "terminal"}): ${errorMessage}`);
|
|
3667
|
+
logAttempt(502, "network_error", errorMessage, {
|
|
3668
|
+
retryable,
|
|
3669
|
+
errorCode,
|
|
3670
|
+
});
|
|
3654
3671
|
tracer?.setError("network_error", errorMessage);
|
|
3655
|
-
|
|
3672
|
+
if (retryable) {
|
|
3673
|
+
tracer?.recordRetry(account.label, "network_error");
|
|
3674
|
+
}
|
|
3656
3675
|
currentUpstreamSpan?.end();
|
|
3676
|
+
if (!retryable) {
|
|
3677
|
+
throw fetchErr;
|
|
3678
|
+
}
|
|
3657
3679
|
return {
|
|
3658
3680
|
continueLoop: true,
|
|
3659
3681
|
retrySameAccount: true,
|
|
@@ -3791,15 +3813,6 @@ async function handleAnthropicRoutedClaudeRequest(args) {
|
|
|
3791
3813
|
attemptNumber: 0,
|
|
3792
3814
|
};
|
|
3793
3815
|
const acctSelectionSpan = tracer?.startAccountSelection();
|
|
3794
|
-
// Try to return to the home primary account if its cooling has expired.
|
|
3795
|
-
// Skipped under quota routing, where the fill order is derived per-request
|
|
3796
|
-
// from live quota (soonest-reset-first) rather than a static home index.
|
|
3797
|
-
const usingQuotaOrder = accountStrategy === "fill-first" &&
|
|
3798
|
-
enabledAccounts.length > 1 &&
|
|
3799
|
-
quotaRoutingEnabled;
|
|
3800
|
-
if (!usingQuotaOrder) {
|
|
3801
|
-
maybeResetPrimaryToHome(enabledAccounts, primaryAccountKey);
|
|
3802
|
-
}
|
|
3803
3816
|
// Never re-hammer accounts with a known active cooldown. When every account
|
|
3804
3817
|
// is cooling, report the earliest persisted retry time without an upstream
|
|
3805
3818
|
// call; restarting the proxy must not erase or bypass this quarantine.
|
|
@@ -4174,10 +4187,6 @@ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrat
|
|
|
4174
4187
|
const runtimeConfigProvider = isClaudeProxyRouteRuntimeOptions(accountAllowlistOrRuntimeOptions)
|
|
4175
4188
|
? accountAllowlistOrRuntimeOptions.runtimeConfigProvider
|
|
4176
4189
|
: undefined;
|
|
4177
|
-
configuredPrimaryAccountKey = primaryAccountKey;
|
|
4178
|
-
configuredAccountAllowlist = accountAllowlist
|
|
4179
|
-
? new Set(accountAllowlist)
|
|
4180
|
-
: undefined;
|
|
4181
4190
|
return {
|
|
4182
4191
|
prefix: `${basePath}/v1`,
|
|
4183
4192
|
routes: [
|
|
@@ -4472,16 +4481,13 @@ function describeTransportError(error) {
|
|
|
4472
4481
|
*/
|
|
4473
4482
|
function isRetryableNetworkError(error) {
|
|
4474
4483
|
const code = getErrorCode(error);
|
|
4475
|
-
// Check non-retryable codes FIRST — before the string-based heuristic
|
|
4476
|
-
// which could false-positive on error messages containing these strings.
|
|
4477
|
-
const NON_RETRYABLE_CODES = ["ENOTFOUND"];
|
|
4478
|
-
if (code && NON_RETRYABLE_CODES.includes(code)) {
|
|
4479
|
-
return false;
|
|
4480
|
-
}
|
|
4481
4484
|
if (code &&
|
|
4482
4485
|
[
|
|
4483
4486
|
"ECONNREFUSED",
|
|
4484
4487
|
"ECONNRESET",
|
|
4488
|
+
// The Anthropic host is fixed, so ENOTFOUND can be a transient resolver
|
|
4489
|
+
// outage. Keep it inside the existing bounded same-account retry budget.
|
|
4490
|
+
"ENOTFOUND",
|
|
4485
4491
|
"ETIMEDOUT",
|
|
4486
4492
|
"EHOSTUNREACH",
|
|
4487
4493
|
"UND_ERR_CONNECT_TIMEOUT",
|
|
@@ -4493,13 +4499,9 @@ function isRetryableNetworkError(error) {
|
|
|
4493
4499
|
}
|
|
4494
4500
|
const message = error instanceof Error ? error.message : String(error);
|
|
4495
4501
|
const normalized = message.toLowerCase();
|
|
4496
|
-
// Exclude ENOTFOUND from string-based heuristic — DNS failures are permanent
|
|
4497
|
-
// and rotating accounts won't help since they all hit the same host.
|
|
4498
|
-
if (normalized.includes("enotfound")) {
|
|
4499
|
-
return false;
|
|
4500
|
-
}
|
|
4501
4502
|
return (normalized.includes("econnrefused") ||
|
|
4502
4503
|
normalized.includes("econnreset") ||
|
|
4504
|
+
normalized.includes("enotfound") ||
|
|
4503
4505
|
normalized.includes("etimedout") ||
|
|
4504
4506
|
normalized.includes("timed out") ||
|
|
4505
4507
|
normalized.includes("connection error") ||
|
|
@@ -4608,14 +4610,6 @@ export const __testHooks = {
|
|
|
4608
4610
|
const state = accountRuntimeState.get(key);
|
|
4609
4611
|
return state ? { ...state } : undefined;
|
|
4610
4612
|
},
|
|
4611
|
-
setConfiguredPrimaryAccountKey: (key) => {
|
|
4612
|
-
configuredPrimaryAccountKey = key;
|
|
4613
|
-
},
|
|
4614
|
-
getConfiguredPrimaryAccountKey: () => configuredPrimaryAccountKey,
|
|
4615
|
-
setConfiguredAccountAllowlist: (allowlist) => {
|
|
4616
|
-
configuredAccountAllowlist = allowlist ? new Set(allowlist) : undefined;
|
|
4617
|
-
},
|
|
4618
|
-
getConfiguredAccountAllowlist: () => configuredAccountAllowlist ? [...configuredAccountAllowlist] : undefined,
|
|
4619
4613
|
setPrimaryAccountIndex: (index) => {
|
|
4620
4614
|
primaryAccountIndex = index;
|
|
4621
4615
|
},
|
|
@@ -4631,8 +4625,6 @@ export const __testHooks = {
|
|
|
4631
4625
|
transientCooldownAdmissionSchedules.clear();
|
|
4632
4626
|
primaryAccountIndex = 0;
|
|
4633
4627
|
lastKnownAccountCount = 0;
|
|
4634
|
-
configuredPrimaryAccountKey = undefined;
|
|
4635
|
-
configuredAccountAllowlist = undefined;
|
|
4636
4628
|
},
|
|
4637
4629
|
polyfillOAuthBody: (bodyStr, isClaudeClientRequest) => polyfillOAuthBody(bodyStr, "test-account-token", null, isClaudeClientRequest),
|
|
4638
4630
|
isAntiAbuseConstruction429,
|
package/dist/types/proxy.d.ts
CHANGED
|
@@ -437,6 +437,8 @@ export type RequestLogEntry = {
|
|
|
437
437
|
responseTimeMs: number;
|
|
438
438
|
errorType?: string;
|
|
439
439
|
errorMessage?: string;
|
|
440
|
+
/** Low-level transport code such as ETIMEDOUT or EADDRNOTAVAIL. */
|
|
441
|
+
errorCode?: string;
|
|
440
442
|
inputTokens?: number;
|
|
441
443
|
outputTokens?: number;
|
|
442
444
|
cacheCreationTokens?: number;
|
|
@@ -461,6 +463,8 @@ export type RequestAttemptLogEntry = {
|
|
|
461
463
|
responseTimeMs: number;
|
|
462
464
|
errorType?: string;
|
|
463
465
|
errorMessage?: string;
|
|
466
|
+
/** Low-level transport code such as ETIMEDOUT or EADDRNOTAVAIL. */
|
|
467
|
+
errorCode?: string;
|
|
464
468
|
/** Whether this failed attempt may be retried without changing the request. */
|
|
465
469
|
retryable?: boolean;
|
|
466
470
|
/** Distinguishes short-lived admission throttles from exhausted quota windows. */
|
|
@@ -514,6 +518,8 @@ export type AnthropicAttemptLogger = (status: number, errorType?: string, errorM
|
|
|
514
518
|
cacheCreationTokens?: number;
|
|
515
519
|
cacheReadTokens?: number;
|
|
516
520
|
retryable?: boolean;
|
|
521
|
+
/** Low-level transport code such as ETIMEDOUT or EADDRNOTAVAIL. */
|
|
522
|
+
errorCode?: string;
|
|
517
523
|
rateLimitKind?: "transient" | "quota";
|
|
518
524
|
cooldownReason?: "transient" | "session" | "weekly" | "unified";
|
|
519
525
|
/** Override used when one account selection performs an OAuth retry fetch. */
|
|
@@ -945,6 +951,80 @@ export type ProxyRuntimeActivity = {
|
|
|
945
951
|
activeRequests: number;
|
|
946
952
|
lastActivityAt: string | null;
|
|
947
953
|
};
|
|
954
|
+
/** Terminal state observed while the HTTP adapter relays a response body. */
|
|
955
|
+
export type ProxyResponseTerminalOutcome = "completed" | "bodyless" | "client_cancelled" | "stream_error";
|
|
956
|
+
/** Non-blocking callbacks for response lifecycle metadata. */
|
|
957
|
+
export type ProxyResponseTrackingObserver = {
|
|
958
|
+
onFirstChunk?: (details: {
|
|
959
|
+
/** Decoded response-body bytes observed by the adapter. */
|
|
960
|
+
observedBodyBytes: number;
|
|
961
|
+
responseChunks: 1;
|
|
962
|
+
}) => void;
|
|
963
|
+
onTerminal?: (details: {
|
|
964
|
+
outcome: ProxyResponseTerminalOutcome;
|
|
965
|
+
/** Decoded response-body bytes observed by the adapter. */
|
|
966
|
+
observedBodyBytes: number;
|
|
967
|
+
responseChunks: number;
|
|
968
|
+
}) => void;
|
|
969
|
+
};
|
|
970
|
+
/** Versioned lifecycle event names persisted by the proxy adapter. */
|
|
971
|
+
export type ProxyLifecycleEventName = "request_accepted" | "response_headers" | "response_first_chunk" | "request_terminal";
|
|
972
|
+
/** Client-facing terminal classifications recorded by lifecycle metadata. */
|
|
973
|
+
export type ProxyLifecycleTerminalOutcome = ProxyResponseTerminalOutcome | "handler_error";
|
|
974
|
+
/** Content-free lifecycle event accepted by the bounded metadata logger. */
|
|
975
|
+
export type ProxyLifecycleEventInput = {
|
|
976
|
+
event: ProxyLifecycleEventName;
|
|
977
|
+
requestId: string;
|
|
978
|
+
method: string;
|
|
979
|
+
path: string;
|
|
980
|
+
model?: string;
|
|
981
|
+
stream?: boolean;
|
|
982
|
+
toolCount?: number;
|
|
983
|
+
sessionHash?: string;
|
|
984
|
+
requestBytes?: number;
|
|
985
|
+
responseStatus?: number;
|
|
986
|
+
/** Decoded response-body bytes observed by the adapter. */
|
|
987
|
+
observedBodyBytes?: number;
|
|
988
|
+
responseChunks?: number;
|
|
989
|
+
elapsedMs?: number;
|
|
990
|
+
terminalOutcome?: ProxyLifecycleTerminalOutcome;
|
|
991
|
+
errorType?: string;
|
|
992
|
+
errorCode?: string;
|
|
993
|
+
timestampMs?: number;
|
|
994
|
+
monotonicMs?: number;
|
|
995
|
+
};
|
|
996
|
+
/** Data-quality counters for the bounded lifecycle metadata sink. */
|
|
997
|
+
export type ProxyLifecycleLoggerSnapshot = {
|
|
998
|
+
enabled: boolean;
|
|
999
|
+
schemaVersion: number;
|
|
1000
|
+
processInstanceId: string;
|
|
1001
|
+
nextSequence: number;
|
|
1002
|
+
attempted: number;
|
|
1003
|
+
enqueued: number;
|
|
1004
|
+
written: number;
|
|
1005
|
+
dropped: number;
|
|
1006
|
+
queueDrops: number;
|
|
1007
|
+
invalidDrops: number;
|
|
1008
|
+
writeDrops: number;
|
|
1009
|
+
writeFailures: number;
|
|
1010
|
+
pending: number;
|
|
1011
|
+
inFlight: number;
|
|
1012
|
+
flushing: boolean;
|
|
1013
|
+
};
|
|
1014
|
+
/** Lifecycle logger configuration. Queue overrides are used by stress tests. */
|
|
1015
|
+
export type ProxyLifecycleLoggerOptions = {
|
|
1016
|
+
enabled: boolean;
|
|
1017
|
+
logDir?: string;
|
|
1018
|
+
queueCapacity?: number;
|
|
1019
|
+
batchSize?: number;
|
|
1020
|
+
flushIntervalMs?: number;
|
|
1021
|
+
};
|
|
1022
|
+
/** Serialized lifecycle line awaiting a bounded batch write. */
|
|
1023
|
+
export type QueuedProxyLifecycleEvent = {
|
|
1024
|
+
logDir: string;
|
|
1025
|
+
date: string;
|
|
1026
|
+
record: Record<string, unknown>;
|
|
1027
|
+
};
|
|
948
1028
|
/** Request metadata retained by the HTTP adapter for terminal error logging. */
|
|
949
1029
|
export type RuntimeRequestMetadata = {
|
|
950
1030
|
requestId: string;
|
|
@@ -954,6 +1034,8 @@ export type RuntimeRequestMetadata = {
|
|
|
954
1034
|
model: string;
|
|
955
1035
|
stream: boolean;
|
|
956
1036
|
toolCount: number;
|
|
1037
|
+
terminalErrorType?: string;
|
|
1038
|
+
terminalErrorCode?: string;
|
|
957
1039
|
};
|
|
958
1040
|
/** Accumulated upstream body capture from a raw stream. */
|
|
959
1041
|
export type RawStreamCapture = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.93.0",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
|
|
6
6
|
"author": {
|