fix(webfetch): support Bun response cleanup
Summary
- feature-detect optional
response.body.dump()in the webfetch redirect cleanup path - preserve bounded Undici dumping when available
- fall back to argument-free
destroy()whendump()is absent or rejects, avoiding uncaught stream-error re-emission - add deterministic coverage for Bun-compatible, standard Undici, and failed-dump cleanup paths
Closes #1089
Root cause
Senpi 2026.8.22-2 runs under Bun after a global Bun install. Bun 1.4.0's bare undici compatibility module returns a redirect BodyReadable with async iteration and destroy(), but no dump(). The previous cleanup called dump() unconditionally, then passed the resulting TypeError to destroy(error), allowing it to surface as an uncaught stream error after the redirected fetch had otherwise succeeded.
Verification
- RED before fix: issue regression failed because
destroy()receivedTypeError: body.dump is not a function - GREEN:
npm --prefix packages/coding-agent test -- test/suite/regressions/1089-webfetch-bun-body-discard.test.ts— 3/3 npm run check— PASS- LSP diagnostics on changed TypeScript files — 0
git diff --check— PASS- real Bun 1.4.0 + local HTTP
302 -> 200through sourcefetchUrl()— exit 0, status 200, bodyok, no uncaught exception - Senpi QA common isolation self-check — 9/9
- Senpi RPC self-test — 4/4
- Senpi CLI smoke self-test — 8/8
QA note
mock-loop.mjs --self-test was attempted on the current upstream main but its synthetic provider setup failed before reaching any tool (requests=0, Unknown provider "mock" / missing synthetic key). This change touches only webfetch response disposal; the focused regression, real Bun redirect run, RPC self-test, and CLI smoke all pass.
Tracker / changelog
- added the required four-heading entry to the nearest webfetch
changes.md - added an Unreleased / Fixed entry referencing #1089
No dependencies or lockfiles changed.
Summary by cubic
Fixes redirect response body cleanup in webfetch to support Bun’s bare undici. Old behavior unconditionally called body.dump() then destroy(error), which could surface as an uncaught stream error when dump was missing; new behavior feature-detects dump, uses it when present, and otherwise calls destroy() without an error (also falls back to destroy() when dump rejects).
- Preserves Undici’s bounded
dump({ limit: 1024 })when available; no change to fetch results or external API. Types markdumpas optional inResponseBodyStream. - Changes are localized to
packages/coding-agent/src/core/extensions/builtin/webfetch/webfetch/fetcher.ts; adds deterministic tests atpackages/coding-agent/test/suite/regressions/1089-webfetch-bun-body-discard.test.ts; updatespackages/coding-agent/CHANGELOG.mdandpackages/coding-agent/src/core/extensions/builtin/webfetch/changes.md. - Risk: low. Only affects redirect cleanup before body consumption. Re-vendoring must keep runtime
dumpdetection and the argument-freedestroy()fallback.