From e50b44e1016d5a3f771431d174d750d63e6357f3 Mon Sep 17 00:00:00 2001 From: "Ioannis L." <44038245+blitzcrieg1@users.noreply.github.com> Date: Sat, 7 Nov 2026 21:19:40 +0200 Subject: [PATCH] fix(account): find Codex's session file when the hook's path does not match The first real Codex session after #395 recorded no account, although its session file existed and held both IDs. The read only accepted a plain path under Codex's home, so a Windows extended path was rejected, and a missing path gave up. It now accepts the extended form, and when the path is missing or unusable it finds the file by the session ID in its name. The ID is checked first, so a crafted value cannot widen the search, and the search stays inside Codex's sessions folder. What Codex actually sent is not known, so which of the two was the cause is not proven. A real session after merge decides whether this fixed it. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- apps/orchestrator/agentmetry/core/account.py | 34 +++++++++++++++---- apps/orchestrator/tests/test_codex_account.py | 32 +++++++++++++++++ docs/agentmetry-event-schema.md | 3 +- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2b7f409..830bdc1 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ These are the numbers from commands, not from a slide. That block is what `agentmetry benchmark` printed from the PyPI 0.9.4 package and from this tree. "Rules covered 13" counts rule ids named in corpus expectations. Twelve of those are published rules. The thirteenth is the experimental rule. Two published rules have no corpus case: `host-subagent-swarm-burst` and `off-hours-activity`. They are named in `apps/orchestrator/tools/generate_sigma_pack.py` so the Sigma pack still has them. -2258 tests collected (`pytest --collect-only -q` from `apps/orchestrator`). The +2269 tests collected (`pytest --collect-only -q` from `apps/orchestrator`). The [0.10.6 release PR's CI run](https://github.com/blitzcrieg1/agentmetry/actions/runs/42082231888) on Python 3.12 reported 2089 passed and 19 skipped on Linux and macOS, 2102 passed and 6 skipped on Windows, and 82.13% line coverage on Linux diff --git a/apps/orchestrator/agentmetry/core/account.py b/apps/orchestrator/agentmetry/core/account.py index acb7448..6eb56a1 100644 --- a/apps/orchestrator/agentmetry/core/account.py +++ b/apps/orchestrator/agentmetry/core/account.py @@ -26,6 +26,7 @@ ) #: The first line of a Codex session file also holds its base instructions. _SESSION_META_MAX_BYTES = 1024 * 1024 +_WINDOWS_EXTENDED_PREFIX = chr(92) * 2 + "?" + chr(92) _CLAUDE_AUTH_OVERRIDE_VARS = ( "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", @@ -244,7 +245,30 @@ def _local_antigravity_account() -> dict[str, str]: return {} -def _codex_session_account(transcript_path: Any) -> dict[str, str]: +def _codex_session_file(home: Path, transcript_path: Any, session_id: Any) -> Path | None: + """Find the session file, from the path the hook named or else from its ID. + + Windows tools can hand over the extended form of a path, which would not + match Codex's home. A session with no usable path is found by its ID, which + Codex puts in the file name. + """ + if isinstance(transcript_path, str) and transcript_path.endswith(".jsonl"): + text = transcript_path + if text.startswith(_WINDOWS_EXTENDED_PREFIX): + text = text[len(_WINDOWS_EXTENDED_PREFIX) :] + path = Path(text).resolve() + if path.is_relative_to(home) and path.is_file(): + return path + session = _clean_id(session_id) + if not session or not session.replace("-", "").isalnum(): + return None + # sessions/YYYY/MM/DD/rollout-