Problem
Copilot-engine workflows on self-hosted runners (including ARC/DinD) fail with exit 127 because node is not discoverable inside AWF chroot. The Node.js binary lives in $HOME/work/_tool/node/... on these runners, but AWF only mounts and scans /opt/hostedtoolcache.
Related upstream fix: github/gh-aw#33219 — works around this from the caller side by passing extra --mount args, but AWF should handle it natively.
Current State
AWF already handles the hosted runner case:
/opt is bind-mounted as /host/opt:ro (covers /opt/hostedtoolcache)
entrypoint.sh dynamically scans /opt/hostedtoolcache/*/ for bin dirs and appends to PATH
Gap
Self-hosted runners (and some ARC configurations) store toolcache under:
$HOME/work/_tool/node/<version>/<arch>/bin/
/home/runner/work/_tool/node/<version>/<arch>/bin/
This path is:
- Not bind-mounted — AWF only mounts whitelisted
$HOME subdirs (.cache, .config, .local, etc.)
- Not scanned for PATH —
entrypoint.sh only scans /opt/hostedtoolcache
Implementation Strategy
1. Bind-mount the toolcache directory (src/services/agent-volumes.ts)
Add a conditional mount for the runner toolcache when it exists:
// Self-hosted runner toolcache (e.g., ARC runners store tools here)
// Only mount if the directory actually exists on the host
const runnerToolDir = path.join(config.homeDir || /home/runner, work/_tool);
// Add: `${runnerToolDir}:/host${runnerToolDir}:ro`
This should be conditional — only add the mount if the source path exists (similar to how other optional mounts work).
2. Extend PATH scanning in entrypoint.sh
Add the self-hosted toolcache to the existing scan loop:
# After the /opt/hostedtoolcache scan (line ~776):
# Also scan self-hosted runner toolcache location
if [ -d "/home/runner/work/_tool" ]; then
for tool_dir in /home/runner/work/_tool/*/; do
for version_dir in "$tool_dir"*/; do
for arch_dir in "$version_dir"*/; do
if [ -d "${arch_dir}bin" ]; then
case ":${PATH}:" in
*":${arch_dir}bin:"*) ;;
*) export PATH="${PATH}:${arch_dir}bin" ;;
esac
fi
done
done
done
fi
Alternatively, generalize the scan to use an env var like AWF_EXTRA_TOOLCACHE_DIRS so the caller can pass additional scan roots.
3. Tests
- Unit test in
agent-volumes.test.ts asserting the conditional mount appears when homeDir is set
- Shell test in
tests/chroot-path-ordering.test.sh verifying the _tool path is scanned
Acceptance Criteria
Problem
Copilot-engine workflows on self-hosted runners (including ARC/DinD) fail with
exit 127becausenodeis not discoverable inside AWF chroot. The Node.js binary lives in$HOME/work/_tool/node/...on these runners, but AWF only mounts and scans/opt/hostedtoolcache.Related upstream fix: github/gh-aw#33219 — works around this from the caller side by passing extra
--mountargs, but AWF should handle it natively.Current State
AWF already handles the hosted runner case:
/optis bind-mounted as/host/opt:ro(covers/opt/hostedtoolcache)entrypoint.shdynamically scans/opt/hostedtoolcache/*/for bin dirs and appends to PATHGap
Self-hosted runners (and some ARC configurations) store toolcache under:
This path is:
$HOMEsubdirs (.cache,.config,.local, etc.)entrypoint.shonly scans/opt/hostedtoolcacheImplementation Strategy
1. Bind-mount the toolcache directory (
src/services/agent-volumes.ts)Add a conditional mount for the runner toolcache when it exists:
This should be conditional — only add the mount if the source path exists (similar to how other optional mounts work).
2. Extend PATH scanning in
entrypoint.shAdd the self-hosted toolcache to the existing scan loop:
Alternatively, generalize the scan to use an env var like
AWF_EXTRA_TOOLCACHE_DIRSso the caller can pass additional scan roots.3. Tests
agent-volumes.test.tsasserting the conditional mount appears whenhomeDiris settests/chroot-path-ordering.test.shverifying the_toolpath is scannedAcceptance Criteria
nodeis discoverable inside chroot on self-hosted runners using$HOME/work/_tool/opt/hostedtoolcacheflow unchanged)