@juspay/neurolink 9.80.3 → 9.81.0

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 (44) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser/neurolink.min.js +352 -352
  3. package/dist/cli/loop/optionsSchema.js +16 -0
  4. package/dist/context/stages/structuredSummarizer.js +15 -4
  5. package/dist/core/analytics.js +22 -0
  6. package/dist/core/constants.d.ts +13 -0
  7. package/dist/core/constants.js +13 -0
  8. package/dist/core/modules/GenerationHandler.js +28 -10
  9. package/dist/core/modules/structuredOutputPolicy.d.ts +19 -0
  10. package/dist/core/modules/structuredOutputPolicy.js +26 -0
  11. package/dist/lib/context/stages/structuredSummarizer.js +15 -4
  12. package/dist/lib/core/analytics.js +22 -0
  13. package/dist/lib/core/constants.d.ts +13 -0
  14. package/dist/lib/core/constants.js +13 -0
  15. package/dist/lib/core/modules/GenerationHandler.js +28 -10
  16. package/dist/lib/core/modules/structuredOutputPolicy.d.ts +19 -0
  17. package/dist/lib/core/modules/structuredOutputPolicy.js +26 -0
  18. package/dist/lib/mcp/externalServerManager.js +21 -6
  19. package/dist/lib/mcp/mcpClientFactory.js +5 -1
  20. package/dist/lib/neurolink.js +88 -28
  21. package/dist/lib/providers/googleNativeGemini3.d.ts +92 -4
  22. package/dist/lib/providers/googleNativeGemini3.js +186 -4
  23. package/dist/lib/providers/googleVertex.d.ts +15 -0
  24. package/dist/lib/providers/googleVertex.js +719 -139
  25. package/dist/lib/types/analytics.d.ts +10 -0
  26. package/dist/lib/types/generate.d.ts +88 -0
  27. package/dist/lib/types/stream.d.ts +21 -1
  28. package/dist/lib/utils/conversationMemory.js +19 -8
  29. package/dist/lib/utils/logSanitize.d.ts +26 -0
  30. package/dist/lib/utils/logSanitize.js +56 -0
  31. package/dist/mcp/externalServerManager.js +21 -6
  32. package/dist/mcp/mcpClientFactory.js +5 -1
  33. package/dist/neurolink.js +88 -28
  34. package/dist/providers/googleNativeGemini3.d.ts +92 -4
  35. package/dist/providers/googleNativeGemini3.js +186 -4
  36. package/dist/providers/googleVertex.d.ts +15 -0
  37. package/dist/providers/googleVertex.js +719 -139
  38. package/dist/types/analytics.d.ts +10 -0
  39. package/dist/types/generate.d.ts +88 -0
  40. package/dist/types/stream.d.ts +21 -1
  41. package/dist/utils/conversationMemory.js +19 -8
  42. package/dist/utils/logSanitize.d.ts +26 -0
  43. package/dist/utils/logSanitize.js +56 -0
  44. package/package.json +1 -1
@@ -35,6 +35,16 @@ export type AnalyticsData = {
35
35
  timestamp: string;
36
36
  cost?: number;
37
37
  context?: JsonValue;
38
+ /** Number of agentic steps (model calls) the turn used. */
39
+ stepsUsed?: number;
40
+ /** Number of external tool calls the turn made (final_result excluded). */
41
+ toolCallCount?: number;
42
+ /** Why the turn ended — see GenerateStopReason. */
43
+ stopReason?: string;
44
+ /** Wall-clock duration of the turn in milliseconds. */
45
+ elapsedMs?: number;
46
+ /** Verbatim provider finish/stop reason for the terminal model call. */
47
+ rawFinishReason?: string;
38
48
  };
39
49
  /**
40
50
  * Stream Analytics Data - Enhanced for performance tracking
@@ -296,7 +296,52 @@ export type GenerateOptions = {
296
296
  * ```
297
297
  */
298
298
  enabledToolNames?: string[];
299
+ /**
300
+ * Request timeout (e.g. 30000, '30s', '2m').
301
+ *
302
+ * PER-STEP semantics in agentic loops: on providers that run a native
303
+ * multi-step tool loop (Vertex Gemini / Vertex Claude), this bounds EACH
304
+ * model call in the loop, not the whole turn — a tool-heavy turn may run
305
+ * far longer than this value in total. Size it for the slowest single
306
+ * step (default 300s), and use `abortSignal` for a total-turn deadline.
307
+ *
308
+ * When set explicitly, a step timeout is surfaced immediately instead of
309
+ * burning internal retries/fallbacks that would re-run the same
310
+ * provider+model with the same doomed budget.
311
+ */
299
312
  timeout?: number | string;
313
+ /**
314
+ * Hard wall-clock cap for the WHOLE agentic turn (all model calls + tool
315
+ * executions), in milliseconds. When the deadline passes the turn ends
316
+ * gracefully with `stopReason: "time-limit"` and an honest time message —
317
+ * never the step-cap text. Unset = no turn-level deadline (the library
318
+ * imposes no product policy).
319
+ *
320
+ * Enforced by the native Vertex loops (Gemini + Claude); AI-SDK-driven
321
+ * providers currently ignore it.
322
+ */
323
+ turnTimeoutMs?: number;
324
+ /**
325
+ * Maximum time with NO progress — no stream chunk received, no tool
326
+ * execution started or finished, no step started — before the turn ends
327
+ * with `stopReason: "stalled"`. Catches wedged tools and hung model calls
328
+ * that a whole-turn deadline would let run to the bitter end.
329
+ * Unset = disabled.
330
+ */
331
+ stallTimeoutMs?: number;
332
+ /**
333
+ * When the remaining turn time drops below this, a wrap-up nudge rides the
334
+ * next tool-result turn telling the model to consolidate what it has and
335
+ * produce its final answer. Defaults to 120_000 when `turnTimeoutMs` is
336
+ * set; ignored when it is not.
337
+ */
338
+ wrapupTimeLeadMs?: number;
339
+ /**
340
+ * Per-tool-execution timeout in milliseconds (default 300_000). A tool
341
+ * that exceeds it fails with an error tool_result and costs one step —
342
+ * the turn continues instead of hanging on a wedged tool.
343
+ */
344
+ toolTimeoutMs?: number;
300
345
  /** AbortSignal for external cancellation of the AI call */
301
346
  abortSignal?: AbortSignal;
302
347
  /**
@@ -549,6 +594,21 @@ export type AdditionalMemoryUser = {
549
594
  /** Max words for this user's condensed memory. Overrides the default maxWords. */
550
595
  maxWords?: number;
551
596
  };
597
+ /**
598
+ * Why an agentic turn ended — the discriminator consumers should branch on
599
+ * instead of sniffing the provider-shaped `finishReason` (whose values are
600
+ * overloaded: e.g. "tool-calls" historically covered both step-cap exits and
601
+ * Gemini MALFORMED_FUNCTION_CALL provider errors).
602
+ *
603
+ * - `completed` — the model finished on its own (text answer or final_result)
604
+ * - `step-cap` — the `maxSteps` budget ran out while the model still wanted tools
605
+ * - `time-limit` — the `turnTimeoutMs` wall-clock deadline passed
606
+ * - `stalled` — no progress (no chunk, no tool start/finish) for `stallTimeoutMs`
607
+ * - `aborted` — the caller's `abortSignal` ended the turn
608
+ * - `provider-error` — the provider/model failed the turn (e.g. persistent
609
+ * MALFORMED_FUNCTION_CALL after retry); usually worth a caller-side retry
610
+ */
611
+ export type GenerateStopReason = "completed" | "step-cap" | "time-limit" | "stalled" | "aborted" | "provider-error";
552
612
  /**
553
613
  * Generate function result type - Primary output format
554
614
  * Future-ready for multi-modal outputs while maintaining text focus
@@ -649,6 +709,20 @@ export type GenerateResult = {
649
709
  provider?: string;
650
710
  model?: string;
651
711
  finishReason?: string;
712
+ /**
713
+ * Why the agentic turn ended, independent of the provider-shaped
714
+ * `finishReason`. Populated by the native Vertex loops (Gemini + Claude);
715
+ * undefined on providers that don't run a native loop — fall back to
716
+ * `finishReason` heuristics there.
717
+ */
718
+ stopReason?: GenerateStopReason;
719
+ /**
720
+ * Verbatim provider finish/stop reason for the turn's terminal model call
721
+ * (e.g. "MALFORMED_FUNCTION_CALL", "MAX_TOKENS", "max_tokens", "tool_use").
722
+ */
723
+ rawFinishReason?: string;
724
+ /** Number of agentic steps (model calls) the turn used. */
725
+ stepsUsed?: number;
652
726
  /**
653
727
  * True when the schema JSON in `content`/`structuredData` was repaired from
654
728
  * malformed model text (jsonrepair ran). The result is still valid JSON.
@@ -869,6 +943,14 @@ export type TextGenerationOptions = {
869
943
  */
870
944
  enabledToolNames?: string[];
871
945
  timeout?: number | string;
946
+ /** Wall-clock cap for the whole agentic turn (ms). See GenerateOptions.turnTimeoutMs. */
947
+ turnTimeoutMs?: number;
948
+ /** Max time with no progress before the turn ends as "stalled" (ms). See GenerateOptions.stallTimeoutMs. */
949
+ stallTimeoutMs?: number;
950
+ /** Remaining-time threshold that triggers the wrap-up nudge (ms). See GenerateOptions.wrapupTimeLeadMs. */
951
+ wrapupTimeLeadMs?: number;
952
+ /** Per-tool-execution timeout (ms, default 300_000). See GenerateOptions.toolTimeoutMs. */
953
+ toolTimeoutMs?: number;
872
954
  /** AbortSignal for external cancellation of the AI call */
873
955
  abortSignal?: AbortSignal;
874
956
  disableTools?: boolean;
@@ -1115,6 +1197,12 @@ export type TextGenerationResult = {
1115
1197
  /** Parsed structured object when a `schema` was requested (see GenerateResult.structuredData). */
1116
1198
  structuredData?: unknown;
1117
1199
  finishReason?: string;
1200
+ /** Turn-exit discriminator from native agentic loops (see GenerateStopReason). */
1201
+ stopReason?: GenerateStopReason;
1202
+ /** Verbatim provider finish/stop reason for the turn's terminal model call. */
1203
+ rawFinishReason?: string;
1204
+ /** Number of agentic steps (model calls) the turn used. */
1205
+ stepsUsed?: number;
1118
1206
  /** True when the schema JSON was repaired from malformed model text. */
1119
1207
  jsonRepaired?: boolean;
1120
1208
  /** True when the schema JSON appears truncated (output hit the token cap). */
@@ -8,7 +8,7 @@ import type { JsonValue, UnknownRecord } from "./common.js";
8
8
  import type { Content, ImageWithAltText } from "./content.js";
9
9
  import type { ChatMessage } from "./conversation.js";
10
10
  import type { StreamNoOutputSentinel } from "./noOutputSentinel.js";
11
- import type { AdditionalMemoryUser } from "./generate.js";
11
+ import type { AdditionalMemoryUser, GenerateStopReason } from "./generate.js";
12
12
  import type { AIModelProviderConfig, NeurolinkCredentials } from "./providers.js";
13
13
  import type { TTSChunk, TTSOptions, TTSResult } from "./tts.js";
14
14
  import type { STTOptions, STTResult } from "./stt.js";
@@ -326,6 +326,14 @@ export type StreamOptions = {
326
326
  schema?: ValidationSchema;
327
327
  tools?: Record<string, Tool>;
328
328
  timeout?: number | string;
329
+ /** Wall-clock cap for the whole agentic turn (ms). See GenerateOptions.turnTimeoutMs. */
330
+ turnTimeoutMs?: number;
331
+ /** Max time with no progress before the turn ends as "stalled" (ms). See GenerateOptions.stallTimeoutMs. */
332
+ stallTimeoutMs?: number;
333
+ /** Remaining-time threshold that triggers the wrap-up nudge (ms). See GenerateOptions.wrapupTimeLeadMs. */
334
+ wrapupTimeLeadMs?: number;
335
+ /** Per-tool-execution timeout (ms, default 300_000). See GenerateOptions.toolTimeoutMs. */
336
+ toolTimeoutMs?: number;
329
337
  /** AbortSignal for external cancellation of the AI call */
330
338
  abortSignal?: AbortSignal;
331
339
  disableTools?: boolean;
@@ -518,6 +526,15 @@ export type StreamResult = {
518
526
  model?: string;
519
527
  usage?: TokenUsage;
520
528
  finishReason?: string;
529
+ /**
530
+ * Why the agentic turn ended (see GenerateStopReason). For background-loop
531
+ * streams (native Vertex paths) prefer `metadata.stopReason` after draining
532
+ * the stream — this top-level field may be a getter that resolves late, and
533
+ * wrapper spreads can snapshot it before the loop finishes.
534
+ */
535
+ stopReason?: GenerateStopReason;
536
+ /** Verbatim provider finish/stop reason for the turn's terminal model call. */
537
+ rawFinishReason?: string;
521
538
  toolCalls?: StreamToolCall[];
522
539
  toolResults?: StreamToolResult[];
523
540
  toolEvents?: AsyncIterable<ToolExecutionEvent>;
@@ -537,6 +554,9 @@ export type StreamResult = {
537
554
  guardrailsBlocked?: boolean;
538
555
  error?: string;
539
556
  finishReason?: string;
557
+ stopReason?: GenerateStopReason;
558
+ rawFinishReason?: string;
559
+ stepsUsed?: number;
540
560
  thoughtSignature?: string;
541
561
  thoughts?: Array<{
542
562
  id?: string;
@@ -5,6 +5,7 @@
5
5
  import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
6
6
  import { tracers } from "../telemetry/tracers.js";
7
7
  import { withTimeout } from "./errorHandling.js";
8
+ import { safeDebugSerialize, sanitizeRecord } from "./logSanitize.js";
8
9
  import { DEFAULT_FALLBACK_THRESHOLD, getConversationMemoryDefaults, MEMORY_THRESHOLD_PERCENTAGE, } from "../config/conversationMemory.js";
9
10
  import { getAvailableInputTokens } from "../constants/contextWindows.js";
10
11
  import { buildSummarizationPrompt } from "../context/prompts/summarizationPrompt.js";
@@ -106,20 +107,30 @@ export function applyConversationMemoryDefaults(userConfig) {
106
107
  * Get conversation history as message array, summarizing if needed.
107
108
  */
108
109
  export async function getConversationMessages(conversationMemory, options) {
109
- logger.debug("[conversationMemoryUtils] getConversationMessages called", {
110
- hasMemory: !!conversationMemory,
111
- memoryType: conversationMemory?.constructor?.name || "NONE",
112
- hasContext: !!options.context,
113
- enableSummarization: options.enableSummarization ?? false,
114
- options: JSON.stringify(options, null, 2),
115
- });
110
+ // Logger Guard: options carries the full conversation history + tool
111
+ // outputs; eager JSON.stringify of it can throw RangeError: Invalid string
112
+ // length and abort the turn. Serialize lazily, bounded, never-throwing,
113
+ // and redacted (sanitizeRecord strips credential/PII keys).
114
+ if (logger.shouldLog("debug")) {
115
+ logger.debug("[conversationMemoryUtils] getConversationMessages called", {
116
+ hasMemory: !!conversationMemory,
117
+ memoryType: conversationMemory?.constructor?.name || "NONE",
118
+ hasContext: !!options.context,
119
+ enableSummarization: options.enableSummarization ?? false,
120
+ options: safeDebugSerialize(sanitizeRecord(options)),
121
+ });
122
+ }
116
123
  if (!conversationMemory || !options.context) {
117
124
  logger.warn("[conversationMemoryUtils] No memory or context, returning empty messages", {
118
125
  hasMemory: !!conversationMemory,
119
126
  memoryType: conversationMemory?.constructor?.name || "NONE",
120
127
  hasContext: !!options.context,
121
128
  enableSummarization: options.enableSummarization ?? false,
122
- options: JSON.stringify(options, null, 2),
129
+ // The options dump is debug-grade detail — don't pay for (or leak)
130
+ // the serialization on every memory-disabled call at warn level.
131
+ ...(logger.shouldLog("debug")
132
+ ? { options: safeDebugSerialize(sanitizeRecord(options)) }
133
+ : {}),
123
134
  });
124
135
  return [];
125
136
  }
@@ -26,6 +26,32 @@
26
26
  * @param maxLen - Maximum number of characters to keep (default 500).
27
27
  */
28
28
  export declare function sanitizeForLog(text: string, maxLen?: number): string;
29
+ /**
30
+ * Stringify non-string message/tool content without ever throwing.
31
+ * JSON.stringify on a giant multimodal/tool-result payload can exceed V8's
32
+ * maximum string length and throw `RangeError: Invalid string length` — a
33
+ * generation turn must degrade to a placeholder, not abort, when that
34
+ * happens. Shared by the SDK core and providers (single source of truth).
35
+ */
36
+ export declare function stringifyContentSafe(content: unknown): string;
37
+ /**
38
+ * Serialize an arbitrary value for a debug log without ever throwing or
39
+ * producing an unbounded string.
40
+ *
41
+ * `JSON.stringify` on a full options object (conversation history + tool
42
+ * outputs) can exceed V8's maximum string length and throw
43
+ * `RangeError: Invalid string length`, which — when evaluated eagerly inside
44
+ * a logger call — aborts the surrounding generation turn (observed in
45
+ * production). This helper caps the output and converts any stringify
46
+ * failure (RangeError, circular structure, BigInt, …) into a placeholder.
47
+ *
48
+ * Callers MUST still gate on `logger.shouldLog("debug")` per the Logger
49
+ * Guard rule — this helper makes serialization safe, not free.
50
+ *
51
+ * @param value - Arbitrary value to serialize.
52
+ * @param maxLen - Maximum number of characters to keep (default 10 000).
53
+ */
54
+ export declare function safeDebugSerialize(value: unknown, maxLen?: number): string;
29
55
  /**
30
56
  * Strip embedded `user:pass@` credentials from a URL's authority component
31
57
  * before logging it or surfacing it in a user-facing error.
@@ -74,6 +74,13 @@ const SENSITIVE_OBJECT_KEYS = [
74
74
  "oauth",
75
75
  "oauthToken",
76
76
  "credentials",
77
+ "authContext",
78
+ "authToken",
79
+ "sessionToken",
80
+ "serviceAccountKey",
81
+ "secretAccessKey",
82
+ // PII, not a secret — but it has no business in debug log dumps.
83
+ "userEmail",
77
84
  ];
78
85
  /**
79
86
  * Truncate `text` to `maxLen` chars then replace embedded secrets with `***`.
@@ -91,6 +98,55 @@ export function sanitizeForLog(text, maxLen = 500) {
91
98
  }
92
99
  return text.slice(0, maxLen).replace(SECRET_PATTERN, "***");
93
100
  }
101
+ /**
102
+ * Stringify non-string message/tool content without ever throwing.
103
+ * JSON.stringify on a giant multimodal/tool-result payload can exceed V8's
104
+ * maximum string length and throw `RangeError: Invalid string length` — a
105
+ * generation turn must degrade to a placeholder, not abort, when that
106
+ * happens. Shared by the SDK core and providers (single source of truth).
107
+ */
108
+ export function stringifyContentSafe(content) {
109
+ if (typeof content === "string") {
110
+ return content;
111
+ }
112
+ try {
113
+ return JSON.stringify(content) ?? String(content);
114
+ }
115
+ catch {
116
+ return "[content too large to serialize]";
117
+ }
118
+ }
119
+ /**
120
+ * Serialize an arbitrary value for a debug log without ever throwing or
121
+ * producing an unbounded string.
122
+ *
123
+ * `JSON.stringify` on a full options object (conversation history + tool
124
+ * outputs) can exceed V8's maximum string length and throw
125
+ * `RangeError: Invalid string length`, which — when evaluated eagerly inside
126
+ * a logger call — aborts the surrounding generation turn (observed in
127
+ * production). This helper caps the output and converts any stringify
128
+ * failure (RangeError, circular structure, BigInt, …) into a placeholder.
129
+ *
130
+ * Callers MUST still gate on `logger.shouldLog("debug")` per the Logger
131
+ * Guard rule — this helper makes serialization safe, not free.
132
+ *
133
+ * @param value - Arbitrary value to serialize.
134
+ * @param maxLen - Maximum number of characters to keep (default 10 000).
135
+ */
136
+ export function safeDebugSerialize(value, maxLen = 10_000) {
137
+ try {
138
+ const json = JSON.stringify(value);
139
+ if (json === undefined) {
140
+ return String(value);
141
+ }
142
+ return json.length > maxLen
143
+ ? `${json.slice(0, maxLen)}…[truncated ${json.length - maxLen} chars]`
144
+ : json;
145
+ }
146
+ catch (error) {
147
+ return `[unserializable: ${error instanceof Error ? error.message : String(error)}]`;
148
+ }
149
+ }
94
150
  /**
95
151
  * Strip embedded `user:pass@` credentials from a URL's authority component
96
152
  * before logging it or surfacing it in a user-facing error.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.80.3",
3
+ "version": "9.81.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
6
6
  "author": {