feat(api): split chat-workflow resume into GET /api/chat/[chatId]/stream

Closed
AR
3d ago
+618-128

Summary

POST /api/chat/workflow is now start-only — a genuinely live run returns 409 instead of silently resuming and dropping the incoming message; resume moves to a dedicated GET /api/chat/[chatId]/stream (live UIMessage stream, or 204 when there's nothing to resume). A terminal stale active_stream_id still self-heals so the next POST starts fresh.

Test plan

  • POST while a run is live → 409, no new run started
  • POST with a terminal/stale active_stream_id → starts a fresh run
  • GET /api/chat/[chatId]/stream with a live run → 200 stream + x-workflow-run-id
  • GET with no active or terminated run → 204

Summary by cubic

Split chat-workflow resume into GET /api/chat/[chatId]/stream and make POST /api/chat/workflow start-only. The resume stream is now wrapped for durability: it avoids orphaned readers, does not cancel runs on disconnect, and treats aborts/late 404s as clean closes; DB errors now surface as 500s.

  • New Features

    • GET /api/chat/[chatId]/stream to resume/reconnect.
      • 200 SSE with live UIMessage stream + x-workflow-run-id; 204 when nothing to resume.
      • Auth via x-api-key or bearer; chat ownership enforced; UUID chatId validation; CORS preflight.
      • Stream is wrapped with wrapWorkflowStreamWatcher to synthesize tool terminators, release inner reader on client cancel, avoid cancelling durable runs, and treat AbortError/late 404 as a clean close.
    • POST /api/chat/workflow never resumes.
      • 409 when a run is live (reconnect via GET); 503 when state is indeterminate (retry POST); terminal stale active_stream_id self-heals for a fresh start.
      • Start gating extracted to gateChatStreamStart.
    • DB handling tightened: selectChats returns null on DB error and callers surface 500; hibernation defers on DB errors; chat creation surfaces 500.
  • Migration

    • Do not use POST /api/chat/workflow to resume; reconnect via GET /api/chat/{chatId}/stream.
    • On POST, handle 409 by reconnecting via GET; handle 503 by retrying the POST shortly.
    • Treat GET 204 as “no active stream”; on 200, consume SSE and read x-workflow-run-id for telemetry.
    • Client disconnects no longer cancel runs; use the stop endpoint to cancel explicitly.

Summary by CodeRabbit

  • New Features

    • Resume interrupted chat streams and reconnect to ongoing conversations via a dedicated resume endpoint.
  • Bug Fixes

    • Clearer reconnect/retry signals when streams conflict or are indeterminate.
    • More robust error handling for chat/session loading, permission checks, and DB failure cases to avoid silent failures.
    • Cleaner stream shutdown behavior on client disconnects (avoids mid-stream errors).
  • Refactor

    • Separation of resume (resume-only endpoint) from new-stream start flow for more predictable behavior.