Skip to content

fix(app): harden session streaming and server health#1831

Merged
benjaminshafii merged 8 commits into
devfrom
fix/session-stream-reload-health
May 18, 2026
Merged

fix(app): harden session streaming and server health#1831
benjaminshafii merged 8 commits into
devfrom
fix/session-stream-reload-health

Conversation

@benjaminshafii
Copy link
Copy Markdown
Member

@benjaminshafii benjaminshafii commented May 18, 2026

Summary

  • Flush streaming transcript deltas outside requestAnimationFrame when the webview is hidden/minimized.
  • Add a stale SSE watchdog that reconnects stuck event subscriptions.
  • Keep recently unselected sessions retained in the workspace event filter so stream deltas keep applying after switching away.
  • Keep the workspace session stream alive when leaving the session route, including trips to Settings, until retained sessions settle or expire.
  • Avoid redundant workspace activation when returning from Settings; server activation reloads the OpenCode engine and can abort an in-flight generation.
  • Preserve composer drafts when the selected model is unavailable by surfacing an error.
  • Filter noisy reload watcher events and reduce transient server disconnect status flips.
  • Update the run-evals skill to select the Different AI Daytona org before sandbox creation.
  • Add React session Flow 21 eval for 500-line streaming interruption across session switches and Settings route changes.

Why

  • Users reported responses appearing stuck until minimizing/reopening, lost prompts after stop/retry, repeated skills reload notifications, frequent OpenWork server disconnects, stream interruption after clicking away from a running session, and stream loss/abort after navigating to Settings.

Issue

  • Closes #

Scope

  • React session stream sync and composer send handling.
  • Session route workspace activation behavior.
  • OpenWork server reload filesystem watcher filtering.
  • App OpenWork server health polling hysteresis and visibility recovery.
  • Daytona eval skill instruction.
  • Human/browser-tool eval documentation.

Out of scope

  • Provider/model backend reliability changes.
  • Full redesign of reload notification UX.

Testing

Ran locally

  • pnpm --filter @openwork/app typecheck
  • pnpm --filter openwork-server typecheck
  • bun test tests/session-sync-permissions.test.ts from apps/app
  • pnpm --filter openwork-server test
  • git diff --check

Ran on Daytona

  • daytona organization use Different AI
  • Created fresh sandbox openwork-pr1831-20260518-091511 and checked out fix/session-stream-reload-health.
  • pnpm install
  • bun test tests/session-sync-permissions.test.ts from /workspace/apps/app
  • Started services and connected browser tools to https://9825-6rkwpszoftylxghh.daytonaproxy01.net.
  • Created /workspace/hello workspace through the UI.
  • Verified route connected in window.__openwork snapshot.
  • Backend model calls could not produce a real assistant stream because the sandbox returned Missing API key from opencode.ai/zen.
  • Used browser tools to switch from Session A to Session B, inject synthetic OpenCode stream events for retained Session A through the app module, switch back to Session A, and verify STREAM_SWITCH_RETENTION_OK rendered in the transcript.
  • Deleted the Daytona sandbox after verification.

Local CDP settings-route verification

  • Used local Electron CDP at http://127.0.0.1:9823.
  • Verified hidden-window streaming flush with a short OK response.
  • Verified immediate Settings navigation and return during a GPT-5.5 run with final marker SETTINGS_STREAM_DONE.
  • A stricter 500-line Settings test exposed persisted MessageAbortedError; root cause was redundant workspace activation on SessionRoute remount reloading the OpenCode engine.
  • Added activation guard so returning from Settings does not reactivate/reload when the server already has the same active workspace.
  • Added eval coverage in evals/react-session-flows.md Flow 21 for 500-line final-marker checks while switching sessions and while navigating to Settings/back.

Result

  • pass: app typecheck, server typecheck, app session sync unit test locally.
  • pass: app session sync unit test in Daytona.
  • pass: Daytona browser verification of retained off-screen session stream event application.
  • pass: eval docs whitespace validation with git diff --check.
  • fail: pnpm --filter openwork-server test has pre-existing/environment failures unrelated to this PR: better-sqlite3 is not supported in Bun in opencode-db.test, and serve-node.test intentionally surfaces a stream error after response start.

CI status

  • pass: not yet available locally.
  • code-related failures: none found in targeted checks.
  • external/env/auth blockers: Bun/better-sqlite3 full server test limitation above; Daytona model backend auth missing for real LLM stream.

Manual verification

  1. Local Electron/CDP fallback at http://127.0.0.1:9823 verified hidden-window streaming flush.
  2. Daytona browser/CDP verified workspace creation, connected route state, session switching, and retained off-screen session stream event rendering.
  3. Local testing identified and fixed Settings return abort caused by redundant workspace activation.
  4. Unit coverage verifies retained session deltas after session switch and retained workspace sync after session route unmount.
  5. Eval coverage now documents repeatable browser-tool checks for both interruption cases with explicit 500-line final markers.

Evidence

  • Local browser-tools screenshot: /var/folders/d9/xqhkvsp94rg0n0n523snqztm0000gn/T/browser-screenshot-1779116257911.png
  • Daytona browser-tools screenshot: /var/folders/d9/xqhkvsp94rg0n0n523snqztm0000gn/T/browser-screenshot-1779121647528.png
  • Local Settings-route marker screenshot: /var/folders/d9/xqhkvsp94rg0n0n523snqztm0000gn/T/browser-screenshot-1779123199366.png

Risk

  • Low-to-medium: stream reconnect timing and retained-session cleanup affect live update behavior. Retained sessions/workspace streams are bounded by TTL and released shortly after idle.

Rollback

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-app Ready Ready Preview, Comment May 18, 2026 5:53pm
openwork-den Ready Ready Preview, Comment May 18, 2026 5:53pm
openwork-den-worker-proxy Ready Ready Preview, Comment May 18, 2026 5:53pm
openwork-landing Ready Ready Preview, Comment, Open in v0 May 18, 2026 5:53pm
openwork-share Ready Ready Preview, Comment May 18, 2026 5:53pm

@benjaminshafii
Copy link
Copy Markdown
Member Author

Reload verification update:

  • Explicit engine reload endpoint still works locally: POST /workspace/:id/engine/reload returned 200 with { ok: true, reloadedAt }.
  • Filesystem watcher still emits real skill reloads: creating .opencode/skills/watch-test/SKILL.md recorded a skills reload with trigger { type: "skill", name: "watch-test", action: "updated" }.
  • Filesystem watcher ignores noisy temp/dot files: creating .opencode/skills/watch-test/.SKILL.md.swp recorded 0 additional reload events.
  • Top-level reloads still work: writing opencode.json recorded config; writing AGENTS.md recorded agents.

No reload regression found in these checks, so I did not make code changes for reload behavior.

@benjaminshafii
Copy link
Copy Markdown
Member Author

UI e2e reload verification update:

  • Updated reload-required prompt to use the same bottom-center compact notification style as the new-provider notification.
  • Removed the session activity elapsed text (Working for Ns) from the task UI.
  • Verified through the local UI/CDP target that a real OpenWork API skill update produced the bottom-center reload prompt: Skill 'ui-reload-api-e2e' was updated. Reload to use it. Reload now.
  • Verified dismissing the prompt and writing an ignored temp/swap file .SKILL.md.swp did not create another reload prompt.
  • Screenshot: /var/folders/d9/xqhkvsp94rg0n0n523snqztm0000gn/T/browser-screenshot-1779125218715.png
  • Verification: pnpm --filter @openwork/app typecheck passed.

Pushed commit: b05639a44 fix(app): align reload notification UI.

@benjaminshafii
Copy link
Copy Markdown
Member Author

History-collapse verification update:

  • Reproduced the issue in the same session on the first same-session history test turn. During the active run, the rendered transcript dropped to only the new user/assistant turn even though the persisted snapshot still had the full history.
  • Root cause: deriveRenderedSessionMessages preferred any non-empty live transcript cache over the snapshot. During prompt submission that live cache can briefly contain only the new turn.
  • Fix: render the snapshot as the history floor and merge live stream updates on top with mergeSnapshotAndLiveMessages(..., { appendLiveOnlyMessages: true }).
  • Also removed the remaining activity divider line from the old Working for Ns header.
  • Verified after the fix: same session had messageCount: 60, newest marker HISTORY_FIX_MANUAL_DONE, and forcing the transcript scroll container to top showed the original tell me a lng story history while/after the new turn.
  • Verification command: pnpm --filter @openwork/app typecheck passed.

Pushed commit: feca84412 fix(app): preserve session history while streaming.

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