Skip to content

Releases: devinoldenburg/codeplane

v29.0.15

26 May 19:35

Choose a tag to compare

Summary

Hotfix for "my queued message disappears" — when a user queued a follow-up, the message appeared in both the timeline (as an optimistic user message) AND the dock (from the server's queue). If promptAsync hit a transient network error, the optimistic timeline message was removed by sendFollowupDraft's error handler — to the user this looked like the message vanishing for no reason. v29.0.15 makes the dock the single source of truth for queued submissions until the server's worker actually runs them.

Fixed

  • sendFollowupDraft gets an optimisticMessage: boolean option (default true, preserves send/steer behavior). When false, the function skips adding the optimistic user message to the timeline AND skips the corresponding remove on failure. queueFollowup in packages/app/src/pages/session.tsx now passes optimisticMessage: false so queued items only appear in the dock — no duplicate representation, no "disappearing" on transient errors.

Verification

  • bun run typecheck clean in packages/app.
  • No server-side changes; existing queue tests still cover the wire contract.

v29.0.15

26 May 19:35

Choose a tag to compare

Summary

Hotfix for "my queued message disappears" — when a user queued a follow-up, the message appeared in both the timeline (as an optimistic user message) AND the dock (from the server's queue). If promptAsync hit a transient network error, the optimistic timeline message was removed by sendFollowupDraft's error handler — to the user this looked like the message vanishing for no reason. v29.0.15 makes the dock the single source of truth for queued submissions until the server's worker actually runs them.

Fixed

  • sendFollowupDraft gets an optimisticMessage: boolean option (default true, preserves send/steer behavior). When false, the function skips adding the optimistic user message to the timeline AND skips the corresponding remove on failure. queueFollowup in packages/app/src/pages/session.tsx now passes optimisticMessage: false so queued items only appear in the dock — no duplicate representation, no "disappearing" on transient errors.

Verification

  • bun run typecheck clean in packages/app.
  • No server-side changes; existing queue tests still cover the wire contract.

v29.0.15

26 May 19:35

Choose a tag to compare

Summary

Hotfix for "my queued message disappears" — when a user queued a follow-up, the message appeared in both the timeline (as an optimistic user message) AND the dock (from the server's queue). If promptAsync hit a transient network error, the optimistic timeline message was removed by sendFollowupDraft's error handler — to the user this looked like the message vanishing for no reason. v29.0.15 makes the dock the single source of truth for queued submissions until the server's worker actually runs them.

Fixed

  • sendFollowupDraft gets an optimisticMessage: boolean option (default true, preserves send/steer behavior). When false, the function skips adding the optimistic user message to the timeline AND skips the corresponding remove on failure. queueFollowup in packages/app/src/pages/session.tsx now passes optimisticMessage: false so queued items only appear in the dock — no duplicate representation, no "disappearing" on transient errors.

Verification

  • bun run typecheck clean in packages/app.
  • No server-side changes; existing queue tests still cover the wire contract.

v29.0.14

26 May 19:31

Choose a tag to compare

Summary

Hotfix for v29.0.13 where the prompt-queue dock would stay empty even after submitting a follow-up — the dock relied solely on session.queue.* SSE events, so any dropped event left the client out of sync with the server's actual queue. v29.0.14 makes the dock self-healing: every control command (enqueue, cancel, reorder, send-now) now refetches the queue snapshot from the server, so the UI converges to the server's truth regardless of SSE delivery hiccups. Also widens cross-directory event routing to include queue events.

Fixed

  • Dock now shows queued items reliably. Web app's queueFollowup / editFollowup / deleteFollowup / reorderFollowup all call a new refreshQueue(sessionID, directory) helper after the API succeeds. TUI's submit() queue path, sendQueuedFollowup, deleteQueuedFollowup, and moveQueuedFollowup do the same. Refetch is one HTTP round-trip; the event-reducer dedupes by id so the bus-event and refresh paths cleanly converge.
  • routeBySessionID in packages/app/src/context/global-sync/event-targets.ts now includes session.queue.created, session.queue.updated, and session.queue.removed. Without this, queue events only routed to the store whose directory exactly matched the event source — worktree-based sessions or scenarios with multiple open project stores would silently miss queue updates in their non-source stores.
  • Reorder 409 conflict path now triggers a refresh so the dock matches what the server actually has rather than waiting for the next event.

Verification

  • bun run typecheck clean in packages/codeplane and packages/app.
  • Existing queue unit + integration tests (test/session/prompt-queue.test.ts, test/server/session-queue.test.ts) cover the server-side reducer paths these new refetches feed; the client refetch is a pure idempotent HTTP call so server contract is unchanged.

v29.0.14

26 May 19:37

Choose a tag to compare

Summary

Hotfix for v29.0.13 where the prompt-queue dock would stay empty even after submitting a follow-up — the dock relied solely on session.queue.* SSE events, so any dropped event left the client out of sync with the server's actual queue. v29.0.14 makes the dock self-healing: every control command (enqueue, cancel, reorder, send-now) now refetches the queue snapshot from the server, so the UI converges to the server's truth regardless of SSE delivery hiccups. Also widens cross-directory event routing to include queue events.

Fixed

  • Dock now shows queued items reliably. Web app's queueFollowup / editFollowup / deleteFollowup / reorderFollowup all call a new refreshQueue(sessionID, directory) helper after the API succeeds. TUI's submit() queue path, sendQueuedFollowup, deleteQueuedFollowup, and moveQueuedFollowup do the same. Refetch is one HTTP round-trip; the event-reducer dedupes by id so the bus-event and refresh paths cleanly converge.
  • routeBySessionID in packages/app/src/context/global-sync/event-targets.ts now includes session.queue.created, session.queue.updated, and session.queue.removed. Without this, queue events only routed to the store whose directory exactly matched the event source — worktree-based sessions or scenarios with multiple open project stores would silently miss queue updates in their non-source stores.
  • Reorder 409 conflict path now triggers a refresh so the dock matches what the server actually has rather than waiting for the next event.

Verification

  • bun run typecheck clean in packages/codeplane and packages/app.
  • Existing queue unit + integration tests (test/session/prompt-queue.test.ts, test/server/session-queue.test.ts) cover the server-side reducer paths these new refetches feed; the client refetch is a pure idempotent HTTP call so server contract is unchanged.

v29.0.14

26 May 19:30

Choose a tag to compare

Summary

Hotfix for v29.0.13 where the prompt-queue dock would stay empty even after submitting a follow-up — the dock relied solely on session.queue.* SSE events, so any dropped event left the client out of sync with the server's actual queue. v29.0.14 makes the dock self-healing: every control command (enqueue, cancel, reorder, send-now) now refetches the queue snapshot from the server, so the UI converges to the server's truth regardless of SSE delivery hiccups. Also widens cross-directory event routing to include queue events.

Fixed

  • Dock now shows queued items reliably. Web app's queueFollowup / editFollowup / deleteFollowup / reorderFollowup all call a new refreshQueue(sessionID, directory) helper after the API succeeds. TUI's submit() queue path, sendQueuedFollowup, deleteQueuedFollowup, and moveQueuedFollowup do the same. Refetch is one HTTP round-trip; the event-reducer dedupes by id so the bus-event and refresh paths cleanly converge.
  • routeBySessionID in packages/app/src/context/global-sync/event-targets.ts now includes session.queue.created, session.queue.updated, and session.queue.removed. Without this, queue events only routed to the store whose directory exactly matched the event source — worktree-based sessions or scenarios with multiple open project stores would silently miss queue updates in their non-source stores.
  • Reorder 409 conflict path now triggers a refresh so the dock matches what the server actually has rather than waiting for the next event.

Verification

  • bun run typecheck clean in packages/codeplane and packages/app.
  • Existing queue unit + integration tests (test/session/prompt-queue.test.ts, test/server/session-queue.test.ts) cover the server-side reducer paths these new refetches feed; the client refetch is a pure idempotent HTTP call so server contract is unchanged.

v29.0.13

26 May 19:11

Choose a tag to compare

Summary

Codeplane v29.0.13 moves the prompt queue (follow-up queue) to be server-authoritative. The queue, FIFO ordering, retries, and execution all live on the server in a SQLite-backed prompt_job table drained by a background worker. Web, TUI, desktop, and mobile clients now only display state and send control commands — submissions survive client disconnects and server restarts, and the queue is consistent across every surface. v29.0.12's tag exists but its npm publish failed on the version-verify step (packages/plugin was not bumped); v29.0.13 carries the same payload plus that fix.

Added

  • Server-authoritative PromptQueue keyed by sessionID with per-session FIFO enforced by SQL; the in-process PromptQueueWorker claims rows on a 1s tick, runs each in a forked fiber on AppRuntime, and records terminal status.
  • HTTP routes GET /session/:id/queue (snapshot, optional ?all=1 to include terminal rows), DELETE /session/:id/queue/:jobID (cancel, idempotent), and POST /session/:id/queue/reorder (rewrite pending order; returns the affected jobs in their new order).
  • Bus events session.queue.created, session.queue.updated, and session.queue.removed published on every PromptQueue mutation so subscribed clients render the queue live without polling.
  • sort_order integer column on prompt_job plus a migration; PromptQueue.claim and PromptQueue.list order by sort_order ASC NULLS LAST, id ASC so explicit reorders sort before never-reordered rows while newly enqueued items still default to FIFO.
  • PromptQueue.reorder validates that every supplied id references a pending row in the session, fails with a PromptQueueConflict (HTTP 409) when a row's status has changed, and rewrites sort_order atomically.
  • In-process wake hook: PromptQueue.setWakeNotifier, registered by the worker on start and dropped on stop, fires safeTick after every successful enqueue so idle sends incur essentially no latency vs. the previous up-to-1s tick delay.
  • 409 PromptQueueConflict status mapped in the error middleware and registered in the OpenAPI responses set.
  • Web app + TUI now keep a prompt_queue: { [sessionID]: PromptQueueJob[] } map in sync state, populated by a session.queue.list snapshot on session change and kept fresh by the three new bus events; the reducer handles snapshot/event races idempotently (drop duplicate Created, treat unknown Updated as create) and prunes completed/cancelled while keeping failed rows visible so users can see the error.
  • TUI follow-up dock derives entirely from the server queue, previews payload text via a payload parser, and routes Send-now, Edit, Delete, and Move via queue.reorder / queue.cancel.
  • Web composer wired to the same model: queueing immediately calls promptAsync (server enqueues); reordering and cancelling go through the new routes; edit cancels the row and pre-fills the composer with the text portion of the payload.
  • Session-working detection refinements: new hasUnansweredUserMessage helper, idle/working resync polling effect in session.tsx that backfills sync state when the working signal lags, and session-turn-working component refinements.
  • New server-errors utility for normalizing SDK error envelopes into user-facing strings.
  • Desktop ui-host updates and TUI feature plugin / route updates (session-v2, system plugins).
  • New integration test test/server/session-queue.test.ts covers list (empty, populated, ?all=1), cancel (idempotent), reorder (success, 409 conflict, 404 not-found).
  • New unit tests in test/session/prompt-queue.test.ts cover Created/Updated bus events on enqueue and claim, per-row Updated events on cancelSession, reorder ordering semantics, reorder rejecting non-pending and unknown ids, and newly-enqueued jobs sorting after reordered ones.

Changed

  • TUI submission unified: every regular prompt — queue, send, and steer disposition — now flows through session.promptAsync. The synchronous client.session.prompt(...) codepath is gone from the TUI; the server's worker is the only execution path. Shell and custom commands keep their dedicated endpoints (they have different semantics).
  • PromptQueue.cancelSession reads affected rows before the bulk update so per-row Updated events can be published; the existing transactional cancellation behavior is preserved.
  • PromptQueue.recover now publishes an Updated event for each recovered row so a freshly reconnected UI sees recovery transitions without re-fetching.
  • The web app's session-page snapshot effect refetches the queue on sdk.directory / params.id change; concurrent SSE events use the same reducer path.
  • TUI double-interrupt no longer calls a local clearFollowupQueuesession.abort already calls PromptQueue.cancelSession server-side, so the queue is wiped on the server.
  • Composer abort handler now maps to session.abort (which cancels both the in-flight turn and all pending queue rows), preserving the user's "stop everything" intent.
  • PromptQueue.list ordering now matches claim so reorders are reflected in the next snapshot.
  • truthy, falsy, and number helpers are now exported from src/flag/flag.ts so flag.test.ts can test parsing semantics directly instead of doing delete require.cache + await import, which previously forked the Flag module across consumers and silently broke any later-loaded test that mutated Flag.X = true while source modules still held the original reference.

Fixed

  • Server-authoritative queue eliminates the freeze / fail-silently / double-fire / does-not-continue failure modes of the previous client-side dispatch loop: there is no longer a client-side effect that watches session_status and tries to dispatch the next job — the server worker owns that, with bounded retries and crash recovery on boot.
  • Worker latency: idle-send no longer waits up to a full TICK_MS (1s) for the worker's interval to fire. The wake hook trips safeTick synchronously after enqueue commits.
  • Best-effort bus publishes inside PromptQueue.enqueue / claim / recordResult / cancelSession / recover swallow publish failures so a transient SSE outage cannot roll back a committed DB mutation; the row is authoritative and clients can always refetch the snapshot.
  • Test isolation: plugin/workspace-adaptor.test.ts now resetDatabase() in afterEach and afterAll so the EventTable rows it writes under CODEPLANE_EXPERIMENTAL_WORKSPACES=true no longer leak into later test files (test/sync, test/workspace) and break their length-based assertions.
  • sync/index.test.ts and workspace/workspace-restore.test.ts now resetDatabase() in beforeEach as a defense-in-depth against any future polluting test.
  • v29.0.12 release was rejected by the npm-release workflow because packages/plugin/package.json was left at 29.0.11 while the rest of the monorepo bumped to 29.0.12. v29.0.13 syncs packages/plugin, packages/script, and packages/shared so the entire workspace ships at one version.

Removed

  • packages/app/src/pages/session/followup-queue.ts and its test — the nextRunnableFollowup client-side dispatch logic is obsolete now that the server worker runs head-of-queue automatically.
  • Web app: followupMutation (TanStack mutation that called sendFollowupDraft), the nextRunnableFollowup createEffect that watched local state and triggered dispatch, the items / failed / paused slots of the persisted followup store, and the local-state-only versions of sendFollowup / deleteFollowup / reorderFollowup.
  • TUI: module-level followupQueue store, the enqueueFollowup / dequeueFollowup / takeFollowup / moveFollowup / clearFollowupQueue helpers, the busy→idle drain createEffect, the QueuedSubmission type's dispatch closure field, and the redundant queue-only promptAsync branch in submit() (collapsed into the unified dispatch closure).

Verification

  • bun run typecheck clean in all 8 packages (codeplane, app, sdk/js, ui, desktop, mobile, shared, plugin).
  • bun test in packages/codeplane: queue-related suites pass (test/session/prompt-queue.test.ts, test/session/prompt-queue-worker.test.ts, test/server/session-queue.test.ts, test/queue.comprehensive.test.ts).
  • bun test --preload ./happydom.ts ./src in packages/app: 614 pass.
  • bun test in packages/ui: 349 pass.
  • bun run build in packages/codeplane produces multi-platform binaries; dist/codeplane-darwin-arm64/bin/codeplane --version returns 29.0.13.
  • End-to-end smoke against the built binary: serve boots, POST /session creates a session, GET /session/:id/queue returns [] for an empty queue.

v29.0.13

26 May 19:11

Choose a tag to compare

Summary

Codeplane v29.0.13 moves the prompt queue (follow-up queue) to be server-authoritative. The queue, FIFO ordering, retries, and execution all live on the server in a SQLite-backed prompt_job table drained by a background worker. Web, TUI, desktop, and mobile clients now only display state and send control commands — submissions survive client disconnects and server restarts, and the queue is consistent across every surface. v29.0.12's tag exists but its npm publish failed on the version-verify step (packages/plugin was not bumped); v29.0.13 carries the same payload plus that fix.

Added

  • Server-authoritative PromptQueue keyed by sessionID with per-session FIFO enforced by SQL; the in-process PromptQueueWorker claims rows on a 1s tick, runs each in a forked fiber on AppRuntime, and records terminal status.
  • HTTP routes GET /session/:id/queue (snapshot, optional ?all=1 to include terminal rows), DELETE /session/:id/queue/:jobID (cancel, idempotent), and POST /session/:id/queue/reorder (rewrite pending order; returns the affected jobs in their new order).
  • Bus events session.queue.created, session.queue.updated, and session.queue.removed published on every PromptQueue mutation so subscribed clients render the queue live without polling.
  • sort_order integer column on prompt_job plus a migration; PromptQueue.claim and PromptQueue.list order by sort_order ASC NULLS LAST, id ASC so explicit reorders sort before never-reordered rows while newly enqueued items still default to FIFO.
  • PromptQueue.reorder validates that every supplied id references a pending row in the session, fails with a PromptQueueConflict (HTTP 409) when a row's status has changed, and rewrites sort_order atomically.
  • In-process wake hook: PromptQueue.setWakeNotifier, registered by the worker on start and dropped on stop, fires safeTick after every successful enqueue so idle sends incur essentially no latency vs. the previous up-to-1s tick delay.
  • 409 PromptQueueConflict status mapped in the error middleware and registered in the OpenAPI responses set.
  • Web app + TUI now keep a prompt_queue: { [sessionID]: PromptQueueJob[] } map in sync state, populated by a session.queue.list snapshot on session change and kept fresh by the three new bus events; the reducer handles snapshot/event races idempotently (drop duplicate Created, treat unknown Updated as create) and prunes completed/cancelled while keeping failed rows visible so users can see the error.
  • TUI follow-up dock derives entirely from the server queue, previews payload text via a payload parser, and routes Send-now, Edit, Delete, and Move via queue.reorder / queue.cancel.
  • Web composer wired to the same model: queueing immediately calls promptAsync (server enqueues); reordering and cancelling go through the new routes; edit cancels the row and pre-fills the composer with the text portion of the payload.
  • Session-working detection refinements: new hasUnansweredUserMessage helper, idle/working resync polling effect in session.tsx that backfills sync state when the working signal lags, and session-turn-working component refinements.
  • New server-errors utility for normalizing SDK error envelopes into user-facing strings.
  • Desktop ui-host updates and TUI feature plugin / route updates (session-v2, system plugins).
  • New integration test test/server/session-queue.test.ts covers list (empty, populated, ?all=1), cancel (idempotent), reorder (success, 409 conflict, 404 not-found).
  • New unit tests in test/session/prompt-queue.test.ts cover Created/Updated bus events on enqueue and claim, per-row Updated events on cancelSession, reorder ordering semantics, reorder rejecting non-pending and unknown ids, and newly-enqueued jobs sorting after reordered ones.

Changed

  • TUI submission unified: every regular prompt — queue, send, and steer disposition — now flows through session.promptAsync. The synchronous client.session.prompt(...) codepath is gone from the TUI; the server's worker is the only execution path. Shell and custom commands keep their dedicated endpoints (they have different semantics).
  • PromptQueue.cancelSession reads affected rows before the bulk update so per-row Updated events can be published; the existing transactional cancellation behavior is preserved.
  • PromptQueue.recover now publishes an Updated event for each recovered row so a freshly reconnected UI sees recovery transitions without re-fetching.
  • The web app's session-page snapshot effect refetches the queue on sdk.directory / params.id change; concurrent SSE events use the same reducer path.
  • TUI double-interrupt no longer calls a local clearFollowupQueuesession.abort already calls PromptQueue.cancelSession server-side, so the queue is wiped on the server.
  • Composer abort handler now maps to session.abort (which cancels both the in-flight turn and all pending queue rows), preserving the user's "stop everything" intent.
  • PromptQueue.list ordering now matches claim so reorders are reflected in the next snapshot.
  • truthy, falsy, and number helpers are now exported from src/flag/flag.ts so flag.test.ts can test parsing semantics directly instead of doing delete require.cache + await import, which previously forked the Flag module across consumers and silently broke any later-loaded test that mutated Flag.X = true while source modules still held the original reference.

Fixed

  • Server-authoritative queue eliminates the freeze / fail-silently / double-fire / does-not-continue failure modes of the previous client-side dispatch loop: there is no longer a client-side effect that watches session_status and tries to dispatch the next job — the server worker owns that, with bounded retries and crash recovery on boot.
  • Worker latency: idle-send no longer waits up to a full TICK_MS (1s) for the worker's interval to fire. The wake hook trips safeTick synchronously after enqueue commits.
  • Best-effort bus publishes inside PromptQueue.enqueue / claim / recordResult / cancelSession / recover swallow publish failures so a transient SSE outage cannot roll back a committed DB mutation; the row is authoritative and clients can always refetch the snapshot.
  • Test isolation: plugin/workspace-adaptor.test.ts now resetDatabase() in afterEach and afterAll so the EventTable rows it writes under CODEPLANE_EXPERIMENTAL_WORKSPACES=true no longer leak into later test files (test/sync, test/workspace) and break their length-based assertions.
  • sync/index.test.ts and workspace/workspace-restore.test.ts now resetDatabase() in beforeEach as a defense-in-depth against any future polluting test.
  • v29.0.12 release was rejected by the npm-release workflow because packages/plugin/package.json was left at 29.0.11 while the rest of the monorepo bumped to 29.0.12. v29.0.13 syncs packages/plugin, packages/script, and packages/shared so the entire workspace ships at one version.

Removed

  • packages/app/src/pages/session/followup-queue.ts and its test — the nextRunnableFollowup client-side dispatch logic is obsolete now that the server worker runs head-of-queue automatically.
  • Web app: followupMutation (TanStack mutation that called sendFollowupDraft), the nextRunnableFollowup createEffect that watched local state and triggered dispatch, the items / failed / paused slots of the persisted followup store, and the local-state-only versions of sendFollowup / deleteFollowup / reorderFollowup.
  • TUI: module-level followupQueue store, the enqueueFollowup / dequeueFollowup / takeFollowup / moveFollowup / clearFollowupQueue helpers, the busy→idle drain createEffect, the QueuedSubmission type's dispatch closure field, and the redundant queue-only promptAsync branch in submit() (collapsed into the unified dispatch closure).

Verification

  • bun run typecheck clean in all 8 packages (codeplane, app, sdk/js, ui, desktop, mobile, shared, plugin).
  • bun test in packages/codeplane: queue-related suites pass (test/session/prompt-queue.test.ts, test/session/prompt-queue-worker.test.ts, test/server/session-queue.test.ts, test/queue.comprehensive.test.ts).
  • bun test --preload ./happydom.ts ./src in packages/app: 614 pass.
  • bun test in packages/ui: 349 pass.
  • bun run build in packages/codeplane produces multi-platform binaries; dist/codeplane-darwin-arm64/bin/codeplane --version returns 29.0.13.
  • End-to-end smoke against the built binary: serve boots, POST /session creates a session, GET /session/:id/queue returns [] for an empty queue.

v29.0.13

26 May 19:01

Choose a tag to compare

Summary

Codeplane v29.0.13 moves the prompt queue (follow-up queue) to be server-authoritative. The queue, FIFO ordering, retries, and execution all live on the server in a SQLite-backed prompt_job table drained by a background worker. Web, TUI, desktop, and mobile clients now only display state and send control commands — submissions survive client disconnects and server restarts, and the queue is consistent across every surface. v29.0.12's tag exists but its npm publish failed on the version-verify step (packages/plugin was not bumped); v29.0.13 carries the same payload plus that fix.

Added

  • Server-authoritative PromptQueue keyed by sessionID with per-session FIFO enforced by SQL; the in-process PromptQueueWorker claims rows on a 1s tick, runs each in a forked fiber on AppRuntime, and records terminal status.
  • HTTP routes GET /session/:id/queue (snapshot, optional ?all=1 to include terminal rows), DELETE /session/:id/queue/:jobID (cancel, idempotent), and POST /session/:id/queue/reorder (rewrite pending order; returns the affected jobs in their new order).
  • Bus events session.queue.created, session.queue.updated, and session.queue.removed published on every PromptQueue mutation so subscribed clients render the queue live without polling.
  • sort_order integer column on prompt_job plus a migration; PromptQueue.claim and PromptQueue.list order by sort_order ASC NULLS LAST, id ASC so explicit reorders sort before never-reordered rows while newly enqueued items still default to FIFO.
  • PromptQueue.reorder validates that every supplied id references a pending row in the session, fails with a PromptQueueConflict (HTTP 409) when a row's status has changed, and rewrites sort_order atomically.
  • In-process wake hook: PromptQueue.setWakeNotifier, registered by the worker on start and dropped on stop, fires safeTick after every successful enqueue so idle sends incur essentially no latency vs. the previous up-to-1s tick delay.
  • 409 PromptQueueConflict status mapped in the error middleware and registered in the OpenAPI responses set.
  • Web app + TUI now keep a prompt_queue: { [sessionID]: PromptQueueJob[] } map in sync state, populated by a session.queue.list snapshot on session change and kept fresh by the three new bus events; the reducer handles snapshot/event races idempotently (drop duplicate Created, treat unknown Updated as create) and prunes completed/cancelled while keeping failed rows visible so users can see the error.
  • TUI follow-up dock derives entirely from the server queue, previews payload text via a payload parser, and routes Send-now, Edit, Delete, and Move via queue.reorder / queue.cancel.
  • Web composer wired to the same model: queueing immediately calls promptAsync (server enqueues); reordering and cancelling go through the new routes; edit cancels the row and pre-fills the composer with the text portion of the payload.
  • Session-working detection refinements: new hasUnansweredUserMessage helper, idle/working resync polling effect in session.tsx that backfills sync state when the working signal lags, and session-turn-working component refinements.
  • New server-errors utility for normalizing SDK error envelopes into user-facing strings.
  • Desktop ui-host updates and TUI feature plugin / route updates (session-v2, system plugins).
  • New integration test test/server/session-queue.test.ts covers list (empty, populated, ?all=1), cancel (idempotent), reorder (success, 409 conflict, 404 not-found).
  • New unit tests in test/session/prompt-queue.test.ts cover Created/Updated bus events on enqueue and claim, per-row Updated events on cancelSession, reorder ordering semantics, reorder rejecting non-pending and unknown ids, and newly-enqueued jobs sorting after reordered ones.

Changed

  • TUI submission unified: every regular prompt — queue, send, and steer disposition — now flows through session.promptAsync. The synchronous client.session.prompt(...) codepath is gone from the TUI; the server's worker is the only execution path. Shell and custom commands keep their dedicated endpoints (they have different semantics).
  • PromptQueue.cancelSession reads affected rows before the bulk update so per-row Updated events can be published; the existing transactional cancellation behavior is preserved.
  • PromptQueue.recover now publishes an Updated event for each recovered row so a freshly reconnected UI sees recovery transitions without re-fetching.
  • The web app's session-page snapshot effect refetches the queue on sdk.directory / params.id change; concurrent SSE events use the same reducer path.
  • TUI double-interrupt no longer calls a local clearFollowupQueuesession.abort already calls PromptQueue.cancelSession server-side, so the queue is wiped on the server.
  • Composer abort handler now maps to session.abort (which cancels both the in-flight turn and all pending queue rows), preserving the user's "stop everything" intent.
  • PromptQueue.list ordering now matches claim so reorders are reflected in the next snapshot.
  • truthy, falsy, and number helpers are now exported from src/flag/flag.ts so flag.test.ts can test parsing semantics directly instead of doing delete require.cache + await import, which previously forked the Flag module across consumers and silently broke any later-loaded test that mutated Flag.X = true while source modules still held the original reference.

Fixed

  • Server-authoritative queue eliminates the freeze / fail-silently / double-fire / does-not-continue failure modes of the previous client-side dispatch loop: there is no longer a client-side effect that watches session_status and tries to dispatch the next job — the server worker owns that, with bounded retries and crash recovery on boot.
  • Worker latency: idle-send no longer waits up to a full TICK_MS (1s) for the worker's interval to fire. The wake hook trips safeTick synchronously after enqueue commits.
  • Best-effort bus publishes inside PromptQueue.enqueue / claim / recordResult / cancelSession / recover swallow publish failures so a transient SSE outage cannot roll back a committed DB mutation; the row is authoritative and clients can always refetch the snapshot.
  • Test isolation: plugin/workspace-adaptor.test.ts now resetDatabase() in afterEach and afterAll so the EventTable rows it writes under CODEPLANE_EXPERIMENTAL_WORKSPACES=true no longer leak into later test files (test/sync, test/workspace) and break their length-based assertions.
  • sync/index.test.ts and workspace/workspace-restore.test.ts now resetDatabase() in beforeEach as a defense-in-depth against any future polluting test.
  • v29.0.12 release was rejected by the npm-release workflow because packages/plugin/package.json was left at 29.0.11 while the rest of the monorepo bumped to 29.0.12. v29.0.13 syncs packages/plugin, packages/script, and packages/shared so the entire workspace ships at one version.

Removed

  • packages/app/src/pages/session/followup-queue.ts and its test — the nextRunnableFollowup client-side dispatch logic is obsolete now that the server worker runs head-of-queue automatically.
  • Web app: followupMutation (TanStack mutation that called sendFollowupDraft), the nextRunnableFollowup createEffect that watched local state and triggered dispatch, the items / failed / paused slots of the persisted followup store, and the local-state-only versions of sendFollowup / deleteFollowup / reorderFollowup.
  • TUI: module-level followupQueue store, the enqueueFollowup / dequeueFollowup / takeFollowup / moveFollowup / clearFollowupQueue helpers, the busy→idle drain createEffect, the QueuedSubmission type's dispatch closure field, and the redundant queue-only promptAsync branch in submit() (collapsed into the unified dispatch closure).

Verification

  • bun run typecheck clean in all 8 packages (codeplane, app, sdk/js, ui, desktop, mobile, shared, plugin).
  • bun test in packages/codeplane: queue-related suites pass (test/session/prompt-queue.test.ts, test/session/prompt-queue-worker.test.ts, test/server/session-queue.test.ts, test/queue.comprehensive.test.ts).
  • bun test --preload ./happydom.ts ./src in packages/app: 614 pass.
  • bun test in packages/ui: 349 pass.
  • bun run build in packages/codeplane produces multi-platform binaries; dist/codeplane-darwin-arm64/bin/codeplane --version returns 29.0.13.
  • End-to-end smoke against the built binary: serve boots, POST /session creates a session, GET /session/:id/queue returns [] for an empty queue.

v29.0.11

26 May 14:59

Choose a tag to compare

Summary

Codeplane v29.0.11 ships a broad desktop reliability, streaming, TUI follow-up, Markdown, config-secret, and session-scoping release. Desktop now reconnects to upgraded servers without stale cached UI or buffered live streams, while web and TUI surfaces get safer session routing, cleaner Markdown, and more precise per-turn diffs.

Added

  • Desktop UI cache metadata now stores an entry hash for index.html, so same-version rebuilt client bundles can invalidate stale local caches.
  • Desktop Electron proxy now has a native SSE path for /global/event and /event, forwarding cookies and instance headers while avoiding Electron session.fetch buffering.
  • TUI prompt now includes an explicit follow-up queue dock with send, edit, delete, move up, and move down actions plus command-palette entries for queued follow-ups.
  • TUI Markdown rendering now uses marked/GFM tokens for headings, lists, task lists, blockquotes, code, links, images, strikethrough, and terminal-friendly table rows.
  • Global sync now has an event-target routing helper that fans session events to relevant open stores by source directory, known session id, and project id.
  • Session turn diff filtering now matches summary diffs against files reported by completed mutating tools.
  • Desktop logger now exposes flush() so tests and diagnostics can wait for queued disk writes deterministically.

Changed

  • Desktop always probes the target server before selecting a cached UI bundle instead of trusting a fresh origin cache entry for 30 days.
  • Desktop clears renderer HTTP cache before loading prepared UI after normal opens, auth bootstrap, and server-upgrade reconnects.
  • Desktop setup, local install/cache, open, and server-upgrade reconnect progress now render as opaque fullscreen surfaces instead of translucent or inline progress fragments.
  • Desktop-managed local instances now delegate same-id version changes to the shared local-instance manager, which restarts the server when binaryVersion changes.
  • Web composer controls now live in the dock tray with a single grouped composer chrome, and the submit/stop button explicitly submits or aborts without focus and overlay edge cases.
  • Session directory listing now filters by path containment plus project id, so child directories are included only when they belong to the same project scope.
  • Secret substitution can distinguish general missing tokens from missing secrets; missing config secrets resolve to empty so the instance can still open.
  • Secret set/delete routes now invalidate config; deleting a managed secret also removes matching global-config placeholders.
  • Server error formatting now unwraps SDK error envelopes and message-like objects before presenting user-facing errors.
  • TUI remote instance forms now mask passwords fully, redact sensitive headers in display, and validate URLs with URL.
  • TUI layout components now clamp dialog, toast, scroll, and content sizes for very small terminal dimensions.
  • ChatGPT/Codex OAuth provider filtering now drops unsupported gpt-5.5-pro from the provider model list.
  • Bash tool guidance now permits user-requested visual artifacts/files instead of forcing every chart-like output through a chart widget.

Fixed

  • Fixed Desktop continuing to load old cached client UI after a server upgrade.
  • Fixed Desktop live stream endings lagging or hanging when SSE responses were buffered by Electron session fetch.
  • Fixed local Desktop instances staying on an old running binary after the selected server version changed.
  • Fixed nested project session events leaking into ancestor project stores while preserving live stream updates for stores that already know the session.
  • Fixed per-message summary diffs showing ambient or stale snapshot files from unrelated tools such as browser screenshots.
  • Fixed missing secret placeholders bricking config loading.
  • Fixed secret deletion leaving stale {secret:name} references in global config.
  • Fixed TUI link clicks opening non-http protocols such as file: or javascript:.
  • Fixed terminal remote form displays leaking the last typed password character or sensitive header values.
  • Fixed logger tests racing temporary directory cleanup and producing false append failures.

Removed

  • Removed the old custom rich Markdown block renderer, hydration, CSS, tests, and story fixtures for chart, stock, KPI, tabs, choice, callout, preview, badge, progress, timeline, quote, file-tree, comparison, and plot blocks.
  • Removed clipboard backend console.log noise from the TUI.
  • Removed Desktop macOS transparent/vibrancy window backgrounds for connect, reconnect, and setup flows.

Verification

  • bun run version:bump patch
  • bun turbo typecheck
  • bun lint
  • git diff --check
  • bun test in packages/app (612 pass)
  • bun test in packages/shared (10708 pass)
  • bun test in packages/ui (347 pass)
  • bun test in packages/desktop (1889 pass)
  • bun test test/config/config.test.ts test/plugin/codex.test.ts test/session/summary.test.ts test/session/session.test.ts test/server/global-session-list.test.ts test/tool/parameters.test.ts test/tool/tools.test.ts test/tool/forge.test.ts test/tool/git.test.ts test/agent/agent.test.ts test/tui-suite/tests/rich-block-markdown.test.tsx test/tui-suite/tests/rich-block.test.tsx test/tui-suite/tests/remote-form.test.tsx test/tui/link.test.ts in packages/codeplane (257 pass)
  • bun test test/session/llm.test.ts in packages/codeplane (15 pass)
  • bun --cwd site typecheck
  • bun --cwd site run build
  • bun --cwd packages/desktop run test:e2e (10 pass)
  • bun --cwd packages/codeplane script/build.ts --skip-install --single (--version and embedded root UI serve smoke passed)