Skip to content

feat: add protected command output capture - #10169

Merged
lpcox merged 11 commits into
mainfrom
copilot/feat-native-protected-command-output-capture
Oct 18, 2026
Merged

lpcox merged 11 commits into
mainfrom
copilot/feat-native-protected-command-output-capture

Conversation

Copilot AI commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Agent-writable capture files cannot provide a trustworthy record of command output, and callers should not have to reproduce AWF’s mount-isolation and lifecycle handling. This adds opt-in host-only capture for non-TTY Compose runtimes, with explicit completeness and attempt metadata.

  • Capture: AWF creates private per-attempt stdout/stderr files before launch, captures raw bytes independently of console presentation, and caps total storage at 64 MiB by default.
  • Isolation: Rejects paths overlapping agent-visible mounts, including symlink aliases, custom mounts, and Docker host-path translations. Also rejects Docker socket access, TTY mode, and unsupported runtimes.
  • Handoff: metadata.json records invocation and attempt IDs, observed exit or interruption, drain status, truncation or capture failures, presentation failures, and teardown. Raw output is not automatically published.
  • Usage:
    awf --capture-command-output "$RUNNER_TEMP/awf-capture-$GITHUB_RUN_ID" \
      --allow-domains github.com -- your-command
    AWF validates the selected path; callers remain responsible for parsing, redaction, and publication.

Copilot AI changed the title [WIP] Add AWF capability for protected command output capture Add protected command output capture Oct 17, 2026
Copilot AI requested a review from lpcox October 17, 2026 19:40
@lpcox
lpcox marked this pull request as ready for review October 17, 2026 20:34
Copilot AI max review requested due to automatic review settings October 17, 2026 20:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved isolation bypasses and output-handling failures undermine the protected-capture contract.

12 open issues
What changed in this PR

Adds opt-in, host-only command output capture to AWF’s non-TTY Compose execution path.

Changes:

  • Adds capture options and isolation validation.
  • Records bounded streams with attempt and completeness metadata.
  • Integrates lifecycle handling, tests, and documentation.
File Description
src/​types/​volume-options.ts Defines capture configuration.
src/​run-agent-command.test.ts Tests raw streams and cancellation.
src/​protected-command-output-capture.ts Implements private capture and metadata.
src/​protected-command-output-capture.test.ts Tests isolation, limits, and outcomes.
src/​enclave/​mount-policy.ts Extends isolation checks for capture.
src/​container-lifecycle.ts Connects streams and attempt tracking.
src/​commands/​validators/​config-assembly.ts Validates capture compatibility.
src/​commands/​main-action.ts Coordinates capture setup and teardown.
src/​commands/​build-config.ts Maps capture CLI options.
src/​commands/​build-config.test.ts Tests option mapping.
src/​cli-workflow.ts Passes capture into execution.
src/​cli-options.ts Registers capture flags.
README.md Introduces capture documentation.
docs/​protected-command-output.md Documents usage and limitations.

Review effort: Max

}
if (commandOutputCapture) {
if (agentCommandStarted) {
await commandOutputCapture.waitForCommandCompletion();
Comment thread src/container-lifecycle.ts Outdated
Comment on lines +482 to +483
if (!destination.write(chunk) && !destinationUnavailable) {
source.pause();
env: NodeJS.ProcessEnv = process.env,
cwd = process.cwd(),
): void {
assertPrivateRootIsolated(config, { root }, env, cwd, 'protected command output');
env: NodeJS.ProcessEnv = process.env,
cwd = process.cwd(),
): void {
assertPrivateRootIsolated(config, { root }, env, cwd, 'protected command output');
Comment on lines +349 to +352
assertPrivateCommandOutputRootIsolated(config, directory);
const maxAttempts = isGvisorRuntime(config.containerRuntime) ? 2 : 1;
return new ProtectedCommandOutputCaptureSession(
directory,
Comment thread src/container-lifecycle.ts Outdated
Comment on lines +471 to +472
source.once('end', removeDestinationListeners);
source.once('close', removeDestinationListeners);
Comment thread src/container-lifecycle.ts Outdated
Comment on lines +499 to +500
forwardOutput(logsProcess.stdout, process.stdout, 'stdout');
forwardOutput(logsProcess.stderr, process.stderr, 'stderr');
reject: false,
env: getLocalDockerEnv(),
});
break;
Comment thread src/protected-command-output-capture.ts Outdated
Comment on lines +195 to +196
writeAll(state.fd, chunk.subarray(0, length));
state.capturedBytes += length;
Comment on lines +251 to +256
close(): void {
if (this.closed) return;
for (const attempt of this.attempts) {
for (const stream of Object.values(attempt.streams)) {
try {
fs.closeSync(stream.fd);
@lpcox

lpcox commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator

@copilot address review feedback

Copilot AI commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address review feedback

Addressed the review feedback in aff73ad (fix: harden protected command output capture). Added regressions for isolation, bounded draining, retries, short writes, diagnostic separation, and sudo-owner handoff.

Comment thread containers/agent/entrypoint.sh
@lpcox

lpcox commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator

@copilot address review feedback

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Copilot review passed with no inline comments.

@copilot Add the ready-for-aw label to this PR to trigger agentic CI smoke tests.

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Documentation Preview

Documentation has been built for this PR.

Download preview artifact

To view locally:

  1. Download the docs-preview-pr-10169 artifact from the workflow run
  2. Unzip and open index.html in your browser

Built from commit eb8bb38

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

Copilot AI commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address review feedback

Capture-mode entrypoint diagnostics are now routed to the original stderr instead of being discarded (7777f97). Updated the regression to verify diagnostics remain visible while stdout stays command-only (`bbbd073).

Comment thread src/protected-command-output-capture.ts Fixed
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅

📡 OTel tracing validated by Smoke OTel Tracing

@github-actions

Copy link
Copy Markdown
Contributor

🪪 Smoke Copilot BYOK AOAI (Entra) is testing Azure OpenAI BYOK (Entra / GitHub OIDC) mode on this pull request...

@github-actions

Copy link
Copy Markdown
Contributor

💎 Smoke Gemini is crystallizing results on this pull request...

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

Smoke Codex

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Smoke Codex · gpt54 · 13.9 AIC · ⊞ 21.0K · ◷

🔮 The oracle has spoken through Smoke Codex

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ Security Guard completed successfully!

Security Guard

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Security Guard · haiku45 · 4.72 AIC · ⊞ 6.0K · ◷

Generated by Security Guard for #10169

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ Smoke Claude passed

Generated by Smoke Claude for #10169

@github-actions

Copy link
Copy Markdown
Contributor

Cloud Hypervisor + Copilot smoke test

  1. Merged PR lookup: PASS
  2. github.com connectivity: PASS (HTTP 200)
  3. File write/read: PASS
  4. example.com blocked: PASS (HTTP 000)
    Overall: PASS

Cloud Hypervisor + Copilot smoke test by Smoke Cloud Hypervisor
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Claude Engine Validation

Check Status
API ✅ PASS
gh CLI ✅ PASS
File ✅ PASS

Overall result: ✅ PASS

Generated by Smoke Claude for #10169 · claude · haiku45 · 49.1 AIC · ⊞ 6.1K · ◷
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke: PRs "Share a release-pinned guest base across Cloud Hypervisor VM profiles"

  • GitHub MCP ✅
  • github.com 200 ✅
  • File write/read ✅
    PASS — @Copilot @lpcox

📰 BREAKING: Report filed by Smoke Copilot
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Copilot BYOK (Direct Mode) ✅

Results:

  • ✅ MCP connectivity (2 recent PRs fetched)
  • ✅ GitHub.com connectivity (HTTP 200)
  • ✅ File write/read (test file verified)
  • ✅ BYOK inference path active

Mode: Direct BYOK (COPILOT_PROVIDER_API_KEY) via api-proxy → api.githubcopilot.com

Status: PASS

🔑 BYOK report filed by Smoke Copilot BYOK
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke services: ✅ Redis PONG, ✅ pg_isready, ✅ SELECT 1. Overall PASS

🔌 Service connectivity validated by Smoke Services
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

OTEL smoke test

  • ✅ S1 Module load: otel.js exports startRequestSpan, setTokenAttributes, setBudgetAttributes, endSpan, endSpanError, shutdown, isEnabled (+ internals)
  • ✅ S2 Tests: 3 suites, 68/68 passed
  • ✅ S3 Env forwarding: OTEL refs in env-passthrough.ts and api-proxy-env-config.ts
  • ✅ S4 onUsage hook present in token-tracker-http.js
  • ⚠️ S5 Diagnostics: no exported-span data was available to verify. Not treated as a failure.

📡 OTel tracing validated by Smoke OTel Tracing
Add label ready-for-aw to run again

@github-actions github-actions Bot added the smoke-copilot-network-isolation Copilot network-isolation egress smoke test label Oct 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

EGRESS_RESULT allow=pass deny=pass

  • ✅ Allowed: api.github.com → 200
  • ✅ Blocked: example.com failed (curl exit 60, self-signed cert from proxy) → "OK: example.com was blocked"
  • Overall: PASS (@lpcox)

🛡️ Egress verdict from Smoke Copilot Network Isolation
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test

  • fix: verify v0.28.66 enclave smoke and preserve proxy usage ✅
  • chore: upgrade gh-aw to v0.92.5 ✅
  • GitHub PR reads ✅
  • Playwright title check ❌
  • File write/read ✅
  • Build AWF ✅
  • Overall status: FAIL

🔮 The oracle has spoken through Smoke Codex
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

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

Overall: 8/8 ecosystems passed — PASS

Notes: the Bun and Deno curl installers were blocked by the sandbox, so I installed both from npm instead. .NET and Maven needed alternate cache and runtime dirs under /tmp/gh-aw/agent, because the default paths were unavailable.

Generated by Build Test Suite for #10169 · copilot · auto · 28.4 AIC · ⊞ 12K · ◷
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Chroot Version Comparison

Runtime Host Version Chroot Version Match?
Python Python 3.12.15 Python 3.12.15 ✅ YES
Node.js v24.21.0 v2.98.0 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Node.js differs between host and chroot (the chroot reports v2.98.0), so the smoke-chroot label was not added.

Tested by Smoke Chroot
Add label ready-for-aw to run again

@lpcox
lpcox merged commit 85a24fe into main Oct 18, 2026
145 of 152 checks passed
@lpcox
lpcox deleted the copilot/feat-native-protected-command-output-capture branch October 18, 2026 17:14

This branch had an error being deployed

1 failed deployment
aoai-model — dcac7bea Deployed Oct 18, 2026 by lpcox via conclusion #2013
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.

feat: native protected command-output capture outside agent-visible mounts

4 participants