Install the complete Codex runtime package

eac3f609d8e9 · AtlantisPleb · · parent af5d458f0dbe

Install the complete Codex runtime package

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified Dockerfile
  • modified docs/operations/scv-staging-qualification.md
  • modified docs/scv-codex-app-server-planning.md
  • modified lib/openagents/scv/executor/codex_app_server.ex
  • modified ops/staging/publish-candidate.sh
  • modified test/openagents/scv/codex_app_server_executor_test.exs
  • modified test/openagents/staging_candidate_contract_test.exs
  • modified test/support/fake_codex_app_server.sh

Diff

8 files changed, +115 -23

Dockerfile modified +12 -7

@@ -134,18 +134,23 @@ RUN sed -i \

134 134
135 135
RUN set -eu; \
136 136
  case "${TARGETARCH:-$(dpkg --print-architecture)}" in \
137
    amd64) codex_arch=x86_64; checksum=0246e2e773834e07f0fb5249ed6ebad12e4591e608f8c7bb97dd6a9690544c36 ;; \
138
    arm64) codex_arch=aarch64; checksum=eb677c80f666b1ab8b4b1d083b66e8d614b1281d960bb6f9fd8ca98f58b38b90 ;; \
137
    amd64) codex_arch=x86_64; checksum=bd758d53d56e41dc65e045f4589df79a038ed197a011adcb52a258e6ad64cfda ;; \
138
    arm64) codex_arch=aarch64; checksum=89cbf79bd5ae6f9c58da47e8079f311c84219350c9c43c070d42f3e9b2a81401 ;; \
139 139
    *) echo "Unsupported architecture: ${TARGETARCH}" >&2; exit 1 ;; \
140 140
  esac; \
141
  archive="codex-${codex_arch}-unknown-linux-musl.tar.gz"; \
141
  archive="codex-package-${codex_arch}-unknown-linux-musl.tar.gz"; \
142 142
  curl -fsSL --retry 3 -o "/tmp/${archive}" \
143 143
    "https://github.com/openai/codex/releases/download/rust-v${CODEX_VERSION}/${archive}"; \
144 144
  echo "${checksum}  /tmp/${archive}" | sha256sum --check --strict; \
145
  tar -xzf "/tmp/${archive}" -C /tmp; \
146
  install -D -m 0755 "/tmp/codex-${codex_arch}-unknown-linux-musl" /usr/local/bin/codex; \
147
  rm "/tmp/${archive}" "/tmp/codex-${codex_arch}-unknown-linux-musl"; \
148
  codex --version
145
  install -d -m 0755 /opt/codex; \
146
  tar -xzf "/tmp/${archive}" -C /opt/codex; \
147
  ln -s /opt/codex/bin/codex /usr/local/bin/codex; \
148
  ln -s /opt/codex/bin/codex-code-mode-host /usr/local/bin/codex-code-mode-host; \
149
  rm "/tmp/${archive}"; \
150
  test -x /opt/codex/codex-resources/bwrap; \
151
  test -x /opt/codex/codex-path/rg; \
152
  codex --version; \
153
  codex-code-mode-host --help >/dev/null
149 154
150 155
# Set the locale
151 156
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen \
docs/operations/scv-staging-qualification.md modified +4 -2

@@ -101,8 +101,10 @@ service account until this path passes.

101 101
   SHA-256 digest, exact repository SHA, Codex thread and turn IDs, event count,
102 102
   usage, and resources.
103 103
7. Query `scv_run_events` for that run. Require `driver_started`,
104
   `driver_session_started`, `turn_started`, at least one activity or message
105
   event, `turn_finished`, and `run_finished`.
104
   `driver_session_started`, `turn_started`, at least one successful
105
   `tool_started` and `tool_completed` pair, `turn_finished`, and
106
   `run_finished`. A message-only report does not prove repository access and
107
   does not qualify the SCV runtime.
106 108
8. Confirm that no disposable workspace or temporary `CODEX_HOME` remains and
107 109
   that the connected account remains **Ready**.
108 110
9. Scan the bounded event payloads, application logs, and public status
docs/scv-codex-app-server-planning.md modified +4 -1

@@ -480,7 +480,10 @@ binaries in the credential compartment.

480 480
481 481
For one admitted account runtime:
482 482
483
1. Resolve a digest-pinned worker image and Codex binary or stable SDK release.
483
1. Resolve a digest-pinned worker image and complete Codex package. The package
484
   must include the app-server entry point, `codex-code-mode-host`, and its
485
   packaged `bwrap`, `rg`, and `zsh` resources. Installing only the `codex`
486
   binary disables code-mode tools and does not qualify an SCV runtime.
484 487
2. Materialize the account secret or credential home into its isolated
485 488
   compartment.
486 489
3. Generate host-owned Codex configuration. Ignore repository-controlled user
lib/openagents/scv/executor/codex_app_server.ex modified +27 -8

@@ -170,6 +170,7 @@ defmodule OpenAgents.SCV.Executor.CodexAppServer do

170 170
        redactions: redactions,
171 171
        notification_count: 0,
172 172
        tool_calls: %{},
173
        completed_tool_calls: %{},
173 174
        usage: %{},
174 175
        client_monitor: monitor,
175 176
        next_heartbeat_ms: monotonic_ms() + @heartbeat_interval_ms

@@ -397,10 +398,18 @@ defmodule OpenAgents.SCV.Executor.CodexAppServer do

397 398
            status: status
398 399
          })
399 400
400
        if event_type == "tool_started" do
401
          %{state | tool_calls: Map.update(state.tool_calls, tool, 1, &(&1 + 1))}
402
        else
403
          state
401
        case {event_type, status} do
402
          {"tool_started", _status} ->
403
            %{state | tool_calls: Map.update(state.tool_calls, tool, 1, &(&1 + 1))}
404
405
          {"tool_completed", "completed"} ->
406
            %{
407
              state
408
              | completed_tool_calls: Map.update(state.completed_tool_calls, tool, 1, &(&1 + 1))
409
            }
410
411
          {_event_type, _status} ->
412
            state
404 413
        end
405 414
    end
406 415
  end

@@ -426,14 +435,23 @@ defmodule OpenAgents.SCV.Executor.CodexAppServer do

426 435
    finished_at = DateTime.utc_now()
427 436
    duration_ms = max(monotonic_ms() - started_ms, 0)
428 437
    {report, report_valid?} = valid_report(state.report, state.report_truncated?)
438
    tool_activity_valid? = map_size(state.completed_tool_calls) > 0
429 439
430 440
    status =
431
      if state.status == "succeeded" and not report_valid?, do: "failed", else: state.status
441
      cond do
442
        state.status != "succeeded" -> state.status
443
        not report_valid? -> "failed"
444
        not tool_activity_valid? -> "failed"
445
        true -> "succeeded"
446
      end
432 447
433 448
    error_code =
434
      if state.status == "succeeded" and not report_valid?,
435
        do: "report_invalid",
436
        else: state.error_code
449
      cond do
450
        state.status != "succeeded" -> state.error_code
451
        not report_valid? -> "report_invalid"
452
        not tool_activity_valid? -> "tool_activity_missing"
453
        true -> nil
454
      end
437 455
438 456
    %{
439 457
      schema: @schema,

@@ -455,6 +473,7 @@ defmodule OpenAgents.SCV.Executor.CodexAppServer do

455 473
      events: %{
456 474
        event_count: state.notification_count,
457 475
        tool_calls: state.tool_calls,
476
        completed_tool_calls: state.completed_tool_calls,
458 477
        usage: state.usage
459 478
      },
460 479
      report: report,
ops/staging/publish-candidate.sh modified +10

@@ -248,6 +248,16 @@ if [ "$embedded_application_revision" != "$git_sha" ] ||

248 248
  exit 1
249 249
fi
250 250
251
docker run --rm \
252
  --entrypoint /bin/sh \
253
  "$application_image" \
254
  -c 'set -eu
255
      test -x /opt/codex/bin/codex
256
      test -x /opt/codex/bin/codex-code-mode-host
257
      test -x /opt/codex/codex-resources/bwrap
258
      test -x /opt/codex/codex-path/rg
259
      /opt/codex/bin/codex-code-mode-host --help >/dev/null'
260
251 261
application_config_digest=$(docker image inspect "$application_image" --format '{{.Id}}')
252 262
builder_config_digest=$(docker image inspect "$builder_image" --format '{{.Id}}')
253 263
test/openagents/scv/codex_app_server_executor_test.exs modified +37

@@ -105,6 +105,7 @@ defmodule OpenAgents.SCV.CodexAppServerExecutorTest do

105 105
    assert result.report.text =~ "SCV completed the inspection"
106 106
    assert result.report.text =~ "[REDACTED]"
107 107
    assert result.events.tool_calls == %{"commandExecution" => 1}
108
    assert result.events.completed_tool_calls == %{"commandExecution" => 1}
108 109
    assert result.usage.total_tokens == 21
109 110
110 111
    assert_receive {:scv_session, %{driver_thread_id: "thr_fixture"}}

@@ -134,6 +135,42 @@ defmodule OpenAgents.SCV.CodexAppServerExecutorTest do

134 135
           )
135 136
  end
136 137
138
  test "fails a report-only run that never accesses the repository", context do
139
    config = Application.fetch_env!(:openagents, :scv_codex)
140
141
    Application.put_env(
142
      :openagents,
143
      :scv_codex,
144
      Keyword.put(config, :client_options, args: ["no_tools"])
145
    )
146
147
    test_process = self()
148
149
    assert {:ok, result} =
150
             CodexAppServer.run(
151
               context.repository,
152
               "Inspect the fixture without changing it.",
153
               account: context.account,
154
               run_id: Ecto.UUID.generate(),
155
               repository_revision: context.revision,
156
               reasoning_effort: "low",
157
               event_sink: fn event ->
158
                 send(test_process, {:scv_event, event})
159
                 :ok
160
               end
161
             )
162
163
    assert result.status == "failed"
164
    assert result.error_code == "tool_activity_missing"
165
    assert result.report.valid
166
    assert result.events.tool_calls == %{}
167
    assert result.events.completed_tool_calls == %{}
168
    assert_receive {:scv_event, %{type: "turn_finished", status: "succeeded"}}
169
170
    assert_receive {:scv_event,
171
                    %{type: "run_finished", status: "failed", error_code: "tool_activity_missing"}}
172
  end
173
137 174
  defp fixture do
138 175
    Path.expand("../../support/fake_codex_app_server.sh", __DIR__)
139 176
  end
test/openagents/staging_candidate_contract_test.exs modified +12

@@ -34,6 +34,18 @@ defmodule OpenAgents.StagingCandidateContractTest do

34 34
             ~s(RELEASE_COOKIE:?RELEASE_COOKIE is required for a distributed node)
35 35
  end
36 36
37
  test "the runtime installs the complete pinned Codex package" do
38
    dockerfile = File.read!("Dockerfile")
39
40
    assert dockerfile =~ ~s(archive="codex-package-${codex_arch}-unknown-linux-musl.tar.gz")
41
    assert dockerfile =~ ~r/amd64\).*checksum=[0-9a-f]{64}/
42
    assert dockerfile =~ ~r/arm64\).*checksum=[0-9a-f]{64}/
43
    assert dockerfile =~ "/opt/codex/bin/codex-code-mode-host"
44
    assert dockerfile =~ "test -x /opt/codex/codex-resources/bwrap"
45
    assert dockerfile =~ "test -x /opt/codex/codex-path/rg"
46
    assert dockerfile =~ "codex-code-mode-host --help"
47
  end
48
37 49
  test "the isolated builder loads runtime configuration without the web role" do
38 50
    dockerfile = File.read!("Dockerfile")
39 51
    runtime_config = File.read!("config/runtime.exs")
test/support/fake_codex_app_server.sh modified +9 -5

@@ -12,7 +12,7 @@ while IFS= read -r line; do

12 12
13 13
  case "${line}" in
14 14
    *'"method":"initialize"'*)
15
      if [ "${mode}" = "run" ]; then
15
      if [ "${mode}" = "run" ] || [ "${mode}" = "no_tools" ]; then
16 16
        case "${line}" in *'"experimentalApi":true'*) : ;; *) exit 41 ;; esac
17 17
      fi
18 18
      printf '{"id":%s,"result":{"userAgent":"fake-codex/0.147.0","codexHome":"%s","platformFamily":"unix","platformOs":"linux"}}\n' "${id}" "${CODEX_HOME}"

@@ -31,7 +31,9 @@ while IFS= read -r line; do

31 31
    *'"method":"account/read"'*)
32 32
      account_reads=$((account_reads + 1))
33 33
34
      if [ "${account_reads}" -eq 1 ] && [ "${mode}" != "run" ]; then
34
      if [ "${account_reads}" -eq 1 ] &&
35
        [ "${mode}" != "run" ] &&
36
        [ "${mode}" != "no_tools" ]; then
35 37
        printf '{"id":%s,"result":{"account":null,"requiresOpenaiAuth":true}}\n' "${id}"
36 38
      else
37 39
        printf '{"id":%s,"result":{"account":{"type":"chatgpt","email":"operator@example.test","planType":"plus"},"requiresOpenaiAuth":true}}\n' "${id}"

@@ -47,7 +49,7 @@ while IFS= read -r line; do

47 49
      printf '{"id":%s,"result":{"status":"canceled"}}\n' "${id}"
48 50
      ;;
49 51
    *'"method":"thread/start"'*)
50
      if [ "${mode}" = "run" ]; then
52
      if [ "${mode}" = "run" ] || [ "${mode}" = "no_tools" ]; then
51 53
        case "${line}" in *'"permissions":"scv-read-only"'*) : ;; *) exit 42 ;; esac
52 54
        grep -q 'default_permissions = "scv-read-only"' "${CODEX_HOME}/config.toml"
53 55
        grep -q '":minimal" = "read"' "${CODEX_HOME}/config.toml"

@@ -59,8 +61,10 @@ while IFS= read -r line; do

59 61
    *'"method":"turn/start"'*)
60 62
      printf '{"id":%s,"result":{"turn":{"id":"turn_fixture","status":"inProgress","items":[],"error":null}}}\n' "${id}"
61 63
      printf '%s\n' '{"method":"turn/started","params":{"turn":{"id":"turn_fixture","status":"inProgress","items":[],"error":null}}}'
62
      printf '%s\n' '{"method":"item/started","params":{"threadId":"thr_fixture","turnId":"turn_fixture","item":{"id":"item_command","type":"commandExecution","command":"redacted","cwd":"/workspace","status":"inProgress"}}}'
63
      printf '%s\n' '{"method":"item/completed","params":{"threadId":"thr_fixture","turnId":"turn_fixture","item":{"id":"item_command","type":"commandExecution","command":"redacted","cwd":"/workspace","status":"completed","exitCode":0}}}'
64
      if [ "${mode}" != "no_tools" ]; then
65
        printf '%s\n' '{"method":"item/started","params":{"threadId":"thr_fixture","turnId":"turn_fixture","item":{"id":"item_command","type":"commandExecution","command":"redacted","cwd":"/workspace","status":"inProgress"}}}'
66
        printf '%s\n' '{"method":"item/completed","params":{"threadId":"thr_fixture","turnId":"turn_fixture","item":{"id":"item_command","type":"commandExecution","command":"redacted","cwd":"/workspace","status":"completed","exitCode":0}}}'
67
      fi
64 68
      printf '{"method":"item/agentMessage/delta","params":{"threadId":"thr_fixture","turnId":"turn_fixture","itemId":"item_message","delta":"%s"}}\n' "${report}"
65 69
      printf '%s\n' '{"method":"thread/tokenUsage/updated","params":{"threadId":"thr_fixture","turnId":"turn_fixture","tokenUsage":{"total":{"totalTokens":21,"inputTokens":13,"cachedInputTokens":3,"cacheWriteInputTokens":0,"outputTokens":8,"reasoningOutputTokens":2},"last":{"totalTokens":21,"inputTokens":13,"cachedInputTokens":3,"cacheWriteInputTokens":0,"outputTokens":8,"reasoningOutputTokens":2},"modelContextWindow":1000}}}'
66 70
      printf '{"method":"item/completed","params":{"threadId":"thr_fixture","turnId":"turn_fixture","item":{"id":"item_message","type":"agentMessage","text":"%s","phase":"final_answer"}}}\n' "${report}"

This page updates live while a promote is in flight · changelog