feat(chat-workflow): forward Privy JWT as RECOUP_ACCESS_TOKEN (cutover Bundle A.4)

Merged
SW
5mo ago
+124-15

Summary

Fifth and final open-agents → api cutover bundle. The chat UI sends a short-lived Privy JWT in the workflow request body as recoupAccessToken. Today api silently strips it via Zod's default .strip() mode and never plumbs it into the sandbox env, so the recoup-api skill's curl examples can't authenticate.

Production reproducer

Both backends, same prompt: "Use the recoup-api skill to call GET /api/accounts/id and tell me the account_id from the response."

Today (before this fix)
api prodrecoup-api skill loads ✓. Curl runs with empty $RECOUP_ACCESS_TOKEN → 401. Agent reports "RECOUP_ACCESS_TOKEN is not set — you'll need to sign in."
open-agents prodSkill loads ✓. Curl returns HTTP 200 with the user's account_id (fb678396-…).

Plumbing (TDD red → green at each layer)

FileChange
lib/chat/validateChatWorkflow.tsAccept recoupAccessToken: z.string().min(1).max(8192).optional() in the body schema (open-agents-shape)
lib/agent/tools/AgentContext.tsAdd recoupAccessToken?: string field — mirrors open-agents/packages/agent/types.ts:29
lib/chat/handleChatWorkflowStream.tsConditionally spread recoupAccessToken into agentContext when validator surfaced it
lib/agent/tools/buildRecoupExecEnv.tsInject RECOUP_ACCESS_TOKEN into the sandbox exec env when set on context

Security

Only forward when the caller sent the field in the body (chat UI path). x-api-key callers don't set it, so their long-lived recoup_sk_… key is never exfiltratable from the sandbox env. Existing risk comment preserved.

Cutover roadmap

#BundleStatus
1C per-message cost/usage✅ merged
2B subagent live progress + transcript✅ merged
3A.7 real cwd in system prompt✅ merged
4A.6 Anthropic prompt cache control✅ merged
5A.4 forward Privy JWT as recoupAccessToken✅ this PR (last one)

After this lands, sandbox.recoupable.com (open-agents UI) can switch from sandbox.recoupable.com/api/chat → recoup-api.vercel.app/api/chat/workflow without UI changes.

Test plan

  • 5 new unit tests (3 buildRecoupExecEnv + 1 validator + 1 handler); 1 handler omit-when-undefined regression
  • Full suite 3144/3144 pass; lint clean
  • E2E on preview: re-run the recoup-api skill prompt with recoupAccessToken in body → expect HTTP 200 + account_id from /api/accounts/id

🤖 Generated with Claude Code


Summary by cubic

Forwards the user's Privy JWT from chat workflow requests into the sandbox as RECOUP_ACCESS_TOKEN so the recoup-api skill can authenticate. Fixes 401s on GET /api/accounts/id and completes cutover Bundle A.4.

  • New Features
    • Accepts optional recoupAccessToken in the body and adds recoupAccessToken? to AgentContext.
    • handleChatWorkflowStream forwards it when present; buildRecoupExecEnv injects RECOUP_ACCESS_TOKEN for skills like recoup-api.
    • Only forwarded for bearer-auth UI requests; never for x-api-key calls (long-lived recoup_sk_* keys are not exposed).

Summary by CodeRabbit

  • New Features
    • Added support for short-lived token authentication in API integrations. The system now uses temporary bearer tokens for API calls instead of permanent long-lived credentials. This reduces security risk and improves protection against unauthorized access. Updated validation and context handling throughout the application stack to fully support the new authentication method.

Review Change Stack