@juspay/neurolink 13.8.10 → 13.8.12

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.
@@ -13,6 +13,8 @@ import { ensureTelemetry, getLangfuseHealthStatus, } from "../../services/server
13
13
  import { logger } from "../../utils/logger.js";
14
14
  import { applyAllClients, applyClientsOnProxyStart, restoreAllClients, } from "../proxy-clients/registry.js";
15
15
  import { resolveProxyConfigPath } from "../../proxy/proxyConfig.js";
16
+ import { readProxyRequestText } from "../../proxy/requestBodyLimit.js";
17
+ import { RawBodyTooLargeError } from "../../server/utils/rawBody.js";
16
18
  import { redactUrlsInText, sanitizeForLog, } from "../../utils/logSanitize.js";
17
19
  import { raceWithAbort, withTimeout, } from "../../utils/async/withTimeout.js";
18
20
  import { startProxyHttpTrace } from "../../proxy/proxyTracer.js";
@@ -1511,6 +1513,13 @@ export async function createProxyStartApp(params) {
1511
1513
  }),
1512
1514
  ]);
1513
1515
  };
1516
+ /** 413 in the wire format this proxy answers its other request errors in. */
1517
+ const respondRequestTooLarge = async (c, metadata, message) => {
1518
+ if (metadata) {
1519
+ await recordRuntimeError(metadata, 413, "request_too_large", message);
1520
+ }
1521
+ return c.json({ type: "error", error: { type: "request_too_large", message } }, 413);
1522
+ };
1514
1523
  app.onError(async (err, c) => {
1515
1524
  const errMsg = err instanceof Error ? err.message : String(err);
1516
1525
  logger.always(`[proxy] unhandled error: ${errMsg}`);
@@ -1703,7 +1712,13 @@ export async function createProxyStartApp(params) {
1703
1712
  let body;
1704
1713
  let rawBody;
1705
1714
  if (method === "post") {
1706
- rawBody = await c.req.text().catch(() => undefined);
1715
+ // Bounded before it is parsed, and so before the share gate below
1716
+ // (which needs the model), the way the server adapters bound theirs.
1717
+ const read = await readProxyRequestText(c.req.raw, route.path);
1718
+ if (read instanceof RawBodyTooLargeError) {
1719
+ return respondRequestTooLarge(c, metadata, read.message);
1720
+ }
1721
+ rawBody = read;
1707
1722
  try {
1708
1723
  body = rawBody ? JSON.parse(rawBody) : emptyBody;
1709
1724
  }
@@ -619,16 +619,28 @@ const HAND_DESCRIPTORS = [
619
619
  astralTokensPerChar: 3,
620
620
  // Images only. Exactly four tiny images succeeded and five were refused
621
621
  // on both models. image/jpg also succeeded and is normalized to JPEG.
622
- // Retain the conservative 256,000-byte encoded-body cap: on 2026-10-04
623
- // both models accepted 520,000 text characters and refused 525,000 with
624
- // 413/code 5021. On 2026-10-03, clef-flash accepted 262,000 and refused
625
- // 270,000. Estimates are the encoded body bytes / 4, give or take one. The
626
- // threshold moved from 65,527 accepted / 67,527 refused to 130,026 /
627
- // 131,276 (clef) and 130,027 / 131,277 (flash); errors still print 65,536.
628
- // Inference: the new interval contains 131,072 (twice the printed
629
- // figure); reason unknown.
630
- // On 2026-10-05 both models accepted PNG bodies up to 492,485 bytes and
631
- // refused 532,505 (flash) and 532,499 (clef) with the same 413; the old 195/202 KB boundary is
622
+ // The 256,000-byte cap on the whole encoded body is NeuroLink's own and is
623
+ // the same for both models. It is well below what the service takes with
624
+ // images, since the 413/code 5021 gate does not count them (2026-10-24:
625
+ // bodies of up to 1.17 MB, images included, passed it on clef-flash). It
626
+ // is not below clef-flash's edge for the text: the estimate is the
627
+ // characters of the JSON without `images`, over four, and the refusal is
628
+ // past twice the window the message prints (24,576 for clef-flash, so
629
+ // 196,608 characters; 65,536 for clef, so 524,288). That is checked on the
630
+ // text alone, per model, in cloudflareClef.ts, 1,024 characters short of
631
+ // the edge. A text that large is out of reach of the state limit below
632
+ // except through the questions or control characters, which JSON writes
633
+ // as up to six characters each, so the margin costs a caller nothing.
634
+ // History: on 2026-10-04 both models accepted 520,000 text characters and
635
+ // refused 525,000 with 413/code 5021 (the window was then 65,536 for both).
636
+ // On 2026-10-03, clef-flash accepted 262,000 and refused 270,000. The
637
+ // threshold had moved from 65,527 accepted / 67,527 refused to 130,026 /
638
+ // 131,276 (clef) and 130,027 / 131,277 (flash) in a day, and the window
639
+ // of clef-flash has since fallen to 24,576 (by 2026-10-20), which moved
640
+ // its edge to 196,608. On 2026-10-05 both models accepted PNG bodies up
641
+ // to 492,485 bytes and refused 532,505 (flash) and 532,499 (clef) with the
642
+ // same 413, whose estimate (133,126) was the whole body over four, images
643
+ // included, which they no longer are. The old 195/202 KB boundary is
632
644
  // historical. See the guide for exact model and date coverage.
633
645
  media: { maxImages: 4, video: false, maxRequestBytes: 256_000 },
634
646
  },
@@ -3,8 +3,10 @@ import type { ProviderModelManifest } from "../../types/index.js";
3
3
  * Cloudflare Clef on Workers AI — the `decide` inference type, not text
4
4
  * generation, and not the Workers AI text models of the `cloudflare` provider.
5
5
  *
6
- * `contextWindow` is the figure Cloudflare documents (65,536 tokens). The
7
- * Workers AI endpoint ignored state text past about 2,048 tokens until
6
+ * `contextWindow` is the window Cloudflare's own error messages print: 65,536
7
+ * tokens for `clef` (the figure its model page documents) and 24,576 for
8
+ * `clef-flash`, which printed 65,536 on 2026-10-04 and 24,576 by 2026-10-20.
9
+ * The Workers AI endpoint ignored state text past about 2,048 tokens until
8
10
  * 2026-10-04 and read far more on 2026-10-07 (hosted service or model:
9
11
  * unknown), so NeuroLink enforces the limit on the descriptor's
10
12
  * `decisionLimits`, not this. `vision` describes `generate()`
@@ -2,8 +2,10 @@
2
2
  * Cloudflare Clef on Workers AI — the `decide` inference type, not text
3
3
  * generation, and not the Workers AI text models of the `cloudflare` provider.
4
4
  *
5
- * `contextWindow` is the figure Cloudflare documents (65,536 tokens). The
6
- * Workers AI endpoint ignored state text past about 2,048 tokens until
5
+ * `contextWindow` is the window Cloudflare's own error messages print: 65,536
6
+ * tokens for `clef` (the figure its model page documents) and 24,576 for
7
+ * `clef-flash`, which printed 65,536 on 2026-10-04 and 24,576 by 2026-10-20.
8
+ * The Workers AI endpoint ignored state text past about 2,048 tokens until
7
9
  * 2026-10-04 and read far more on 2026-10-07 (hosted service or model:
8
10
  * unknown), so NeuroLink enforces the limit on the descriptor's
9
11
  * `decisionLimits`, not this. `vision` describes `generate()`
@@ -34,7 +36,7 @@ export const cloudflareClefManifest = {
34
36
  "clef-flash": {
35
37
  aliases: ["@cf/cloudflare/clef-flash"],
36
38
  displayName: "Cloudflare Clef-flash (9B)",
37
- contextWindow: 65_536,
39
+ contextWindow: 24_576,
38
40
  maxOutputTokens: 256,
39
41
  vision: false,
40
42
  functionCalling: false,
@@ -41,6 +41,15 @@ export declare class CloudflareClefProvider extends SystemOneDecisionProvider {
41
41
  * differs from the path.
42
42
  */
43
43
  protected buildDecisionBody(state: DecisionState, questions: Record<string, Record<string, unknown>>, model: string, media?: DecisionPreparedMedia): Record<string, unknown>;
44
+ /**
45
+ * Cloudflare's 413/code 5021 gate, applied before the request is sent: the
46
+ * text of the body, without its images, may not pass eight characters per
47
+ * token of the model's window, less a margin (see `TEXT_GATE_WINDOW_TOKENS`).
48
+ * Refused as `max_tokens_exceeded`, which is how Cloudflare's own 413 is
49
+ * classified, so a caller sees the same kind whether the refusal is made here
50
+ * or there.
51
+ */
52
+ protected assertWithinVendorLimits(payload: Record<string, unknown>, model: string): void;
44
53
  /** A success is `{ result: { model, answers, usage }, success: true }`. */
45
54
  protected readDecisionPayload(payload: unknown): unknown;
46
55
  protected parseDecisionError(status: number, payload: unknown, requestId: string | undefined): DecisionError;
@@ -15,6 +15,32 @@ const MODEL_PREFIX = "@cf/cloudflare/";
15
15
  * is merely large and retry it into the same wait.
16
16
  */
17
17
  const TIMEOUT_MS_PER_THOUSAND_STATE_TOKENS = 250;
18
+ /**
19
+ * The context window each model's 413/code 5021 message prints. Cloudflare
20
+ * refuses a request with that code when its estimate passes twice the window,
21
+ * and the estimate is the characters of the request's JSON text, without its
22
+ * `images` array, over four: the images are not counted. Measured to the
23
+ * character on 2026-10-23 and 2026-10-24: clef-flash accepted a text of 196,609
24
+ * characters and refused 196,610 (twice 24,576, times four, is 196,608), clef
25
+ * accepted 524,288 and refused 524,292, with and without images of up to
26
+ * 1.1 MB in the body. The window of clef-flash fell from 65,536 (2026-10-04) to
27
+ * 24,576 (by 2026-10-20), so these are measurements to re-check, not constants
28
+ * of the model. A model missing from the map has no limit of this
29
+ * kind here: the service answers for it.
30
+ */
31
+ const TEXT_GATE_WINDOW_TOKENS = new Map([
32
+ ["clef", 65_536],
33
+ ["clef-flash", 24_576],
34
+ ]);
35
+ /** The estimate is characters / 4 and the refusal is past twice the window. */
36
+ const TEXT_GATE_CHARACTERS_PER_WINDOW_TOKEN = 8;
37
+ /**
38
+ * Kept below the measured edge. The edge is exact for the bodies that were
39
+ * probed (one question, an ASCII state, `JSON.stringify`), so the margin is for
40
+ * what they did not vary, and it is 0.5% of the smaller cap. See the
41
+ * descriptor for why it costs a caller nothing.
42
+ */
43
+ const TEXT_GATE_MARGIN_CHARACTERS = 1_024;
18
44
  /**
19
45
  * Cloudflare account ids are 32 hex digits. The pattern is wider so that a
20
46
  * change of format needs no release, but it still keeps `/`, `.`, `?` and
@@ -320,6 +346,31 @@ export class CloudflareClefProvider extends SystemOneDecisionProvider {
320
346
  ...(images.length > 0 ? { images } : {}),
321
347
  };
322
348
  }
349
+ /**
350
+ * Cloudflare's 413/code 5021 gate, applied before the request is sent: the
351
+ * text of the body, without its images, may not pass eight characters per
352
+ * token of the model's window, less a margin (see `TEXT_GATE_WINDOW_TOKENS`).
353
+ * Refused as `max_tokens_exceeded`, which is how Cloudflare's own 413 is
354
+ * classified, so a caller sees the same kind whether the refusal is made here
355
+ * or there.
356
+ */
357
+ assertWithinVendorLimits(payload, model) {
358
+ const wire = wireModel(model);
359
+ const window = wire === undefined ? undefined : TEXT_GATE_WINDOW_TOKENS.get(wire);
360
+ if (window === undefined) {
361
+ return;
362
+ }
363
+ const limit = window * TEXT_GATE_CHARACTERS_PER_WINDOW_TOKEN -
364
+ TEXT_GATE_MARGIN_CHARACTERS;
365
+ const characters = JSON.stringify({ ...payload, images: undefined }).length;
366
+ if (characters > limit) {
367
+ throw this.decisionError({
368
+ kind: "max_tokens_exceeded",
369
+ message: `The request's text, without its images, is ${characters} characters; ${this.vendorLabel()}'s "${wire}" model accepts at most ${limit}. Shorten the state or ask shorter questions.`,
370
+ retryable: false,
371
+ });
372
+ }
373
+ }
323
374
  /** A success is `{ result: { model, answers, usage }, success: true }`. */
324
375
  readDecisionPayload(payload) {
325
376
  return isRecord(payload) && isRecord(payload.result)
@@ -335,7 +386,7 @@ export class CloudflareClefProvider extends SystemOneDecisionProvider {
335
386
  .slice(0, MAX_ERROR_MESSAGE_CHARS) || fallback;
336
387
  if (kind === "max_tokens_exceeded") {
337
388
  message +=
338
- " Cloudflare counts the whole request, image data included, at about four characters per token.";
389
+ " Cloudflare counts the request's text, without its images, at about four characters per token.";
339
390
  }
340
391
  else if (parsed?.code === 7000) {
341
392
  message += `. Check the model name (Clef is served as "clef" and "clef-flash") and the base URL, which must end in /client/v4 and name your account.`;
@@ -71,6 +71,12 @@ export declare abstract class SystemOneDecisionProvider extends BaseProvider {
71
71
  protected abstract decisionEndpoint(model: string): string;
72
72
  protected abstract decisionHeaders(): Record<string, string>;
73
73
  protected abstract buildDecisionBody(state: DecisionState, questions: Record<string, Record<string, unknown>>, model: string, media?: DecisionPreparedMedia): Record<string, unknown>;
74
+ /**
75
+ * A refusal the vendor's own gate would make on the request about to be
76
+ * sent, which the descriptor's limits cannot express. Throw the refusal
77
+ * (see `decisionError`); the default has no such gate.
78
+ */
79
+ protected assertWithinVendorLimits(_payload: Record<string, unknown>, _model: string): void;
74
80
  protected abstract parseDecisionError(status: number, payload: unknown, requestId: string | undefined): DecisionError;
75
81
  protected abstract readRequestId(headers: Headers): string | undefined;
76
82
  /**
@@ -141,6 +141,12 @@ export class SystemOneDecisionProvider extends BaseProvider {
141
141
  missingConfigMessage() {
142
142
  return undefined;
143
143
  }
144
+ /**
145
+ * A refusal the vendor's own gate would make on the request about to be
146
+ * sent, which the descriptor's limits cannot express. Throw the refusal
147
+ * (see `decisionError`); the default has no such gate.
148
+ */
149
+ assertWithinVendorLimits(_payload, _model) { }
144
150
  /**
145
151
  * The System One wire calls the yes/no primitive `noul`; this codebase and
146
152
  * every SDK exposing it call it `boolean`.
@@ -287,8 +293,10 @@ export class SystemOneDecisionProvider extends BaseProvider {
287
293
  this.encodeQuestion(question),
288
294
  ]));
289
295
  const media = await this.prepareMedia(request);
290
- const body = JSON.stringify(this.buildDecisionBody(request.state, wireQuestions, resolvedModel, media));
296
+ const payload = this.buildDecisionBody(request.state, wireQuestions, resolvedModel, media);
297
+ const body = JSON.stringify(payload);
291
298
  this.assertWithinRequestBytes(body);
299
+ this.assertWithinVendorLimits(payload, resolvedModel);
292
300
  const timeoutMs = request.timeoutMs ??
293
301
  this.defaultTimeoutMs(questionEntries.length, stateTokens) ??
294
302
  this.defaultTimeout ??
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Request-body ceiling for the proxy's POST routes.
3
+ *
4
+ * The body is read before anything else about the request is known (the model,
5
+ * the share token), so it is bounded first, chunk by chunk, the way the server
6
+ * adapters bound theirs. Each route gets the request size limit its API
7
+ * documents; one environment variable overrides all of them.
8
+ *
9
+ * @module proxy/requestBodyLimit
10
+ */
11
+ import { RawBodyTooLargeError } from "../server/utils/rawBody.js";
12
+ /**
13
+ * Environment variable that sets the ceiling for every POST route, replacing
14
+ * the per-route defaults below: a byte count, or `64mb` / `512kb` / `1gb`.
15
+ */
16
+ export declare const PROXY_MAX_REQUEST_BODY_ENV = "NEUROLINK_PROXY_MAX_REQUEST_BODY_BYTES";
17
+ /**
18
+ * 32 MiB. The Claude Messages API and Token Counting API document a 32 MB
19
+ * maximum request size and answer a larger request with 413 `request_too_large`:
20
+ * https://platform.claude.com/docs/en/api/errors#request-size-limits
21
+ *
22
+ * Also the limit for a POST route with no upstream API of its own (the peer
23
+ * endpoints), which has nothing documented to follow.
24
+ */
25
+ export declare const DEFAULT_PROXY_MAX_REQUEST_BODY_BYTES: number;
26
+ /**
27
+ * 100 MiB. The Gemini API documents inline data as "100 MB per request or
28
+ * payload (50 MB for PDFs)":
29
+ * https://ai.google.dev/gemini-api/docs/file-input-methods
30
+ */
31
+ export declare const GEMINI_PROXY_MAX_REQUEST_BODY_BYTES: number;
32
+ /**
33
+ * 64 MiB. A local cap, not an OpenAI figure: OpenAI documents no general
34
+ * request-body limit for the Responses or Chat Completions APIs. The only
35
+ * per-request payload figure it states is the image input requirement, "Up to
36
+ * 512 MB total payload per request":
37
+ * https://developers.openai.com/api/docs/guides/images-vision
38
+ * (file inputs are separately limited to 50 MB combined:
39
+ * https://developers.openai.com/api/docs/guides/file-inputs). The proxy holds
40
+ * a body in memory while it reads it, about four times its size, so it does
41
+ * not take the 512 MB figure as its ceiling; a client that sends more raises
42
+ * `NEUROLINK_PROXY_MAX_REQUEST_BODY_BYTES`. The Codex route speaks the
43
+ * Responses format and documents nothing of its own, so it uses the same cap.
44
+ */
45
+ export declare const OPENAI_PROXY_MAX_REQUEST_BODY_BYTES: number;
46
+ /**
47
+ * The ceiling in bytes for one route: the environment override when it is a
48
+ * positive size, else that route's documented limit.
49
+ *
50
+ * @param routePath - The route as registered, for example `/v1/messages`.
51
+ */
52
+ export declare function resolveProxyMaxRequestBodyBytes(routePath: string, env?: Record<string, string | undefined>): number;
53
+ /**
54
+ * The request body as text, `undefined` when it cannot be read (a client that
55
+ * hung up mid-body), or the error when it is over the route's ceiling. Over the
56
+ * ceiling the stream is cancelled, so nothing past it is buffered.
57
+ *
58
+ * @param routePath - The route as registered, for example `/v1/messages`.
59
+ */
60
+ export declare function readProxyRequestText(request: Request, routePath: string, env?: Record<string, string | undefined>): Promise<string | undefined | RawBodyTooLargeError>;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Request-body ceiling for the proxy's POST routes.
3
+ *
4
+ * The body is read before anything else about the request is known (the model,
5
+ * the share token), so it is bounded first, chunk by chunk, the way the server
6
+ * adapters bound theirs. Each route gets the request size limit its API
7
+ * documents; one environment variable overrides all of them.
8
+ *
9
+ * @module proxy/requestBodyLimit
10
+ */
11
+ import { RawBodyTooLargeError, readBoundedRequestBody, tryParseByteLimit, } from "../server/utils/rawBody.js";
12
+ import { logger } from "../utils/logger.js";
13
+ /**
14
+ * Environment variable that sets the ceiling for every POST route, replacing
15
+ * the per-route defaults below: a byte count, or `64mb` / `512kb` / `1gb`.
16
+ */
17
+ export const PROXY_MAX_REQUEST_BODY_ENV = "NEUROLINK_PROXY_MAX_REQUEST_BODY_BYTES";
18
+ const MIB = 1024 * 1024;
19
+ /**
20
+ * 32 MiB. The Claude Messages API and Token Counting API document a 32 MB
21
+ * maximum request size and answer a larger request with 413 `request_too_large`:
22
+ * https://platform.claude.com/docs/en/api/errors#request-size-limits
23
+ *
24
+ * Also the limit for a POST route with no upstream API of its own (the peer
25
+ * endpoints), which has nothing documented to follow.
26
+ */
27
+ export const DEFAULT_PROXY_MAX_REQUEST_BODY_BYTES = 32 * MIB;
28
+ /**
29
+ * 100 MiB. The Gemini API documents inline data as "100 MB per request or
30
+ * payload (50 MB for PDFs)":
31
+ * https://ai.google.dev/gemini-api/docs/file-input-methods
32
+ */
33
+ export const GEMINI_PROXY_MAX_REQUEST_BODY_BYTES = 100 * MIB;
34
+ /**
35
+ * 64 MiB. A local cap, not an OpenAI figure: OpenAI documents no general
36
+ * request-body limit for the Responses or Chat Completions APIs. The only
37
+ * per-request payload figure it states is the image input requirement, "Up to
38
+ * 512 MB total payload per request":
39
+ * https://developers.openai.com/api/docs/guides/images-vision
40
+ * (file inputs are separately limited to 50 MB combined:
41
+ * https://developers.openai.com/api/docs/guides/file-inputs). The proxy holds
42
+ * a body in memory while it reads it, about four times its size, so it does
43
+ * not take the 512 MB figure as its ceiling; a client that sends more raises
44
+ * `NEUROLINK_PROXY_MAX_REQUEST_BODY_BYTES`. The Codex route speaks the
45
+ * Responses format and documents nothing of its own, so it uses the same cap.
46
+ */
47
+ export const OPENAI_PROXY_MAX_REQUEST_BODY_BYTES = 64 * MIB;
48
+ /**
49
+ * Registered route path (a suffix, so a base path does not matter) to limit.
50
+ * The first match wins; no match means the default above.
51
+ */
52
+ const ROUTE_LIMITS = [
53
+ [/\/v1\/messages(?:\/count_tokens)?$/, DEFAULT_PROXY_MAX_REQUEST_BODY_BYTES],
54
+ [/\/v1\/chat\/completions$/, OPENAI_PROXY_MAX_REQUEST_BODY_BYTES],
55
+ [/\/backend-api\/codex\/responses$/, OPENAI_PROXY_MAX_REQUEST_BODY_BYTES],
56
+ [/\/v1beta\/models\/[^/]+$/, GEMINI_PROXY_MAX_REQUEST_BODY_BYTES],
57
+ ];
58
+ let warnedAbout;
59
+ /**
60
+ * The ceiling in bytes for one route: the environment override when it is a
61
+ * positive size, else that route's documented limit.
62
+ *
63
+ * @param routePath - The route as registered, for example `/v1/messages`.
64
+ */
65
+ export function resolveProxyMaxRequestBodyBytes(routePath, env = process.env) {
66
+ const raw = env[PROXY_MAX_REQUEST_BODY_ENV]?.trim();
67
+ if (raw) {
68
+ const parsed = tryParseByteLimit(raw);
69
+ if (parsed !== undefined && parsed > 0) {
70
+ return parsed;
71
+ }
72
+ if (warnedAbout !== raw) {
73
+ warnedAbout = raw;
74
+ logger.always(`[proxy] ignoring ${PROXY_MAX_REQUEST_BODY_ENV}=${JSON.stringify(raw)}: expected a positive byte count or a size such as 64mb; using each route's documented limit`);
75
+ }
76
+ }
77
+ return (ROUTE_LIMITS.find(([pattern]) => pattern.test(routePath))?.[1] ??
78
+ DEFAULT_PROXY_MAX_REQUEST_BODY_BYTES);
79
+ }
80
+ /**
81
+ * The request body as text, `undefined` when it cannot be read (a client that
82
+ * hung up mid-body), or the error when it is over the route's ceiling. Over the
83
+ * ceiling the stream is cancelled, so nothing past it is buffered.
84
+ *
85
+ * @param routePath - The route as registered, for example `/v1/messages`.
86
+ */
87
+ export async function readProxyRequestText(request, routePath, env = process.env) {
88
+ try {
89
+ const bytes = await readBoundedRequestBody(request, resolveProxyMaxRequestBodyBytes(routePath, env));
90
+ return bytes instanceof RawBodyTooLargeError
91
+ ? bytes
92
+ : new TextDecoder().decode(bytes);
93
+ }
94
+ catch {
95
+ return undefined;
96
+ }
97
+ }
@@ -6,6 +6,10 @@
6
6
  import type { NeuroLink } from "../../neurolink.js";
7
7
  import { BaseServerAdapter } from "../abstract/baseServerAdapter.js";
8
8
  import type { MiddlewareDefinition, RouteDefinition, ServerAdapterConfig } from "../../types/index.js";
9
+ /**
10
+ * Hono-specific server adapter
11
+ * Supports multiple runtimes: Bun, Deno, Node.js
12
+ */
9
13
  export declare class HonoServerAdapter extends BaseServerAdapter {
10
14
  private app;
11
15
  private server?;
@@ -14,6 +18,7 @@ export declare class HonoServerAdapter extends BaseServerAdapter {
14
18
  private requestContextStore;
15
19
  private HTTPExceptionCtor;
16
20
  private streamSSEFn;
21
+ private parseFormBody;
17
22
  constructor(neurolink: NeuroLink, config?: ServerAdapterConfig);
18
23
  /**
19
24
  * Create rate limiter middleware for Hono
@@ -74,5 +79,13 @@ export declare class HonoServerAdapter extends BaseServerAdapter {
74
79
  getNativeServer(): unknown;
75
80
  private extractHeaders;
76
81
  private extractQuery;
82
+ /**
83
+ * A request's context, or the 413 that answers a JSON or form body over its
84
+ * limit. That answer is built here, before the context exists, so no
85
+ * middleware (an auth check included) and no route runs for the request.
86
+ * An upload over the limit still reaches its route as the error itself,
87
+ * which answers in its own wire format.
88
+ */
89
+ private createRequestContext;
77
90
  private extractBody;
78
91
  }