Chapter I · AI agent stack

Claude Code via tmux Relay

Tested on
Claude Code first-party OAuth, Opus 4.8, tmux, OpenClaw 2026.6.2 host workflow
Last updated
2026-06-29

How to let OpenClaw drive Claude Code through an interactive tmux session, both for second-opinion review and for scripted one-shot calls, keeping Claude in its first-party harness rather than treating it as a raw backend.

Update (late June 2026): Anthropic reverted the print-mode change this guide was written around. claude -p / print mode works again, so this relay is no longer required and claude -p is fine for scripted automation. Keep this guide anyway: the tmux relay is still the better lane when you want a recoverable, human-attachable, first-party Claude Code session (interactive review with visible permission prompts), and the one-shot bridge below is a resilient JSON-envelope fallback. The June 2026 breakage is kept below as the war story that explains why the relay exists.

There are two lanes here:

  1. Interactive review sessions - a long-lived named tmux session you send prompts to and capture answers from. Good for second opinions and cross-review.
  2. One-shot relay - a script that spins up a throwaway tmux session per request, gets the answer as a JSON envelope, and tears the session down. It is a drop-in replacement for claude -p --output-format json that does not depend on print mode at all, and it is what one-liner wrappers and cron jobs call.

The first lane is the original pattern. The second is what made the whole claude -p script ecosystem survive the June 2026 print-mode outage (since reverted) and stays useful as insurance against a repeat.

What this is

Claude Code can run as a normal interactive terminal app inside tmux. OpenClaw or another local orchestrator can then:

This is a strong Claude escalation lane for code review and architecture review when you want a real, recoverable second opinion from Claude Code. With print mode working again you can also call claude -p directly for simple scripted reviews; the relay wins when you want an attachable session and visible permission prompts.

ACP still has a place when you explicitly need an ACP endpoint, but the tmux relay is simpler to inspect, easier to recover, and closer to how Claude Code is meant to be used.

The June 2026 lesson

The lesson is not just “tmux works.” The real lesson is that Claude Code should stay in Claude Code.

In April 2026, direct Claude subscription OAuth through third-party harnesses stopped being reliable. ACPX was the first working repair because it launched Claude Code instead of impersonating it.

By the June 2026 notes, claude -p / print-mode automation briefly had a second problem: it drew from Claude’s separate Usage bucket. Anthropic reverted that in late June 2026, so claude -p is fine again. Driving an interactive Claude Code session through tmux is still the more recoverable lane when OpenClaw or Codex needs Claude, but it is now a choice, not a forced workaround.

That keeps:

Why this way

The old post-April-2026 fix was “run Claude Code through ACPX.” That avoided direct third-party OAuth use, but it still turned Claude Code into a subprocess endpoint and often pushed users toward one-shot print-mode habits.

The tmux relay keeps the interaction model honest:

Needtmux relay behavior
First-party authClaude Code owns OAuth in ~/.claude/
Human recoveryAttach with tmux attach -t <session>
OpenClaw controlUse ordinary shell tools to start, send, capture, and stop
Review safetyLaunch in --permission-mode plan for read-only critique
Coding smoke testsUse --permission-mode acceptEdits only in contained workspaces
One-shot promptsPaste a prompt file into the existing session instead of claude -p

The orchestrator is not pretending Claude is just another stateless model. It is delegating to the Claude Code harness and collecting the result.

Prerequisites

Verify Claude Code is using the first-party provider:

claude auth status

The useful fields are:

{
  "loggedIn": true,
  "authMethod": "oauth_token",
  "apiProvider": "firstParty"
}

Do not copy token values into notes, prompts, issues, or public docs.

Before / After

Before: OpenClaw routes Opus work through direct Claude backends, claude -p, or ACPX one-shots. Recovery is awkward, permission prompts can fail in non-interactive sessions, and the review lane is easy to confuse with a normal model backend.

After: Claude Code runs in a named tmux session. OpenClaw sends bounded review prompts, captures the answer, and uses the result as review evidence. Codex and Claude can review each other’s work without sharing one context window or bypassing either harness.

Implementation

1. Start a review session

Use plan mode for second-opinion review:

tmux new-session -d -s claude-code-review \
  'cd /path/to/repo && claude --model opus --permission-mode plan --name openclaw-review'

On first launch in a new workspace, Claude Code may stop at the trust prompt. Attach once and answer it manually:

tmux attach -t claude-code-review

Detach with Ctrl-b then d.

2. Send a prompt

For a short prompt:

tmux send-keys -t claude-code-review:0.0 -l -- \
  "Review the current git diff for correctness, security risk, missing tests, and unclear assumptions. Return findings first with file paths and line numbers where possible. Do not edit files."
tmux send-keys -t claude-code-review:0.0 Enter

For a longer one-shot prompt, write the prompt to a local file and paste it into the tmux session:

tmux load-buffer /tmp/claude-review-prompt.txt
tmux paste-buffer -t claude-code-review:0.0 -d
tmux send-keys -t claude-code-review:0.0 Enter

That is still an interactive Claude Code session. It is not claude -p.

3. Capture the result

tmux capture-pane -t claude-code-review:0.0 -p -S -200

For long reviews, capture the full pane:

tmux capture-pane -t claude-code-review:0.0 -p -S -

Save captured output as a local review artifact, not as canonical memory. Promote only durable lessons or verified fixes through your normal handoff process.

4. Use a wrapper script

A public-safe wrapper lives at ../templates/ai-stack/claude-tmux-relay.sh. Copy or adapt it into your private workspace scripts:

CLAUDE_TMUX_SESSION=claude-code-review \
CLAUDE_WORKSPACE=/path/to/repo \
CLAUDE_PERMISSION_MODE=plan \
templates/ai-stack/claude-tmux-relay.sh start

templates/ai-stack/claude-tmux-relay.sh send \
  "Review the current git diff. Findings first. Do not edit files."

templates/ai-stack/claude-tmux-relay.sh capture -200

One-shot relay: print-mode behavior without print mode

Print mode works again as of late June 2026, so this bridge is now a resilience choice rather than a necessity. It earned its keep during the June 2026 outage, when claude -p --output-format json started failing auth (401 on print mode while interactive sessions still worked) - and it is still worth having, because a relay caller never depends on print mode at all. If you want that insurance, build a one-shot bridge that drives the real TUI through tmux and emits the same JSON envelope the callers already parse.

The bridge does this per request:

  1. Spawn a throwaway session. Name it <prefix>-<pid>-<timestamp> so concurrent calls never collide, launch claude --model <model> --permission-mode <mode> in the requested working directory.
  2. Auto-answer the trust prompt only. Poll capture-pane for the folder-trust prompt text and send Enter once. Nothing else gets auto-approved.
  3. Ask for the answer as a file, not chat. Wrap the user prompt with an instruction that the task is not complete until the model uses its Write tool to put ONLY the final answer at an exact temp file path. Chat output scrolls, wraps, and interleaves with the TUI; a file is unambiguous.
  4. Paste, then submit with C-m. Send the prompt via load-buffer + paste-buffer, sleep briefly, then send-keys C-m. A symbolic Enter after a bracketed paste sometimes leaves the text sitting at the prompt; C-m has been reliable.
  5. Poll the result file for stability. Wait until the file exists, is non-empty, and its size has been stable for a second. Claude may write incrementally.
  6. Refuse permission prompts. If the pane shows Do you want to proceed? or similar, fail with the pane tail in the error instead of auto-approving. A one-shot text task should never need tool escalations beyond the single Write; if it asks, something is wrong.
  7. Emit the envelope. Print {"is_error": false, "result": "<file contents>", ...} on success, {"is_error": true, "result": "<reason>"} on failure, then kill the session. Existing claude -p callers keep working unchanged.

Two robustness details that earn their keep:

One-liner wrappers

With the bridge in place, task-specific one-liners become trivial bash: build a system prompt, feed stdin or a file through the relay, gate on the envelope, print .result. The pattern:

#!/usr/bin/env bash
# opus-rewrite - one-shot Opus text task via the tmux relay
set -euo pipefail
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1

TMUX_RELAY="$HOME/.local/share/agent-scripts/claude-tmux-oneshot.py"
RELAY_MODEL="claude-opus-4-8"
RELAY_TIMEOUT="${OPUS_RELAY_TIMEOUT:-600}"

SYSTEM_PROMPT="You are a precise rewriting assistant. Output ONLY the rewritten text, no commentary."

# stdin, file, or --prompt "text"
if [[ "${1:-}" == "--prompt" ]]; then
  shift; CONTENT="$*"
elif [[ -n "${1:-}" ]] && [[ -f "$1" ]]; then
  CONTENT=$(cat "$1")
elif [[ ! -t 0 ]]; then
  CONTENT=$(cat)
else
  echo "Usage: opus-rewrite [file | --prompt 'text' | stdin]" >&2; exit 1
fi

ENVELOPE=$(printf '%s\n\n%s' "$SYSTEM_PROMPT" "$CONTENT" \
  | timeout $((RELAY_TIMEOUT + 60)) "$TMUX_RELAY" \
      --model "$RELAY_MODEL" \
      --cwd "$HOME/workspace" \
      --timeout "$RELAY_TIMEOUT" \
      --session-prefix "opus-rewrite")

if [[ "$(jq -r 'if .is_error == false then "ok" else "err" end' <<<"$ENVELOPE" 2>/dev/null || echo err)" != "ok" ]]; then
  echo "opus-rewrite: relay error: $(jq -r '.result // empty' <<<"$ENVELOPE" | head -c 300)" >&2
  exit 1
fi

RESULT=$(jq -r '.result // empty' <<<"$ENVELOPE")
[[ -n "$RESULT" ]] || { echo "opus-rewrite: empty result" >&2; exit 1; }
printf '%s\n' "$RESULT"

On this stack a handful of task-specific wrappers in ~/bin follow this exact shape, and cron jobs call them like any other shell command, with the relay timeout bumped for batch work.

Watch the jq gate carefully. jq '.is_error // true' looks right and is wrong: // fires on false as well as null, so a successful envelope (is_error: false) would read as an error. Compare explicitly with if .is_error == false.

One migration note: converting wrappers is per-script surgery. Every script that shells out to claude --print or claude -p keeps failing until it is moved onto the relay, and the failures look like auth problems, not code problems. Inventory your wrappers (grep -l 'claude.*-p\|--print' ~/bin/*) and migrate the ones you actually use first.

OpenClaw agent usage

OpenClaw should treat Claude Code tmux as a tool lane, not as the main model or a fallback model.

Start or reuse the session from an OpenClaw shell-capable tool, cron job, or private skill:

CLAUDE_TMUX_SESSION=claude-code-review \
CLAUDE_WORKSPACE=/path/to/repo \
CLAUDE_PERMISSION_MODE=plan \
templates/ai-stack/claude-tmux-relay.sh start

Send a bounded review prompt:

templates/ai-stack/claude-tmux-relay.sh send \
  "Review the current git diff for correctness, security risk, missing tests, and unclear assumptions. Findings first. Do not edit files."

Capture the pane and summarize the actionable findings back into the OpenClaw turn:

templates/ai-stack/claude-tmux-relay.sh capture -300

For longer prompts, OpenClaw should write a local prompt file and use send-file:

templates/ai-stack/claude-tmux-relay.sh send-file /tmp/claude-review-prompt.txt

Do not put Claude Code in agents.defaults.model.fallbacks. The fallback chain is for compatible model backends, not an interactive review harness.

Codex usage

Codex can use the same relay after it implements or reviews a change. The useful loop is:

  1. Codex makes the change and runs the normal tests.
  2. Codex writes a review prompt that includes the task, acceptance criteria, and current diff focus.
  3. Codex sends that prompt to Claude Code through tmux.
  4. Codex captures Claude’s response, validates each finding, applies only the real fixes, and reruns tests.

Example from a Codex repo session:

git diff --stat > /tmp/claude-review-prompt.txt
printf '\nReview this change. Findings first. Do not edit files.\n' >> /tmp/claude-review-prompt.txt

CLAUDE_TMUX_SESSION=claude-code-review \
CLAUDE_WORKSPACE="$PWD" \
CLAUDE_PERMISSION_MODE=plan \
templates/ai-stack/claude-tmux-relay.sh start

templates/ai-stack/claude-tmux-relay.sh send-file /tmp/claude-review-prompt.txt
templates/ai-stack/claude-tmux-relay.sh capture -300

If Claude reports a finding, Codex owns the verification. Claude’s output is review evidence, not an automatic patch.

Codex + Claude cross-review

Use two independent harnesses when the work deserves adversarial review:

  1. Codex implements or reviews the first pass in its normal repo harness.
  2. OpenClaw sends Claude Code a tmux prompt asking for read-only review of the diff, plan, or test failure.
  3. OpenClaw captures Claude’s findings as a local artifact.
  4. Codex reviews Claude’s findings, rejects weak ones, fixes valid ones, and runs tests.
  5. For high-risk changes, send the final diff back through Claude Code in plan mode for a final read-only pass.

Prompt shape:

You are the independent reviewer. Review the current git diff only.

Focus on:
- correctness bugs
- security risk
- missing tests
- behavior changes not reflected in docs
- assumptions the implementing agent may have missed

Return only actionable findings first. Include file paths and line numbers when possible.
Do not edit files.

This keeps each harness honest. Codex gets the fast build/test loop. Claude Code gets a bounded second-opinion lane. OpenClaw owns routing, evidence, and memory handoff.

Verification

Run a no-tools probe:

tmux new-session -d -s claude-code-tmux-test \
  'cd /path/to/repo && claude --model opus --permission-mode plan --name openclaw-tmux-test'

tmux send-keys -t claude-code-tmux-test:0.0 -l -- "Reply exactly: TMUX_OK. Do not use tools."
tmux send-keys -t claude-code-tmux-test:0.0 Enter

tmux capture-pane -t claude-code-tmux-test:0.0 -p -S -100

Expected result: the captured pane includes TMUX_OK.

For a contained coding smoke test, create a scratch directory and use acceptEdits only there:

mkdir -p /tmp/claude-tmux-coding-test
tmux new-session -d -s claude-code-tmux-code-test \
  'cd /tmp/claude-tmux-coding-test && claude --model opus --permission-mode acceptEdits --name tmux-code-test'

Do not use acceptEdits in a production repo unless that is the explicit task.

Gotchas

  1. A fresh workspace may block on the trust prompt. Attach once, answer the prompt, then detach.

  2. tmux ls exits non-zero when no server exists. Scripts should treat that as “no sessions yet,” not as a broken host.

  3. Plan mode is the default for review. It keeps Claude in a critique lane and avoids surprise edits.

  4. One-shot does not have to mean print mode. For interactive sessions, use a prompt file plus tmux paste-buffer. For scripted callers, the one-shot relay bridge above avoids print mode entirely. claude -p works again as of late June 2026, so it is a fine option for simple scripted calls; reach for the relay when you want recoverable, attachable sessions.

  5. Capture output is evidence, not memory. Review artifacts can be noisy. Promote only durable, verified facts through memory handoffs.

  6. Keep ACPX documented as an explicit compatibility lane. Some OpenClaw setups still need ACP. Do not present ACPX as the only path to Claude Code.

  7. jq '.is_error // true' is a bug. The // alternative operator treats false like null, so a healthy envelope reads as an error. Use if .is_error == false then ... end. This one shipped and bit us.

  8. Submit pasted prompts with C-m, not Enter. After a bracketed paste, a symbolic Enter keystroke sometimes leaves the prompt text unsubmitted in the TUI input. A short sleep then send-keys C-m submits reliably.

  9. Never auto-approve tool permission prompts in the one-shot lane. The bridge auto-answers exactly one prompt, folder trust, and fails loudly with the pane tail on anything else. A text task that suddenly wants shell access is a prompt-injection smell, not an inconvenience to click through.

  10. Wrapper migration is per-script (historical, June 2026). During the June 2026 outage, print mode dying did not break your scripts in one obvious place; it broke each claude -p caller individually with what looked like an auth flake. Anthropic has since reverted that, so claude -p callers work again. This note stays as the recovery playbook if print mode ever regresses: inventory wrappers (grep -l 'claude.*-p\|--print' ~/bin/*) and migrate the ones you use.

Templates