refactor(sessions): migrate /api/sessions/[sessionId]/chats/[chatId] to recoupable API#37
refactor(sessions): migrate /api/sessions/[sessionId]/chats/[chatId] to recoupable API#37arpitgupta1214 wants to merge 7 commits into
Conversation
…Id] to recoupable API The chat-snapshot read now lives at recoupable api (api#562, docs#209). Drops the local GET handler on the route and rewires the only caller (session-chat-content's `refreshCurrentChatSnapshot`) to a new `getRecoupSessionChat` helper that authenticates with a Privy bearer token. PATCH/DELETE on the same path stay in place. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThis PR removes the legacy session chat API route handlers ( ChangesSession chat API migration to recoupable
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
No issues found across 4 files
You're on the cubic free plan with 19 free PR reviews remaining this month. Upgrade for unlimited reviews.
…nId]/chats/[chatId] to recoupable API
The full chat-by-id route now lives at recoupable api (api#562, docs#209):
GET, PATCH, and DELETE.
- Deletes the local route + tests entirely (was the last remaining
caller of in-process `requireOwnedSessionChat` for this path)
- New helpers `lib/recoupable/{patch,delete}-recoup-session-chat.ts`
(Privy Bearer; surface server `error` text on non-2xx)
- Rewires `useSessionChats.{renameChat,deleteChat}` and
`SessionChatProvider.updateChatModel` through the new helpers
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ion-chat-by-id # Conflicts: # apps/web/hooks/use-session-chats.ts
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/lib/recoupable/delete-recoup-session-chat.ts`:
- Around line 26-30: Replace the bare type assertion for the error payload in
deleteRecoupSessionChat with a Zod-validated parse: import z from "zod", define
an errorSchema (e.g., z.object({ error: z.string().optional() })), call
errorSchema.safeParse(await res.json().catch(() => ({}))) to get a parsed
result, extract the error string only when parsed.success is true (e.g.,
errorMessage = parsed.success ? parsed.data.error : undefined), and use that
errorMessage in the thrown Error instead of the current payload.error.
In `@apps/web/lib/recoupable/get-recoup-session-chat.ts`:
- Around line 36-38: The GET error branch in getRecoupSessionChat currently
throws only status and statusText, losing the server-provided JSON error; update
the error handling for the response object (res) so that when !res.ok you parse
the response body (await res.json() or res.text() as appropriate) and include
the parsed error message/details in the thrown Error (e.g., include the parsed
error payload alongside res.status and res.statusText) so callers/UI can surface
the server error payload.
- Line 40: The return currently does an unsafe cast in getRecoupSessionChat
(return res.json() as Promise<GetRecoupSessionChatResponse>) — replace it by
defining a Zod schema (e.g., RecoupSessionChatSchema) that matches the expected
response, use await res.json() then validate with
RecoupSessionChatSchema.safeParse(parsed) and if valid return the parsed data
typed via z.infer<typeof RecoupSessionChatSchema>, otherwise throw or handle an
error (keep the same error-handling pattern used in fetch-or-create-account.ts /
fetch-account-subscription.ts). Ensure the new schema and inferred type replace
GetRecoupSessionChatResponse usage for type safety.
In `@apps/web/lib/recoupable/patch-recoup-session-chat.ts`:
- Around line 40-47: The current code casts res.json() responses unchecked in
patchRecoupSessionChat (the error payload cast to {error?: string} and the final
return cast to PatchRecoupSessionChatResponse); replace those casts with Zod
validation: define a zod schema for the error shape and another for the
success/PatchRecoupSessionChatResponse shape, derive types with z.infer, parse
the JSON from res.json() with schema.safeParse or schema.parse, and when parsing
fails throw a descriptive Error including the validation errors (or the original
HTTP status) so only validated data is returned from patchRecoupSessionChat.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b943ae8f-32e5-44b8-a954-e7e3220469b8
📒 Files selected for processing (8)
apps/web/app/api/sessions/[sessionId]/chats/[chatId]/route.test.tsapps/web/app/api/sessions/[sessionId]/chats/[chatId]/route.tsapps/web/app/sessions/[sessionId]/chats/[chatId]/session-chat-content.tsxapps/web/app/sessions/[sessionId]/chats/[chatId]/session-chat-context.tsxapps/web/hooks/use-session-chats.tsapps/web/lib/recoupable/delete-recoup-session-chat.tsapps/web/lib/recoupable/get-recoup-session-chat.tsapps/web/lib/recoupable/patch-recoup-session-chat.ts
💤 Files with no reviewable changes (2)
- apps/web/app/api/sessions/[sessionId]/chats/[chatId]/route.ts
- apps/web/app/api/sessions/[sessionId]/chats/[chatId]/route.test.ts
| const payload = (await res.json().catch(() => ({}))) as { error?: string }; | ||
| throw new Error( | ||
| payload.error ?? | ||
| `deleteRecoupSessionChat: ${res.status} ${res.statusText}`, | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the target file to understand the full context
head -50 apps/web/lib/recoupable/delete-recoup-session-chat.tsRepository: recoupable/open-agents
Length of output: 1059
🏁 Script executed:
# Check if Zod is used in this project and in similar contexts
rg -i "zod" --type ts --type tsx -l | head -20Repository: recoupable/open-agents
Length of output: 94
🏁 Script executed:
# Search for error handling patterns with JSON responses in the codebase
rg "res\.json\(\)" --type ts --type tsx -A 3 -B 1 | head -60Repository: recoupable/open-agents
Length of output: 94
🏁 Script executed:
# Look for z.object or z.infer patterns to see how validation is done
rg "z\.object|z\.infer" --type ts --type tsx -B 2 -A 2 | head -80Repository: recoupable/open-agents
Length of output: 94
🏁 Script executed:
# Try searching for Zod without file type specification
rg "from.*zod|import.*zod" -i | head -20Repository: recoupable/open-agents
Length of output: 1245
🏁 Script executed:
# Look for error handling patterns more broadly
rg "res\.json\(\)" -A 3 -B 1 | head -100Repository: recoupable/open-agents
Length of output: 6121
🏁 Script executed:
# Check if this codebase uses Zod at all
rg "z\." --type ts | head -30Repository: recoupable/open-agents
Length of output: 2391
🏁 Script executed:
# Look at similar API boundary calls in the codebase
find apps/web/lib/recoupable -name "*.ts" -type f | xargs -I {} bash -c 'echo "=== {} ===" && head -5 {}'Repository: recoupable/open-agents
Length of output: 6173
🏁 Script executed:
cat -n apps/web/lib/recoupable/fetch-account-credits.tsRepository: recoupable/open-agents
Length of output: 1228
🏁 Script executed:
cat -n apps/web/lib/recoupable/fetch-account-subscription.tsRepository: recoupable/open-agents
Length of output: 989
Use a Zod schema to validate the error response payload, consistent with other Recoupable API calls.
The error payload should be validated with a Zod schema and .safeParse() rather than a bare type assertion. Other similar calls in this directory (fetch-account-credits.ts, fetch-account-subscription.ts) follow this pattern. Define a simple schema like:
Example pattern
const errorSchema = z.object({ error: z.string().optional() });
const parsed = errorSchema.safeParse(await res.json().catch(() => ({})));
const errorMessage = parsed.success ? parsed.data.error : undefined;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/lib/recoupable/delete-recoup-session-chat.ts` around lines 26 - 30,
Replace the bare type assertion for the error payload in deleteRecoupSessionChat
with a Zod-validated parse: import z from "zod", define an errorSchema (e.g.,
z.object({ error: z.string().optional() })), call errorSchema.safeParse(await
res.json().catch(() => ({}))) to get a parsed result, extract the error string
only when parsed.success is true (e.g., errorMessage = parsed.success ?
parsed.data.error : undefined), and use that errorMessage in the thrown Error
instead of the current payload.error.
| if (!res.ok) { | ||
| throw new Error(`getRecoupSessionChat: ${res.status} ${res.statusText}`); | ||
| } |
There was a problem hiding this comment.
Surface server error text on non-2xx GET responses.
This currently drops the JSON error payload and only throws status text, which makes diagnostics/UI messaging less actionable than PATCH/DELETE.
💡 Suggested fix
if (!res.ok) {
- throw new Error(`getRecoupSessionChat: ${res.status} ${res.statusText}`);
+ const payload = (await res.json().catch(() => ({}))) as { error?: string };
+ throw new Error(
+ payload.error ??
+ `getRecoupSessionChat: ${res.status} ${res.statusText}`,
+ );
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!res.ok) { | |
| throw new Error(`getRecoupSessionChat: ${res.status} ${res.statusText}`); | |
| } | |
| if (!res.ok) { | |
| const payload = (await res.json().catch(() => ({}))) as { error?: string }; | |
| throw new Error( | |
| payload.error ?? | |
| `getRecoupSessionChat: ${res.status} ${res.statusText}`, | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/lib/recoupable/get-recoup-session-chat.ts` around lines 36 - 38, The
GET error branch in getRecoupSessionChat currently throws only status and
statusText, losing the server-provided JSON error; update the error handling for
the response object (res) so that when !res.ok you parse the response body
(await res.json() or res.text() as appropriate) and include the parsed error
message/details in the thrown Error (e.g., include the parsed error payload
alongside res.status and res.statusText) so callers/UI can surface the server
error payload.
| throw new Error(`getRecoupSessionChat: ${res.status} ${res.statusText}`); | ||
| } | ||
|
|
||
| return res.json() as Promise<GetRecoupSessionChatResponse>; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check if the file exists and examine it
find . -name "get-recoup-session-chat.ts" -type fRepository: recoupable/open-agents
Length of output: 119
🏁 Script executed:
# If found, read the file to see the context around line 40
cat -n apps/web/lib/recoupable/get-recoup-session-chat.tsRepository: recoupable/open-agents
Length of output: 1518
🏁 Script executed:
# Search for GetRecoupSessionChatResponse type definition
rg "GetRecoupSessionChatResponse" -A 5 -B 2Repository: recoupable/open-agents
Length of output: 1605
🏁 Script executed:
# Look for Zod schemas in the same directory
fd -e ts -e tsx apps/web/lib/recoupable/ | head -20Repository: recoupable/open-agents
Length of output: 468
🏁 Script executed:
# Check if there are any existing Zod schemas in the recoupable directory
rg "z\\..*\\(" apps/web/lib/recoupable/ | head -20Repository: recoupable/open-agents
Length of output: 1763
🏁 Script executed:
# Check how Zod schemas are implemented in a similar file
head -50 apps/web/lib/recoupable/fetch-or-create-account.tsRepository: recoupable/open-agents
Length of output: 1481
🏁 Script executed:
# See the complete pattern for how Zod is used
head -70 apps/web/lib/recoupable/fetch-or-create-account.ts | tail -25Repository: recoupable/open-agents
Length of output: 775
Replace unchecked response casting with Zod schema validation.
Line 40 uses unsafe type assertion (res.json() as Promise<GetRecoupSessionChatResponse>) which skips runtime validation. Per coding guidelines and the pattern throughout this directory (see fetch-or-create-account.ts, fetch-account-subscription.ts, etc.), create a Zod schema, validate with safeParse(), and derive the TypeScript type with z.infer<typeof schema>.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/lib/recoupable/get-recoup-session-chat.ts` at line 40, The return
currently does an unsafe cast in getRecoupSessionChat (return res.json() as
Promise<GetRecoupSessionChatResponse>) — replace it by defining a Zod schema
(e.g., RecoupSessionChatSchema) that matches the expected response, use await
res.json() then validate with RecoupSessionChatSchema.safeParse(parsed) and if
valid return the parsed data typed via z.infer<typeof RecoupSessionChatSchema>,
otherwise throw or handle an error (keep the same error-handling pattern used in
fetch-or-create-account.ts / fetch-account-subscription.ts). Ensure the new
schema and inferred type replace GetRecoupSessionChatResponse usage for type
safety.
| const payload = (await res.json().catch(() => ({}))) as { error?: string }; | ||
| throw new Error( | ||
| payload.error ?? | ||
| `patchRecoupSessionChat: ${res.status} ${res.statusText}`, | ||
| ); | ||
| } | ||
|
|
||
| return res.json() as Promise<PatchRecoupSessionChatResponse>; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
cat -n apps/web/lib/recoupable/patch-recoup-session-chat.tsRepository: recoupable/open-agents
Length of output: 1714
Replace unchecked type assertions with Zod schema validation at the API boundary.
Both error and success responses from res.json() are cast without validation. At runtime, malformed payloads can propagate silently into chat state. Define Zod schemas for both error and success responses, parse payloads with these schemas, and derive types using z.infer instead of casting.
Lines 40, 47 — unchecked assertions
const payload = (await res.json().catch(() => ({}))) as { error?: string };
throw new Error(
payload.error ??
`patchRecoupSessionChat: ${res.status} ${res.statusText}`,
);
}
return res.json() as Promise<PatchRecoupSessionChatResponse>;
Per coding guidelines: "Use Zod schemas for validation and derive types with z.infer".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/lib/recoupable/patch-recoup-session-chat.ts` around lines 40 - 47,
The current code casts res.json() responses unchecked in patchRecoupSessionChat
(the error payload cast to {error?: string} and the final return cast to
PatchRecoupSessionChatResponse); replace those casts with Zod validation: define
a zod schema for the error shape and another for the
success/PatchRecoupSessionChatResponse shape, derive types with z.infer, parse
the JSON from res.json() with schema.safeParse or schema.parse, and when parsing
fails throw a descriptive Error including the validation errors (or the original
HTTP status) so only validated data is returned from patchRecoupSessionChat.
Drops the `import type { Chat } from "@/lib/db/schema"` from the new
patch helper. Defines `RecoupChat` inline matching what the recoupable
API actually returns (camelCase keys, ISO-string timestamps), so the
helper is self-contained and doesn't depend on the local Drizzle
declaration.
Drizzle's inferred `Chat` claims `Date` for timestamps but the wire
data is strings — so this also catches a long-standing latent type
lie at the boundary. Casts at the two consumer call sites keep the
diff surgical until the rest of the chat state is typed against the
wire format.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hat export The patch helper exported `RecoupChat` but nothing imported it — and a per-action helper isn't the natural place to own the wire-format type anyway. Inline the shape in `PatchRecoupSessionChatResponse` where it's actually used. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…format casts
Reverts the wire-format experiment in favor of the existing
convention from the list/create helpers — `import type { Chat } from
"@/lib/db/schema"`. Removes the `as unknown as` casts at the two
consumer call sites since the types now line up natively.
The Drizzle Chat type claims `Date` for timestamps while runtime
values are ISO strings (Next.js serializes at the server/client
boundary), but nothing calls Date methods on these fields so the lie
is harmless. Fixing it is a separate cleanup, not in scope here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The chat-by-id page no longer reaches into the local Drizzle schema for the chat row. Instead it calls getRecoupSessionChat with the Privy access token from the session cookie — same auth pattern already used by resolveAccountIdFromPrivyToken on the server side. This drops the cast in setChatInfo: chatInfo state and the SessionChatProvider chat prop are now typed as RecoupSessionChat end-to-end (matches the api wire format that toChatResponse returns). dbMessages still come from Drizzle for now (used for assistant message duration calcs); that's a follow-up when message rows are also migrated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/web/app/sessions/[sessionId]/chats/[chatId]/page.tsx">
<violation number="1" location="apps/web/app/sessions/[sessionId]/chats/[chatId]/page.tsx:67">
P1: Do not swallow all `getRecoupSessionChat` errors here; for non-retry paths this turns real API/auth failures into false 404s.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
You're on the cubic free plan with 19 free PR reviews remaining this month. Upgrade for unlimited reviews.
Re-trigger cubic
| } catch { | ||
| // Likely 404 while the optimistic chat hasn't persisted yet. | ||
| } |
There was a problem hiding this comment.
P1: Do not swallow all getRecoupSessionChat errors here; for non-retry paths this turns real API/auth failures into false 404s.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/sessions/[sessionId]/chats/[chatId]/page.tsx, line 67:
<comment>Do not swallow all `getRecoupSessionChat` errors here; for non-retry paths this turns real API/auth failures into false 404s.</comment>
<file context>
@@ -51,14 +55,17 @@ const OPTIMISTIC_CHAT_RETRY_ATTEMPTS = 50;
+ try {
+ const data = await getRecoupSessionChat(sessionId, chatId, accessToken);
+ return data.chat;
+ } catch {
+ // Likely 404 while the optimistic chat hasn't persisted yet.
}
</file context>
| } catch { | |
| // Likely 404 while the optimistic chat hasn't persisted yet. | |
| } | |
| } catch (error) { | |
| if (maxAttempts === 1) { | |
| throw error; | |
| } | |
| // Likely 404 while the optimistic chat hasn't persisted yet. | |
| } |
Tip: Review your code locally with the cubic CLI to iterate faster.
Summary
The full chat-by-id route now lives at recoupable API (api#562, docs#209).
lib/recoupable/:get-recoup-session-chat,patch-recoup-session-chat,delete-recoup-session-chat(all Privy Bearer;cache: "no-store"on GET; surface servererrortext on non-2xx)session-chat-content.refreshCurrentChatSnapshot→ GET helperuseSessionChats.renameChat→ PATCH helperuseSessionChats.deleteChat→ DELETE helperSessionChatProvider.updateChatModel→ PATCH helperTest plan
🤖 Generated with Claude Code
Summary by CodeRabbit
Refactor
Tests