inspect-codex-app-server 2026.10.29.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. inspect_codex_app_server-2026.10.29.1/.github/workflows/ci.yml +196 -0
  2. inspect_codex_app_server-2026.10.29.1/.gitignore +6 -0
  3. inspect_codex_app_server-2026.10.29.1/AGENTS.md +5 -0
  4. inspect_codex_app_server-2026.10.29.1/LICENSE +21 -0
  5. inspect_codex_app_server-2026.10.29.1/PKG-INFO +189 -0
  6. inspect_codex_app_server-2026.10.29.1/README.md +174 -0
  7. inspect_codex_app_server-2026.10.29.1/TEST_POLICY.md +47 -0
  8. inspect_codex_app_server-2026.10.29.1/docs/releases.md +51 -0
  9. inspect_codex_app_server-2026.10.29.1/docs/technical-notes.md +106 -0
  10. inspect_codex_app_server-2026.10.29.1/examples/arithmetic.py +19 -0
  11. inspect_codex_app_server-2026.10.29.1/examples/compose.yaml +6 -0
  12. inspect_codex_app_server-2026.10.29.1/pyproject.toml +37 -0
  13. inspect_codex_app_server-2026.10.29.1/scripts/generate_protocol.py +158 -0
  14. inspect_codex_app_server-2026.10.29.1/scripts/update_dependencies.py +175 -0
  15. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/__init__.py +8 -0
  16. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/_generated_protocol.py +1628 -0
  17. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/_generated_rpc.py +135 -0
  18. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/agent.py +266 -0
  19. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/auth.py +30 -0
  20. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/client.py +1049 -0
  21. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/compat.py +226 -0
  22. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/container.py +353 -0
  23. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/evidence.py +201 -0
  24. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/mapping.py +1630 -0
  25. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/policy.py +101 -0
  26. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/py.typed +0 -0
  27. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/runtime/__init__.py +1 -0
  28. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/runtime/budget.py +59 -0
  29. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/runtime/limits.py +100 -0
  30. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/runtime/modelapi.py +24 -0
  31. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/runtime/process.py +62 -0
  32. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/session.py +1769 -0
  33. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/solver.py +254 -0
  34. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/subagent.py +67 -0
  35. inspect_codex_app_server-2026.10.29.1/src/inspect_codex_app_server/tools.py +90 -0
  36. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/__init__.py +1 -0
  37. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/conftest.py +97 -0
  38. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/support.py +275 -0
  39. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_accounting.py +429 -0
  40. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_agent.py +982 -0
  41. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_client.py +419 -0
  42. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_container.py +302 -0
  43. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_evaluation.py +406 -0
  44. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_mapping.py +508 -0
  45. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_native_configuration.py +201 -0
  46. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_native_inputs.py +178 -0
  47. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_native_models.py +171 -0
  48. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_native_resume.py +149 -0
  49. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_native_subagents.py +299 -0
  50. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_native_tools.py +378 -0
  51. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_provisioning.py +326 -0
  52. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_session.py +172 -0
  53. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_shutdown.py +367 -0
  54. inspect_codex_app_server-2026.10.29.1/tests/app_server_tests/test_transport.py +192 -0
  55. inspect_codex_app_server-2026.10.29.1/tests/conftest.py +17 -0
  56. inspect_codex_app_server-2026.10.29.1/uv.lock +3225 -0
@@ -0,0 +1,196 @@
1
+ name: Compatibility and releases
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ schedule:
7
+ - cron: '17 * * * *'
8
+ workflow_dispatch:
9
+ inputs:
10
+ release:
11
+ description: Publish a release even if dependency versions are unchanged
12
+ type: boolean
13
+ default: false
14
+ repository_dispatch:
15
+ types: [upstream-release]
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ concurrency:
21
+ group: compatibility-${{ github.ref }}
22
+ cancel-in-progress: false
23
+
24
+ jobs:
25
+ check:
26
+ name: Check pinned or candidate dependencies
27
+ runs-on: ubuntu-24.04
28
+ timeout-minutes: 20
29
+ outputs:
30
+ changed: ${{ steps.candidate.outputs.changed }}
31
+ version: ${{ steps.candidate.outputs.version }}
32
+ tag: ${{ steps.candidate.outputs.tag }}
33
+ prerelease: ${{ steps.candidate.outputs.prerelease }}
34
+ base_sha: ${{ github.sha }}
35
+ steps:
36
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
37
+ with:
38
+ persist-credentials: false
39
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
40
+ with:
41
+ python-version: '3.13'
42
+ - name: Install locked development dependencies
43
+ run: uv sync --locked --group dev
44
+ - name: Prepare dependency updates or a requested release
45
+ id: candidate
46
+ if: >-
47
+ github.ref_name == github.event.repository.default_branch &&
48
+ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' ||
49
+ github.event_name == 'repository_dispatch')
50
+ env:
51
+ GH_TOKEN: ${{ github.token }}
52
+ RELEASE_REQUESTED: ${{ github.event_name == 'workflow_dispatch' && inputs.release }}
53
+ run: |
54
+ release_args=()
55
+ if test "$RELEASE_REQUESTED" = true; then
56
+ release_args+=(--release)
57
+ fi
58
+ uv run --no-sync python scripts/update_dependencies.py --output-dir "$RUNNER_TEMP/release" "${release_args[@]}"
59
+ - name: Lock candidate dependencies and regenerate the native protocol
60
+ if: steps.candidate.outputs.changed == 'true'
61
+ run: |
62
+ uv lock
63
+ uv sync --locked --group dev
64
+ uv run --no-sync python scripts/generate_protocol.py
65
+ - name: Check protocol, lint, and types
66
+ if: steps.candidate.outputs.changed == 'true' || steps.candidate.outcome == 'skipped'
67
+ run: |
68
+ uv run --no-sync python scripts/generate_protocol.py --check
69
+ uv run --no-sync ruff check .
70
+ uv run --no-sync ty check
71
+ - name: Default tests, including the real pinned Codex executable
72
+ if: steps.candidate.outputs.changed == 'true' || steps.candidate.outcome == 'skipped'
73
+ run: uv run --no-sync pytest -q --keep-failed-artifacts="$RUNNER_TEMP/failures" --junitxml="$RUNNER_TEMP/default.xml"
74
+ - name: Prepare Docker acceptance
75
+ if: steps.candidate.outputs.changed == 'true' || steps.candidate.outcome == 'skipped'
76
+ run: docker pull python:3.13-slim
77
+ - name: Docker acceptance
78
+ if: steps.candidate.outputs.changed == 'true' || steps.candidate.outcome == 'skipped'
79
+ run: uv run --no-sync pytest -q -m containers --keep-failed-artifacts="$RUNNER_TEMP/failures" --junitxml="$RUNNER_TEMP/containers.xml"
80
+ - name: Build wheel and source archive
81
+ if: steps.candidate.outputs.changed == 'true' || steps.candidate.outcome == 'skipped'
82
+ run: uv build --no-sources --out-dir "$RUNNER_TEMP/release/dist"
83
+ - name: Save candidate source changes for review or publication
84
+ if: always() && steps.candidate.outputs.changed == 'true'
85
+ run: git diff --binary > "$RUNNER_TEMP/release/candidate.patch"
86
+ - name: Retain passing release candidate
87
+ if: steps.candidate.outputs.changed == 'true'
88
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
89
+ with:
90
+ name: release-candidate
91
+ path: ${{ runner.temp }}/release
92
+ if-no-files-found: error
93
+ retention-days: 14
94
+ - name: Retain failure diagnostics and candidate changes
95
+ if: failure()
96
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
97
+ with:
98
+ name: compatibility-failure
99
+ include-hidden-files: true
100
+ path: |
101
+ ${{ runner.temp }}/failures
102
+ ${{ runner.temp }}/*.xml
103
+ ${{ runner.temp }}/release
104
+ if-no-files-found: ignore
105
+ retention-days: 7
106
+
107
+ record:
108
+ name: Record tested pins and draft the GitHub release
109
+ needs: check
110
+ if: needs.check.outputs.changed == 'true'
111
+ runs-on: ubuntu-24.04
112
+ timeout-minutes: 5
113
+ permissions:
114
+ contents: write
115
+ steps:
116
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
117
+ with:
118
+ ref: ${{ needs.check.outputs.base_sha }}
119
+ fetch-depth: 0
120
+ - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
121
+ with:
122
+ name: release-candidate
123
+ path: ${{ runner.temp }}/release
124
+ - name: Commit and tag exactly the tested source
125
+ env:
126
+ RELEASE_VERSION: ${{ needs.check.outputs.version }}
127
+ RELEASE_TAG: ${{ needs.check.outputs.tag }}
128
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
129
+ run: |
130
+ git apply --check "$RUNNER_TEMP/release/candidate.patch"
131
+ git apply "$RUNNER_TEMP/release/candidate.patch"
132
+ git add pyproject.toml uv.lock README.md src/inspect_codex_app_server/container.py src/inspect_codex_app_server/_generated_protocol.py src/inspect_codex_app_server/_generated_rpc.py
133
+ expected_tree=$(git write-tree)
134
+ if git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then
135
+ # A rerun after a partial publication must reuse the tested tree.
136
+ test "$(git rev-parse "$RELEASE_TAG^{tree}")" = "$expected_tree"
137
+ else
138
+ git config user.name 'github-actions[bot]'
139
+ git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
140
+ git commit -m "Release inspect-codex-app-server $RELEASE_VERSION"
141
+ git tag -a "$RELEASE_TAG" -m "Compatibility release $RELEASE_VERSION"
142
+ # A concurrent default-branch change rejects the entire push.
143
+ git push --atomic origin "HEAD:refs/heads/$DEFAULT_BRANCH" "refs/tags/$RELEASE_TAG"
144
+ fi
145
+ - name: Attach the tested distributions to a draft release
146
+ env:
147
+ GH_TOKEN: ${{ github.token }}
148
+ GH_REPO: ${{ github.repository }}
149
+ RELEASE_VERSION: ${{ needs.check.outputs.version }}
150
+ RELEASE_TAG: ${{ needs.check.outputs.tag }}
151
+ PRERELEASE: ${{ needs.check.outputs.prerelease }}
152
+ run: |
153
+ if gh release view "$RELEASE_TAG" --json isDraft > "$RUNNER_TEMP/existing-release.json"; then
154
+ if jq -e '.isDraft' "$RUNNER_TEMP/existing-release.json"; then
155
+ gh release upload "$RELEASE_TAG" "$RUNNER_TEMP"/release/dist/* --clobber
156
+ fi
157
+ else
158
+ options=(--verify-tag --draft --title "inspect-codex-app-server $RELEASE_VERSION" --notes-file "$RUNNER_TEMP/release/notes.md")
159
+ if test "$PRERELEASE" = true; then
160
+ options+=(--prerelease)
161
+ fi
162
+ gh release create "$RELEASE_TAG" "${options[@]}" "$RUNNER_TEMP"/release/dist/*
163
+ fi
164
+
165
+ publish:
166
+ name: Publish the tested distributions to PyPI
167
+ needs: record
168
+ runs-on: ubuntu-24.04
169
+ timeout-minutes: 10
170
+ environment: pypi
171
+ permissions:
172
+ id-token: write
173
+ steps:
174
+ - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
175
+ with:
176
+ name: release-candidate
177
+ path: candidate
178
+ - uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # release/v1
179
+ with:
180
+ packages-dir: candidate/dist
181
+ skip-existing: true
182
+
183
+ release:
184
+ name: Publish the GitHub release after PyPI succeeds
185
+ needs: [check, publish]
186
+ runs-on: ubuntu-24.04
187
+ timeout-minutes: 5
188
+ permissions:
189
+ contents: write
190
+ steps:
191
+ - name: Publish the draft
192
+ env:
193
+ GH_TOKEN: ${{ github.token }}
194
+ GH_REPO: ${{ github.repository }}
195
+ RELEASE_TAG: ${{ needs.check.outputs.tag }}
196
+ run: gh release edit "$RELEASE_TAG" --draft=false
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ dist/
6
+ logs/
@@ -0,0 +1,5 @@
1
+ # Inspect Codex package guidance
2
+
3
+ Follow [TEST_POLICY.md](TEST_POLICY.md) when changing this package or its tests.
4
+
5
+ Read [technical-notes.md](docs/technical-notes.md) for additional information about how this package works.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ihor Makhynia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,189 @@
1
+ Metadata-Version: 2.5
2
+ Name: inspect-codex-app-server
3
+ Version: 2026.10.29.1
4
+ Summary: Run native Codex app-server sessions in Inspect AI
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: aiohttp>=3.9
9
+ Requires-Dist: anyio>=4
10
+ Requires-Dist: inspect-ai==0.3.286
11
+ Requires-Dist: pydantic>=2
12
+ Requires-Dist: shortuuid>=1
13
+ Requires-Dist: typing-extensions>=4.12
14
+ Description-Content-Type: text/markdown
15
+
16
+ # inspect-codex-app-server
17
+
18
+ An [Inspect AI](https://inspect.aisi.org.uk/) solver and agent that run native Codex sessions inside Docker sandboxes via the Codex App Server JSON-RPC protocol.
19
+
20
+ Codex manages inference, tool execution, and subagent orchestration, while Inspect AI manages evaluation, scoring, tool governance, and token, message, and time limits. Cost and model-generation turn limits are unsupported.
21
+
22
+ ## Key Features
23
+
24
+ - **Use Your ChatGPT Subscription**: Run evaluations directly against your ChatGPT subscription plan (or API key) using native Codex authentication.
25
+ - **Docker Sandbox Execution**: Runs Codex as root with full access inside Docker sandboxes, including filesystem tools, bash commands, and subagents.
26
+ - **Inspect Agent**: `codex_agent()` composes with `as_solver()`, `as_tool()`, `handoff()` and `run()`, with react-style `submit`, `attempts`, `on_continue` and `retry_refusals`.
27
+ - **Inspect Tool Bridging**: Exposes Inspect tools (`use_tools`, MCP tool sources) directly to Codex as dynamic tools with validation, approval and review handling.
28
+ - **Inspect Approvals**: Routes Codex's native command and file-change approval requests to the active Inspect approver.
29
+ - **Native Skills, Web Search and Roles**: Installs Inspect skills as Codex skills, sets the native web search mode, and declares agent roles for Codex's child agents.
30
+ - **Operator Interruption and Checkpoints**: Responds to operator interrupts and messages through Inspect's agent channel, and resumes from Inspect checkpoints.
31
+ - **Session & Quota Management**: Handles rate limits, quota pauses (`quota_hold_sec`), transient turn-start retries with backoff, and token budgeting.
32
+ - **Transcript & Usage Tracking**: Reconstructs model activity, tool events, and subagent spans into Inspect transcripts with live metrics.
33
+ - **Evidence & Artifact Capture**: Retains protocol transcripts, session databases, and execution manifests for debugging and auditability.
34
+
35
+ ## Prerequisites & Installation
36
+
37
+ - Python 3.11+
38
+ - Docker (with Inspect Docker sandbox support)
39
+ - Node.js and npm when a compatible pinned Codex bundle is unavailable.
40
+
41
+ For an npm-equipped container image, preinstall with
42
+ `RUN npm install -g --prefix /opt/codex @openai/codex@0.165.0` and add
43
+ `/opt/codex/bin` to `PATH`. See [provisioning constraints](docs/technical-notes.md#provisioning-and-authentication).
44
+
45
+ Install the package:
46
+
47
+ ```bash
48
+ pip install -e .
49
+ ```
50
+
51
+ ## Authentication
52
+
53
+ Run `codex login` on your host and provide its `~/.codex/auth.json`, or select
54
+ another file with `CODEX_AUTH_PATH` / `auth_path`.
55
+
56
+ Supported authentication modes:
57
+ - `"chatgpt"`: Uses ChatGPT subscription credentials.
58
+ - `"api_key"`: Uses an OpenAI API key.
59
+ - `"existing"`: Uses the container configuration and available credentials without enforcing an authentication mode.
60
+
61
+ Credentials are copied into `/root/.cache/codex-inspect/auth.json` inside the
62
+ container. See [configuration, credential storage, and logging details](docs/technical-notes.md#provisioning-and-authentication).
63
+
64
+ ## Quickstart
65
+
66
+ Define a task using `codex_app_server`:
67
+
68
+ ```python
69
+ from pathlib import Path
70
+ from inspect_ai import Task, task
71
+ from inspect_ai.dataset import Sample
72
+ from inspect_ai.scorer import match
73
+ from inspect_codex_app_server import codex_app_server
74
+
75
+ @task
76
+ def arithmetic():
77
+ return Task(
78
+ dataset=[Sample(input="What is 19 + 23? Reply with the number only.", target="42")],
79
+ solver=codex_app_server(
80
+ authentication="chatgpt",
81
+ timeout_sec=600,
82
+ max_child_agents=1,
83
+ ),
84
+ scorer=match(),
85
+ sandbox=("docker", str(Path(__file__).with_name("compose.yaml"))),
86
+ )
87
+ ```
88
+
89
+ Run evaluation:
90
+
91
+ ```bash
92
+ inspect eval examples/arithmetic.py --model codex/default
93
+ ```
94
+
95
+ ## Inspect Agent
96
+
97
+ `codex_agent()` runs the same native session as an Inspect agent, so it composes like `react()`:
98
+
99
+ ```python
100
+ from inspect_ai.agent import as_solver, as_tool
101
+ from inspect_codex_app_server import CodexSubagent, codex_agent
102
+
103
+ coder = codex_agent(
104
+ name="coder",
105
+ description="Solves programming tasks in the sandbox.",
106
+ prompt="Work carefully and verify your answer.",
107
+ submit=True, # accept answers from the root submit tool
108
+ attempts=3, # score each submission; steer onward when incorrect
109
+ subagents=[CodexSubagent(name="reviewer", description="Reviews changes.", token_limit=200_000)],
110
+ )
111
+
112
+ solver = as_solver(coder) # or as_tool(coder), handoff(coder), run(coder, ...)
113
+ ```
114
+
115
+ See the [Inspect feature mapping](docs/technical-notes.md#inspect-feature-mapping)
116
+ and [model settings and input formats](docs/technical-notes.md#model-settings-and-conversation-input)
117
+ for compatibility details.
118
+
119
+ ### Interruption and checkpoints
120
+
121
+ Operator interrupts stop native work and wait for follow-up input. Inspect
122
+ checkpoints can resume the root thread; scoring-only resume starts no Codex
123
+ process. See [interruption and resume notes](docs/technical-notes.md#operator-interruption-and-checkpoint-resume).
124
+
125
+ ## Solver Options
126
+
127
+ The `codex_app_server` solver accepts the following configuration parameters:
128
+
129
+ | Parameter | Type | Default | Description |
130
+ |---|---|---|---|
131
+ | `authentication` | `"existing"` \| `"chatgpt"` \| `"api_key"` | `"existing"` | Required authentication mode. |
132
+ | `timeout_sec` | `float` | `600.0` | Run budget including setup; teardown runs separately. |
133
+ | `max_child_agents` | `int` | `4` | Maximum concurrent subagents per sample. |
134
+ | `developer_instructions` | `str \| None` | `None` | Thread developer instructions. |
135
+ | `base_instructions` | `str \| None` | `None` | Replacement for Codex's base instructions. |
136
+ | `reasoning_effort` | `str \| None` | `None` | Model reasoning effort (defaults to the evaluation's `GenerateConfig`). |
137
+ | `reasoning_summary` | `str \| None` | `None` | Reasoning summary detail (defaults to the evaluation's `GenerateConfig`). |
138
+ | `retry_refusals` | `int \| None` | `None` | Policy-blocked turns to continue before finishing with `content_filter`. |
139
+ | `skills` | `list[str \| Path \| Skill] \| None` | `None` | Inspect skills installed as Codex skills. |
140
+ | `web_search` | `"disabled"` \| `"cached"` \| `"indexed"` \| `"live"` \| `None` | `None` | Native web search mode. |
141
+ | `subagents` | `list[CodexSubagent] \| None` | `None` | Native agent roles for Codex's child agents. |
142
+ | `service_tier` | `str \| None` | `None` | Service tier requested on root turns (e.g. `"priority"`, `"flex"`). |
143
+ | `cwd` | `str` | `"/app"` | Working directory inside the container. |
144
+ | `transport` | `"stdio"` \| `"websocket"` | `"stdio"` | Transport protocol to communicate with the app server. |
145
+ | `policy` | `RunPolicy \| None` | `None` | Custom execution lifecycle and completion policy. |
146
+ | `codex_path` | `str \| None` | `None` | Host binary override (`CODEX_PATH` also works), taking precedence over image reuse. |
147
+ | `auth_path` | `str \| None` | `None` | Explicit path to the host `auth.json`. |
148
+
149
+ ## Custom Lifecycle Policies (`RunPolicy`)
150
+
151
+ `RunPolicy` configures completion rules, quota waits, setup hooks, and timed
152
+ steering reminders. See the [lifecycle example and limits](docs/technical-notes.md#limits-and-lifecycle-policies).
153
+
154
+ ## Development & Testing
155
+
156
+ The default suite includes real pinned Codex tests using local scripted model
157
+ responses, without credentials or paid inference. Docker acceptance additionally
158
+ requires a running Docker daemon and `docker pull python:3.13-slim`.
159
+
160
+ ```bash
161
+ # Install development dependencies
162
+ uv sync --group dev
163
+
164
+ # Run the default suite, including native tests; excludes Docker acceptance
165
+ uv run pytest -q
166
+
167
+ # Run only tests that launch the real pinned Codex executable
168
+ uv run pytest -q -m native
169
+
170
+ # Run Docker acceptance separately
171
+ uv run pytest -q -m containers
172
+
173
+ # Run linter and type checker
174
+ uv run ruff check .
175
+ uv run ty check
176
+
177
+ # Validate generated JSON-RPC protocol schemas
178
+ uv run python scripts/generate_protocol.py --check
179
+ ```
180
+
181
+ ## Automated compatibility releases
182
+
183
+ The [workflow](.github/workflows/ci.yml) checks pushes and pull requests, and can
184
+ check upstream releases hourly and publish passing candidates to GitHub and
185
+ PyPI. See [release setup, publication, and retry notes](docs/releases.md).
186
+
187
+ ## License
188
+
189
+ [MIT](LICENSE)
@@ -0,0 +1,174 @@
1
+ # inspect-codex-app-server
2
+
3
+ An [Inspect AI](https://inspect.aisi.org.uk/) solver and agent that run native Codex sessions inside Docker sandboxes via the Codex App Server JSON-RPC protocol.
4
+
5
+ Codex manages inference, tool execution, and subagent orchestration, while Inspect AI manages evaluation, scoring, tool governance, and token, message, and time limits. Cost and model-generation turn limits are unsupported.
6
+
7
+ ## Key Features
8
+
9
+ - **Use Your ChatGPT Subscription**: Run evaluations directly against your ChatGPT subscription plan (or API key) using native Codex authentication.
10
+ - **Docker Sandbox Execution**: Runs Codex as root with full access inside Docker sandboxes, including filesystem tools, bash commands, and subagents.
11
+ - **Inspect Agent**: `codex_agent()` composes with `as_solver()`, `as_tool()`, `handoff()` and `run()`, with react-style `submit`, `attempts`, `on_continue` and `retry_refusals`.
12
+ - **Inspect Tool Bridging**: Exposes Inspect tools (`use_tools`, MCP tool sources) directly to Codex as dynamic tools with validation, approval and review handling.
13
+ - **Inspect Approvals**: Routes Codex's native command and file-change approval requests to the active Inspect approver.
14
+ - **Native Skills, Web Search and Roles**: Installs Inspect skills as Codex skills, sets the native web search mode, and declares agent roles for Codex's child agents.
15
+ - **Operator Interruption and Checkpoints**: Responds to operator interrupts and messages through Inspect's agent channel, and resumes from Inspect checkpoints.
16
+ - **Session & Quota Management**: Handles rate limits, quota pauses (`quota_hold_sec`), transient turn-start retries with backoff, and token budgeting.
17
+ - **Transcript & Usage Tracking**: Reconstructs model activity, tool events, and subagent spans into Inspect transcripts with live metrics.
18
+ - **Evidence & Artifact Capture**: Retains protocol transcripts, session databases, and execution manifests for debugging and auditability.
19
+
20
+ ## Prerequisites & Installation
21
+
22
+ - Python 3.11+
23
+ - Docker (with Inspect Docker sandbox support)
24
+ - Node.js and npm when a compatible pinned Codex bundle is unavailable.
25
+
26
+ For an npm-equipped container image, preinstall with
27
+ `RUN npm install -g --prefix /opt/codex @openai/codex@0.165.0` and add
28
+ `/opt/codex/bin` to `PATH`. See [provisioning constraints](docs/technical-notes.md#provisioning-and-authentication).
29
+
30
+ Install the package:
31
+
32
+ ```bash
33
+ pip install -e .
34
+ ```
35
+
36
+ ## Authentication
37
+
38
+ Run `codex login` on your host and provide its `~/.codex/auth.json`, or select
39
+ another file with `CODEX_AUTH_PATH` / `auth_path`.
40
+
41
+ Supported authentication modes:
42
+ - `"chatgpt"`: Uses ChatGPT subscription credentials.
43
+ - `"api_key"`: Uses an OpenAI API key.
44
+ - `"existing"`: Uses the container configuration and available credentials without enforcing an authentication mode.
45
+
46
+ Credentials are copied into `/root/.cache/codex-inspect/auth.json` inside the
47
+ container. See [configuration, credential storage, and logging details](docs/technical-notes.md#provisioning-and-authentication).
48
+
49
+ ## Quickstart
50
+
51
+ Define a task using `codex_app_server`:
52
+
53
+ ```python
54
+ from pathlib import Path
55
+ from inspect_ai import Task, task
56
+ from inspect_ai.dataset import Sample
57
+ from inspect_ai.scorer import match
58
+ from inspect_codex_app_server import codex_app_server
59
+
60
+ @task
61
+ def arithmetic():
62
+ return Task(
63
+ dataset=[Sample(input="What is 19 + 23? Reply with the number only.", target="42")],
64
+ solver=codex_app_server(
65
+ authentication="chatgpt",
66
+ timeout_sec=600,
67
+ max_child_agents=1,
68
+ ),
69
+ scorer=match(),
70
+ sandbox=("docker", str(Path(__file__).with_name("compose.yaml"))),
71
+ )
72
+ ```
73
+
74
+ Run evaluation:
75
+
76
+ ```bash
77
+ inspect eval examples/arithmetic.py --model codex/default
78
+ ```
79
+
80
+ ## Inspect Agent
81
+
82
+ `codex_agent()` runs the same native session as an Inspect agent, so it composes like `react()`:
83
+
84
+ ```python
85
+ from inspect_ai.agent import as_solver, as_tool
86
+ from inspect_codex_app_server import CodexSubagent, codex_agent
87
+
88
+ coder = codex_agent(
89
+ name="coder",
90
+ description="Solves programming tasks in the sandbox.",
91
+ prompt="Work carefully and verify your answer.",
92
+ submit=True, # accept answers from the root submit tool
93
+ attempts=3, # score each submission; steer onward when incorrect
94
+ subagents=[CodexSubagent(name="reviewer", description="Reviews changes.", token_limit=200_000)],
95
+ )
96
+
97
+ solver = as_solver(coder) # or as_tool(coder), handoff(coder), run(coder, ...)
98
+ ```
99
+
100
+ See the [Inspect feature mapping](docs/technical-notes.md#inspect-feature-mapping)
101
+ and [model settings and input formats](docs/technical-notes.md#model-settings-and-conversation-input)
102
+ for compatibility details.
103
+
104
+ ### Interruption and checkpoints
105
+
106
+ Operator interrupts stop native work and wait for follow-up input. Inspect
107
+ checkpoints can resume the root thread; scoring-only resume starts no Codex
108
+ process. See [interruption and resume notes](docs/technical-notes.md#operator-interruption-and-checkpoint-resume).
109
+
110
+ ## Solver Options
111
+
112
+ The `codex_app_server` solver accepts the following configuration parameters:
113
+
114
+ | Parameter | Type | Default | Description |
115
+ |---|---|---|---|
116
+ | `authentication` | `"existing"` \| `"chatgpt"` \| `"api_key"` | `"existing"` | Required authentication mode. |
117
+ | `timeout_sec` | `float` | `600.0` | Run budget including setup; teardown runs separately. |
118
+ | `max_child_agents` | `int` | `4` | Maximum concurrent subagents per sample. |
119
+ | `developer_instructions` | `str \| None` | `None` | Thread developer instructions. |
120
+ | `base_instructions` | `str \| None` | `None` | Replacement for Codex's base instructions. |
121
+ | `reasoning_effort` | `str \| None` | `None` | Model reasoning effort (defaults to the evaluation's `GenerateConfig`). |
122
+ | `reasoning_summary` | `str \| None` | `None` | Reasoning summary detail (defaults to the evaluation's `GenerateConfig`). |
123
+ | `retry_refusals` | `int \| None` | `None` | Policy-blocked turns to continue before finishing with `content_filter`. |
124
+ | `skills` | `list[str \| Path \| Skill] \| None` | `None` | Inspect skills installed as Codex skills. |
125
+ | `web_search` | `"disabled"` \| `"cached"` \| `"indexed"` \| `"live"` \| `None` | `None` | Native web search mode. |
126
+ | `subagents` | `list[CodexSubagent] \| None` | `None` | Native agent roles for Codex's child agents. |
127
+ | `service_tier` | `str \| None` | `None` | Service tier requested on root turns (e.g. `"priority"`, `"flex"`). |
128
+ | `cwd` | `str` | `"/app"` | Working directory inside the container. |
129
+ | `transport` | `"stdio"` \| `"websocket"` | `"stdio"` | Transport protocol to communicate with the app server. |
130
+ | `policy` | `RunPolicy \| None` | `None` | Custom execution lifecycle and completion policy. |
131
+ | `codex_path` | `str \| None` | `None` | Host binary override (`CODEX_PATH` also works), taking precedence over image reuse. |
132
+ | `auth_path` | `str \| None` | `None` | Explicit path to the host `auth.json`. |
133
+
134
+ ## Custom Lifecycle Policies (`RunPolicy`)
135
+
136
+ `RunPolicy` configures completion rules, quota waits, setup hooks, and timed
137
+ steering reminders. See the [lifecycle example and limits](docs/technical-notes.md#limits-and-lifecycle-policies).
138
+
139
+ ## Development & Testing
140
+
141
+ The default suite includes real pinned Codex tests using local scripted model
142
+ responses, without credentials or paid inference. Docker acceptance additionally
143
+ requires a running Docker daemon and `docker pull python:3.13-slim`.
144
+
145
+ ```bash
146
+ # Install development dependencies
147
+ uv sync --group dev
148
+
149
+ # Run the default suite, including native tests; excludes Docker acceptance
150
+ uv run pytest -q
151
+
152
+ # Run only tests that launch the real pinned Codex executable
153
+ uv run pytest -q -m native
154
+
155
+ # Run Docker acceptance separately
156
+ uv run pytest -q -m containers
157
+
158
+ # Run linter and type checker
159
+ uv run ruff check .
160
+ uv run ty check
161
+
162
+ # Validate generated JSON-RPC protocol schemas
163
+ uv run python scripts/generate_protocol.py --check
164
+ ```
165
+
166
+ ## Automated compatibility releases
167
+
168
+ The [workflow](.github/workflows/ci.yml) checks pushes and pull requests, and can
169
+ check upstream releases hourly and publish passing candidates to GitHub and
170
+ PyPI. See [release setup, publication, and retry notes](docs/releases.md).
171
+
172
+ ## License
173
+
174
+ [MIT](LICENSE)
@@ -0,0 +1,47 @@
1
+ # Package test gates
2
+
3
+ ## Admission
4
+
5
+ Extend an existing scenario first. Add a case only when it catches a named defect
6
+ existing tests miss, with an independently derived expected result. For regressions,
7
+ verify failure against the broken behavior when practical; report if unverified.
8
+
9
+ ## Required assertions by change
10
+
11
+ | Changed behavior | Gate |
12
+ | --- | --- |
13
+ | Accounting / evidence | Duplicate wire and rollout records count once; unrelated threads contribute zero. Resume after 25 tokens, consume 5: recorded total 30, newly charged tokens 5. |
14
+ | Completion / errors | Child answers cannot replace the root answer. Provider/transport failures persist as execution errors; refusals and limits retain distinct outcomes. |
15
+ | Interruption | Interrupt active native and bridged work: tools stop before redirect. Operator wait exceeds remaining deadline: redirect still resumes. |
16
+ | Shutdown | Stall a real process, close within a bounded timeout, verify its PID disappears and the final protocol record survives. |
17
+ | Checkpoints | Fresh native home resumes the same thread with history, tools, and accurate usage. Scoring-only resume starts no Codex process. |
18
+ | Native options / RPC | Installed pinned binary demonstrates the new assumption, with valid and invalid controls where applicable. A scripted app-server peer alone is insufficient. |
19
+
20
+ Preserve or extend the affected row's evidence. Different ordering, ownership,
21
+ or failure mechanisms justify separate cases; different numbers or model labels
22
+ on the same path do not.
23
+
24
+ ## Checks before merge
25
+
26
+ Run from this package directory:
27
+
28
+ | Change | Required commands |
29
+ | --- | --- |
30
+ | Code / tests | `uv run pytest -q`; `uv run ruff check .`; `uv run ty check` |
31
+ | Provisioning, transport, lifecycle, checkpoints; every release | Also `uv run pytest -q -m containers` |
32
+ | Codex/Inspect pin, RPC schema, generator | All above plus `uv run python scripts/generate_protocol.py --check`; regenerate bindings |
33
+ | Documentation only | Check changed links and commands |
34
+
35
+ Default pytest includes native tests and excludes containers. Missing pinned-binary
36
+ or Docker prerequisites must fail visibly. Report required checks left unrun.
37
+
38
+ ## Reject / prune
39
+
40
+ Remove duplicate helper, forwarding, call-count, source-layout, and presentation
41
+ checks unless they protect a distinct external contract. Keep behavior-preserving
42
+ refactors assertion-neutral. No coverage quotas or mandatory tests per edit.
43
+
44
+ Peers reject unexpected RPCs and never reproduce adapter accounting/recovery.
45
+ Use real processes/files for lifecycle claims; own temporary homes, logs, tasks,
46
+ and containers. Synchronize on events; bound teardown. No flaky-test retries,
47
+ user-artifact cleanup, live credentials, or paid inference in correctness gates.