diff --git a/CHANGELOG.md b/CHANGELOG.md index 608e801..407b03e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Codex plugin and agent image upgraded to `@openai/codex@0.63.0`. ([#113](https://github.com/tact-lang/pitaya/pull/113)) +- Codex plugin and agent image upgraded to `@openai/codex@0.73.0`. ([#113](https://github.com/tact-lang/pitaya/pull/113), [#150](https://github.com/tact-lang/pitaya/pull/150)) - Event sanitizer now redacts JWT-like tokens more accurately while leaving normal URLs untouched. ([#107](https://github.com/tact-lang/pitaya/pull/107)) - Logging overhauled for headless runs: console shows clean lifecycle lines by default, `--verbose`/`logging.console_verbose` surface agent/tool steps, and all raw DEBUG stays in per-run files. ([#117](https://github.com/tact-lang/pitaya/pull/117)) - Enabled Codex web search by default. ([#115](https://github.com/tact-lang/pitaya/pull/115)) diff --git a/Dockerfile b/Dockerfile index 4abd246..7b22096 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ RUN apt-get update && apt-get install -y \ RUN mkdir -p /workspace && chown -R node:node /workspace # Install both agent CLIs globally so they are available regardless of volume mounts -RUN npm install -g @anthropic-ai/claude-code@2.0.8 @openai/codex@0.63.0 \ +RUN npm install -g @anthropic-ai/claude-code@2.0.8 @openai/codex@0.73.0 \ && npm cache clean --force # Switch to the non-root user expected by the runner and set defaults diff --git a/README.md b/README.md index 311217e..2981478 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ uv tool install pitaya Pitaya runs agents inside Docker. You need a Docker image that contains the agent CLI(s) you plan to use: - `claude` (for `--plugin claude-code`) -- `codex` (for `--plugin codex`, Pitaya bundles `@openai/codex@0.63.0` by default) +- `codex` (for `--plugin codex`, Pitaya bundles `@openai/codex@0.73.0` by default) You can build a ready‑to‑use image from this repository’s [Dockerfile](./Dockerfile): diff --git a/docs/plugins.md b/docs/plugins.md index c82b4f6..bc4b834 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -3,7 +3,7 @@ Pitaya supports multiple agent tools via runner plugins. The two built‑ins are: - Claude Code (`--plugin claude-code`) -- Codex CLI (`--plugin codex`) — Pitaya runs `codex exec --json` from `@openai/codex@0.63.0` inside the agent container +- Codex CLI (`--plugin codex`) — Pitaya runs `codex exec --json` from `@openai/codex@0.73.0` inside the agent container ## Models diff --git a/src/pitaya/runner/plugins/codex.py b/src/pitaya/runner/plugins/codex.py index f2d69e7..8ea055a 100644 --- a/src/pitaya/runner/plugins/codex.py +++ b/src/pitaya/runner/plugins/codex.py @@ -9,7 +9,6 @@ from pitaya.shared.plugin import PluginCapabilities, RunnerPlugin from pitaya.exceptions import AgentError from .codex_env import ( - ENV_API_KEY, collect_codex_env, select_provider_base_url, select_provider_env_key, @@ -213,26 +212,34 @@ def _apply_provider_overrides( provider_name = kwargs.get("provider_name") or select_provider_name( provider_env_key ) - if provider_env_key and ( - provider_env_key != ENV_API_KEY or provider_base_url or provider_name - ): - provider_label = provider_name or "pitaya_env" - cmd += ["-c", f"model_provider={provider_label}"] - provider_display = provider_name or "PitayaProvider" - provider_parts = [f'name="{provider_display}"'] - if provider_base_url: - provider_parts.append(f'base_url="{provider_base_url}"') - provider_parts.append(f'env_key="{provider_env_key}"') - cmd += [ - "-c", - ( - f"model_providers.{provider_label}=" - "{" + ", ".join(provider_parts) + "}" - ), - ] + if not provider_env_key: if model: cmd += ["-c", f'model="{model}"'] - elif model: + return + + # Codex 0.71+ ships an OpenAI provider that expects interactive auth + # (auth.json / login). When Pitaya supplies OPENAI_API_KEY, we must + # force an env-backed provider to keep headless runs working. + provider_label = provider_name or "pitaya_env" + cmd += ["-c", f"model_provider={provider_label}"] + + provider_display = provider_name or "PitayaProvider" + provider_parts = [f'name="{provider_display}"'] + if provider_base_url: + provider_parts.append(f'base_url="{provider_base_url}"') + provider_parts.append(f'env_key="{provider_env_key}"') + + if provider_env_key in ("OPENAI_API_KEY", "CODEX_API_KEY"): + provider_parts.append('wire_api="responses"') + + cmd += [ + "-c", + ( + f"model_providers.{provider_label}=" + "{" + ", ".join(provider_parts) + "}" + ), + ] + if model: cmd += ["-c", f'model="{model}"'] def _append_session_and_prompt( diff --git a/tests/unit/test_codex_plugin.py b/tests/unit/test_codex_plugin.py index d294043..50a13a7 100644 --- a/tests/unit/test_codex_plugin.py +++ b/tests/unit/test_codex_plugin.py @@ -43,3 +43,26 @@ async def test_build_command_resume_with_prompt() -> None: assert any("features.web_search_request=true" in arg for arg in cmd) assert cmd[-1] == "do thing" assert cmd[-2] == "sess-123" + + +@pytest.mark.asyncio +async def test_build_command_forces_env_provider_for_openai_key( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.delenv("CODEX_ENV_KEY", raising=False) + monkeypatch.delenv("CODEX_API_KEY", raising=False) + monkeypatch.delenv("OPENROUTER_API_KEY", raising=False) + monkeypatch.setenv("OPENAI_API_KEY", "sk-test") + + plugin = CodexPlugin() + cmd = await plugin.build_command( + prompt="hello", + model="openai/gpt-5.1", + session_id=None, + ) + + # Should override provider even when using OPENAI_API_KEY so Codex doesn't + # require interactive OpenAI auth. + assert any("model_provider=pitaya_env" in arg for arg in cmd) + assert any("model_providers.pitaya_env" in arg for arg in cmd) + assert any('wire_api="responses"' in arg for arg in cmd)