@juspay/neurolink 12.49.1 → 12.49.3

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.
@@ -220,9 +220,15 @@ export function createGeminiLoopAdapter(config) {
220
220
  },
221
221
  mapFinishReason(rawStopReason, hadToolCallsAtCap) {
222
222
  const mapped = mapGeminiFinishReason(rawStopReason);
223
- // A turn cut off at the step cap ended because of the cap, not because
224
- // the last response said "STOP".
225
- return hadToolCallsAtCap && mapped === "stop" ? "tool-calls" : mapped;
223
+ // "tool-calls" from this hook means the step cap cut the turn off with
224
+ // calls still pending, so a last response that said "STOP" does not make
225
+ // it a finished turn. Gemini's own tool-call limit (TOO_MANY_TOOL_CALLS)
226
+ // also maps to "tool-calls" on the turn's result, but it is not that
227
+ // cap, so it counts here only when the cap was actually hit.
228
+ if (mapped === "stop" || mapped === "tool-calls") {
229
+ return hadToolCallsAtCap ? "tool-calls" : "stop";
230
+ }
231
+ return mapped;
226
232
  },
227
233
  };
228
234
  }
@@ -110,21 +110,35 @@ export declare function computeMaxSteps(rawMaxSteps?: number): number;
110
110
  * Map a `@google/genai` `Candidate.finishReason` enum value onto NeuroLink's
111
111
  * unified finish reason, mirroring anthropic.ts `mapAnthropicStopReason`.
112
112
  *
113
- * Enum values per `@google/genai` `FinishReason`: STOP, MAX_TOKENS, SAFETY,
114
- * RECITATION, LANGUAGE, OTHER, BLOCKLIST, PROHIBITED_CONTENT, SPII,
115
- * MALFORMED_FUNCTION_CALL, IMAGE_SAFETY, UNEXPECTED_TOOL_CALL,
116
- * FINISH_REASON_UNSPECIFIED. Unknown / unset / non-terminal values default to
117
- * "stop" (a clean completion is the safe assumption).
113
+ * Every member of `@google/genai`'s `FinishReason` and where it lands:
114
+ *
115
+ * - "length": MAX_TOKENS, and CONTINUATION (the response hit the per-request
116
+ * token limit before the model was done; it can be continued with the
117
+ * returned continuation token)
118
+ * - "tool-calls": TOO_MANY_TOOL_CALLS (the model called tools consecutively
119
+ * until the system cut the turn off, so it still wanted tools)
120
+ * - "error": MALFORMED_FUNCTION_CALL, UNEXPECTED_TOOL_CALL
121
+ * - "content-filter": SAFETY, RECITATION, BLOCKLIST, PROHIBITED_CONTENT, SPII,
122
+ * LANGUAGE (generation stopped over an unsupported language), and the image
123
+ * refusals IMAGE_SAFETY, IMAGE_PROHIBITED_CONTENT, IMAGE_RECITATION
124
+ * - "stop": STOP, and the members that name no cause the unified type can
125
+ * carry: OTHER, IMAGE_OTHER, NO_IMAGE, FINISH_REASON_UNSPECIFIED. An unknown
126
+ * or unset value also reads "stop" (a clean completion is the safe
127
+ * assumption), so a member a newer SDK adds needs an explicit case here.
118
128
  *
119
129
  * Returns a plain string (not a `{ unified, raw }` object): the Vertex result
120
130
  * builders and the consuming layer (neurolink.ts `finishReason || "unknown"`
121
131
  * and `finishReason === "length"`) compare against plain strings.
122
132
  *
123
133
  * MALFORMED_FUNCTION_CALL / UNEXPECTED_TOOL_CALL map to "error", NOT
124
- * "tool-calls": they are provider/model failures, while "tool-calls" is the
125
- * exclusive contract for "step budget exhausted while the model still wanted
126
- * tools" — consumers (e.g. curator's step-cap intercept) branch on it and
127
- * were rendering fake step-limit messages for 2-4-step malformed-call turns.
134
+ * "tool-calls": they are provider/model failures, while "tool-calls" means the
135
+ * turn ended with the model still wanting tools because a tool-call budget ran
136
+ * out: the loop's own step cap, or Gemini's TOO_MANY_TOOL_CALLS limit. Consumers
137
+ * (e.g. curator's step-cap intercept) branch on it and were rendering fake
138
+ * step-limit messages for 2-4-step malformed-call turns. `stopReason` is the
139
+ * discriminator that tells the two budgets apart: TOO_MANY_TOOL_CALLS reads
140
+ * "tool-calls" here but stays `stopReason: "completed"`, because `maxSteps` did
141
+ * not run out.
128
142
  */
129
143
  export declare function mapGeminiFinishReason(raw: string | null | undefined): "stop" | "length" | "tool-calls" | "content-filter" | "error";
130
144
  /**
@@ -491,26 +491,43 @@ export function computeMaxSteps(rawMaxSteps) {
491
491
  * Map a `@google/genai` `Candidate.finishReason` enum value onto NeuroLink's
492
492
  * unified finish reason, mirroring anthropic.ts `mapAnthropicStopReason`.
493
493
  *
494
- * Enum values per `@google/genai` `FinishReason`: STOP, MAX_TOKENS, SAFETY,
495
- * RECITATION, LANGUAGE, OTHER, BLOCKLIST, PROHIBITED_CONTENT, SPII,
496
- * MALFORMED_FUNCTION_CALL, IMAGE_SAFETY, UNEXPECTED_TOOL_CALL,
497
- * FINISH_REASON_UNSPECIFIED. Unknown / unset / non-terminal values default to
498
- * "stop" (a clean completion is the safe assumption).
494
+ * Every member of `@google/genai`'s `FinishReason` and where it lands:
495
+ *
496
+ * - "length": MAX_TOKENS, and CONTINUATION (the response hit the per-request
497
+ * token limit before the model was done; it can be continued with the
498
+ * returned continuation token)
499
+ * - "tool-calls": TOO_MANY_TOOL_CALLS (the model called tools consecutively
500
+ * until the system cut the turn off, so it still wanted tools)
501
+ * - "error": MALFORMED_FUNCTION_CALL, UNEXPECTED_TOOL_CALL
502
+ * - "content-filter": SAFETY, RECITATION, BLOCKLIST, PROHIBITED_CONTENT, SPII,
503
+ * LANGUAGE (generation stopped over an unsupported language), and the image
504
+ * refusals IMAGE_SAFETY, IMAGE_PROHIBITED_CONTENT, IMAGE_RECITATION
505
+ * - "stop": STOP, and the members that name no cause the unified type can
506
+ * carry: OTHER, IMAGE_OTHER, NO_IMAGE, FINISH_REASON_UNSPECIFIED. An unknown
507
+ * or unset value also reads "stop" (a clean completion is the safe
508
+ * assumption), so a member a newer SDK adds needs an explicit case here.
499
509
  *
500
510
  * Returns a plain string (not a `{ unified, raw }` object): the Vertex result
501
511
  * builders and the consuming layer (neurolink.ts `finishReason || "unknown"`
502
512
  * and `finishReason === "length"`) compare against plain strings.
503
513
  *
504
514
  * MALFORMED_FUNCTION_CALL / UNEXPECTED_TOOL_CALL map to "error", NOT
505
- * "tool-calls": they are provider/model failures, while "tool-calls" is the
506
- * exclusive contract for "step budget exhausted while the model still wanted
507
- * tools" — consumers (e.g. curator's step-cap intercept) branch on it and
508
- * were rendering fake step-limit messages for 2-4-step malformed-call turns.
515
+ * "tool-calls": they are provider/model failures, while "tool-calls" means the
516
+ * turn ended with the model still wanting tools because a tool-call budget ran
517
+ * out: the loop's own step cap, or Gemini's TOO_MANY_TOOL_CALLS limit. Consumers
518
+ * (e.g. curator's step-cap intercept) branch on it and were rendering fake
519
+ * step-limit messages for 2-4-step malformed-call turns. `stopReason` is the
520
+ * discriminator that tells the two budgets apart: TOO_MANY_TOOL_CALLS reads
521
+ * "tool-calls" here but stays `stopReason: "completed"`, because `maxSteps` did
522
+ * not run out.
509
523
  */
510
524
  export function mapGeminiFinishReason(raw) {
511
525
  switch (raw) {
512
526
  case "MAX_TOKENS":
527
+ case "CONTINUATION":
513
528
  return "length";
529
+ case "TOO_MANY_TOOL_CALLS":
530
+ return "tool-calls";
514
531
  case "MALFORMED_FUNCTION_CALL":
515
532
  case "UNEXPECTED_TOOL_CALL":
516
533
  return "error";
@@ -519,7 +536,10 @@ export function mapGeminiFinishReason(raw) {
519
536
  case "BLOCKLIST":
520
537
  case "PROHIBITED_CONTENT":
521
538
  case "SPII":
539
+ case "LANGUAGE":
522
540
  case "IMAGE_SAFETY":
541
+ case "IMAGE_PROHIBITED_CONTENT":
542
+ case "IMAGE_RECITATION":
523
543
  return "content-filter";
524
544
  default:
525
545
  return "stop";
@@ -4,14 +4,19 @@
4
4
  * Supports comprehensive NO_PROXY pattern matching including wildcards, domains, ports, and CIDR
5
5
  */
6
6
  /**
7
- * Comprehensive NO_PROXY bypass check supporting multiple pattern types
7
+ * NO_PROXY bypass check. The value is a comma- or space-separated list; each
8
+ * entry is matched case-insensitively.
8
9
  *
9
- * Supported patterns:
10
+ * Supported entries:
10
11
  * - "*" - bypass all requests
11
- * - "example.com" - exact hostname match
12
- * - ".example.com" - domain suffix match (matches example.com and subdomains)
13
- * - "localhost:8080" - hostname with specific port
14
- * - "192.168.1.0/24" - CIDR notation for IP ranges
12
+ * - "example.com", ".example.com", "*.example.com" - the host and any subdomain
13
+ * - "example.com:8443" - the same, on that port only; a URL without a port is
14
+ * on the scheme's default (80 for http, 443 for https)
15
+ * - "example.com." - the same as "example.com": one trailing dot is ignored on
16
+ * an entry and on the URL's host name, so "https://api.example.com./" matches
17
+ * - "192.168.1.10", "::1", "[::1]:8080" - an IP literal, exactly
18
+ * - "192.168.1.0/24" - an IPv4 CIDR range, for IPv4 literal targets (IPv6 CIDR
19
+ * ranges are not supported)
15
20
  *
16
21
  * @param targetUrl - The URL to check for proxy bypass
17
22
  * @param noProxyEnv - Optional NO_PROXY environment variable value (if not provided, reads from process.env)
@@ -27,15 +27,124 @@ function isIpInCIDR(ip, cidr) {
27
27
  return false;
28
28
  }
29
29
  }
30
+ const IPV4_LITERAL = /^\d{1,3}(\.\d{1,3}){3}$/;
30
31
  /**
31
- * Comprehensive NO_PROXY bypass check supporting multiple pattern types
32
+ * The form a URL gives an IPv6 literal (`[::1]`, lower-case, compressed), so
33
+ * `[0:0:0:0:0:0:0:1]` and `[::1]` compare equal. `null` when it is not one.
34
+ */
35
+ function canonicalIpv6Host(address) {
36
+ try {
37
+ return new URL(`http://[${address}]`).hostname;
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ }
43
+ /** A fully qualified name's trailing dot, which a URL keeps and curl ignores. */
44
+ function withoutTrailingDot(name) {
45
+ return name.endsWith(".") ? name.slice(0, -1) : name;
46
+ }
47
+ /**
48
+ * curl's rules for one list entry: an optional leading `.` or `*.` is ignored;
49
+ * the entry names that host and every subdomain of it, an IP literal only
50
+ * itself; and a `:port` restricts it to that port. IPv6 is written bare (`::1`)
51
+ * or bracketed (`[::1]:8080`), and only the bracketed form can carry a port.
52
+ * One trailing dot is ignored on the entry's domain and on the URL's host name,
53
+ * so `example.com.` and `example.com` name the same host; an entry made only of
54
+ * dots names none.
55
+ */
56
+ function matchesNoProxyEntry(entry, hostname, port) {
57
+ let host = entry;
58
+ let entryPort;
59
+ if (entry.startsWith("[")) {
60
+ const close = entry.indexOf("]");
61
+ if (close === -1) {
62
+ return false;
63
+ }
64
+ host = entry.slice(0, close + 1);
65
+ const rest = entry.slice(close + 1);
66
+ if (rest !== "") {
67
+ if (!/^:\d+$/.test(rest)) {
68
+ return false;
69
+ }
70
+ entryPort = rest.slice(1);
71
+ }
72
+ }
73
+ else {
74
+ const colons = entry.split(":").length - 1;
75
+ if (colons === 1) {
76
+ const separator = entry.indexOf(":");
77
+ host = entry.slice(0, separator);
78
+ entryPort = entry.slice(separator + 1);
79
+ if (!/^\d+$/.test(entryPort)) {
80
+ return false;
81
+ }
82
+ }
83
+ else if (colons > 1) {
84
+ host = `[${entry}]`;
85
+ }
86
+ }
87
+ if (entryPort !== undefined && entryPort !== port) {
88
+ return false;
89
+ }
90
+ if (host.startsWith("[")) {
91
+ return canonicalIpv6Host(host.slice(1, -1)) === hostname;
92
+ }
93
+ host = host.replace(/^(\*\.|\.)/, "");
94
+ if (/^\.*$/.test(host) || /[\s*/]/.test(host)) {
95
+ return false;
96
+ }
97
+ if (IPV4_LITERAL.test(host)) {
98
+ return hostname === host;
99
+ }
100
+ const domain = withoutTrailingDot(host);
101
+ // curl does not take "127.0.0.1." for an address, so it names nothing.
102
+ if (IPV4_LITERAL.test(domain)) {
103
+ return false;
104
+ }
105
+ const name = withoutTrailingDot(hostname);
106
+ return name === domain || name.endsWith(`.${domain}`);
107
+ }
108
+ /**
109
+ * The rules this function applied before it followed curl, unchanged and still
110
+ * consulted, so that a configuration that bypassed the proxy through them keeps
111
+ * doing so. That includes where they are looser than curl (`.example.com` also
112
+ * matches `notexample.com`) and an IPv4 CIDR range such as `192.168.1.0/24`,
113
+ * which curl-style entries do not express.
114
+ */
115
+ function matchesLegacyNoProxyEntry(lowerPattern, hostname, port) {
116
+ // Domain suffix match (.example.com)
117
+ if (lowerPattern.startsWith(".")) {
118
+ const suffix = lowerPattern.slice(1);
119
+ return hostname.endsWith(suffix) || hostname === suffix;
120
+ }
121
+ // Port-specific match (hostname:port)
122
+ if (lowerPattern.includes(":")) {
123
+ const [patternHost, patternPort] = lowerPattern.split(":");
124
+ return hostname === patternHost && port === patternPort;
125
+ }
126
+ // CIDR notation (192.168.1.0/24) - only for IP addresses
127
+ if (lowerPattern.includes("/")) {
128
+ // Only apply CIDR when target is an IP literal
129
+ return IPV4_LITERAL.test(hostname) && isIpInCIDR(hostname, lowerPattern);
130
+ }
131
+ // Exact hostname match
132
+ return hostname === lowerPattern;
133
+ }
134
+ /**
135
+ * NO_PROXY bypass check. The value is a comma- or space-separated list; each
136
+ * entry is matched case-insensitively.
32
137
  *
33
- * Supported patterns:
138
+ * Supported entries:
34
139
  * - "*" - bypass all requests
35
- * - "example.com" - exact hostname match
36
- * - ".example.com" - domain suffix match (matches example.com and subdomains)
37
- * - "localhost:8080" - hostname with specific port
38
- * - "192.168.1.0/24" - CIDR notation for IP ranges
140
+ * - "example.com", ".example.com", "*.example.com" - the host and any subdomain
141
+ * - "example.com:8443" - the same, on that port only; a URL without a port is
142
+ * on the scheme's default (80 for http, 443 for https)
143
+ * - "example.com." - the same as "example.com": one trailing dot is ignored on
144
+ * an entry and on the URL's host name, so "https://api.example.com./" matches
145
+ * - "192.168.1.10", "::1", "[::1]:8080" - an IP literal, exactly
146
+ * - "192.168.1.0/24" - an IPv4 CIDR range, for IPv4 literal targets (IPv6 CIDR
147
+ * ranges are not supported)
39
148
  *
40
149
  * @param targetUrl - The URL to check for proxy bypass
41
150
  * @param noProxyEnv - Optional NO_PROXY environment variable value (if not provided, reads from process.env)
@@ -51,7 +160,7 @@ export function shouldBypassProxy(targetUrl, noProxyEnv) {
51
160
  const hostname = url.hostname.toLowerCase();
52
161
  const port = url.port || (url.protocol === "https:" ? "443" : "80");
53
162
  const patterns = noProxy
54
- .split(",")
163
+ .split(/[,\s]+/)
55
164
  .map((p) => p.trim())
56
165
  .filter(Boolean);
57
166
  for (const pattern of patterns) {
@@ -60,30 +169,8 @@ export function shouldBypassProxy(targetUrl, noProxyEnv) {
60
169
  if (lowerPattern === "*") {
61
170
  return true;
62
171
  }
63
- // Domain suffix match (.example.com)
64
- if (lowerPattern.startsWith(".")) {
65
- const suffix = lowerPattern.slice(1);
66
- if (hostname.endsWith(suffix) || hostname === suffix) {
67
- return true;
68
- }
69
- }
70
- // Port-specific match (hostname:port)
71
- else if (lowerPattern.includes(":")) {
72
- const [patternHost, patternPort] = lowerPattern.split(":");
73
- if (hostname === patternHost && port === patternPort) {
74
- return true;
75
- }
76
- }
77
- // CIDR notation (192.168.1.0/24) - only for IP addresses
78
- else if (lowerPattern.includes("/")) {
79
- // Only apply CIDR when target is an IP literal
80
- if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname) &&
81
- isIpInCIDR(hostname, lowerPattern)) {
82
- return true;
83
- }
84
- }
85
- // Exact hostname match
86
- else if (hostname === lowerPattern) {
172
+ if (matchesNoProxyEntry(lowerPattern, hostname, port) ||
173
+ matchesLegacyNoProxyEntry(lowerPattern, hostname, port)) {
87
174
  return true;
88
175
  }
89
176
  }
@@ -4604,6 +4604,7 @@
4604
4604
  {"objectID":"8569960f5df3455464e766e118794ee1116e63943713b4e396669abb164ea759","title":"stopReason — the turn-exit discriminator","url":"/docs/features/turn-time-budget#stopreason-the-turn-exit-discriminator","content":"finishReason is provider-shaped and historically overloaded (\"tool-calls\"\ncovered both step-cap exits and Gemini MALFORMED_FUNCTION_CALL failures).\nBranch on result.stopReason instead:\n\n| stopReason | Meaning |\n| ---------------- | ------------------------------------------------------------------ |\n| completed | The model finished on its own (text answer or final_result) |\n| step-cap | The maxSteps budget ran out while the model still wanted tools |\n| time-limit | The turnTimeoutMs (or defensive) wall-clock deadline passed |\n| stalled | No progress for stallTimeoutMs |\n| aborted | The caller's abortSignal ended the turn |\n| context-cap | The in-loop context guard stopped the turn (Vertex loops only) |\n| provider-error | Provider/model failure (e.g. persistent MALFORMED_FUNCTION_CALL) |\n\nA loop only reports what it can observe. The AI Studio loops never report\ncontext-cap, because their context guard reclaims room or does nothing — it\nhas no stop-the-turn branch to report.\n\nAlso on the result: rawFinishReason (the verbatim provider value, e.g.\nMALFORMED_FUNCTION_CALL, max_tokens) and stepsUsed. On stream()\nresults, read metadata.stopReason / metadata.rawFinishReason /\nmetadata.stepsUsed after draining the stream — background loops resolve\nthem at close, and metadata is the mutable reference that survives wrapper\nspreads.\n\nProviders without a native loop leave stopReason undefined — keep any\nlegacy finishReason heuristics as a fallback.","hierarchy":{"lvl0":"Features","lvl1":"Turn Time Budget","lvl2":"stopReason — the turn-exit discriminator","lvl3":""}},
4605
4605
  {"objectID":"beefbc9fd412625dfb71572fe43e8c19a1634aa91a3778825806507e43eb5f84","title":"Honest terminal messages","url":"/docs/features/turn-time-budget#honest-terminal-messages","content":"Each exit cause has its own user-facing message; the step-cap text\n(\"...reached the N-step limit...\") is emitted only when step >= maxSteps\ngenuinely terminated the loop:\ntime-limit: _\"I had to stop after Xm Ys — this turn hit its processing time\n limit. I completed N tool calls before stopping; ask me to continue and\n I'll pick up from there.\"_\nstalled: _\"I had to stop because this turn made no progress for Xs — a tool\n or model call appears to be stuck. ...\"_\naborted: \"This turn was stopped before I could finish. ...\"","hierarchy":{"lvl0":"Features","lvl1":"Turn Time Budget","lvl2":"Honest terminal messages","lvl3":""}},
4606
4606
  {"objectID":"8a1f451537135b3f1ef892866610bb0dbabfe58ed1764bac2753046b8da98222","title":"MALFORMED_FUNCTION_CALL handling","url":"/docs/features/turn-time-budget#malformed_function_call-handling","content":"Gemini's MALFORMED_FUNCTION_CALL / UNEXPECTED_TOOL_CALL finish reasons\nmap to unified finishReason: \"error\" (never \"tool-calls\"). A\nMALFORMED_FUNCTION_CALL step is retried once with a corrective note;\nif it persists, the turn ends with stopReason: \"provider-error\" — usually\nworth a caller-side retry.","hierarchy":{"lvl0":"Features","lvl1":"Turn Time Budget","lvl2":"MALFORMED_FUNCTION_CALL handling","lvl3":""}},
4607
+ {"objectID":"b858770b3853e797dfaac8ecfbd0c1f86c320e6a94e46a2e60f8407f328f6090","title":"Other Gemini finish reasons","url":"/docs/features/turn-time-budget#other-gemini-finish-reasons","content":"The native Gemini loops (AI Studio and Vertex) map the SDK's finish reason onto\nthe unified values below and keep the verbatim value in rawFinishReason.\nVertex puts the unified value on finishReason; on AI Studio it is the\nunified field of the finish part that model middleware receives.\n\n| Gemini finish reason | finishReason |\n| ----------------------------------------------------------------------------------------- | ---------------- |\n| MAX_TOKENS, CONTINUATION (token limit reached before the model was done) | length |\n| TOO_MANY_TOOL_CALLS (the model called tools until the system cut the turn off) | tool-calls |\n| MALFORMED_FUNCTION_CALL, UNEXPECTED_TOOL_CALL | error |\n| SAFETY, RECITATION, BLOCKLIST, PROHIBITED_CONTENT, SPII, LANGUAGE, IMAGE_*¹ | content-filter |\n| STOP, OTHER, FINISH_REASON_UNSPECIFIED, IMAGE_OTHER, NO_IMAGE | stop |\n\n¹ IMAGE_SAFETY, IMAGE_PROHIBITED_CONTENT and IMAGE_RECITATION.\n\ntool-calls also marks a turn that ran out of maxSteps, so branch on\nstopReason to tell the two apart: TOO_MANY_TOOL_CALLS leaves\nstopReason: \"completed\", a spent step budget reports step-cap. A value the\nSDK adds later reads stop until it is mapped.","hierarchy":{"lvl0":"Features","lvl1":"Turn Time Budget","lvl2":"Other Gemini finish reasons","lvl3":""}},
4607
4608
  {"objectID":"a975fa8c9a970f09409f0cd78f26180dbef30bebfc60396f0eebda7776d8a3c3","title":"Telemetry","url":"/docs/features/turn-time-budget#telemetry","content":"result.analytics (when enableAnalytics is on) carries stepsUsed,\n toolCallCount, stopReason, elapsedMs, rawFinishReason.\nThe SDK emitter fires turn:lifecycle events (alongside\n tool:start/tool:end) for non-completed exits, tool timeouts, and\n malformed-call retries:","hierarchy":{"lvl0":"Features","lvl1":"Turn Time Budget","lvl2":"Telemetry","lvl3":""}},
4608
4609
  {"objectID":"4237dd71854673dfa0c8c155353abb6bf61149758ab7cd851436a49841b8dba3","title":"Video Analysis","url":"/docs/features/video-analysis","content":"Video Analysis\n\nComprehensive video analysis for NeuroLink, powered by Gemini 2.0 Flash. This feature goes beyond basic visual description—it provides a deep logical audit of video sequences to understand \"why\" and \"how\" events occur.\n\nKey Capabilities\nLogical Analysis: Dissect any video to extract the underlying intent, cause-and-effect, and logical progression.\nAction-Reaction Chain: A step-by-step audit of user or system actions and their immediate visual results.\nEvidence-Based Reporting: Detailed reasoning backed by structured visual indicators (colors, labels, text) in JSON format.\nStrategic Verdicts: High-level assessments of whether a workflow succeeded or failed logically.\n\nQuick Start\n\nHow It Works\nFrame Extraction: The system uses ffmpeg to extract high-quality keyframes from the video at calculated intervals.\nAnalysis Pipeline: These frames are sent to Gemini 2.0 Flash with a specialized system instruction focused on critical logic auditing.\nUnified Results: The resulting report is added directly to your standard generation output.\n\nUsage\n\nCLI Usage\n\nAnalyze any video file with a natural language prompt.\n\nSDK Usage\n\nIntegrate video analysis into your TypeScript/JavaScript projects.\n\nAdvanced SDK Examples\n\nCustom Model Configuration\nFine-tune the analysis by adjusting token limits and temperature.\n\nDisabling Tool Interference\nBy default, the model might try to use available tools. For pure video analysis, you can disable them.\n\nExamples\nUI/UX Bug Analysis\n\nIdentify why a user is unable to complete a form or where the interface is misleading.\n\nPrompt: \"Find why the user is getting stuck at the payment step. Look for validation errors or hidden UI elements.\"\nSilent Failure Detection\n\nDetect cases where an action is taken but the system provides no feedback (no loaders, no success messages).\n\nPrompt: \"Audit the 'Submit' button click. Is there a visual 'bond' between the click and the next state? Report any lag or missing loading indicators.\"\nWorkflow Validation\n\nVerify if a complex multi-step process follows the intended business logic.\n\nPrompt: \"Trace the logical progression from 'Item Selection' to 'Checkout'. Does every state change correspond to a user action?\"\nComparison Analysis\n\nCompare two recordings to find discrepancies in behavior.\n\nPrompt: \"Compare these two clips. The first one is the expected behavior and the second one has a bug. Identify the exact frame or timestamp where the logic deviates.\"\n\nCommand Gallery\n\nQuick CLI recipes for common tasks:\n\nThe Analysis Report\n\nThe output is structured into four major sections designed to give you a complete understanding of the video:\nStrategic Overview & Intent: Defines the core activity, expected logic, and provides a primary verdict.\nThe Action-Reaction Chain: A granular, step-by-step audit of attempts, results, and technical inferences.\nCritical Findings: Categorized milestones or anomalies with root cause analysis and visual evidence in JSON.\nFinal Assessment: A conclusive summary of the logical flow based on the observed evidence.\n\nBest Practices\nFrame Depth: Short videos (under 10s) get high-density frame coverage (1 per second), while long ones are intelligently sampled.\nPrompt Precision: While the model is a \"Critical Logic Auditor,\" you can guide it with specific questions about the activity.\nFormat: The analysis is returned as text in result.content, making it easy to store, display, or pipe to other tools.","hierarchy":{"lvl0":"Features","lvl1":"Video Analysis","lvl2":"","lvl3":""}},
4609
4610
  {"objectID":"288c031674e78fd4fcd753b2d753bff8b085367a321fac3e9845113df6361a2d","title":"Video Analysis","url":"/docs/features/video-analysis#video-analysis","content":"Comprehensive video analysis for NeuroLink, powered by Gemini 2.0 Flash. This feature goes beyond basic visual description—it provides a deep logical audit of video sequences to understand \"why\" and \"how\" events occur.","hierarchy":{"lvl0":"Features","lvl1":"Video Analysis","lvl2":"Video Analysis","lvl3":""}},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.49.1",
3
+ "version": "12.49.3",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {