Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
160 commits
Select commit Hold shift + click to select a range
c80b9d2
docs: design configurable Stream Deck Plus dials
tlk3 Aug 9, 2026
80b95b4
docs: plan Stream Deck Plus dial implementation
tlk3 Aug 9, 2026
50b8d14
feat: add configurable Codex dial presets
tlk3 Aug 9, 2026
fb57437
fix: harden Codex dial settings normalization
tlk3 Aug 9, 2026
5f64164
feat: add dial selectors and live feedback model
tlk3 Aug 9, 2026
855a585
fix: align dial usage runtime contract
tlk3 Aug 9, 2026
6839f3a
fix: harden dial selector boundaries
tlk3 Aug 9, 2026
27937c3
feat: expose reasoning and refresh usage snapshots
tlk3 Aug 9, 2026
0cf0dc9
fix: harden usage refresh ordering
tlk3 Aug 9, 2026
7d3a3c6
fix: require fresh usage refresh snapshots
tlk3 Aug 9, 2026
ecbf1c3
fix: validate refreshed usage scalars
tlk3 Aug 9, 2026
38e1226
feat: dispatch configurable Codex dial gestures
tlk3 Aug 9, 2026
c6f85f2
fix: isolate Codex dial gesture lifecycles
tlk3 Aug 9, 2026
a42b7d0
fix: close dial lifecycle integration races
tlk3 Aug 9, 2026
2aa8b4e
feat: register the Stream Deck Plus Codex Dial
tlk3 Aug 9, 2026
e7dc640
test: keep dial packaging checks side-effect free
tlk3 Aug 9, 2026
e001584
feat: configure Codex dials per knob
tlk3 Aug 9, 2026
5cd2f93
fix: align dial inspector with runtime settings
tlk3 Aug 9, 2026
7b7c851
docs: explain configurable Stream Deck Plus dials
tlk3 Aug 9, 2026
8a90c30
fix: align Stream Deck Plus release guidance
tlk3 Aug 9, 2026
1f0e474
fix: enforce dial availability feedback
tlk3 Aug 9, 2026
1b37d80
fix: refine dial availability states
tlk3 Aug 9, 2026
680a607
fix: audit declared Codex Dial assets
tlk3 Aug 9, 2026
2eaa02b
fix: harden dial feedback and command bounds
tlk3 Aug 9, 2026
2be01ec
fix: harden relay snapshots and protected refreshes
tlk3 Aug 9, 2026
5c857a2
fix: reject symlinked release assets
tlk3 Aug 9, 2026
01d88dc
fix: close relay reconnect and rendering gaps
tlk3 Aug 9, 2026
00d20b3
docs: specify Fast feedback and reasoning fix
tlk3 Aug 9, 2026
6bb57ec
docs: plan Fast and reasoning corrections
tlk3 Aug 9, 2026
a2de49a
feat: expose authoritative Fast mode state
tlk3 Aug 9, 2026
abbb21c
test: execute serialized Fast state helpers
tlk3 Aug 9, 2026
43c3ef2
fix: show live Fast mode feedback
tlk3 Aug 9, 2026
e48eae8
fix: queue Fast refresh after active poll
tlk3 Aug 9, 2026
aaebdf6
fix: refresh Fast state after failed poll
tlk3 Aug 9, 2026
de66f5d
test: await Fast feedback rendering
tlk3 Aug 9, 2026
ff2af3f
fix: order Fast feedback renders
tlk3 Aug 9, 2026
a4975b8
fix: stop Fast refresh after shutdown
tlk3 Aug 9, 2026
6da2c4d
fix: use dedicated reasoning commands
tlk3 Aug 9, 2026
c0e105e
docs: explain Fast and reasoning feedback
tlk3 Aug 9, 2026
13fd1d1
fix: resolve current Codex command runner
tlk3 Aug 9, 2026
ec4b7bc
docs: make installation plan portable
tlk3 Aug 9, 2026
8073099
fix: scope direct runner to reasoning
tlk3 Aug 10, 2026
dd93a54
docs: clean design whitespace
tlk3 Aug 10, 2026
afe1328
docs: specify Reasoning Ultra toggle
tlk3 Aug 10, 2026
5bfa155
docs: specify readable usage countdowns
tlk3 Aug 10, 2026
3e39df5
docs: plan Reasoning Ultra toggle
tlk3 Aug 10, 2026
83ea3e8
docs: plan readable usage countdowns
tlk3 Aug 10, 2026
3cb961d
feat: show usage reset days
tlk3 Aug 10, 2026
dcfa262
feat: configure Ultra per reasoning dial
tlk3 Aug 10, 2026
1a51122
test: cover Ultra setting migration
tlk3 Aug 10, 2026
dc096c2
feat: stop reasoning dial before Ultra
tlk3 Aug 10, 2026
6929674
test: execute guarded reasoning branch
tlk3 Aug 10, 2026
7464048
fix: harden Ultra reasoning guard
tlk3 Aug 10, 2026
0e4e3e0
fix: serialize guarded reasoning updates
tlk3 Aug 10, 2026
c4c6d11
feat: relay Ultra guard results
tlk3 Aug 10, 2026
225c342
fix: close Ultra relay lifecycle races
tlk3 Aug 10, 2026
e1ad165
fix: validate remote reasoning outcomes
tlk3 Aug 10, 2026
4c81b6f
docs: explain Ultra and usage feedback
tlk3 Aug 10, 2026
db29ada
fix: serialize Ultra notice feedback
tlk3 Aug 10, 2026
27c888a
fix: gate remote Ultra policy support
tlk3 Aug 10, 2026
bd1c5bd
docs: specify immediate reasoning feedback
tlk3 Aug 10, 2026
d15ca65
docs: plan immediate reasoning feedback
tlk3 Aug 10, 2026
c9067e5
fix: read current Codex reasoning model
tlk3 Aug 10, 2026
3f8ca81
fix: reject ambiguous reasoning model data
tlk3 Aug 10, 2026
3c6c7f2
fix: anchor current reasoning model discovery
tlk3 Aug 10, 2026
d96dde2
fix: avoid blocking watcher Node discovery
tlk3 Aug 11, 2026
f8f7308
fix: bound watcher Spotlight fallback
tlk3 Aug 11, 2026
f504285
fix: clean up interrupted Spotlight lookup
tlk3 Aug 11, 2026
7b8bdeb
docs: use visible reasoning model labels
tlk3 Aug 11, 2026
60c1c24
docs: refine reasoning feedback ordering
tlk3 Aug 11, 2026
652fb37
docs: make guide update test-driven
tlk3 Aug 11, 2026
523cc08
fix: resolve visible reasoning model label
tlk3 Aug 11, 2026
c49afcb
fix: harden reasoning metadata discovery
tlk3 Aug 11, 2026
cd3002f
fix: handle live reasoning model leaves
tlk3 Aug 11, 2026
eda1046
fix: reject hidden reasoning triggers
tlk3 Aug 11, 2026
5529a63
docs: align reasoning leaf matching
tlk3 Aug 11, 2026
e07651c
feat: return confirmed reasoning effort
tlk3 Aug 11, 2026
801e872
fix: await and validate reasoning feedback
tlk3 Aug 11, 2026
d95748b
fix: render reasoning feedback immediately
tlk3 Aug 11, 2026
8d9426a
fix: match reasoning feedback by host identity
tlk3 Aug 11, 2026
0001acf
fix: pin relay host platform identity
tlk3 Aug 11, 2026
5de5d0f
docs: explain immediate reasoning feedback
tlk3 Aug 11, 2026
e9e37f6
fix: recover timed-out reasoning mutex
tlk3 Aug 11, 2026
b456786
fix: dedupe reasoning catalog records
tlk3 Aug 11, 2026
aec5e76
test: make reasoning timeout lifecycle deterministic
tlk3 Aug 11, 2026
dc6765f
fix: validate reasoning effort identifiers
tlk3 Aug 11, 2026
2305d68
fix: validate renderer snapshot effort identifiers
tlk3 Aug 11, 2026
a20b074
fix: accept current reasoning catalog keys
tlk3 Aug 11, 2026
2d00fae
fix: require exact reasoning query keys
tlk3 Aug 11, 2026
6583066
docs: design model presets dial
tlk3 Aug 11, 2026
9396d4c
docs: use native paired model selection
tlk3 Aug 11, 2026
c57903b
docs: plan model presets dial
tlk3 Aug 11, 2026
6364017
feat: add model preset dial settings
tlk3 Aug 11, 2026
73b64da
fix: harden dial settings normalization
tlk3 Aug 11, 2026
85aa830
feat: expose the active Codex model catalog
tlk3 Aug 11, 2026
72b0a83
fix: harden model catalog snapshots
tlk3 Aug 11, 2026
91e529a
fix: validate and transport model catalog authority
tlk3 Aug 11, 2026
7487c55
fix: commit relay snapshot state after publication
tlk3 Aug 11, 2026
1c706b0
feat: apply model presets through Codex
tlk3 Aug 11, 2026
fbcc535
fix: harden model preset selector authorization
tlk3 Aug 11, 2026
dbe1fa0
fix: validate model selector callback arity safely
tlk3 Aug 11, 2026
d14edea
feat: configure model presets per dial
tlk3 Aug 11, 2026
5cc610c
fix: preserve model preset dial customization
tlk3 Aug 11, 2026
13dabd4
fix: clarify model preset editor state
tlk3 Aug 11, 2026
63ecb6c
feat: switch model presets from the dial
tlk3 Aug 11, 2026
607d525
fix: serialize model and reasoning dial changes
tlk3 Aug 11, 2026
22b3179
fix: patch local reasoning before preset resolution
tlk3 Aug 11, 2026
fcd4b72
fix: reconcile unconfirmed reasoning before presets
tlk3 Aug 11, 2026
224326b
fix: cancel model mutations on shutdown
tlk3 Aug 11, 2026
7d693be
fix: bound shared model mutation backlog
tlk3 Aug 11, 2026
ab15bcb
fix: fence stale model mutation results
tlk3 Aug 11, 2026
b319a5b
fix: reject queued mutations on shutdown
tlk3 Aug 11, 2026
dd9bf76
fix: wait for committed model authority
tlk3 Aug 11, 2026
93abdfc
fix: reconcile model state after confirmation
tlk3 Aug 11, 2026
f1f6835
feat: relay confirmed model preset changes
tlk3 Aug 11, 2026
5f5fc05
fix: require authoritative preset snapshot match
tlk3 Aug 11, 2026
3d22c9b
fix: harden nested relay snapshot parsing
tlk3 Aug 11, 2026
e52db1d
fix: validate relay command envelopes
tlk3 Aug 11, 2026
500f4b8
fix: degrade on failed preset snapshot
tlk3 Aug 11, 2026
d8d7d3b
fix: bound relay client pending commands
tlk3 Aug 11, 2026
f8ec590
fix: handle relay close failures on shutdown
tlk3 Aug 11, 2026
6b88fdf
fix: require exact relay server messages
tlk3 Aug 11, 2026
be5c7ea
docs: explain the model presets dial
tlk3 Aug 11, 2026
3f2d04d
docs: correct model presets release guidance
tlk3 Aug 11, 2026
98614fd
fix: reserve global mutation order at enqueue
tlk3 Aug 11, 2026
e19846f
fix: scope property inspector messages to action
tlk3 Aug 11, 2026
fd15a54
fix: authorize live model catalog projections
tlk3 Aug 11, 2026
b774a34
fix: accept current Codex fiber depth
tlk3 Aug 11, 2026
eb4c9e0
test: wait for relay identity invalidation
tlk3 Aug 11, 2026
da58c38
fix: refresh property inspector action context
tlk3 Aug 11, 2026
60e10f6
fix: use property inspector registration context
tlk3 Aug 11, 2026
0a566d4
docs: clean Stream Deck Plus plan formatting
tlk3 Aug 12, 2026
48ce1de
fix: restore standalone Codex keycaps
tlk3 Aug 12, 2026
5580b3a
fix: route reasoning keycaps through guarded controls
tlk3 Aug 12, 2026
c80c319
docs: design non-destructive bridge recovery
tlk3 Aug 16, 2026
bf1dae8
fix: prevent automatic Codex restarts
tlk3 Aug 16, 2026
5b7f524
fix: activate Micro without legacy Statsig
tlk3 Aug 16, 2026
8c62d73
fix: accept disposable model catalog responses
tlk3 Sep 3, 2026
b2b6fff
fix: recognize GPT-prefixed model picker labels
tlk3 Sep 5, 2026
bfa27e8
fix: recover task status through desktop IPC after normal Codex launches
tlk3 Sep 8, 2026
10efad9
fix: restore account usage after normal macOS Codex launches
tlk3 Sep 10, 2026
fc0b134
fix: expose normal-launch usage diagnostics and control limits
tlk3 Sep 10, 2026
c86701e
fix: accept larger Codex desktop task snapshots
tlk3 Sep 13, 2026
3a943f4
fix: stream desktop task snapshots with bounded metadata
tlk3 Sep 13, 2026
35bbe3a
fix: retain task controls during catalog outages
tlk3 Sep 17, 2026
7ecc6c3
fix: fail closed on invalid task catalogs
tlk3 Sep 17, 2026
0930763
fix: survive integrated terminal restarts
tlk3 Sep 18, 2026
0d39fac
fix: launch macOS watcher without shell
tlk3 Sep 20, 2026
ce5eef6
fix: keep desktop IPC health stable
tlk3 Sep 20, 2026
abccca4
fix: recover bridge during safe startup window
tlk3 Sep 20, 2026
e02a728
fix: start watcher without background deferral
tlk3 Sep 20, 2026
1cee552
fix: guard startup recovery races
tlk3 Sep 20, 2026
3057ce1
fix: bound startup recovery completion
tlk3 Sep 20, 2026
b053cec
fix: survive Codex desktop layout updates
tlk3 Sep 30, 2026
ec3d075
fix: exclude internal reviews and discover shared Micro runtime
tlk3 Oct 2, 2026
519e917
fix: preserve model preset dropdowns during background refreshes
tlk3 Oct 2, 2026
a53d115
fix: read model catalog from active selector ancestry
tlk3 Oct 2, 2026
9b7a079
fix(macos): preserve startup bridge recovery window
tlk3 Oct 4, 2026
aa73c62
docs: record verified startup recovery deployment
tlk3 Oct 4, 2026
1e72dd4
fix: exit plugin process after shutdown signals
tlk3 Oct 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## Unreleased

- Added a per-knob Stream Deck + Model Presets dial that immediately and continuously wraps through configurable model/reasoning pairs using Codex's native paired selection path.
- Added live model and reasoning dropdowns with add, remove, drag, accessible reorder controls, preserved unavailable entries, and an opt-in Ultra policy.
- Added `SWITCHING…` as non-optimistic pre-confirmation feedback, plus active-position and `UNLISTED` feedback derived only from confirmed state, along with empty, unavailable, and health feedback without keyboard or focus navigation.
- Added capability-gated Model Presets support for authenticated Mac/Windows relays; older peers fail closed without partial switching.
- Fixed standalone command keycaps such as Terminal after current Codex builds moved their guarded runner behind the live Micro bridge import.

## 0.7.0.2 - 2026-07-22

- Normalize remote snapshot timestamps to local receipt time so ordinary Mac/Windows clock differences cannot hide working, selected, approval, or usage state.
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Windows-only and Mac-only mode have no relay, no second computer dependency, and
- Native key-down/key-up handling for Micro slots `ACT06` through `ACT12`.
- Native joystick up, right, down, left, and encoder click.
- Dedicated reasoning-effort up/down buttons with press-and-hold repeat.
- Configurable Stream Deck + knobs with independent rotate, press, touch, and live feedback controls, including per-knob Model Presets that immediately switch among ordered model/reasoning pairs; see the [Stream Deck + guide](docs/STREAM_DECK_PLUS.md).
- Live usage controls: a configurable circular 5-hour/weekly limit key and a two-window overview.
- A centered reset-credit counter with a deliberate 1.2-second hold before an applicable credit can be consumed.
- A local `codex://threads/new` action for a new task.
Expand All @@ -43,9 +44,10 @@ Windows-only and Mac-only mode have no relay, no second computer dependency, and
- Elgato Stream Deck 6.6 or newer on the computer connected to the Stream Deck.
- Node.js 20 or newer for the platform launcher.
- Windows 10+ or macOS 13+.
- Tested hardware: standard 15-key Stream Deck MK.2.
- Tested keypad hardware: standard 15-key Stream Deck MK.2.
- Stream Deck + is required for the Encoder-only **Codex Dial** action.

Other Stream Deck models may work, but the included layout and physical-device testing target the normal 5×3 MK.2.
Other keypad models may work, but the included 15-key layout and its existing physical-device testing target the normal 5×3 MK.2. Stream Deck + dial setup and its separate verification boundary are documented in the [Stream Deck + guide](docs/STREAM_DECK_PLUS.md).

## Quick install

Expand All @@ -56,6 +58,7 @@ Other Stream Deck models may work, but the included layout and physical-device t
3. Follow [Windows](docs/WINDOWS.md), [macOS](docs/MACOS.md), or [Windows + Mac](docs/MULTI_HOST.md).
4. In **Codex Settings > Codex Micro**, choose the agent source, action assignments, joystick actions, and encoder behavior.
5. Build the two Stream Deck pages below.
6. On Stream Deck +, add the optional four-knob [Status-focused Codex Dial layout](docs/STREAM_DECK_PLUS.md#status-focused-four-knob-defaults). Its Model Presets knob uses live Codex model/reasoning choices, continuous wrap, Press None, and Fast Mode on touch by default.

The iPhone companion is currently source-only: **a Mac with Xcode is required
to build, sign, and install it**, even when the phone will control only a
Expand Down Expand Up @@ -161,9 +164,11 @@ The current build was locally validated against:

The Windows physical-device path and the Windows+Mac relay were exercised on the real setup. The macOS launcher, watcher, native bridge, and plugin package are validated; a Stream Deck physically attached to the Mac has not yet been hardware-tested. These are tested versions, not strict maximums.

Normal macOS launches also support [task status, navigation, and account usage](docs/DESKTOP_IPC.md), without debug flags or a Codex restart. Usage is read through the bundled CLI, independently of task status; missing account windows remain unavailable. Composer/native action controls still require the renderer bridge; the fallback does not claim full control parity.

## Troubleshooting

Start with [Troubleshooting](docs/TROUBLESHOOTING.md). The important rule is: restart only the Stream Deck plugin/app for plugin updates. The macOS watcher never launches a closed Codex app; after a manual app start it permits at most one guarded recovery restart and opens a global cooldown before any later recovery.
Start with [Troubleshooting](docs/TROUBLESHOOTING.md). Restart only the Stream Deck plugin/app for plugin updates. The macOS watcher never launches a closed Codex app or automatically restarts an active session. After a normal launch, the plugin can recover task status through desktop IPC; native renderer controls require an explicitly requested bridge-enabled restart.

## Build and release validation

Expand Down
15 changes: 14 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,19 @@ The same plugin runs on Windows and macOS. It discovers the local loopback port
4. dispatch Micro HID and joystick events;
5. emulate native encoder-rotation HID events for reasoning-effort changes;
6. resolve standalone keycap actions from Codex's live Micro keycap registry and current official command runner;
7. read Codex's renderer-owned `rate-limit-status` query and normalize its current 5-hour, weekly, and reset-credit state.
7. read Codex's renderer-owned `rate-limit-status` query and normalize its current 5-hour, weekly, and reset-credit state;
8. best-effort read the active visible composer's optional reasoning effort for honest Encoder feedback.

The bridge does not emulate a USB HID device and installs no driver.

Usage data remains part of the same typed host snapshot, but usage and reset credits are account-scoped and therefore do not follow the Mac/Windows function-key target. The controller prefers a healthy local account snapshot and falls back to the paired host only when local usage is unavailable. Window identity is derived from the duration returned by Codex rather than from primary/secondary ordering. A missing 5-hour window is represented as unavailable, and Automatic mode falls back to weekly. The bridge refreshes a stale renderer-owned usage query at most once every 15 seconds, so background-window values do not depend on Codex receiving focus.

Reset consumption is the only mutating usage operation. It is a narrow typed relay command and calls Codex's current native reset-credit client only after the Stream Deck key has been held for 1.2 seconds. The bridge verifies both availability and applicability, selects an available plan-supported credit, uses a unique redemption request ID, and then refreshes the renderer query. No credential, raw endpoint access, or arbitrary request surface is exposed to the relay.

`Codex Dial` is one Encoder-only Stream Deck action with versioned, per-instance settings. Its pure dial domain normalizes preset and custom settings against allow-listed bindings, expands each paired-rotation detent into an ordered command, reconciles selector identity, and derives the five stable feedback fields. The action adapter forwards Encoder rotate, down, up, and touch events to the controller; it does not add a second command surface. Rotation can dispatch paired controls immediately or update a selector preview, while press and touch remain independent.

An explicit usage refresh is a typed operation. Locally, one renderer evaluation requests a fresh `rate-limit-status` query and rejects rather than replacing last-known usage when the forced refresh cannot return valid data. Remotely, the relay advertises `usage-refresh`, binds capability and snapshot state to the current authenticated connection generation, publishes a fresh post-command snapshot, and only then acknowledges success. Older peers may omit the optional capability and reasoning field.

### Optional multi-host relay

The Mac watcher can host an authenticated WebSocket relay on loopback behind an
Expand Down Expand Up @@ -147,6 +152,14 @@ The controller uses non-overlapping self-scheduled refreshes and caches the last
image sent to each action instance. Unchanged keys therefore produce no repeated
USB image writes. Animated frames are limited to working and approval states.

Encoder feedback uses a custom 200 x 100 layout with keyed title, value, detail,
indicator, and accent fields. The controller keeps selector and overview state,
an async command queue, and the last normalized feedback signature per visible
dial action. `setFeedback` is skipped when that signature is unchanged. Dial
registration, settings replacement, disappearance, and in-flight render work
are generation-aware so an obsolete action instance cannot overwrite its
replacement. See the [Stream Deck + guide](STREAM_DECK_PLUS.md) for the user-facing behavior.

## Trust boundary

CDP provides privileged access to the Codex renderer. Binding to `127.0.0.1` prevents direct access from another machine, but not from another process running as the same local user. Treat the launcher-started session like any other local debugging session:
Expand Down
78 changes: 78 additions & 0 deletions docs/DESKTOP_IPC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Normal macOS launches

When Codex opens without remote-debugging flags, Codex Deck can attach to the
existing, user-owned `CODEX_HOME/ipc/ipc.sock` instead. It never starts a router,
changes Codex configuration, rewrites the app, or restarts Codex.

This connection supports the six most recent local tasks and their live activity,
pending-input and unread status. Task buttons open the exact task through Codex's
`codex://threads/<UUID>` URL handler. Task catalog reads use macOS's bundled
SQLite executable in read-only mode and exclude archived tasks and subagents.
After one validated read, a temporary SQLite-open failure retains that in-memory
catalog while live IPC status continues; the next valid read replaces it. Invalid
catalog data still fails closed instead of being hidden by the cache.

Account usage is read separately through the bundled Codex CLI's short-lived
`app-server --stdio` helper. It sends only initialization and
`account/rateLimits/read`, uses the account's `codex` bucket (never substitutes
Spark), and caches/throttles automatic reads for 30 seconds. Background reads do
not delay task updates. Manual Usage refresh requests a fresh read. Missing
windows remain unavailable; the plugin does not manufacture a five-hour window
when the account reports only weekly usage. Reset-credit counts can be displayed,
but spending credits remains unavailable without the native applicability check.

The helper has bounded output and a ten-second timeout; only its own child process
is terminated after the read. Account errors clear usage without degrading a
connected task bridge. No desktop restart, alternate launcher, credential entry,
or Codex settings change is required. Current macOS builds place the bundled
executable at `/Applications/Codex.app/Contents/Resources/codex-cli/CodexCLI.app/Contents/MacOS/codex`;
Codex Deck retains `/Applications/Codex.app/Contents/Resources/codex` as a
compatibility fallback for older builds.

The IPC fallback does **not** expose active composer authority. Model presets,
reasoning, native action keys and joystick/encoder commands still
require the renderer bridge. They are not emulated by guessing from the last
model used by a task. The fallback supplies an unavailable native action layout
and omits active-model and active-task fields. The existing renderer path
is preferred whenever it reconnects.

If a task has no live owner/snapshot, its status is unknown (warning indicator),
not falsely idle. Connection loss clears live state; reconnect subscribes again.
Snapshots and revision-checked patches are projected to status metadata; task
messages, turns and request contents are not retained or logged. Unsupported
versions, missed revisions and nested status changes require a fresh snapshot.
The fallback reads recent ordering rather than pinned/custom Micro assignments.

This uses Codex's internal IPC stream version 11 and following version 1, not a
stable public API. Incoming frames are parsed incrementally with socket
backpressure: complete task history is never buffered or assembled into objects.
Only bounded status metadata is retained, including when a snapshot is larger
than the previous 32/64 MiB limits. The wire protocol's 32-bit frame length does
not determine an allocation. Each frame has a 30-second processing deadline;
router responses allow ten seconds for large snapshots ahead of them. Outgoing
frames remain bounded to 32 MiB. Invalid JSON, metadata-limit violations, or frame
timeouts close the connection with a 30-second retry cooldown. A future protocol change can
require another compatibility update. Complete normal-launch parity for composer
and action controls still requires an additional supported desktop interface.

The tokenizer is pinned to `stream-json` 1.9.1 for the plugin's Node 20 runtime;
the current 3.x dependency chain requires Node 22. Only `Parser.js` and its UTF-8
decoder are bundled. The npm advisory about quadratic-depth processing in the
package's filters applies to modules this integration does not import or ship;
no filters or assemblers are used. Projection also caps nesting at 128 levels,
recognized keys at 128 characters, retained scalars at 4,096 characters, and
cumulative retained metadata at 256 KiB. Oversized unknown keys and task history
are discarded incrementally. Unsupported patch values invalidate the affected
task status and request a fresh snapshot.

On Codex 26.903.61454, the inspected IPC routes expose task owner/follower updates,
not a general desktop command registry/dispatcher or the focused composer's
model and reasoning selection. Updating a task's next-turn settings is not an
equivalent replacement for changing the current composer. Full normal-launch
support needs an authenticated desktop interface for focused task/composer state,
validated model/effort changes, and native action dispatch. This implementation
does not impersonate privileged clients or bypass desktop-access restrictions.

Verification covers fragmented framing, activity projection, live subscription,
revision gaps, reconnect cleanup, and connection-only renderer fallback. A live
read-only probe was also run against the existing Codex IPC socket without CDP.
14 changes: 11 additions & 3 deletions docs/MACOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ Tailscale remote access remains a separate optional profile; see
**Start Codex Deck.command** is the double-clickable equivalent of `start`.
5. Open **Codex Settings > Codex Micro**, configure the native slots, and add the actions from the [recommended layout](../README.md#recommended-15-key-layout). Leave the Windows/Mac target position empty or replace it with another action.

### Stream Deck + knobs

On a Stream Deck +, drag one **Codex Dial** action onto each knob you want to use. The recommended Model Presets, Agents, Actions, and Usage presets, independent rotate/press/touch settings, and the macOS physical verification checklist are in the [Stream Deck + guide](STREAM_DECK_PLUS.md). The guide's checklist is the verification boundary; this setup page does not by itself claim that a specific build completed hardware QA.

If an archive tool removed executable permissions, restore only the two launcher files:

```zsh
Expand All @@ -51,28 +55,32 @@ chmod +x start-codex-deck.sh "Start Codex Deck.command"
./start-codex-deck.sh install
```

`install` copies the watcher runtime into Application Support and installs a per-user LaunchAgent. It does not restart a normal Codex session already open during first installation and never launches Codex while the app is closed. After you open Codex normally, a later unbridged process must remain stable before it may receive one graceful recovery restart. A global cooldown blocks further automatic recovery across replacement process IDs, preventing restart loops after crashes, power loss, or incomplete app startup.
`install` copies the watcher runtime into Application Support and installs a per-user LaunchAgent. It leaves a normal Codex session already open during installation untouched and never launches Codex while the app is closed. After observing Codex closed, when the running watcher observes a newly opened normal Codex process, it waits ten seconds for that exact process to stabilize, then starts at most one bridge-enabled recovery while the process is still less than 30 seconds old. The guarded shutdown and relaunch must finish within 60 seconds of that process start. The recovery is pinned to that process generation, app, and executable; it is cancelled if the process closes, is replaced, or crosses the completion deadline. Established sessions and replacements without an observed closed interval are never restarted in the background. Task status and usage remain available through the normal-launch fallback whenever renderer-only controls are unavailable.

Update by extracting the new launcher and running `install` again. The stable host identity, optional relay configuration, and user-owned icons are preserved.

The watcher validates only running app bundles; it never delays a closed-app observation with Spotlight or installed-app scans. Explicit launcher commands retain fallback installation discovery. Restarting the watcher preserves any normal Codex session already present on its first observation, even a recently opened one. Stored recovery deadlines and cooldown remain in effect, but watcher startup does not add a new recovery delay. If macOS restores Codex before the watcher can observe it closed, renderer controls still require the explicit `start --restart` command below, after active work finishes.

`watcher.log` records decision transitions, process age, and observation intervals. This distinguishes a preserved existing session from a missed startup window without logging thread contents or emitting a line every second.

## Commands

```zsh
./start-codex-deck.sh dry-run
./start-codex-deck.sh self-test
./start-codex-deck.sh start
./start-codex-deck.sh start --restart
./start-codex-deck.sh install
./start-codex-deck.sh uninstall
```

`start` asks for an explicit `yes` before restarting an already-running normal Codex session. Codex launches through LaunchServices so Input Monitoring/TCC permissions remain attached to the signed app bundle.
`start` reuses an existing renderer bridge and otherwise leaves a running normal Codex session untouched. `start --restart` is the explicit restart form. It reports the handoff as accepted, records the eventual completed, rejected, or failed result under `~/Library/Application Support/CodexDeck/restart-handoffs/`, and hands execution to a detached helper before Codex closes. That lets the relaunch finish even when invoked from Codex's integrated terminal. Concurrent requests are serialized, and a stale request cannot close a replacement Codex process. Codex launches through LaunchServices so Input Monitoring/TCC permissions remain attached to the signed app bundle.

## Files

```text
~/Library/Application Support/CodexDeck/
codex-deck-macos.mjs
watcher-launch.sh
codex-micro-bridge.json
host.json
watcher-state.json
Expand Down
Loading