refactor(process): adopt Execa execution layer#105939
Conversation
Dependency GuardThis PR changes dependency-related files. Maintainers should confirm these changes are intentional. Changed files:
Maintainer follow-up:
|
Dependency graph changes notedThis PR includes dependency graph changes. The dependency guard is informational because the PR author is a repository admin or a member of
Security review is still recommended before merge when the dependency graph change is intentional. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 44abfe608f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| signal?.addEventListener("abort", onAbort, { once: true }); | ||
| armNoOutputTimer(); | ||
|
|
||
| child.stdout?.on("data", (chunk) => { |
There was a problem hiding this comment.
Restore stdout/stderr error handlers
When runCommandWithTimeout is used with a child whose stdout or stderr stream emits an error (for example an EIO/EPIPE from a broken pipe or inherited fd), these streams now only have data listeners, so Node treats the unhandled error event as fatal and can crash the process. The pre-refactor implementation installed no-op error handlers before reading output; keep that guard here as well so process execution failures are reported through the command result instead of an uncaught stream error.
Useful? React with 👍 / 👎.
44abfe6 to
005c918
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 005c918fc4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| child.stdout?.on("data", onData); | ||
| child.stderr?.on("data", onData); |
There was a problem hiding this comment.
Register the drain watcher after real output readers
When this helper is installed before the caller's capture/readline listeners, these data listeners put stdout/stderr into flowing mode and can consume any chunk emitted in that gap; onData ignores pre-exit chunks, so fast commands can lose the beginning (or all) of their output. The new callers do exactly that (for example src/process/exec.ts:514 before the capture listeners at src/process/exec.ts:613, and similarly bash/find/grep), whereas the old wait helper was attached after the real readers.
Useful? React with 👍 / 👎.
|
Codex review: needs real behavior proof before merge. Reviewed July 13, 2026, 1:44 AM ET / 05:44 UTC. Summary PR surface: Source -57, Tests -603, Config +1, Other +313. Total -346 across 23 files. Reproducibility: yes. at source level: a focused test can return a launched child whose stdout or stderr emits Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Restore the old stream-error safety contract in the shared child-process lifecycle layer, retain caller-specific error reporting where needed, and add a focused regression test that emits stdout and stderr errors after launch before merging the Execa migration. Do we have a high-confidence way to reproduce the issue? Yes at source level: a focused test can return a launched child whose stdout or stderr emits Is this the best way to solve the issue? No, not at the current head. Adopting one canonical Execa-backed wrapper is a maintainable direction, but the shared replacement should preserve the previous stream-error contract rather than leaving each migrated caller vulnerable or requiring scattered guards. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 6a0eecdb4451. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source -57, Tests -603, Config +1, Other +313. Total -346 across 23 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (2 earlier review cycles) |
1379e05 to
1681295
Compare
1681295 to
7e18480
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0346352907
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // on inherited stdio handles held by detached descendants. | ||
| waitForChildProcess(child) | ||
| .then((code) => { | ||
| void child |
There was a problem hiding this comment.
Restore bash stream error guards
When the local bash child’s stdout or stderr emits an error event (for example EIO/EPIPE from a broken pipe or inherited fd), this Execa promise replacement no longer installs the no-op stream error handlers that the removed waitForChildProcess helper provided, while the code still only adds data listeners above. Node treats an unhandled stream error as fatal, so a bash tool execution can crash the process instead of settling through the existing command result path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4904d68815
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Public process helpers for plugins that spawn or probe local commands. | ||
|
|
||
| export * from "../process/exec.js"; | ||
| export { |
There was a problem hiding this comment.
Preserve existing process-runtime exports
When a third-party plugin imports a helper that was previously exposed by the wildcard export from openclaw/plugin-sdk/process-runtime (for example spawnCommand or SpawnCommandOptions from src/process/exec.ts), this explicit allowlist removes that export from the public SDK subpath declared in package.json, so those plugins fail at import/typecheck time after upgrading. Keep the old exports as deprecated compatibility or add a documented major-version removal path instead of silently narrowing this public SDK surface.
Useful? React with 👍 / 👎.
|
Land-ready at The first hosted dependency run exposed one related issue: Core proof remains:
Known proof gap: no native Windows live run. Windows-specific command, codepage, PATHEXT, and taskkill behavior has focused mocked-platform coverage plus the hosted Windows test. Per maintainer instruction, the remaining hosted CI wait is relaxed; related dependency validation is green. |
* refactor(process): adopt execa execution layer * fix(process): preserve launch error semantics * chore(process): ratchet exec size baseline * fix(process): preserve Windows and error contracts * chore(plugin-sdk): ratchet wildcard surface budget * test(process): allow resolved Windows executable paths * fix(process): preserve Windows shim completion * test(process): isolate Execa mocks * style(process): format Windows exec test * style(process): apply Windows test formatting * test(process): normalize Windows system path casing * fix(process): harden execa edge handling * test(tui): preserve ESM fixture semantics * fix(process): preserve PATHEXT lookup contract * fix(process): keep invocation type internal
* refactor(process): adopt execa execution layer * fix(process): preserve launch error semantics * chore(process): ratchet exec size baseline * fix(process): preserve Windows and error contracts * chore(plugin-sdk): ratchet wildcard surface budget * test(process): allow resolved Windows executable paths * fix(process): preserve Windows shim completion * test(process): isolate Execa mocks * style(process): format Windows exec test * style(process): apply Windows test formatting * test(process): normalize Windows system path casing * fix(process): harden execa edge handling * test(tui): preserve ESM fixture semantics * fix(process): preserve PATHEXT lookup contract * fix(process): keep invocation type internal
Closes #105920
What Problem This Solves
OpenClaw's main process-execution wrapper manually coordinated spawn, timeout, abort, output, and close races, while several production call sites bypassed that behavior with raw
node:child_processcalls.Why This Change Was Made
This adopts Execa 9.6.1 behind the canonical process wrapper, removes the replaced timer/signal race machinery, and moves descendant-pipe draining into
src/process/child-process.ts. OpenClaw still owns its security and platform contracts: argv-only execution,.cmd/.bathandling for CVE-2024-27980, Windows console-codepage decoding, environment normalization, process-tree termination, capped output capture, timeout, and abort behavior.The first migration batch routes agent session exec plus bash/find/grep helpers through the wrapper. The command queue remains unchanged.
User Impact
No intended user-visible behavior change. Process execution now has one canonical lifecycle path, with consistent timeout, abort, environment, output-limit, and descendant-pipe handling across the migrated surfaces.
Evidence
8c6fbd47e33; current base merged atbafabba0dfc5.44eaedaf7e4.pnpm tsgo:core,pnpm check:test-types,pnpm plugin-sdk:surface:check, and the then-current LOC gate passed.check-dependenciesjob passed after making the internal-only invocation type module-local.git diff --cached --checkand post-mergegit diff HEAD^2..HEAD --checkpassed. Resolution preserves both Execa's dependency graph and current main's dependency graph; main's removed LOC baseline stays removed.No changelog entry: internal execution-layer refactor with no intended user-visible behavior change.