feat: route new chats through recoup-api /api/chat/workflow

Merged
SW
5mo ago
+471-10

Summary

  • New chats now provision a recoup-api session + sandbox before the first message, then stream through recoup-api's /api/chat/workflow.
  • Existing chats opened from history keep using the legacy /api/chat until session_id is backfilled onto their rows.
  • api owns cloneUrl construction. The client passes organizationId (or omits it for a personal session); the api derives the canonical workspace repo via ensurePersonalRepo (recoupable/api#618, #620) and returns cloneUrl on the session row, which the client then hands to createSandbox.

What changed

LayerChange
lib/sandboxes/{createSandbox,SandboxCreateRequestError}.tscreateSandbox() function added next to the existing Sandbox type (4 callers unchanged); uses getClientApiBaseUrl() to respect the API override
lib/sessions/createSession.tsNew folder, POST /api/sessions. Body shrunk to { title?, organizationId? }; response type exposes session.cloneUrl
hooks/useChatTransport.tsBranches on sessionId — present → /api/chat/workflow with transport-injected body; absent → legacy /api/chat
hooks/useVercelChat.ts, providers/VercelChatProvider.tsx, components/VercelChat/chat.tsxThreaded optional sessionId through
components/VercelChat/NewChatBootstrap.tsxClient wrapper: reads selectedOrgId from OrganizationProvider, calls createSession({ organizationId }), reads session.cloneUrl off the response, then createSandbox(cloneUrl, sessionId) → renders <Chat sessionId>. startedRef guards against React StrictMode double-mount
app/chat/page.tsxRenders NewChatBootstrap instead of Chat
app/chat/[roomId]/page.tsxUntouched → existing chats stay on legacy /api/chat

Test plan

  • On the preview deployment, open /chat → confirm the bootstrap loader briefly shows, then the chat surface mounts
  • Send a first message → confirm POST recoup-api.vercel.app/api/chat/workflow in DevTools Network (200, streaming response)
  • Confirm POST /api/sessions precedes it, body is {} for personal or { organizationId: "<uuid>" } for org; response includes session.cloneUrl
  • Confirm POST /api/sandbox is called with repoUrl == the api-returned cloneUrl (canonical recoupable/<accountId> or recoupable/<orgId> shape)
  • Refresh the resulting /chat/[roomId] page → confirm it falls back to legacy POST /api/chat (sessionId is lost on reload — expected; backfill not yet run)
  • Open an old chat from history → confirm it still works (legacy path)
  • React StrictMode dev-mode: confirm only one session + one sandbox are created per new-chat mount

Out of scope

  • Backfill session_id onto existing chat rows + provision sandboxes on first open of a legacy chat
  • Delete the legacy /api/chat route on the chat side once all chats use the workflow path
  • MCP tools, Composio tools, artist context injection, title generation, email tool — intentionally not ported

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • New session-based chat initialization that automatically provisions sandboxes and chat environments
    • Session-aware chat request routing with fallback to legacy infrastructure
    • Improved sandbox creation error handling with actionable error messages and reconnection guidance

Review Change Stack