Skip to content

Add Stream Deck Plus dials, touch controls, and model presets - #13

Open
tlk3 wants to merge 160 commits into
dazer1234:mainfrom
tlk3:agent/stream-deck-plus-controls
Open

tlk3 wants to merge 160 commits into
dazer1234:mainfrom
tlk3:agent/stream-deck-plus-controls

Conversation

@tlk3

@tlk3 tlk3 commented Aug 12, 2026 •

Copy link
Copy Markdown

Summary

This expands Codex Deck into a first-class Stream Deck Plus integration. A new Encoder-only Codex Dial action lets each knob independently configure rotation, press, touch-strip tap, and LCD feedback while preserving all existing keypad behavior.

The default four-knob layout covers:

  • Model Presets — immediately cycle through user-defined model/reasoning pairs with continuous wrap and confirmed-state feedback
  • Agents — select occupied agents, show live status/context/host feedback, and focus on press
  • Actions — select and run configured Codex Micro actions
  • Usage — select Auto/5-hour/Weekly views, toggle the overview, and refresh from touch

What changed

  • Added the Stream Deck Plus Codex Dial manifest action, encoder layout, icons, and per-action property inspector.
  • Added per-knob presets plus fully independent rotation, press, touch, and feedback settings.
  • Added accessible live model/reasoning editors with add, remove, drag, keyboard reorder controls, unavailable-entry preservation, and an opt-in Ultra policy.
  • Added native paired model/reasoning switching through Codex's own application callback. The plugin does not type, synthesize keyboard navigation, or optimistically claim a selection before Codex confirms it.
  • Added immediate confirmed reasoning feedback, ULTRA OFF fail-closed behavior, and compact day/hour/minute usage-reset labels.
  • Added controller-wide ordering, bounded backpressure, generation/lifecycle fencing, and truthful SWITCHING, UNLISTED, UNAVAILABLE, degraded, and offline states.
  • Extended the authenticated Mac/Windows relay with additive capabilities for confirmed reasoning feedback and Model Presets. Older peers are refused before a partial command is sent.
  • Added non-destructive macOS bridge recovery: the watcher now degrades and retries without terminating or relaunching Codex, including when legacy Statsig is unavailable.
  • Added macOS launcher hardening, Stream Deck Plus documentation, release-audit coverage, and extensive domain/controller/bridge/relay/property-inspector tests.
  • Restored standalone command keycaps such as Terminal against the current Codex Micro runner layout while keeping dedicated reasoning mutations on their guarded path.

User impact

Stream Deck Plus owners can use the touch screen and four knobs as live Codex controls instead of mapping the device as keypad-only. The most common workflow—jumping between a few model/reasoning combinations—now takes one detent and updates the LCD from confirmed Codex state.

Existing Stream Deck keypad actions and single-host behavior remain unchanged. Stream Deck Plus support is optional and configured per knob.

Compatibility and safety

  • Uses allow-listed typed actions only; no arbitrary commands, URLs, keyboard focus movement, or global hotkeys.
  • Keeps the Chrome DevTools endpoint loopback-only.
  • Bounds and validates model catalogs, relay payloads, pending work, and renderer traversal.
  • Preserves independent Windows-only, macOS-only, and optional multi-host operation.
  • Does not include Codex/Elgato protected artwork, local profiles, logs, tokens, or generated release bundles.

Validation

  • npm ci
  • npm run check
  • npm test — 435 passed, 1 expected skip, 0 failed
  • npm run validate
  • npm run audit:release — 3 artifact roots passed
  • git diff --check origin/main...HEAD

Manual/live verification:

  • macOS 26.5.1
  • Codex for macOS 26.810.52044 (live renderer and native Micro activation verification)
  • Stream Deck 7.4.2 (22730)
  • physical Stream Deck Plus
  • live Codex macOS renderer integration, including persisted per-knob settings, touch actions, immediate dial feedback, Model Presets, restart persistence, and local bridge recovery

The cross-platform Stream Deck SDK, Windows launcher, and authenticated relay paths have automated coverage. This PR does not claim new physical Stream Deck Plus hardware verification on Windows.

tlk3 added 30 commits August 9, 2026 12:54
@tlk3

tlk3 commented Sep 13, 2026

Copy link
Copy Markdown
Author

Fixed a fallback disconnect loop in c86701e. Codex 26.908.40834 emitted a 37,577,454-byte task snapshot, exceeding the plugin's previous 32 MiB receive limit. The plugin disconnected, cooled down for 30 seconds, retried the same snapshot, and degraded all controls repeatedly.

Incoming frames now permit up to 64 MiB; outgoing messages retain the 32 MiB limit. Invalid/oversized frames still disconnect with cooldown, and diagnostics report only numeric frame sizes rather than task content. The new regression delivers a fragmented 36 MiB payload followed by another valid message. This raises the bounded compatibility limit; it is not an unlimited streaming parser.

Validation: 461 tests passed, one expected skip; TypeScript, build, Stream Deck validation, release audit, and independent QA passed. Live checks against the running updated Codex processed the formerly rejected snapshot and continued receiving activity and weekly usage. Installed the verified plugin and reloaded Stream Deck only.

Separate unresolved issue: the Codex update also relaunched without renderer debug flags. This patch restores the task/usage fallback; Model Preset and native Action controls still require the renderer connection and a separately authorized bridge-enabled restart.

@tlk3

tlk3 commented Sep 13, 2026

Copy link
Copy Markdown
Author

Replaced buffered desktop-status decoding with incremental metadata projection in 3a943f4. The prior limit increase was insufficient: live task snapshots grew from ~36 MiB to ~72 MiB, again disconnecting the entire task/Usage fallback.

The live IPC path now streams JSON with socket backpressure and retains only bounded task-status metadata. Task history, tool output, and request bodies are skipped without assembling whole snapshots. Frame length no longer determines a buffer allocation. UTF-8 and split/coalesced frames, revision-checked patches, useful status/request updates, cancellation, and reconnect behavior are preserved. Unsupported patch values invalidate the affected task instead of inventing status. Frame deadlines and metadata/depth limits remain enforced; errors never include task content.

Validation: 472 tests passed, one expected skip; TypeScript, build, Stream Deck package validation, release audit, and independent agent QA passed. Streaming 70/80 MiB fixtures pass bounded-memory tests, including on Stream Deck's Node 20.20.0 runtime. Independent real-socket QA also passed an 80 MiB snapshot followed by reset/reconnect. Live probes against the previously failing running Codex continued receiving task status and weekly usage, with sampled heap around 9–14 MiB. Installed the matching bundle and reloaded Stream Deck only.

Dependency note: pinned stream-json 1.9.1 for Node 20 compatibility. The bundle source map confirms only Parser.js and Utf8Stream.js are included. npm reports a moderate advisory for its filter modules, which this integration neither imports nor bundles; the tokenizer also has an independent depth cap. Three existing high findings belong to the Elgato development CLI dependency tree. These audit findings are disclosed rather than described as a clean npm audit.

This resolves the snapshot-size disconnect mechanism. It does not restore the renderer connection removed by a Codex update: Model Preset and native Actions still need the previously documented bridge-enabled launch.

@tlk3

tlk3 commented Sep 17, 2026

Copy link
Copy Markdown
Author

Resolved another normal-launch degradation path in 7ecc6c3.

The Stream Deck logs showed recurring /usr/bin/sqlite3 -readonly failures with unable to open database file (14). A single transient catalog read was marking the whole local host degraded even though the desktop IPC stream, task status, navigation, and usage remained healthy.

The IPC fallback now retains the last validated six-task catalog only for that exact SQLite open-error condition, continues applying live task-status updates, and replaces the cache immediately after the next successful read. Other execution failures, malformed JSON, and invalid/unsupported catalog data still fail closed. No task messages are persisted or logged, and this does not invent model/composer/action authority.

Verification: TypeScript check; 475 tests passed with 1 expected skip; build; Stream Deck validation; release audit; independent code review; installed bundle checksum match; live Stream Deck-only reload connected to desktop IPC with local=ready, current task status, and weekly usage. Codex was not restarted.

@tlk3

tlk3 commented Sep 18, 2026

Copy link
Copy Markdown
Author

Resolved the macOS integrated-terminal restart failure in 0930763.

Root cause: start --restart waited inside the calling terminal while terminating Codex. When invoked from Codex's integrated terminal, that terminal and the launcher died before the bridge-enabled relaunch could occur, leaving Model Presets and Actions unavailable after what looked like a restart.

The explicit restart path now:

  • hands off to a detached helper before Codex exits;
  • pins the authorized process generation and exact app/executable paths;
  • revalidates immediately before termination and again before launch;
  • serializes concurrent restart requests; and
  • persists accepted/running/completed/rejected/failed outcomes under Application Support.

Regression coverage includes a real detached subprocess surviving its parent terminal, stale-generation/install rejection, concurrency serialization, and persisted outcomes. Fresh verification: 480 tests passed, 1 skipped; TypeScript check, build, Stream Deck validation, and release audit all passed. Independent review found no remaining P1/P2 issues.

The updated launcher runtime is installed locally without restarting the current Codex process. One explicit bridge-enabled restart is still required to verify the live Stream Deck recovery on Codex 26.915.31945 (9922).

@tlk3

tlk3 commented Sep 20, 2026

Copy link
Copy Markdown
Author

Resolved the recurring post-reboot/post-update degraded state in 3057ce1.

Root causes:

  • The macOS LaunchAgent could stall in shell startup or be delayed by Background process scheduling, so the watcher never reached Node after login.
  • Desktop IPC re-ran process verification on every refresh, allowing a transient ps failure to mark otherwise healthy controls degraded.
  • A normal Codex launch could remain without the renderer bridge indefinitely after the watcher came online.

Fixes:

  • Launch the installed watcher directly with a stable Node executable and no shell or Background deferral.
  • Verify the Codex process once per IPC connection/reconnection instead of once per refresh.
  • Add a bounded one-time startup recovery after the watcher has observed Codex closed, with exact process/generation checks, a stable-start window, one attempt per generation, cooldown, and repeated deadline checks before termination and relaunch. The installer deliberately leaves an already-running normal Codex session untouched.

Verification:

  • 487 tests passed, 1 expected skip
  • build and TypeScript check passed
  • Stream Deck validation passed
  • three-root release audit passed
  • macOS launcher self-test passed
  • independent review found no P0-P2 issues
  • installed LaunchAgent is running direct Node, the installed watcher and plugin hashes match the reviewed build, and the existing Codex PID remained unchanged during installation.

@tlk3

tlk3 commented Sep 30, 2026

Copy link
Copy Markdown
Author

Resolved the post-upgrade unavailable regressions in commit b053cec.

Root causes and fixes:

  • Codex Desktop 26.928.20755 moved its bundled CLI from Contents/Resources/codex to Contents/Resources/codex-cli/CodexCLI.app/Contents/MacOS/codex. The usage reader now prefers the new path and retains the legacy fallback, so Usage no longer becomes unavailable after this app update.
  • Startup bridge recovery used the same 30-second deadline for both deciding whether a launch was recent enough and completing the guarded recovery. The required stability/discovery delay could consume most of that budget, causing recovery to cancel before it began and leaving Actions and Model Presets unavailable. Recovery must still trigger within 30 seconds, but now receives a separate 60-second absolute completion deadline.
  • Added regression coverage for both app layouts and for recovery triggered after stabilization, and updated the macOS/IPC troubleshooting docs.

Verification on the patched tree:

  • 491 tests: 490 passed, 1 skipped, 0 failed
  • build and TypeScript check passed
  • Stream Deck validation passed
  • release audit passed for all 3 artifact roots
  • macOS launcher self-test passed
  • live installed plugin reconnected to desktop IPC and reported Codex weekly usage available
  • current Codex session remained untouched during installation

The model catalog remains live/dynamic; newly released models were not the cause of this failure.

@tlk3

tlk3 commented Oct 2, 2026

Copy link
Copy Markdown
Author

Patched two further compatibility issues observed with Codex 26.928.31416 on macOS 27.0.1 in commit ec3d075.

  • Automatic Guardian review tasks have a structured source.subagent but can have no agent_path. The normal-launch catalog now filters that metadata before selecting the six slots. This preserves real user chats regardless of their title. A real SQLite regression reproduces the bad selection and verifies the corrected list.
  • Codex moved its native event bus into an app-shared renderer chunk. Activation and verification now inspect shared/primary app chunks as well as legacy modules. This addresses the repeated activation timeouts with nativeEventBus:false. The new behavior test failed before the fix and passes afterward.

Verification: 492 tests passed, 1 Windows-only test skipped, 0 failed. Build, TypeScript check, Stream Deck validation, release audit, and macOS launcher self-test passed. Independent agent review found no blocking issues. The live installed plugin now selects the intended user chats, receives working/unread states, and reports weekly usage available. Current thread-stream protocol remains version 11; no arbitrary protocol-version fallback was added.

Remaining live-validation boundary: the current normal Codex launch has no renderer/debug connection. Model presets and native actions remain unavailable until a bridge-enabled reopen. At login Codex started about a minute before its watcher, outside the existing recovery window; the watcher deliberately preserved that established session. Troubleshooting now documents this login ordering and the explicit restart path. The runtime-discovery fix is tested but full model/action recovery has not yet been verified against a reopened live app. This update does not widen the automatic restart window or claim the reboot-ordering limitation is fixed.

@tlk3

tlk3 commented Oct 2, 2026

Copy link
Copy Markdown
Author

Fixed the Model Presets property-inspector dropdown dismissal in commit 519e917.

Cause: fresh snapshots change snapshotGeneration and trigger catalog deliveries. The inspector rebuilt all preset rows for every delivery and unchanged settings echo, removing the native model/reasoning select controls while the menu was open.

The editor now retains existing controls when the rendered inputs (saved pairs, model catalog, and Ultra preference) are unchanged. Real catalog changes, offline transitions, and user edits still refresh the editor. Rejected duplicate selections explicitly restore the saved values, and reasoning edits refresh labels/Add availability before saving so their echo does not cause another rebuild.

Verification: regression reproduced the old detachment and passes with the patch; 38 inspector/dial tests passed; full suite 493 passed, 1 skipped, 0 failed; TypeScript check, build, Stream Deck validation, and release audit passed. Independent agent QA found no unresolved issues.

Installed the verified inspector on macOS without restarting Codex or changing profiles/presets. Live native-menu confirmation remains pending because the current Codex model catalog is unavailable; automated coverage verifies select identity across repeated snapshots/settings echoes and real catalog/offline changes.

@tlk3

tlk3 commented Oct 2, 2026

Copy link
Copy Markdown
Author

Fixed the inactive Add Preset button in a53d115.

Live diagnosis: the editor was receiving no catalog because full-root React traversal hit its 30,000-node/context safety gate in a large conversation. The visible selector had a valid catalog provider on a 314-ancestor path with about 2,060 contexts. The button correctly refused to invent unsupported model/effort pairs, but the catalog reader unnecessarily depended on unrelated conversation size.

Catalog discovery now follows the unique visible selector fiber through its provider ancestors, bounded to 512 fibers and 8,192 contexts, without traversing unrelated child/sibling branches. Legacy root discovery remains for surfaces without a selector fiber. Audited data descriptors, unique-selector checks, strict catalog validation, cycle detection, and fail-closed exhaustion remain enforced. The paired-selector ancestry budget is also bounded at 512 to accommodate the same current UI depth.

Regression tests cover oversized conversations, 314-ancestor application, exact-boundary termination versus cycles, exhausted node/context budgets, malformed tails, accessor avoidance, and ambiguous selector fibers. Independent review found a boundary-cycle issue during development; it was reproduced and repaired before shipping.

Verification: 496 passed, 1 skipped, 0 failed; TypeScript check, build, Stream Deck validation, release audit, and independent agent QA passed. Installed the compiled plugin and restarted only its process. Codex kept the same PID and running sessions. Native Stream Deck settings now show a connected catalog; clicking Add Preset created an editable row. Removed that temporary QA row to preserve the existing preset list. Live active-model switching was not exercised.

@tlk3

tlk3 commented Oct 4, 2026

Copy link
Copy Markdown
Author

Patched macOS startup recovery in 9b7a079a02a538734c52f635462700ec3cbc3f69 after another normal-launch session left Model Presets and Actions unavailable while IPC-backed controls still worked.

  • The watcher now observes validated running process candidates only. Spotlight/all-installed-app scans no longer delay closed-app or startup observations. Explicit launcher discovery retains its lazy fallback and running-installation priority.
  • Resuming the watcher preserves real pending deadlines/cooldown/attempt history but no longer fabricates a 30-second delay that competes with the 30-second recovery-trigger window.
  • First-observation sessions are preserved, including young sessions. Startup eligibility is explicitly tied to a generation seen after a closed-app observation, so rapid app replacements cannot cause an unexpected automatic restart. Ten-second stability, exact-process authorization, 30-second trigger and 60-second completion limits remain intact.
  • Added transition-only decision logging with process age and observation intervals, plus startup/discovery/preservation/replacement regressions and macOS documentation.

Verification: 507 tests passed, 1 skipped, 0 failed; 46 focused launcher/watcher tests passed. TypeScript, build, Stream Deck validation, release audit, and built-launcher self-test passed. Independent QA found two important gaps; both were repaired with regression coverage before shipping.

Installed the verified watcher locally without restarting Codex. Installed/built runtime hashes match; the first live observation completed 344 ms after watcher startup and correctly preserved the established Codex process. Read-only observation samples took 318–342 ms.

Live verification boundary: the current normal-launch Codex process still lacks the renderer bridge, so its Model Presets and Actions require an explicit user-run start --restart after active work finishes. Current model switching and the next real reboot have not been validated with this change. macOS restoring Codex before the watcher first observes it closed remains a documented explicit-restart case, not a claim of unconditional boot recovery.

@tlk3

tlk3 commented Oct 4, 2026

Copy link
Copy Markdown
Author

Follow-up in 1e72dd4: fix plugin shutdown so SIGTERM/SIGINT stop the controller and exit instead of leaving the Stream Deck SDK socket alive. A subprocess regression reproduced the old behavior (STOP logged, process remained alive) and passes with the fix. Signal-delivery integration tests are explicitly POSIX-only because Windows child.kill force-terminates rather than delivering Node signals.

Verification: 510 tests total, 509 passed, one existing skip, zero failures; TypeScript check, package validation, and release audit passed. Focused tests also passed using the installed Stream Deck Node 20.20.0 runtime. Independent agent QA passed. Removed a machine-specific path from earlier startup documentation so the release audit remains clean.

Installed the verified plugin build locally. A normal SIGTERM now ends the patched process; its replacement reconnects to the native renderer and reports local=ready. The running Codex process was preserved. The Stream Deck CLI restart command still reported success without replacing an already-running plugin; that separate CLI behavior is not claimed fixed. Model/action data are available in a direct bridge read, but physical LCD recovery still requires confirmation, so this update does not claim the full unavailable-label report resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant