v0.3.3 — Per-call model override in MCP + CLI
Summary
Adds optional `model` to all three review tools so callers can switch models per request instead of relying on the startup-cached config value.
- MCP: `review_plan`, `review_code`, `review_precommit` each accept `model?: string`
- CLI: `--model ` on all three subcommands
- Resolution order: explicit tool-call `model` → `.reviewbridge.json` → built-in default
- Conflict rule: passing both `session_id` and `model` returns `INVALID_INPUT` — resumed threads are bound to their original model
- Multi-chunk aware: the override is applied on chunk 1 (`startThread`); chunks 2..N inherit via `resumeThread` without re-asserting the model
Why this matters
When OpenAI ships a new flagship model, it lands in the API tier immediately but lags a few days on the ChatGPT-subscription Codex tier. During that window, `v0.3.0`/`v0.3.1` users on ChatGPT auth hit `MODEL_ERROR: ... not supported when using Codex with a ChatGPT account`. `v0.3.2` shipped a targeted error tip. `v0.3.3` now lets Claude Code (or any caller) act on that tip directly — just pass `model: "gpt-5.4"` on the next call. No config edit, no MCP restart.
Also useful for per-tool model selection in general: cheaper model for precommit, heavier for plan review.
Usage
MCP (Claude Code calls this automatically after reading the error tip):
```
review_code({ diff, model: "gpt-5.4" })
```
CLI:
```bash
git diff HEAD | npx codex-claude-bridge@latest review-code --diff - --model gpt-5.4
npx codex-claude-bridge@latest review-precommit --model gpt-5.4
```
Safety
- The SDK forwards `--model` to `codex exec` unconditionally whenever set in `ThreadOptions`, including on `resumeThread`. This release strips `model` from the resume-path options entirely, so a thread's original model is preserved across multi-chunk reviews. Omitting the override on resume is correct-by-construction rather than relying on SDK behavior.
- `model: ""` is rejected at both the MCP (via `z.string().min(1)`) and CLI (via trim-and-coerce-to-undefined) layers.
- `session_id + model` is rejected at the handler layer before any session-tracker mutation, so invalid input never marks an active session `failed` in SQLite.
- `classifyError` now receives the resolved model (override or config default) on all failure paths, so error messages report the actually-running model even on chunks 2..N of chunked reviews.
Changes
| Change | Detail |
|---|---|
| MCP schemas | `model?: string` added to review_plan, review_code, review_precommit (z.string().min(1).optional()) |
| CLI flags | `--model ` on all three subcommands; empty-string normalization |
| Client plumbing | New `resumeThreadOpts` helper strips model on resume; `resolvedModel` threaded through `runReview` for error context |
| Tests | 8 new (client-layer override paths, conflict rejection, multi-chunk inheritance for both reviewCode and reviewPrecommit, handler-level forwarding); 487 total |
| README | New `model` row per tool parameter table |
Upgrading
```bash
claude mcp remove codex-bridge
claude mcp add codex-bridge -- npx -y codex-claude-bridge@latest
```
The `@latest` pin makes `npx` re-resolve on every launch, so future updates are automatic.