A Cursor-style coding agent backed by Claude and a persistent declaw
sandbox. The agent has five tools for everyday work (shell,
read_file, write_file, ls) plus one special tool:
pty_interactive, which hands your local TTY to the sandbox when
the agent decides it needs human input.
Use case
Sometimes an agent hits a wall that requires a human: an OAuth
login, a sudo password, a TUI license agreement, or a vim session.
Instead of failing or asking you to switch to another terminal, the
agent calls pty_interactive with a reason, you approve with y/n,
and your terminal drops into a live shell inside the sandbox. When
the interactive command finishes, control returns to the agent loop.
What you’ll learn
- Defining tool schemas for the Anthropic Messages API
- Running a multi-turn agent loop with tool-use stop reason
- PTY handoff pattern: raw-mode local TTY forwarding with
SIGWINCH
- Capturing PTY output in a thread-safe
bytearray and feeding it back to the agent
- Trimming captured output to keep the agent’s context bounded
Prerequisites
This example requires a real TTY (sys.stdin.isatty()). It will
not work inside a Jupyter notebook or piped stdin.
How PTY handoff works
When the agent calls pty_interactive:
- The script prints the agent’s reason and the command in a
box and asks
Proceed? [y/N].
- On
y, sbx.pty.create() opens a PTY session. The local TTY
switches to raw mode (tty.setraw), and a SIGWINCH handler
propagates window resizes.
- Every local keystroke is forwarded via
handle.send_stdin().
Every byte from the sandbox streams to stdout via on_data and
is also captured in a bytearray.
Ctrl-D sends exit\n to close the remote shell cleanly.
- The captured output (trimmed to ~4 KB) is returned as the tool
result so the agent knows what happened during the session.
Running it
You are prompted for a goal. Try something like:
The agent will use shell to install gh, then call
pty_interactive(cmd="gh auth login", reason="GitHub OAuth flow requires browser interaction"). You approve, complete the login in
the live terminal, press Ctrl-D, and the agent continues.
Full source
See cookbook/examples/agent-with-pty/main.py in the repo.