The Harness That Gives AI Agents Real Hands: OpenHands' PTY Sandbox, Rebuilt in 500 Lines of Java
An agent that cannot safely run real commands with persistent state is a toy. The PTY session, sentinel protocol, and output trimming shown here are the minimum viable architecture for any agent that modifies files, runs CI, or operates a terminal — and virtual threads make it cheap to run thousands of such sessions concurrently on a handful of OS threads.
Three prior systems gave an agent a thinking loop, deterministic control flow, and self-editing memory — but none could safely touch a real filesystem or run commands with persistent state. OpenHands fills that gap with a Harness architecture built on four physical pillars. Container isolation makes `rm -rf /` physically impossible. A long-lived bash session inside a tmux pane means `cd` and `export` persist across commands, something `bash -c` per command can never do. A sentinel protocol injected into the shell prompt captures real exit codes and marks command boundaries precisely.
When a command produces 100,000 lines of output, a trimmer keeps the head, tail, and every line containing Error or Traceback while discarding the middle — preventing token-window amnesia without losing critical diagnostics. A defensive parser never crashes on malformed model output; it degrades gracefully into an InvalidFormatObservation that the model reads and uses to self-correct on the next turn. The entire system communicates through a typed event stream where Actions and Observations alternate, making every step auditable and replayable.
The article strips OpenHands' production codebase down to the essential 5% — the event system, the persistent PTY session with its sentinel protocol, and the output safeguards — then rebuilds it in about 500 lines of zero-dependency Java 25 code using virtual threads, sealed interfaces, and record pattern matching. A Groovy version collapses the same logic into a single 333-line file, demonstrating where dynamic languages win for prototyping and where strong typing pays off for production harnesses.
The four-pillar Harness — isolation, session persistence, trimming, guardrails — is not OpenHands-specific; it is the checklist any agent framework must satisfy before it can run unattended in production.
The sentinel protocol is a clever abuse of the shell's own prompt-rendering lifecycle: bash evaluates `$?` and `$(pwd)` at PS1 render time, so the metadata is always truthful and requires no separate side-channel.
OpenHands' real codebase is 90% engineering scaffolding (Docker SDK, k8s, frontend, telemetry); the architectural soul lives in roughly 5% of the code — the event system, the PTY session, and the output safeguards.
The jump from CodeAct-era ` ```bash ` block parsing to strongly-typed `TerminalAction` tool calls marks the moment agent-command protocols stopped being text-parsing problems and became schema-enforced contracts.
Virtual threads are a perfect fit for agent runtimes: command execution is pure blocking I/O, and collapsing thousands of waiting tasks onto a small carrier pool is exactly the problem Project Loom solved.
The Groovy-to-Java comparison surfaces a real engineering tension: dynamic map-and-closure styles erase ceremony and speed up prototyping, but sealed types catch event-dispatch bugs at compile time — a property that grows in value as the number of action types increases.