@juspay/neurolink 12.47.6 → 12.47.8

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.
@@ -125,7 +125,7 @@ export async function createCopilotCliReader() {
125
125
  errors.push({
126
126
  cliId: CLI_ID,
127
127
  filePath: dbPath,
128
- message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (needs Node >=22.13.0; on 22.5-22.12 pass --experimental-sqlite)`,
128
+ message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (unflagged since Node 22.13.0 and 23.4.0; on 22.5.0 to 22.12.x and 23.0 to 23.3 pass --experimental-sqlite)`,
129
129
  });
130
130
  return { cliId: CLI_ID, totals, filesScanned: 0, errors };
131
131
  }
@@ -375,7 +375,7 @@ export async function createCursorReader() {
375
375
  errors.push({
376
376
  cliId: CLI_ID,
377
377
  filePath: chatsRoot(),
378
- message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (needs Node >=22.13.0; on 22.5-22.12 pass --experimental-sqlite)`,
378
+ message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (unflagged since Node 22.13.0 and 23.4.0; on 22.5.0 to 22.12.x and 23.0 to 23.3 pass --experimental-sqlite)`,
379
379
  });
380
380
  return { cliId: CLI_ID, totals, filesScanned: 0, errors };
381
381
  }
@@ -318,7 +318,7 @@ export async function createHermesReader() {
318
318
  errors.push({
319
319
  cliId: CLI_ID,
320
320
  filePath: hermesHome(),
321
- message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (needs Node >=22.13.0; on 22.5-22.12 pass --experimental-sqlite)`,
321
+ message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (unflagged since Node 22.13.0 and 23.4.0; on 22.5.0 to 22.12.x and 23.0 to 23.3 pass --experimental-sqlite)`,
322
322
  });
323
323
  return { cliId: CLI_ID, totals, filesScanned: 0, errors };
324
324
  }
@@ -81,8 +81,8 @@ export async function createOpenCodeReader() {
81
81
  const models = new Set();
82
82
  const dbPath = databasePath();
83
83
  // `node:sqlite` arrived in Node 22.5.0 behind `--experimental-sqlite`, was
84
- // unflagged in 22.13.0 and is still marked experimental, so it can be
85
- // absent or change shape. Imported lazily and behind a try/catch: a
84
+ // unflagged in 22.13.0 and 23.4.0 and is still marked experimental, so it
85
+ // can be absent or change shape. Imported lazily and behind a try/catch: a
86
86
  // runtime without it must degrade to a reported failure for this one
87
87
  // reader, not take down a scan of all the others.
88
88
  let DatabaseSync;
@@ -103,7 +103,7 @@ export async function createOpenCodeReader() {
103
103
  errors.push({
104
104
  cliId: CLI_ID,
105
105
  filePath: dbPath,
106
- message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (needs Node >=22.13.0; on 22.5-22.12 pass --experimental-sqlite)`,
106
+ message: `node:sqlite unavailable on this runtime: ${error instanceof Error ? error.message : String(error)} (unflagged since Node 22.13.0 and 23.4.0; on 22.5.0 to 22.12.x and 23.0 to 23.3 pass --experimental-sqlite)`,
107
107
  });
108
108
  return { cliId: CLI_ID, totals, filesScanned: 0, errors };
109
109
  }
@@ -13,7 +13,7 @@ import { createOAuthFetch } from "../../proxy/oauthFetch.js";
13
13
  import { createProxyFetch } from "../../proxy/proxyFetch.js";
14
14
  import { getCapturedLimitSnapshot, getCapturedResponseHeaders, logClaudeLimitSnapshot, runInLimitCaptureScope, setLimitSpanAttributes, withLimitCapture, wrapFetchWithLimitCapture, } from "./rateLimitCapture.js";
15
15
  import { AuthenticationError, NetworkError, ProviderError, RateLimitError, } from "../../types/index.js";
16
- import { classifyProviderError } from "../../utils/errorClassifier.js";
16
+ import { classifyProviderError, messageNamesStatus, } from "../../utils/errorClassifier.js";
17
17
  import { logger } from "../../utils/logger.js";
18
18
  import { drainDetachedPump } from "../../utils/drainDetachedPump.js";
19
19
  import { ANTHROPIC_ELISION_NOTE, planAnthropicLoopReclaim, previewAnthropicToolResultText, } from "../../context/anthropicLoopGuard.js";
@@ -1442,7 +1442,8 @@ export class AnthropicProvider extends BaseProvider {
1442
1442
  },
1443
1443
  {
1444
1444
  match: (ctx) => ctx.statusCode === 429 ||
1445
- /rate limit|too_many_requests|429/i.test(ctx.message),
1445
+ /rate limit|too_many_requests/i.test(ctx.message) ||
1446
+ messageNamesStatus(ctx.message, 429),
1446
1447
  errorClass: RateLimitError,
1447
1448
  message: "Anthropic rate limit exceeded. Please try again later.",
1448
1449
  },
@@ -1,7 +1,7 @@
1
1
  import { APIVersions } from "../constants/enums.js";
2
2
  import { AuthenticationError } from "../types/index.js";
3
3
  import { logger } from "../utils/logger.js";
4
- import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../utils/errorClassifier.js";
4
+ import { classifyProviderError, DEFAULT_ERROR_RULES, messageNamesStatus, } from "../utils/errorClassifier.js";
5
5
  import { createAzureAPIKeyConfig, createAzureEndpointConfig, validateApiKey, } from "../utils/providerConfig.js";
6
6
  import { transformParamsForLogging } from "../utils/transformationUtils.js";
7
7
  import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
@@ -158,15 +158,20 @@ export class AzureOpenAIProvider extends OpenAIChatCompletionsProvider {
158
158
  return this.azureDeployment;
159
159
  }
160
160
  formatProviderError(error) {
161
- // RULING 1 fix: previously only a raw "401" substring was special-cased —
162
- // everything else (429, 404, network errors, 5xx) fell through to one
163
- // generic ProviderError. The 401 branch below is preserved byte-for-byte
164
- // (message text + the raw-substring match style); DEFAULT_ERROR_RULES now
165
- // gives 429/404/network/5xx their correct classifications instead of the
166
- // old undifferentiated catch-all.
161
+ // RULING 1 fix: previously only a "401" in the message text was
162
+ // special-cased — everything else (429, 404, network errors, 5xx) fell
163
+ // through to one generic ProviderError. The 401 branch below keeps its
164
+ // message text byte-for-byte; DEFAULT_ERROR_RULES now gives
165
+ // 429/404/network/5xx their correct classifications instead of the old
166
+ // undifferentiated catch-all. The branch still reads the text, not the
167
+ // structured status, so a 401 whose message names no status still falls
168
+ // through to DEFAULT_ERROR_RULES as before. What changed is the match: a
169
+ // status-shaped "401" ("status code 401", a leading "401 ...") rather than
170
+ // any three digits, so a request id or token count no longer reads as an
171
+ // auth failure.
167
172
  const rules = [
168
173
  {
169
- match: (ctx) => /401/.test(ctx.message),
174
+ match: (ctx) => messageNamesStatus(ctx.message, 401),
170
175
  errorClass: AuthenticationError,
171
176
  message: "Invalid Azure OpenAI API key or endpoint.",
172
177
  },
@@ -15,7 +15,7 @@ import { isSchemaComplexityError } from "../../core/modules/structuredOutputPoli
15
15
  import { redactUrlForError, stringifyContentSafe, } from "../../utils/logSanitize.js";
16
16
  import { googleSdkProxyHttpOptions } from "../../proxy/proxyFetch.js";
17
17
  import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../../types/index.js";
18
- import { classifyProviderError } from "../../utils/errorClassifier.js";
18
+ import { classifyProviderError, messageNamesStatus, } from "../../utils/errorClassifier.js";
19
19
  import { ERROR_CODES, NeuroLinkError } from "../../utils/errorHandling.js";
20
20
  import { applyVertexAnthropicCacheBreakpoints } from "../../utils/anthropicCacheBreakpoints.js";
21
21
  import { FileDetector } from "../../utils/fileDetector.js";
@@ -6523,7 +6523,8 @@ export class GoogleVertexProvider extends BaseProvider {
6523
6523
  // exhaustion surfaces as overloaded_error (HTTP 529) — same
6524
6524
  // operational meaning as a 429, so classify it here instead of the
6525
6525
  // generic 5xx branch below.
6526
- match: (ctx) => /QUOTA_EXCEEDED|RATE_LIMIT_EXCEEDED|rate limit|429/i.test(ctx.message) ||
6526
+ match: (ctx) => /QUOTA_EXCEEDED|RATE_LIMIT_EXCEEDED|rate limit/i.test(ctx.message) ||
6527
+ messageNamesStatus(ctx.message, 429) ||
6527
6528
  statusCode === 429 ||
6528
6529
  statusCode === 529 ||
6529
6530
  /overloaded/i.test(ctx.message),
@@ -6564,7 +6565,8 @@ export class GoogleVertexProvider extends BaseProvider {
6564
6565
  message: (ctx) => `Connection error: ${ctx.message}`,
6565
6566
  },
6566
6567
  {
6567
- match: (ctx) => /500|502|503|504|server error|Internal Server Error|INTERNAL|UNAVAILABLE/i.test(ctx.message) ||
6568
+ match: (ctx) => /server error|Internal Server Error|INTERNAL|UNAVAILABLE/i.test(ctx.message) ||
6569
+ [500, 502, 503, 504].some((code) => messageNamesStatus(ctx.message, code)) ||
6568
6570
  (statusCode !== undefined && statusCode >= 500 && statusCode < 600),
6569
6571
  errorClass: ProviderError,
6570
6572
  message: (ctx) => `Google Vertex AI server error: ${ctx.message}. Please try again later.`,
@@ -1,6 +1,6 @@
1
1
  import { AIProviderName, NvidiaNimModels } from "../../constants/enums.js";
2
2
  import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../../types/index.js";
3
- import { classifyProviderError, DEFAULT_ERROR_RULES, } from "../../utils/errorClassifier.js";
3
+ import { classifyProviderError, DEFAULT_ERROR_RULES, messageNamesStatus, } from "../../utils/errorClassifier.js";
4
4
  import { logger } from "../../utils/logger.js";
5
5
  import { redactUrlCredentials } from "../../utils/logSanitize.js";
6
6
  import { createNvidiaNimConfig, getProviderModel, validateApiKey, } from "../../utils/providerConfig.js";
@@ -260,24 +260,28 @@ export class NvidiaNimProvider extends OpenAIChatCompletionsProvider {
260
260
  // parameter retry (K5) need the original "Bad Request" message to
261
261
  // surface.
262
262
  {
263
- match: (ctx) => /Invalid API key|401|Unauthorized/.test(ctx.message),
263
+ match: (ctx) => /Invalid API key|Unauthorized/.test(ctx.message) ||
264
+ messageNamesStatus(ctx.message, 401),
264
265
  errorClass: AuthenticationError,
265
266
  message: "Invalid NVIDIA NIM API key. Get one at https://build.nvidia.com/settings/api-keys",
266
267
  },
267
268
  {
268
- match: (ctx) => /rate limit|429/.test(ctx.message),
269
+ match: (ctx) => /rate limit/.test(ctx.message) ||
270
+ messageNamesStatus(ctx.message, 429),
269
271
  errorClass: RateLimitError,
270
272
  message: "NVIDIA NIM rate limit exceeded",
271
273
  },
272
274
  {
273
275
  // NIM answers most of its roster with a 404 whose text ("Function …
274
276
  // not found for account …") names no model, so the status decides.
275
- match: (ctx) => ctx.statusCode === 404 || /404|model_not_found/.test(ctx.message),
277
+ match: (ctx) => ctx.statusCode === 404 ||
278
+ /model_not_found/.test(ctx.message) ||
279
+ messageNamesStatus(ctx.message, 404),
276
280
  errorClass: InvalidModelError,
277
281
  message: () => `NVIDIA NIM model '${this.modelName}' not available. Browse the catalog at https://build.nvidia.com/models`,
278
282
  },
279
283
  {
280
- match: (ctx) => /quota|403/.test(ctx.message),
284
+ match: (ctx) => /quota/.test(ctx.message) || messageNamesStatus(ctx.message, 403),
281
285
  errorClass: ProviderError,
282
286
  message: "NVIDIA NIM quota exceeded for your account",
283
287
  },
@@ -21,6 +21,27 @@ import { type ProviderErrorRule } from "../types/index.js";
21
21
  * it identically before this change, so it is not made overridable.
22
22
  */
23
23
  export declare function classifyProviderError(error: unknown, rules: ProviderErrorRule[], provider: string, modelName?: string): Error;
24
+ /**
25
+ * True when `status` is written in `message` as an HTTP status, as opposed to
26
+ * an unrelated number that happens to have the same digits ("max_tokens (429)
27
+ * exceeds the model limit", a request id, a token count). Provider rules that
28
+ * once tested the bare digits (`/429/`) classified such a 400 as a rate limit.
29
+ *
30
+ * Status-shaped means one of:
31
+ * - the text starts with it, the way some SDKs format an API error
32
+ * ("429 {...}", "401 Unauthorized");
33
+ * - it follows "status", "status code" or "HTTP" ("Request failed with status
34
+ * code 429", "HTTP/1.1 429");
35
+ * - it sits next to the word "error" in either order ("error 429", "429 error",
36
+ * and the nested JSON body `{"error":{"code":429`).
37
+ *
38
+ * The gap between the word and the number is bounded and holds no digit, so
39
+ * unrelated digits further along cannot bridge the match. This is the shape the
40
+ * shared 5xx rule in `DEFAULT_ERROR_RULES` already uses, so the two stay in
41
+ * step. It is a heuristic over text: a rule that can read the structured
42
+ * status should still check `ctx.statusCode` first.
43
+ */
44
+ export declare function messageNamesStatus(message: string, status: number): boolean;
24
45
  /**
25
46
  * Generic fallback rule table covering the five categories every
26
47
  * OpenAI-compatible provider already hand-rolled near-identically:
@@ -127,6 +127,33 @@ const MODEL_WORD = `\\bmodel\\b${NOT_A_ROUTE_MODIFIER}`;
127
127
  const MODEL_OR_DEPLOYMENT = `\\b(?:model|deployment)\\b${NOT_A_ROUTE_MODIFIER}`;
128
128
  const NAMED_MODEL_ERROR = `(?:unknown|no such|invalid|unsupported) model${NOT_A_ROUTE_MODIFIER}`;
129
129
  const MODEL_404_TEXT = new RegExp(`model[_ ]?not[_ ]?found|${NAMED_MODEL_ERROR}|${MODEL_OR_DEPLOYMENT}.{0,120}\\b(?:does not exist|not found|unavailable|not (?:available|supported))\\b|\\b(?:does not exist|not found)\\b.{0,120}${MODEL_OR_DEPLOYMENT}|unable to access.{0,60}${MODEL_WORD}`, "i");
130
+ /**
131
+ * True when `status` is written in `message` as an HTTP status, as opposed to
132
+ * an unrelated number that happens to have the same digits ("max_tokens (429)
133
+ * exceeds the model limit", a request id, a token count). Provider rules that
134
+ * once tested the bare digits (`/429/`) classified such a 400 as a rate limit.
135
+ *
136
+ * Status-shaped means one of:
137
+ * - the text starts with it, the way some SDKs format an API error
138
+ * ("429 {...}", "401 Unauthorized");
139
+ * - it follows "status", "status code" or "HTTP" ("Request failed with status
140
+ * code 429", "HTTP/1.1 429");
141
+ * - it sits next to the word "error" in either order ("error 429", "429 error",
142
+ * and the nested JSON body `{"error":{"code":429`).
143
+ *
144
+ * The gap between the word and the number is bounded and holds no digit, so
145
+ * unrelated digits further along cannot bridge the match. This is the shape the
146
+ * shared 5xx rule in `DEFAULT_ERROR_RULES` already uses, so the two stay in
147
+ * step. It is a heuristic over text: a rule that can read the structured
148
+ * status should still check `ctx.statusCode` first.
149
+ */
150
+ export function messageNamesStatus(message, status) {
151
+ if (!Number.isInteger(status) || status < 100 || status > 599) {
152
+ return false;
153
+ }
154
+ const code = String(status);
155
+ return new RegExp(`^\\s*${code}\\b|\\b(?:error|status(?:\\s*code)?)\\b\\D{0,12}\\b${code}\\b|\\bhttp(?:/\\d(?:\\.\\d)?)?[\\s:-]{0,3}${code}\\b|\\b${code}\\b\\D{0,12}\\berror\\b`, "i").test(message);
156
+ }
130
157
  /**
131
158
  * Generic fallback rule table covering the five categories every
132
159
  * OpenAI-compatible provider already hand-rolled near-identically:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.47.6",
3
+ "version": "12.47.8",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {
@@ -80,7 +80,6 @@
80
80
  "test:client": "pnpm exec tsx test/continuous-test-suite-client.ts",
81
81
  "test:context": "pnpm exec tsx test/continuous-test-suite-context.ts",
82
82
  "test:evaluation": "pnpm exec tsx test/continuous-test-suite-evaluation.ts",
83
- "test:handler-registry": "pnpm exec tsx test/continuous-test-suite-handler-registry.ts",
84
83
  "test:harness-offline-timeout": "pnpm exec tsx test/continuous-test-suite-harness-offline-timeout.ts",
85
84
  "test:resolve-request-kind": "pnpm exec tsx test/continuous-test-suite-resolve-request-kind.ts",
86
85
  "test:mcp": "pnpm exec tsx test/continuous-test-suite-mcp-infra.ts",