ladder-mcp 1.0.2 → 1.1.2

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/CHANGELOG.md CHANGED
@@ -1,115 +1,242 @@
1
- # Changelog
2
-
3
- All notable changes to Ladder_mcp are documented here. Format loosely follows
4
- [Keep a Changelog](https://keepachangelog.com/); this project uses
5
- [Semantic Versioning](https://semver.org/).
6
-
7
- ## [1.0.2] - 2026-06-28
8
-
9
- Security and robustness patch release addressing findings from an external
10
- adversarial review of `src/`.
11
-
12
- ### Fixed
13
-
14
- - **`isAuthenticated` false-positive on corrupt credentials** (`environment.ts`):
15
- a credentials file that exists but is unreadable or invalid JSON was previously
16
- reported as authenticated. It now fails closed and returns `false`, satisfying
17
- NFR-5 honest diagnostics.
18
- - **Unbounded `stdout`/`stderr` capture in `runKimi`** (`kimi-runner.ts`): CLI
19
- output is now accumulated through `appendCapped` with a hard ceiling
20
- (`MAX_CAPTURE_CHARS`), preventing memory exhaustion on runaway output.
21
- - **Unvalidated `max_output_tokens` and `work_dir`** (`input-validation.ts`):
22
- non-finite, zero, or negative token counts are clamped to the default budget;
23
- `work_dir` must be an absolute path to an existing directory before Kimi is
24
- spawned. Used by `kimi_analyze` and `kimi_resume`.
25
-
26
- ### Security
27
-
28
- - **Arbitrary command persisted to `mcp.json`** (`kimi-mcp-config.ts`):
29
- `kimi_generate_mcp_config` now validates `command` against an allow-list of
30
- `node`, `npx`, or an absolute path to an existing file. Other values (e.g.
31
- `powershell`) are rejected and nothing is written.
32
-
33
- ## [1.0.1] - 2026-06-28
34
-
35
- Bug-fix release. Issues found by exercising all 20 tools live against a real
36
- Kimi CLI (the mock-only unit tests had missed them).
37
-
38
- ### Fixed
39
-
40
- - **ACP responses lost spaces** (`kimi_chat`): streamed token chunks were each
41
- trimmed and newline-joined, so `"Two plus two equals four."` came back as
42
- `"Twoplustwoequalsfour."`. Text fragments are now concatenated as-is and only
43
- the final string is trimmed. (CLI tools `kimi_analyze`/`kimi_resume` were never
44
- affected.)
45
- - **`kimi_export_session` silently no-op'd** while reporting `ok: true`: when no
46
- session id was given, `kimi export` defaults to the most recent session and asks
47
- `Export previous session …? [Y/n]` — a prompt that `-y` does not suppress in Kimi
48
- CLI 0.20.1, so with stdin closed it exited 0 without writing. The tool now
49
- resolves the most recent session id itself and passes it explicitly (skipping the
50
- prompt), always passes `-y`, and verifies the output file exists before reporting
51
- success.
52
-
53
- ### Changed
54
-
55
- - `kimi_acp_sessions` now accepts `limit` (default 20) and `work_dir` filters,
56
- for parity with `kimi_list_sessions`; previously it dumped every ACP session
57
- across all projects in one response.
58
-
59
- ## [1.0.0] - 2026-06-27
60
-
61
- First release. Windows-first MCP bridge for Kimi Code CLI v24, rebuilt in a
62
- fresh `./src` application (package `ladder-mcp`). Logic is ported from the
63
- read-only `kimi-code-mcp/` reference; the legacy `CacheManager`/warmup layer is
64
- not carried over.
65
-
66
- ### Added v1 core (Epics 1-3)
67
-
68
- - Robust environment resolver (`environment.ts`): discovers `kimi.exe` via PATH
69
- and `~/.kimi-code/bin/`, resolves the `~/.kimi-code/` catalog, credentials and
70
- version with no hardcoded POSIX paths; no silent fallback to legacy `~/.kimi/`.
71
- - CLI adapter (`kimi-runner.ts`): correct Kimi CLI v24 arguments
72
- (`-p`, `--output-format stream-json`, `-S`, `-C`, `--auto`), `stream-json`
73
- parsing, native session resume, Windows process-tree termination on timeout.
74
- - API adapter (`kimi-api.ts`): contextless `kimi_query` / `kimi_verify` reading
75
- key/endpoint from `KIMICODE_API_KEY` or `~/.kimi-code/config.toml`.
76
- - Six v1 MCP tools: `kimi_analyze`, `kimi_query`, `kimi_verify`, `kimi_resume`,
77
- `kimi_list_sessions`, `kimi_status`.
78
-
79
- ### Added vNext expansion (Epic 4)
80
-
81
- - **CLI admin & capabilities** (`transports/cli-admin.ts`): `kimi_capabilities`,
82
- `kimi_doctor`, `kimi_provider_list`, `kimi_export_session`,
83
- `kimi_visualize_session`. Export requires an explicit `output_path`, is
84
- confined to the working directory, and excludes the global diagnostic log by
85
- default.
86
- - **ACP MVP over stdio** (`transports/acp.ts`): JSON-RPC client for `kimi acp`
87
- with newline-delimited and `Content-Length` framing; `kimi_chat`,
88
- `kimi_acp_sessions`, `kimi_cancel`.
89
- - **Background task lifecycle** (`task-store.ts`): in-process task store with
90
- `kimi_chat background=true`, `kimi_task_status`, `kimi_task_output`,
91
- `kimi_task_cancel`.
92
- - **Kimi-hosted MCP config + read-only desktop probes**
93
- (`kimi-mcp-config.ts`, `desktop-work.ts`): `kimi_generate_mcp_config`,
94
- `kimi_desktop_status`, `kimi_budget_probe`. Desktop access is
95
- experimental/read-only no token-store reads, no web-auth replay, no desktop
96
- Work task submission.
97
-
98
- ### Security & robustness (adversarial review)
99
-
100
- Hardened against path traversal/TOCTOU on export, writes under the read-only
101
- reference tree, unbounded ACP frame/task memory, process crash on malformed ACP
102
- JSON, non-idempotent task cancellation, and UTF-8 byte-boundary truncation.
103
-
104
- The remaining lower-risk review findings (W1-W12) were also resolved before
105
- release: raw ACP response-id matching (no `NaN` coercion), bounded ACP update
106
- buffer, clamped ACP request timeouts, smaller buffers for parallel capability
107
- probes, a timeout around task cancel hooks, a size cap on the desktop status
108
- probe body, validated `kimi_chat` timeout input, and `kimi_cancel` rejecting
109
- ambiguous task_id+session_id calls. Regression tests added (49 total).
110
-
111
- ### Known limitations
112
-
113
- - Windows 11 only (NFR-1); POSIX branches are not a target.
114
-
115
- [1.0.0]: https://semver.org/
1
+ # Changelog
2
+
3
+ All notable changes to Ladder_mcp are documented here. Format loosely follows
4
+ [Keep a Changelog](https://keepachangelog.com/); this project uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [1.1.2] - 2026-06-28
8
+
9
+ Live-progress and cancellation reliability for `kimi_code`.
10
+
11
+ ### Added
12
+
13
+ - The CLI transport now surfaces the running action: `stream-json` `tool_calls`
14
+ records emit immediate `tool_call` progress events (e.g. `Read src/types.ts`)
15
+ and `plan` records emit `plan` events, matching the ACP transport. A CLI job
16
+ no longer looks idle while it works.
17
+
18
+ ### Fixed
19
+
20
+ - Cancelling a `kimi_code` call now actually kills the underlying `kimi.exe`
21
+ process in every mode (foreground CLI, background CLI, foreground ACP).
22
+ Previously only background ACP honored cancellation; the others leaked the
23
+ child until the timeout fired. `runKimi` accepts an `AbortSignal`, skips
24
+ spawning when already aborted, and kills the process tree on abort.
25
+ - The ACP client is hardened against a `close()`-before-`start()` race, and an
26
+ aborted ACP run now reports `Kimi cancelled` instead of a raw exit code.
27
+
28
+ ### Changed
29
+
30
+ - `kimi_code`'s `transport` and `background` parameter descriptions now explain
31
+ the cli/acp trade-off and where to read the full progress log, so agents pick
32
+ a mode deliberately.
33
+
34
+ ## [1.1.0] - 2026-06-28
35
+
36
+ Breaking tool-surface redesign: the MCP tool list is now an intent-first set of
37
+ 6 default tools, with niche/admin features gated behind `LADDER_EXPERIMENTAL=1`.
38
+
39
+ ### Changed
40
+
41
+ - `src/index.ts` now registers 6 default tools: `kimi_code`, `kimi_ask`,
42
+ `kimi_sessions`, `kimi_tasks`, `kimi_status`, `kimi_setup`.
43
+ - `kimi_code` folds `kimi_analyze`, `kimi_resume`, and `kimi_chat`. It defaults to
44
+ `transport='cli'` (validated `work_dir` via `validateWorkDir`) and supports
45
+ `edit`, `background`, `session_id`, and `new_session`.
46
+ - `kimi_ask` folds `kimi_query` and `kimi_verify`; providing `context` switches to
47
+ verify mode.
48
+ - `kimi_sessions` folds `kimi_list_sessions` and `kimi_acp_sessions` behind the
49
+ `source` parameter (`cli`, `acp`, `all`).
50
+ - `kimi_tasks` action-dispatches `status`, `output`, and `cancel` (including ACP
51
+ session cancellation via `session_id`).
52
+ - `kimi_status` detail-switches between `basic` and `full`; `full` adds
53
+ capabilities, doctor, and provider list. `isError` is now true whenever a
54
+ remediation is required.
55
+ - `kimi_setup` renames `kimi_generate_mcp_config` and resolves the default server
56
+ command from `process.execPath`.
57
+
58
+ ### Removed
59
+
60
+ The 20-tool default surface is replaced by the 6 tools above. The following tool
61
+ names are no longer registered by default:
62
+
63
+ - `kimi_analyze`, `kimi_resume`, `kimi_chat`
64
+ - `kimi_query`, `kimi_verify`
65
+ - `kimi_list_sessions`, `kimi_acp_sessions`
66
+ - `kimi_task_status`, `kimi_task_output`, `kimi_task_cancel`, `kimi_cancel`
67
+ - `kimi_capabilities`, `kimi_doctor`, `kimi_provider_list`
68
+ - `kimi_generate_mcp_config`
69
+
70
+ ### Migration
71
+
72
+ | Old tool | New |
73
+ |---|---|
74
+ | `kimi_analyze` | `kimi_code` (edit=false) |
75
+ | `kimi_resume` | `kimi_code` (session_id set) |
76
+ | `kimi_chat` | `kimi_code` (transport='acp') |
77
+ | `kimi_query` | `kimi_ask` |
78
+ | `kimi_verify` | `kimi_ask` (context/role set) |
79
+ | `kimi_list_sessions` | `kimi_sessions` (source='cli') |
80
+ | `kimi_acp_sessions` | `kimi_sessions` (source='acp') |
81
+ | `kimi_task_status` | `kimi_tasks` (action='status') |
82
+ | `kimi_task_output` | `kimi_tasks` (action='output') |
83
+ | `kimi_task_cancel` | `kimi_tasks` (action='cancel') |
84
+ | `kimi_cancel` | `kimi_tasks` (action='cancel', task_id or session_id) |
85
+ | `kimi_status` | `kimi_status` |
86
+ | `kimi_capabilities` | `kimi_status` (detail='full') |
87
+ | `kimi_doctor` | `kimi_status` (detail='full') |
88
+ | `kimi_provider_list` | `kimi_status` (detail='full') |
89
+ | `kimi_generate_mcp_config` | `kimi_setup` |
90
+ | `kimi_export_session` | experimental (unchanged name) |
91
+ | `kimi_visualize_session` | experimental |
92
+ | `kimi_desktop_status` | experimental |
93
+ | `kimi_budget_probe` | experimental |
94
+
95
+ The 4 experimental tools (`kimi_export_session`, `kimi_visualize_session`,
96
+ `kimi_desktop_status`, `kimi_budget_probe`) now register only when
97
+ `process.env.LADDER_EXPERIMENTAL === '1'`.
98
+
99
+ ### Added
100
+
101
+ - **Bidirectional ACP transport** (`transports/acp.ts`): the bridge now answers the
102
+ agent's client-bound JSON-RPC requests, so file edits and permission flows over
103
+ ACP complete instead of hanging. Includes auto-approved `session/request_permission`
104
+ and a `fs/read_text_file`/`fs/write_text_file`/`fs/read_directory` proxy.
105
+ - **Live-progress visibility**: long-running `kimi_code` work surfaces progress
106
+ background tasks grow a timestamped log, foreground calls emit MCP
107
+ `notifications/progress`, and a stall watchdog flags silent hangs. Token streams
108
+ are coalesced so the log is readable.
109
+ - **Single-source versioning** (`version.ts`) read from `package.json`, plus
110
+ `release:patch/minor/major` scripts and a `preversion` typecheck+test gate.
111
+
112
+ ### Security
113
+
114
+ - **ACP fs proxy is sandboxed** to the session `work_dir` with realpath-based
115
+ containment (blocks symlink/junction escape, case-folding bypass, drive-relative
116
+ and UNC paths); fails closed when the work dir is unset. Permission auto-approve
117
+ fails closed when no single-use option is offered. File reads are size-capped.
118
+ - **`assertSafeCommand`** (`kimi-mcp-config.ts`) rejects PATH-resolved bare
119
+ `node`/`npx`, accepting only `process.execPath` or an absolute existing file;
120
+ `assertWritableProjectTarget` uses realpath containment instead of substring match.
121
+
122
+ ### Fixed
123
+
124
+ - Process-tree termination on Windows (`taskkill /T /F`) for ACP close/cancel and
125
+ `runKimi` timeout, with awaited exit so callers don't race a dying tree.
126
+ - Timeouts in `runKimi` and `runKimiApi` are clamped; `timeout_ms` tool args must be
127
+ positive integers.
128
+ - `parseKimiStreamJson` tolerates leading whitespace; resume-hint extraction is
129
+ fuzzier; `contentToText` keeps placeholders for non-text parts.
130
+ - `task-store` no longer appends to terminal tasks; `runKimi` preserves both stdout
131
+ and stderr on failure; disk-discovered sessions recover their real `work_dir`.
132
+ - Capped the `readBodyCapped` non-streaming fallback (`desktop-work.ts`).
133
+
134
+ ## [1.0.2] - 2026-06-28
135
+
136
+ Security and robustness patch release addressing findings from an external
137
+ adversarial review of `src/`.
138
+
139
+ ### Fixed
140
+
141
+ - **`isAuthenticated` false-positive on corrupt credentials** (`environment.ts`):
142
+ a credentials file that exists but is unreadable or invalid JSON was previously
143
+ reported as authenticated. It now fails closed and returns `false`, satisfying
144
+ NFR-5 honest diagnostics.
145
+ - **Unbounded `stdout`/`stderr` capture in `runKimi`** (`kimi-runner.ts`): CLI
146
+ output is now accumulated through `appendCapped` with a hard ceiling
147
+ (`MAX_CAPTURE_CHARS`), preventing memory exhaustion on runaway output.
148
+ - **Unvalidated `max_output_tokens` and `work_dir`** (`input-validation.ts`):
149
+ non-finite, zero, or negative token counts are clamped to the default budget;
150
+ `work_dir` must be an absolute path to an existing directory before Kimi is
151
+ spawned. Used by `kimi_analyze` and `kimi_resume`.
152
+
153
+ ### Security
154
+
155
+ - **Arbitrary command persisted to `mcp.json`** (`kimi-mcp-config.ts`):
156
+ `kimi_generate_mcp_config` now validates `command` against an allow-list of
157
+ `node`, `npx`, or an absolute path to an existing file. Other values (e.g.
158
+ `powershell`) are rejected and nothing is written.
159
+
160
+ ## [1.0.1] - 2026-06-28
161
+
162
+ Bug-fix release. Issues found by exercising all 20 tools live against a real
163
+ Kimi CLI (the mock-only unit tests had missed them).
164
+
165
+ ### Fixed
166
+
167
+ - **ACP responses lost spaces** (`kimi_chat`): streamed token chunks were each
168
+ trimmed and newline-joined, so `"Two plus two equals four."` came back as
169
+ `"Twoplustwoequalsfour."`. Text fragments are now concatenated as-is and only
170
+ the final string is trimmed. (CLI tools `kimi_analyze`/`kimi_resume` were never
171
+ affected.)
172
+ - **`kimi_export_session` silently no-op'd** while reporting `ok: true`: when no
173
+ session id was given, `kimi export` defaults to the most recent session and asks
174
+ `Export previous session …? [Y/n]` — a prompt that `-y` does not suppress in Kimi
175
+ CLI 0.20.1, so with stdin closed it exited 0 without writing. The tool now
176
+ resolves the most recent session id itself and passes it explicitly (skipping the
177
+ prompt), always passes `-y`, and verifies the output file exists before reporting
178
+ success.
179
+
180
+ ### Changed
181
+
182
+ - `kimi_acp_sessions` now accepts `limit` (default 20) and `work_dir` filters,
183
+ for parity with `kimi_list_sessions`; previously it dumped every ACP session
184
+ across all projects in one response.
185
+
186
+ ## [1.0.0] - 2026-06-27
187
+
188
+ First release. Windows-first MCP bridge for Kimi Code CLI v24, rebuilt in a
189
+ fresh `./src` application (package `ladder-mcp`). Logic is ported from the
190
+ read-only `kimi-code-mcp/` reference; the legacy `CacheManager`/warmup layer is
191
+ not carried over.
192
+
193
+ ### Added — v1 core (Epics 1-3)
194
+
195
+ - Robust environment resolver (`environment.ts`): discovers `kimi.exe` via PATH
196
+ and `~/.kimi-code/bin/`, resolves the `~/.kimi-code/` catalog, credentials and
197
+ version with no hardcoded POSIX paths; no silent fallback to legacy `~/.kimi/`.
198
+ - CLI adapter (`kimi-runner.ts`): correct Kimi CLI v24 arguments
199
+ (`-p`, `--output-format stream-json`, `-S`, `-C`, `--auto`), `stream-json`
200
+ parsing, native session resume, Windows process-tree termination on timeout.
201
+ - API adapter (`kimi-api.ts`): contextless `kimi_query` / `kimi_verify` reading
202
+ key/endpoint from `KIMICODE_API_KEY` or `~/.kimi-code/config.toml`.
203
+ - Six v1 MCP tools: `kimi_analyze`, `kimi_query`, `kimi_verify`, `kimi_resume`,
204
+ `kimi_list_sessions`, `kimi_status`.
205
+
206
+ ### Added — vNext expansion (Epic 4)
207
+
208
+ - **CLI admin & capabilities** (`transports/cli-admin.ts`): `kimi_capabilities`,
209
+ `kimi_doctor`, `kimi_provider_list`, `kimi_export_session`,
210
+ `kimi_visualize_session`. Export requires an explicit `output_path`, is
211
+ confined to the working directory, and excludes the global diagnostic log by
212
+ default.
213
+ - **ACP MVP over stdio** (`transports/acp.ts`): JSON-RPC client for `kimi acp`
214
+ with newline-delimited and `Content-Length` framing; `kimi_chat`,
215
+ `kimi_acp_sessions`, `kimi_cancel`.
216
+ - **Background task lifecycle** (`task-store.ts`): in-process task store with
217
+ `kimi_chat background=true`, `kimi_task_status`, `kimi_task_output`,
218
+ `kimi_task_cancel`.
219
+ - **Kimi-hosted MCP config + read-only desktop probes**
220
+ (`kimi-mcp-config.ts`, `desktop-work.ts`): `kimi_generate_mcp_config`,
221
+ `kimi_desktop_status`, `kimi_budget_probe`. Desktop access is
222
+ experimental/read-only — no token-store reads, no web-auth replay, no desktop
223
+ Work task submission.
224
+
225
+ ### Security & robustness (adversarial review)
226
+
227
+ Hardened against path traversal/TOCTOU on export, writes under the read-only
228
+ reference tree, unbounded ACP frame/task memory, process crash on malformed ACP
229
+ JSON, non-idempotent task cancellation, and UTF-8 byte-boundary truncation.
230
+
231
+ The remaining lower-risk review findings (W1-W12) were also resolved before
232
+ release: raw ACP response-id matching (no `NaN` coercion), bounded ACP update
233
+ buffer, clamped ACP request timeouts, smaller buffers for parallel capability
234
+ probes, a timeout around task cancel hooks, a size cap on the desktop status
235
+ probe body, validated `kimi_chat` timeout input, and `kimi_cancel` rejecting
236
+ ambiguous task_id+session_id calls. Regression tests added (49 total).
237
+
238
+ ### Known limitations
239
+
240
+ - Windows 11 only (NFR-1); POSIX branches are not a target.
241
+
242
+ [1.0.0]: https://semver.org/
package/README.md CHANGED
@@ -6,7 +6,7 @@ client like Claude Code can run codebase analysis, native sessions, API
6
6
  queries, ACP chat, background tasks, and CLI admin/diagnostics — all on Windows
7
7
  without hardcoded POSIX assumptions.
8
8
 
9
- > Status: **v1.0.1** ([npm](https://www.npmjs.com/package/ladder-mcp)).
9
+ > Status: **v1.1.2** ([npm](https://www.npmjs.com/package/ladder-mcp)).
10
10
  > Supported platform is **Windows 11 only**.
11
11
 
12
12
  ## Requirements
@@ -19,7 +19,7 @@ without hardcoded POSIX assumptions.
19
19
  ## Quick start (from npm)
20
20
 
21
21
  You don't need to clone or build — the package is published on npm and your MCP
22
- client launches it via `npx`.
22
+ client launches it via `npx`, or you can install the package directly.
23
23
 
24
24
  **Claude Code (one command):**
25
25
 
@@ -50,7 +50,16 @@ Then in Claude Code run `/mcp` (should show `kimi-code: connected`) and call
50
50
  Prefer a global install? `npm install -g ladder-mcp`, then use `ladder-mcp` as the
51
51
  command instead of `npx -y ladder-mcp`.
52
52
 
53
- To let Kimi Code itself host this server, use the `kimi_generate_mcp_config`
53
+ Or install locally into your project:
54
+
55
+ ```bash
56
+ npm install ladder-mcp
57
+ ```
58
+
59
+ Then point your MCP config at `./node_modules/.bin/ladder-mcp` (or use
60
+ `npx -y ladder-mcp`, which resolves the locally installed copy when available).
61
+
62
+ To let Kimi Code itself host this server, use the `kimi_setup`
54
63
  tool to produce/merge a `.kimi-code/mcp.json` entry.
55
64
 
56
65
  ## Build from source (contributors)
@@ -63,26 +72,23 @@ npm run build # compiles src/ -> dist/ (tests excluded)
63
72
  Quick checks:
64
73
 
65
74
  ```bash
66
- npm test # vitest (50 tests)
75
+ npm test # vitest (153 tests)
67
76
  npm run typecheck # tsc --noEmit (incl. tests)
68
77
  npm run dev # run the server from source via tsx
69
78
  ```
70
79
 
71
80
  ## Tools
72
81
 
73
- **Core (v1)** — `kimi_analyze`, `kimi_query`, `kimi_verify`, `kimi_resume`,
74
- `kimi_list_sessions`, `kimi_status`
75
-
76
- **CLI admin & capabilities** — `kimi_capabilities`, `kimi_doctor`,
77
- `kimi_provider_list`, `kimi_export_session`, `kimi_visualize_session`
78
-
79
- **ACP (chat over stdio)** — `kimi_chat`, `kimi_acp_sessions`, `kimi_cancel`
82
+ **Core (v1.1)** — `kimi_code`, `kimi_ask`, `kimi_sessions`, `kimi_tasks`,
83
+ `kimi_status`, `kimi_setup`
80
84
 
81
- **Background tasks** `kimi_task_status`, `kimi_task_output`, `kimi_task_cancel`
82
- (set `background=true` on `kimi_chat`)
85
+ `kimi_code` supports both the native CLI transport (default) and the ACP
86
+ JSON-RPC transport (`transport: 'acp'`); set `background=true` to track long
87
+ work as a background task.
83
88
 
84
- **Kimi-hosted config & desktop (read-only)** — `kimi_generate_mcp_config`,
85
- `kimi_desktop_status`, `kimi_budget_probe`
89
+ **Experimental (off by default)** — `kimi_export_session`,
90
+ `kimi_visualize_session`, `kimi_desktop_status`, `kimi_budget_probe`. Enable
91
+ with the environment variable `LADDER_EXPERIMENTAL=1`.
86
92
 
87
93
  ## Safety boundaries
88
94
 
@@ -7,8 +7,16 @@ export const MAX_PROBE_BODY_BYTES = 256 * 1024;
7
7
  // The surrounding AbortController still bounds total time.
8
8
  export async function readBodyCapped(response) {
9
9
  const reader = response.body?.getReader();
10
- if (!reader)
11
- return { text: await response.text(), truncated: false };
10
+ if (!reader) {
11
+ // The body is not streamable; still cap the returned text so a non-streaming
12
+ // response cannot return an unbounded body.
13
+ const text = await response.text();
14
+ const bytes = Buffer.byteLength(text, 'utf-8');
15
+ if (bytes > MAX_PROBE_BODY_BYTES) {
16
+ return { text: Buffer.from(text, 'utf-8').subarray(0, MAX_PROBE_BODY_BYTES).toString('utf-8'), truncated: true };
17
+ }
18
+ return { text, truncated: false };
19
+ }
12
20
  const decoder = new TextDecoder();
13
21
  let text = '';
14
22
  let total = 0;