@juspay/neurolink 11.16.0 → 11.16.2
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 +4 -3
- package/dist/auth/providers/BaseAuthProvider.d.ts +1 -1
- package/dist/browser/neurolink.min.js +350 -350
- package/dist/core/geminiLoopAdapter.js +26 -8
- package/dist/core/loopEngine.js +13 -3
- package/dist/providers/googleNativeGemini3/utils.d.ts +16 -0
- package/dist/providers/googleNativeGemini3/utils.js +38 -23
- package/dist/providers/googleVertex/client.js +14 -0
- package/dist/types/loopEngine.d.ts +10 -0
- package/package.json +5 -9
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* and original -> sanitized when writing `functionResponse` parts. Neither the
|
|
17
17
|
* engine nor any other adapter needs to know sanitization happened.
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
19
|
+
import { resolveLiveTool } from "../tools/toolDiscovery.js";
|
|
20
|
+
import { collectStreamChunksIncremental, guardToolExecutor, extractTextFromParts, mapGeminiFinishReason, pushModelResponseToHistory, refreshNativeToolDeclarations, } from "../providers/googleNativeGemini3/utils.js";
|
|
20
21
|
export function createGeminiLoopAdapter(config) {
|
|
21
22
|
/**
|
|
22
23
|
* Sanitized wire name -> the name the caller registered. Rebuilt per step
|
|
@@ -87,17 +88,34 @@ export function createGeminiLoopAdapter(config) {
|
|
|
87
88
|
}
|
|
88
89
|
: {}),
|
|
89
90
|
resolveToolOnMiss: (name) => {
|
|
90
|
-
|
|
91
|
+
// resolveLiveTool, NOT a plain record lookup. A deferred-catalog tool is
|
|
92
|
+
// not a key on the record at all — it lives behind a symbol-keyed
|
|
93
|
+
// resolver — so `liveTools[name]` returns undefined for exactly the
|
|
94
|
+
// tools this hook exists to hydrate, and the model gets TOOL_NOT_FOUND
|
|
95
|
+
// for a tool it was told about.
|
|
96
|
+
const tool = resolveLiveTool(config.liveTools, name);
|
|
91
97
|
const execute = tool?.execute;
|
|
92
98
|
if (!execute) {
|
|
93
99
|
return undefined;
|
|
94
100
|
}
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
// Registered into the turn's DedupExecuteMap and taken back through
|
|
102
|
+
// `.get()`, so a hydrated tool gets the same per-turn result cache as a
|
|
103
|
+
// declared one. A tool the model just discovered is the one most likely
|
|
104
|
+
// to be re-requested with identical arguments.
|
|
105
|
+
const executeMap = config.declarations?.executeMap;
|
|
106
|
+
let resolved = execute;
|
|
107
|
+
if (executeMap) {
|
|
108
|
+
executeMap.set(name, execute);
|
|
109
|
+
resolved = executeMap.get(name) ?? execute;
|
|
110
|
+
}
|
|
111
|
+
const guarded = config.toolGuards
|
|
112
|
+
? guardToolExecutor(name, resolved, config.toolGuards)
|
|
113
|
+
: // Wrapped because the hook types `opts` as `unknown`, and a function
|
|
114
|
+
// declaring a narrower options type is not assignable to one
|
|
115
|
+
// accepting `unknown`. One assertion at the boundary, never a double
|
|
116
|
+
// assertion.
|
|
117
|
+
async (args, opts) => resolved(args, opts);
|
|
118
|
+
return { execute: guarded };
|
|
101
119
|
},
|
|
102
120
|
async executeStep(request, channel, signal) {
|
|
103
121
|
const rawStream = await config.sendStep(request.raw, signal);
|
package/dist/core/loopEngine.js
CHANGED
|
@@ -87,9 +87,19 @@ async function dispatchStepTools(params) {
|
|
|
87
87
|
// a deferred-catalog placeholder is exactly that shape, and it is
|
|
88
88
|
// precisely what hydration exists to resolve.
|
|
89
89
|
const declaredTool = tools?.[call.name];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
let tool = declaredTool;
|
|
91
|
+
if (!declaredTool?.execute) {
|
|
92
|
+
const hydrated = adapter.resolveToolOnMiss?.(call.name);
|
|
93
|
+
if (hydrated) {
|
|
94
|
+
tool = hydrated;
|
|
95
|
+
// It resolves NOW, so any strikes standing against this name were
|
|
96
|
+
// recorded while it genuinely did not resolve — snapshot artifacts of
|
|
97
|
+
// a deferred catalog, not failures of a tool that exists. Leaving them
|
|
98
|
+
// in place would disable the tool at the exact moment it became
|
|
99
|
+
// usable.
|
|
100
|
+
failedTools.delete(call.name);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
93
103
|
if (!tool?.execute) {
|
|
94
104
|
const output = breaker
|
|
95
105
|
? {
|
|
@@ -91,6 +91,22 @@ export declare function buildNativeToolDeclarations(tools: Record<string, Tool>,
|
|
|
91
91
|
* TOOL_NOT_FOUND. Mutates the snapshot in place — the request config holds
|
|
92
92
|
* `toolsConfig` by reference — and returns true when anything was added.
|
|
93
93
|
*/
|
|
94
|
+
/**
|
|
95
|
+
* Everything a native Gemini loop wraps around a tool call that the shared
|
|
96
|
+
* engine does not do itself.
|
|
97
|
+
*
|
|
98
|
+
* Order matters. `raceWithAbort` sits INSIDE `withTimeout` so a turn-level
|
|
99
|
+
* abort is observed the moment it fires rather than after the tool settles,
|
|
100
|
+
* and the timeout still bounds a tool that neither settles nor honours its
|
|
101
|
+
* signal. The progress pings bracket the await because the stall watchdog is
|
|
102
|
+
* a whole-turn interval comparing wall-clock against the last progress mark —
|
|
103
|
+
* without them a legitimately slow tool reads as a stalled turn and is killed.
|
|
104
|
+
*
|
|
105
|
+
* Exported because a tool hydrated MID-TURN has to be wrapped the same way as
|
|
106
|
+
* one declared up front; keeping this inline made the discovered tool the one
|
|
107
|
+
* executor in the system that ran raw.
|
|
108
|
+
*/
|
|
109
|
+
export declare function guardToolExecutor(name: string, execute: NonNullable<Tool["execute"]>, guards: GeminiToolExecutionGuards): (args: Record<string, unknown>, opts: unknown) => Promise<unknown>;
|
|
94
110
|
/**
|
|
95
111
|
* Build the tool record handed to `runAgenticLoop`, routed through the turn's
|
|
96
112
|
* DedupExecuteMap.
|
|
@@ -456,6 +456,41 @@ export function buildNativeToolDeclarations(tools, reservedNames) {
|
|
|
456
456
|
* TOOL_NOT_FOUND. Mutates the snapshot in place — the request config holds
|
|
457
457
|
* `toolsConfig` by reference — and returns true when anything was added.
|
|
458
458
|
*/
|
|
459
|
+
/**
|
|
460
|
+
* Everything a native Gemini loop wraps around a tool call that the shared
|
|
461
|
+
* engine does not do itself.
|
|
462
|
+
*
|
|
463
|
+
* Order matters. `raceWithAbort` sits INSIDE `withTimeout` so a turn-level
|
|
464
|
+
* abort is observed the moment it fires rather than after the tool settles,
|
|
465
|
+
* and the timeout still bounds a tool that neither settles nor honours its
|
|
466
|
+
* signal. The progress pings bracket the await because the stall watchdog is
|
|
467
|
+
* a whole-turn interval comparing wall-clock against the last progress mark —
|
|
468
|
+
* without them a legitimately slow tool reads as a stalled turn and is killed.
|
|
469
|
+
*
|
|
470
|
+
* Exported because a tool hydrated MID-TURN has to be wrapped the same way as
|
|
471
|
+
* one declared up front; keeping this inline made the discovered tool the one
|
|
472
|
+
* executor in the system that ran raw.
|
|
473
|
+
*/
|
|
474
|
+
export function guardToolExecutor(name, execute, guards) {
|
|
475
|
+
return async (args, opts) => {
|
|
476
|
+
const call = () => Promise.resolve(execute(args, opts));
|
|
477
|
+
guards.onProgress?.();
|
|
478
|
+
try {
|
|
479
|
+
const raced = guards.abortSignal
|
|
480
|
+
? raceWithAbort(call(), guards.abortSignal)
|
|
481
|
+
: call();
|
|
482
|
+
return await (guards.toolTimeoutMs === undefined
|
|
483
|
+
? raced
|
|
484
|
+
: withTimeout(raced, guards.toolTimeoutMs, `Tool "${name}" execution timed out after ${guards.toolTimeoutMs}ms`));
|
|
485
|
+
}
|
|
486
|
+
finally {
|
|
487
|
+
// In `finally`, not after a successful await: a tool that times out or
|
|
488
|
+
// throws has still consumed real time, and skipping the mark there would
|
|
489
|
+
// leave the watchdog measuring from before the call.
|
|
490
|
+
guards.onProgress?.();
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
459
494
|
/**
|
|
460
495
|
* Build the tool record handed to `runAgenticLoop`, routed through the turn's
|
|
461
496
|
* DedupExecuteMap.
|
|
@@ -487,29 +522,9 @@ export function buildDedupedEngineTools(declarations, tools, guards) {
|
|
|
487
522
|
* mark — without them a legitimately slow tool reads as a stalled turn and
|
|
488
523
|
* gets killed.
|
|
489
524
|
*/
|
|
490
|
-
const guard = (name, execute) =>
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (!guards) {
|
|
494
|
-
return call();
|
|
495
|
-
}
|
|
496
|
-
guards.onProgress?.();
|
|
497
|
-
try {
|
|
498
|
-
const raced = guards.abortSignal
|
|
499
|
-
? raceWithAbort(call(), guards.abortSignal)
|
|
500
|
-
: call();
|
|
501
|
-
return await (guards.toolTimeoutMs === undefined
|
|
502
|
-
? raced
|
|
503
|
-
: withTimeout(raced, guards.toolTimeoutMs, `Tool "${name}" execution timed out after ${guards.toolTimeoutMs}ms`));
|
|
504
|
-
}
|
|
505
|
-
finally {
|
|
506
|
-
// In `finally`, not after a successful await: a tool that times out or
|
|
507
|
-
// throws has still consumed real time, and skipping the mark there
|
|
508
|
-
// would leave the watchdog measuring from before the call.
|
|
509
|
-
guards.onProgress?.();
|
|
510
|
-
}
|
|
511
|
-
};
|
|
512
|
-
};
|
|
525
|
+
const guard = (name, execute) => guards
|
|
526
|
+
? guardToolExecutor(name, execute, guards)
|
|
527
|
+
: async (args, opts) => execute(args, opts);
|
|
513
528
|
if (declarations) {
|
|
514
529
|
for (const [safeName, originalName] of declarations.originalNameMap) {
|
|
515
530
|
const execute = declarations.executeMap.get(safeName);
|
|
@@ -1613,6 +1613,13 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1613
1613
|
classifyResultFailure: (output) => extractToolFailureText(output) ?? undefined,
|
|
1614
1614
|
},
|
|
1615
1615
|
liveTools: options.tools ?? {},
|
|
1616
|
+
// A tool hydrated mid-turn gets the SAME bound, abort race and stall
|
|
1617
|
+
// ping as one declared up front — see guardToolExecutor.
|
|
1618
|
+
toolGuards: {
|
|
1619
|
+
toolTimeoutMs: toolExecTimeoutMs,
|
|
1620
|
+
abortSignal: effectiveSignal,
|
|
1621
|
+
onProgress: () => turnClock.noteProgress(),
|
|
1622
|
+
},
|
|
1616
1623
|
...(declarations ? { declarations } : {}),
|
|
1617
1624
|
...(useFinalResultTool
|
|
1618
1625
|
? {
|
|
@@ -2401,6 +2408,13 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
2401
2408
|
classifyResultFailure: (output) => extractToolFailureText(output) ?? undefined,
|
|
2402
2409
|
},
|
|
2403
2410
|
liveTools: options.tools ?? {},
|
|
2411
|
+
// A tool hydrated mid-turn gets the SAME bound, abort race and stall
|
|
2412
|
+
// ping as one declared up front — see guardToolExecutor.
|
|
2413
|
+
toolGuards: {
|
|
2414
|
+
toolTimeoutMs: toolExecTimeoutMs,
|
|
2415
|
+
abortSignal: effectiveSignal,
|
|
2416
|
+
onProgress: () => turnClock.noteProgress(),
|
|
2417
|
+
},
|
|
2404
2418
|
...(declarations ? { declarations } : {}),
|
|
2405
2419
|
...(useFinalResultTool
|
|
2406
2420
|
? {
|
|
@@ -326,6 +326,16 @@ export type GeminiLoopAdapterCoreConfig = {
|
|
|
326
326
|
* step with that step's real token counts.
|
|
327
327
|
*/
|
|
328
328
|
noteUsage?: (inputTokens: number, outputTokens: number) => void;
|
|
329
|
+
/**
|
|
330
|
+
* The same guards `buildDedupedEngineTools` wraps declared tools in, applied
|
|
331
|
+
* to one hydrated mid-turn.
|
|
332
|
+
*
|
|
333
|
+
* Without this a tool discovered during a turn is the ONE executor that runs
|
|
334
|
+
* raw: no per-turn dedup, no execution timeout, no stall-clock ping. That is
|
|
335
|
+
* the opposite of what discovery is for — the tool the model just found is
|
|
336
|
+
* the one most likely to be called repeatedly with the same arguments.
|
|
337
|
+
*/
|
|
338
|
+
toolGuards?: GeminiToolExecutionGuards;
|
|
329
339
|
/**
|
|
330
340
|
* Name of the terminal structured-output tool when one is in play. A call
|
|
331
341
|
* to it ends the turn: its arguments ARE the answer, so it is reported as
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "11.16.
|
|
3
|
+
"version": "11.16.2",
|
|
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": {
|
|
@@ -22,9 +22,8 @@
|
|
|
22
22
|
"url": "https://github.com/sponsors/juspay"
|
|
23
23
|
},
|
|
24
24
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
26
|
-
"
|
|
27
|
-
"pnpm": ">=8.0.0"
|
|
25
|
+
"node": ">=22.0.0",
|
|
26
|
+
"pnpm": ">=10.0.0"
|
|
28
27
|
},
|
|
29
28
|
"scripts": {
|
|
30
29
|
"dev": "vite dev",
|
|
@@ -444,7 +443,7 @@
|
|
|
444
443
|
"koa": "^3.1.1",
|
|
445
444
|
"koa-bodyparser": "^4.4.1",
|
|
446
445
|
"livekit-server-sdk": "^2.15.4",
|
|
447
|
-
"mammoth": "
|
|
446
|
+
"mammoth": "1.12.0",
|
|
448
447
|
"mediabunny": "^1.40.1",
|
|
449
448
|
"music-metadata": "^11.11.2",
|
|
450
449
|
"pdf-parse": "^2.4.5",
|
|
@@ -460,9 +459,7 @@
|
|
|
460
459
|
"@changesets/cli": "^2.29.8",
|
|
461
460
|
"@electric-sql/pglite": "^0.4.6",
|
|
462
461
|
"@eslint/js": "^10.0.1",
|
|
463
|
-
"@juspay/hippocampus": ">=0.1.7",
|
|
464
462
|
"@opentelemetry/api": "^1.9.0",
|
|
465
|
-
"@opentelemetry/sdk-trace-base": "^2.6.0",
|
|
466
463
|
"@opentelemetry/sdk-trace-node": "^2.6.0",
|
|
467
464
|
"@semantic-release/changelog": "^6.0.3",
|
|
468
465
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
@@ -481,7 +478,7 @@
|
|
|
481
478
|
"@types/koa": "^3.0.1",
|
|
482
479
|
"@types/koa-bodyparser": "^4.3.13",
|
|
483
480
|
"@types/koa__cors": "^5.0.1",
|
|
484
|
-
"@types/node": "^
|
|
481
|
+
"@types/node": "^22.20.0",
|
|
485
482
|
"@types/react": "^19.2.10",
|
|
486
483
|
"@types/tar-stream": "^3.1.4",
|
|
487
484
|
"@types/ws": "^8.18.1",
|
|
@@ -571,7 +568,6 @@
|
|
|
571
568
|
"esbuild",
|
|
572
569
|
"protobufjs",
|
|
573
570
|
"puppeteer",
|
|
574
|
-
"sqlite3",
|
|
575
571
|
"ffmpeg-static",
|
|
576
572
|
"sharp"
|
|
577
573
|
],
|