* docs: turn README into an index and move reference detail to docs/guide The README had grown to 393 KB because every change appended its notes there. The text moves verbatim into 18 topic guides under docs/guide/; only headings are re-leveled and relative links re-pointed. The README keeps the introduction, a quick start and a guide index. Contributor test notes move to CONTRIBUTING.md. * docs: send user docs to topic guides and budget the root README The rule 'update README.md for user-facing changes' is what grew the README. Guidance now points to docs/guide/, and check_agent_guidance.py fails when the root README exceeds 16 KiB (AG003). * chore: remove unreferenced screenshots and finished plan notes pr-build/ holds two PR screenshots (3 MB) from #1986. plans/ holds the plan for the merged #4049. docs/SKILL_NAME_CONFLICT_FIX.md is a February change note. Nothing references them. * docs: route always-loaded AGENTS.md files to scoped guides * docs(agents): cut scoped guides to scope, verify, traps, decisions Each AGENTS.md keeps four kinds of content: the scope of the directory, the verification commands, traps a local test run does not show, and decisions the code cannot state. Values, field lists, function inventories, test names, issue numbers and history are deleted, because the code, the tests and git state them. Search and fetch providers share one community guide. Four guides with no remaining content are removed. The size check now allows 6 KiB per file (hard limit 8 KiB) and 24 KiB per chain (hard limit 32 KiB). * style: format frontend guide table and guidance test imports --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
BoxLite backend
Runs each DeerFlow sandbox as a BoxLite micro-VM — a daemonless, OCI-native VM with its own kernel (libkrun/KVM on Linux, Hypervisor.framework on macOS). Motivated by the resource/cold-start pain with the default AIO Docker sandbox in #3439 and #3213; discussion in #3936.
Configuration
sandbox:
use: deerflow.community.boxlite:BoxliteProvider
image: python:3.12-slim # any OCI image (default: python:3.12-slim)
memory_mib: 1024 # per-box memory cap (optional)
cpus: 2 # per-box vCPUs (optional)
replicas: 3 # active + warm VM cap per gateway process (default: 3)
idle_timeout: 600 # warm VM idle seconds before stop; 0 disables
health_check_skip_seconds: 0.0 # optional low-latency mode: skip reclaim
# health checks for recent releases; 0 keeps
# reliability-first validation (default: 0.0)
environment: # injected into every command
PYTHONUNBUFFERED: "1"
Install the optional runtime before selecting this provider:
pip install "deerflow-harness[boxlite]"
The boxlite package is an optional DeerFlow harness extra, not part of the
default install. It is also limited to the host platforms and architectures
where BoxLite publishes wheels and can boot micro-VMs. Unsupported development
hosts, such as Windows, should use another sandbox provider or run DeerFlow from
a supported Linux/macOS environment.
Host requirement: BoxLite boots micro-VMs, so a Linux host needs KVM — i.e. nested virtualization when DeerFlow runs inside a cloud VM. macOS uses Hypervisor.framework. This is the main deployment constraint to weigh vs. the container-based providers.
Design
DeerFlow's Sandbox contract is synchronous; BoxLite's SDK is async-native and
its box handles are event-loop-affine. The provider owns one private asyncio
loop on a daemon thread and marshals every coroutine onto it via
run_coroutine_threadsafe. BoxLite boxes are named deterministically from
user_id:thread_id, released into an in-process warm pool after each agent turn,
and reclaimed by the same thread on the next acquire.
Startup recovery uses the async runtime's list_info() and get() on that same
private loop. Recovered native box handles collect stdout and stderr concurrently
and return the SDK's ExecResult, matching fresh SimpleBox commands. Recovery
does not use the thread-affine greenlet sync API or create replacements for boxes
that disappear between listing and lookup; normal acquisition owns creation.
| File | Role |
|---|---|
provider.py |
SandboxProvider lifecycle + the private-loop bridge |
box.py |
Sandbox adapter; execute_command + file ops |
Contract coverage
The full Sandbox surface is implemented. File operations run as shell commands
inside the box and reuse deerflow.sandbox.search, mirroring e2b_sandbox:
execute_command—sh -lc, with per-call env and timeout.read_file/write_file/update_file—catand chunkedbase64(binary-safe, no arg-size limit).download_file— 100 MB cap, restricted to the/mnt/user-dataprefix.list_dir/glob/grep—find/grepwith busybox-portable flags; results filtered/capped in Python.
Command timeouts pass the same deadline to the SDK and event-loop bridge. A
bridge deadline returns Error: command timed out followed by Exit Code: 124.
BoxLite's SimpleBox.exec converts SDK wait failures to ExecResult(exit_code=-1);
if that result arrives before the bridge deadline, DeerFlow preserves -1.
The bridge handler catches Python's builtin TimeoutError, which is distinct
from the SDK's boxlite.TimeoutError.
The provider creates /mnt/user-data/{workspace,uploads,outputs} and
/mnt/skills on box start so those virtual paths resolve natively.
tests/test_boxlite_provider.py covers recovery, health checks, commands, and
shutdown without the optional SDK. With deerflow-harness[boxlite] installed,
tests/test_boxlite_recovery_sdk.py additionally checks the installed SDK's native
command signature and SimpleBox result semantics against an offline native
runtime double. These contract tests do not start a VM or require KVM/HVF.
Warm-pool capacity is governed by sandbox.replicas across active + warm VMs.
sandbox.idle_timeout controls how long released warm VMs stay running; 0
disables idle reaping. Active boxes are never evicted to satisfy the cap.
Status
Verified end-to-end against a live box (provider resolution → execute_command
→ file ops) on macOS/HVF. Linux/KVM validation and benchmarks vs. the AIO
sandbox are tracked in
#3936.