Skip to content

Commit 5601d71

Browse files
authored
fix(gator): preserve literal Codex account routing identity (#4215)
* fix(gator): preserve literal Codex account routing identity Signed-off-by: John Myers <johntmyers@users.noreply.github.com> * chore(gator): retain latest Codex default Signed-off-by: John Myers <johntmyers@users.noreply.github.com> --------- Signed-off-by: John Myers <johntmyers@users.noreply.github.com> Co-authored-by: John Myers <johntmyers@users.noreply.github.com>
1 parent 8760396 commit 5601d71

6 files changed

Lines changed: 37 additions & 6 deletions

File tree

‎.agents/skills/launch-openshell-gator/SKILL.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ jq -e '.tokens.access_token and .tokens.refresh_token and .tokens.account_id' "$
8080

8181
If this fails, run the local Codex login flow outside the gator launch. If Codex was recently reauthenticated and gateway refresh fails later, relaunch with `--reset-refresh` once.
8282

83+
The launcher passes the non-secret host account ID as literal `CODEX_ACCOUNT_ID` for Codex's local workspace routing. The harness must not use the opaque `CODEX_AUTH_ACCOUNT_ID` credential placeholder for that comparison. Access tokens remain placeholders, and refresh material remains gateway-only.
84+
8385
### Step 5: Verify Gateway Is Registered And Alive
8486

8587
Use the target gateway from the operator request or current session context. Do not assume a gateway name. If the operator did not specify one, list registered gateways and ask before launching when the correct target is ambiguous.

‎scripts/agents/gator/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ The launcher:
3737
- Scans `profile_paths` in manifest order and imports or updates `providers/github-gator.yaml`.
3838
- Creates or updates the `github-gator` provider from `gh auth token`.
3939
- Selects the requested harness and bakes the common runtime into the immutable sandbox payload.
40+
- Builds with the latest Codex CLI by default. Docker can reuse the cached install layer; rebuild with that layer's cache disabled to fetch a newer release.
4041
- For `--harness codex`, imports `providers/codex-gator.yaml`, creates or updates the `codex-gator` provider from `$HOME/.codex/auth.json`, and stores the refresh token as gateway-only refresh material.
4142
- For `--harness codex`, configures gateway-managed refresh for `CODEX_AUTH_ACCESS_TOKEN` and rotates it before launching the sandbox.
43+
- Passes the non-secret Codex account ID from host auth as `CODEX_ACCOUNT_ID`. The harness writes it literally into `auth.json` for local workspace discovery; access tokens remain opaque provider placeholders and refresh tokens remain gateway-only.
4244
- Enables `agent_policy_proposals_enabled` and `proposal_approval_mode=auto` at gateway scope.
4345
- Uses the gator image policy copied to `/etc/openshell/policy.yaml`.
4446
- Installs the gator-specific `gh` wrapper from `gator/bin/gh` as `/usr/local/bin/gh` to fail closed when same-head-SHA history cannot be checked, prevent duplicate dispositions, and require versioned review payloads.

‎scripts/agents/gator/agent.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44
id: gator
5-
payload_version: 9
5+
payload_version: 10
66
display_name: Gator Gate Agent
77
description: Validate and monitor OpenShell GitHub issues and pull requests through the gator state machine.
88

‎scripts/agents/run.sh‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,12 @@ HARNESS_ENV_ARGS=(
801801

802802
case "$HARNESS" in
803803
codex)
804+
# Codex compares account IDs locally during workspace discovery. Unlike
805+
# bearer tokens, this non-secret identifier must not be a placeholder.
806+
[[ -n "${CODEX_AUTH_ACCOUNT_ID:-}" ]] || fail "missing Codex account ID"
807+
[[ "$CODEX_AUTH_ACCOUNT_ID" != openshell:resolve:* ]] || fail "Codex account ID must be a literal identifier"
804808
HARNESS_ENV_ARGS+=(
809+
"CODEX_ACCOUNT_ID=$CODEX_AUTH_ACCOUNT_ID"
805810
"CODEX_MODEL=${CODEX_MODEL:-$HARNESS_MODEL}"
806811
"CODEX_REASONING=${CODEX_REASONING:-$HARNESS_REASONING}"
807812
)

‎scripts/agents/runtime/harnesses/codex/exec.sh‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ require_env() {
1616
}
1717

1818
require_env CODEX_AUTH_ACCESS_TOKEN
19-
require_env CODEX_AUTH_ACCOUNT_ID
19+
require_env CODEX_ACCOUNT_ID
20+
[[ "$CODEX_ACCOUNT_ID" != openshell:resolve:* ]] || { echo "Codex account ID must be a literal identifier" >&2; exit 1; }
2021
require_env GITHUB_TOKEN
2122

2223
PROMPT_FILE="$1"
@@ -58,7 +59,8 @@ fs.writeFileSync(path, JSON.stringify({
5859
id_token: providerValue("CODEX_AUTH_ID_TOKEN") || fallbackIdToken,
5960
access_token: providerValue("CODEX_AUTH_ACCESS_TOKEN"),
6061
refresh_token: providerValue("CODEX_AUTH_REFRESH_TOKEN") || "gateway-managed-refresh-token",
61-
account_id: providerValue("CODEX_AUTH_ACCOUNT_ID"),
62+
// Workspace discovery compares this non-secret identifier locally.
63+
account_id: process.env.CODEX_ACCOUNT_ID,
6264
},
6365
last_refresh: new Date().toISOString(),
6466
}, null, 2));

‎scripts/agents/runtime/harnesses/codex/exec_test.sh‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ trap 'rm -rf "$TMP_DIR"' EXIT
1111

1212
ACCESS_PLACEHOLDER="openshell:resolve:env:s$(printf 'a%.0s' {1..64})_CODEX_AUTH_ACCESS_TOKEN"
1313
ACCOUNT_PLACEHOLDER="openshell:resolve:env:v42_CODEX_AUTH_ACCOUNT_ID"
14+
ACCOUNT_ID="test-codex-account"
1415

1516
cat > "$TMP_DIR/codex" <<'MOCK'
1617
#!/usr/bin/env bash
@@ -23,8 +24,11 @@ fi
2324
access="$(jq -r '.tokens.access_token' "$HOME/.codex/auth.json")"
2425
account="$(jq -r '.tokens.account_id' "$HOME/.codex/auth.json")"
2526
[[ "$access" == "$EXPECTED_ACCESS_PLACEHOLDER" ]]
26-
[[ "$account" == "$EXPECTED_ACCOUNT_PLACEHOLDER" ]]
27-
printf '%s\n' 'ok - Codex auth preserves opaque provider placeholders'
27+
[[ "$account" == "$EXPECTED_ACCOUNT_ID" ]]
28+
[[ "$account" != openshell:resolve:* ]]
29+
[[ "$(jq -r '.tokens.refresh_token' "$HOME/.codex/auth.json")" == "gateway-managed-refresh-token" ]]
30+
[[ "$(node -e 'console.log((require("fs").statSync(process.env.HOME + "/.codex/auth.json").mode & 0o777).toString(8))')" == 600 ]]
31+
printf '%s\n' 'ok - Codex auth preserves token placeholders and uses a literal account ID'
2832
MOCK
2933
chmod +x "$TMP_DIR/codex"
3034
printf '%s\n' 'test prompt' > "$TMP_DIR/prompt.md"
@@ -33,7 +37,23 @@ OPENSHELL_AGENT_HOME="$TMP_DIR/home" \
3337
CODEX_BIN="$TMP_DIR/codex" \
3438
CODEX_AUTH_ACCESS_TOKEN="$ACCESS_PLACEHOLDER" \
3539
CODEX_AUTH_ACCOUNT_ID="$ACCOUNT_PLACEHOLDER" \
40+
CODEX_ACCOUNT_ID="$ACCOUNT_ID" \
3641
GITHUB_TOKEN="openshell:resolve:env:v42_GITHUB_TOKEN" \
3742
EXPECTED_ACCESS_PLACEHOLDER="$ACCESS_PLACEHOLDER" \
38-
EXPECTED_ACCOUNT_PLACEHOLDER="$ACCOUNT_PLACEHOLDER" \
43+
EXPECTED_ACCOUNT_ID="$ACCOUNT_ID" \
3944
bash "$SCRIPT_DIR/exec.sh" "$TMP_DIR/prompt.md"
45+
46+
# Fail before launching Codex when the routing identity is absent or opaque.
47+
for invalid_account_id in "" "$ACCOUNT_PLACEHOLDER"; do
48+
if OPENSHELL_AGENT_HOME="$TMP_DIR/invalid-home" \
49+
CODEX_BIN="$TMP_DIR/codex" \
50+
CODEX_AUTH_ACCESS_TOKEN="$ACCESS_PLACEHOLDER" \
51+
CODEX_ACCOUNT_ID="$invalid_account_id" \
52+
GITHUB_TOKEN="placeholder" \
53+
bash "$SCRIPT_DIR/exec.sh" "$TMP_DIR/prompt.md" >"$TMP_DIR/error" 2>&1; then
54+
echo "unexpected success with invalid Codex account ID" >&2
55+
exit 1
56+
fi
57+
[[ ! -e "$TMP_DIR/invalid-home/.codex/auth.json" ]]
58+
done
59+
printf '%s\n' 'ok - missing and placeholder account IDs are rejected'

0 commit comments

Comments
 (0)