fix: preserve retained children after Bun output shutdown (#163633)

Preserve retained descendants when Bun completes supervised command output. Keep the older-Bun FFI half-close, but do not treat Darwin's benign repeated-shutdown ENOTCONN as lost lineage; the host still independently requires output EOF and process extinction.

The retained-child regression passes 3/3 on Node 24, Bun 86bd, and Bun 1e6f on macOS arm64 and Linux x64. Full applicable process/CUA and Mac-gate coverage, explicit desktop controls, P2 review, changed-file checks, and exact-head CI pass. Related: #163456 and openclaw/bun#76.
This commit is contained in:
Peter Steinberger 2026-10-02 10:40:33 -07:00 • committed by GitHub
parent b26dde75dc
commit cf43499dc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 4 additions and 5 deletions

View file

@ -228,7 +228,7 @@ config or state. Gateway startup logs include the decision and its reason.
## Known limitations
- **Supervised command output:** Large piped responses, including CUA screenshots, preserve backpressure under Bun. Completed output reaches EOF while process cleanup retains authority, including on builds that retain duplicate standard-output descriptors.
- **Supervised command output:** Large piped responses, including CUA screenshots, preserve backpressure under Bun. Completed output reaches EOF while process cleanup retains authority, including on builds that retain duplicate standard-output descriptors. A runtime that already closed its output socket does not trigger descendant cleanup.
- **Text boundaries:** OpenClaw works around a [JSC segment lookup bug](https://github.com/oven-sh/WebKit/pull/753) that can include the preceding cluster when a lookup starts on an emoji's high surrogate. Message chunking and terminal cells preserve the intended grapheme boundaries on Bun without runtime configuration changes.
- **Desktop WebSockets:** OpenClaw uses the installed `ws` transport for desktop observers and paired-node desktop/portal streams. Bun 1.4.2's built-in `ws` server adapter lacks pause/resume and the Duplex stream bridge; the installed transport preserves backpressure, payload limits, and cleanup when a desktop disconnects.
- **Lifecycle scripts:** Bun blocks dependency lifecycle scripts unless explicitly trusted with `bun pm trust`.

View file

@ -589,10 +589,9 @@ export function runServiceChildGroupAnchor(): void {
};
for (const stream of ["stdout", "stderr"] as const) {
pipeline(command[stream]!, process[stream], () => {
// Mirror Node's uv_shutdown until Bun stdio end() shuts down inherited socketpairs.
if (shutdownOutput?.(stream === "stdout" ? 1 : 2, 1) === -1) {
void requestCleanup("lineage-lost");
}
// Older Bun needs this half-close; newer Bun can report ENOTCONN after its own.
// Shutdown completion is not lineage loss; the host still requires output EOF.
shutdownOutput?.(stream === "stdout" ? 1 : 2, 1);
pendingOutput.delete(stream);
void settleRoot();
});