@juspay/neurolink 12.35.2 → 12.35.4

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.
package/dist/neurolink.js CHANGED
@@ -8146,8 +8146,30 @@ Current user's request: ${currentInput}`;
8146
8146
  const streamResult = await this.processStreamResult(processedStream, enhancedOptions, factoryResult);
8147
8147
  streamResult.finishReason =
8148
8148
  streamState.finishReason || streamResult.finishReason;
8149
- streamResult.toolCalls = streamState.toolCalls;
8150
- streamResult.toolResults = streamState.toolResults;
8149
+ // Unresolved #1819 review comment: a top-level cross-provider fallback
8150
+ // (handleStreamFallback, inside `processedStream` above) only
8151
+ // reassigns `streamState.toolCalls`/`toolResults` once the caller
8152
+ // starts draining the stream — strictly AFTER this point, since
8153
+ // `processedStream` is a lazy async generator and the
8154
+ // `processStreamResult` call above resolves without pulling a single
8155
+ // chunk. A plain value copy here (`streamResult.toolCalls =
8156
+ // streamState.toolCalls`) would freeze whatever streamState held at
8157
+ // THIS instant — the primary attempt's (usually empty) arrays —
8158
+ // because a property assignment is not a live binding. Define live
8159
+ // getters instead so every later read reflects streamState's current
8160
+ // value, mirroring the pattern #1819 already used to keep
8161
+ // finishReason/usage/model live via getters over streamState/
8162
+ // mcpStreamOutcome.
8163
+ Object.defineProperty(streamResult, "toolCalls", {
8164
+ enumerable: true,
8165
+ configurable: true,
8166
+ get: () => streamState.toolCalls,
8167
+ });
8168
+ Object.defineProperty(streamResult, "toolResults", {
8169
+ enumerable: true,
8170
+ configurable: true,
8171
+ get: () => streamState.toolResults,
8172
+ });
8151
8173
  if (!streamResult.usage) {
8152
8174
  streamResult.usage = mcpStreamOutcome.usage;
8153
8175
  }
@@ -9103,13 +9125,27 @@ Current user's request: ${currentInput}`;
9103
9125
  model: source?.model ?? config.options.model,
9104
9126
  usage: streamResult.usage,
9105
9127
  finishReason: streamResult.finishReason,
9106
- toolCalls: streamResult.toolCalls,
9107
- toolResults: streamResult.toolResults,
9108
9128
  analytics: streamResult.analytics,
9109
9129
  evaluation: streamResult.evaluation,
9110
9130
  events: config.events && config.events.length > 0 ? config.events : undefined,
9111
9131
  metadata: source ? undefined : Object.assign({}, responseMetadata),
9112
9132
  };
9133
+ // Unresolved #1819 review comment: toolCalls/toolResults must stay bound
9134
+ // to `streamResult`'s own live getters (defined in stream(), right after
9135
+ // processStreamResult) rather than the plain values a
9136
+ // `toolCalls: streamResult.toolCalls` literal above would have captured
9137
+ // — that reads the getter once and freezes whatever streamState held
9138
+ // before a top-level cross-provider fallback (handleStreamFallback) had
9139
+ // a chance to reassign it. Copying the property DESCRIPTOR, not its
9140
+ // current value, keeps every later read on `response` live too.
9141
+ const toolCallsDescriptor = Object.getOwnPropertyDescriptor(streamResult, "toolCalls");
9142
+ if (toolCallsDescriptor) {
9143
+ Object.defineProperty(response, "toolCalls", toolCallsDescriptor);
9144
+ }
9145
+ const toolResultsDescriptor = Object.getOwnPropertyDescriptor(streamResult, "toolResults");
9146
+ if (toolResultsDescriptor) {
9147
+ Object.defineProperty(response, "toolResults", toolResultsDescriptor);
9148
+ }
9113
9149
  if (!source) {
9114
9150
  return response;
9115
9151
  }
@@ -9117,14 +9153,26 @@ Current user's request: ${currentInput}`;
9117
9153
  // finishReason, stopReason for aborts and time limits), so those fields
9118
9154
  // stay plain values here. Copying the provider's getter for them would
9119
9155
  // report the raw vendor reason, e.g. Anthropic's end_turn, instead.
9120
- const gradedTerminalFields = new Set([
9156
+ //
9157
+ // toolCalls/toolResults are excluded for a different reason: `source`
9158
+ // (the PRIMARY provider's own result) may itself define them as live
9159
+ // getters over BaseProvider.withStreamModelFallback's `activeResult`
9160
+ // (#1819) — which only ever tracks a SAME-PROVIDER model retry. Copying
9161
+ // that descriptor here would silently re-point `response.toolCalls`/
9162
+ // `toolResults` back at the primary attempt, undoing the descriptor
9163
+ // copy above that binds them to `streamResult`'s `streamState`-backed
9164
+ // getters — the ones that actually track a top-level cross-provider
9165
+ // fallback.
9166
+ const liveAccessorExclusions = new Set([
9121
9167
  "finishReason",
9122
9168
  "stopReason",
9123
9169
  "rawFinishReason",
9170
+ "toolCalls",
9171
+ "toolResults",
9124
9172
  ]);
9125
9173
  for (const key of Object.getOwnPropertyNames(source)) {
9126
9174
  const descriptor = Object.getOwnPropertyDescriptor(source, key);
9127
- if (!gradedTerminalFields.has(key) &&
9175
+ if (!liveAccessorExclusions.has(key) &&
9128
9176
  descriptor &&
9129
9177
  typeof descriptor.get === "function") {
9130
9178
  Object.defineProperty(response, key, descriptor);
@@ -9133,7 +9181,19 @@ Current user's request: ${currentInput}`;
9133
9181
  Object.defineProperty(response, "metadata", {
9134
9182
  enumerable: true,
9135
9183
  configurable: true,
9136
- get: () => Object.assign(source.metadata ?? {}, responseMetadata),
9184
+ get: () => {
9185
+ // A caller mutation (`result.metadata.x = 1`) must persist across
9186
+ // reads. The previous `Object.assign(source.metadata ?? {},
9187
+ // responseMetadata)` allocated a brand-new object every time
9188
+ // `source.metadata` was undefined, so the mutation landed on a
9189
+ // value discarded immediately after. Installing a persistent object
9190
+ // into `source.metadata` the first time makes every later read
9191
+ // return the SAME object, so mutations on it stick.
9192
+ if (source.metadata === undefined) {
9193
+ source.metadata = {};
9194
+ }
9195
+ return Object.assign(source.metadata, responseMetadata);
9196
+ },
9137
9197
  set: (value) => {
9138
9198
  source.metadata = value;
9139
9199
  },
@@ -28,8 +28,12 @@ export declare abstract class BaseServerAdapter extends EventEmitter {
28
28
  constructor(neurolink: NeuroLink, config?: ServerAdapterConfig);
29
29
  /**
30
30
  * Initialize the underlying server framework
31
+ * May be async: adapters that dynamically import their framework package
32
+ * (e.g. Hono) return a Promise; adapters with a synchronous no-op stub that
33
+ * defer real setup to their own overridden `initialize()` (Express/Fastify/
34
+ * Koa) still return void.
31
35
  */
32
- protected abstract initializeFramework(): void;
36
+ protected abstract initializeFramework(): void | Promise<void>;
33
37
  /**
34
38
  * Register a route with the framework
35
39
  */
@@ -145,7 +145,7 @@ export class BaseServerAdapter extends EventEmitter {
145
145
  const startTime = Date.now();
146
146
  try {
147
147
  // Initialize framework-specific setup
148
- this.initializeFramework();
148
+ await this.initializeFramework();
149
149
  // Register built-in middleware
150
150
  this.registerBuiltInMiddleware();
151
151
  // Register built-in routes
@@ -16,6 +16,8 @@ export declare class HonoServerAdapter extends BaseServerAdapter {
16
16
  private rateLimitStore;
17
17
  private rateLimitCleanupInterval?;
18
18
  private requestContextStore;
19
+ private HTTPExceptionCtor;
20
+ private streamSSEFn;
19
21
  constructor(neurolink: NeuroLink, config?: ServerAdapterConfig);
20
22
  /**
21
23
  * Create rate limiter middleware for Hono
@@ -27,8 +29,10 @@ export declare class HonoServerAdapter extends BaseServerAdapter {
27
29
  private cleanupRateLimitStore;
28
30
  /**
29
31
  * Initialize Hono framework
32
+ * Dynamically imports Hono and its middleware so requiring only another
33
+ * adapter (Express/Fastify/Koa) never pulls Hono's import graph in.
30
34
  */
31
- protected initializeFramework(): void;
35
+ protected initializeFramework(): Promise<void>;
32
36
  /**
33
37
  * Register route with Hono
34
38
  */
@@ -3,13 +3,6 @@
3
3
  * Primary server adapter implementation using Hono framework
4
4
  * Hono is chosen for its performance, TypeScript-first design, and edge compatibility
5
5
  */
6
- import { Hono } from "hono";
7
- import { cors } from "hono/cors";
8
- import { HTTPException } from "hono/http-exception";
9
- import { logger as honoLogger } from "hono/logger";
10
- import { secureHeaders } from "hono/secure-headers";
11
- import { streamSSE } from "hono/streaming";
12
- import { timeout } from "hono/timeout";
13
6
  import { logger } from "../../utils/logger.js";
14
7
  import { AlreadyRunningError, ServerStopError, wrapError } from "../errors.js";
15
8
  import { BaseServerAdapter } from "../abstract/baseServerAdapter.js";
@@ -25,6 +18,11 @@ export class HonoServerAdapter extends BaseServerAdapter {
25
18
  rateLimitCleanupInterval;
26
19
  // Store context by request ID for sharing between middleware and route handlers
27
20
  requestContextStore = new Map();
21
+ // Populated by initializeFramework's dynamic imports; used by methods that
22
+ // run after initialization (route registration, streaming) and so cannot
23
+ // rely on initializeFramework's own local closure variables.
24
+ HTTPExceptionCtor;
25
+ streamSSEFn;
28
26
  constructor(neurolink, config = {}) {
29
27
  super(neurolink, config);
30
28
  }
@@ -113,8 +111,21 @@ export class HonoServerAdapter extends BaseServerAdapter {
113
111
  }
114
112
  /**
115
113
  * Initialize Hono framework
114
+ * Dynamically imports Hono and its middleware so requiring only another
115
+ * adapter (Express/Fastify/Koa) never pulls Hono's import graph in.
116
116
  */
117
- initializeFramework() {
117
+ async initializeFramework() {
118
+ const [{ Hono }, { cors }, { HTTPException }, { logger: honoLogger }, { secureHeaders }, { streamSSE }, { timeout },] = await Promise.all([
119
+ this.importFrameworkDependency("hono", "hono"),
120
+ this.importFrameworkDependency("hono/cors", "hono"),
121
+ this.importFrameworkDependency("hono/http-exception", "hono"),
122
+ this.importFrameworkDependency("hono/logger", "hono"),
123
+ this.importFrameworkDependency("hono/secure-headers", "hono"),
124
+ this.importFrameworkDependency("hono/streaming", "hono"),
125
+ this.importFrameworkDependency("hono/timeout", "hono"),
126
+ ]);
127
+ this.HTTPExceptionCtor = HTTPException;
128
+ this.streamSSEFn = streamSSE;
118
129
  this.app = new Hono();
119
130
  // Add secure headers
120
131
  this.app.use("*", secureHeaders());
@@ -284,7 +295,7 @@ export class HonoServerAdapter extends BaseServerAdapter {
284
295
  route: route.path,
285
296
  error: errorMessage,
286
297
  });
287
- throw new HTTPException(500, { message: errorMessage });
298
+ throw new this.HTTPExceptionCtor(500, { message: errorMessage });
288
299
  }
289
300
  finally {
290
301
  // Untrack connection when request completes
@@ -304,7 +315,7 @@ export class HonoServerAdapter extends BaseServerAdapter {
304
315
  c.header(key, value);
305
316
  }
306
317
  }
307
- return streamSSE(c, async (stream) => {
318
+ return this.streamSSEFn(c, async (stream) => {
308
319
  try {
309
320
  // Get streaming result from handler
310
321
  const result = await route.handler(ctx);