@juspay/neurolink 11.2.4 → 11.3.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/dist/adapters/providerImageAdapter.js +1 -1
  3. package/dist/browser/neurolink.min.js +391 -391
  4. package/dist/cli/commands/mcp.js +8 -8
  5. package/dist/constants/contextWindows.js +1 -1
  6. package/dist/constants/enums.d.ts +2 -2
  7. package/dist/constants/enums.js +2 -2
  8. package/dist/constants/tokens.d.ts +1 -1
  9. package/dist/constants/tokens.js +1 -1
  10. package/dist/core/loopEngine.d.ts +23 -0
  11. package/dist/core/loopEngine.js +245 -0
  12. package/dist/core/nativeToolFormat.d.ts +33 -0
  13. package/dist/core/nativeToolFormat.js +30 -0
  14. package/dist/core/streamChannel.d.ts +10 -0
  15. package/dist/core/streamChannel.js +76 -0
  16. package/dist/lib/adapters/providerImageAdapter.js +1 -1
  17. package/dist/lib/constants/contextWindows.js +1 -1
  18. package/dist/lib/constants/enums.d.ts +2 -2
  19. package/dist/lib/constants/enums.js +2 -2
  20. package/dist/lib/constants/tokens.d.ts +1 -1
  21. package/dist/lib/constants/tokens.js +1 -1
  22. package/dist/lib/core/loopEngine.d.ts +23 -0
  23. package/dist/lib/core/loopEngine.js +246 -0
  24. package/dist/lib/core/nativeToolFormat.d.ts +33 -0
  25. package/dist/lib/core/nativeToolFormat.js +31 -0
  26. package/dist/lib/core/streamChannel.d.ts +10 -0
  27. package/dist/lib/core/streamChannel.js +77 -0
  28. package/dist/lib/providers/anthropic/cacheControl.d.ts +12 -0
  29. package/dist/lib/providers/anthropic/cacheControl.js +15 -0
  30. package/dist/lib/providers/anthropic/client.js +12 -45
  31. package/dist/lib/providers/googleAiStudio/client.js +7 -5
  32. package/dist/lib/providers/googleNativeGemini3/utils.d.ts +4 -11
  33. package/dist/lib/providers/googleNativeGemini3/utils.js +1 -75
  34. package/dist/lib/providers/googleVertex/client.js +28 -30
  35. package/dist/lib/providers/googleVertex/constants.js +1 -1
  36. package/dist/lib/providers/openaiChatCompletionsBase.js +10 -12
  37. package/dist/lib/providers/openaiChatCompletionsClient.d.ts +1 -5
  38. package/dist/lib/providers/openaiChatCompletionsClient.js +0 -26
  39. package/dist/lib/types/index.d.ts +3 -0
  40. package/dist/lib/types/index.js +3 -0
  41. package/dist/lib/types/loopEngine.d.ts +78 -0
  42. package/dist/lib/types/loopEngine.js +2 -0
  43. package/dist/lib/types/nativeTools.d.ts +16 -0
  44. package/dist/lib/types/nativeTools.js +2 -0
  45. package/dist/lib/types/openaiCompatible.d.ts +2 -2
  46. package/dist/lib/types/providers.d.ts +0 -13
  47. package/dist/lib/types/streaming.d.ts +15 -0
  48. package/dist/lib/types/streaming.js +2 -0
  49. package/dist/providers/anthropic/cacheControl.d.ts +12 -0
  50. package/dist/providers/anthropic/cacheControl.js +14 -0
  51. package/dist/providers/anthropic/client.js +12 -45
  52. package/dist/providers/googleAiStudio/client.js +7 -5
  53. package/dist/providers/googleNativeGemini3/utils.d.ts +4 -11
  54. package/dist/providers/googleNativeGemini3/utils.js +1 -75
  55. package/dist/providers/googleVertex/client.js +28 -30
  56. package/dist/providers/googleVertex/constants.js +1 -1
  57. package/dist/providers/openaiChatCompletionsBase.js +10 -12
  58. package/dist/providers/openaiChatCompletionsClient.d.ts +1 -5
  59. package/dist/providers/openaiChatCompletionsClient.js +0 -26
  60. package/dist/types/index.d.ts +3 -0
  61. package/dist/types/index.js +3 -0
  62. package/dist/types/loopEngine.d.ts +78 -0
  63. package/dist/types/loopEngine.js +1 -0
  64. package/dist/types/nativeTools.d.ts +16 -0
  65. package/dist/types/nativeTools.js +1 -0
  66. package/dist/types/openaiCompatible.d.ts +2 -2
  67. package/dist/types/providers.d.ts +0 -13
  68. package/dist/types/streaming.d.ts +15 -0
  69. package/dist/types/streaming.js +1 -0
  70. package/package.json +3 -1
@@ -1545,7 +1545,7 @@ export class MCPCommandFactory {
1545
1545
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
1546
1546
  tools: [
1547
1547
  ${tools
1548
- .map((toolName) => ` { name: "${toolName}", description: "TODO: Add description for ${toolName}", inputSchema: { type: "object", properties: {}, required: [] } },`)
1548
+ .map((toolName) => ` { name: "${toolName}", description: "Add a description for ${toolName}", inputSchema: { type: "object", properties: {}, required: [] } },`)
1549
1549
  .join("\n")}
1550
1550
  ],
1551
1551
  }));
@@ -1554,7 +1554,7 @@ ${tools
1554
1554
  switch (request.params.name) {
1555
1555
  ${tools
1556
1556
  .map((toolName) => ` case "${toolName}":
1557
- // TODO: Implement ${toolName} tool
1557
+ // Implement ${toolName} tool logic here
1558
1558
  return {
1559
1559
  content: [{ type: "text", text: "${toolName} executed successfully" }],
1560
1560
  };`)
@@ -1661,7 +1661,7 @@ npm run build # Build for production
1661
1661
 
1662
1662
  ## Tools
1663
1663
 
1664
- ${tools.length > 0 ? tools.map((t) => `- **${t}**: TODO: Add description`).join("\n") : "- **hello**: A simple hello tool"}
1664
+ ${tools.length > 0 ? tools.map((t) => `- **${t}**: add a description`).join("\n") : "- **hello**: A simple hello tool"}
1665
1665
  `;
1666
1666
  fs.writeFileSync(path.join(serverDir, "README.md"), readme);
1667
1667
  }
@@ -1682,7 +1682,7 @@ ${tools.length > 0 ? tools.map((t) => `- **${t}**: TODO: Add description`).join(
1682
1682
  const _safeName = toolName.replace(/-/g, "_");
1683
1683
  const keyword = index === 0 ? "if" : "elif";
1684
1684
  return ` ${keyword} name == "${toolName}":
1685
- # TODO: Implement ${toolName} tool
1685
+ # Implement ${toolName} tool logic here
1686
1686
  return [TextContent(type="text", text="${toolName} executed successfully")]`;
1687
1687
  })
1688
1688
  .join("\n");
@@ -1710,7 +1710,7 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
1710
1710
  .map((toolName) => `
1711
1711
  Tool(
1712
1712
  name="${toolName}",
1713
- description="TODO: Add description for ${toolName}",
1713
+ description="Add a description for ${toolName}",
1714
1714
  inputSchema={
1715
1715
  "type": "object",
1716
1716
  "properties": {},
@@ -1798,7 +1798,7 @@ neurolink mcp add ${serverName} python ${path.join(serverDir, "server.py")}
1798
1798
 
1799
1799
  ## Tools
1800
1800
 
1801
- ${tools.length > 0 ? tools.map((t) => `- **${t}**: TODO: Add description`).join("\n") : "- **hello**: A simple hello tool"}
1801
+ ${tools.length > 0 ? tools.map((t) => `- **${t}**: add a description`).join("\n") : "- **hello**: A simple hello tool"}
1802
1802
  `;
1803
1803
  fs.writeFileSync(path.join(serverDir, "README.md"), readme);
1804
1804
  }
@@ -1840,7 +1840,7 @@ if (handlers[request.params.name]) {
1840
1840
  };
1841
1841
  }`;
1842
1842
  const toolList = tools.length > 0
1843
- ? tools.map((t) => `{ name: "${t}", description: "TODO: Add description", inputSchema: { type: "object", properties: {} } }`)
1843
+ ? tools.map((t) => `{ name: "${t}", description: "Add a description", inputSchema: { type: "object", properties: {} } }`)
1844
1844
  : [
1845
1845
  `{ name: "hello", description: "A simple hello tool", inputSchema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] } }`,
1846
1846
  ];
@@ -1896,7 +1896,7 @@ neurolink mcp add ${serverName} node ${path.join(serverDir, "server.js")}
1896
1896
 
1897
1897
  ## Tools
1898
1898
 
1899
- ${tools.length > 0 ? tools.map((t) => `- **${t}**: TODO: Add description`).join("\n") : "- **hello**: A simple hello tool"}
1899
+ ${tools.length > 0 ? tools.map((t) => `- **${t}**: add a description`).join("\n") : "- **hello**: A simple hello tool"}
1900
1900
  `;
1901
1901
  fs.writeFileSync(path.join(serverDir, "README.md"), readme);
1902
1902
  }
@@ -285,7 +285,7 @@ export const MODEL_CONTEXT_WINDOWS = {
285
285
  "anthropic.claude-opus-4-6-v1:0": 1_000_000,
286
286
  "anthropic.claude-sonnet-4-6": 1_000_000,
287
287
  // Claude 4.5
288
- "anthropic.claude-opus-4-5-20251124-v1:0": 200_000,
288
+ "anthropic.claude-opus-4-5-20251101-v1:0": 200_000,
289
289
  "anthropic.claude-sonnet-4-5-20250929-v1:0": 200_000,
290
290
  "anthropic.claude-haiku-4-5-20251001-v1:0": 200_000,
291
291
  // Claude legacy
@@ -69,7 +69,7 @@ export declare enum OpenRouterModels {
69
69
  export declare enum BedrockModels {
70
70
  CLAUDE_4_6_OPUS = "anthropic.claude-opus-4-6-v1:0",
71
71
  CLAUDE_4_6_SONNET = "anthropic.claude-sonnet-4-6",
72
- CLAUDE_4_5_OPUS = "anthropic.claude-opus-4-5-20251124-v1:0",
72
+ CLAUDE_4_5_OPUS = "anthropic.claude-opus-4-5-20251101-v1:0",
73
73
  CLAUDE_4_5_SONNET = "anthropic.claude-sonnet-4-5-20250929-v1:0",
74
74
  CLAUDE_4_5_HAIKU = "anthropic.claude-haiku-4-5-20251001-v1:0",
75
75
  CLAUDE_4_1_OPUS = "anthropic.claude-opus-4-1-20250805-v1:0",
@@ -256,7 +256,7 @@ export declare enum AzureOpenAIModels {
256
256
  export declare enum VertexModels {
257
257
  CLAUDE_4_6_OPUS = "claude-opus-4-6",
258
258
  CLAUDE_4_6_SONNET = "claude-sonnet-4-6",
259
- CLAUDE_4_5_OPUS = "claude-opus-4-5@20251124",
259
+ CLAUDE_4_5_OPUS = "claude-opus-4-5@20251101",
260
260
  CLAUDE_4_5_SONNET = "claude-sonnet-4-5@20250929",
261
261
  CLAUDE_4_5_HAIKU = "claude-haiku-4-5@20251001",
262
262
  CLAUDE_4_0_SONNET = "claude-sonnet-4@20250514",
@@ -91,7 +91,7 @@ export var BedrockModels;
91
91
  BedrockModels["CLAUDE_4_6_OPUS"] = "anthropic.claude-opus-4-6-v1:0";
92
92
  BedrockModels["CLAUDE_4_6_SONNET"] = "anthropic.claude-sonnet-4-6";
93
93
  // Claude 4.5 Series (September-November 2025)
94
- BedrockModels["CLAUDE_4_5_OPUS"] = "anthropic.claude-opus-4-5-20251124-v1:0";
94
+ BedrockModels["CLAUDE_4_5_OPUS"] = "anthropic.claude-opus-4-5-20251101-v1:0";
95
95
  BedrockModels["CLAUDE_4_5_SONNET"] = "anthropic.claude-sonnet-4-5-20250929-v1:0";
96
96
  BedrockModels["CLAUDE_4_5_HAIKU"] = "anthropic.claude-haiku-4-5-20251001-v1:0";
97
97
  // Claude 4 Series (May-August 2025)
@@ -356,7 +356,7 @@ export var VertexModels;
356
356
  VertexModels["CLAUDE_4_6_OPUS"] = "claude-opus-4-6";
357
357
  VertexModels["CLAUDE_4_6_SONNET"] = "claude-sonnet-4-6";
358
358
  // Claude 4.5 Series (September-November 2025)
359
- VertexModels["CLAUDE_4_5_OPUS"] = "claude-opus-4-5@20251124";
359
+ VertexModels["CLAUDE_4_5_OPUS"] = "claude-opus-4-5@20251101";
360
360
  VertexModels["CLAUDE_4_5_SONNET"] = "claude-sonnet-4-5@20250929";
361
361
  VertexModels["CLAUDE_4_5_HAIKU"] = "claude-haiku-4-5@20251001";
362
362
  // Claude 4 Series (May 2025)
@@ -122,7 +122,7 @@ export declare const PROVIDER_TOKEN_LIMITS: {
122
122
  readonly "gemini-1.5-pro": 8192;
123
123
  readonly "gemini-1.5-flash": 8192;
124
124
  readonly "claude-sonnet-4-5@20250929": 8192;
125
- readonly "claude-opus-4-5@20251124": 8192;
125
+ readonly "claude-opus-4-5@20251101": 8192;
126
126
  readonly "claude-haiku-4-5@20251001": 8192;
127
127
  readonly "claude-sonnet-4@20250514": 4096;
128
128
  readonly "claude-opus-4@20250514": 4096;
@@ -136,7 +136,7 @@ export const PROVIDER_TOKEN_LIMITS = {
136
136
  "gemini-1.5-flash": 8192,
137
137
  // Claude 4.5 Series (September-November 2025)
138
138
  "claude-sonnet-4-5@20250929": 8192,
139
- "claude-opus-4-5@20251124": 8192,
139
+ "claude-opus-4-5@20251101": 8192,
140
140
  "claude-haiku-4-5@20251001": 8192,
141
141
  // Claude 4 Series (May 2025)
142
142
  "claude-sonnet-4@20250514": 4096,
@@ -0,0 +1,23 @@
1
+ import type { AgenticLoopAdapter, AgenticLoopOptions, AgenticLoopResult } from "../types/index.js";
2
+ /**
3
+ * Run one adapter-parameterized agentic tool-calling turn. Owns the
4
+ * maxSteps-bounded loop, generic tool dispatch (with an opt-in
5
+ * TOOL_NOT_FOUND/failure-strike breaker — see AgenticLoopAdapter.toolFailureBreaker),
6
+ * per-step usage accumulation, a single optional malformed-call retry,
7
+ * chunk emission through streamChannel, and a pre-first-chunk 429/5xx
8
+ * retry (via withProviderRetry) around every adapter.executeStep() call.
9
+ * The retry wrap is unconditional and adapter-agnostic — every migrated
10
+ * provider gets it for free, not just the ones that had a hand-rolled
11
+ * version before migration (see Verified Fact 4-adjacent note in Task 4
12
+ * Step 1 and the Risks & Rollback "Deliberate behavior changes" list for
13
+ * which families are gaining this for the first time). Everything
14
+ * wire-format-specific (building the request, parsing the SDK response,
15
+ * serializing tool results back into the conversation, mapping the raw
16
+ * stop reason) is delegated to `adapter`.
17
+ */
18
+ export declare function runAgenticLoop<TConversation>(adapter: AgenticLoopAdapter<TConversation>, initialConversation: TConversation, options: AgenticLoopOptions): {
19
+ stream: AsyncIterable<{
20
+ content: string;
21
+ }>;
22
+ resultPromise: Promise<AgenticLoopResult<TConversation>>;
23
+ };
@@ -0,0 +1,245 @@
1
+ import { createStreamChannel } from "./streamChannel.js";
2
+ import { logger } from "../utils/logger.js";
3
+ import { withProviderRetry } from "../utils/providerRetry.js";
4
+ /**
5
+ * Marks a step error that occurred AFTER at least one chunk had already
6
+ * been streamed to the consumer for this step. Retrying at that point
7
+ * would duplicate or interleave already-emitted output, so this wrapper
8
+ * deliberately carries none of the original error's status/retry
9
+ * metadata (`.statusCode`/`.status`, no APICallError/NeuroLinkError
10
+ * branding) — that makes `withProviderRetry`'s internal
11
+ * `isRetryableProviderError()` check return false via its duck-typed
12
+ * fallback, which ends the retry loop on the very next classification
13
+ * instead of sleeping and re-invoking `adapter.executeStep`. The engine
14
+ * unwraps back to the original `cause` before it ever reaches the
15
+ * caller — see the try/catch around the `withProviderRetry` call below.
16
+ */
17
+ class PostEmissionStepError extends Error {
18
+ cause;
19
+ constructor(cause) {
20
+ super(cause instanceof Error ? cause.message : String(cause));
21
+ this.cause = cause;
22
+ }
23
+ }
24
+ function sumUsage(a, b) {
25
+ return {
26
+ inputTokens: a.inputTokens + b.inputTokens,
27
+ outputTokens: a.outputTokens + b.outputTokens,
28
+ cacheReadTokens: (a.cacheReadTokens ?? 0) + (b.cacheReadTokens ?? 0) || undefined,
29
+ cacheWriteTokens: (a.cacheWriteTokens ?? 0) + (b.cacheWriteTokens ?? 0) || undefined,
30
+ reasoningTokens: (a.reasoningTokens ?? 0) + (b.reasoningTokens ?? 0) || undefined,
31
+ };
32
+ }
33
+ /**
34
+ * Run one adapter-parameterized agentic tool-calling turn. Owns the
35
+ * maxSteps-bounded loop, generic tool dispatch (with an opt-in
36
+ * TOOL_NOT_FOUND/failure-strike breaker — see AgenticLoopAdapter.toolFailureBreaker),
37
+ * per-step usage accumulation, a single optional malformed-call retry,
38
+ * chunk emission through streamChannel, and a pre-first-chunk 429/5xx
39
+ * retry (via withProviderRetry) around every adapter.executeStep() call.
40
+ * The retry wrap is unconditional and adapter-agnostic — every migrated
41
+ * provider gets it for free, not just the ones that had a hand-rolled
42
+ * version before migration (see Verified Fact 4-adjacent note in Task 4
43
+ * Step 1 and the Risks & Rollback "Deliberate behavior changes" list for
44
+ * which families are gaining this for the first time). Everything
45
+ * wire-format-specific (building the request, parsing the SDK response,
46
+ * serializing tool results back into the conversation, mapping the raw
47
+ * stop reason) is delegated to `adapter`.
48
+ */
49
+ export function runAgenticLoop(adapter, initialConversation, options) {
50
+ const channel = createStreamChannel();
51
+ const internalAbort = new AbortController();
52
+ const onCallerAbort = () => internalAbort.abort();
53
+ options.abortSignal?.addEventListener("abort", onCallerAbort);
54
+ if (options.abortSignal?.aborted) {
55
+ internalAbort.abort();
56
+ }
57
+ const failedTools = new Map();
58
+ let malformedRetryUsed = false;
59
+ const resultPromise = (async () => {
60
+ let conversation = initialConversation;
61
+ let usage = { inputTokens: 0, outputTokens: 0 };
62
+ let finalText = "";
63
+ let rawStopReason;
64
+ const allToolCalls = [];
65
+ const allToolExecutions = [];
66
+ let hadToolCallsAtCap = false;
67
+ try {
68
+ for (let step = 0; step < adapter.maxSteps; step++) {
69
+ if (internalAbort.signal.aborted) {
70
+ break;
71
+ }
72
+ if (adapter.planReclaim) {
73
+ const reclaimed = adapter.planReclaim(conversation, step);
74
+ if (reclaimed) {
75
+ conversation = reclaimed.conversation;
76
+ }
77
+ }
78
+ const request = adapter.buildStepRequest(conversation, step);
79
+ // Pre-first-chunk 429/5xx retry: watch whether THIS attempt of
80
+ // THIS step pushes anything to the shared channel before it
81
+ // throws. `hasEmitted` resets at the top of every attempt
82
+ // withProviderRetry makes; the instant an attempt emits and then
83
+ // throws, the thrown error is rewrapped as a PostEmissionStepError
84
+ // (no status/branding info survives the rewrap), which
85
+ // isRetryableProviderError() duck-types as non-retryable — so
86
+ // withProviderRetry gives up immediately instead of sleeping and
87
+ // re-invoking executeStep, which would duplicate/interleave
88
+ // output already sent to the consumer. The original error (not
89
+ // the wrapper) is what the caller of runAgenticLoop ultimately
90
+ // sees, via the unwrap in the catch below.
91
+ let hasEmitted = false;
92
+ const watchedChannel = {
93
+ push: (chunk) => {
94
+ hasEmitted = true;
95
+ channel.push(chunk);
96
+ },
97
+ };
98
+ let stepResult;
99
+ try {
100
+ stepResult = await withProviderRetry(async () => {
101
+ hasEmitted = false;
102
+ try {
103
+ return await adapter.executeStep(request, watchedChannel, internalAbort.signal);
104
+ }
105
+ catch (err) {
106
+ throw hasEmitted ? new PostEmissionStepError(err) : err;
107
+ }
108
+ }, undefined, // no OTel span threaded through the engine today; adapters instrument their own steps if they need span-level detail
109
+ `${adapter.providerLabel}.step`);
110
+ }
111
+ catch (err) {
112
+ throw err instanceof PostEmissionStepError ? err.cause : err;
113
+ }
114
+ usage = sumUsage(usage, stepResult.usage);
115
+ rawStopReason = stepResult.rawStopReason;
116
+ if (adapter.isMalformedStep?.(stepResult) &&
117
+ !malformedRetryUsed &&
118
+ !internalAbort.signal.aborted) {
119
+ malformedRetryUsed = true;
120
+ logger.warn(`[${adapter.providerLabel}] Malformed function call at step ${step + 1}/${adapter.maxSteps}; retrying once.`);
121
+ conversation =
122
+ adapter.buildMalformedRetryNote?.(conversation) ?? conversation;
123
+ continue;
124
+ }
125
+ if (stepResult.toolCalls.length === 0) {
126
+ finalText = stepResult.text || finalText;
127
+ break;
128
+ }
129
+ if (step === adapter.maxSteps - 1) {
130
+ hadToolCallsAtCap = true;
131
+ }
132
+ const toolResults = [];
133
+ for (const call of stepResult.toolCalls) {
134
+ allToolCalls.push(call);
135
+ const breaker = adapter.toolFailureBreaker;
136
+ const failInfo = breaker ? failedTools.get(call.name) : undefined;
137
+ if (breaker && failInfo && failInfo.count >= breaker.maxRetries) {
138
+ const output = {
139
+ error: `TOOL_PERMANENTLY_FAILED: "${call.name}" has failed ${failInfo.count} times. Last error: ${failInfo.lastError}.`,
140
+ status: "permanently_failed",
141
+ do_not_retry: true,
142
+ };
143
+ toolResults.push({
144
+ ...call,
145
+ output,
146
+ error: output.error,
147
+ permanentlyFailed: true,
148
+ });
149
+ allToolExecutions.push({
150
+ name: call.name,
151
+ input: call.args,
152
+ output,
153
+ });
154
+ continue;
155
+ }
156
+ const tool = options.tools?.[call.name];
157
+ if (!tool?.execute) {
158
+ const output = breaker
159
+ ? {
160
+ error: `TOOL_NOT_FOUND: "${call.name}" does not exist.`,
161
+ status: "permanently_failed",
162
+ do_not_retry: true,
163
+ }
164
+ : { error: `Tool not found: ${call.name}` };
165
+ toolResults.push({
166
+ ...call,
167
+ output,
168
+ error: output.error,
169
+ permanentlyFailed: !!breaker,
170
+ });
171
+ allToolExecutions.push({
172
+ name: call.name,
173
+ input: call.args,
174
+ output,
175
+ });
176
+ continue;
177
+ }
178
+ try {
179
+ const output = await tool.execute(call.args, {
180
+ toolCallId: call.id,
181
+ abortSignal: internalAbort.signal,
182
+ });
183
+ toolResults.push({ ...call, output });
184
+ allToolExecutions.push({
185
+ name: call.name,
186
+ input: call.args,
187
+ output,
188
+ });
189
+ }
190
+ catch (err) {
191
+ const message = err instanceof Error ? err.message : String(err);
192
+ if (breaker) {
193
+ const current = failedTools.get(call.name) ?? {
194
+ count: 0,
195
+ lastError: "",
196
+ };
197
+ current.count++;
198
+ current.lastError = message;
199
+ failedTools.set(call.name, current);
200
+ }
201
+ const output = { error: message, status: "failed" };
202
+ toolResults.push({ ...call, output, error: message });
203
+ allToolExecutions.push({
204
+ name: call.name,
205
+ input: call.args,
206
+ output,
207
+ });
208
+ }
209
+ }
210
+ conversation = adapter.buildToolResultMessages(conversation, stepResult, toolResults);
211
+ }
212
+ const finishReason = adapter.mapFinishReason(rawStopReason, hadToolCallsAtCap);
213
+ return {
214
+ text: finalText,
215
+ toolCalls: allToolCalls,
216
+ toolExecutions: allToolExecutions,
217
+ usage,
218
+ finishReason,
219
+ rawStopReason,
220
+ conversation,
221
+ };
222
+ }
223
+ catch (err) {
224
+ // Must run before the finally block's channel.close(): a consumer
225
+ // parked in the channel's iterable is woken by whichever of
226
+ // error()/close() runs first, and close() alone would let a
227
+ // stream-only consumer observe a clean end of stream for a turn that
228
+ // actually failed. Calling error() here — synchronously, inside this
229
+ // catch — guarantees it lands before close(), with no dependence on
230
+ // microtask scheduling (unlike an outer promise-chained catch handler
231
+ // on this IIFE, which would run in a later microtask after `finally`
232
+ // already closed the channel).
233
+ channel.error(err);
234
+ throw err;
235
+ }
236
+ finally {
237
+ // close() after error() is harmless: it only flips `done`, and
238
+ // streamChannel.error() keeps its error state intact regardless of a
239
+ // later close() call.
240
+ channel.close();
241
+ options.abortSignal?.removeEventListener("abort", onCallerAbort);
242
+ }
243
+ })();
244
+ return { stream: channel.iterable, resultPromise };
245
+ }
@@ -0,0 +1,33 @@
1
+ import type { NativeAnthropicToolDeclaration, NativeToolDeclarationsResult, Tool } from "../types/index.js";
2
+ /**
3
+ * Convert a NeuroLink tool record into the wire-format a native
4
+ * (non-AI-SDK) provider SDK expects. Absorbs the direct-Anthropic
5
+ * provider's `toolsToAnthropic` — previously duplicated between the
6
+ * streaming loop's pre-loop snapshot and its mid-turn discovery-hydration
7
+ * call site (both in anthropic/client.ts). Gemini's `functionDeclarations`
8
+ * shape was already centralized in `buildNativeToolDeclarations`; this
9
+ * function is a thin facade over it so every native provider calls one
10
+ * entry point.
11
+ *
12
+ * Scope note: Vertex's Claude-on-Vertex `input_schema` builder
13
+ * (`buildAnthropicToolDeclaration` in googleVertex/client.ts) is
14
+ * deliberately NOT routed through this function. Despite the superficial
15
+ * similarity, it is not a byte-for-byte duplicate of `toolsToAnthropic`: it
16
+ * strips the converted schema down to `{type, properties, required}` only
17
+ * (dropping any other JSON-Schema keywords `convertZodToJsonSchema` may
18
+ * produce), it always runs `inlineJsonSchema` (Anthropic-direct never has),
19
+ * it prefers `parameters` over `inputSchema` (the opposite fallback order
20
+ * from Anthropic-direct), and it has no `cache_control` support of its own
21
+ * because Vertex applies cache breakpoints later via the separate
22
+ * `applyVertexAnthropicCacheBreakpoints` pass. Collapsing these into one
23
+ * shared implementation would risk a live tool-calling regression on one
24
+ * provider or the other; see the Task 2 report for the full comparison.
25
+ *
26
+ * These are genuine TS overload declarations, not redeclarations: the base
27
+ * `no-redeclare` ESLint rule doesn't understand the overload-signatures +
28
+ * implementation pattern (the TS-aware `@typescript-eslint/no-redeclare`
29
+ * variant would, but isn't enabled project-wide), so each signature below
30
+ * is individually exempted.
31
+ */
32
+ export declare function toNativeToolDeclarations(tools: Record<string, Tool>, format: "input_schema"): NativeAnthropicToolDeclaration[] | undefined;
33
+ export declare function toNativeToolDeclarations(tools: Record<string, Tool>, format: "functionDeclarations"): NativeToolDeclarationsResult;
@@ -0,0 +1,30 @@
1
+ import { buildNativeToolDeclarations } from "../providers/googleNativeGemini3/utils.js";
2
+ import { cacheControlOf } from "../providers/anthropic/cacheControl.js";
3
+ import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
4
+ // eslint-disable-next-line no-redeclare -- TS overload implementation signature, not a redeclaration
5
+ export function toNativeToolDeclarations(tools, format) {
6
+ if (format === "functionDeclarations") {
7
+ return buildNativeToolDeclarations(tools);
8
+ }
9
+ const entries = Object.entries(tools ?? {});
10
+ if (entries.length === 0) {
11
+ return undefined;
12
+ }
13
+ return entries.map(([name, tool]) => {
14
+ const t = tool;
15
+ const rawSchema = t.inputSchema ?? t.parameters;
16
+ const input_schema = (rawSchema
17
+ ? convertZodToJsonSchema(rawSchema)
18
+ : { type: "object", properties: {} });
19
+ // GenerationHandler marks the last tool definition with a cache
20
+ // breakpoint when prompt caching is active — keep honoring it.
21
+ const cc = cacheControlOf(tool);
22
+ const declaration = {
23
+ name,
24
+ ...(t.description ? { description: t.description } : {}),
25
+ input_schema,
26
+ ...(cc ? { cache_control: cc } : {}),
27
+ };
28
+ return declaration;
29
+ });
30
+ }
@@ -0,0 +1,10 @@
1
+ import type { StreamChannel } from "../types/index.js";
2
+ /**
3
+ * Create a push-based channel bridging a background producer (an agentic
4
+ * tool-calling loop) with an async-iterable consumer, enabling truly
5
+ * incremental streaming: values are yielded to the caller as they arrive
6
+ * rather than being buffered until the producer finishes.
7
+ */
8
+ export declare function createStreamChannel<T = {
9
+ content: string;
10
+ }>(): StreamChannel<T>;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Create a push-based channel bridging a background producer (an agentic
3
+ * tool-calling loop) with an async-iterable consumer, enabling truly
4
+ * incremental streaming: values are yielded to the caller as they arrive
5
+ * rather than being buffered until the producer finishes.
6
+ */
7
+ export function createStreamChannel() {
8
+ const queue = [];
9
+ let done = false;
10
+ let fatalError = undefined;
11
+ // Tracked separately from `fatalError`: a producer can legitimately
12
+ // reject with `undefined` (e.g. `throw undefined`), and `fatalError !==
13
+ // undefined` would then be indistinguishable from "no error occurred",
14
+ // closing the stream cleanly instead of surfacing the failure.
15
+ let hasError = false;
16
+ let notify = null;
17
+ function wake() {
18
+ if (notify) {
19
+ const fn = notify;
20
+ notify = null;
21
+ fn();
22
+ }
23
+ }
24
+ function push(value) {
25
+ if (done) {
26
+ return;
27
+ }
28
+ queue.push(value);
29
+ wake();
30
+ }
31
+ function close() {
32
+ done = true;
33
+ wake();
34
+ }
35
+ function error(err) {
36
+ done = true;
37
+ fatalError = err;
38
+ hasError = true;
39
+ wake();
40
+ }
41
+ let readIndex = 0;
42
+ async function* iterable() {
43
+ try {
44
+ while (true) {
45
+ if (readIndex < queue.length) {
46
+ yield queue[readIndex++];
47
+ // Periodically compact consumed entries to avoid unbounded retention.
48
+ if (readIndex > 1024 && readIndex * 2 >= queue.length) {
49
+ queue.splice(0, readIndex);
50
+ readIndex = 0;
51
+ }
52
+ }
53
+ else if (done) {
54
+ if (hasError) {
55
+ throw fatalError instanceof Error
56
+ ? fatalError
57
+ : new Error(String(fatalError));
58
+ }
59
+ return;
60
+ }
61
+ else {
62
+ await new Promise((resolve) => {
63
+ notify = resolve;
64
+ });
65
+ }
66
+ }
67
+ }
68
+ finally {
69
+ // Consumer stopped reading (disconnect/cancel): stop buffering.
70
+ done = true;
71
+ queue.length = 0;
72
+ notify?.();
73
+ }
74
+ }
75
+ return { push, close, error, iterable: iterable() };
76
+ }
@@ -412,7 +412,7 @@ const VISION_CAPABILITIES = {
412
412
  "claude-opus-4-5",
413
413
  "claude-opus-4.5",
414
414
  "anthropic.claude-opus-4-5",
415
- "anthropic.claude-opus-4-5-20251124-v1:0",
415
+ "anthropic.claude-opus-4-5-20251101-v1:0",
416
416
  "claude-haiku-4-5",
417
417
  "claude-haiku-4.5",
418
418
  "anthropic.claude-haiku-4-5",
@@ -285,7 +285,7 @@ export const MODEL_CONTEXT_WINDOWS = {
285
285
  "anthropic.claude-opus-4-6-v1:0": 1_000_000,
286
286
  "anthropic.claude-sonnet-4-6": 1_000_000,
287
287
  // Claude 4.5
288
- "anthropic.claude-opus-4-5-20251124-v1:0": 200_000,
288
+ "anthropic.claude-opus-4-5-20251101-v1:0": 200_000,
289
289
  "anthropic.claude-sonnet-4-5-20250929-v1:0": 200_000,
290
290
  "anthropic.claude-haiku-4-5-20251001-v1:0": 200_000,
291
291
  // Claude legacy
@@ -69,7 +69,7 @@ export declare enum OpenRouterModels {
69
69
  export declare enum BedrockModels {
70
70
  CLAUDE_4_6_OPUS = "anthropic.claude-opus-4-6-v1:0",
71
71
  CLAUDE_4_6_SONNET = "anthropic.claude-sonnet-4-6",
72
- CLAUDE_4_5_OPUS = "anthropic.claude-opus-4-5-20251124-v1:0",
72
+ CLAUDE_4_5_OPUS = "anthropic.claude-opus-4-5-20251101-v1:0",
73
73
  CLAUDE_4_5_SONNET = "anthropic.claude-sonnet-4-5-20250929-v1:0",
74
74
  CLAUDE_4_5_HAIKU = "anthropic.claude-haiku-4-5-20251001-v1:0",
75
75
  CLAUDE_4_1_OPUS = "anthropic.claude-opus-4-1-20250805-v1:0",
@@ -256,7 +256,7 @@ export declare enum AzureOpenAIModels {
256
256
  export declare enum VertexModels {
257
257
  CLAUDE_4_6_OPUS = "claude-opus-4-6",
258
258
  CLAUDE_4_6_SONNET = "claude-sonnet-4-6",
259
- CLAUDE_4_5_OPUS = "claude-opus-4-5@20251124",
259
+ CLAUDE_4_5_OPUS = "claude-opus-4-5@20251101",
260
260
  CLAUDE_4_5_SONNET = "claude-sonnet-4-5@20250929",
261
261
  CLAUDE_4_5_HAIKU = "claude-haiku-4-5@20251001",
262
262
  CLAUDE_4_0_SONNET = "claude-sonnet-4@20250514",
@@ -91,7 +91,7 @@ export var BedrockModels;
91
91
  BedrockModels["CLAUDE_4_6_OPUS"] = "anthropic.claude-opus-4-6-v1:0";
92
92
  BedrockModels["CLAUDE_4_6_SONNET"] = "anthropic.claude-sonnet-4-6";
93
93
  // Claude 4.5 Series (September-November 2025)
94
- BedrockModels["CLAUDE_4_5_OPUS"] = "anthropic.claude-opus-4-5-20251124-v1:0";
94
+ BedrockModels["CLAUDE_4_5_OPUS"] = "anthropic.claude-opus-4-5-20251101-v1:0";
95
95
  BedrockModels["CLAUDE_4_5_SONNET"] = "anthropic.claude-sonnet-4-5-20250929-v1:0";
96
96
  BedrockModels["CLAUDE_4_5_HAIKU"] = "anthropic.claude-haiku-4-5-20251001-v1:0";
97
97
  // Claude 4 Series (May-August 2025)
@@ -356,7 +356,7 @@ export var VertexModels;
356
356
  VertexModels["CLAUDE_4_6_OPUS"] = "claude-opus-4-6";
357
357
  VertexModels["CLAUDE_4_6_SONNET"] = "claude-sonnet-4-6";
358
358
  // Claude 4.5 Series (September-November 2025)
359
- VertexModels["CLAUDE_4_5_OPUS"] = "claude-opus-4-5@20251124";
359
+ VertexModels["CLAUDE_4_5_OPUS"] = "claude-opus-4-5@20251101";
360
360
  VertexModels["CLAUDE_4_5_SONNET"] = "claude-sonnet-4-5@20250929";
361
361
  VertexModels["CLAUDE_4_5_HAIKU"] = "claude-haiku-4-5@20251001";
362
362
  // Claude 4 Series (May 2025)
@@ -122,7 +122,7 @@ export declare const PROVIDER_TOKEN_LIMITS: {
122
122
  readonly "gemini-1.5-pro": 8192;
123
123
  readonly "gemini-1.5-flash": 8192;
124
124
  readonly "claude-sonnet-4-5@20250929": 8192;
125
- readonly "claude-opus-4-5@20251124": 8192;
125
+ readonly "claude-opus-4-5@20251101": 8192;
126
126
  readonly "claude-haiku-4-5@20251001": 8192;
127
127
  readonly "claude-sonnet-4@20250514": 4096;
128
128
  readonly "claude-opus-4@20250514": 4096;
@@ -136,7 +136,7 @@ export const PROVIDER_TOKEN_LIMITS = {
136
136
  "gemini-1.5-flash": 8192,
137
137
  // Claude 4.5 Series (September-November 2025)
138
138
  "claude-sonnet-4-5@20250929": 8192,
139
- "claude-opus-4-5@20251124": 8192,
139
+ "claude-opus-4-5@20251101": 8192,
140
140
  "claude-haiku-4-5@20251001": 8192,
141
141
  // Claude 4 Series (May 2025)
142
142
  "claude-sonnet-4@20250514": 4096,