@juspay/neurolink 11.11.4 → 11.11.6

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.
@@ -46,6 +46,7 @@ export * from "./ppt.js";
46
46
  export * from "./processor.js";
47
47
  export * from "./providers.js";
48
48
  export * from "./proxy.js";
49
+ export * from "./proxyClient.js";
49
50
  export * from "./rag.js";
50
51
  export * from "./scorer.js";
51
52
  export * from "./sdk.js";
@@ -47,6 +47,7 @@ export * from "./ppt.js";
47
47
  export * from "./processor.js";
48
48
  export * from "./providers.js";
49
49
  export * from "./proxy.js";
50
+ export * from "./proxyClient.js";
50
51
  export * from "./rag.js";
51
52
  export * from "./scorer.js";
52
53
  export * from "./sdk.js";
@@ -0,0 +1,54 @@
1
+ /**
2
+ * One AI coding CLI the proxy can point at itself.
3
+ *
4
+ * Adding a CLI means adding one implementation of this type and one line in
5
+ * `src/cli/proxy-clients/registry.ts`. Nothing else in the proxy should need
6
+ * to know the client exists.
7
+ */
8
+ export type CliProxyClientConfigurator = {
9
+ /** Stable kebab-case identifier, e.g. "claude-code". */
10
+ id: string;
11
+ /** Human-readable name used in CLI output, e.g. "Claude Code". */
12
+ displayName: string;
13
+ /**
14
+ * Whether this CLI appears to be installed. Configurators must not create
15
+ * config files for a CLI the user never installed.
16
+ */
17
+ detect: () => Promise<boolean>;
18
+ /**
19
+ * Point the CLI at the proxy. `proxyBaseUrl` is the bare proxy origin
20
+ * (e.g. "http://127.0.0.1:55669"); the configurator appends whatever path
21
+ * suffix its CLI needs. Returns false when nothing was written, so callers
22
+ * never print a success message for work that did not happen.
23
+ */
24
+ apply: (proxyBaseUrl: string) => Promise<boolean>;
25
+ /**
26
+ * Restore the user's previous configuration. `proxyBaseUrl` is the same bare
27
+ * origin; a configurator that finds a different URL configured must leave it
28
+ * alone and return false.
29
+ */
30
+ restore: (proxyBaseUrl: string) => Promise<boolean>;
31
+ };
32
+ /** Outcome of applying one configurator, for per-client CLI reporting. */
33
+ export type CliProxyClientApplyResult = {
34
+ id: string;
35
+ displayName: string;
36
+ /** True only when the configurator actually wrote configuration. */
37
+ applied: boolean;
38
+ /** Present when the configurator threw; the caller decides how loud to be. */
39
+ error?: Error;
40
+ };
41
+ /** Outcome of restoring one configurator. */
42
+ export type CliProxyClientRestoreResult = {
43
+ id: string;
44
+ displayName: string;
45
+ /** True only when a previous configuration was actually restored. */
46
+ restored: boolean;
47
+ error?: Error;
48
+ };
49
+ /**
50
+ * Raw contents of a Qwen Code `settings.json`. Deliberately open-ended: the
51
+ * configurator rewrites only `security.auth` and must round-trip every other
52
+ * key the user has set, including ones this repo does not know about.
53
+ */
54
+ export type CliQwenSettings = Record<string, unknown>;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=proxyClient.js.map
@@ -17,7 +17,7 @@ import { withTimeout } from "../../utils/async/index.js";
17
17
  import { estimateTokens } from "../../utils/tokenEstimation.js";
18
18
  import { transformToolExecutions } from "../../utils/transformationUtils.js";
19
19
  import { resolveToolExecutionRecords } from "../../core/toolExecutionRecorder.js";
20
- import { buildGeminiResponseSchema, buildNativeConfig, collectStreamChunks, computeMaxSteps, createContextGuard, buildUserPartsWithMultimodal, executeNativeToolCalls, extractTextFromParts, extractThoughtSignature, handleMaxStepsTermination, prependConversationMessages, pushModelResponseToHistory, refreshNativeToolDeclarations, DedupExecuteMap, } from "../googleNativeGemini3/index.js";
20
+ import { buildGeminiResponseSchema, buildNativeConfig, computeMaxSteps, createContextGuard, buildUserPartsWithMultimodal, extractThoughtSignature, handleMaxStepsTermination, prependConversationMessages, } from "../googleNativeGemini3/index.js";
21
21
  import { createStreamChannel } from "../../core/streamChannel.js";
22
22
  import { toNativeToolDeclarations } from "../../core/nativeToolFormat.js";
23
23
  import { createProxyFetch } from "../../proxy/proxyFetch.js";
@@ -1004,8 +1004,6 @@ export class GoogleAIStudioProvider extends BaseProvider {
1004
1004
  });
1005
1005
  // Convert tools (a0269210: trust options.tools — already merged + filtered upstream)
1006
1006
  let toolsConfig;
1007
- let executeMap = new DedupExecuteMap();
1008
- let originalNameMap = new Map();
1009
1007
  let declarationsResult;
1010
1008
  const shouldUseTools = !options.disableTools;
1011
1009
  // Structured output (JSON format or schema) is incompatible with
@@ -1032,8 +1030,6 @@ export class GoogleAIStudioProvider extends BaseProvider {
1032
1030
  const result = toNativeToolDeclarations(tools, "functionDeclarations");
1033
1031
  declarationsResult = result;
1034
1032
  toolsConfig = result.toolsConfig;
1035
- executeMap = result.executeMap;
1036
- originalNameMap = result.originalNameMap;
1037
1033
  logger.debug("[GoogleAIStudio] Converted tools for native SDK generate", {
1038
1034
  toolCount: toolsConfig[0].functionDeclarations.length,
1039
1035
  toolNames: toolsConfig[0].functionDeclarations.map((t) => t.name),
@@ -1066,114 +1062,128 @@ export class GoogleAIStudioProvider extends BaseProvider {
1066
1062
  const allToolCalls = [];
1067
1063
  const toolExecutions = [];
1068
1064
  let step = 0;
1069
- const failedTools = new Map();
1070
1065
  // Cheap reclaim trigger — see the stream twin.
1071
1066
  const contextGuard = createContextGuard(getContextWindowSize("googleAiStudio", modelName));
1072
1067
  // Agentic loop for tool calling
1073
- while (step < maxSteps) {
1074
- // In-turn context guard see the stream twin.
1075
- if (step === 0 || contextGuard.shouldStop()) {
1076
- if (reclaimAiStudioContext(currentContents, modelName, contextGuard.projectedNextPromptTokens)) {
1077
- contextGuard.resetAfterReclaim();
1068
+ // Same shared engine as the streaming twin. This path has no
1069
+ // consumer channelgenerate() returns one result rather than
1070
+ // streaming so the engine's stream is drained and discarded, and
1071
+ // the turn's text comes from the result.
1072
+ const baseAdapter = createGeminiLoopAdapter({
1073
+ providerLabel: "GoogleAIStudio",
1074
+ maxSteps,
1075
+ toolFailureBreaker: { maxRetries: DEFAULT_TOOL_MAX_RETRIES },
1076
+ liveTools: options.tools ?? {},
1077
+ ...(declarationsResult ? { declarations: declarationsResult } : {}),
1078
+ buildRequest: (contents) => ({
1079
+ model: modelName,
1080
+ contents,
1081
+ config,
1082
+ ...(composedSignal
1083
+ ? { httpOptions: { signal: composedSignal } }
1084
+ : {}),
1085
+ }),
1086
+ sendStep: async (request) => client.models.generateContentStream(request),
1087
+ noteUsage: (inputTokens, outputTokens) => {
1088
+ contextGuard.noteUsage(inputTokens, outputTokens);
1089
+ },
1090
+ planReclaim: (contents, stepIndex) => {
1091
+ if (stepIndex !== 0 && !contextGuard.shouldStop()) {
1092
+ return undefined;
1078
1093
  }
1079
- }
1080
- if (composedSignal?.aborted) {
1081
- throw composedSignal.reason instanceof Error
1082
- ? composedSignal.reason
1083
- : new Error("Request aborted");
1084
- }
1085
- step++;
1086
- // Mid-turn discovery sync — see the stream twin.
1087
- if (declarationsResult) {
1088
- refreshNativeToolDeclarations(options.tools, declarationsResult);
1089
- }
1090
- logger.debug(`[GoogleAIStudio] Native SDK generate step ${step}/${maxSteps}`);
1091
- try {
1092
- const stream = await client.models.generateContentStream({
1093
- model: modelName,
1094
- contents: currentContents,
1095
- config,
1096
- ...(composedSignal
1097
- ? { httpOptions: { signal: composedSignal } }
1098
- : {}),
1099
- });
1100
- const chunkResult = await collectStreamChunks(stream);
1101
- totalInputTokens += chunkResult.inputTokens;
1102
- totalOutputTokens += chunkResult.outputTokens;
1103
- totalCacheReadTokens += chunkResult.cacheReadTokens ?? 0;
1104
- totalReasoningTokens += chunkResult.reasoningTokens ?? 0;
1105
- contextGuard.noteUsage(chunkResult.inputTokens, chunkResult.outputTokens);
1106
- const stepText = extractTextFromParts(chunkResult.rawResponseParts);
1107
- // If no function calls, we're done
1108
- if (chunkResult.stepFunctionCalls.length === 0) {
1109
- finalText = stepText;
1110
- break;
1094
+ const working = [...contents];
1095
+ if (!reclaimAiStudioContext(working, modelName, contextGuard.projectedNextPromptTokens)) {
1096
+ return undefined;
1111
1097
  }
1112
- lastStepText = stepText;
1113
- // Record tool call events on the span
1114
- for (const fc of chunkResult.stepFunctionCalls) {
1098
+ contextGuard.resetAfterReclaim();
1099
+ return working;
1100
+ },
1101
+ });
1102
+ const adapter = {
1103
+ ...baseAdapter,
1104
+ buildToolResultMessages: (contents, stepResult, toolResults) => {
1105
+ step++;
1106
+ for (const call of stepResult.toolCalls) {
1115
1107
  span.addEvent("gen_ai.tool_call", {
1116
- "tool.name": fc.name,
1108
+ "tool.name": call.name,
1117
1109
  "tool.step": step,
1118
1110
  });
1111
+ allToolCalls.push({ toolName: call.name, args: call.args });
1119
1112
  }
1120
- logger.debug(`[GoogleAIStudio] Executing ${chunkResult.stepFunctionCalls.length} function calls in generate`);
1121
- // Add model response with ALL parts (including thoughtSignature) to history
1122
- // This is critical for Gemini 3 - it requires thought signatures in subsequent turns
1123
- pushModelResponseToHistory(currentContents, chunkResult.rawResponseParts, chunkResult.stepFunctionCalls);
1124
- const toolCallsBefore = allToolCalls.length;
1125
- const toolExecsBefore = toolExecutions.length;
1126
- const functionResponses = await executeNativeToolCalls("[GoogleAIStudio]", chunkResult.stepFunctionCalls, executeMap, failedTools, allToolCalls, {
1127
- toolExecutions,
1128
- abortSignal: composedSignal,
1129
- originalNameMap,
1130
- liveTools: options.tools,
1131
- declarations: declarationsResult,
1132
- });
1133
- // Persist this step's tool calls/results into conversation memory.
1134
- const stepToolCalls = allToolCalls.slice(toolCallsBefore);
1135
- const stepToolExecs = toolExecutions.slice(toolExecsBefore);
1136
- if (stepToolCalls.length > 0 || stepToolExecs.length > 0) {
1137
- const stepThoughtSig = extractThoughtSignature(chunkResult.rawResponseParts);
1138
- withTimeout(this.handleToolExecutionStorage(stepToolCalls.map((tc, i) => ({
1139
- toolName: tc.toolName,
1140
- args: tc.args,
1113
+ lastStepText = stepResult.text || lastStepText;
1114
+ for (const result of toolResults) {
1115
+ toolExecutions.push({
1116
+ name: result.name,
1117
+ input: result.args,
1118
+ output: result.output,
1119
+ });
1120
+ }
1121
+ if (toolResults.length > 0) {
1122
+ const stepThoughtSig = extractThoughtSignature(stepResult.raw.rawResponseParts);
1123
+ withTimeout(this.handleToolExecutionStorage(stepResult.toolCalls.map((call, i) => ({
1124
+ toolName: call.name,
1125
+ args: call.args,
1141
1126
  ...(i === 0 && stepThoughtSig
1142
1127
  ? { thoughtSignature: stepThoughtSig }
1143
1128
  : {}),
1144
1129
  stepIndex: step,
1145
- })), stepToolExecs.map((te) => ({
1146
- toolName: te.name,
1147
- output: te.output,
1130
+ })), toolResults.map((result) => ({
1131
+ toolName: result.name,
1132
+ output: result.output,
1148
1133
  stepIndex: step,
1149
1134
  })), options, new Date()), TOOL_STORAGE_TIMEOUT_MS, "tool storage write timed out").catch((error) => {
1150
- logger.warn("[GoogleAIStudio] Failed to store native generate tool executions", {
1135
+ logger.warn("[GoogleAIStudio] Failed to store native tool executions", {
1151
1136
  error: error instanceof Error ? error.message : String(error),
1152
1137
  });
1153
1138
  });
1154
1139
  }
1155
- // Add function responses to history — the @google/genai SDK
1156
- // only accepts "user" and "model" as valid roles in contents.
1157
- // Function/tool responses must use role: "user" (matching the
1158
- // SDK's own automaticFunctionCalling implementation).
1159
- currentContents.push({
1160
- role: "user",
1161
- parts: functionResponses,
1162
- });
1163
- // Project this step's growth: the appended tool results ride
1164
- // the next prompt, which the provider has not reported on yet.
1140
+ const next = baseAdapter.buildToolResultMessages(contents, stepResult, toolResults);
1165
1141
  try {
1166
- contextGuard.noteAppendedChars(JSON.stringify(functionResponses).length);
1142
+ const appended = next[next.length - 1];
1143
+ contextGuard.noteAppendedChars(JSON.stringify(appended?.parts ?? []).length);
1167
1144
  }
1168
1145
  catch {
1169
1146
  /* estimation is best-effort — never break the loop */
1170
1147
  }
1148
+ return next;
1149
+ },
1150
+ };
1151
+ const engineTools = {};
1152
+ for (const [name, tool] of Object.entries(options.tools ?? {})) {
1153
+ const execute = tool?.execute;
1154
+ if (!execute) {
1155
+ continue;
1171
1156
  }
1172
- catch (error) {
1173
- logger.error("[GoogleAIStudio] Native SDK generate error", error);
1174
- throw this.handleProviderError(error);
1157
+ engineTools[name] = {
1158
+ execute: async (args, opts) => execute(args, opts),
1159
+ };
1160
+ }
1161
+ const { stream: engineStream, resultPromise } = runAgenticLoop(adapter, currentContents, {
1162
+ tools: engineTools,
1163
+ ...(composedSignal ? { abortSignal: composedSignal } : {}),
1164
+ });
1165
+ // Drained, not consumed: nothing streams out of generate(), but an
1166
+ // undrained channel would stall the engine mid-turn.
1167
+ const drain = (async () => {
1168
+ for await (const chunk of engineStream) {
1169
+ void chunk;
1175
1170
  }
1171
+ })();
1172
+ let engineResult;
1173
+ try {
1174
+ engineResult = await resultPromise;
1175
+ }
1176
+ catch (error) {
1177
+ await drain.catch(() => { });
1178
+ logger.error("[GoogleAIStudio] Native SDK generate error", error);
1179
+ throw this.handleProviderError(error);
1176
1180
  }
1181
+ await drain;
1182
+ totalInputTokens += engineResult.usage.inputTokens;
1183
+ totalOutputTokens += engineResult.usage.outputTokens;
1184
+ totalCacheReadTokens += engineResult.usage.cacheReadTokens ?? 0;
1185
+ totalReasoningTokens += engineResult.usage.reasoningTokens ?? 0;
1186
+ finalText = engineResult.text;
1177
1187
  finalText = handleMaxStepsTermination("[GoogleAIStudio]", step, maxSteps, finalText, lastStepText);
1178
1188
  const responseTime = Date.now() - startTime;
1179
1189
  // Set token usage and finish reason on the span
@@ -46,6 +46,7 @@ export * from "./ppt.js";
46
46
  export * from "./processor.js";
47
47
  export * from "./providers.js";
48
48
  export * from "./proxy.js";
49
+ export * from "./proxyClient.js";
49
50
  export * from "./rag.js";
50
51
  export * from "./scorer.js";
51
52
  export * from "./sdk.js";
@@ -47,6 +47,7 @@ export * from "./ppt.js";
47
47
  export * from "./processor.js";
48
48
  export * from "./providers.js";
49
49
  export * from "./proxy.js";
50
+ export * from "./proxyClient.js";
50
51
  export * from "./rag.js";
51
52
  export * from "./scorer.js";
52
53
  export * from "./sdk.js";
@@ -0,0 +1,54 @@
1
+ /**
2
+ * One AI coding CLI the proxy can point at itself.
3
+ *
4
+ * Adding a CLI means adding one implementation of this type and one line in
5
+ * `src/cli/proxy-clients/registry.ts`. Nothing else in the proxy should need
6
+ * to know the client exists.
7
+ */
8
+ export type CliProxyClientConfigurator = {
9
+ /** Stable kebab-case identifier, e.g. "claude-code". */
10
+ id: string;
11
+ /** Human-readable name used in CLI output, e.g. "Claude Code". */
12
+ displayName: string;
13
+ /**
14
+ * Whether this CLI appears to be installed. Configurators must not create
15
+ * config files for a CLI the user never installed.
16
+ */
17
+ detect: () => Promise<boolean>;
18
+ /**
19
+ * Point the CLI at the proxy. `proxyBaseUrl` is the bare proxy origin
20
+ * (e.g. "http://127.0.0.1:55669"); the configurator appends whatever path
21
+ * suffix its CLI needs. Returns false when nothing was written, so callers
22
+ * never print a success message for work that did not happen.
23
+ */
24
+ apply: (proxyBaseUrl: string) => Promise<boolean>;
25
+ /**
26
+ * Restore the user's previous configuration. `proxyBaseUrl` is the same bare
27
+ * origin; a configurator that finds a different URL configured must leave it
28
+ * alone and return false.
29
+ */
30
+ restore: (proxyBaseUrl: string) => Promise<boolean>;
31
+ };
32
+ /** Outcome of applying one configurator, for per-client CLI reporting. */
33
+ export type CliProxyClientApplyResult = {
34
+ id: string;
35
+ displayName: string;
36
+ /** True only when the configurator actually wrote configuration. */
37
+ applied: boolean;
38
+ /** Present when the configurator threw; the caller decides how loud to be. */
39
+ error?: Error;
40
+ };
41
+ /** Outcome of restoring one configurator. */
42
+ export type CliProxyClientRestoreResult = {
43
+ id: string;
44
+ displayName: string;
45
+ /** True only when a previous configuration was actually restored. */
46
+ restored: boolean;
47
+ error?: Error;
48
+ };
49
+ /**
50
+ * Raw contents of a Qwen Code `settings.json`. Deliberately open-ended: the
51
+ * configurator rewrites only `security.auth` and must round-trip every other
52
+ * key the user has set, including ones this repo does not know about.
53
+ */
54
+ export type CliQwenSettings = Record<string, unknown>;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "11.11.4",
3
+ "version": "11.11.6",
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": {