Skip to content
Open
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: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Settings live in `~/.reverse-api/config.json` and can be edited via `/settings`
"opencode_provider": "opencode",
"copilot_model": "gpt-5",
"cursor_model": "composer-2.5",
"codex_model": null,
"output_dir": null,
"output_language": "python",
"real_time_sync": true,
Expand All @@ -127,7 +128,8 @@ Settings live in `~/.reverse-api/config.json` and can be edited via `/settings`
```

- **Models**: Sonnet 5.5, Opus 5.5 (most capable), Opus 4.6 and Haiku 5.5 (fastest). Sonnet 4.6 stays the default until you choose another; it and Haiku 4.5 are still accepted in config and by `--model`. For OpenCode see [models.dev](https://models.dev).
- **SDK**: `claude` (default), `opencode`, `cursor`, or `copilot` (GitHub Copilot).
- **SDK**: `claude` (default), `opencode`, `cursor`, `copilot` (GitHub Copilot), or `codex`.
- **Codex SDK**: `sdk: "codex"` reuses the local Codex CLI login, preferring the Codex Desktop-bundled CLI and falling back to `codex` on `PATH`. `codex_model` is optional; RAE defaults to `gpt-6-luna`. Codex browser capture currently requires `agent_provider: "auto"`, which provides Playwright MCP and a HAR. The adapter supplies per-run MCP configuration and does not edit the global Codex config.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The bundled Codex CLI preference is implemented only on Windows, but this documentation presents it as platform-independent. Qualify the preference as Windows-only, or implement bundled-CLI discovery on the other supported platforms.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At README.md, line 132:

<comment>The bundled Codex CLI preference is implemented only on Windows, but this documentation presents it as platform-independent. Qualify the preference as Windows-only, or implement bundled-CLI discovery on the other supported platforms.</comment>

<file context>
@@ -127,7 +128,8 @@ Settings live in `~/.reverse-api/config.json` and can be edited via `/settings`
 - **Models**: Sonnet 5.5, Opus 5.5 (most capable), Opus 4.6 and Haiku 5.5 (fastest). Sonnet 4.6 stays the default until you choose another; it and Haiku 4.5 are still accepted in config and by `--model`. For OpenCode see [models.dev](https://models.dev).
-- **SDK**: `claude` (default), `opencode`, `cursor`, or `copilot` (GitHub Copilot).
+- **SDK**: `claude` (default), `opencode`, `cursor`, `copilot` (GitHub Copilot), or `codex`.
+- **Codex SDK**: `sdk: "codex"` reuses the local Codex CLI login, preferring the Codex Desktop-bundled CLI and falling back to `codex` on `PATH`. `codex_model` is optional; RAE defaults to `gpt-6-luna`. Codex browser capture currently requires `agent_provider: "auto"`, which provides Playwright MCP and a HAR. The adapter supplies per-run MCP configuration and does not edit the global Codex config.
 - **Claude settings isolation**: Claude sessions don't load your Claude Code settings files, `CLAUDE.md`, hooks, plugins, permission rules or MCP servers, so your personal setup doesn't steer the agent. Organization-managed policy still applies, and Claude Code still reads its own state such as `~/.claude.json` and auto memory. If `~/.claude/settings.json` provides your authentication (`apiKeyHelper`, or Bedrock, Vertex or proxy variables under `env`), set `claude_setting_sources` to the layers to load, e.g. `["user"]` or `["user", "project", "local"]`. Each layer you list is loaded in full, including its hooks, plugins, permission rules and MCP servers.
 - **OpenCode setup**: with `sdk: "opencode"`, RAE reuses an existing server or downloads/starts `opencode-ai@latest` through `npx`; a global OpenCode installation is not required. Fresh configurations default to the free `opencode/big-pickle` model. `/settings` shows a loading spinner, then offers a searchable **OpenCode Provider / Model** picker populated from the server's connected, tool-capable catalog and marks free OpenCode options. Before creating a session, RAE validates the saved pair again and suggests currently available free models when configuration is invalid. Compatible older servers are reused with a version warning. Node.js 20+ is required for automatic startup. Password-protected servers use `OPENCODE_SERVER_PASSWORD` and optional `OPENCODE_SERVER_USERNAME`. Override startup with `OPENCODE_BASE_URL`, `RAE_OPENCODE_PACKAGE`, or `RAE_OPENCODE_AUTO_START=0`.
</file context>
Suggested change
- **Codex SDK**: `sdk: "codex"` reuses the local Codex CLI login, preferring the Codex Desktop-bundled CLI and falling back to `codex` on `PATH`. `codex_model` is optional; RAE defaults to `gpt-6-luna`. Codex browser capture currently requires `agent_provider: "auto"`, which provides Playwright MCP and a HAR. The adapter supplies per-run MCP configuration and does not edit the global Codex config.
- **Codex SDK**: `sdk: "codex"` reuses the local Codex CLI login. On Windows, it prefers the Codex Desktop-bundled CLI and falls back to `codex` on `PATH`. `codex_model` is optional; RAE defaults to `gpt-6-luna`. Codex browser capture currently requires `agent_provider: "auto"`, which provides Playwright MCP and a HAR. The adapter supplies per-run MCP configuration and does not edit the global Codex config.

- **Claude settings isolation**: Claude sessions don't load your Claude Code settings files, `CLAUDE.md`, hooks, plugins, permission rules or MCP servers, so your personal setup doesn't steer the agent. Organization-managed policy still applies, and Claude Code still reads its own state such as `~/.claude.json` and auto memory. If `~/.claude/settings.json` provides your authentication (`apiKeyHelper`, or Bedrock, Vertex or proxy variables under `env`), set `claude_setting_sources` to the layers to load, e.g. `["user"]` or `["user", "project", "local"]`. Each layer you list is loaded in full, including its hooks, plugins, permission rules and MCP servers.
- **OpenCode setup**: with `sdk: "opencode"`, RAE reuses an existing server or downloads/starts `opencode-ai@latest` through `npx`; a global OpenCode installation is not required. Fresh configurations default to the free `opencode/big-pickle` model. `/settings` shows a loading spinner, then offers a searchable **OpenCode Provider / Model** picker populated from the server's connected, tool-capable catalog and marks free OpenCode options. Before creating a session, RAE validates the saved pair again and suggests currently available free models when configuration is invalid. Compatible older servers are reused with a version warning. Node.js 20+ is required for automatic startup. Password-protected servers use `OPENCODE_SERVER_PASSWORD` and optional `OPENCODE_SERVER_USERNAME`. Override startup with `OPENCODE_BASE_URL`, `RAE_OPENCODE_PACKAGE`, or `RAE_OPENCODE_AUTO_START=0`.
- **Ollama through OpenCode**: choose provider `ollama` in `/settings`; RAE starts an installed Ollama daemon if needed, lists only installed models with tool calling and 64k+ context, and supplies OpenCode's provider config inline. Models are never downloaded silently. Override with `RAE_OLLAMA_BASE_URL` or `RAE_OLLAMA_AUTO_START=0`.
Expand Down
4 changes: 3 additions & 1 deletion skills/reverse-api-engineer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ reverse-api-engineer show <run_id> --json
reverse-api-engineer agent -p "<goal>" -u "<start url>" --dry-run
```

This validates the prompt, URL, config and environment without opening a browser. Fix every `error` check before running. A `warn` on the API key is fine when the SDK authenticates another way (for example a logged-in Claude Code).
This validates the prompt, URL, config and environment without opening a browser. Fix every `error` check before running. A `warn` on the API key is fine when the SDK authenticates another way (for example a logged-in Claude Code or Codex CLI).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The Codex example doesn't fit this sentence. Codex never gets an API-key warn; its preflight reports sdk:codex login status instead. Consider dropping "or Codex CLI" here, since the Codex login is covered by the new paragraph below.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At skills/reverse-api-engineer/SKILL.md, line 48:

<comment>The Codex example doesn't fit this sentence. Codex never gets an API-key `warn`; its preflight reports `sdk:codex` login status instead. Consider dropping "or Codex CLI" here, since the Codex login is covered by the new paragraph below.</comment>

<file context>
@@ -45,10 +45,12 @@ reverse-api-engineer show <run_id> --json

-This validates the prompt, URL, config and environment without opening a browser. Fix every error check before running. A warn on the API key is fine when the SDK authenticates another way (for example a logged-in Claude Code).
+This validates the prompt, URL, config and environment without opening a browser. Fix every error check before running. A warn on the API key is fine when the SDK authenticates another way (for example a logged-in Claude Code or Codex CLI).

would_run.agent_provider in the output is the browser the run will use. chrome-mcp means the user's own Chrome with their logins; auto and agent-browser start a fresh browser without them. When the task needs the user's login and the provider is not chrome-mcp, ask before switching it (see sessions).
</file context>


</details>

```suggestion
This validates the prompt, URL, config and environment without opening a browser. Fix every `error` check before running. A `warn` on the API key is fine when the SDK authenticates another way (for example a logged-in Claude Code).


`would_run.agent_provider` in the output is the browser the run will use. `chrome-mcp` means the user's own Chrome with their logins; `auto` and `agent-browser` start a fresh browser without them. When the task needs the user's login and the provider is not `chrome-mcp`, ask before switching it (see [sessions](references/sessions.md)).

The `codex` SDK reuses the local Codex CLI login. In the current RAE adapter, Codex agent capture requires `agent_provider: "auto"` so Playwright MCP records the HAR; it does not support `chrome-mcp` or `agent-browser` capture. `agent --dry-run` reports Codex login and browser-provider checks before opening a browser.

For `chrome-mcp`, the `chrome-mcp:auto-connect` check covers the connection to the user's Chrome:

- `ok`: Chrome is reachable. The user still has to accept Chrome's connection prompt when the run starts.
Expand Down
6 changes: 4 additions & 2 deletions skills/reverse-api-engineer/references/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ rae calls a model through the SDK selected in its config:
| `opencode` | An OpenCode server rae starts or reuses. Defaults to a free model; can route to local models through Ollama |
| `copilot` | GitHub Copilot credentials (`GITHUB_COPILOT_TOKEN`) |
| `cursor` | `CURSOR_API_KEY` |
| `codex` | Existing local Codex CLI login; Codex Desktop's bundled CLI is preferred when present, then `codex` on `PATH` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The Desktop-bundled CLI preference only exists on Windows (resolve_codex_command returns early only under sys.platform == "win32"). Qualify this row, for example "on Windows, Codex Desktop's bundled CLI is preferred when present; otherwise codex on PATH."

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At skills/reverse-api-engineer/references/setup.md, line 20:

<comment>The Desktop-bundled CLI preference only exists on Windows (`resolve_codex_command` returns early only under `sys.platform == "win32"`). Qualify this row, for example "on Windows, Codex Desktop's bundled CLI is preferred when present; otherwise `codex` on `PATH`."</comment>

<file context>
@@ -17,8 +17,9 @@ rae calls a model through the SDK selected in its config:
 | `opencode` | An OpenCode server rae starts or reuses. Defaults to a free model; can route to local models through Ollama |
 | `copilot` | GitHub Copilot credentials (`GITHUB_COPILOT_TOKEN`) |
 | `cursor` | `CURSOR_API_KEY` |
+| `codex` | Existing local Codex CLI login; Codex Desktop's bundled CLI is preferred when present, then `codex` on `PATH` |
 
-`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own.
</file context>
Suggested change
| `codex` | Existing local Codex CLI login; Codex Desktop's bundled CLI is preferred when present, then `codex` on `PATH` |
| `codex` | Existing local Codex CLI login; on Windows, Codex Desktop's bundled CLI is preferred when present, then `codex` on `PATH` |


`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own.
`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own. Codex defaults to `gpt-6-luna` unless `codex_model` or `--model` overrides it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: --model is not a usable Codex model override: the agent option accepts only Claude model IDs, so it rejects gpt-6-luna and arbitrary Codex IDs. Remove --model from this Codex documentation or make the CLI's model validation SDK-aware.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At skills/reverse-api-engineer/references/setup.md, line 22:

<comment>`--model` is not a usable Codex model override: the agent option accepts only Claude model IDs, so it rejects `gpt-6-luna` and arbitrary Codex IDs. Remove `--model` from this Codex documentation or make the CLI's model validation SDK-aware.</comment>

<file context>
@@ -17,8 +17,9 @@ rae calls a model through the SDK selected in its config:
+| `codex` | Existing local Codex CLI login; Codex Desktop's bundled CLI is preferred when present, then `codex` on `PATH` |
 
-`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own.
+`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own. Codex defaults to `gpt-6-luna` unless `codex_model` or `--model` overrides it.
 
 ## Settings
</file context>
Suggested change
`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own. Codex defaults to `gpt-6-luna` unless `codex_model` or `--model` overrides it.
`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own. Codex defaults to `gpt-6-luna` unless `codex_model` overrides it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: -m/--model now also applies to the codex SDK, but the Settings intro still says it is for the claude SDK only. Update that sentence ("Only two have per-run flags on agent... -m/--model (for the claude and codex SDKs)") so the two passages agree.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At skills/reverse-api-engineer/references/setup.md, line 22:

<comment>`-m/--model` now also applies to the `codex` SDK, but the Settings intro still says it is for the `claude` SDK only. Update that sentence ("Only two have per-run flags on `agent`... `-m/--model` (for the `claude` and `codex` SDKs)") so the two passages agree.</comment>

<file context>
@@ -17,8 +17,9 @@ rae calls a model through the SDK selected in its config:
+| `codex` | Existing local Codex CLI login; Codex Desktop's bundled CLI is preferred when present, then `codex` on `PATH` |
 
-`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own.
+`agent --dry-run` reports which of these it can see. A missing env var is a warning, not an error, because some SDKs find credentials on their own. Codex defaults to `gpt-6-luna` unless `codex_model` or `--model` overrides it.
 
 ## Settings
</file context>


## Settings

Expand All @@ -28,8 +29,9 @@ Settings live in `~/.reverse-api/config.json`. Only two have per-run flags on `a
|---|---|
| `agent_provider` | `chrome-mcp` (the user's real Chrome, no HAR; default for new configs in recent versions), `auto` (Playwright MCP, records a HAR; the default in older versions), `agent-browser` (Vercel agent-browser CLI, records a HAR) |
| `output_language` | `python`, `javascript`, `typescript`, `go`, `java`, `csharp`, `php`, `ruby`, `c`, `powershell` |
| `sdk` | `claude`, `opencode`, `copilot`, `cursor` |
| `sdk` | `claude`, `opencode`, `copilot`, `cursor`, `codex` |
| `claude_code_model` | Model for the `claude` SDK; `agent -m` overrides it per run |
| `codex_model` | Optional model for the `codex` SDK; defaults to `gpt-6-luna` |
| `output_dir` | Where runs are stored; `null` means `~/.reverse-api/runs` |

The user can also change them interactively by running `reverse-api-engineer` and typing `/settings`.
Expand Down
103 changes: 94 additions & 9 deletions src/reverse_api/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ def default_model_for_configured_sdk(sdk: str | None = None) -> str:
return str(config_manager.get("copilot_model") or "gpt-5")
if s == "cursor":
return str(config_manager.get("cursor_model") or "composer-2.5")
if s == "codex":
return str(config_manager.get("codex_model") or "gpt-6-luna")
return str(config_manager.get("claude_code_model") or "claude-sonnet-4-6")


Expand Down Expand Up @@ -453,11 +455,26 @@ def _build_dry_run_payload(
"copilot": "GITHUB_COPILOT_TOKEN",
"cursor": "CURSOR_API_KEY",
}.get(sdk)
if sdk_env_var is None:
if sdk == "codex":
from .codex_engineer import codex_login_status

codex_ok, codex_message = codex_login_status()
checks.append({
"name": "sdk:codex",
"status": "ok" if codex_ok else "error",
"message": codex_message,
})
if agent_provider != "auto":
checks.append({
"name": "sdk:codex:browser",
"status": "error",
"message": "Codex agent capture requires agent_provider='auto' to save a Playwright HAR",
})
elif sdk_env_var is None:
checks.append({
"name": "sdk",
"status": "error",
"message": f"unknown sdk {sdk!r}; expected 'claude', 'opencode', 'copilot', or 'cursor'",
"message": f"unknown sdk {sdk!r}; expected 'claude', 'opencode', 'copilot', 'cursor', or 'codex'",
})
elif not os.environ.get(sdk_env_var):
checks.append({
Expand Down Expand Up @@ -582,14 +599,16 @@ def _build_dry_run_payload(
"opencode": "opencode_model",
"copilot": "copilot_model",
"cursor": "cursor_model",
"codex": "codex_model",
}.get(sdk, "claude_code_model")
sdk_default_model = {
"claude": "claude-sonnet-4-6",
"opencode": "claude-opus-4-6",
"copilot": "gpt-5",
"cursor": "composer-2.5",
"codex": "gpt-6-luna",
}.get(sdk, "claude-sonnet-4-6")
resolved_model = model or config_manager.get(sdk_model_key, sdk_default_model)
resolved_model = model or config_manager.get(sdk_model_key) or sdk_default_model

return {
"schema_version": AGENT_JSON_SCHEMA_VERSION,
Expand Down Expand Up @@ -855,6 +874,7 @@ def _handle_settings_action(mode_color: str = THEME_PRIMARY) -> bool: # noqa: C
choices = [
Choice(title="Agent Provider", value="agent_provider"),
Choice(title="Claude Code Model", value="claude_code_model"),
Choice(title="Codex Model", value="codex_model"),
Choice(title="Copilot Model", value="copilot_model"),
Choice(title="Cursor Model", value="cursor_model"),
Choice(title="Cursor Web Search", value="cursor_web_search"),
Expand Down Expand Up @@ -906,6 +926,7 @@ def _handle_settings_action(mode_color: str = THEME_PRIMARY) -> bool: # noqa: C
elif action == "sdk":
sdk_choices = [
Choice(title="claude", value="claude"),
Choice(title="codex", value="codex"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Choosing codex in /settings leaves agent_provider at its default chrome-mcp, so every agent run then fails in CodexAutoEngineer with the "requires agent_provider='auto'" error. When the codex sdk is selected, also set agent_provider to auto, or block the selection with a clear message.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/reverse_api/cli.py, line 929:

<comment>Choosing codex in /settings leaves `agent_provider` at its default `chrome-mcp`, so every `agent` run then fails in `CodexAutoEngineer` with the "requires agent_provider='auto'" error. When the codex sdk is selected, also set `agent_provider` to `auto`, or block the selection with a clear message.</comment>

<file context>
@@ -906,6 +926,7 @@ def _handle_settings_action(mode_color: str = THEME_PRIMARY) -> bool:  # noqa: C
     elif action == "sdk":
         sdk_choices = [
             Choice(title="claude", value="claude"),
+            Choice(title="codex", value="codex"),
             Choice(title="copilot", value="copilot"),
             Choice(title="cursor", value="cursor"),
</file context>

Choice(title="copilot", value="copilot"),
Choice(title="cursor", value="cursor"),
Choice(title="opencode", value="opencode"),
Expand All @@ -927,6 +948,17 @@ def _handle_settings_action(mode_color: str = THEME_PRIMARY) -> bool: # noqa: C
config_manager.set("sdk", sdk)
console.print(f" [dim]updated[/dim] sdk: {sdk}\n")

elif action == "codex_model":
current = str(config_manager.get("codex_model") or "")
model = questionary.text(
"Codex model (blank uses GPT-6 Luna)",
default=current,
).ask()
if model is not None:
selected = model.strip() or None
config_manager.set("codex_model", selected)
console.print(f" [dim]updated[/dim] codex model: {selected or 'gpt-6-luna'}\n")

elif action == "output_language":
lang_choices = [
Choice(title="python", value="python"),
Expand Down Expand Up @@ -1524,7 +1556,7 @@ def _session_browser_note(agent_provider: str, headless: bool) -> str | None:
return None


def run_auto_capture(
def run_auto_capture( # noqa: C901 - SDK/provider/session lifecycle dispatch
prompt: str | None = None,
url: str | None = None,
model: str | None = None,
Expand Down Expand Up @@ -1619,16 +1651,32 @@ def run_auto_capture(
url=url,
model=model,
mode=mode_label,
sdk=sdk,
paths={"har_dir": str(get_har_dir(run_id, output_dir))},
)

try:
output_language = config_manager.get("output_language", "python")
from .auto_engineer import ClaudeAutoEngineer, CopilotAutoEngineer, OpenCodeAutoEngineer
from .codex_engineer import CodexAutoEngineer
from .cursor_engineer import CursorAutoEngineer

engineer: ClaudeAutoEngineer | CopilotAutoEngineer | OpenCodeAutoEngineer | CursorAutoEngineer
if sdk == "opencode":
engineer: ClaudeAutoEngineer | CopilotAutoEngineer | OpenCodeAutoEngineer | CursorAutoEngineer | CodexAutoEngineer
if sdk == "codex":
engineer = CodexAutoEngineer(
run_id=run_id,
prompt=prompt,
codex_model=model or config_manager.get("codex_model", "default"),
output_dir=output_dir,
agent_provider=agent_provider,
enable_sync=config_manager.get("real_time_sync", False),
sdk=sdk,
output_language=output_language,
interactive=interactive,
headless=headless,
start_url=url,
)
elif sdk == "opencode":
from .auto_engineer import OpenCodeAutoEngineer

engineer = OpenCodeAutoEngineer(
Expand Down Expand Up @@ -1704,7 +1752,10 @@ def run_auto_capture(
if resume:
core.resume_session, core.cost_before = _resume_point(resume)
# A new browser stretch records its own HAR instead of replacing the run's capture.
engineer.mcp_run_id = f"{run_id}-{int(time.time())}"
if hasattr(engineer, "mcp_run_id"):
engineer.mcp_run_id = f"{run_id}-{int(time.time())}"
if sdk == "codex":
engineer.har_path = get_har_dir(engineer.mcp_run_id, output_dir) / "recording.har"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Resumed Codex captures are written under a timestamped HAR directory but that path is not persisted or used by the JSON payload. Preserve the returned HAR path in run history and have payload construction use it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/reverse_api/cli.py, line 1758:

<comment>Resumed Codex captures are written under a timestamped HAR directory but that path is not persisted or used by the JSON payload. Preserve the returned HAR path in run history and have payload construction use it.</comment>

<file context>
@@ -1704,7 +1752,10 @@ def run_auto_capture(
+            if hasattr(engineer, "mcp_run_id"):
+                engineer.mcp_run_id = f"{run_id}-{int(time.time())}"
+            if sdk == "codex":
+                engineer.har_path = get_har_dir(engineer.mcp_run_id, output_dir) / "recording.har"
 
         if json_event_sink is not None:
</file context>


if json_event_sink is not None:
from .json_stream import attach_json_stream_to_engineer
Expand Down Expand Up @@ -1755,6 +1806,15 @@ def run_auto_capture(
"mode": mode_label,
"script_path": (result or {}).get("script_path"),
"usage": (result or {}).get("usage", {}),
**(
{
"har_path": (result or {}).get("har_path"),
"har_entries": (result or {}).get("har_entries", 0),
"mcp_tool_calls": (result or {}).get("mcp_tool_calls", []),
}
if sdk == "codex"
else {}
),
**(
{"error": "interrupted"}
if interrupted
Expand Down Expand Up @@ -2032,6 +2092,7 @@ def run_engineer(
"opencode": resume_model or config_manager.get("opencode_model", DEFAULT_OPENCODE_MODEL),
"copilot": resume_model or config_manager.get("copilot_model", "gpt-5"),
"cursor": model or config_manager.get("cursor_model", "composer-2.5"),
"codex": resume_model or model or config_manager.get("codex_model") or "gpt-6-luna",
}.get(sdk) or model or config_manager.get("claude_code_model", "claude-sonnet-4-6")
on_session = _session_recorder(run_id, "engineer", sdk, session_model, output_dir, output_mode)

Expand Down Expand Up @@ -2101,6 +2162,26 @@ def run_engineer(
cost_before=cost_before,
on_session=on_session,
)
elif sdk == "codex":
result = run_reverse_engineering(
run_id=run_id,
har_path=har_path,
prompt=prompt,
model=session_model,
codex_model=session_model,
output_dir=output_dir,
sdk=sdk,
enable_sync=enable_sync,
additional_instructions=additional_instructions,
is_fresh=is_fresh,
output_language=output_language,
output_mode=output_mode,
interactive=interactive,
json_event_sink=json_event_sink,
resume_session=resume_session,
cost_before=cost_before,
on_session=on_session,
)
else:
result = run_reverse_engineering(
run_id=run_id,
Expand Down Expand Up @@ -2456,8 +2537,12 @@ def _show_error_payload(message: str, rid: str | None = None) -> dict:
usage = run.get("usage", {})
details["input_tokens"] = usage.get("input_tokens", 0)
details["output_tokens"] = usage.get("output_tokens", 0)
details["cache_creation_input_tokens"] = usage.get("cache_creation_input_tokens", 0)
details["cache_read_input_tokens"] = usage.get("cache_read_input_tokens", 0)
details["cache_creation_input_tokens"] = usage.get(
"cache_creation_input_tokens", usage.get("cache_write_tokens", usage.get("cache_creation_tokens", 0))
)
details["cache_read_input_tokens"] = usage.get(
"cache_read_input_tokens", usage.get("cache_read_tokens", 0)
)

# Artifact paths with existence
har_dir = base_dir / "har" / rid
Expand Down
Loading