feat(chat): cut workflow POST over to recoup-api /api/chat/workflow

Merged
SW
5mo ago
+259-84

Closes #42.

Summary

All five backend cutover bundles landed on recoup-api (cross-validation evidence in monorepo PROGRESS.md). Wire format is byte-compatible with what this client emits and consumes today, so the flip is one URL constant + one new headers callback.

Changes

FileWhat
apps/web/lib/chat/chat-api-client.tsCHAT_API_POST_PATH repointed from relative /api/chat → ${RECOUPABLE_API_BASE_URL}/api/chat/workflow. Reuses the existing prod/test split helper.
apps/web/app/.../hooks/use-session-chat-runtime.tsAdded headers callback on AbortableChatTransport that attaches the Privy JWT as Authorization: Bearer ... so recoup-api's validateAuthContext accepts the cross-origin request. The same JWT continues to ride in the request body (recoupAccessToken field) — recoup-api uses that to populate the sandbox env's RECOUP_ACCESS_TOKEN (Bundle A.4 plumbing).

Wire-format compatibility verified end-to-end

  • ✅ message-metadata chunks (api PR #592)
  • ✅ task-tool async-generator yield sequence (api PR #594)
  • ✅ Environment section + cwd in system prompt (api PR #597)
  • ✅ Anthropic prompt cache control (api PR #599) — Bundle A.6 dropped Turn 2 cost ~72%
  • ✅ Privy JWT → sandbox RECOUP_ACCESS_TOKEN env (api PR #601) — recoup-api skill curl examples now authenticate

Known partial degradation (NOT regressions blocking this PR, per direction)

Why
Stop button can't cancel a recoup-api workflow runPOST /api/chat/{chatId}/stop still routes to open-agents' own backend, but the run-id lives on recoup-api's Vercel Workflow runtime — open-agents' getRun(runId).cancel() can't reach it. Workflow keeps spending tokens until natural completion.
Refresh-mid-stream loses the in-flight messageGET /api/chat/{chatId}/stream still routes to open-agents — has no record of recoup-api's runs, returns null.

Both unblock once the matching sub-routes are ported to recoup-api (separate effort).

Test plan

  • Typecheck: turbo typecheck clean across all workspaces
  • Lint: bun run check passes
  • Smoke test on preview: send a fresh message → expect 200 stream from recoup-api.vercel.app (not sandbox.recoupable.com/api/chat), text-deltas render, message-metadata cost badge appears
  • Follow-up turn: verify Anthropic cache reuse (turn 2 cost ~10× cheaper than turn 1)
  • recoup-api skill: invoke /recoup-api → expect HTTP 200 + account_id (not "RECOUP_ACCESS_TOKEN is not set")
  • Tool calls: bash, task subagent, ask_user_question — all render in UI
  • Known-broken paths (acknowledged): stop button + page-refresh-mid-stream

Rollback

Single-line revert of CHAT_API_POST_PATH in chat-api-client.ts.

🤖 Generated with Claude Code


Summary by cubic

Switched chat workflow POSTs, sandbox provisioning, and reconnect/status polling to recoup-api with Privy JWT auth, and stopped clobbering sandbox_state on cross‑project misses. Wire format is unchanged; stop and mid‑stream resume still hit the old backend and are temporarily degraded.

  • Refactors

    • Repointed CHAT_API_POST_PATH to ${RECOUPABLE_API_BASE_URL}/api/chat/workflow; AbortableChatTransport adds Authorization: Bearer <Privy JWT> and keeps recoupAccessToken in the body.
    • Moved provisioning to ${RECOUPABLE_API_BASE_URL}/api/sandbox; createSandbox() now sends { repoUrl, sessionId }, accepts an optional token, and useSandboxCreate() fetches a fresh Privy token per call.
    • Repointed sandbox reconnect and status polling to ${RECOUPABLE_API_BASE_URL}/api/sandbox/* with Authorization: Bearer.
    • Removed resolveSandboxCredentials() and its usages; dropped unused branch/isNewBranch/sandboxType params from createSandbox() and consumers. Added tests for sandbox creation request shape and auth header.
  • Bug Fixes

    • Stopped writing hibernated/cleared sandbox_state on local diff, files, files/content, and skills routes when a sandbox appears unavailable; recoup-api remains the source of truth (routes still return 409).

Summary by CodeRabbit

  • New Features

    • Cross-origin sandbox and chat calls now include optional Bearer token authorization and route to the external recoup-api endpoints.
  • Bug Fixes

    • Improved sandbox credential resolution and ensured credentials are applied when present.
    • Chat transport now sends authorization header for outbound requests when a token is available.
  • Tests

    • Added tests covering credential resolution and sandbox creation request behavior.

Review Change Stack