Skip to content

fix: load packaged native addons from the isolated session - #75

Merged
paulcam206 merged 5 commits into
openclaw:mainfrom
anna-dingler:fix/stage-native-modules-for-isolated-session
Sep 19, 2026
Merged

paulcam206 merged 5 commits into
openclaw:mainfrom
anna-dingler:fix/stage-native-modules-for-isolated-session

Conversation

@anna-dingler

@anna-dingler anna-dingler commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Problem

openclaw tui, openclaw doctor, and openclaw onboard --classic fail inside the isolated session with:

Cannot find the native Koffi module; did you bundle it correctly?

The package is not at fault. The file is present and intact — bypassing koffi's loader with process.dlopen gives the real error:

ERR_DLOPEN_FAILED | Access is denied.
...\app\node_modules\koffi\node_modules\@koromix\koffi-win32-x64\win32_x64\koffi.node

The isolated-session agent identity may read packaged content but may not map it as an executable image. Copying the same bytes to a writable location loads fine (OK 3.1.6).

The message misleads twice over: koffi's loadStatic swallows the real error in a bare catch {}, and OpenClaw then wraps the result in disk-space / XDG_CACHE_HOME advice that has nothing to do with the cause.

Why now

The limitation is long-standing, and SessionHelperStager already documents it for this package's own helper executable. What changed is reachability: koffi@3.1.6 is pinned identically at the old and new upstream revisions, but the new revision moved Windows private-directory and process-start handling onto koffi, putting a native load on the path that opens the shared state database. #15 removed the payload staging that had been masking it, and #36's pin bump made it reachable. clawctl setup still succeeded because it touches only JSON.

Measured on the affected build, all eight win32-x64 addons fail identically — pi-tui, node-pty (×2), fs-safe, cua-driver (×2), koffi, tree-sitter-bash. koffi was simply the first called without a guard. sqlite-vec's vec0.dll is in the same state.

Fix

Setup mirrors the dependency packages carrying native artifacts into the agent's own LocalState and redirects resolution to them.

  • Whole owning package directories are copied. A package locates its sibling libraries and helper executables relative to its own directory, so this is what keeps vec0.dll and node-pty's OpenConsole.exe correct without intercepting each loading mechanism separately (there are three: CJS dlopen, ESM import.meta.resolve, and derived sibling paths).
  • A packaged preload redirects both CommonJS and ESM resolution, delivered via NODE_OPTIONS rather than argv so the Node.js workers OpenClaw spawns inherit it, and gated on the staged file existing. The inherited NODE_OPTIONS is preserved and the redirect appended.
  • The staged set is discovered by scanning app\node_modules, never hard-coded, so a future upstream revision that adds a native dependency is staged automatically instead of failing when something first calls it.
  • Staging is keyed by a content identifier over the staged files' bytes, writes its marker last so an interrupted copy is redone, and renames a superseded root before deleting it so a root still in use by a running process is left whole rather than partly deleted.

Trust boundary

Application code still executes from the immutable package; this is deliberately not a payload stager. Only ~55 MB / 1,074 native-bearing files are mirrored, out of 426 MB / 36,463. The copies live in the agent profile that already holds the extracted Node.js runtime and is written and read by the identity that executes them — not the guest-writable shared workspace.

README.md, CONTRIBUTING.md, and .github/copilot-instructions.md are updated, since the existing "do not copy the payload" guidance needed to name this exception.

Evidence: installed package on a test VM

A test-signed x64 MSIX built from this branch was installed on a clean VM. The commands that previously died on the Koffi load now work.

clawctl gateway-service status — the gateway starts and listens, which is the path that opens the shared state database through koffi:

clawctl gateway-service status showing the gateway listening on port 18789

openclaw tui — previously the hard failure, since it loads koffi unguarded. It now connects to the session and round-trips a message:

openclaw tui connected to ws://127.0.0.1:18789, agent main, exchanging a message

The Control UI served by that gateway, reached from the browser:

OpenClaw Control UI at 127.0.0.1:18789/chat/main showing a chat message

Scope of this evidence, stated precisely: the VM build is commit 9696a50, which carries the staging fix but not the three review fixes that follow it. It therefore demonstrates that staging plus redirection resolves the reported failure on an installed package; it does not exercise inherited NODE_OPTIONS, in-use root reclamation, or byte-based content identity, which are covered by unit tests.

Known gaps

Stated plainly, because several are not closed by this PR:

Gap Status
ARM64 Unverified. The code paths are architecture-neutral and discovery is by scan, but no ARM64 package has been installed and exercised.
In-place upgrade Not demonstrated. The install above was clean. How the new persisted agentNativeRoot field behaves upgrading from a build that never wrote it is untested.
Natives resolved by a constructed absolute path Not redirected. The preload hooks module resolution; code that hands process.dlopen a path it built by string concatenation bypasses it. No such call is known in the current payload, but this is not enforced.
Natives outside app\node_modules Not staged. Discovery scans node_modules only. Verified that all 19 native entries in the current package live there, so there is no blind spot at this revision — but a future payload that ships one elsewhere would regress silently.
Cross-package relative escapes Not covered. Staging whole owning package directories fixes siblings within a package; a package reaching outside its own directory for a native would still resolve into the package.
Superseded-root reclamation is best effort A root still in use is deliberately retained and reclaimed by a later setup, so the agent profile can transiently hold more than one ~55 MB root.
No build-time gate on the preload Nothing asserts node\native-redirect.mjs is actually present in the composed MSIX. Given that metadata files are a release trust boundary here, that check is probably worth adding.
Module._resolveFilename An internal Node.js API. Stable in practice and widely relied on, but not a documented contract.

Validation

Local dotnet restore cannot reach the NuGet feed from my sandbox, so build and test coverage is CI's.

Before the VM run, the redirect was verified against the real MSIX in isolation: two koffi trees extracted from the artifact, the "package" copy's koffi.node deliberately corrupted to 7 bytes. Without the preload both CJS and ESM reproduce the exact user-facing error; with it, CJS OK 3.1.6 / ESM OK 3.1.6.

The partial-delete hazard behind the reclamation fix was reproduced directly: with a read handle open on one file, Directory.Delete(recursive: true) removed a sibling file before throwing, while Directory.Move threw IOException and left the tree intact.

@clawsweeper

clawsweeper Bot commented Sep 18, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added P1 Urgent regression or broken agent/channel workflow affecting real users now. merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Sep 18, 2026
@clawsweeper

clawsweeper Bot commented Sep 18, 2026

Copy link
Copy Markdown

Codex review: blocked before merge. Reviewed September 18, 2026, 8:51 PM ET / September 19, 2026, 00:51 UTC (Revision 4).

ClawSweeper review

What this changes

Setup copies native dependency packages into the isolated agent’s profile and redirects foreground commands, shells, and the gateway to those copies while preserving the agent’s Node.js options.

Merge readiness

Blocked before merge - 3 items remain

This remains a useful fix absent from current main. The three latest review findings are addressed; no remaining blocking code defect was established. Installed upgrade and architecture validation remain incomplete.

Priority: P1
Reviewed head: 32df93ecb6e7d166b75af98a479854bcd418a368

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) The repaired implementation is coherent, but demonstrated runtime coverage remains narrower than the changed setup and lifetime behavior.
Proof confidence 🦐 gold shrimp (3/6) Not applicable: The collaborator-authored PR is exempt from the external-contributor proof gate. Inspected x64 VM screenshots support earlier staging recovery, but do not establish installed upgrade, ARM64, or the subsequent options and lease repairs; those remain separate validation blockers.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: The collaborator-authored PR is exempt from the external-contributor proof gate. Inspected x64 VM screenshots support earlier staging recovery, but do not establish installed upgrade, ARM64, or the subsequent options and lease repairs; those remain separate validation blockers.
Evidence reviewed 8 items Verified review boundary: Reviewed the introduced delta from pinned main 26e22e4 to the checkout head. The supplied verified test merge has those exact main and head parents; no base-only changes were attributed to this PR.
Previous findings addressed: Both guest launch owners append the named preload to the guest environment and hold a consumer lease. Staging retries reclamation on unchanged-content reuse. Focused tests cover guest options, gateway request delivery, held roots, and cleanup after lease release.
Existing upgrade and dependency safeguards: Payload construction uses npm's nested installation strategy. SessionHelperStager requires a version-specific helper and verifies its bytes before dispatch; official composition sets the launcher's assembly version from the MSIX version. These safeguards prevent treating missing hoisted dependencies or ordinary official upgrades silently reusing an old staging record as established defects.
Findings None None.
Security None None.

How this fits together

The Windows launcher starts OpenClaw inside an isolated agent session. Setup prepares native dependencies, and Node.js resolution redirects their loads from the installed MSIX into the agent-owned copies.

flowchart TD
  A[Installed MSIX dependencies] --> B[Setup scans native packages]
  B --> C[Agent profile copies]
  C --> D[Recorded staging root]
  D --> E[Guest launch environment and lease]
  E --> F[Node module resolution]
  F --> G[Foreground commands and gateway]
Loading

Before merge

  • Resolve merge risk (P1) - The new persisted staging root has no installed, state-preserving upgrade evidence; existing installations must successfully complete required setup without losing configuration or session state.
  • Resolve merge risk (P1) - ARM64 native loading and the revised guest-options and lease behavior have not been demonstrated in an installed package; the available VM evidence predates those repairs.
  • Complete next step (P2) - Complete installed-package validation for the repaired head: fresh installation, state-preserving upgrade, guest options and retained-root behavior, and ARM64 native loading.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test growth Production +834/-10; tests +558/-13 Production growth implements scanning, redirection, protocol transport, and lifetime protection for the documented loader failure.
Prior findings 3 addressed Guest environment composition, consumer leases, and cleanup retries now have corresponding implementation and focused coverage.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #77
Summary: This PR is the explicitly identified native-loading repair candidate.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge-risk options

Maintainer options:

  1. Complete installed-package validation (recommended)
    Demonstrate fresh and state-preserving upgrades, guest option preservation, retained-shell delayed loads, and ARM64 native loading before landing.

Technical review

Best possible solution:

Retain narrowly scoped agent-owned staging, with demonstrated fresh and state-preserving upgraded operation on both supported architectures.

Do we have a high-confidence way to reproduce the issue?

Yes: the reported installed-session gateway/TUI path and direct loader error identify a concrete failure, corroborated by the existing helper-staging constraint. This read-only review did not execute Windows reproduction.

Is this the best way to solve the issue?

Yes: scanning and redirecting native-bearing packages fits the existing nested payload and agent-owned runtime model; installed upgrade validation is still needed to establish readiness.

AGENTS.md: not found in the target repository.

Codex review notes: model internal, reasoning medium; reviewed against 26e22e4657a2.

Labels

Label changes:

  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The collaborator-authored PR is exempt from the external-contributor proof gate. Inspected x64 VM screenshots support earlier staging recovery, but do not establish installed upgrade, ARM64, or the subsequent options and lease repairs; those remain separate validation blockers.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The reported native-loader failure prevents real isolated gateway and CLI workflows.
  • merge-risk: 🚨 compatibility: Persisted staging state and setup changes still lack installed upgrade validation.
  • merge-risk: 🚨 availability: Native resolution is required for startup, while ARM64 and revised lifetime behavior remain unverified in installed packages.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The collaborator-authored PR is exempt from the external-contributor proof gate. Inspected x64 VM screenshots support earlier staging recovery, but do not establish installed upgrade, ARM64, or the subsequent options and lease repairs; those remain separate validation blockers.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The collaborator-authored PR is exempt from the external-contributor proof gate. Inspected x64 VM screenshots support earlier staging recovery, but do not establish installed upgrade, ARM64, or the subsequent options and lease repairs; those remain separate validation blockers.

Evidence

What I checked:

  • Verified review boundary: Reviewed the introduced delta from pinned main 26e22e4 to the checkout head. The supplied verified test merge has those exact main and head parents; no base-only changes were attributed to this PR. (32df93ecb6e7)
  • Previous findings addressed: Both guest launch owners append the named preload to the guest environment and hold a consumer lease. Staging retries reclamation on unchanged-content reuse. Focused tests cover guest options, gateway request delivery, held roots, and cleanup after lease release. (src/OpenClaw.SessionHost/SessionNativeStager.cs:164, 32df93ecb6e7)
  • Existing upgrade and dependency safeguards: Payload construction uses npm's nested installation strategy. SessionHelperStager requires a version-specific helper and verifies its bytes before dispatch; official composition sets the launcher's assembly version from the MSIX version. These safeguards prevent treating missing hoisted dependencies or ordinary official upgrades silently reusing an old staging record as established defects. (src/OpenClaw.Launcher/Session/SessionHelperStager.cs:59, 32df93ecb6e7)
  • Installed proof and its limits: Inspected all three prepared primary-PR screenshots: gateway status shows listening, the TUI shows a connected session, and the browser shows the chat page. The captured body explicitly identifies build 9696a50 and excludes later options, lease, and content-identity fixes from that VM run. The September 19 collaborator comment confirms upgraded-package, ARM64, and refreshed VM validation remain outstanding. Captured context sourceRevision: 2f1aa4e018b58ebef3c1eb2b5d639a97b5a85cc5360800c12e7ae75712722231. (9696a508c764)
  • Still needed on main and latest release: Pinned main's runtime installer prepares Node.js but contains no native-package staging. The latest release remains v2026.9.4-msix.2 at a99bb66; no release tag contains this PR head. No supplied merged PR implements this native-loading repair. (src/OpenClaw.SessionHost/SessionRuntimeInstaller.cs:104, 26e22e4657a2)
  • Release check: GitHub's latest-release endpoint reports v2026.9.4-msix.2 targeting a99bb66. (a99bb66d2162)

Likely related people:

  • paulcam206: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • anna-dingler: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Provide installed fresh and state-preserving upgrade results for the repaired head, including configuration and session retention.
  • Verify guest NODE_OPTIONS and delayed native loads while a shell remains open across staging replacement.
  • Complete ARM64 installed native-loading validation.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
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.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (3 earlier review cycles)
  • reviewed 2026-09-18T06:37:12.449Z sha 9696a50 :: blocked before merge. :: [P1] Preserve inherited agent NODE_OPTIONS when adding the preload | [P1] Retain staged roots until their running consumers have exited | [P2] Identify staged content using bytes or immutable package provenance
  • reviewed 2026-09-18T07:18:09.032Z sha a6a849b :: blocked before merge. :: [P1] [P1] Append the preload to the guest's NODE_OPTIONS | [P1] [P1] Retain staged roots for consumers without open file handles | [P1] [P1] Validate the staged root against the installed payload | [P3] [P3] Retry safe reclamation when reusing the current root
  • reviewed 2026-09-18T07:29:02.796Z sha a6a849b :: blocked before merge. :: [P1] [P1] Append the preload to the guest's NODE_OPTIONS | [P1] [P1] Retain staged roots for consumers without open handles | [P3] [P3] Retry safe reclamation when reusing the current root

@clawsweeper clawsweeper Bot added the proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. label Sep 18, 2026
@ChazGo

ChazGo commented Sep 18, 2026

Copy link
Copy Markdown

@anna-dingler FYI I hit this issue that you're fixing when updating the plugin UI for the latest POR changes for gateway isolation

anna-dingler and others added 5 commits September 18, 2026 17:39
The isolated-session agent identity may read package content but may not
map it as an executable image. Loading any packaged `.node` therefore
fails with ERR_DLOPEN_FAILED / access denied, even though the same bytes
load from a writable location and the file is present and intact.

This surfaced as an unrelated-looking message. Koffi's loader swallows the
real error in a bare catch and reports only "Cannot find the native Koffi
module; did you bundle it correctly?", which OpenClaw then wraps in
disk-space and XDG_CACHE_HOME advice. The package was never at fault.

The limitation is long-standing, and `SessionHelperStager` already
documents it for this package's own helper executable. What changed is
reachability: the pinned upstream revision moved Windows private-directory
and process-start handling onto koffi, putting a native load on the path
that opens the shared state database. `setup` still worked because it
touches only JSON, while `tui`, `doctor`, and `onboard` failed. Verified
on the affected build: all eight win32-x64 addons fail identically, so
koffi was simply the first to be called unguarded.

Setup now mirrors the dependency packages that carry native artifacts into
the agent's own LocalState and redirects resolution to them. Whole owning
package directories are copied, because a package locates its sibling
libraries and helper executables relative to its own directory; that is
what keeps sqlite-vec's vec0.dll and node-pty's OpenConsole.exe correct
without intercepting each one. A packaged preload redirects both CommonJS
and ESM resolution, delivered through NODE_OPTIONS so the Node.js workers
OpenClaw starts inherit it, and gated on the staged file existing.

The staged set is discovered by scanning `app\node_modules`, never
hard-coded, so a future upstream revision that adds a native dependency is
staged automatically rather than failing once something calls it.

Application code still executes from the immutable package: only ~55 MB of
1,074 native-bearing files are mirrored, out of 426 MB and 36,463 files.
The copies live in the agent profile that already holds the extracted
Node.js runtime and is written and read by the identity that executes
them, not in the guest-writable shared workspace, so the trust boundary is
unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24c979ef-3fc7-4ec2-8fef-451bf67263d6
Addresses three defects found in review of the staging change.

Inherited NODE_OPTIONS was replaced rather than extended. Both production
callers left the existing value unread, so an agent configured with
options such as --max-old-space-size lost them the first time setup staged
native packages. Foreground and gateway launches now pass the inherited
value through, and an agent-launch test asserts the redirect is appended
to it.

Reclaiming a superseded staged root deleted it recursively while a gateway
or foreground process could still be loading from it. Ordinary setup
reuses a running session, and a recursive delete removes the unlocked
files before it fails on the first mapped image, so catching the failure
could not undo the damage: measured locally, a sibling JavaScript file was
already gone once the exception surfaced. Each root is now renamed before
deletion. Windows refuses to rename a directory holding an open file, so a
root still in use fails the rename intact and is reclaimed by a later
setup instead.

Staged content was identified by path, length, and write time. That does
not distinguish revisions: an npm package republished at the same length
carries its tarball's timestamps, and a Developer Mode layout is rewritten
in place, so changed executable content could be served from an identifier
already on disk. The identifier now covers each file's bytes. Hashing the
staged set costs one pass over roughly 55 MB during setup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24c979ef-3fc7-4ec2-8fef-451bf67263d6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24c979ef-3fc7-4ec2-8fef-451bf67263d6
Host-supplied launch environment values are assigned over the agent
account's own, so composing NODE_OPTIONS on the host replaced whatever
the agent had set and could push a host-only preload path into the
agent's Node.js processes. The launcher now names the preload option in
the launch request and the guest appends it to the agent's own value,
the same way PathPrefix already works for PATH.

Reclaiming a superseded native root treated a successful rename as proof
that nothing was using it. A consumer need not hold any staged file open:
an agent shell sitting at a prompt carries the redirect in its
environment and resolves through that root on every later OpenClaw run.
Each launch now holds the root's marker open for its whole lifetime, so
the rename is a real lifetime check, and reclamation also runs on the
unchanged-content reuse path so releasing a consumer actually frees the
root it was holding.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The gateway launch is a second guest boundary and had no coverage for the
named native redirect, so a regression there would only surface as native
addons failing to load in a supervised session. Assert the delivered launch
request names the preload and the staged root without setting NODE_OPTIONS,
that a session with no staged natives names neither, and that both fields
survive the launch protocol round trip.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paulcam206
paulcam206 force-pushed the fix/stage-native-modules-for-isolated-session branch from a6a849b to 32df93e Compare September 19, 2026 00:47
@paulcam206

Copy link
Copy Markdown
Collaborator

Thanks — all three findings are addressed, and the branch is rebased onto main (now includes #79#81).

[P1] Append the preload to the guest's NODE_OPTIONS

The host no longer composes NODE_OPTIONS at all. It now names the option and lets the guest append it, following the existing PathPrefix precedent — host-supplied Environment entries are assigned over the agent's, so a value that must merge with the agent's own can never be built host-side.

  • SessionLaunchRequest gained nodeOptionsSuffix (and nativeRootPath, below).
  • SessionProcessLauncher.AppendNodeOptions mirrors PrependPath; called from both SessionProcessLauncher.Run (attached) and SessionSupervisor.Supervise (detached gateway).
  • BuildNativeRedirect now returns only the two root variables; the new BuildNativeRedirectNodeOption returns the --import value.
  • Both launch paths (Program agent + clawctl pwsh) and the gateway path set the suffix.

Covered by AgentLaunchNamesTheNativeRedirectWithoutSettingNodeOptions (asserts the request carries no NODE_OPTIONS even when the invoking host has --host-only-flag set) and three guest-boundary tests that assert the agent's own NODE_OPTIONS is preserved, used alone, and left untouched when no suffix is named.

[P1] Retain staged roots for consumers without open handles

You were right that "the rename succeeded" was not a lifetime check: an idle clawctl pwsh holds the redirect in its environment with no file open. There is now an explicit lease.

  • SessionNativeStager.OpenConsumerLease opens <root>\.staged-content-id with FileShare.Read — which denies delete sharing, so Windows refuses to rename the root while it is held.
  • Both guest launch paths hold the lease for the launched process's whole lifetime (both already WaitForExit, so it spans the child and anything it spawns).
  • It returns null on any error, so bookkeeping can never block a launch.

ASupersededRootWithALiveConsumerAndNoOpenFilesIsLeftIntact reproduces exactly your scenario; AConsumerLeaseIsNotRequiredForALaunchToProceed pins the fail-open behaviour.

[P3] Retry safe reclamation when reusing the current root

Stage's early return on a matching marker now calls RemoveSupersededRoots before returning, so releasing a consumer lets the next unchanged-content setup reclaim the retained root instead of leaking it forever. Covered by AReleasedRootIsReclaimedByTheNextUnchangedContentSetup.

Also added

Gateway-path coverage, since the detached launch is a second guest boundary that had none: the delivered SessionLaunchRequest names the preload and the staged root without setting NODE_OPTIONS, a session with no staged natives names neither, and both new fields survive the launch-protocol round trip.

Not addressed here

The three remaining merge risks (installed-package upgrade, ARM64 runtime verification, refreshed VM evidence) are validation rather than code, and need a real machine — they are not covered by this push.

scripts\Test-DotNetQuality.ps1 is clean (0 warnings) and the suite is 807/807 green.

@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Sep 19, 2026
@paulcam206
paulcam206 marked this pull request as ready for review September 19, 2026 01:05
@paulcam206

Copy link
Copy Markdown
Collaborator

I don't love it, but we don't have another workaround at the moment :shipit:

@clawsweeper

clawsweeper Bot commented Sep 19, 2026

Copy link
Copy Markdown

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix: load packaged native addons from the isolated session This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@paulcam206
paulcam206 merged commit 8215c34 into openclaw:main Sep 19, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P1 Urgent regression or broken agent/channel workflow affecting real users now. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants