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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected] @openai/codex@0.63.0 \
RUN npm install -g @anthropic-ai/[email protected] @openai/codex@0.73.0 \
&& npm cache clean --force

# Switch to the non-root user expected by the runner and set defaults
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down
2 changes: 1 addition & 1 deletion docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
45 changes: 26 additions & 19 deletions src/pitaya/runner/plugins/codex.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/test_codex_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Loading