|
| 1 | +--- |
| 2 | +title: Harness runtime config |
| 3 | +description: Reference for the concrete HarnessConfig objects that SDK adapters send to the broker. |
| 4 | +--- |
| 5 | + |
| 6 | +Use this page when you need the exact `HarnessConfig` shape. For a shorter |
| 7 | +guide with Codex, Claude, and OpenCode examples, start with |
| 8 | +[Harnesses](/docs/harnesses). |
| 9 | + |
| 10 | +The broker only executes serializable config data. SDK adapters can help you |
| 11 | +build that data, but the broker does not call SDK functions after spawn and does |
| 12 | +not keep a named harness registry. |
| 13 | + |
| 14 | +## Runtime Categories |
| 15 | + |
| 16 | +| Runtime | Use for | Broker capabilities | |
| 17 | +| --- | --- | --- | |
| 18 | +| `pty` | Terminal-backed CLIs such as Codex or Claude Code | process spawn, PTY stream, input, resize, snapshot, delivery, release | |
| 19 | +| `headless` | Non-terminal sessions such as OpenCode app-server | session delivery, release | |
| 20 | + |
| 21 | +When `runtime` is `headless`, `driver` defaults to `app_server`. |
| 22 | + |
| 23 | +## Terms |
| 24 | + |
| 25 | +| Term | Meaning | |
| 26 | +| --- | --- | |
| 27 | +| Harness config | Concrete `pty` or `headless` JSON the broker can validate and run | |
| 28 | +| Harness adapter | SDK or userland helper that returns a harness config | |
| 29 | +| Named harness | SDK-side shortcut in `new AgentRelay({ harnesses })` | |
| 30 | +| `harnessConfig` | Spawn field carrying the concrete config to the broker | |
| 31 | + |
| 32 | +Named harnesses are local SDK ergonomics. The SDK resolves them to an inline |
| 33 | +`harnessConfig` before the spawn request reaches the broker. |
| 34 | + |
| 35 | +## PTY Config |
| 36 | + |
| 37 | +Use `pty` for terminal-backed coding harnesses: |
| 38 | + |
| 39 | +```typescript |
| 40 | +type PtyHarnessConfig = { |
| 41 | + runtime: 'pty'; |
| 42 | + command: string; |
| 43 | + args: string[]; |
| 44 | + cwd?: string; |
| 45 | + env?: Record<string, string>; |
| 46 | + sessionId?: string; |
| 47 | + delivery?: { |
| 48 | + mode?: 'pty-injection'; |
| 49 | + format?: 'relay-block'; |
| 50 | + }; |
| 51 | + metadata?: Record<string, unknown>; |
| 52 | +}; |
| 53 | +``` |
| 54 | + |
| 55 | +Example: |
| 56 | + |
| 57 | +```typescript |
| 58 | +const harnessConfig = { |
| 59 | + runtime: 'pty', |
| 60 | + command: 'codex', |
| 61 | + args: ['resume', sessionId], |
| 62 | + cwd, |
| 63 | + env: { |
| 64 | + PATH: process.env.PATH ?? '', |
| 65 | + CODEX_HOME: process.env.CODEX_HOME ?? '', |
| 66 | + }, |
| 67 | + sessionId, |
| 68 | +} satisfies ResolvedHarnessConfig; |
| 69 | +``` |
| 70 | + |
| 71 | +The broker owns the spawned process, PTY stream, raw input, resize, snapshots, |
| 72 | +message injection, and release behavior for this runtime. |
| 73 | + |
| 74 | +## Headless App-Server Config |
| 75 | + |
| 76 | +Use `headless` for a non-terminal agent session controlled through an app |
| 77 | +server. OpenCode is the first supported protocol: |
| 78 | + |
| 79 | +```typescript |
| 80 | +type HeadlessAppServerHarnessConfig = { |
| 81 | + runtime: 'headless'; |
| 82 | + driver?: 'app_server'; |
| 83 | + protocol: 'opencode' | string; |
| 84 | + endpoint: string; |
| 85 | + sessionId: string; |
| 86 | + auth?: { |
| 87 | + type: 'bearer' | 'basic' | 'none'; |
| 88 | + token?: string; |
| 89 | + username?: string; |
| 90 | + password?: string; |
| 91 | + }; |
| 92 | + host?: { |
| 93 | + ownership?: 'broker-owned' | 'attached'; |
| 94 | + pid?: number; |
| 95 | + }; |
| 96 | + release?: 'abort' | 'detach' | 'delete'; |
| 97 | + metadata?: Record<string, unknown>; |
| 98 | +}; |
| 99 | +``` |
| 100 | + |
| 101 | +Example: |
| 102 | + |
| 103 | +```typescript |
| 104 | +const harnessConfig = { |
| 105 | + runtime: 'headless', |
| 106 | + protocol: 'opencode', |
| 107 | + endpoint: 'http://127.0.0.1:4096', |
| 108 | + sessionId: 'ses_123', |
| 109 | + host: { ownership: 'attached', pid: 34567 }, |
| 110 | + release: 'abort', |
| 111 | +} satisfies ResolvedHarnessConfig; |
| 112 | +``` |
| 113 | + |
| 114 | +For now, app-server configs are attach-only. `host.ownership: 'broker-owned'` |
| 115 | +is reserved until the broker owns app-server lifecycle supervision. If |
| 116 | +`host.pid` is provided, the broker reports that PID as the harness process ID. |
| 117 | + |
| 118 | +## Adapter Pattern |
| 119 | + |
| 120 | +An adapter should return config data: |
| 121 | + |
| 122 | +```typescript |
| 123 | +function companyClaude(): ResolvedHarnessConfig { |
| 124 | + return { |
| 125 | + runtime: 'pty', |
| 126 | + command: 'claude', |
| 127 | + args: [ |
| 128 | + '--dangerously-skip-permissions', |
| 129 | + '--append-system-prompt', |
| 130 | + 'Follow the company review rubric.', |
| 131 | + ], |
| 132 | + }; |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +Register stable configs by name: |
| 137 | + |
| 138 | +```typescript |
| 139 | +const relay = new AgentRelay({ |
| 140 | + harnesses: { |
| 141 | + 'company-claude': companyClaude(), |
| 142 | + }, |
| 143 | +}); |
| 144 | +``` |
| 145 | + |
| 146 | +Use an inline `harnessConfig` when setup changes per spawn, such as creating a |
| 147 | +Codex session: |
| 148 | + |
| 149 | +```typescript |
| 150 | +const sessionId = await createCodexSession({ cwd, task }); |
| 151 | + |
| 152 | +await relay.spawn('CodexReviewer', 'codex', task, { |
| 153 | + harnessConfig: { |
| 154 | + runtime: 'pty', |
| 155 | + command: 'codex', |
| 156 | + args: ['resume', sessionId], |
| 157 | + cwd, |
| 158 | + sessionId, |
| 159 | + }, |
| 160 | +}); |
| 161 | +``` |
| 162 | + |
| 163 | +Do not copy the whole process environment into `env`, and do not put secrets in |
| 164 | +`metadata`. `env` and `auth` are visible to the broker, so pass explicit |
| 165 | +allowlists. |
| 166 | + |
| 167 | +## Spawn Payloads |
| 168 | + |
| 169 | +`POST /api/spawn` accepts `harnessConfig`: |
| 170 | + |
| 171 | +```json |
| 172 | +{ |
| 173 | + "name": "CodexReviewer", |
| 174 | + "cli": "codex", |
| 175 | + "task": "Review the current diff.", |
| 176 | + "harnessConfig": { |
| 177 | + "runtime": "pty", |
| 178 | + "command": "codex", |
| 179 | + "args": ["resume", "session_123"], |
| 180 | + "sessionId": "session_123" |
| 181 | + } |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +The broker rejects `harnessId`. Relaycast spawns that need custom behavior |
| 186 | +should also send a full inline `harnessConfig`, which keeps each spawn |
| 187 | +self-contained across local, remote, and multi-broker deployments. |
| 188 | + |
0 commit comments