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
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
},
"metadata": {
"description": "Run jobs on Codex, Grok Build, ZCode and Kimi Code from Claude Code like Claude agents.",
"version": "2.2.0"
"version": "2.2.1"
},
"plugins": [
{
"name": "relay",
"description": "Run jobs on Codex, Grok Build, ZCode and Kimi Code from Claude Code like Claude agents.",
"version": "2.2.0",
"version": "2.2.1",
"author": {
"name": "Libar"
},
Expand Down
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ Claude Code update can break.
| `fast` | `true` asks OpenAI for its priority tier after `/relay:setup --fast-tier allowed` | `false` |
| `network` | `true`, `false`; `write` and `read-only` only | `false` |
| `web-search` | `live`, `cached`, `disabled` | `live` |
| `tool-output-token-limit` | Codex only: `compact`, `balanced`, `deep`, `inherit` or a positive integer of tokens retained per tool result | Codex configuration, else the model's default Relay budget (`deep`: a quarter of its context window) |
| `cd` | absolute working directory | the agent's directory |
| `add-dir` | absolute extra writable root, repeatable; Codex `write` only | none |
| `brief` | absolute path of the brief file; nothing follows `---` | the text after `---` |
Expand Down Expand Up @@ -250,6 +251,51 @@ model_reasoning_effort = "high"

Your Codex sign-in, API key and provider configuration apply as they do in the Codex CLI.

Codex's `tool_output_token_limit` controls how much of each tool result is retained in model
history. Codex's own default keeps 10,000 tokens, which cuts the middle out of a review's diff or a
long log, so Relay gives every Codex job a budget. Each model has three presets, in tokens per tool
result:

- `deep`: a quarter of the model's context window, to the nearest power of two (65536 for a 272k
window, 131072 for 500k). This is the default.
- `balanced`: half of `deep`.
- `compact`: a quarter of `deep`.

The window comes from Codex's `model_context_window` setting, else from Codex's model catalog
cache; when neither reports it, 272000 is assumed. Unless `model_context_window` is configured,
models reviewed by hand override the formula in
[`tool-output-budgets.json`](plugins/relay/scripts/lib/backends/codex/tool-output-budgets.json):

| Model | `compact` | `balanced` | `deep` | Default |
| --- | ---: | ---: | ---: | --- |
| `gpt-6-astra` | 32768 | 65536 | 131072 | `balanced` |
| `gpt-6.1-sol` | 32768 | 65536 | 131072 | `deep` |
| `gpt-6.1-terra` | 32768 | 65536 | 131072 | `deep` |
| `gpt-6-luna` | 16384 | 32768 | 65536 | `deep` |

The values are proposed engineering judgments, not vendor settings or measured optima. A job that
sets no budget gets the Codex configuration's `tool_output_token_limit` when there is one, else its
model's default, and its state block names the budget and its basis. `/relay:setup` shows the
budget a job on the default model gets and where it comes from. Setup never writes it.

To choose for one job, add `tool-output-token-limit:` to its header with `compact`, `balanced`,
`deep`, a number of tokens, or `inherit` to send no budget and leave Codex's configuration or default
in charge. The same `--tool-output-token-limit` flag works with `start`, `task`, `review` and
`adversarial-review`. A chosen budget is stored with the job, shown in its state and JSON status,
and reapplied on continue, resume and worker recovery. A preset resolves each time the thread
opens, after Relay reads the Codex configuration, and the state block names its value. Other backends reject this option.

Codex's code mode, which current models use for every tool call, returns 10,000 tokens of each
`exec` or `wait` result unless the call asks for more, whatever the budget is; no setting changes
that. When a job's effective budget (its own, the Codex configuration's, or Relay's default) is
above 10,000, Relay adds a developer instruction with the budget and the
`// @exec: {"max_output_tokens": N}` pragma, after any developer instructions you configured.

Larger results use more context and can bring compaction forward. This budget does not increase
the model's context window or generated-answer limit, and cannot restore output already
truncated by a tool. Codex CLI and Relay update independently; a Relay update does not replace the
`codex` executable used by jobs.

## What the relay depends on in Claude Code

The relay reads two things Claude Code does not publish as an interface: the agent transcript and
Expand Down
8 changes: 5 additions & 3 deletions docs/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ lib/backends/
| `backendFor(request)` | the backend of a stored request; a request without `backend` is the default's |
| `backendWith(capability)` | the one backend that has a capability; it throws unless exactly one has it |
| `BACKEND_HEADER_KEYS` | every backend's own header keys, mapped to their request fields |
| `backendHeaderValues(options)` | those keys read from command flags, so `start` and the reviews take `--grok-home` and `--kimi-home` |
| `backendHeaderValues(options)` | those keys read from command flags for `start`, `task` and reviews |

`task --backend <name>` checks the name with `backendNamed` and stores it in the request. The stop
gate picks its backend with `backendWith("stopGate")`: it probes that backend, starts its review
Expand Down Expand Up @@ -115,9 +115,11 @@ Generic code derives the rest:
backend but the default. Grok prints `grok-home:` for a home other than `~/.grok`, and Kimi
`kimi-home:` for one other than `~/.kimi-code`.
- `view.settingLines?(request)` supplies backend settings at the settings position of the launch
block and the state block, after `tier: fast`. Codex uses it for `mcp-off:`.
block and the state block, after `tier: fast`. Codex uses it for `mcp-off:` and an explicit
`tool-output-token-limit:`.
- `view.json(request)` supplies the backend's fields for `status --json` and `start --json`,
beside `backend`. Grok adds `grokHome` and Kimi `kimiHome`. A setting line adds no JSON field.
beside `backend`. Grok adds `grokHome`, Kimi `kimiHome`, and Codex an explicit
`toolOutputTokenLimit`. A setting line does not automatically add a JSON field.
- `view.completionInferenceLine?(inference, { sessionId, turnId })` formats the current attempt's
recorded inference as a `note:` line, or returns null when it does not apply. Generic code
passes the inference as backend data and does not read it. Codex checks the inference's thread
Expand Down
19 changes: 19 additions & 0 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,25 @@ binaries and never start a real backend.
writes nothing there. `default` takes the configured `model` and `model_reasoning_effort`. Sign-in
is the CLI's: ChatGPT, an API key, or none when the configured provider needs no OpenAI sign-in
(setup's `not-needed`, from openai/codex-plugin-cc#177).
- **Tool-output budget.** Codex keeps 10,000 tokens of each tool result by default, which truncates
a review's diff, so every Codex job gets a Relay budget. Presets are formulas of the model's
context window: `deep` is a quarter of it to the nearest power of two and the default,
`balanced` half of `deep`, `compact` a quarter. The window is `model_context_window`, else the
model's entry in Codex's `models_cache.json`, else 272000; `model/list` reports neither window
nor budget. `codex/tool-output-budgets.json` holds hand-reviewed values that replace the formula
for named models, with a version, unless `model_context_window` is configured: the reviewed
values assume the catalog's window. A job's `tool-output-token-limit` takes a preset, a number or
`inherit`. A preset resolves at each thread open, after `config/read`, so a configured
`model_context_window` bounds it whether or not the job names its model. The number
is sent as `config.tool_output_token_limit` on thread start and resume, including recovery, and on
a native review's source thread. Without a choice, each thread open asks `config/read`: a
configured budget stays Codex's; otherwise Relay sends the model's default and writes a `note:`
naming the value and its basis. Code mode cuts each `exec` and `wait` result to 10,000 tokens
unless the call asks for more (`max_output_tokens`, `max_tokens`), whatever the budget; no
setting changes that default. So when the effective budget is above 10,000, the thread opens
with `developerInstructions` naming the budget and the pragma, appended to the configured
`developer_instructions`.
See [Codex configuration](../README.md#codex-configuration) for usage and limits.
- **Job MCP servers off.** `setup --job-mcp-off name,...` stores a user-level list. A job passes
`-c mcp_servers.<name>.enabled=false` for each listed name the home's `config.toml` defines and
skips any other name with a `note:`, because Codex refuses to start on a setting for a server its
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@libar-dev/relay-plugin-cc",
"version": "2.2.0",
"version": "2.2.1",
"private": true,
"type": "module",
"description": "Run jobs on Codex, Grok Build, ZCode and Kimi Code from Claude Code like Claude agents.",
Expand Down
2 changes: 1 addition & 1 deletion plugins/relay/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "relay",
"version": "2.2.0",
"version": "2.2.1",
"description": "Run jobs on Codex, Grok Build, ZCode and Kimi Code from Claude Code like Claude agents.",
"author": {
"name": "Libar"
Expand Down
19 changes: 19 additions & 0 deletions plugins/relay/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## 2.2.1 (2026-10-30)

- Codex jobs and reviews no longer lose the middle of large tool results. Codex keeps 10,000
tokens of each result by default, which cut review diffs and logs short. Every Codex job now gets
a budget unless it or the Codex configuration sets one: the model's `deep` preset, a quarter of
its context window to the nearest power of two (65536 for 272k, 131072 for 500k). `balanced` is
half of that and `compact` a quarter. Hand-reviewed models override the formula: gpt-6-astra
defaults to `balanced` (65536) with `deep` at 131072.
- Code mode, which current Codex models use for every tool call, returns 10,000 tokens of each
`exec` result unless the call asks for more, whatever the budget. When a job's budget, from the
job, the Codex configuration or Relay's default, is above that, Relay now tells the model the
budget and the `// @exec: {"max_output_tokens": N}` pragma in developer instructions, after any
the user configured. Native reviews print their budget note.
- Codex jobs accept `tool-output-token-limit` in the header, or `--tool-output-token-limit` on
start, task and review commands: a preset, a number of tokens, or `inherit`. The flag wins over
a prompt file's header. The budget is stored
with the job and reapplied on continue, resume and recovery. Other backends reject it.
`/relay:setup` shows the budget a job gets and its source, without changing configuration.

## 2.2.0 (2026-10-22)

From a survey of every T3 Code thread on three machines, about 360 relay jobs from 17 to 22 October,
Expand Down
3 changes: 2 additions & 1 deletion plugins/relay/commands/adversarial-review.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Run a read-only review that challenges the local diff's approach and design, on Codex, Grok, ZCode or Kimi (pick one with --backend); its findings come back as schema-checked JSON
argument-hint: '[--wait|--background] [--backend codex|grok|zcode|kimi] [--model <m>] [--effort <e>] [--grok-home H] [--kimi-home H] [--base <ref>] [--scope auto|working-tree|branch] [focus ...]'
argument-hint: '[--wait|--background] [--backend codex|grok|zcode|kimi] [--model <m>] [--effort <e>] [--grok-home H] [--kimi-home H] [--tool-output-token-limit compact|balanced|deep|inherit|N] [--base <ref>] [--scope auto|working-tree|branch] [focus ...]'
allowed-tools: Read, Glob, Grep, Bash(node:*), Bash(git:*), AskUserQuestion
---

Expand Down Expand Up @@ -35,6 +35,7 @@ Execution mode rules:

Backend selection (the flags mirror a job header's keys):
- `--backend codex|grok|zcode|kimi` picks the backend; without it, Codex. Nothing stores a default.
- `--tool-output-token-limit compact|balanced|deep|inherit|<tokens>` sets Codex's per-tool-result history budget for this review. Without it, the Codex configuration's budget applies, else the model's default Relay budget (`deep`, a quarter of its context window, or its reviewed value). Other backends reject it.
- `--model <model>` and `--effort <effort>` are passed as given. Without `--model` and `--effort`, Codex, Grok and Kimi use their configured defaults, and ZCode uses its selected default model at effort `high`, the plugin's default (ZCode's own is `max`), as a job header does.
- A Codex review runs in the Codex home (`~/.codex`, or `CODEX_HOME`); `--grok-home <name|path>` runs a Grok review in that Grok home, and `--kimi-home <name|path>` a Kimi review in that Kimi home.
- A combination a job would refuse (`--grok-home` without Grok, `--kimi-home` without Kimi) is refused the same way, before anything runs.
Expand Down
3 changes: 2 additions & 1 deletion plugins/relay/commands/review.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Run a read-only code review of the local git diff (working tree or branch): Codex's own reviewer on Codex, the plugin's review prompt on Grok, ZCode or Kimi; pick one with --backend
argument-hint: '[--wait|--background] [--backend codex|grok|zcode|kimi] [--model <m>] [--effort <e>] [--grok-home H] [--kimi-home H] [--base <ref>] [--scope auto|working-tree|branch]'
argument-hint: '[--wait|--background] [--backend codex|grok|zcode|kimi] [--model <m>] [--effort <e>] [--grok-home H] [--kimi-home H] [--tool-output-token-limit compact|balanced|deep|inherit|N] [--base <ref>] [--scope auto|working-tree|branch]'
allowed-tools: Read, Glob, Grep, Bash(node:*), Bash(git:*), AskUserQuestion
---

Expand Down Expand Up @@ -33,6 +33,7 @@ Execution mode rules:

Backend selection (the flags mirror a job header's keys):
- `--backend codex|grok|zcode|kimi` picks the backend; without it, Codex. Nothing stores a default.
- `--tool-output-token-limit compact|balanced|deep|inherit|<tokens>` sets Codex's per-tool-result history budget for this review. Without it, the Codex configuration's budget applies, else the model's default Relay budget (`deep`, a quarter of its context window, or its reviewed value). Other backends reject it.
- `--model <model>` and `--effort <effort>` are passed as given. Without `--model` and `--effort`, Codex, Grok and Kimi use their configured defaults, and ZCode uses its selected default model at effort `high`, the plugin's default (ZCode's own is `max`), as a job header does.
- A Codex review runs in the Codex home (`~/.codex`, or `CODEX_HOME`); `--grok-home <name|path>` runs a Grok review in that Grok home, and `--kimi-home <name|path>` a Kimi review in that Kimi home.
- A combination a job would refuse (`--grok-home` without Grok, `--kimi-home` without Kimi) is refused the same way, before anything runs.
Expand Down
6 changes: 6 additions & 0 deletions plugins/relay/commands/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ or `not-installed`, print its `detail` and the command it names, and go on; only
install offer below. Print every entry of a block's `warnings`; for Grok, a warning that the binary
is a script names the real binary and `GROK_BINARY`.

When a Codex block has `toolOutputTokenLimit`, show its `value` and `source` below the table.
For a null value, show the source text alone. This is the budget a job on the default model
gets: the Codex configuration's, else the model's default Relay budget (`deep`, a quarter of its
context window, or its reviewed value). Setup only reads it. A job's `tool-output-token-limit`
header (`compact`, `balanced`, `deep`, `inherit` or a number) overrides it for that job.

`--job-mcp-off <name,name...>` stores a user-level list. A Codex job passes
`mcp_servers.<name>.enabled=false` for each name the Codex home's `config.toml` defines as
`[mcp_servers.<name>]`; it skips an undefined name with a note and never passes it. Use
Expand Down
5 changes: 4 additions & 1 deletion plugins/relay/scripts/cli/start.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ function readLaunchFromPrompt(text) {

function readLaunchFromOptions(options) {
if (options["prompt-file"]) {
return readLaunchFromPrompt(readInputFile(options["prompt-file"]));
// A backend flag given beside a prompt file wins over the same key in its header.
const launch = readLaunchFromPrompt(readInputFile(options["prompt-file"]));
const flags = Object.entries(backendHeaderValues(options)).filter(([, value]) => value != null);
return { ...launch, values: { ...launch.values, ...Object.fromEntries(flags) } };
}
if (!options["brief-file"]) {
throw new UsageError("start needs --prompt-file <file>, or header options and --brief-file <file>.");
Expand Down
5 changes: 3 additions & 2 deletions plugins/relay/scripts/cli/task.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { backendNamed, backendWith } from "../lib/backends/index.mjs";
import { backendNamed, backendWith, BACKEND_HEADER_KEYS, backendHeaderValues } from "../lib/backends/index.mjs";
import { parseCommandInput, resolveCommandCwd, createTaskJob, outputResult } from "./shared.mjs";
import { default as fs } from "node:fs";
import { default as path } from "node:path";
Expand All @@ -18,7 +18,7 @@ const STOP_REVIEW_TASK_MARKER = "Run a stop-gate review of the previous Claude t

export async function handleTask(argv) {
const { options, positionals } = parseCommandInput(argv, {
valueOptions: ["backend", "model", "effort", "mode", "cwd", "cd", "prompt-file", "web-search", "network"],
valueOptions: ["backend", "model", "effort", "mode", "cwd", "cd", "prompt-file", "web-search", "network", ...Object.keys(BACKEND_HEADER_KEYS)],
booleanOptions: ["json", "write", "background"],
aliasMap: { m: "model" }
});
Expand All @@ -35,6 +35,7 @@ export async function handleTask(argv) {
const request = buildLaunchRequest(
{
backend: options.backend == null ? undefined : backendNamed(options.backend).name,
...backendHeaderValues(options),
model: options.model ?? "default",
effort: options.effort ?? "default",
mode: options.write ? "write" : options.mode,
Expand Down
1 change: 1 addition & 0 deletions plugins/relay/scripts/companion.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function printUsage() {
" companion.mjs start --model <m> --effort <e> [--mode yolo|write|read-only] [--network true|false]",
` [--fast] [--backend ${names}] ${homeFlags} [--web-search live|cached|disabled]`,
" [--cd <dir>] [--add-dir <dir>]... --brief-file <f>",
" Codex start/task/review/adversarial-review: [--tool-output-token-limit compact|balanced|deep|inherit|<tokens>]",
" companion.mjs wait <job> [--timeout-seconds N] [--state-only] [--exit-zero-while-running] [--json]",
" companion.mjs status [<job>] [--all] [--json]",
" companion.mjs result <job> [--state-only] [--json]",
Expand Down
Loading
Loading