fix(webfetch): support Bun response cleanup

Merged
HE
2mo ago
+129-7

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() when dump() 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() received TypeError: 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 -> 200 through source fetchUrl() — exit 0, status 200, body ok, 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 mark dump as optional in ResponseBodyStream.
  • Changes are localized to packages/coding-agent/src/core/extensions/builtin/webfetch/webfetch/fetcher.ts; adds deterministic tests at packages/coding-agent/test/suite/regressions/1089-webfetch-bun-body-discard.test.ts; updates packages/coding-agent/CHANGELOG.md and packages/coding-agent/src/core/extensions/builtin/webfetch/changes.md.
  • Risk: low. Only affects redirect cleanup before body consumption. Re-vendoring must keep runtime dump detection and the argument-free destroy() fallback.