Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "codex-delegate",
"description": "Hand token-heavy mechanical work (batch edits, scaffolding, refactors, test generation, plotting scripts) from Claude to Codex CLI.",
"version": "0.1.3",
"version": "0.1.4",
"author": {
"name": "Wenyu Chiou"
},
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ marketplace; see that repo's CHANGELOG for the catalog-side history.

## [Unreleased]

### Added (manifest `0.1.4`)

- Optional `--service-tier` / `-ServiceTier <value|default>` passes
`-c service_tier="<value>"` to `codex exec`. The env var
`CODEX_DELEGATE_SERVICE_TIER` supplies a default when the flag is absent;
the flag wins over the env var, and `default` (or neither set) passes
nothing so `~/.codex/config.toml` applies. The value is lowercased and must
be one word of letters `a-z` (else exit 2 before Codex runs); the wrapper
keeps no list of valid tiers and does not check tier support. `-ServiceTier`
is declared last (after `-Effort`) so existing positional PowerShell calls
keep their bindings.
- `result.json` gains `service_tier` (immediately after `reasoning_effort`: the
value passed, or `default` when none was passed).
- `SKILL.md` and `references/` (`model-selection.md`, `wrapper.md`,
`output-contract.md`, `runtime-compatibility.md`) document the new option.
Tests in `tests/test_wrappers.py` cover pass-through on both wrappers and
both copies, plus precedence, invalid-value refusal (canonical copies),
combined `--effort` + `--service-tier`, and ambient env isolation.

### Added (manifest `0.1.3`)

- Optional `--effort` / `-Effort <level|default>` passes
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Current wrapper tests cover:
- result contracts, quota/error classification, stdin closure, and changed-file snapshots
- caller-relative/repo-relative brief resolution when Codex uses a different working root
- canonical/packaged wrapper parity and isolated portable-skill execution
- `--model` / `--effort` pass-through only when given, including the `CODEX_DELEGATE_EFFORT` default and invalid-value refusal
- `--model` / `--effort` / `--service-tier` pass-through only when given, including the `CODEX_DELEGATE_EFFORT` and `CODEX_DELEGATE_SERVICE_TIER` defaults and invalid-value refusal
- PowerShell parity (runs with pwsh or Windows PowerShell when available)

## Installation
Expand All @@ -117,9 +117,10 @@ from the loaded skill; the host resolves the plugin's versioned installation pat
For direct portable-skill installations, use `<skill-root>/scripts/run_codex.sh`.
Do not assume marketplace plugins live in `~/.claude/skills/`.

Current stable manifest: `0.1.0`; unreleased patches propose `0.1.3` (`0.1.1`:
Current stable manifest: `0.1.0`; unreleased patches propose `0.1.4` (`0.1.1`:
compatibility fixes; `0.1.2`: no built-in default model; `0.1.3`: optional
`--effort` / `-Effort` and `CODEX_DELEGATE_EFFORT`). See [runtime/package compatibility](skills/codex-delegate/references/runtime-compatibility.md)
`--effort` / `-Effort` and `CODEX_DELEGATE_EFFORT`; `0.1.4`: optional
`--service-tier` / `-ServiceTier` and `CODEX_DELEGATE_SERVICE_TIER`). See [runtime/package compatibility](skills/codex-delegate/references/runtime-compatibility.md)
for the observed CLI version, current primary docs, and verification limits.

Default scope is `user` (this OS account, all projects). Add
Expand Down
4 changes: 2 additions & 2 deletions README_zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ python -m pytest -q

- success path 的 `result.json` 輸出
- PowerShell wrapper contract 行為
- `--model` / `--effort` 只有指定時才傳給 Codex,含 `CODEX_DELEGATE_EFFORT` 預設值與無效值拒絕
- `--model` / `--effort` / `--service-tier` 只有指定時才傳給 Codex,含 `CODEX_DELEGATE_EFFORT`、`CODEX_DELEGATE_SERVICE_TIER` 預設值與無效值拒絕

## 安裝

Expand Down Expand Up @@ -124,6 +124,6 @@ Marketplace plugin 請從載入的 skill 使用
`<skill-root>/scripts/run_codex.sh`;封裝內的 Bash/PowerShell wrapper
與根目錄版本必須逐位元組一致。

目前穩定 manifest 是 `0.1.0`,尚未發布的修補提議 `0.1.3`(`0.1.1`:相容性修補;`0.1.2`:不再內建預設 model;`0.1.3`:可選的 `--effort` / `-Effort` 與 `CODEX_DELEGATE_EFFORT`)。
目前穩定 manifest 是 `0.1.0`,尚未發布的修補提議 `0.1.4`(`0.1.1`:相容性修補;`0.1.2`:不再內建預設 model;`0.1.3`:可選的 `--effort` / `-Effort` 與 `CODEX_DELEGATE_EFFORT`;`0.1.4`:可選的 `--service-tier` / `-ServiceTier` 與 `CODEX_DELEGATE_SERVICE_TIER`)。
版本、`-C` 工作目錄契約與未驗證項目見
[相容性紀錄](skills/codex-delegate/references/runtime-compatibility.md)。
19 changes: 17 additions & 2 deletions scripts/run_codex.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ param(
[string]$LogFile = "",
[string]$BriefFile = "",
[bool]$Synchronous = $true,
# Declared last so existing positional calls keep their bindings.
[string]$Effort = ""
# Appended after the original parameters so existing positional calls
# keep their bindings; -ServiceTier follows the same rule.
[string]$Effort = "",
[string]$ServiceTier = ""
)

# ValidateSet is case-insensitive, while Codex requires canonical values.
Expand All @@ -29,6 +31,17 @@ if ($Effort -and $Effort -cnotmatch '^[a-z]+\z') {
}
$effortLabel = if ($Effort) { $Effort } else { "default" }

# -ServiceTier wins over CODEX_DELEGATE_SERVICE_TIER; "default" (or neither
# set) passes no override (parity with run_codex.sh). Only the shape is checked.
if (-not $ServiceTier) { $ServiceTier = "$env:CODEX_DELEGATE_SERVICE_TIER" }
$ServiceTier = $ServiceTier.ToLowerInvariant()
if ($ServiceTier -eq "default") { $ServiceTier = "" }
if ($ServiceTier -and $ServiceTier -cnotmatch '^[a-z]+\z') {
[Console]::Error.WriteLine("Error: -ServiceTier (or CODEX_DELEGATE_SERVICE_TIER) must be one word such as fast or flex, or default; got '$ServiceTier'")
exit 2
}
$serviceTierLabel = if ($ServiceTier) { $ServiceTier } else { "default" }

# Default --repo to the caller's working directory.
# Resolved here (not at param default) so it reflects the shell's PWD
# at invocation time, not the script's parse-time location.
Expand Down Expand Up @@ -178,6 +191,7 @@ function Write-ResultJson {
' "delegate": "codex",',
(' "model": ' + (ConvertTo-JsonScalar $ModelUsed) + ","),
(' "reasoning_effort": ' + (ConvertTo-JsonScalar $effortLabel) + ","),
(' "service_tier": ' + (ConvertTo-JsonScalar $serviceTierLabel) + ","),
(' "log_file": ' + (ConvertTo-JsonScalar $logPath) + ","),
(' "output_file": ' + (ConvertTo-JsonScalar $OutputFile) + ","),
(' "summary": ' + (ConvertTo-JsonScalar $Summary) + ","),
Expand Down Expand Up @@ -269,6 +283,7 @@ $codexArgs = @("exec", "--sandbox", $Sandbox, "-C", $Repo)
# Empty -Model = no -m flag, so codex uses its own configured default model.
if ($Model) { $codexArgs += @("-m", $Model) }
if ($Effort) { $codexArgs += @("-c", "model_reasoning_effort=$Effort") }
if ($ServiceTier) { $codexArgs += @("-c", "service_tier=`"$ServiceTier`"") }
$modelLabel = if ($Model) { "codex/$Model" } else { "codex/default" }
if ($OutputFile) { $codexArgs += @("-o", $OutputFile) }
$codexArgs += $safePrompt
Expand Down
20 changes: 20 additions & 0 deletions scripts/run_codex.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ write_result_json() {
printf ' "delegate": "codex",\n'
printf ' "model": %s,\n' "$(printf '%s' "$model" | json_escape)"
printf ' "reasoning_effort": %s,\n' "$(printf '%s' "$EFFORT_LABEL" | json_escape)"
printf ' "service_tier": %s,\n' "$(printf '%s' "$SERVICE_TIER_LABEL" | json_escape)"
printf ' "log_file": %s,\n' "$(printf '%s' "$LOG_PATH" | json_escape)"
printf ' "output_file": %s,\n' "$(printf '%s' "$OUTPUT_FILE" | json_escape)"
printf ' "summary": %s,\n' "$(printf '%s' "$summary" | json_escape)"
Expand All @@ -102,6 +103,8 @@ REPO="${PWD}"
MODEL=""
# Empty = no -c override, so codex uses its configured model_reasoning_effort.
EFFORT=""
# Empty = no -c override, so codex uses its configured service_tier.
SERVICE_TIER=""
SANDBOX="workspace-write"
OUTPUT_FILE=""
LOG_FILE=""
Expand All @@ -113,6 +116,7 @@ while [[ $# -gt 0 ]]; do
--repo) REPO="$2"; shift 2 ;;
--model) MODEL="$2"; shift 2 ;;
--effort) EFFORT="$2"; shift 2 ;;
--service-tier) SERVICE_TIER="$2"; shift 2 ;;
--sandbox) SANDBOX="$2"; shift 2 ;;
--output-file) OUTPUT_FILE="$2"; shift 2 ;;
--log-file) LOG_FILE="$2"; shift 2 ;;
Expand Down Expand Up @@ -146,6 +150,21 @@ case "$EFFORT" in
esac
EFFORT_LABEL="${EFFORT:-default}"

# --service-tier wins over CODEX_DELEGATE_SERVICE_TIER; "default" (or neither
# set) passes no override. Only the shape is checked here: one lowercase word.
SERVICE_TIER="${SERVICE_TIER:-${CODEX_DELEGATE_SERVICE_TIER:-}}"
case "$SERVICE_TIER" in
*$'\n'*) echo "Error: --service-tier (or CODEX_DELEGATE_SERVICE_TIER) must be one word such as fast or flex, or default" >&2; exit 2 ;;
esac
SERVICE_TIER="$(printf '%s' "$SERVICE_TIER" | tr '[:upper:]' '[:lower:]')"
case "$SERVICE_TIER" in
""|default) SERVICE_TIER="" ;;
*[!abcdefghijklmnopqrstuvwxyz]*)
echo "Error: --service-tier (or CODEX_DELEGATE_SERVICE_TIER) must be one word such as fast or flex, or default; got '$SERVICE_TIER'" >&2
exit 2 ;;
esac
SERVICE_TIER_LABEL="${SERVICE_TIER:-default}"

# Brief discipline: refuse inline prompts > 500 chars unless a brief is on disk.
# Auditability gap fix (research-hub v0.89.1 post-release audit, 2026-05-15):
# inline --prompt with no on-disk brief leaves orphan results and breaks
Expand Down Expand Up @@ -260,6 +279,7 @@ printf '%s' "$PROMPT" > "$PROMPT_FILE"
CODEX_ARGS=("exec" "--sandbox" "$SANDBOX" "-C" "$REPO")
[[ -n "$MODEL" ]] && CODEX_ARGS+=("-m" "$MODEL")
[[ -n "$EFFORT" ]] && CODEX_ARGS+=("-c" "model_reasoning_effort=$EFFORT")
[[ -n "$SERVICE_TIER" ]] && CODEX_ARGS+=("-c" "service_tier=\"$SERVICE_TIER\"")
[[ -n "$OUTPUT_FILE" ]] && CODEX_ARGS+=("-o" "$OUTPUT_FILE")
MODEL_LABEL="codex/${MODEL:-default}"
CODEX_ARGS+=("$(cat "$PROMPT_FILE")")
Expand Down
7 changes: 4 additions & 3 deletions skills/codex-delegate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,15 @@ Full routing table and good/bad examples: `references/delegation-targets.md`.

## Output contract

`.result.json` includes at minimum: `status` (success|fallback|error), `delegate` ("codex"), `model`, `reasoning_effort`, `log_file`, `output_file`, `summary`, `risks`, `files_changed`, `tests_run`, `timestamp_utc`. Full schema and status semantics: `references/output-contract.md`.
`.result.json` includes at minimum: `status` (success|fallback|error), `delegate` ("codex"), `model`, `reasoning_effort`, `service_tier`, `log_file`, `output_file`, `summary`, `risks`, `files_changed`, `tests_run`, `timestamp_utc`. Full schema and status semantics: `references/output-contract.md`.

## Compatibility

- Tested with `@openai/codex` 0.128.0–0.144.1 (May–July 2026). Should work with any version that accepts `codex exec --sandbox workspace-write`.
- Wrapper default model: none. Without `--model` / `-Model` the wrapper passes no `-m`, so Codex uses its own configured default (reported as `codex/default`); pass `--model` or `-Model` to pick one. Availability and pricing depend on the account and installed CLI. `references/model-selection.md` contains a historical A/B snapshot, not a current price guarantee. Use the interactive `/model` picker to inspect available models.
- Wrapper default reasoning effort: none. Without `--effort` / `-Effort` (or `CODEX_DELEGATE_EFFORT`) the wrapper passes no `-c model_reasoning_effort`, so the effort in `~/.codex/config.toml` applies (reported as `default`). Pass `--effort <level>` (for example `low`, `medium`, `high`, `xhigh`) per call, or set `CODEX_DELEGATE_EFFORT` so delegation defaults to a cheaper effort than interactive use; the flag wins over the env var. See `references/model-selection.md`.
- Wrapper calls `codex exec --sandbox workspace-write -C <repo> [-m <model>] [-c model_reasoning_effort=<effort>]`. In observed CLI `0.159.0-alpha.7`, `-C`/`--cd` selects a working-root directory, not a context file; `--full-auto` is absent. Check `codex exec --help` for your installed version. See `references/runtime-compatibility.md` for the primary-source checks and limits.
- Wrapper default service tier: none. Without `--service-tier` / `-ServiceTier` (or `CODEX_DELEGATE_SERVICE_TIER`) the wrapper passes no `-c service_tier`, so the tier in `~/.codex/config.toml` applies (reported as `default`). Pass `--service-tier <value>` (for example `fast` or `flex`) per call, or set `CODEX_DELEGATE_SERVICE_TIER`; the flag wins over the env var, and `default` passes nothing. See `references/model-selection.md`.
- Wrapper calls `codex exec --sandbox workspace-write -C <repo> [-m <model>] [-c model_reasoning_effort=<effort>] [-c service_tier="<tier>"]`. In observed CLI `0.159.0-alpha.7`, `-C`/`--cd` selects a working-root directory, not a context file; `--full-auto` is absent. Check `codex exec --help` for your installed version. See `references/runtime-compatibility.md` for the primary-source checks and limits.
- `codex exec` is non-interactive. Sandbox selection is not blanket permission: the installed host's approval and policy settings still apply. The observed CLI has no exec-local `--ask-for-approval` option; do not infer bypassed approval or expand access from that absence.
- Direct `codex exec` calls must close stdin (`</dev/null`) to avoid the historical hang (issue #20919).
- PowerShell wrapper requires `$ErrorActionPreference` to NOT be `Stop` so stderr writes (warnings, banners) don't trip the catch block.
Expand Down Expand Up @@ -152,4 +153,4 @@ the discipline pays the context twice. For bidirectional multi-round work
- `references/patterns.md` — five single-task delegation shapes (context file, parallel, resume, structured output, review mode)
- `references/multi-agent.md` — leaf role in router/leaves architecture; when to route through `research-hub-multi-ai` or `agent-task-splitter`
- `references/examples.md` — concrete invocation examples on `codex-cli` 0.128.0+ syntax
- `references/model-selection.md` — when to pass `--model` / `--effort`, plus a historical A/B snapshot of a cheaper vs a pricier model
- `references/model-selection.md` — when to pass `--model` / `--effort` / `--service-tier`, plus a historical A/B snapshot of a cheaper vs a pricier model
18 changes: 15 additions & 3 deletions skills/codex-delegate/references/model-selection.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Model and Effort Selection
# Model, Effort and Service Tier Selection

The wrapper has two optional knobs, and by default it turns neither:
The wrapper has three optional knobs, and by default it turns none of them:

| Knob | Bash / PowerShell | Passed to Codex as | Without it |
|---|---|---|---|
| Model | `--model <id>` / `-Model <id>` | `-m <id>` | `model` from `~/.codex/config.toml` (reported as `codex/default`) |
| Reasoning effort | `--effort <level>` / `-Effort <level>` | `-c model_reasoning_effort=<value>` | `model_reasoning_effort` from `~/.codex/config.toml` (reported as `default`) |
| Service tier | `--service-tier <value>` / `-ServiceTier <value>` | `-c service_tier="<value>"` | `service_tier` from `~/.codex/config.toml` (reported as `default`) |

Since manifest `0.1.2` the wrapper passes no `-m` unless asked. Earlier versions always passed `-m gpt-5.5` (from commit `70e6fdc`, 2026-05-15), which fails with HTTP 400 on accounts that lose access to that model; the wrapper still has no built-in model id. Since manifest `0.1.3` it also passes no effort unless asked. Both Codex defaults live in `~/.codex/config.toml` as plain strings, not tables:
Since manifest `0.1.2` the wrapper passes no `-m` unless asked. Earlier versions always passed `-m gpt-5.5` (from commit `70e6fdc`, 2026-05-15), which fails with HTTP 400 on accounts that lose access to that model; the wrapper still has no built-in model id. Since manifest `0.1.3` it also passes no effort unless asked. Since manifest `0.1.4` it also passes no service tier unless asked. The Codex defaults live in `~/.codex/config.toml` as plain strings, not tables:

```toml
model = "<model id from the /model picker>"
Expand All @@ -32,6 +33,17 @@ Rough guide:
- `xhigh`: one-shot tasks where subtle correctness matters and the extra cost does not multiply across many calls.
- `max` / `ultra`, where the model offers them: cost more than `xhigh`; rarely worth it for delegated mechanical work.

## Service tier

Service tier selects Codex's speed/usage tier when a call needs it:

- **Per call:** `--service-tier fast` (bash) / `-ServiceTier fast` (PowerShell), or combined with effort: `--effort ultra --service-tier fast` (PowerShell: `-Effort ultra -ServiceTier fast`).
- **For all delegations:** set `CODEX_DELEGATE_SERVICE_TIER` in the environment that runs the wrapper. Precedence is flag, then `CODEX_DELEGATE_SERVICE_TIER`, then `service_tier` in `~/.codex/config.toml`. `--service-tier default` (or `-ServiceTier default`) ignores the env var and passes nothing.

The wrapper lowercases the value, checks only the shape (one word of letters `a-z`; anything else exits 2 before Codex runs), and passes `-c service_tier="<value>"`. It keeps no list of valid tiers, and what Codex does with an unsupported one is not established here (a config load accepted an arbitrary string on 2026-10-23), so a typo that passes the shape check can reach `result.json` as `service_tier` unchanged. Windows PowerShell 5.1 drops the embedded double quotes when launching `codex`, so the child receives the bare `service_tier=fast`; Codex's `-c` parser accepts both forms.

Observed on Codex CLI 0.163.0 with model `gpt-6.1-sol`: `codex exec -c 'service_tier="fast"' ...` is accepted, and a test session's rollout file recorded `"service_tier":"priority"` (`priority` is the API name of the Fast tier). `~/.codex/models_cache.json` lists, for `gpt-6.1-sol`, `service_tiers: [{id: "priority", name: "Fast", description: "2x speed, increased usage"}]` and `additional_speed_tiers: ["fast"]`, and the Codex binary's config enum shows `fast` and `flex`. Fast spends more usage for speed, so it is a deliberate choice, not a default. Whether other models offer a tier is not established here; check the `/model` picker or the model cache.

## Choosing a model

- Delegate to one of the newest models that is not the most expensive tier. Codex's own model list (`~/.codex/models_cache.json`, or the `/model` picker) describes each model; pick the current general-purpose "workhorse", not the "frontier" tier. Token cost compounds across N calls, and the brief plus Claude's review carry the judgment.
Expand Down
Loading
Loading