# Differences in Secret Names and Response Extraction Paths for Claude Code, Codex, OpenCode, and CodeLayer Agent Runners

> Compare secret names and response extraction paths for Claude Code, Codex, OpenCode, and CodeLayer agent runners. Understand API keys and data handling.

- Repository: [HumanLayer/skills](https://github.com/humanlayer/skills)
- Tags: comparison
- Published: 2026-09-13

---

**Claude Code and CodeLayer require `ANTHROPIC_API_KEY`, Codex requires `OPENAI_API_KEY`, and OpenCode supports either provider, while response extraction ranges from native file output in Codex to complex JSON parsing in Claude Code and ANSI color stripping in CodeLayer.**

The `humanlayer/skills` repository defines standardized GitHub Actions workflows for orchestrating LLM agents, with each runner template in [`plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md) specifying distinct secret names and output processing logic. Understanding these differences ensures your CI pipelines correctly authenticate with providers and reliably extract the final assistant response for pull request bodies.

## Secret Name Variations by Agent Provider

Each agent runner expects a specific environment variable for API authentication based on its underlying LLM provider.

### Anthropic-Based Runners (Claude Code and CodeLayer)

Both **Claude Code** and **CodeLayer** integrate with Anthropic's API, requiring the `ANTHROPIC_API_KEY` secret. In the workflow definitions, this appears as:

```yaml
env:
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

```

According to the agent runner templates, Claude Code references this at line 28, while CodeLayer specifies it at line 17 for the environment block and line 23 for the secret injection.

### OpenAI-Based Runners (Codex)

The **Codex CLI** runner exclusively uses OpenAI's API, expecting the `OPENAI_API_KEY` secret. The workflow references this at line 64:

```yaml
env:
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

```

### Provider-Agnostic Runners (OpenCode)

**OpenCode** supports multiple providers, typically defaulting to Anthropic or OpenAI depending on configuration. The example implementation in the repository uses `ANTHROPIC_API_KEY` at line 90, though the runner can accept `OPENAI_API_KEY` when configured for OpenAI models.

## Response Extraction Path Implementations

The method for isolating the final assistant message varies significantly across runners due to differences in output formats.

### Claude Code: Stream-JSON Parsing

Claude Code outputs **stream-JSON** format when using `--output-format stream-json`, requiring multi-stage extraction to isolate text content. As implemented in [`agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/agent-runner-templates.md) at lines 44-48, the extraction pipeline filters for the last assistant message containing text content:

```bash
cat /tmp/agent-output.txt | grep '^{' | jq -s '[.[] | select(.type == "assistant" and .message.content)] | last | .message.content[] | select(.type == "text") | .text' -r > /tmp/pr-body.md

```

This command first filters JSON lines, then selects the final assistant message and extracts nested text objects.

### Codex: Native Output Flag

Codex eliminates post-processing complexity by providing the `--output-last-message` flag, which writes the final response directly to a specified file. At line 73 of the templates, the implementation simply passes:

```bash
codex exec "$PROMPT" --output-last-message /tmp/pr-body.md

```

No additional extraction commands are required, as the CLI handles file output internally.

### OpenCode: Structured JSON Extraction

When invoked with `--format json`, OpenCode produces structured output requiring extraction from a `.messages` array. Lines 7-10 of the templates demonstrate filtering for the last assistant role entry:

```bash
cat /tmp/agent-output.txt | jq -r '.messages | map(select(.role == "assistant")) | last | .content' > /tmp/pr-body.md

```

This approach assumes the JSON output contains a top-level messages array with role-based entries.

### CodeLayer: ANSI Color Stripping

CodeLayer emits **ANSI-colored plain text** rather than structured JSON, necessitating either color code removal or custom parsing. The basic extraction at lines 42-46 uses `sed` to strip escape sequences:

```bash
cat /tmp/agent-output.txt | sed 's/\x1b\[[0-9;]*m//g' > /tmp/pr-body.md

```

Alternatively, the repository provides a custom parser script located at [`ci-scripts/codelayer-output.ts`](https://github.com/humanlayer/skills/blob/main/ci-scripts/codelayer-output.ts), invoked via Bun for more sophisticated processing:

```bash
bun ci-scripts/codelayer-output.ts < /tmp/agent-output.txt > /tmp/pr-body.md

```

## Complete Workflow Examples

Below are production-ready GitHub Actions snippets demonstrating secret injection and response extraction for each agent.

### Claude Code Configuration

```yaml
- uses: actions/setup-node@v4
  with:
    node-version: 24
- run: npm install -g @anthropic-ai/claude-code
- name: Run Claude Code
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
  run: |
    claude -p "$PROMPT" \
      --permission-mode bypassPermissions \
      --output-format stream-json \
      --verbose \
      2>&1 | tee /tmp/agent-output.txt

- name: Extract PR body
  run: |
    cat /tmp/agent-output.txt \
      | grep '^{' \
      | jq -s '[.[] | select(.type == "assistant" and .message.content)] | last | .message.content[] | select(.type == "text") | .text' -r \
      > /tmp/pr-body.md

```

### Codex CLI Configuration

```yaml
- uses: actions/setup-node@v4
  with:
    node-version: 24
- run: npm install -g @openai/codex
- name: Login Codex
  env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
  run: printenv OPENAI_API_KEY | codex login --with-api-key
- name: Run Codex
  run: |
    codex exec "$PROMPT" \
      --cd "$GITHUB_WORKSPACE" \
      --ask-for-approval never \
      --sandbox danger-full-access \
      --json \
      --output-last-message /tmp/pr-body.md \
      2>&1 | tee /tmp/agent-output.txt

```

### OpenCode Configuration

```yaml
- uses: oven-sh/setup-bun@v2
- run: bun install -g opencode-ai
- name: Run OpenCode
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
  run: |
    opencode run "$PROMPT" \
      --dir "$GITHUB_WORKSPACE" \
      --model anthropic/claude-sonnet-4-5 \
      --format json \
      --dangerously-skip-permissions \
      2>&1 | tee /tmp/agent-output.txt

- name: Extract PR body
  run: |
    cat /tmp/agent-output.txt \
      | jq -r '.messages | map(select(.role == "assistant")) | last | .content' \
      > /tmp/pr-body.md

```

### CodeLayer Configuration

```yaml
- uses: oven-sh/setup-bun@v2
- name: Run CodeLayer
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    FORCE_COLOR: "3"
  run: |
    bunx @humanlayer/cli@latest codelayer \
      --provider anthropic \
      --model claude-opus-4-8 \
      --thinking high \
      --prompt "$PROMPT" \
      2>&1 | tee /tmp/agent-output.txt

- name: Extract PR body
  run: |
    cat /tmp/agent-output.txt | sed 's/\x1b\[[0-9;]*m//g' > /tmp/pr-body.md

```

## Summary

- **Claude Code** and **CodeLayer** both require the `ANTHROPIC_API_KEY` secret, while **Codex** exclusively uses `OPENAI_API_KEY` and **OpenCode** supports either provider.
- **Response extraction complexity** varies significantly: Codex requires no post-processing due to its `--output-last-message` flag, while Claude Code needs complex `jq` filters for stream-JSON, OpenCode uses simple JSON path extraction, and CodeLayer requires ANSI color code stripping.
- The **agent runner templates** located at [`plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/plugins/design-control-loop/skills/design-control-loop/references/agent-runner-templates.md) serve as the authoritative reference for implementing these workflows in the `humanlayer/skills` repository.
- **CodeLayer** uniquely offers a dual extraction approach: basic `sed` color stripping or a custom TypeScript parser at [`ci-scripts/codelayer-output.ts`](https://github.com/humanlayer/skills/blob/main/ci-scripts/codelayer-output.ts).

## Frequently Asked Questions

### Why do Claude Code and CodeLayer use the same secret name but different extraction methods?

Both agents integrate with Anthropic's API, hence the shared `ANTHROPIC_API_KEY` requirement. However, Claude Code outputs structured stream-JSON requiring nested `jq` queries to isolate the final text content, while CodeLayer emits ANSI-colored plain text that requires color code stripping or custom parsing to produce clean markdown.

### Can I use OpenAI models with the OpenCode runner instead of Anthropic?

Yes. While the example implementation in [`agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/agent-runner-templates.md) demonstrates `ANTHROPIC_API_KEY` usage, OpenCode supports provider configuration through its CLI flags. You would substitute `OPENAI_API_KEY` in the environment block and adjust the `--model` parameter to reference an OpenAI model identifier rather than the Anthropic `claude-sonnet-4-5` example.

### What happens if I don't strip ANSI codes from CodeLayer output?

The extracted PR body will contain raw terminal escape sequences (color codes, formatting characters) that render as gibberish or invisible characters in GitHub's markdown viewer. The `sed` command `s/\x1b\[[0-9;]*m//g` removes these sequences, or alternatively, the custom Bun parser at [`ci-scripts/codelayer-output.ts`](https://github.com/humanlayer/skills/blob/main/ci-scripts/codelayer-output.ts) handles both color stripping and potential structural formatting.

### Is the `--output-last-message` flag available in Claude Code or OpenCode?

No. The `--output-last-message` flag is specific to the Codex CLI. Claude Code requires stream-JSON parsing with `jq`, and OpenCode requires JSON message array extraction. Attempting to use this flag with the other agents will result in an unrecognized argument error or unexpected behavior.