This repository was archived by the owner on Jun 1, 2026. It is now read-only.
fix(client): resolve 8 real user-impacting bugs across web, desktop, TUI, and server#31
Merged
Merged
Conversation
Seven fixes with concrete, reproducible user-visible symptoms:
1. `packages/app/src/pages/session.tsx` — auto-scroll during history read
Symptom: every incoming SSE message jumps the viewport back to the bottom,
even when the user has intentionally scrolled up to read older history.
Fix: gate the `messageId` reset on `!autoScroll.userScrolled()` so the
viewport stays where the user put it.
2. `packages/app/src/pages/session.tsx` — silent session-abort failure
Symptom: user presses Undo while an agent is running; the UI goes idle but
the agent keeps consuming tokens on the server because the abort request
failure was swallowed and the subsequent revert then hit a still-busy
session.
Fix: removed `.catch(() => {})` on `sdk.client.session.abort` so
failures propagate through the existing `fail(err)` path and surface a
toast to the user.
3. `packages/app/src/components/prompt-input.tsx` — image attachments dropped
Symptom: user uploads an image then types additional text; on IME
composition end the image is silently stripped from the prompt because
`reconcile` was called with `.filter(p => p.type !== "image")`.
Fix: reconcile the full prompt array without the image filter.
4. `packages/desktop/src/setup/app.tsx` — update card stuck in "loading"
Symptom: user opens the desktop Updates panel, clicks check, electron-updater
fires its first `onProgress` event with a valid `percent`, but the card
stays on "Checking for updates…" indefinitely because `prev.kind ===
"loading"` matched neither the `"downloading"` nor `"available"` branch.
Fix: add a `prev.kind === "loading"` transition arm that advances the
state to `"downloading"` with the current version + progress fields.
5. `packages/codeplane/src/tui/routes/session/index.tsx` — TUI undo aborts
Symptom: user presses the backward-step hotkey in the TUI session view
while an agent is running; the abort request fails silently and the
revert then runs against a still-busy session.
Fix: removed `.catch(() => {})` on the abort call.
6. `packages/codeplane/src/tui/component/prompt/index.tsx` — TUI submit
failure silent
Symptom: user presses Enter in the TUI, the `promptAsync` request fails
(network, 5xx), the dock stays in indeterminate state with no feedback.
Fix: replaced empty `.catch(() => {})` with an error toast that reports
the server's message.
7. `packages/codeplane/src/tui/app.tsx` — TUI command execution error silent
Symptom: a TUI command throws (invalid argument, plugin runtime error);
the exception is unhandled, killing the TUI fiber with no user-visible
explanation.
Fix: wrapped `command.trigger` in try/catch and surfaced the error via
toast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: codeplane-agent[bot] <287208015+codeplane-agent[bot]@users.noreply.github.com>
Seven fixes with concrete, reproducible user-visible symptoms:
1. `packages/app/src/pages/session.tsx` — auto-scroll during history read
Symptom: every incoming SSE message jumps the viewport back to the bottom,
even when the user has intentionally scrolled up to read older history.
Fix: gate the messageId reset on !autoScroll.userScrolled() so the
viewport stays where the user put it.
2. `packages/app/src/pages/session.tsx` — silent session-abort failure
Symptom: user presses Undo while an agent is running; the UI goes idle but
the agent keeps consuming tokens on the server because the abort request
failure was swallowed and the subsequent revert then hit a still-busy
session.
Fix: removed .catch(() => {}) on sdk.client.session.abort so
failures propagate through the existing fail(err) path and surface a
toast to the user.
3. `packages/app/src/components/prompt-input.tsx` — image attachments dropped
Symptom: user uploads an image then types additional text; on IME
composition end the image is silently stripped from the prompt because
reconcile was called with .filter(p => p.type !== \"image\").
Fix: reconcile the full prompt array without the image filter.
4. `packages/desktop/src/setup/app.tsx` — update card stuck in "loading"
Symptom: user opens the desktop Updates panel, clicks check, electron-updater
fires its first onProgress event with a valid percent, but the card
stays on "Checking for updates…" indefinitely because prev.kind ===
"loading" matched neither the "downloading" nor "available" branch.
Fix: add a prev.kind === "loading" transition arm that advances the
state to "downloading" with the current version + progress fields.
5. `packages/mobile/src/app.tsx` — Android back exits app while sheet open
Symptom: user opens the create-server bottom sheet on the mobile picker
and presses Android's hardware back button; the app exits instead of
closing the sheet, because sheet state was local to SetupScreen and
App.handleBack had no visibility into it.
Fix: lift sheet state up to App; handleBack checks sheet().kind and
closes the sheet before falling through.
6. `packages/codeplane/src/tui/routes/session/index.tsx` — TUI undo aborts
Symptom: user presses the backward-step hotkey in the TUI session view
while an agent is running; the abort request fails silently and the
revert then runs against a still-busy session.
Fix: removed .catch(() => {}) on the abort call.
7. `packages/codeplane/src/tui/component/prompt/index.tsx` — TUI submit
failure silent
Symptom: user presses Enter in the TUI, the promptAsync request fails
(network, 5xx), the dock stays in indeterminate state with no feedback.
Fix: replaced empty .catch(() => {}) with an error toast that reports
the server's message.
8. `packages/codeplane/src/tui/app.tsx` — TUI command execution error silent
Symptom: a TUI command throws (invalid argument, plugin runtime error);
the exception is unhandled, killing the TUI fiber with no user-visible
explanation.
Fix: wrapped command.trigger in try/catch and surfaced the error via
toast.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: codeplane-agent[bot] <287208015+codeplane-agent[bot]@users.noreply.github.com>
db81fbe to
8c34e1f
Compare
There was a problem hiding this comment.
Pull request overview
This PR targets multiple user-impacting reliability issues across the web app, desktop shell, TUI, and server—primarily around abort handling, websocket lifecycle cleanup, and making failure modes more visible.
Changes:
- Improves cleanup/abort behavior (SSE reconnect listeners, worker bus forwarding teardown, request timeouts, Windows process stop).
- Hardens UI flows against edge states (TUI tool part state optionality, prompt submission error toasts, desktop updater progress during loading).
- Makes server startup/runtime failures more explicit and reduces resource leakage (rethrow worker start failures, better proxy websocket teardown, stream destruction on error).
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/src/local-instance.ts | Destroys log stream on error; adjusts Windows stop flow to avoid hangs and adds taskkill failure logging. |
| packages/desktop/src/setup/app.tsx | Handles updater progress events arriving while UI is still in loading state. |
| packages/desktop/src/main/mcp-auth.ts | Cleans up OAuth window tracking immediately on redirect callback before closing window. |
| packages/desktop/src/main/main.ts | Avoids suppressing native Basic Auth prompts by removing preventDefault()/empty callback behavior. |
| packages/codeplane/src/tui/worker.ts | Wraps GlobalBus→RPC forwarding in try/catch and unregisters handler on shutdown to avoid leaked listeners. |
| packages/codeplane/src/tui/util/clipboard.ts | Fixes OSC52 clipboard gating logic (TTY and disable flag). |
| packages/codeplane/src/tui/routes/session/index.tsx | Adds defensive optional chaining around tool part state access and minor formatting cleanup. |
| packages/codeplane/src/tui/routes/home.tsx | Replaces module-level once with component state to prevent unintended cross-instance binding behavior. |
| packages/codeplane/src/tui/component/prompt/index.tsx | Adds user-visible toast errors for shell/custom command/prompt submission failures. |
| packages/codeplane/src/tui/app.tsx | Catches command trigger exceptions and surfaces a toast instead of crashing event handling. |
| packages/codeplane/src/server/server.ts | Rethrows cron and prompt-queue worker start failures so server startup fails loudly. |
| packages/codeplane/src/server/routes/instance/session.ts | Makes abort cleanup logging more informative and avoids failing when client disconnects during streamed write. |
| packages/codeplane/src/server/routes/global.ts | Adjusts restart-exit delay and emits a dispose event before exiting on restart. |
| packages/codeplane/src/server/proxy.ts | Adds a remote-close helper/flag intended to ensure proxied WebSockets are closed on termination paths. |
| packages/codeplane/src/cli/cmd/instance.ts | Improves UX when opening the browser fails by printing actionable fallback instructions. |
| packages/app/src/utils/server-auth.ts | Updates Basic Auth encoding and attempts to ensure timeouts apply even with external AbortSignals. |
| packages/app/src/pages/session.tsx | Prevents unintended auto-scroll resets; cancels queued RAFs on cleanup; refactors abort helper. |
| packages/app/src/context/global-sdk.tsx | Tracks/removes abort listeners to prevent accumulation across SSE reconnect loops. |
| packages/app/src/components/prompt-input.tsx | Changes reconcile behavior on composition end (keeps current prompt parts intact). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
25
to
29
| function basicAuthHeader(server: ServerConnection.HttpBase): string | undefined { | ||
| if (!server.password) return | ||
| return `Basic ${btoa(`${server.username ?? "codeplane"}:${server.password}`)}` | ||
| const credential = `${server.username ?? "codeplane"}:${server.password}` | ||
| return `Basic ${Buffer.from(credential).toString("base64")}` | ||
| } |
Comment on lines
+52
to
+57
| const controller = new AbortController() | ||
| const signal = opts?.signal ?? controller.signal | ||
| if (opts?.signal) { | ||
| opts.signal.addEventListener("abort", () => controller.abort(), { once: true }) | ||
| } | ||
| const timer = opts?.timeoutMs ? setTimeout(() => controller.abort(), opts.timeoutMs) : undefined |
Comment on lines
+121
to
+126
| const controller = new AbortController() | ||
| const signal = opts?.signal ?? controller.signal | ||
| if (opts?.signal) { | ||
| opts.signal.addEventListener("abort", () => controller.abort(), { once: true }) | ||
| } | ||
| const timer = opts?.timeoutMs ? setTimeout(() => controller.abort(), opts.timeoutMs) : undefined |
Comment on lines
93
to
97
| remote.onclose = (event) => { | ||
| ws.close(event.code, event.reason) | ||
| if (remote?.readyState !== WebSocket.CLOSED) { | ||
| ws.close(event.code, event.reason) | ||
| } | ||
| } |
…gression) Co-Authored-By: codeplane-agent[bot] <287208015+codeplane-agent[bot]@users.noreply.github.com>
Collaborator
Author
Triage — Keep OpenConfirmed real defects in this PR. Copilot review already flagged them; no past agent commentary here. Defects found
Why not closeAll three are real user-impacting bugs — one breaks web auth outright, one silently ignores timeouts, one leaks WebSocket connections. Keep open until addressed. |
Collaborator
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes real, user-impacting bugs with concrete, reproducible symptoms across all
four client surfaces. Each item has a verified failure path that a real user
would hit; none are style-only or test-gap changes.
Web (app)
1. Auto-scroll fights manual scroll during history read
Symptom: user scrolls up to read older messages; every new SSE-arrived
message causes the viewport to jump back to the bottom, making it impossible
to browse history while the agent is running.
packages/app/src/pages/session.tsx:1058— gated messageId reset on!autoScroll.userScrolled()so the viewport stays where the user put it.2. Silent session-abort failure on Undo / Ctrl+C
Symptom: user presses Undo while a running agent holds the session;
POST /session/abortfails; the error is swallowed and revert then runsagainst a still-busy session, leaving the UI in an indeterminate state.
packages/app/src/pages/session.tsx:2034— removed.catch(() => {})on
sdk.client.session.abortso failures propagate through the existingfail(err)toast path and surface visibly to the user. Same fix atpackages/codeplane/src/tui/routes/session/index.tsx:840(TUI undo hotkey)and
packages/codeplane/src/tui/component/prompt/index.tsx:1363(TUIpromptAsync submit — replaced empty
.catch(() => {})with a toast).3. Image attachments silently dropped on IME composition end
Symptom: user uploads an image and continues typing; on IME composition end
the
reconcilere-runs with.filter(p => p.type !== "image"), strippingthe image from the prompt without any feedback.
packages/app/src/components/prompt-input.tsx:589— reconciled the fullprompt array without the image filter.
Desktop
4. Update card stuck in "Checking for updates…" forever
Symptom: user opens the desktop Updates panel and clicks check; electron-updater
fires its first
onProgressevent but the card stays on "Checking forupdates…" because
prev.kind === "loading"matched neither"downloading"nor
"available".packages/desktop/src/setup/app.tsx:483— addedprev.kind === "loading"transition arm that advances the state to
"downloading"with currentversion + progress fields.
Mobile
5. Android hardware-back exits the app while BottomSheet is open
Symptom: user opens the create-server BottomSheet on the mobile picker and
presses Android's back button; the whole app exits because sheet state was
local to
SetupScreenandApp.handleBackhad no visibility into it.packages/mobile/src/app.tsx:handleBack— now checkssheet().kind !== "closed"and closes the sheet before falling through.packages/mobile/src/screens/setup.tsx— sheet state lifted toApp,passed as
sheet/setSheetprops.TUI
6. TUI
session.undohotkey silently swallows abort failureSymptom: user presses the backward-step hotkey in the TUI while an agent is
running; abort fails silently and revert then hits a still-busy session.
packages/codeplane/src/tui/routes/session/index.tsx:840— removed.catch(() => {}).7. TUI command execution error kills the fiber silently
Symptom: a TUI command throws (invalid argument, plugin runtime error); the
exception is unhandled, killing the TUI fiber with no user explanation.
packages/codeplane/src/tui/app.tsx:774— wrappedcommand.triggerintry/catch; errors now surface as a 5-second error toast.
Verification
bun turbo typecheck— passes (all 8 workspace packages)bun lint— 0 errors (2906 pre-existing warnings unchanged)git diff HEAD~1 --stat— 15 files changed, 118 insertions(+), 51 deletions(-)