Documentation for `codex proto` missing

Resolved 💬 3 comments Opened Sep 11, 2025 by davidak Closed Nov 9, 2025

What is the type of issue?

_No response_

What is the issue?

There is no documentation that explains how codex proto should be used.

Where did you find it?

I tried to use codex proto.

This is not helpful:

codex proto -h
Run the Protocol stream via stdin/stdout

Usage: codex proto [OPTIONS]

Options:
  -c, --config <key=value>  Override a configuration value that would otherwise be loaded from `~/.codex/config.toml`. Use a dotted path (`foo.bar.baz`) to override
                            nested values. The `value` portion is parsed as JSON. If it fails to parse as JSON, the raw string is used as a literal
  -h, --help                Print help (see more with '--help')

User story

I want to use codex as an AI Agent to create a paper for Agents4Science. For that i need logs of any input and output including actions. The interactive CLI UI has no option to save such log in the current directory, so i tried to create a wrapper.

I got to a point where the initial prompt is sent just from letting ChatGPT guess the right command from error messages, but then i can't send any additional input. Also, i can't write files and don't have internet access. Parameters seem not to be consistent across modes.

#!/usr/bin/env bash
set -euo pipefail

# ================== INTERNAL PARAMS (your settings kept) ==================
PARAMS=(
  -c 'model="gpt-5"'
  -c 'tools.web_search=true'
  -c 'approval_mode="on-failure"'
  -c 'sandbox_permissions=["workspace-write"]'
  -c 'sandbox_workspace_write.network_access=true'
)
# ========================================================================

[[ $# -eq 1 ]] || { echo "Usage: $0 <start_prompt.md>" >&2; exit 1; }
PROMPT="$1"
[[ -f "$PROMPT" ]] || { echo "error: prompt not found: $PROMPT" >&2; exit 2; }

codex help 2>/dev/null | grep -qE '(^|[[:space:]])proto([[:space:]]|$)' \
  || { echo "error: 'codex proto' not available" >&2; exit 3; }

mkdir -p logs
TS="$(date +%Y-%m-%dT%H-%M-%S)"
LOG="logs/codex-${TS}.log"

# ---- helpers -------------------------------------------------------------
json_escape() {
  sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' \
      -e ':a;N;$!ba;s/\r//g' -e ':b;s/\n/\\n/g;tb'
}
mk_id() { printf '%s%04d' "$(date +%s%3N)" "$RANDOM"; }

send_json() {  # $1 = raw json line
  printf '%s\n' "$1" | tee -a "$LOG" >"$FIFO"
}

make_user_turn() {
  printf '{"id":"%s","op":{"type":"user_turn"}}\n' "$(mk_id)"
}
make_user_input() { # $1=text
  local esc; esc="$(printf '%s' "$1" | json_escape)"
  printf '{"id":"%s","op":{"type":"user_input","items":[{"type":"text","text":"%s"}]}}\n' "$(mk_id)" "$esc"
}

# last call_id from this LOG (prefers latest patch request; falls back to any call_id)
last_call_id() {
  # try specific apply_patch_approval_request first
  local id
  id="$(tac "$LOG" | grep -m1 -o '"apply_patch_approval_request"|"exec_approval_request"' | head -n1 >/dev/null; \
        tac "$LOG" | grep -m1 -o '"call_id":"[^"]*"' | head -n1 | sed -E 's/.*"call_id":"([^"]*)".*/\1/')"
  printf '%s' "${id:-}"
}

approve_patch_last() { # $1=approved true|false, $2=reason
  local cid; cid="$(last_call_id)"
  [[ -n "$cid" ]] || { echo "warn: no call_id found in log" | tee -a "$LOG" >&2; return 1; }
  local esc; esc="$(printf '%s' "${2:-}" | json_escape)"
  send_json "{\"id\":\"$(mk_id)\",\"op\":{\"type\":\"patch_approval\",\"call_id\":\"$cid\",\"approved\":$1,\"reason\":\"$esc\"}}"
}

exec_approval() { # $1=approved true|false, $2=reason (uses last call_id if present)
  local cid; cid="$(last_call_id)"
  local esc; esc="$(printf '%s' "${2:-}" | json_escape)"
  # call_id optional; include if found
  if [[ -n "$cid" ]]; then
    send_json "{\"id\":\"$(mk_id)\",\"op\":{\"type\":\"exec_approval\",\"approved\":$1,\"reason\":\"$esc\",\"call_id\":\"$cid\"}}"
  else
    send_json "{\"id\":\"$(mk_id)\",\"op\":{\"type\":\"exec_approval\",\"approved\":$1,\"reason\":\"$esc\"}}"
  fi
}

# ---- header log ----------------------------------------------------------
{
  echo "=== Codex proto @ ${TS} ==="
  echo "cwd: $(pwd)"
  echo -n "params:"; printf " %s" "${PARAMS[@]}"; echo
  echo "method: proto (JSON lines)"
  echo "--- PROMPT begin ---"
  cat "$PROMPT"
  echo "--- PROMPT end ---"
  echo
} >> "$LOG"

# ---- start proto ---------------------------------------------------------
FIFO="$(mktemp -u /tmp/codex_in.XXXXXX)"; mkfifo "$FIFO"
trap 'rm -f "$FIFO"' EXIT INT TERM

stdbuf -oL codex proto "${PARAMS[@]}" <"$FIFO" | tee -a "$LOG" &
PID=$!

# initial prompt: open turn, then send prompt text
RAW_PROMPT="$(cat "$PROMPT")"
printf "%s\n" "$RAW_PROMPT" >> "$LOG"
send_json "$(make_user_input "$RAW_PROMPT")"

echo "[run_codex] proto started. Logging: $LOG"
echo "[run_codex] Type messages; Ctrl-D to end."
echo "[shortcuts] /turn | /approve [reason] | /reject [reason] | /execok [reason] | /execno [reason]"

# interactive loop
while IFS= read -r line </dev/tty; do
  # empty -> ignore
  [[ -z "$line" ]] && continue

  # slash commands
  case "$line" in
    /turn)
      send_json "$(make_user_turn)"
      continue
      ;;
    /approve\ *)
      reason="${line#"/approve "}"
      approve_patch_last true "$reason" || true
      continue
      ;;
    /approve)
      approve_patch_last true "" || true
      continue
      ;;
    /reject\ *)
      reason="${line#"/reject "}"
      approve_patch_last false "$reason" || true
      continue
      ;;
    /reject)
      approve_patch_last false "" || true
      continue
      ;;
    /execok\ *)
      reason="${line#"/execok "}"
      exec_approval true "$reason"
      continue
      ;;
    /execok)
      exec_approval true ""
      continue
      ;;
    /execno\ *)
      reason="${line#"/execno "}"
      exec_approval false "$reason"
      continue
      ;;
    /execno)
      exec_approval false ""
      continue
      ;;
  esac

  # normal chat: open turn then send input
  printf "HUMAN: %s\n" "$line" >> "$LOG"
  send_json "$(make_user_turn)"
  send_json "$(make_user_input "$line")"
done

wait "$PID" || true
echo "[run_codex] session closed." >> "$LOG"

My initial prompt is this to test writing files and internet search:

Write a haiku about being an AI Agent scientist and save it to a Markdown file. It should contain the current lottery numbers (6 out of 49).

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗