Skip to content

feat: tee execution output to a VFS file; native capture offload for Deep Agents - #2818

Merged
chaliy merged 1 commit into
mainfrom
claude/project-thread-3u3lh2
Nov 3, 2026
Merged

chaliy merged 1 commit into
mainfrom
claude/project-thread-3u3lh2

Conversation

@chaliy

@chaliy chaliy commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Михайло · project thread

Large command output can now be written in full to a file on the virtual filesystem as the command runs, and the Deep Agents backend uses this to keep big execute results greppable.


What changed

  • Core: Execution::output_file(path, max_bytes). Combined stdout and stderr are written, in the order they're produced, into a VFS file that is created (or truncated) before the script runs.
    • The file is bounded by its own byte cap and FsLimits, not by max_stdout_bytes/max_stderr_bytes. The bytes stay in the sandbox, the same as a { script; } > file 2>&1 redirect. capture and the sinks keep the in-memory caps.
    • If the filesystem refuses a write (for example a size limit), the tee stops but the call still succeeds.
    • The script runs in the current shell, so cd, export and functions carry over to the next call.
  • Python: Bash/BashTool execute and execute_sync accept output_file= and max_output_file_bytes= (absolute VFS path, 10 MiB default). These can't be combined with on_output.
  • Deep Agents: BashkitBackend now subclasses BaseSandbox with enable_capture_offload = True, because the framework only offers capture to BaseSandbox types.
    • execute_with_offload/aexecute_with_offload use the native file output instead of the upstream bash wrapper. That wrapper's ( eval … ) subshell would drop shell state between commands.
    • Small output is returned whole and the file is removed. Large output stays at /large_tool_results/<id>, with the same head/tail preview and clip notices as upstream.
    • Every inherited shell or python3 helper, including als/awrite/aedit, is overridden with direct VFS code.

Why

Today, large execute output passes through Bashkit's 1 MB in-memory stdout limit before deepagents saves it to a file, so anything past 1 MB is lost. Upstream's capture wrapper avoids that, but it runs the command in a subshell, which would break Bashkit's persistent shell. Writing to the file natively keeps up to 10 MB and keeps the shell state.

Before / After

Before (main) After
3 MB of output through the agent's execute Cut at 1 MB in memory; deepagents' fallback saves what's left All 3 MB saved at /large_tool_results/<id>; model gets a head/tail preview
cd /tmp; export K=1 in a large-output command, then echo $K $PWD 1 /tmp 1 /tmp (no subshell)
execute_sync(cmd, output_file="/tmp/o.log") n/a /tmp/o.log holds 3,000,004 bytes while stdout is capped at 1 MiB with stdout_truncated=True

Tests:

  • New Rust integration tests in output_file_tests: output beyond the stdout cap plus shell state, the file's own cap and truncation of old content, FsLimits bounding without failing the call, and rejection of relative or missing-parent paths.
  • New Python tests: offload keeps the full output and stays greppable, the inline, capped and timeout cases, and the real create_deep_agent loop offloading 2 MB.
  • pytest crates/bashkit-python/tests: 975 passed, 2 skipped. The three suites that need the bashkit_random_fs fixture crate were not run locally.
  • clippy (including --no-default-features), cargo doc -D warnings --all-features, ruff, just check-okf and just check-doc-links are clean.

Risk

  • Medium-low.
  • This is a new host-requested write path that is not subject to the in-memory output caps. It is bounded by its cap and FsLimits, and is noted in TM-DOS-058.
  • BashkitBackend's base class changes to BaseSandbox. All of that class's execute-based helpers are overridden.

Checklist

  • Tests added or updated
  • Backward compatibility considered (additive APIs; the backend's public methods are unchanged)


<a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1796271858&installation_model_id=447267&pr_number=2818&ref=codesmith_pr_footer&repository=everruns%2Fbashkit&return_to=https%3A%2F%2Fgithub.com%2Feverruns%2Fbashkit%2Fpull%2F2818&signature=c517dc1ec01cdbc675f4814db9093b8d679ea920d29b474070144484426af886"&gt;&lt;picture&gt;&lt;source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"&gt;&lt;source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"&gt;&lt;img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png" width="190" height="28"&gt;
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Generated by Claude Code

…Deep Agents

- core: Execution::output_file(path, max_bytes) writes combined stdout+stderr
  to a VFS file bounded by its own cap and FsLimits, not the in-memory caps
- python: execute/execute_sync accept output_file / max_output_file_bytes
- deepagents: BashkitBackend subclasses BaseSandbox with
  enable_capture_offload and implements execute_with_offload natively, so
  large output stays greppable in /large_tool_results without a subshell
@chaliy chaliy self-assigned this Nov 3, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit 6a69b03 Commit Preview URL

Branch Preview URL
Nov 03 2026, 04:24 AM

@chaliy
chaliy merged commit de51a06 into main Nov 3, 2026
57 checks passed
@chaliy
chaliy deleted the claude/project-thread-3u3lh2 branch November 3, 2026 04:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant