fix(chat-workflow): explicitly close the SSE writable so chat.status unsticks

Merged
SW
5mo ago
+107-8

Summary

Follow-up to PR #604 — closes the second half of the same bug. The DB clear from #604 was working correctly, but users were still seeing the stop button stuck for ~2 minutes after the stream ended.

Root cause: runAgentStep calls writer.releaseLock() which doesn't close the underlying writable. The writable stays open until Vercel Workflow's runtime garbage-collects the run (observed ~2 min), so the SSE response holds its HTTP connection open and the AI SDK chat hook stays in submitted waiting on a stream-end signal that never arrives promptly.

open-agents avoids this by explicitly calling writable.close() in a "use step" inside the workflow body — same pattern this PR mirrors.

Changes

  • New app/lib/workflows/closeChatStream.ts "use step" that calls writable.close() and defensively swallows errors.
  • runAgentWorkflow's finally block now runs both cleanups in parallel via Promise.all([clearChatActiveStream, closeChatStream]) — same shape as open-agents'.
  • 3 new closeChatStream tests + 2 new runAgentWorkflow tests (success and throw paths both close the writable).

Test plan

  • pnpm test — 3154 → 3159 passing (5 new)
  • pnpm lint:check — clean for touched files
  • Manual: send message in open-agents UI, confirm send button reverts within ~1s of stream ending (was 2m+)

Linked

  • Source of bug: #604 review tests showed DB clears correctly but UI stays stuck — this PR closes the UI-side half
  • Tracking issue: #605

🤖 Generated with Claude Code


Summary by cubic

Explicitly closes the chat SSE writable at the end of runAgentWorkflow so the stream ends immediately and the UI unsticks (stop button/chat.status leaves submitted). Fixes the ~2‑minute hang seen after #604.

  • Bug Fixes
    • Added closeChatStream "use step" to call writable.close() and swallow errors.
    • Updated runAgentWorkflow to run closeChatStream and clearChatActiveStream in parallel via Promise.all.
    • Added tests for closeChatStream and for closing on both success and error paths in runAgentWorkflow; tests live under __tests__/.