feat(chat-workflow): POST /api/chat/workflow route stub (PR 2 of 5)

Merged
SW
6mo ago
+463-0

Summary

Implements the route stub for the new POST /api/chat/workflow endpoint documented in recoupable/docs#221. Validates the full request contract, performs all auth + ownership + sandbox checks, and returns a hardcoded UIMessage stream with an x-workflow-run-id: stub-<uuid> header — so the chat-side team can integrate against the real response shape today while the workflow itself is being ported from open-agents in PRs 3-5.

Files

  • app/api/chat/workflow/route.ts — thin POST shim + OPTIONS for CORS (matches existing /api/chat/route.ts convention)
  • lib/chat/handleChatWorkflowStream.ts — auth → validate body → session ownership → sandbox check → chat ownership → stub UIMessage stream
  • lib/chat/validateChatWorkflowBody.ts — Zod schema matching the OpenAPI ChatWorkflowRequest (messages, chatId, sessionId, optional context.contextLimit)
  • lib/chat/__tests__/handleChatWorkflowStream.test.ts — 12 tests
  • lib/chat/__tests__/validateChatWorkflowBody.test.ts — 11 tests

Status codes (match contract docs)

StatusTrigger
200UIMessage stream + x-workflow-run-id: stub-<uuid> header
400invalid JSON / invalid body / "Sandbox not initialized"
401validateAuthContext passthrough (no/invalid auth)
403session not owned by the API key's account
404session not found / chat not found / chat exists but under a different session
500selectSessions returned null (DB error)

409 deferred to the wire-up PR that adds compareAndSetChatActiveStreamId — no workflow to dedupe yet.

Test plan

  • TDD red → green: tests written first, watched fail, then implemented
  • 23/23 new tests pass (pnpm test lib/chat/__tests__/{validateChatWorkflowBody,handleChatWorkflowStream}.test.ts)
  • Full suite: 2901/2901 pass (pnpm test --run)
  • Lint clean (pnpm lint)
  • Smoke test against a real Vercel preview: send a valid body with an existing chat/session/sandbox, confirm stream emits "Hello from /api/chat/workflow" and x-workflow-run-id header is present

Position in the PR series

This is PR 2 of 5 for porting open-agents' /api/chat into api as /api/chat/workflow:

  1. ✅ recoupable/docs#221 — contract docs (merged)
  2. ✅ This PR — route stub
  3. ⏳ Port 10 sandbox tool files + buildAgentTools factory (no wiring)
  4. ⏳ Port route-level helpers (createChatRuntime, agentCustomInstructions, extractOrgId, compareAndSetChatActiveStreamId, touchChat, reconcileExistingActiveStream, persistLatestUserMessage)
  5. ⏳ Wire it all together — runAgentWorkflow + runAgentStep + replace stub with real start()

🤖 Generated with Claude Code


Summary by cubic

Adds a stub POST /api/chat/workflow endpoint that validates input, enforces auth and ownership, checks sandbox state, and streams a hardcoded UIMessage with an x-workflow-run-id header. Refactors validation into a dedicated validateChatWorkflow and reuses shared error helpers so clients can integrate with the final response shape now (per recoupable/docs#221).

  • New Features

    • New route with CORS-friendly OPTIONS handler.
    • Request validation via validateChatWorkflow: messages, chatId, sessionId, optional context.contextLimit; augments with accountId/orgId/authToken.
    • Auth and data checks: API key auth, session ownership, chat belongs to session, sandbox active.
    • Returns text/event-stream UIMessage stub with x-workflow-run-id: stub-<uuid>; implements 200/400/401/403/404/500 per contract.
  • Refactors

    • Moved auth + body parsing into validateChatWorkflow; handler now delegates with a single call.
    • Switched to shared errorResponse and validationErrorResponse utilities.
    • Updated tests to match the new seams.
    • Removed unrelated local artifacts that were accidentally included to keep the diff scoped.