@juspay/neurolink 12.9.0 → 12.9.1

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.
@@ -26,7 +26,7 @@ import { ProxyRuntimeConfigStore } from "../../proxy/runtimeConfig.js";
26
26
  import { startProxyLogCleanupScheduler } from "../../proxy/logCleanupScheduler.js";
27
27
  import { anthropicAccountKeysEqual, createAccountAllowlist, isAccountAllowed, LEGACY_ANTHROPIC_ACCOUNT_KEY, normalizeAnthropicAccountKey, shouldLoadFallbackCredential, } from "../../proxy/accountSelection.js";
28
28
  import { resolveProxyStatusAccountIdentity } from "../../proxy/codexAccountUsage.js";
29
- import { beginProxyRequest, getProxyActivitySnapshot, trackProxyResponse, } from "../../proxy/proxyActivity.js";
29
+ import { beginProxyRequest, getProxyActivitySnapshot, takeProxyResponseObservers, trackProxyResponse, } from "../../proxy/proxyActivity.js";
30
30
  import { flushProxyLifecycleEvents, getProxyLifecycleLoggerSnapshot, hashProxyLifecycleSessionId, logProxyLifecycleEvent, } from "../../proxy/proxyLifecycle.js";
31
31
  import { describeInstallFailure, getGlobalInstallArgs, isTransientInstallFailure, resolveGlobalInstaller, validateInstalledVersion, } from "../../proxy/globalInstaller.js";
32
32
  import { startUpdaterWorkerSupervisor } from "../../proxy/updaterSupervisor.js";
@@ -1120,6 +1120,27 @@ function registerProxyRequestTracking(app, requestMetadata, readiness) {
1120
1120
  responseStatus,
1121
1121
  elapsedMs: performance.now() - startedMonotonicMs,
1122
1122
  });
1123
+ const routeResponseObservers = takeProxyResponseObservers(metadata);
1124
+ const notifyRouteFirstChunk = (details) => {
1125
+ for (const observer of routeResponseObservers) {
1126
+ try {
1127
+ observer.onFirstChunk?.(details);
1128
+ }
1129
+ catch {
1130
+ // Route-level accounting must never interfere with the relay.
1131
+ }
1132
+ }
1133
+ };
1134
+ const notifyRouteTerminal = (details) => {
1135
+ for (const observer of routeResponseObservers) {
1136
+ try {
1137
+ observer.onTerminal?.(details);
1138
+ }
1139
+ catch {
1140
+ // Route-level accounting must never interfere with the relay.
1141
+ }
1142
+ }
1143
+ };
1123
1144
  c.res = trackProxyResponse(c.res, finish, {
1124
1145
  onFirstChunk: ({ observedBodyBytes, responseChunks }) => {
1125
1146
  logProxyLifecycleEvent({
@@ -1137,6 +1158,10 @@ function registerProxyRequestTracking(app, requestMetadata, readiness) {
1137
1158
  responseChunks,
1138
1159
  elapsedMs: performance.now() - startedMonotonicMs,
1139
1160
  });
1161
+ notifyRouteFirstChunk({
1162
+ observedBodyBytes,
1163
+ responseChunks,
1164
+ });
1140
1165
  },
1141
1166
  onTerminal: ({ outcome, observedBodyBytes, responseChunks }) => {
1142
1167
  logProxyLifecycleEvent({
@@ -1157,6 +1182,11 @@ function registerProxyRequestTracking(app, requestMetadata, readiness) {
1157
1182
  errorType: metadata.terminalErrorType,
1158
1183
  errorCode: metadata.terminalErrorCode,
1159
1184
  });
1185
+ notifyRouteTerminal({
1186
+ outcome,
1187
+ observedBodyBytes,
1188
+ responseChunks,
1189
+ });
1160
1190
  },
1161
1191
  });
1162
1192
  }
@@ -1484,7 +1514,10 @@ export async function createProxyStartApp(params) {
1484
1514
  neurolink: params.neurolink,
1485
1515
  toolRegistry: params.neurolink.getToolRegistry(),
1486
1516
  timestamp: Date.now(),
1487
- metadata: {},
1517
+ // Keep route terminal observers on the runtime request metadata so the
1518
+ // outer response tracker can notify them without adding a second body
1519
+ // wrapper to streaming routes.
1520
+ metadata: (metadata ?? {}),
1488
1521
  // Route handlers publish limit/quota headers here. Only the streaming
1489
1522
  // paths build their own Response (and set headers directly); every
1490
1523
  // JSON and error path returns a plain object, so without this the
@@ -1,4 +1,6 @@
1
1
  import type { ProxyActivitySnapshot, ProxyResponseTrackingObserver } from "../types/index.js";
2
+ export declare function registerProxyResponseObserver(metadata: object, observer: ProxyResponseTrackingObserver): void;
3
+ export declare function takeProxyResponseObservers(metadata: object): ProxyResponseTrackingObserver[];
2
4
  /** Track one client-facing proxy request until its response body settles. */
3
5
  export declare function beginProxyRequest(): () => void;
4
6
  export declare function getProxyActivitySnapshot(): ProxyActivitySnapshot;
@@ -3,6 +3,24 @@ import { logger } from "../utils/logger.js";
3
3
  const PROXY_RESPONSE_CANCEL_TIMEOUT_MS = 1_000;
4
4
  let activeRequests = 0;
5
5
  let lastActivityAtMs = null;
6
+ // Route handlers can attach terminal observers to their request context without
7
+ // wrapping the response body a second time. The HTTP runtime drains every
8
+ // response through one tracker, which fans these observers out at the point
9
+ // where bytes actually leave the proxy.
10
+ const responseObserversByMetadata = new WeakMap();
11
+ export function registerProxyResponseObserver(metadata, observer) {
12
+ const existing = responseObserversByMetadata.get(metadata);
13
+ if (existing) {
14
+ existing.push(observer);
15
+ return;
16
+ }
17
+ responseObserversByMetadata.set(metadata, [observer]);
18
+ }
19
+ export function takeProxyResponseObservers(metadata) {
20
+ const observers = responseObserversByMetadata.get(metadata) ?? [];
21
+ responseObserversByMetadata.delete(metadata);
22
+ return observers;
23
+ }
6
24
  function touchActivity() {
7
25
  lastActivityAtMs = Date.now();
8
26
  }
@@ -24,7 +24,7 @@ import { loadAccountQuotas, saveAccountQuota, } from "../../proxy/accountQuota.j
24
24
  import { createCodexUsageTap } from "../../proxy/codexUsage.js";
25
25
  import { CODEX_ACCOUNT_PREFIX, parseCodexRateLimitHeaders, } from "../../proxy/codexAccountUsage.js";
26
26
  import { buildClientAttribution } from "../../proxy/clientAttribution.js";
27
- import { trackProxyResponse } from "../../proxy/proxyActivity.js";
27
+ import { registerProxyResponseObserver } from "../../proxy/proxyActivity.js";
28
28
  import { logRequest, logRequestAttempt } from "../../proxy/requestLogger.js";
29
29
  import { parseRetryAfterMs } from "../../proxy/routingPolicy.js";
30
30
  import { recordAttempt, recordAttemptError, recordFinalError, recordFinalSuccess, } from "../../proxy/usageStats.js";
@@ -459,7 +459,7 @@ export async function handleCodexResponsesRequest(ctx) {
459
459
  status: upstream.status,
460
460
  headers,
461
461
  });
462
- return trackProxyResponse(relay, () => undefined, {
462
+ registerProxyResponseObserver(ctx.metadata, {
463
463
  onTerminal: ({ outcome }) => {
464
464
  void usageSeen
465
465
  .then((usage) => {
@@ -491,6 +491,7 @@ export async function handleCodexResponsesRequest(ctx) {
491
491
  .catch(() => undefined);
492
492
  },
493
493
  });
494
+ return relay;
494
495
  }
495
496
  const errText = await upstream.text().catch(() => "");
496
497
  // 401/403 → try a forced token refresh once, then rotate.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.9.0",
3
+ "version": "12.9.1",
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": {