@juspay/neurolink 12.14.16 → 12.14.18

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.
@@ -264,14 +264,30 @@ export class AmazonBedrockProvider extends BaseProvider {
264
264
  provider: this.getProviderName(),
265
265
  ...(finishReason !== undefined && { finishReason }),
266
266
  ...(rawFinishReason !== undefined && { rawFinishReason }),
267
- toolsUsed: toolExecutions.map((execution) => execution.name),
267
+ // `toolsUsed` means RAN, not "was asked for". `runAgenticLoop` records
268
+ // every dispatch it performed, including the ones that never reached a
269
+ // tool: a name the model invented (TOOL_NOT_FOUND), one the breaker had
270
+ // already given up on (TOOL_PERMANENTLY_FAILED), and one that threw.
271
+ // Exactly those carry `error`; a call that reached the tool and got a
272
+ // value back does not. That is the same line `nativeGenerateLoop` draws
273
+ // — its `toolsUsed.push(name)` sits inside the try, after the guarded
274
+ // call returns — so filtering here makes Bedrock's generate path report
275
+ // what every other generate path reports, instead of inflating
276
+ // analytics, cost attribution and audit with calls that never ran.
277
+ //
278
+ // `toolCalls` and `toolExecutions` stay complete on purpose: they are
279
+ // the record of what the turn ATTEMPTED, and a failed attempt is
280
+ // precisely what a caller debugging a turn needs to see.
281
+ toolsUsed: toolExecutions
282
+ .filter((execution) => execution.error === undefined)
283
+ .map((execution) => execution.name),
268
284
  toolCalls: toolExecutions.map((execution) => ({
269
285
  toolCallId: execution.id,
270
286
  toolName: execution.name,
271
287
  args: execution.input,
272
288
  })),
273
289
  toolExecutions: resolveToolExecutionRecords(options, transformToolExecutions(toolExecutions)),
274
- enhancedWithTools: toolExecutions.length > 0,
290
+ enhancedWithTools: toolExecutions.some((execution) => execution.error === undefined),
275
291
  };
276
292
  }
277
293
  async conversationLoop(options) {
@@ -983,7 +999,15 @@ export class AmazonBedrockProvider extends BaseProvider {
983
999
  // alone left every streamed Bedrock turn reporting no tool calls,
984
1000
  // however many it made.
985
1001
  toolExecutions: outcome.result?.toolExecutions ?? [],
986
- toolsUsed: (outcome.result?.toolExecutions ?? []).map((execution) => execution.name),
1002
+ // Same "ran, not asked for" rule as the generate path above.
1003
+ // `toolCallCount` still derives from the unfiltered
1004
+ // `toolExecutions`, which is correct — a dispatch that failed was
1005
+ // still a call — so this list is a fallback today rather than an
1006
+ // observable surface. It is filtered anyway so that the moment
1007
+ // anything does surface it, it is not surfacing the same lie.
1008
+ toolsUsed: (outcome.result?.toolExecutions ?? [])
1009
+ .filter((execution) => execution.error === undefined)
1010
+ .map((execution) => execution.name),
987
1011
  }, Date.now() - startTime, {
988
1012
  requestId: `bedrock-stream-${Date.now()}`,
989
1013
  streamingMode: true,
@@ -25,17 +25,40 @@ function asNonEmptyString(value) {
25
25
  return typeof value === "string" && value.length > 0 ? value : undefined;
26
26
  }
27
27
  function buildSystemInstructions(body) {
28
+ const instructions = [];
28
29
  if (typeof body.system === "string") {
29
- return body.system || undefined;
30
+ if (body.system) {
31
+ instructions.push(body.system);
32
+ }
30
33
  }
31
- if (Array.isArray(body.system)) {
34
+ else if (Array.isArray(body.system)) {
32
35
  const text = body.system
33
36
  .map((block) => (typeof block.text === "string" ? block.text : ""))
34
37
  .filter(Boolean)
35
38
  .join("\n\n");
36
- return text || undefined;
39
+ if (text) {
40
+ instructions.push(text);
41
+ }
37
42
  }
38
- return undefined;
43
+ for (const message of body.messages) {
44
+ // Claude's public Messages type restricts message roles to user/assistant,
45
+ // but Claude Code can emit an inline system message for context-management
46
+ // edits. Preserve its text as instructions instead of sending a non-leading
47
+ // system item to the Codex conversation input.
48
+ if (message.role !== "system") {
49
+ continue;
50
+ }
51
+ const text = typeof message.content === "string"
52
+ ? message.content
53
+ : message.content
54
+ .map((block) => (block.type === "text" ? block.text : ""))
55
+ .filter(Boolean)
56
+ .join("\n");
57
+ if (text) {
58
+ instructions.push(text);
59
+ }
60
+ }
61
+ return instructions.length > 0 ? instructions.join("\n\n") : undefined;
39
62
  }
40
63
  function imageUrlForBlock(block) {
41
64
  if (block.source.type === "url" && block.source.url) {
@@ -126,14 +149,26 @@ function convertClaudeMessage(role, content) {
126
149
  });
127
150
  continue;
128
151
  }
129
- messageContent.push(toCodexContentPart(block, role));
152
+ // Claude Code context-management messages can contain `tool_addition`
153
+ // blocks. The live tool definitions are already carried by `body.tools`;
154
+ // these history markers must not become undefined array entries because
155
+ // JSON encodes array undefined values as null and Codex rejects them.
156
+ if (block.type === "tool_addition") {
157
+ continue;
158
+ }
159
+ const contentPart = toCodexContentPart(block, role);
160
+ if (contentPart) {
161
+ messageContent.push(contentPart);
162
+ }
130
163
  }
131
164
  flushMessage();
132
165
  return input;
133
166
  }
134
167
  /** Convert a Claude Messages request into the ChatGPT Codex Responses shape. */
135
168
  export function convertClaudeRequestToCodex(body, model, reasoningEffort) {
136
- const input = body.messages.flatMap((message) => convertClaudeMessage(message.role, message.content));
169
+ const input = body.messages.flatMap((message) => message.role === "system"
170
+ ? []
171
+ : convertClaudeMessage(message.role, message.content));
137
172
  const request = {
138
173
  model,
139
174
  input,
@@ -510,11 +510,16 @@ export type CreateThinkingConfigOptions = {
510
510
  * Native SDK thinkingConfig structure for Gemini native SDK.
511
511
  *
512
512
  * `thinkingLevel` and `thinkingBudget` are mutually exclusive on the wire:
513
- * Gemini 3 models accept `thinkingLevel`; Gemini 2.5 (and earlier) models
514
- * reject it outright with INVALID_ARGUMENT "thinking_level not supported by
515
- * this model" and require the numeric `thinkingBudget` instead. Both fields
516
- * are optional here so a single type can represent either shape — callers
517
- * building the request pick whichever field the target model accepts.
513
+ * Gemini 3 models accept `thinkingLevel`; Gemini 2.5 models reject it outright
514
+ * with INVALID_ARGUMENT "thinking_level not supported by this model" and
515
+ * require the numeric `thinkingBudget` instead. Gemini 2.5 is also the only
516
+ * family `createNativeThinkingConfig` emits a budget for:
517
+ * `getGemini25ThinkingBudgetRange` has no verified range for anything earlier,
518
+ * so those models get no `thinkingConfig` at all — and a one-time WARN —
519
+ * rather than a guessed shape that might itself 400.
520
+ *
521
+ * Both fields are optional here so a single type can represent either shape —
522
+ * callers building the request pick whichever field the target model accepts.
518
523
  */
519
524
  export type NativeThinkingConfig = {
520
525
  includeThoughts: boolean;