Skip to content

fix: capture full session state — replace blanket ~/.copilot mount, add --session-state-dir#1593

Merged
lpcox merged 6 commits intomainfrom
fix/copilot-session-state-chroot-mount
Apr 2, 2026
Merged

fix: capture full session state — replace blanket ~/.copilot mount, add --session-state-dir#1593
lpcox merged 6 commits intomainfrom
fix/copilot-session-state-chroot-mount

Conversation

@lpcox
Copy link
Copy Markdown
Collaborator

@lpcox lpcox commented Apr 2, 2026

Summary

Fixes #1592events.jsonl and the full Copilot CLI session directory are never captured in workflow artifacts because the chroot bind mount shadows the AWF workDir volume mount.

Problem

Two competing volume mounts for ~/.copilot/session-state:

  1. Pre-chroot volume (line 777): workDir/agent-session-state → ~/.copilot/session-state:rw
  2. Chroot bind mount (line 837): ~/.copilot → /host~/.copilot:rw

After chroot to /host, mount #2 wins — Copilot CLI writes the entire session directory tree (events.jsonl, session.db, plan.md, checkpoints/, etc.) directly to the host filesystem, completely bypassing the AWF workDir. Nothing appears in artifacts.

Additionally, mounting the entire ~/.copilot directory exposes configuration and cached auth state to the sandboxed agent unnecessarily.

Changes

Commit 1: Replace blanket mount with targeted mounts

Replace the single ~/.copilot chroot bind mount with two targeted mounts pointing to the AWF workDir:

Path Before After
~/.copilot/session-state/ Host filesystem (lost) AWF workDir ✅
~/.copilot/logs/ Host filesystem AWF workDir ✅
~/.copilot/ (rest) Host dir exposed rw Empty writable dir (secure) ✅

Commit 2: Add --session-state-dir flag

Following the --proxy-logs-dir pattern, adds a --session-state-dir option for writing the full session directory tree to a predictable, timeout-safe path suitable for artifact upload:

awf --session-state-dir /tmp/gh-aw/sandbox/agent/session-state \
    --allow-domains github.com -- copilot ...
  • With --session-state-dir: Written directly to specified path during execution; survives workDir deletion; permissions fixed during cleanup
  • Without (default): Written to workDir/agent-session-state; moved to /tmp/awf-agent-session-state-<timestamp> during cleanup
  • Also supports AWF_SESSION_STATE_DIR environment variable

Files changed: src/types.ts, src/cli.ts, src/docker-manager.ts, src/docker-manager.test.ts

Testing

  • All existing tests pass (3 pre-existing macOS-only failures unrelated)
  • Added test for sessionStateDir config option

Related

…ate and logs mounts

The blanket ~/.copilot bind mount at the chroot path (/host$HOME/.copilot)
shadowed the pre-chroot AWF workDir volume mounts for session-state and logs.
After chroot, Copilot CLI wrote events.jsonl to the host filesystem via the
bind mount, bypassing the AWF workDir entirely. This prevented events.jsonl
from being captured in workflow artifacts.

Replace the single ~/.copilot mount with two targeted mounts that map AWF
workDir subdirectories (agent-session-state, agent-logs) to the chroot paths.
This ensures:

- events.jsonl is captured in AWF workDir (fixable in artifacts)
- Agent logs go through AWF workDir (consistent with non-chroot mode)
- Host ~/.copilot contents (config, auth state) are no longer exposed to the
  sandboxed agent — it gets an empty writable ~/.copilot from the empty home
  volume instead

Fixes #1592

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 2, 2026 01:32
@lpcox lpcox requested a review from Mossaka as a code owner April 2, 2026 01:32
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 85.73% 85.71% 📉 -0.02%
Statements 85.62% 85.60% 📉 -0.02%
Functions 86.66% 86.66% ➡️ +0.00%
Branches 78.43% 78.33% 📉 -0.10%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 85.5% → 85.5% (-0.09%) 85.1% → 85.0% (-0.09%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an artifact-capture bug in chroot mode by preventing a broad ~/.copilot bind mount from shadowing AWF workDir mounts, ensuring Copilot CLI events.jsonl and logs are written into the AWF workDir (and improving sandbox isolation by not exposing the host’s full ~/.copilot).

Changes:

  • Replace the blanket chroot mount of ~/.copilot with targeted chroot mounts for ~/.copilot/session-state and ~/.copilot/logs sourced from workDir.
  • Update inline documentation to reflect the new security/behavior rationale.
  • Update the volume-mount unit test to assert the new targeted mounts and the absence of the blanket mount.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/docker-manager.ts Replaces the chroot ~/.copilot blanket mount with targeted mounts for session-state and logs from workDir to ensure artifacts are captured and host config isn’t exposed.
src/docker-manager.test.ts Adjusts expectations to verify targeted ~/.copilot/{session-state,logs} mounts and confirm the blanket ~/.copilot mount is absent.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

🤖 Smoke test results for Claude engine:

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1593

@github-actions

This comment has been minimized.

Add a --session-state-dir option (following the --proxy-logs-dir pattern)
that writes the entire Copilot CLI session directory tree directly to a
predictable, external path. This makes the full session state (events.jsonl,
session.db, plan.md, checkpoints, etc.) available for artifact upload
without relying on post-cleanup /tmp paths.

When specified:
- Session state is written directly to the given path during execution
- Permissions are fixed during cleanup (chmod -R a+rX)
- The path survives AWF workDir deletion (timeout-safe)

When not specified (default):
- Session state goes to workDir/agent-session-state as before
- Moved to /tmp/awf-agent-session-state-<timestamp> during cleanup

Also supports AWF_SESSION_STATE_DIR environment variable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lpcox lpcox changed the title fix: replace blanket ~/.copilot chroot mount with targeted mounts fix: capture full session state — replace blanket ~/.copilot mount, add --session-state-dir Apr 2, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke Test Results

GitHub MCP: #1581 feat: add esbuild single-file bundle as lightweight distribution, #1588 fix: rename and scope token analyzer to Copilot workflows
Playwright: github.com title contains "GitHub"
File Write: /tmp/gh-aw/agent/smoke-test-claude-23881010728.txt created
Bash Verify: File content confirmed

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1593

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copilot CLI needs to create ~/.copilot/pkg/ for package extraction. When
the blanket ~/.copilot bind mount was replaced with targeted sub-mounts
(session-state, logs), Docker auto-created the .copilot directory as
root-owned to serve as a mount point. This caused EACCES when Copilot CLI
tried to mkdir ~/.copilot/pkg as the runner user.

Fix by pre-creating .copilot inside the empty home volume directory with
correct UID/GID ownership, so it's writable after chroot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 85.73% 85.74% ➡️ +0.01%
Statements 85.62% 85.63% ➡️ +0.01%
Functions 86.66% 86.66% ➡️ +0.00%
Branches 78.43% 78.29% 📉 -0.14%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 85.5% → 85.6% (+0.05%) 85.1% → 85.1% (+0.05%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke Test Results — PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1593

@github-actions

This comment has been minimized.

Removing the blanket ~/.copilot mount broke MCP config access and package
extraction. Copilot CLI needs:
- ~/.copilot/mcp-config.json (MCP server config, written by gh-aw framework)
- ~/.copilot/pkg/ (package extraction during startup)

Restore the host ~/.copilot bind mount and overlay session-state and logs
from the AWF workDir on top. Docker processes mounts in order, so the
later session-state and logs mounts shadow the corresponding paths under
the parent ~/.copilot mount.

Result:
- MCP config and packages accessible from host (as before)
- session-state → AWF workDir (events.jsonl captured)
- logs → AWF workDir (agent logs captured)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 85.73% 85.71% 📉 -0.02%
Statements 85.62% 85.60% 📉 -0.02%
Functions 86.66% 86.66% ➡️ +0.00%
Branches 78.43% 78.33% 📉 -0.10%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 85.5% → 85.5% (-0.07%) 85.1% → 85.0% (-0.07%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke test results (run 23881935575)

✅ GitHub MCP — #1581 feat: add esbuild single-file bundle as lightweight distribution, #1588 fix: rename and scope token analyzer to Copilot workflows
✅ Playwright — github.com title contains "GitHub"
✅ File write — /tmp/gh-aw/agent/smoke-test-claude-23881935575.txt created
✅ Bash verify — file content confirmed

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1593

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke Test Results — Run 23881935565

Test Result
GitHub MCP (last 2 merged PRs: #1588 "fix: rename and scope token analyzer to Copilot workflows", #1586 "fix: recompile token-usage-analyzer lock file")
Playwright (github.com title contains "GitHub")
File write (smoke-test-copilot-23881935565.txt)
Bash verify (cat file)

Overall: PASS

Author: @lpcox | No assignees

📰 BREAKING: Report filed by Smoke Copilot for issue #1593

@github-actions

This comment has been minimized.

AWF runs as root but the agent container runs as the host user (e.g., UID
1001). The session-state and agent-logs overlay directories were created
by root and never chowned, so Copilot CLI could not create session
subdirectories or write events.jsonl.

Chown both directories to the host user's UID/GID after creation, matching
how squid-logs are chowned to the proxy user.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 85.73% 85.73% ➡️ +0.00%
Statements 85.62% 85.62% ➡️ +0.00%
Functions 86.66% 86.66% ➡️ +0.00%
Branches 78.43% 78.33% 📉 -0.10%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 85.5% → 85.6% (+0.01%) 85.1% → 85.1% (+0.01%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke Test Results

GitHub MCP#1581 feat: add esbuild single-file bundle as lightweight distribution; #1588 fix: rename and scope token analyzer to Copilot workflows
Playwright — github.com title contains "GitHub"
File Write/tmp/gh-aw/agent/smoke-test-claude-23882121359.txt created and verified
Bash — file contents confirmed

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1593

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke test results @lpcox

✅ GitHub MCP — Last 2 merged PRs: #1588 "fix: rename and scope token analyzer to Copilot workflows", #1586 "fix: recompile token-usage-analyzer lock file"
✅ Playwright — github.com title contains "GitHub"
✅ File write — /tmp/gh-aw/agent/smoke-test-copilot-23882121383.txt created and verified
✅ Bash — file read back successfully

Overall: PASS

📰 BREAKING: Report filed by Smoke Copilot for issue #1593

@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3 ❌ NO
Node.js v24.14.0 v20.20.1 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall: ❌ NOT all tests passed — Python and Node.js versions differ between host and chroot environments.

Tested by Smoke Chroot for issue #1593

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Add tests covering the sessionStateDir conditional cleanup paths to fix
the branch coverage regression (-0.10%):

- Preserve session state to /tmp when sessionStateDir is not specified
- Chmod session state in-place when sessionStateDir is specified

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 85.73% 85.96% 📈 +0.23%
Statements 85.62% 85.84% 📈 +0.22%
Functions 86.66% 86.66% ➡️ +0.00%
Branches 78.43% 78.55% 📈 +0.12%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 85.5% → 86.5% (+0.94%) 85.1% → 86.0% (+0.92%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke Test Results — PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1593

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

🏗️ Build Test Suite Results

⚠️ ALL CLONES FAILEDgh CLI is not authenticated in this environment (GH_TOKEN not set). All test repositories could not be cloned.

Ecosystem Project Build/Install Tests Status
Bun elysia ❌ CLONE_FAILED N/A ❌ FAIL
Bun hono ❌ CLONE_FAILED N/A ❌ FAIL
C++ fmt ❌ CLONE_FAILED N/A ❌ FAIL
C++ json ❌ CLONE_FAILED N/A ❌ FAIL
Deno oak ❌ CLONE_FAILED N/A ❌ FAIL
Deno std ❌ CLONE_FAILED N/A ❌ FAIL
.NET hello-world ❌ CLONE_FAILED N/A ❌ FAIL
.NET json-parse ❌ CLONE_FAILED N/A ❌ FAIL
Go color ❌ CLONE_FAILED N/A ❌ FAIL
Go env ❌ CLONE_FAILED N/A ❌ FAIL
Go uuid ❌ CLONE_FAILED N/A ❌ FAIL
Java gson ❌ CLONE_FAILED N/A ❌ FAIL
Java caffeine ❌ CLONE_FAILED N/A ❌ FAIL
Node.js clsx ❌ CLONE_FAILED N/A ❌ FAIL
Node.js execa ❌ CLONE_FAILED N/A ❌ FAIL
Node.js p-limit ❌ CLONE_FAILED N/A ❌ FAIL
Rust fd ❌ CLONE_FAILED N/A ❌ FAIL
Rust zoxide ❌ CLONE_FAILED N/A ❌ FAIL

Overall: 0/8 ecosystems passed — ❌ FAIL

Error Details

All 8 ecosystems failed with the same error:

gh: To use GitHub CLI in a GitHub Actions workflow, set the GH_TOKEN environment variable. Example:
  env:
    GH_TOKEN: ${{ github.token }}

The gh CLI is not authenticated. The workflow needs GH_TOKEN to be set in the environment to clone repositories from Mossaka/gh-aw-firewall-test-*.

Generated by Build Test Suite for issue #1593 ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke Test Results

Test Status
GitHub MCP (last 2 merged PRs)
Playwright (github.com title check)
File writing
Bash verification

Last 2 merged PRs: #1588 "fix: rename and scope token analyzer to Copilot workflows", #1586 "fix: recompile token-usage-analyzer lock file" (author: @lpcox)

Overall: PASS

📰 BREAKING: Report filed by Smoke Copilot for issue #1593

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.14.0 v20.20.1
Go go1.22.12 go1.22.12

Overall: ❌ Not all versions match

Go versions match, but Python and Node.js differ between host and chroot environments. The chroot uses system-installed versions (Ubuntu 22.04 defaults) while the host has newer versions from tool caches.

Tested by Smoke Chroot for issue #1593

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke Test: GitHub Actions Services Connectivity ✅

All checks passed:

Check Result
Redis PINGhost.docker.internal:6379 PONG
pg_isreadyhost.docker.internal:5432 ✅ accepting connections
psql SELECT 1smoketest db as postgres ✅ returned 1

🔌 Service connectivity validated by Smoke Services

@github-actions

This comment has been minimized.

@lpcox lpcox merged commit 6536923 into main Apr 2, 2026
63 of 65 checks passed
@lpcox lpcox deleted the fix/copilot-session-state-chroot-mount branch April 2, 2026 04:30
github-actions bot pushed a commit that referenced this pull request Apr 2, 2026
… var

Add the --session-state-dir CLI flag and AWF_SESSION_STATE_DIR environment
variable to the documentation, introduced in #1593.

- docs/usage.md: add --session-state-dir to the CLI options list (after
  --proxy-logs-dir), and expand the Agent Session State description to
  mention the flag's timeout-safe, predictable-path behaviour useful for
  GitHub Actions artifact uploads
- docs/environment.md: add AWF_SESSION_STATE_DIR to the internal variables
  table and update the note to clarify it is user-configurable

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Smoke Test: PASS (Claude, run 23900672204)

💥 [THE END] — Illustrated by Smoke Claude

@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

🔮 The ancient spirits stir, and the smoke has been read.
The firewall run speaks true where tools were present, and marks clear omens where integrations were absent.
This oracle leaves its sigil here as witness to the trial.

🔮 The oracle has spoken through Smoke Codex

Mossaka pushed a commit that referenced this pull request Apr 2, 2026
… var (#1600)

Add the --session-state-dir CLI flag and AWF_SESSION_STATE_DIR environment
variable to the documentation, introduced in #1593.

- docs/usage.md: add --session-state-dir to the CLI options list (after
  --proxy-logs-dir), and expand the Agent Session State description to
  mention the flag's timeout-safe, predictable-path behaviour useful for
  GitHub Actions artifact uploads
- docs/environment.md: add AWF_SESSION_STATE_DIR to the internal variables
  table and update the note to clarify it is user-configurable

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: events.jsonl not captured — chroot bind mount bypasses AWF workDir volume

2 participants