How does opencode handle terminals or processes?
How does open code handle "terminals" or "processes"?
OpenCode handles terminals and processes through several layers, primarily centered around a Pseudo-terminal (PTY) management system and a dedicated Bash tool for the AI agent.
###1. Backend PTY Management
The core logic for terminals resides in packages/opencode/src/pty/index.ts. OpenCode uses the bun-pty library to spawn and manage PTY sessions.
- Session Lifecycle: The
Ptymodule maintains an internal registry (state) of active sessions. Each session contains the PTY process, an output buffer, and a set of active WebSocket subscribers. - Data Handling: When a PTY process produces output (
onData), it broadcasts it to all connected WebSocket subscribers. If no subscribers are connected, it appends the data to a local buffer (up to 2MB) so that it can be replayed when a client reconnects. - Platform Support: Through the
Shellutility (packages/opencode/src/shell/shell.ts), OpenCode detects the preferred shell based on the operating system (e.g., trying to find Git Bash on Windows, or usingzsh/bashon macOS/Linux).
###2. Real-time Communication (WebSockets)
The backend exposes PTY management through an API (packages/opencode/src/server/routes/pty.ts) and a WebSocket endpoint (/pty/:ptyID/connect).
- Real-time Streaming: Users interact with terminals via WebSockets, allowing for low-latency, bi-directional communication (sending keystrokes to the PTY and receiving terminal output).
- Hono Middleware: The server uses Hono with Bun's
upgradeWebSocketto bridge the HTTP API with the PTY management logic.
###3. Frontend Terminal Emulator
The frontend (packages/app/src/components/terminal.tsx) provides the user interface for terminals.
- Ghostty Integration: It uses
ghostty-webas the terminal emulator component. - State Persistence: Terminal buffers can be serialized and persisted to the workspace state. This allows the application to restore the terminal's visible history even after a restart or reload.
- Responsive Resizing: The UI monitors container size and sends
resizecommands (cols/rows) back to the backend PTY process to ensure the shell stays in sync with the window size.
###4. AI Process Execution (Bash Tool)
When the AI agent needs to run a command, it uses the Bash tool (packages/opencode/src/tool/bash.ts).
- Direct Execution: Unlike the interactive TUI terminals, the Bash tool typically uses
child_process.spawnto execute commands directly. - Command Parsing: It uses
web-tree-sitterto parse commands before execution. This allows OpenCode to check for permissions, especially if the command attempts to access directories outside the project root. - Process Tree Cleanup: To ensure no "zombie" processes are left behind, OpenCode implements a recursive
killTreeutility that handles process termination across different platforms (usingtaskkillon Windows and signal groups on Unix-like systems).
###5. LSP and Background Processes
OpenCode also manages processes for Language Servers (LSP) (packages/opencode/src/lsp/server.ts). It automatically spawns and communicates with various language servers (like gopls, pyright, deno, etc.) to provide code intelligence features to the agent.
when does a bash/shell command go to a terminal? does the llm await the output? or it instantly resolves with the terminal id and it's up to the llm to read the output after?
In OpenCode, the handling of bash/shell commands by the AI agent is distinct from the interactive terminal (PTY) used by the human user.
###1. When does a command go to a terminal?
Strictly speaking, commands executed by the AI agent via the bash tool do not go to a terminal (PTY). Instead, they are executed as ephemeral background processes using Node.js/Bun's spawn.
- Agent Commands: Run in an isolated, non-interactive shell environment. They do not share state with the terminal tabs you see in the UI.
- User Commands: When you type in a terminal tab in the OpenCode TUI or Desktop app, that command goes to a persistent Pseudo-terminal (PTY) session managed in
packages/opencode/src/pty/index.ts.
###2. Does the LLM await the output?
Yes, by default, the LLM awaits the output.
In packages/opencode/src/tool/bash.ts, the execute function follows this flow:
- It spawns the process.
- It captures
stdoutandstderrinto a local buffer. - It awaits a Promise that resolves only when the process emits an
exitorerrorevent. - Once the process completes, it returns the accumulated output string to the LLM.
###3. Can it "instantly resolve"?
The tool only "instantly resolves" if the LLM specifically chooses to background the process using standard shell syntax (e.g., &).
- Synchronous:
npm install— The agent waits until the installation is finished before continuing the conversation. - Asynchronous:
node server.js &— The shell launches the process and exits immediately. Thebashtool then resolves instantly, returning whatever initial output was produced (often just a job ID), and the agent moves to the next step while the server continues running in the background of the operating system.
###4. Why not use the interactive terminal?
OpenCode's design separates agent execution from the user's interactive terminal for several reasons:
- Non-Interactivity: The agent is instructed to use non-interactive flags (like
npm init -y) because it cannot "see" or respond to interactive prompts (likey/nconfirmations) that might appear in a PTY. - Concurrency: The agent can run multiple commands in parallel without messing up the user's current shell prompt or command history.
- Safety: Capturing output directly as a string is more reliable for the LLM than trying to scrape a terminal buffer which might contain complex ANSI escape codes and formatting.
###Summary
| Feature | Agent bash Tool | User's Terminal (PTY) |
|---|---|---|
| Persistence | Ephemeral (process dies after command) | Persistent (shell stays alive) |
| Output | Captured as text for LLM | Streamed to UI via WebSockets |
| Interactivity | Non-interactive only | Full interactivity |
| Blocking | Blocks LLM until exit (unless & used) | Never blocks LLM |
END OF POST