--- title: Build a Coding Agent Using OpenCode SDK and Daytona description: Step-by-step guide to building an autonomous coding agent using OpenCode and Daytona sandboxes. --- This guide walks you through running the [OpenCode](https://opencode.ai/) autonomous coding agent inside a secure Daytona sandbox. The OpenCode server runs fully isolated in the sandbox and the script connects to the server using the [OpenCode SDK](https://www.npmjs.com/package/@opencode-ai/sdk). The agent can develop full-stack web apps, write code in any language, install dependencies, and run scripts. It can also start and manage dev servers, and generate [preview links](/docs/en/preview/) for live apps. ### 1. Workflow Overview When you run the script, it creates a Daytona sandbox, installs OpenCode, and starts the OpenCode server inside the sandbox. You send prompts and see responses in your terminal. On exit, the sandbox is deleted. ``` $ npm run start Creating sandbox... Installing OpenCode in sandbox... Preview: https://4096-0626a08c-08e7-4fbe-9799-d160bac66b08.daytonaproxy01.net Press Ctrl+C at any time to exit. User: Create a minimalist collaborative whiteboard app Thinking... 🔨 ✓ Run: Check current directory structure ✓ 3 todos 📝 Add home/daytona/SPEC.md ✓ 2 todos 📝 Add home/daytona/index.html ✓ 1 todos ✓ home/daytona/SPEC.md ✓ https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Outfit:wght@400;500;700&display=swap (text/css; charset=utf-8) ✓ home/daytona/index.html 🔨 ✓ Run: List files in directory 🔨 ✓ Run: Start HTTP server for whiteboard 🔨 ✓ Run: Check if server is running ✓ 0 todos Your minimalist collaborative whiteboard app is ready! **Preview URL:** https://8080-0626a08c-08e7-4fbe-9799-d160bac66b08.daytonaproxy01.net ## SyncBoard - Features **Drawing Tools:** - Pencil (freehand drawing) - Line, Rectangle, Ellipse (shape tools with preview) - Eraser (removes intersecting strokes) **Customization:** - 6 vibrant color presets - 3 stroke widths (thin/medium/thick) - Keyboard shortcuts: `P` `L` `R` `E` `X` for tools, `1` `2` `3` for stroke widths **Collaboration:** - Simulated real-time collaboration with other users - Unique user colors for each participant - Auto-saves to localStorage every 2 seconds **Actions:** - Undo/Redo (stores 20 actions, Ctrl+Z / Ctrl+Shift+Z) - Clear canvas (with confirmation dialog) - Coordinates display in status bar **Design:** - Dark theme with coral/teal accents - Dot grid background pattern - Smooth animations and hover effects - Responsive layout for mobile devices User: Cleaning up... ``` When your task involves running or previewing a web application, the agent can host the app and generate a [preview link](/docs/en/preview/) for you to inspect the live result: SyncBoard collaborative whiteboard app generated by OpenCode agent You can continue interacting with your agent until you are finished. When you exit the program, the sandbox will be deleted automatically. ### 2. Project Setup #### Clone the Repository First, clone the Daytona [repository](https://github.com/daytona/guides.git) and navigate to the OpenCode SDK example: ```bash git clone https://github.com/daytona/guides.git cd guides/typescript/opencode/opencode-sdk ``` #### Configure Environment Get your API key from the [Daytona Dashboard](https://app.daytona.io/dashboard/keys). Copy `.env.example` to `.env` and add your key: ```bash DAYTONA_API_KEY=your_daytona_key ``` #### Local Usage Node.js 18 or newer is required to run this example. Please ensure your environment meets this requirement before proceeding. Install dependencies and run the example: ```bash npm install npm run start ``` The agent will start and wait for your prompt. ### 3. Understanding the Script OpenCode uses a client-server model: The [server](https://opencode.ai/docs/server/) manages coding agents, sessions and configuration. Clients communicate with the server [over HTTP](https://opencode.ai/docs/server/#apis) to run prompts in sessions and receive streamed responses. #### Initialization On startup, the script: 1. Creates a new [Daytona sandbox](/docs/en/sandboxes). 2. Installs OpenCode globally in the sandbox. 3. Starts the OpenCode server in the sandbox. 4. Gets the preview URL for the server and creates a client with that base URL. 5. Enters the readline loop to send prompts to the server and receive streamed responses. 6. On Ctrl+C, deletes the sandbox and exits. #### OpenCode Server The OpenCode server runs inside the sandbox and handles all agent work: LLM calls, tools (bash, write, etc.), and code execution. The script starts it with: ```ts const envVar = injectEnvVar('OPENCODE_CONFIG_CONTENT', opencodeConfig) const command = await sandbox.process.executeSessionCommand(sessionId, { command: `${envVar} opencode serve --port ${PORT} --hostname ${HOSTNAME}`, runAsync: true, }) ``` A custom configuration is injected which includes a system prompt. The system prompt tells the agent it runs in a sandbox, to use `/home/daytona` for file operations, and the preview URL pattern so it can provide correct preview links. #### OpenCode Client The client runs on your machine and talks to the server using the [OpenCode SDK](https://opencode.ai/docs/sdk/). `Session.create` sets up the SDK client and subscribes to the event stream so the client can show tool activity (e.g. “✓ Run: …”, “📝 Add …”) as the agent works: ```ts const client = createOpencodeClient({ baseUrl }) const sessionRes = await client.session.create({ body: { title: 'Daytona query' } }) const sessionId = sessionRes.data?.id if (!sessionId) throw new Error('Failed to create OpenCode session:' + sessionRes.error) const events = await client.event.subscribe() ``` To send a prompt, the client calls `session.prompt` and processes events from the existing stream to show tool activity: ```ts const promptPromise = this.client.session.prompt({ path: { id: this.sessionId }, body: { parts: [{ type: 'text', text: query } satisfies TextPartInput] }, }) for await (const event of takeUntil(this.events.stream, promptPromise)) { printEvent(this.sessionId, event) } ``` #### Main loop When the server is ready, the script creates a session and runs a readline loop: ```ts const session = await Session.create(baseUrl) const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) while (true) { const query = await new Promise((resolve) => rl.question('User: ', resolve)) if (!query.trim()) continue await session.runQuery(query) } ``` The readline loop waits for user input, sends it to the agent, and prints the response. **Key advantages:** - Secure, isolated execution in Daytona sandboxes - OpenCode SDK client in your terminal; server runs in the sandbox - Support for 75+ LLM providers - All agent code execution happens inside the sandbox - Automatic preview link generation for deployed services - Custom agent configuration for Daytona-specific workflows