Skip to content

Harness and Model ​

The harness is what runs the agent. claude is the default and runs the Claude Agent SDK. codex runs OpenAI's Codex SDK. Both come with Bishop, so there's no separate agent CLI to install.

json
{
  "agent": {
    "harness": "codex"
  }
}

--harness overrides it for one run, and an unsupported name fails at startup naming the ones that exist:

$ bishop --harness codex
$ bishop --harness gemini
unknown harness "gemini"; supported: claude, codex

Model credentials ​

Bishop manages no model credentials. Each harness authenticates however its own CLI does in that directory, so make claude or codex work there first.

  • Claude uses ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN if one is set, and otherwise whatever login claude has on that machine. Bishop logs which it used at startup.
  • Codex uses codex login, or CODEX_API_KEY in the environment or .env. Bishop warns at startup when it can find neither, since the first turn would otherwise fail on authentication with nothing to explain it.

OPENAI_API_KEY isn't read, because codex doesn't read it either. With only OPENAI_API_KEY set and no login, codex sends no credential at all and the request comes back 401 Missing bearer. Picking it up would also override a working codex login and move billing to the API key without saying so.

Switching harness ​

A session belongs to the harness that started it, so switching leaves running threads unable to resume. Bishop notices, logs it, and starts each thread a fresh session on its next message rather than failing the turn. Nothing else is lost.

Codex threads read differently in one way that shows: their tool cards are vaguer, because a command comes through as a raw shell line and nothing else, which Bishop won't put in a thread.

Model and effort ​

Every harness has a model and a reasoning level, so both sit at the top of agent rather than inside agent.options:

json
{
  "agent": {
    "harness": "codex",
    "model": "gpt-5.6-codex",
    "effort": "high"
  }
}

Both are free strings, passed to the harness as written. The names belong to the harness and change with it, so Bishop doesn't check them: claude takes claude-sonnet-5 and low through max, codex takes gpt-5.6-codex and modelReasoningEffort's levels.

Nothing checks the value before the first turn. A name the harness doesn't know surfaces in a thread instead, differently by harness. Codex fails the turn with the API's own complaint about the field. Claude answers a bad model with the agent saying in prose that the model is wrong, and ignores an effort level it doesn't recognize. So an effort typo under Claude is silent: check the log line at startup, which names the model and effort Bishop passed.

--model and --effort override the config for one run:

sh
bishop --harness claude --model claude-opus-5 --effort high

Setting the same thing again in agent.options loses to these, and startup logs a warning naming the key it overrode. ! model changes them for one conversation.

Agent options ​

agent.options is passed to the harness unvalidated. Under Claude it's the Agent SDK's own options object, not a settings file: anything from Claude Code's settings reference goes in the nested settings object, which is shaped like a .claude/settings.json. A settings key written at the top level is dropped by the SDK without a word, so put it a level down:

json
{
  "agent": {
    "options": {
      "maxTurns": 30,
      "settings": {
        "permissions": { "allow": ["Bash(python3:*)"] }
      }
    }
  }
}

These layer over the agent's own settings files rather than replacing them, so a project that sets model in .claude/settings.json keeps it unless agent.options names model too.

Bishop sets cwd, resume, systemPrompt, abortController, sessionId, forkSession, and canUseTool itself and ignores them here, logging a warning for each. They decide which agent runs, which conversation it continues, and how Bishop tells the agent who it's talking to.

Bishop sets two defaults, both overridable:

  • permissionMode: "auto". Nothing in a conversation can answer a permission prompt, and an agent needing approval for a tool reports itself blocked instead of doing the work. Override it if you want something stricter.
  • settings.autoMemoryEnabled: false. Auto memory writes to ~/.claude/projects/<agent-directory>/memory, outside the agent's own repository, so it doesn't travel when you deploy the agent somewhere else, and a git-URL agent's directory changes with every commit. Turn it back on with "settings": { "autoMemoryEnabled": true }. Bishop warns if you put it at the top of agent.options, where the harness ignores it.

hooks and settings merge instead of being replaced. Your hooks run alongside the one worktree mode installs, and your settings object keeps Bishop's defaults for the keys it doesn't name. Pointing settings at a settings file instead opts out of both defaults, which Bishop warns about.

Under Codex ​

agent.options takes Codex's own fields instead:

json
{
  "agent": {
    "harness": "codex",
    "options": {
      "sandboxMode": "danger-full-access",
      "additionalDirectories": ["/srv/data"]
    }
  }
}

Bishop's defaults are approvalPolicy: "never", sandboxMode: "workspace-write", networkAccessEnabled: true, and skipGitRepoCheck: true. Nothing in a conversation can answer an approval prompt, so an agent that asks for one stalls the turn. The sandbox is the compensation: writes stay inside the working directory, and network access stays on because an agent that can't push, install, or fetch fails at something in most threads. Override any of them here.

Bishop sets workingDirectory and developer_instructions itself and ignores them here. config and configOverrides reach Codex's own --config mechanism, which is how anything in config.toml can be set per agent. A config object you set is merged with Bishop's rather than replacing it.

One config for both harnesses ​

agent.options is written in one harness's vocabulary, so a config meant to run either one keeps a block per harness:

json
{
  "agent": {
    "harness": "claude",
    "worktrees": true,
    "effort": "high",

    "harnesses": {
      "claude": {
        "options": { "maxTurns": 30 }
      },
      "codex": {
        "model": "gpt-5.6-codex",
        "options": { "sandboxMode": "danger-full-access" }
      }
    }
  }
}

bishop runs Claude with its block, and bishop --harness codex runs Codex with its own model and sandbox setting.

A block takes model, effort, and options. model and effort layer over the ones beside harness, so effort above applies to both harnesses and Codex adds a model. options doesn't layer: it replaces agent.options outright, since merging two objects written in different vocabularies would splice one harness's keys into the other's call.

agent.options is then the options for whichever harness has no block of its own. Set both for the same harness and startup warns that the block wins. Switch to a harness with no block and startup warns that it's being handed options meant for the other one. A harness name Bishop doesn't run fails at startup, so a misspelled block can't quietly do nothing.