@juspay/neurolink 13.0.3 → 13.1.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 +10 -2
- package/dist/browser/neurolink.min.js +419 -419
- package/dist/constants/enums.d.ts +2 -1
- package/dist/constants/enums.js +1 -0
- package/dist/localUsage/grokReader.js +6 -2
- package/dist/mcp/mcpRegistryClient.d.ts +70 -8
- package/dist/mcp/mcpRegistryClient.js +313 -23
- package/dist/mcp/mcpRegistryRemote.d.ts +50 -0
- package/dist/mcp/mcpRegistryRemote.js +289 -0
- package/dist/middleware/utils/guardrailsUtils.js +21 -9
- package/dist/middleware/utils/regexSafety.d.ts +46 -0
- package/dist/middleware/utils/regexSafety.js +483 -0
- package/dist/providers/catalog/provider-catalog.schema.json +1 -1
- package/dist/providers/catalog/reka.json +15 -5
- package/dist/providers/cloudflareClef.js +5 -0
- package/dist/providers/decide/baseUrl.d.ts +8 -0
- package/dist/providers/decide/baseUrl.js +31 -0
- package/dist/providers/perplexityDecider.js +5 -0
- package/dist/providers/systemOneDecision.js +27 -3
- package/dist/types/guardrails.d.ts +54 -0
- package/dist/types/mcp.d.ts +105 -9
- package/dist/types/providerCatalog.d.ts +8 -7
- package/docs-site/static/search-index.json +52 -45
- package/package.json +8 -4
|
@@ -3,6 +3,7 @@ import { sniffImageMimeType } from "../utils/imageDetection.js";
|
|
|
3
3
|
import { logger } from "../utils/logger.js";
|
|
4
4
|
import { redactUrlForError } from "../utils/logSanitize.js";
|
|
5
5
|
import { getProviderModel } from "../utils/providerConfig.js";
|
|
6
|
+
import { cleartextBaseURLProblem } from "./decide/baseUrl.js";
|
|
6
7
|
import { isRecord, redactCredentials, SystemOneDecisionProvider, } from "./systemOneDecision.js";
|
|
7
8
|
const PERPLEXITY_DEFAULT_BASE_URL = "https://api.perplexity.ai";
|
|
8
9
|
const PERPLEXITY_ROUTE = "/v1/decisions";
|
|
@@ -50,6 +51,10 @@ function baseURLProblem(baseURL) {
|
|
|
50
51
|
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
51
52
|
return `The Perplexity base URL must start with https:// or http://. ${fix}`;
|
|
52
53
|
}
|
|
54
|
+
const cleartext = cleartextBaseURLProblem(url, "Perplexity", fix);
|
|
55
|
+
if (cleartext) {
|
|
56
|
+
return cleartext;
|
|
57
|
+
}
|
|
53
58
|
return url.username || url.password || url.search || url.hash
|
|
54
59
|
? `The Perplexity base URL must not carry credentials, a query string or a fragment. ${fix}`
|
|
55
60
|
: undefined;
|
|
@@ -56,6 +56,21 @@ export function redactCredentials(message, apiKey) {
|
|
|
56
56
|
.replace(/data:[a-z]+\/[a-z0-9.+-]+;base64,[A-Za-z0-9+/=]+/gi, "[media]")
|
|
57
57
|
.trim();
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether a failed `fetch` was one that refused a redirect. undici words every
|
|
61
|
+
* transport failure as `TypeError: fetch failed` and puts the reason on
|
|
62
|
+
* `cause`, so the chain is walked for its "unexpected redirect" text.
|
|
63
|
+
*/
|
|
64
|
+
function isRedirectRefusal(error) {
|
|
65
|
+
let current = error;
|
|
66
|
+
for (let depth = 0; depth < 5 && current instanceof Error; depth++) {
|
|
67
|
+
if (current.message === "unexpected redirect") {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
current = current.cause;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
59
74
|
/**
|
|
60
75
|
* Rejects with the signal's reason when it aborts, so a caller tearing a turn
|
|
61
76
|
* down is not held, nor its process kept alive, for the length of a vendor's
|
|
@@ -261,6 +276,9 @@ export class SystemOneDecisionProvider extends BaseProvider {
|
|
|
261
276
|
headers: this.decisionHeaders(),
|
|
262
277
|
body,
|
|
263
278
|
signal,
|
|
279
|
+
// A 307 or 308 would replay the decision body at whatever address
|
|
280
|
+
// the server names.
|
|
281
|
+
redirect: "error",
|
|
264
282
|
});
|
|
265
283
|
const latencyMs = Date.now() - startedAt;
|
|
266
284
|
const requestId = this.readRequestId(response.headers);
|
|
@@ -351,12 +369,18 @@ export class SystemOneDecisionProvider extends BaseProvider {
|
|
|
351
369
|
// turn is already being torn down.
|
|
352
370
|
const aborted = request.signal?.aborted === true;
|
|
353
371
|
const isTimeout = error instanceof Error && error.name === "TimeoutError";
|
|
372
|
+
const redirected = isRedirectRefusal(error);
|
|
354
373
|
lastError = {
|
|
355
374
|
kind: aborted ? "network" : isTimeout ? "timeout" : "network",
|
|
356
|
-
|
|
357
|
-
|
|
375
|
+
// `fetch` words a refused redirect as "fetch failed", which says
|
|
376
|
+
// nothing about why, and asking again meets the same answer. The
|
|
377
|
+
// address it points at is the server's to choose, so it is not quoted.
|
|
378
|
+
message: redirected
|
|
379
|
+
? `${label} answered with a redirect, which is refused so the decision is not sent anywhere else. Point the base URL at the final address.`
|
|
380
|
+
: redactCredentials(redactUrlsInText(error instanceof Error ? error.message : String(error)), this.decisionApiKey()),
|
|
381
|
+
retryable: !aborted && !redirected,
|
|
358
382
|
};
|
|
359
|
-
if (
|
|
383
|
+
if (!lastError.retryable || attempt === this.maxRetries) {
|
|
360
384
|
throw this.decisionError(lastError);
|
|
361
385
|
}
|
|
362
386
|
await sleep(2 ** attempt * 250 + Math.random() * 250);
|
|
@@ -50,6 +50,13 @@ export type PrecallEvaluationConfig = {
|
|
|
50
50
|
* - Phone: '\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b'
|
|
51
51
|
* - SSN: '\\b\\d{3}-\\d{2}-\\d{4}\\b'
|
|
52
52
|
* - Custom words: '\\b(word1|word2|word3)\\b'
|
|
53
|
+
*
|
|
54
|
+
* A pattern is refused, skipped and logged as an error (never thrown at the
|
|
55
|
+
* caller) when it is longer than 1000 characters or has a shape that can
|
|
56
|
+
* backtrack catastrophically: a repeated group with a variable-length part
|
|
57
|
+
* that can also match what follows it, a repetition bound above 500, or
|
|
58
|
+
* overlapping alternatives under a repetition. See
|
|
59
|
+
* `BadWordsConfig.regexPatterns`.
|
|
53
60
|
*/
|
|
54
61
|
sanitizationPatterns?: string[];
|
|
55
62
|
/**
|
|
@@ -67,6 +74,37 @@ export type PrecallEvaluationConfig = {
|
|
|
67
74
|
export type BadWordsConfig = {
|
|
68
75
|
enabled?: boolean;
|
|
69
76
|
list?: string[];
|
|
77
|
+
/**
|
|
78
|
+
* Regular expressions to redact, each applied with the `gi` flags. When set
|
|
79
|
+
* they take priority over `list`.
|
|
80
|
+
*
|
|
81
|
+
* Patterns run synchronously on the whole response, so a pattern that can
|
|
82
|
+
* backtrack catastrophically would stall the process. A pattern is refused,
|
|
83
|
+
* skipped and logged as an error (the same handling as an invalid regex,
|
|
84
|
+
* never thrown at the caller) when it:
|
|
85
|
+
*
|
|
86
|
+
* - is longer than 1000 characters;
|
|
87
|
+
* - repeats a group in which a variable-length part (`+`, `*`, `?`, `{n,m}`)
|
|
88
|
+
* can match the same text as whatever follows it, so the input can be
|
|
89
|
+
* split many ways: `(a+)+`, `(\w+\s*)*`, `(.*a){10}`, `(a?a?)+`,
|
|
90
|
+
* `(a{1,30}){1,30}`;
|
|
91
|
+
* - repeats a group whose alternatives may overlap, such as `(a|aa)+`;
|
|
92
|
+
* - chains more than a handful of overlapping optional or open-ended parts,
|
|
93
|
+
* such as `a?a?a?a?a?a?a?a?a?` or `.*a.*b.*c.*d.*e.*f`;
|
|
94
|
+
* - has a repetition bound above 500, such as `a{1,1000}`.
|
|
95
|
+
*
|
|
96
|
+
* A repeated group is fine when each variable-length part is followed by
|
|
97
|
+
* something it cannot match: `(\d{1,3}\.){3}`, `([a-z0-9-]+\.)+` and
|
|
98
|
+
* `(?:\d[ -]?){13,16}` all run.
|
|
99
|
+
*
|
|
100
|
+
* A skipped pattern filters nothing. Use `list` for plain terms, or flatten
|
|
101
|
+
* the pattern (`[\w-]+` instead of `([\w-]+)+`).
|
|
102
|
+
*
|
|
103
|
+
* Example patterns:
|
|
104
|
+
* - SSN: '\\d{3}-\\d{2}-\\d{4}'
|
|
105
|
+
* - Card number: '\\d{16}'
|
|
106
|
+
* - Words: '\\b(password|secret)\\b'
|
|
107
|
+
*/
|
|
70
108
|
regexPatterns?: string[];
|
|
71
109
|
/**
|
|
72
110
|
* Text to use when replacing filtered content.
|
|
@@ -109,8 +147,24 @@ export type ContentFilteringResult = {
|
|
|
109
147
|
hasChanges: boolean;
|
|
110
148
|
appliedFilters: string[];
|
|
111
149
|
filteringStats: {
|
|
150
|
+
/** Regex patterns that were compiled and run. */
|
|
112
151
|
regexPatternsApplied: number;
|
|
152
|
+
/**
|
|
153
|
+
* Regex patterns refused before they ran because they were too long or
|
|
154
|
+
* could backtrack catastrophically. Invalid patterns are not counted.
|
|
155
|
+
*/
|
|
156
|
+
regexPatternsRejected?: number;
|
|
113
157
|
stringFiltersApplied: number;
|
|
114
158
|
totalMatches: number;
|
|
115
159
|
};
|
|
116
160
|
};
|
|
161
|
+
/**
|
|
162
|
+
* Outcome of vetting a caller-supplied regular expression before it is
|
|
163
|
+
* compiled and run against model output.
|
|
164
|
+
*/
|
|
165
|
+
export type RegexPatternVerdict = {
|
|
166
|
+
safe: true;
|
|
167
|
+
} | {
|
|
168
|
+
safe: false;
|
|
169
|
+
reason: string;
|
|
170
|
+
};
|
package/dist/types/mcp.d.ts
CHANGED
|
@@ -1319,25 +1319,42 @@ export type RegistryConfig = {
|
|
|
1319
1319
|
*/
|
|
1320
1320
|
type: RegistrySourceType;
|
|
1321
1321
|
/**
|
|
1322
|
-
*
|
|
1322
|
+
* Base URL of the registry. `official` defaults to
|
|
1323
|
+
* `https://registry.modelcontextprotocol.io`; `custom` requires it. The
|
|
1324
|
+
* client appends `/v0.1/servers`, so pass the origin (a trailing `/v0.1`
|
|
1325
|
+
* is accepted too). Only `http:` and `https:` URLs without embedded
|
|
1326
|
+
* credentials are used.
|
|
1323
1327
|
*/
|
|
1324
1328
|
url?: string;
|
|
1325
1329
|
/**
|
|
1326
|
-
*
|
|
1330
|
+
* Bearer token sent as `Authorization: Bearer <token>` to this registry
|
|
1331
|
+
* only. It is never emitted in events or logs. A registry with a token is
|
|
1332
|
+
* queried only over `https:` or on a loopback host (`localhost`,
|
|
1333
|
+
* `127.0.0.0/8`, `[::1]`); a plain `http:` URL to any other host is skipped
|
|
1334
|
+
* so the token never crosses the network in clear.
|
|
1327
1335
|
*/
|
|
1328
1336
|
authToken?: string;
|
|
1329
1337
|
/**
|
|
1330
|
-
* Request timeout in milliseconds
|
|
1338
|
+
* Request timeout in milliseconds. Overrides the client-wide `timeout`
|
|
1339
|
+
* for this registry.
|
|
1331
1340
|
*/
|
|
1332
1341
|
timeout?: number;
|
|
1333
1342
|
/**
|
|
1334
|
-
*
|
|
1343
|
+
* Set to `false` to turn off caching of this registry's responses. It
|
|
1344
|
+
* cannot override a client-wide `enableCache: false`, which turns caching
|
|
1345
|
+
* off for every registry.
|
|
1335
1346
|
*/
|
|
1336
1347
|
enableCache?: boolean;
|
|
1337
1348
|
/**
|
|
1338
|
-
* Cache TTL in milliseconds
|
|
1349
|
+
* Cache TTL in milliseconds for this registry's responses. Falls back to
|
|
1350
|
+
* the client-wide `defaultCacheTTL`.
|
|
1339
1351
|
*/
|
|
1340
1352
|
cacheTTL?: number;
|
|
1353
|
+
/**
|
|
1354
|
+
* Maximum number of list pages (100 servers each) fetched when this
|
|
1355
|
+
* registry is merged into `search()` / `getAllEntries()`. Defaults to 5.
|
|
1356
|
+
*/
|
|
1357
|
+
maxPages?: number;
|
|
1341
1358
|
};
|
|
1342
1359
|
/**
|
|
1343
1360
|
* Search options for registry queries
|
|
@@ -1395,11 +1412,24 @@ export type RegistrySearchResult = {
|
|
|
1395
1412
|
*/
|
|
1396
1413
|
export type MCPRegistryClientConfig = {
|
|
1397
1414
|
/**
|
|
1398
|
-
*
|
|
1415
|
+
* Registries to consult when `remoteLookup` is on. Only `official` and
|
|
1416
|
+
* `custom` registries are queried; `npm` and `github` entries are accepted
|
|
1417
|
+
* for compatibility but skipped. Defaults to the official MCP Registry.
|
|
1399
1418
|
*/
|
|
1400
1419
|
registries?: RegistryConfig[];
|
|
1401
1420
|
/**
|
|
1402
|
-
*
|
|
1421
|
+
* Opt in to remote lookups. Default `false`: the client then reads only
|
|
1422
|
+
* its built-in catalog and custom entries and never touches the network,
|
|
1423
|
+
* whatever `registries` says. When `true`, `getEntry()` falls back to the
|
|
1424
|
+
* configured registries on a local miss and `search()` / `getAllEntries()`
|
|
1425
|
+
* merge their servers after the local ones. Remote entries are always
|
|
1426
|
+
* `verified: false`, and any network or parse failure falls back to the
|
|
1427
|
+
* local result instead of throwing.
|
|
1428
|
+
*/
|
|
1429
|
+
remoteLookup?: boolean;
|
|
1430
|
+
/**
|
|
1431
|
+
* Enable automatic caching. `false` turns it off for the merged list and
|
|
1432
|
+
* for every registry, including one that sets its own `enableCache: true`.
|
|
1403
1433
|
*/
|
|
1404
1434
|
enableCache?: boolean;
|
|
1405
1435
|
/**
|
|
@@ -1407,14 +1437,80 @@ export type MCPRegistryClientConfig = {
|
|
|
1407
1437
|
*/
|
|
1408
1438
|
defaultCacheTTL?: number;
|
|
1409
1439
|
/**
|
|
1410
|
-
* Request timeout
|
|
1440
|
+
* Request timeout in milliseconds for each remote registry request
|
|
1441
|
+
* (default 10000). A registry's own `timeout` takes precedence.
|
|
1411
1442
|
*/
|
|
1412
1443
|
timeout?: number;
|
|
1413
1444
|
/**
|
|
1414
|
-
* User
|
|
1445
|
+
* User-Agent header sent on every remote registry request
|
|
1415
1446
|
*/
|
|
1416
1447
|
userAgent?: string;
|
|
1417
1448
|
};
|
|
1449
|
+
/**
|
|
1450
|
+
* An input (environment variable or header) declared by a registry record.
|
|
1451
|
+
* Mirrors the `KeyValueInput` schema of the official MCP Registry API.
|
|
1452
|
+
*/
|
|
1453
|
+
export type RemoteRegistryInput = {
|
|
1454
|
+
name: string;
|
|
1455
|
+
isRequired: boolean;
|
|
1456
|
+
isSecret: boolean;
|
|
1457
|
+
};
|
|
1458
|
+
/**
|
|
1459
|
+
* Transport declared by a registry record (`stdio`, `streamable-http` or
|
|
1460
|
+
* `sse`). `url` is present for the remote transports.
|
|
1461
|
+
*/
|
|
1462
|
+
export type RemoteRegistryTransport = {
|
|
1463
|
+
type: string;
|
|
1464
|
+
url?: string;
|
|
1465
|
+
};
|
|
1466
|
+
/**
|
|
1467
|
+
* A package a registry record can be installed from (`npm`, `pypi`, `oci`, ...)
|
|
1468
|
+
*/
|
|
1469
|
+
export type RemoteRegistryPackage = {
|
|
1470
|
+
registryType: string;
|
|
1471
|
+
identifier: string;
|
|
1472
|
+
version?: string;
|
|
1473
|
+
transport?: RemoteRegistryTransport;
|
|
1474
|
+
environmentVariables: RemoteRegistryInput[];
|
|
1475
|
+
};
|
|
1476
|
+
/**
|
|
1477
|
+
* Server description inside a registry record. Mirrors the `ServerDetail`
|
|
1478
|
+
* schema of the official MCP Registry API, reduced to the fields the client
|
|
1479
|
+
* reads; everything else in the response is ignored.
|
|
1480
|
+
*/
|
|
1481
|
+
export type RemoteRegistryServer = {
|
|
1482
|
+
name: string;
|
|
1483
|
+
description: string;
|
|
1484
|
+
version: string;
|
|
1485
|
+
repositoryUrl?: string;
|
|
1486
|
+
websiteUrl?: string;
|
|
1487
|
+
packages: RemoteRegistryPackage[];
|
|
1488
|
+
remotes: RemoteRegistryTransport[];
|
|
1489
|
+
};
|
|
1490
|
+
/**
|
|
1491
|
+
* One validated record from a remote registry: the server plus the status
|
|
1492
|
+
* and timestamp the official registry reports under `_meta`.
|
|
1493
|
+
*/
|
|
1494
|
+
export type RemoteRegistryRecord = {
|
|
1495
|
+
server: RemoteRegistryServer;
|
|
1496
|
+
status?: string;
|
|
1497
|
+
updatedAt?: string;
|
|
1498
|
+
};
|
|
1499
|
+
/**
|
|
1500
|
+
* One validated page of a registry's server list
|
|
1501
|
+
*/
|
|
1502
|
+
export type RemoteRegistryPage = {
|
|
1503
|
+
records: RemoteRegistryRecord[];
|
|
1504
|
+
nextCursor?: string;
|
|
1505
|
+
};
|
|
1506
|
+
/**
|
|
1507
|
+
* A registry the client can query: its configuration plus the resolved,
|
|
1508
|
+
* credential-free API base URL (`.../v0.1`)
|
|
1509
|
+
*/
|
|
1510
|
+
export type RemoteRegistryTarget = {
|
|
1511
|
+
config: RegistryConfig;
|
|
1512
|
+
apiBase: string;
|
|
1513
|
+
};
|
|
1418
1514
|
/**
|
|
1419
1515
|
* Load balancing strategies
|
|
1420
1516
|
*/
|
|
@@ -31,12 +31,13 @@ export type CatalogModelSpec = {
|
|
|
31
31
|
/**
|
|
32
32
|
* Per-model override of the provider-level `capabilities.tools`, for a
|
|
33
33
|
* "model-dependent" provider whose vendor docs name which specific models
|
|
34
|
-
* accept tool definitions (e.g. Reka:
|
|
35
|
-
*
|
|
36
|
-
* answer", same as every model before this field
|
|
37
|
-
* when `capabilities.tools` is
|
|
38
|
-
*
|
|
39
|
-
*
|
|
34
|
+
* accept tool definitions (e.g. Reka: its Models table has a Tools column,
|
|
35
|
+
* Yes for reka-edge-2603 and No for reka-flash-3). Absent means "inherit
|
|
36
|
+
* the provider-level answer", same as every model before this field
|
|
37
|
+
* existed. Only meaningful when `capabilities.tools` is
|
|
38
|
+
* `"model-dependent"` — a provider that already declares a plain
|
|
39
|
+
* `true`/`false` has no need for it, since that single value already
|
|
40
|
+
* applies to every model it serves.
|
|
40
41
|
*/
|
|
41
42
|
tools?: boolean;
|
|
42
43
|
status: CatalogModelStatus;
|
|
@@ -263,7 +264,7 @@ export type ProviderCatalogJson = {
|
|
|
263
264
|
};
|
|
264
265
|
export type DecideFormatterName = "fastapi-validation";
|
|
265
266
|
export type DecideBaseURLTransform = "strip-trailing-slash" | "strip-v1-suffix";
|
|
266
|
-
/** A check a base URL must pass once normalised; `origin` allows only an http
|
|
267
|
+
/** A check a base URL must pass once normalised; `origin` allows only an https URL (http only for localhost, 127.0.0.1 or [::1]) with no userinfo, query string or fragment. */
|
|
267
268
|
export type DecideBaseURLShape = "origin";
|
|
268
269
|
export type DecideErrorRule = {
|
|
269
270
|
/** One status or a list of them. A rule sets this or `statusMin`, never both. */
|