Skip to content

v1.12.3 — Now on Codex as Well

Latest

Choose a tag to compare

@CronusL-1141 CronusL-1141 released this 09 Sep 05:55
· 2 commits to master since this release

A patch release. Codex now actually sees the unread line at the top of its turn, the runtime work that had lived only in five uncommitted desktop worktrees is on master, and the README says out loud that the OS runs on Codex. Tests 3,285 -> 3,391; psutil becomes a declared dependency.

Fixed

  • Codex could not see the unread line (c6c4cdb, merged in 14bfeba) - the hook ran, its audit recorded 376 characters written, and the model answered that it had no unread context. Codex's host does not inject a hook's plain stdout; it takes the standard hookSpecificOutput.additionalContext JSON. The entry script now emits that form for non-empty output; the no-unread path stays silent and the diagnostic path stays on stderr, so nothing changes for the cases that already worked, and the audit records the wire length. Written by the peer, reviewed here, and measured after merge on both the Codex CLI (a blind marker with a random first line appeared in the model's context before any tool call, and the model returned it verbatim after its tool call was denied) and the desktop app (the user typed one word). What is measured is prompt-time delivery; an idle Codex session is still not woken by mail.
  • psutil was load-bearing but undeclared (5d528fec) - the runtime reconciliation merged in this release imported psutil inside a try/except and then relied on it. With the module absent, a stale startup lock could never be reclaimed (the previous code reclaimed by age), the API never auto-started after a 20 s wait, the PID ledger always read empty, and the recovery branch for a stuck API was dead code, so a second uvicorn could start on a random port against the same SQLite file. Every test passed on the development machine because psutil arrived transitively through an unrelated package. Found by the review of the consolidated branch (one reviewer per dimension, one refuter per finding; two dimensions hit this independently). psutil is now declared in pyproject.toml, plugin/requirements.txt and the installer's fallback list, and absence degrades instead of paralysing: liveness falls back to kill(0) plus ps, a stale lock is reclaimed only once its owner is confirmed dead and the file has not changed underneath, and without psutil the code never authorises a kill and never starts a second instance on an occupied port.
  • The health check read the port from a constant (5d528fec) - os_health_check derived the port from the import-time API_URL constant (always 8000) while the API records its real port in a file, so on the documented fallback path where 8000 is taken and the API lands on a random port, PID reconciliation always reported not_managed and the reported URL was wrong. The two new tests patched the constant and hid it. It now uses the dynamic URL.
  • One hung request froze a Dashboard refresh prefix for good (5d528fec) - the coalesced refresh keeps a key in flight until its request settles, and apiFetch has no timeout, so one request that never returned stopped every later refresh of that prefix; the previous behaviour (cancelRefetch: true) had self-healed by cancelling and re-issuing. A 30 s deadline now settles the key and cancels only the request captured at that moment; a newer request on the same key is left alone.
  • Legacy WebSocket close swallowed cancellation (ad11886) - on the legacy websockets backend, close() catches CancelledError and shields the TCP teardown, so a timed-out send could still hold the HTTP acknowledgement hostage: 0.704 s measured against a 0.10 s budget on a real loopback handshake. Close now runs as a tracked task that is cancelled on deadline without being awaited, and late results are consumed by a done callback. 0.704 s -> 0.103 s.

Added

  • Runtime consolidation from five desktop worktrees (ad11886, 5d528fec, merged in a85b7f7) - WebSocket broadcast isolates slow clients with bounded concurrent sends instead of awaiting them in series (that serial await is what pushed hook posts past their 1.5 s budget); the HTTP admission middleware keeps one of the five SQLite slots for hook events, so ordinary API traffic cannot starve them; the PID ledger reconciles on demand, adopting only a listener on the managed port whose process identity is verified, and os_restart_api(source_root=..., dry_run=true) checks imports without stopping the service; the Dashboard coalesces query refreshes over 200 ms without cancelling requests in flight. 1,178 lines of new tests, and the realtime hook tests now have a runner (npm test) wired into preflight.sh and CI, which also installs from pyproject.toml instead of a hand-kept list. The consolidation itself was done by the peer harness over the OS channel: it merged the five trees into one branch, this side reviewed (five dimensions, nine confirmed findings, one blocking), it fixed, this side re-verified and merged. The twelve desktop worktrees are gone; the six that held uncommitted work were first archived as local commits so the pre-consolidation state can be rebuilt.
  • channel_wait reports how the message arrived (c360c9e, merged in 8ab3dd8) - successful responses carry delivery_source: replay for the initial inbox read, event for a read triggered by a WebSocket event, timeout_read for the final read after the wait expires. Until now a message returned by a wait could not be told apart from one the final read would have found anyway, which left the push path unverifiable. Error responses claim no source.
  • The README says the OS runs on Codex - a standing note under the tagline, separate from the per-release line, states what a Codex session gets (the same task wall, memory, reports, Dashboard and MCP tools, fed by its own native hooks; cross-harness messaging with a prompt-time unread line), what is still Claude Code only (one-command install, the session-start briefing, direction-layer memory for new subagents, waking an idle session), and that Codex setup is manual today. The os-channel skill's per-harness capability table was rewritten to match what is now measured.

Changed

  • Worktrees live inside the repository (1b68077) - .worktrees/ is ignored and the multi-session discipline in CLAUDE.md now requires it. Twelve sibling directories had accumulated on the Desktop, and sibling paths also fall outside the OS's subdirectory-based project attribution.
  • Release checklist step 11 restarts the local API first (c55b696) - /api/health reports the version constant held by the running process, so a long-lived API keeps answering with the previous version after the files change. The same root cause tripped both the peer harness and the Dashboard on the same day.

Upgrade notes

  • psutil is installed by python install.py --update, which import-checks every declared dependency. Without it, on POSIX, an existing API is still recognised and a dead lock owner still reclaimed; uncertain identities never authorise a kill or a duplicate service.
  • Codex users: the unread hook changed script content only. hooks.json and the trust lock are unchanged, so no re-trust is needed; the installed script takes effect on the next prompt.
  • Deferred, on the task wall: a recorded PID reused by an unrelated live process blocks auto-start with a warning until cleared, because the identity check does not yet distinguish "not our API" from "cannot tell"; and the guardrail middleware still rejects hook payloads that happen to contain path-traversal or code-injection lookalikes, a pre-existing loss path now recorded against the silent-drop task.