@juspay/neurolink 11.10.0 → 11.11.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.
@@ -150,9 +150,11 @@ export function runAgenticLoop(adapter, initialConversation, options) {
150
150
  permanentlyFailed: true,
151
151
  });
152
152
  allToolExecutions.push({
153
+ id: call.id,
153
154
  name: call.name,
154
155
  input: call.args,
155
156
  output,
157
+ error: output.error,
156
158
  });
157
159
  continue;
158
160
  }
@@ -181,9 +183,11 @@ export function runAgenticLoop(adapter, initialConversation, options) {
181
183
  permanentlyFailed: !!breaker,
182
184
  });
183
185
  allToolExecutions.push({
186
+ id: call.id,
184
187
  name: call.name,
185
188
  input: call.args,
186
189
  output,
190
+ error: output.error,
187
191
  });
188
192
  continue;
189
193
  }
@@ -194,6 +198,7 @@ export function runAgenticLoop(adapter, initialConversation, options) {
194
198
  });
195
199
  toolResults.push({ ...call, output });
196
200
  allToolExecutions.push({
201
+ id: call.id,
197
202
  name: call.name,
198
203
  input: call.args,
199
204
  output,
@@ -213,9 +218,11 @@ export function runAgenticLoop(adapter, initialConversation, options) {
213
218
  const output = { error: message, status: "failed" };
214
219
  toolResults.push({ ...call, output, error: message });
215
220
  allToolExecutions.push({
221
+ id: call.id,
216
222
  name: call.name,
217
223
  input: call.args,
218
224
  output,
225
+ error: message,
219
226
  });
220
227
  }
221
228
  }
@@ -150,9 +150,11 @@ export function runAgenticLoop(adapter, initialConversation, options) {
150
150
  permanentlyFailed: true,
151
151
  });
152
152
  allToolExecutions.push({
153
+ id: call.id,
153
154
  name: call.name,
154
155
  input: call.args,
155
156
  output,
157
+ error: output.error,
156
158
  });
157
159
  continue;
158
160
  }
@@ -181,9 +183,11 @@ export function runAgenticLoop(adapter, initialConversation, options) {
181
183
  permanentlyFailed: !!breaker,
182
184
  });
183
185
  allToolExecutions.push({
186
+ id: call.id,
184
187
  name: call.name,
185
188
  input: call.args,
186
189
  output,
190
+ error: output.error,
187
191
  });
188
192
  continue;
189
193
  }
@@ -194,6 +198,7 @@ export function runAgenticLoop(adapter, initialConversation, options) {
194
198
  });
195
199
  toolResults.push({ ...call, output });
196
200
  allToolExecutions.push({
201
+ id: call.id,
197
202
  name: call.name,
198
203
  input: call.args,
199
204
  output,
@@ -213,9 +218,11 @@ export function runAgenticLoop(adapter, initialConversation, options) {
213
218
  const output = { error: message, status: "failed" };
214
219
  toolResults.push({ ...call, output, error: message });
215
220
  allToolExecutions.push({
221
+ id: call.id,
216
222
  name: call.name,
217
223
  input: call.args,
218
224
  output,
225
+ error: message,
219
226
  });
220
227
  }
221
228
  }
@@ -23,6 +23,7 @@
23
23
  */
24
24
  import { resolveDeferredTool } from "../../tools/toolDiscovery.js";
25
25
  import { stringifyAnthropicToolOutput } from "./toolOutput.js";
26
+ import { stringifyFinalResultInput } from "./structuredOutput.js";
26
27
  /** Map Anthropic's stop_reason onto the unified finish reason. */
27
28
  function mapAnthropicFinishReason(rawStopReason, hadToolCallsAtCap) {
28
29
  switch (rawStopReason) {
@@ -271,10 +272,19 @@ export function createAnthropicLoopAdapter(config) {
271
272
  // never dispatched, never counted against the breaker, and never shows
272
273
  // up as a tool execution.
273
274
  const terminal = config.finalResultToolName
274
- ? allCalls.find((call) => call.name === config.finalResultToolName)
275
+ ? [...toolByIndex.values()].find((pending) => pending.name === config.finalResultToolName)
275
276
  : undefined;
276
277
  const toolCalls = terminal ? [] : allCalls;
277
- const finalText = terminal ? JSON.stringify(terminal.args) : text;
278
+ // The RAW accumulated input_json, never the parsed-then-restringified
279
+ // args. `parseArgs` yields {} for a payload the token cap cut off
280
+ // mid-string, so re-stringifying would turn a truncated answer into
281
+ // "{}" and lose it outright. `stringifyFinalResultInput` canonicalizes
282
+ // when the JSON parses and returns it verbatim when it does not, which
283
+ // is what lets the caller's coercion layer repair a partial payload
284
+ // into a partial object instead of nothing.
285
+ const finalText = terminal
286
+ ? stringifyFinalResultInput(terminal.inputJson)
287
+ : text;
278
288
  return {
279
289
  text: finalText,
280
290
  ...(reasoning ? { reasoning } : {}),
@@ -1001,7 +1001,16 @@ export class GoogleAIStudioProvider extends BaseProvider {
1001
1001
  if (wantsNativeJsonRequested && exclusionInForce) {
1002
1002
  logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request (generate()).");
1003
1003
  }
1004
- if (shouldUseTools && !exclusionInForce) {
1004
+ // Both conjuncts, matching the warning directly above and the
1005
+ // stream path's gate. `isToolsSchemaExclusionInForce` answers "does
1006
+ // the tools/schema exclusion APPLY to this provider and model", and
1007
+ // is true for any Gemini request that has tools at all — it is not
1008
+ // "the exclusion is triggered". Testing `!exclusionInForce` alone
1009
+ // therefore made this branch reachable only when there were NO
1010
+ // tools, so every caller-supplied tool was dropped on the generate
1011
+ // path whether or not structured output was ever requested.
1012
+ if (shouldUseTools &&
1013
+ !(wantsNativeJsonRequested && exclusionInForce)) {
1005
1014
  const tools = options.tools || {};
1006
1015
  if (Object.keys(tools).length > 0) {
1007
1016
  const result = toNativeToolDeclarations(tools, "functionDeclarations");
@@ -271,10 +271,24 @@ export type AgenticLoopOptions = {
271
271
  export type AgenticLoopResult<TConversation> = {
272
272
  text: string;
273
273
  toolCalls: AgenticLoopToolCall[];
274
+ /**
275
+ * Every tool dispatch the loop performed, in order, including the ones that
276
+ * failed.
277
+ *
278
+ * `id` and `error` are carried because providers persist tool activity as
279
+ * paired call/result records keyed by the provider's own tool-call id, and
280
+ * a result that failed is stored differently from one that succeeded. A
281
+ * shape with only name/input/output cannot reconstruct either, so a
282
+ * provider migrating its hand-rolled loop onto this engine would have to
283
+ * silently drop both from its history — which is a behaviour change, not a
284
+ * refactor.
285
+ */
274
286
  toolExecutions: Array<{
287
+ id: string;
275
288
  name: string;
276
289
  input: Record<string, unknown>;
277
290
  output: unknown;
291
+ error?: string;
278
292
  }>;
279
293
  usage: AgenticLoopUsage;
280
294
  finishReason: string;
@@ -23,6 +23,7 @@
23
23
  */
24
24
  import { resolveDeferredTool } from "../../tools/toolDiscovery.js";
25
25
  import { stringifyAnthropicToolOutput } from "./toolOutput.js";
26
+ import { stringifyFinalResultInput } from "./structuredOutput.js";
26
27
  /** Map Anthropic's stop_reason onto the unified finish reason. */
27
28
  function mapAnthropicFinishReason(rawStopReason, hadToolCallsAtCap) {
28
29
  switch (rawStopReason) {
@@ -271,10 +272,19 @@ export function createAnthropicLoopAdapter(config) {
271
272
  // never dispatched, never counted against the breaker, and never shows
272
273
  // up as a tool execution.
273
274
  const terminal = config.finalResultToolName
274
- ? allCalls.find((call) => call.name === config.finalResultToolName)
275
+ ? [...toolByIndex.values()].find((pending) => pending.name === config.finalResultToolName)
275
276
  : undefined;
276
277
  const toolCalls = terminal ? [] : allCalls;
277
- const finalText = terminal ? JSON.stringify(terminal.args) : text;
278
+ // The RAW accumulated input_json, never the parsed-then-restringified
279
+ // args. `parseArgs` yields {} for a payload the token cap cut off
280
+ // mid-string, so re-stringifying would turn a truncated answer into
281
+ // "{}" and lose it outright. `stringifyFinalResultInput` canonicalizes
282
+ // when the JSON parses and returns it verbatim when it does not, which
283
+ // is what lets the caller's coercion layer repair a partial payload
284
+ // into a partial object instead of nothing.
285
+ const finalText = terminal
286
+ ? stringifyFinalResultInput(terminal.inputJson)
287
+ : text;
278
288
  return {
279
289
  text: finalText,
280
290
  ...(reasoning ? { reasoning } : {}),
@@ -1001,7 +1001,16 @@ export class GoogleAIStudioProvider extends BaseProvider {
1001
1001
  if (wantsNativeJsonRequested && exclusionInForce) {
1002
1002
  logger.warn("[GoogleAIStudio] Gemini does not support tools and JSON schema output simultaneously. Disabling tools for this request (generate()).");
1003
1003
  }
1004
- if (shouldUseTools && !exclusionInForce) {
1004
+ // Both conjuncts, matching the warning directly above and the
1005
+ // stream path's gate. `isToolsSchemaExclusionInForce` answers "does
1006
+ // the tools/schema exclusion APPLY to this provider and model", and
1007
+ // is true for any Gemini request that has tools at all — it is not
1008
+ // "the exclusion is triggered". Testing `!exclusionInForce` alone
1009
+ // therefore made this branch reachable only when there were NO
1010
+ // tools, so every caller-supplied tool was dropped on the generate
1011
+ // path whether or not structured output was ever requested.
1012
+ if (shouldUseTools &&
1013
+ !(wantsNativeJsonRequested && exclusionInForce)) {
1005
1014
  const tools = options.tools || {};
1006
1015
  if (Object.keys(tools).length > 0) {
1007
1016
  const result = toNativeToolDeclarations(tools, "functionDeclarations");
@@ -271,10 +271,24 @@ export type AgenticLoopOptions = {
271
271
  export type AgenticLoopResult<TConversation> = {
272
272
  text: string;
273
273
  toolCalls: AgenticLoopToolCall[];
274
+ /**
275
+ * Every tool dispatch the loop performed, in order, including the ones that
276
+ * failed.
277
+ *
278
+ * `id` and `error` are carried because providers persist tool activity as
279
+ * paired call/result records keyed by the provider's own tool-call id, and
280
+ * a result that failed is stored differently from one that succeeded. A
281
+ * shape with only name/input/output cannot reconstruct either, so a
282
+ * provider migrating its hand-rolled loop onto this engine would have to
283
+ * silently drop both from its history — which is a behaviour change, not a
284
+ * refactor.
285
+ */
274
286
  toolExecutions: Array<{
287
+ id: string;
275
288
  name: string;
276
289
  input: Record<string, unknown>;
277
290
  output: unknown;
291
+ error?: string;
278
292
  }>;
279
293
  usage: AgenticLoopUsage;
280
294
  finishReason: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "11.10.0",
3
+ "version": "11.11.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": {