agenticreplay 0.1.11 → 0.1.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.
Files changed (50) hide show
  1. package/node_modules/@agenticreplay/adapters/package.json +6 -6
  2. package/node_modules/@agenticreplay/core/package.json +3 -3
  3. package/node_modules/@agenticreplay/fs-capture/package.json +2 -2
  4. package/node_modules/@agenticreplay/mcp-shim/dist/cli.js +0 -0
  5. package/node_modules/@agenticreplay/mcp-shim/package.json +1 -1
  6. package/node_modules/@agenticreplay/node-instrument/package.json +1 -1
  7. package/node_modules/@agenticreplay/plugin-api/package.json +1 -1
  8. package/node_modules/@agenticreplay/providers/package.json +3 -3
  9. package/node_modules/@agenticreplay/proxy/dist/request-body.d.ts +9 -0
  10. package/node_modules/@agenticreplay/proxy/dist/request-body.d.ts.map +1 -0
  11. package/node_modules/@agenticreplay/proxy/dist/request-body.js +65 -0
  12. package/node_modules/@agenticreplay/proxy/dist/request-body.js.map +1 -0
  13. package/node_modules/@agenticreplay/proxy/dist/server.d.ts +3 -0
  14. package/node_modules/@agenticreplay/proxy/dist/server.d.ts.map +1 -1
  15. package/node_modules/@agenticreplay/proxy/dist/server.js +121 -124
  16. package/node_modules/@agenticreplay/proxy/dist/server.js.map +1 -1
  17. package/node_modules/@agenticreplay/proxy/package.json +5 -5
  18. package/node_modules/@agenticreplay/schema/package.json +1 -1
  19. package/node_modules/@agenticreplay/shell-shim/package.json +3 -3
  20. package/node_modules/@agenticreplay/viewer/package.json +2 -2
  21. package/node_modules/fast-uri/.github/workflows/release.yml +35 -0
  22. package/node_modules/fast-uri/README.md +8 -0
  23. package/node_modules/fast-uri/index.js +98 -18
  24. package/node_modules/fast-uri/lib/utils.js +52 -0
  25. package/node_modules/fast-uri/package.json +1 -1
  26. package/node_modules/fast-uri/test/host-delimiter-security.test.js +94 -0
  27. package/node_modules/fast-uri/test/relative-normalization-security.test.js +156 -0
  28. package/node_modules/fast-uri/test/reserved-path-normalization.test.js +32 -4
  29. package/node_modules/fast-uri/test/scheme-validation.test.js +53 -14
  30. package/package.json +12 -12
  31. package/packages/cli/dist/cli.js +0 -0
  32. package/packages/cli/dist/commands/attach.js +1 -0
  33. package/packages/cli/dist/commands/attach.js.map +1 -1
  34. package/packages/cli/dist/commands/record.d.ts.map +1 -1
  35. package/packages/cli/dist/commands/record.js +7 -0
  36. package/packages/cli/dist/commands/record.js.map +1 -1
  37. package/packages/cli/dist/exchange-events.d.ts +2 -0
  38. package/packages/cli/dist/exchange-events.d.ts.map +1 -1
  39. package/packages/cli/dist/exchange-events.js +36 -24
  40. package/packages/cli/dist/exchange-events.js.map +1 -1
  41. package/packages/cli/dist/main.js +1 -0
  42. package/packages/cli/dist/main.js.map +1 -1
  43. package/packages/cli/dist/out.d.ts +6 -0
  44. package/packages/cli/dist/out.d.ts.map +1 -1
  45. package/packages/cli/dist/out.js +50 -5
  46. package/packages/cli/dist/out.js.map +1 -1
  47. package/packages/cli/dist/tls-capture.d.ts.map +1 -1
  48. package/packages/cli/dist/tls-capture.js +4 -3
  49. package/packages/cli/dist/tls-capture.js.map +1 -1
  50. package/packages/cli/package.json +11 -11
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticreplay/adapters",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -17,11 +17,11 @@
17
17
  "dist"
18
18
  ],
19
19
  "dependencies": {
20
- "@agenticreplay/core": "0.1.11",
21
- "@agenticreplay/node-instrument": "0.1.11",
22
- "@agenticreplay/plugin-api": "0.1.11",
23
- "@agenticreplay/proxy": "0.1.11",
24
- "@agenticreplay/shell-shim": "0.1.11"
20
+ "@agenticreplay/core": "0.1.12",
21
+ "@agenticreplay/node-instrument": "0.1.12",
22
+ "@agenticreplay/plugin-api": "0.1.12",
23
+ "@agenticreplay/proxy": "0.1.12",
24
+ "@agenticreplay/shell-shim": "0.1.12"
25
25
  },
26
26
  "engines": {
27
27
  "node": ">=24.0.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticreplay/core",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -17,8 +17,8 @@
17
17
  "dist"
18
18
  ],
19
19
  "dependencies": {
20
- "@agenticreplay/plugin-api": "0.1.11",
21
- "@agenticreplay/schema": "0.1.11",
20
+ "@agenticreplay/plugin-api": "0.1.12",
21
+ "@agenticreplay/schema": "0.1.12",
22
22
  "any-ascii": "0.3.3"
23
23
  },
24
24
  "engines": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticreplay/fs-capture",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -17,7 +17,7 @@
17
17
  "dist"
18
18
  ],
19
19
  "dependencies": {
20
- "@agenticreplay/schema": "0.1.11"
20
+ "@agenticreplay/schema": "0.1.12"
21
21
  },
22
22
  "engines": {
23
23
  "node": ">=24.0.0"
File without changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticreplay/mcp-shim",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticreplay/node-instrument",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticreplay/plugin-api",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticreplay/providers",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -17,8 +17,8 @@
17
17
  "dist"
18
18
  ],
19
19
  "dependencies": {
20
- "@agenticreplay/plugin-api": "0.1.11",
21
- "@agenticreplay/schema": "0.1.11"
20
+ "@agenticreplay/plugin-api": "0.1.12",
21
+ "@agenticreplay/schema": "0.1.12"
22
22
  },
23
23
  "engines": {
24
24
  "node": ">=24.0.0"
@@ -0,0 +1,9 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ /** A capture limit limits retention, never the request sent to the provider. */
3
+ export declare function readRequestBody(req: IncomingMessage, limit: number, reserve: (bytes: number) => boolean, release: (bytes: number) => void): Promise<{
4
+ buffer?: Buffer;
5
+ stream?: ReadableStream<Uint8Array>;
6
+ bytes: () => number;
7
+ dispose: () => void;
8
+ }>;
9
+ //# sourceMappingURL=request-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-body.d.ts","sourceRoot":"","sources":["../src/request-body.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjD,gFAAgF;AAChF,wBAAsB,eAAe,CACnC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,EACnC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAC/B,OAAO,CAAC;IACT,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC,CA4DD"}
@@ -0,0 +1,65 @@
1
+ import { Readable } from 'node:stream';
2
+ /** A capture limit limits retention, never the request sent to the provider. */
3
+ export async function readRequestBody(req, limit, reserve, release) {
4
+ const chunks = [];
5
+ const iterator = req[Symbol.asyncIterator]();
6
+ let bytes = 0;
7
+ let reserved = 0;
8
+ const dispose = () => {
9
+ release(reserved);
10
+ reserved = 0;
11
+ chunks.length = 0;
12
+ };
13
+ try {
14
+ while (true) {
15
+ const next = await iterator.next();
16
+ if (next.done)
17
+ return { buffer: Buffer.concat(chunks), bytes: () => bytes, dispose };
18
+ const chunk = next.value;
19
+ bytes += chunk.length;
20
+ if (bytes <= limit && reserve(chunk.length)) {
21
+ reserved += chunk.length;
22
+ chunks.push(chunk);
23
+ continue;
24
+ }
25
+ // Do not exit a for-await loop here: its implicit return() destroys IncomingMessage.
26
+ // Hand the same iterator to fetch and stream the remainder with its backpressure.
27
+ async function* forward() {
28
+ try {
29
+ while (chunks.length > 0) {
30
+ const prefix = chunks.shift();
31
+ yield prefix;
32
+ const held = Math.min(reserved, prefix.length);
33
+ reserved -= held;
34
+ release(held);
35
+ }
36
+ yield chunk;
37
+ while (true) {
38
+ const part = await iterator.next();
39
+ if (part.done)
40
+ return;
41
+ const tail = part.value;
42
+ bytes += tail.length;
43
+ yield tail;
44
+ }
45
+ }
46
+ finally {
47
+ dispose();
48
+ await iterator.return?.();
49
+ }
50
+ }
51
+ return {
52
+ stream: Readable.toWeb(Readable.from(forward(), { objectMode: false, highWaterMark: 65536 }), {
53
+ strategy: { highWaterMark: 65536, size: (chunk) => chunk.byteLength },
54
+ }),
55
+ bytes: () => bytes,
56
+ dispose,
57
+ };
58
+ }
59
+ }
60
+ catch (error) {
61
+ dispose();
62
+ throw error;
63
+ }
64
+ }
65
+ //# sourceMappingURL=request-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-body.js","sourceRoot":"","sources":["../src/request-body.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,gFAAgF;AAChF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAoB,EACpB,KAAa,EACb,OAAmC,EACnC,OAAgC;IAOhC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClB,QAAQ,GAAG,CAAC,CAAC;QACb,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,CAAC,CAAC;IACF,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,IAAI,CAAC,IAAI;gBAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;YACrF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,CAAC;YACnC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;YACtB,IAAI,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5C,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;gBACzB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YACD,qFAAqF;YACrF,kFAAkF;YAClF,KAAK,SAAS,CAAC,CAAC,OAAO;gBACrB,IAAI,CAAC;oBACH,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACzB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAG,CAAC;wBAC/B,MAAM,MAAM,CAAC;wBACb,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;wBAC/C,QAAQ,IAAI,IAAI,CAAC;wBACjB,OAAO,CAAC,IAAI,CAAC,CAAC;oBAChB,CAAC;oBACD,MAAM,KAAK,CAAC;oBACZ,OAAO,IAAI,EAAE,CAAC;wBACZ,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;wBACnC,IAAI,IAAI,CAAC,IAAI;4BAAE,OAAO;wBACtB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAe,CAAC;wBAClC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC;wBACrB,MAAM,IAAI,CAAC;oBACb,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACT,OAAO,EAAE,CAAC;oBACV,MAAM,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC5B,CAAC;YACH,CAAC;YACD,OAAO;gBACL,MAAM,EAAE,QAAQ,CAAC,KAAK,CACpB,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,EACrE;oBACE,QAAQ,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE;iBAClF,CAC4B;gBAC/B,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK;gBAClB,OAAO;aACR,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,CAAC;QACV,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -140,6 +140,8 @@ export interface ProxyOptions {
140
140
  upstream?: Record<string, string>;
141
141
  dialects?: Dialect[];
142
142
  onExchange?: (e: RecordedExchange) => void;
143
+ /** Keep completed bodies for exchanges(); default true. Disk recorders should disable this. */
144
+ retainExchanges?: boolean;
143
145
  onRequestStart?: (request: NetRequest) => void | Promise<void>;
144
146
  maxCapturedBytes?: number;
145
147
  maxBufferedBytes?: number;
@@ -171,6 +173,7 @@ export interface ProxyHandle {
171
173
  url: string;
172
174
  port: number;
173
175
  stats(): ProxyStats;
176
+ /** Empty when retainExchanges is false; onExchange still receives every completed exchange. */
174
177
  exchanges(): RecordedExchange[];
175
178
  /** Present only when TLS interception is on. */
176
179
  tls?: TlsInterceptInfo;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
2
  import { createServer } from 'node:http';
3
+ import { Readable } from 'node:stream';
4
+ import { readRequestBody } from './request-body.js';
3
5
  import { AUTH_REQUEST_HEADERS, AUTH_RESPONSE_HEADERS, BINARY_BODY_PREFIX, Redactor, fetchPinned, } from '@agenticreplay/core';
4
6
  import { anthropicToCanonicalRequest, anthropicToCanonicalResponse, openaiToCanonicalRequest, openaiToCanonicalResponse, parseAnthropicSse, parseOpenaiSse, parseResponsesSse, responsesToCanonicalRequest, responsesToCanonicalResponse, } from '@agenticreplay/providers';
5
7
  import { anthropicDialect, codexDialect, openaiDialect, responsesDialect, selectDialect, } from './dialects.js';
@@ -315,29 +317,6 @@ export function defaultDialects() {
315
317
  }),
316
318
  ];
317
319
  }
318
- class BodyLimitError extends Error {
319
- bytes;
320
- limit;
321
- constructor(bytes, limit) {
322
- super(`request body exceeded capture limit of ${limit} bytes`);
323
- this.bytes = bytes;
324
- this.limit = limit;
325
- }
326
- }
327
- async function readBody(req, limit) {
328
- const chunks = [];
329
- let bytes = 0;
330
- for await (const chunk of req) {
331
- const buffer = chunk;
332
- bytes += buffer.length;
333
- if (bytes > limit) {
334
- req.resume();
335
- throw new BodyLimitError(bytes, limit);
336
- }
337
- chunks.push(buffer);
338
- }
339
- return { body: Buffer.concat(chunks), bytes };
340
- }
341
320
  /** Is this something a dialect could parse at all? Cheap guard, run before anything is forwarded. */
342
321
  function isReadable(rawBody) {
343
322
  try {
@@ -378,6 +357,13 @@ export async function createProxy(options) {
378
357
  // `fetchImpl` stays the seam the tests inject through; only the default moves.
379
358
  const doFetch = options.fetchImpl ?? fetchPinned;
380
359
  const captured = [];
360
+ let exchangeSequence = 0;
361
+ function deliverExchange(exchange) {
362
+ exchangeSequence += 1;
363
+ if (options.retainExchanges !== false)
364
+ captured.push(exchange);
365
+ options.onExchange?.(exchange);
366
+ }
381
367
  // Plain base-URL traffic needs the same aggregate bound as intercepted traffic. This counter
382
368
  // covers bodies retained by concurrent fetch responses while each response still streams to the
383
369
  // caller; it is released only after that response has been materialized into evidence.
@@ -438,7 +424,7 @@ export async function createProxy(options) {
438
424
  status: exchange.status,
439
425
  streamed,
440
426
  headers: scalarHeaders(exchange.requestHeaders),
441
- seq: captured.length,
427
+ seq: exchangeSequence,
442
428
  requestId: exchange.requestId,
443
429
  requestStartedAt: exchange.requestStartedAt,
444
430
  durationMs: exchange.durationMs,
@@ -452,8 +438,7 @@ export async function createProxy(options) {
452
438
  // worth having in the trace. The port is kept only where https does not imply it.
453
439
  upstream: `https://${exchange.host}${exchange.port === 443 ? '' : `:${exchange.port}`}`,
454
440
  });
455
- captured.push(built);
456
- options.onExchange?.(built);
441
+ deliverExchange(built);
457
442
  // Keep the authoritative wire record even when the dialect projection succeeds. The
458
443
  // model event is a convenient replay view; the net event retains headers, framing and
459
444
  // transport completion details needed to audit capture fidelity.
@@ -691,7 +676,7 @@ export async function createProxy(options) {
691
676
  status,
692
677
  streamed: isStreamed,
693
678
  headers: scalarHeaders(recordableHeaders),
694
- seq: captured.length,
679
+ seq: exchangeSequence,
695
680
  requestId,
696
681
  requestStartedAt: startedAt,
697
682
  durationMs: Date.now() - startedAt,
@@ -699,8 +684,7 @@ export async function createProxy(options) {
699
684
  // client announced, and a fork's substitution can send it somewhere else again.
700
685
  upstream: origin,
701
686
  });
702
- captured.push(exchange);
703
- options.onExchange?.(exchange);
687
+ deliverExchange(exchange);
704
688
  }
705
689
  }
706
690
  /**
@@ -779,12 +763,12 @@ export async function createProxy(options) {
779
763
  const scrubbed = String(err).split(origin).join(scrubOrigin(origin));
780
764
  return redactor.redactString(withoutCredentials(scrubbed)).value;
781
765
  }
782
- async function passThrough(path, rawBody, headers, recordableHeaders, res, startedAt, requestId, forwardBase, requestEncoding) {
766
+ async function passThrough(path, rawBody, headers, recordableHeaders, res, startedAt, requestId, forwardBase, requestEncoding, uncaptured) {
783
767
  stats.passedThrough += 1;
784
768
  const origin = passthroughOrigin(headers, forwardBase);
785
769
  let upstreamRes;
786
770
  try {
787
- upstreamRes = await doFetch(`${origin}${path}`, {
771
+ upstreamRes = await doFetch(`${origin}${uncaptured?.upstreamPath ?? path}`, {
788
772
  method: 'POST',
789
773
  // `upstreamHeaders` too, as `goLive` does. Omitting them sent a gateway the agent's own
790
774
  // credential — often the `agenticreplay-recorded` placeholder — and the call came back 401 for a
@@ -794,7 +778,8 @@ export async function createProxy(options) {
794
778
  ...headers,
795
779
  ...headersForOrigin(origin),
796
780
  },
797
- body: rawBody,
781
+ body: uncaptured?.body ?? rawBody,
782
+ ...(uncaptured ? { duplex: 'half' } : {}),
798
783
  });
799
784
  }
800
785
  catch (err) {
@@ -821,8 +806,8 @@ export async function createProxy(options) {
821
806
  path,
822
807
  requestHeaders: recordableHeaders,
823
808
  requestBody: rawBody,
824
- requestBytes: Buffer.byteLength(rawBody),
825
- requestTruncated: false,
809
+ requestBytes: uncaptured?.bytes() ?? Buffer.byteLength(rawBody),
810
+ requestTruncated: uncaptured !== undefined,
826
811
  status: 0,
827
812
  responseHeaders: {},
828
813
  responseBody: `agenticreplay did not forward this call: ${reasonFor(err, origin)}`,
@@ -856,8 +841,8 @@ export async function createProxy(options) {
856
841
  intercepted: false,
857
842
  requestHeaders: recordableHeaders,
858
843
  requestBody: rawBody,
859
- requestBytes: Buffer.byteLength(rawBody),
860
- requestTruncated: false,
844
+ requestBytes: uncaptured?.bytes() ?? Buffer.byteLength(rawBody),
845
+ requestTruncated: uncaptured !== undefined,
861
846
  status: upstreamRes.status,
862
847
  responseHeaders,
863
848
  responseBody: partial?.text ?? '',
@@ -898,8 +883,8 @@ export async function createProxy(options) {
898
883
  // must not now be written down. §7 says never write it, which is not the same as never relay it.
899
884
  requestHeaders: recordableHeaders,
900
885
  requestBody: rawBody,
901
- requestBytes: Buffer.byteLength(rawBody),
902
- requestTruncated: false,
886
+ requestBytes: uncaptured?.bytes() ?? Buffer.byteLength(rawBody),
887
+ requestTruncated: uncaptured !== undefined,
903
888
  status: upstreamRes.status,
904
889
  responseHeaders,
905
890
  responseBody: body,
@@ -914,7 +899,17 @@ export async function createProxy(options) {
914
899
  durationMs: Date.now() - startedAt,
915
900
  transformations: [
916
901
  ...captureHeaderTransformations(headers, recordableHeaders),
917
- ...captureEncodingTransformation('request.body', requestEncoding),
902
+ ...(uncaptured
903
+ ? [
904
+ {
905
+ kind: 'omission',
906
+ location: 'request.body',
907
+ rule: 'capture_buffer_limit',
908
+ count: 1,
909
+ reason: 'request forwarded without retaining its body',
910
+ },
911
+ ]
912
+ : captureEncodingTransformation('request.body', requestEncoding)),
918
913
  ...captureResponseHeaderTransformations(upstreamRes.headers),
919
914
  ...captureEncodingTransformation('response.body', responseEncoding),
920
915
  ...captureUrlTransformations(origin),
@@ -1275,99 +1270,100 @@ export async function createProxy(options) {
1275
1270
  json(res, 501, { error: { message } });
1276
1271
  return;
1277
1272
  }
1278
- let rawBody;
1279
- let requestBytes;
1280
- let requestReservedBytes = 0;
1281
- try {
1282
- const read = await readBody(req, options.maxCapturedBytes ?? DEFAULT_MAX_CAPTURED_BYTES);
1283
- rawBody = decodeBody(read.body, Array.isArray(req.headers['content-encoding'])
1284
- ? req.headers['content-encoding'].join(', ')
1285
- : req.headers['content-encoding'], options.maxCapturedBytes ?? DEFAULT_MAX_CAPTURED_BYTES);
1286
- requestBytes = read.bytes;
1287
- requestReservedBytes = Buffer.byteLength(rawBody);
1288
- if (plainBufferedBytes + requestReservedBytes > plainBufferLimit) {
1289
- throw new BodyLimitError(requestReservedBytes, plainBufferLimit);
1290
- }
1291
- plainBufferedBytes += requestReservedBytes;
1273
+ const headers = {};
1274
+ const recordableHeaders = {};
1275
+ for (const [k, v] of Object.entries(req.headers)) {
1276
+ const key = k.toLowerCase();
1277
+ if (HOP_BY_HOP_HEADERS.has(key))
1278
+ continue;
1279
+ if (v === undefined)
1280
+ continue;
1281
+ headers[k] = Array.isArray(v) ? v.join(', ') : v;
1292
1282
  }
1293
- catch (err) {
1294
- if (!(err instanceof BodyLimitError)) {
1295
- options.onNetExchange?.({
1296
- requestId,
1297
- requestStartedAt: startedAt,
1298
- host: req.headers.host ?? 'local',
1299
- port: 0,
1300
- intercepted: false,
1301
- method: req.method ?? 'POST',
1302
- path: requestedPath,
1303
- requestHeaders: {},
1304
- requestBody: '',
1305
- requestBytes: 0,
1306
- requestTruncated: true,
1307
- status: 0,
1308
- responseHeaders: {},
1309
- responseBody: '',
1310
- responseTruncated: false,
1311
- responseComplete: false,
1312
- responseError: String(err),
1313
- responseBytes: 0,
1314
- durationMs: Date.now() - startedAt,
1315
- });
1316
- json(res, 400, {
1317
- error: { message: `agenticreplay could not decode request body: ${String(err)}` },
1318
- });
1319
- return;
1320
- }
1321
- options.onNetExchange?.({
1322
- requestId,
1323
- requestStartedAt: startedAt,
1324
- host: req.headers.host ?? 'local',
1325
- port: 0,
1326
- intercepted: false,
1327
- method: req.method ?? 'POST',
1328
- path: requestedPath,
1329
- requestHeaders: {},
1330
- requestBody: '',
1331
- requestBytes: err.bytes,
1332
- requestTruncated: true,
1333
- status: 0,
1334
- responseHeaders: {},
1335
- responseBody: '',
1336
- responseTruncated: false,
1337
- responseComplete: false,
1338
- responseError: String(err),
1339
- responseBytes: 0,
1340
- durationMs: Date.now() - startedAt,
1341
- });
1342
- json(res, 413, { error: { message: String(err) } });
1343
- return;
1283
+ // Node's parsed headers combine repeated fields. Keep the raw field order and multiplicity
1284
+ // for persisted evidence while retaining the scalar map above for fetch forwarding.
1285
+ for (let index = 0; index + 1 < req.rawHeaders.length; index += 2) {
1286
+ const key = req.rawHeaders[index].toLowerCase();
1287
+ if (HOP_BY_HOP_HEADERS.has(key) || SECRET_REQUEST_HEADERS.has(key))
1288
+ continue;
1289
+ const value = req.rawHeaders[index + 1];
1290
+ const prior = recordableHeaders[key];
1291
+ recordableHeaders[key] =
1292
+ prior === undefined ? value : Array.isArray(prior) ? [...prior, value] : [prior, value];
1344
1293
  }
1345
1294
  const inboundRequestHeader = req.headers['content-encoding'];
1346
1295
  const inboundRequestEncoding = Array.isArray(inboundRequestHeader)
1347
1296
  ? inboundRequestHeader.join(', ')
1348
1297
  : inboundRequestHeader;
1298
+ let read;
1299
+ let decodedReservation = 0;
1300
+ const reserve = (bytes) => {
1301
+ if (plainBufferedBytes + bytes > plainBufferLimit)
1302
+ return false;
1303
+ plainBufferedBytes += bytes;
1304
+ return true;
1305
+ };
1306
+ const release = (bytes) => {
1307
+ plainBufferedBytes = Math.max(0, plainBufferedBytes - bytes);
1308
+ };
1349
1309
  try {
1350
- const headers = {};
1351
- const recordableHeaders = {};
1352
- for (const [k, v] of Object.entries(req.headers)) {
1353
- const key = k.toLowerCase();
1354
- if (HOP_BY_HOP_HEADERS.has(key))
1355
- continue;
1356
- if (v === undefined)
1357
- continue;
1358
- headers[k] = Array.isArray(v) ? v.join(', ') : v;
1310
+ let rawBody;
1311
+ try {
1312
+ read = await readRequestBody(req, options.maxCapturedBytes ?? DEFAULT_MAX_CAPTURED_BYTES, reserve, release);
1313
+ if (read.buffer) {
1314
+ rawBody = decodeBody(read.buffer, inboundRequestEncoding, options.maxCapturedBytes ?? DEFAULT_MAX_CAPTURED_BYTES);
1315
+ const extra = Math.max(0, Buffer.byteLength(rawBody) - read.buffer.length);
1316
+ if (reserve(extra))
1317
+ decodedReservation = extra;
1318
+ else
1319
+ rawBody = undefined;
1320
+ }
1359
1321
  }
1360
- // Node's parsed headers combine repeated fields. Keep the raw field order and multiplicity
1361
- // for persisted evidence while retaining the scalar map above for fetch forwarding.
1362
- for (let index = 0; index + 1 < req.rawHeaders.length; index += 2) {
1363
- const key = req.rawHeaders[index].toLowerCase();
1364
- if (HOP_BY_HOP_HEADERS.has(key) || SECRET_REQUEST_HEADERS.has(key))
1365
- continue;
1366
- const value = req.rawHeaders[index + 1];
1367
- const prior = recordableHeaders[key];
1368
- recordableHeaders[key] =
1369
- prior === undefined ? value : Array.isArray(prior) ? [...prior, value] : [prior, value];
1322
+ catch (error) {
1323
+ // A compressed conversation can be small on the wire but too large to capture decoded.
1324
+ // Relay its original encoding instead of returning the recorder's size error to the agent.
1325
+ if (!read?.buffer || error.code !== 'ERR_BUFFER_TOO_LARGE') {
1326
+ options.onNetExchange?.({
1327
+ ...startRequest,
1328
+ requestBytes: read?.bytes() ?? 0,
1329
+ requestBody: '',
1330
+ requestTruncated: true,
1331
+ status: 0,
1332
+ responseHeaders: {},
1333
+ responseBody: '',
1334
+ responseBytes: 0,
1335
+ responseTruncated: false,
1336
+ responseComplete: false,
1337
+ responseError: String(error),
1338
+ intercepted: false,
1339
+ durationMs: Date.now() - startedAt,
1340
+ });
1341
+ json(res, 400, {
1342
+ error: { message: `agenticreplay could not decode request body: ${String(error)}` },
1343
+ });
1344
+ return;
1345
+ }
1346
+ }
1347
+ if (rawBody === undefined) {
1348
+ const queryAt = requestedPath.indexOf('?');
1349
+ const query = queryAt === -1 ? '' : requestedPath.slice(queryAt);
1350
+ await passThrough(requestedPath, '',
1351
+ // This path forwards the original wire bytes, including compression. Preserve its
1352
+ // known length; providers that require Content-Length need not accept chunked uploads.
1353
+ {
1354
+ ...headers,
1355
+ ...(req.headers['content-length'] === undefined
1356
+ ? {}
1357
+ : { 'content-length': req.headers['content-length'] }),
1358
+ }, recordableHeaders, res, startedAt, requestId, startOrigin, undefined, {
1359
+ body: read.stream ??
1360
+ Readable.toWeb(Readable.from([read.buffer])),
1361
+ bytes: read.bytes,
1362
+ upstreamPath: dialect && !forward ? `${dialect.requestPath}${query}` : requestedPath,
1363
+ });
1364
+ return;
1370
1365
  }
1366
+ const requestBytes = read.bytes();
1371
1367
  // `decodeBody` above gives the origin the serialized JSON rather than the compressed wire
1372
1368
  // bytes. Remove the stale framing headers or the origin will try to decompress plaintext.
1373
1369
  if (headers['content-encoding'] !== undefined) {
@@ -1419,7 +1415,8 @@ export async function createProxy(options) {
1419
1415
  await goLive(dialect, requestedPath, rawBody, headers, recordableHeaders, res, startedAt, requestId, inboundRequestEncoding, forward?.base);
1420
1416
  }
1421
1417
  finally {
1422
- plainBufferedBytes = Math.max(0, plainBufferedBytes - requestReservedBytes);
1418
+ read?.dispose();
1419
+ release(decodedReservation);
1423
1420
  }
1424
1421
  }
1425
1422
  const host = options.host ?? '127.0.0.1';