Skip to content

Conversation

@sweetmantech
Copy link
Contributor

@sweetmantech sweetmantech commented Jan 19, 2026

Summary

  • Match /api/emails/inbound behavior by automatically creating a room when roomId is not provided in chat requests
  • Ensures conversations are properly tracked even when clients don't specify a roomId upfront
  • Handles both UIMessage format (parts) and simple message format (content)

Test plan

  • Added 6 unit tests for auto room creation behavior
  • All 499 recoup-api tests pass (pnpm test)
  • Manual test: Send chat request without roomId, verify room is created
  • Verify generated roomId is returned in response

🤖 Generated with Claude Code


Note

Introduces consistent conversation setup and message persistence across chat and email.

  • Auto-creates roomId and persists the latest user message in validateChatRequest via setupConversation; returns the final roomId to clients
  • Adds setupConversation utility to create rooms (when needed) and insert user memories (with optional memoryId for deduplication)
  • Adds saveChatCompletion utility and uses it in handleChatGenerate and email reply flow to store assistant messages; handleChatGenerate now includes roomId in the response and tolerates persistence failures
  • Normalizes mixed message inputs using new convertToUiMessages, isUiMessage, and getTextContent helpers
  • Extensive unit/integration tests added/updated for auto room creation, message persistence, and message normalization

Written by Cursor Bugbot for commit d6b9f61. This will update automatically on new commits. Configure here.

sidneyswift and others added 4 commits January 16, 2026 22:26
Add assistant message persistence to handleChatGenerate, matching the pattern
from /api/emails/inbound. When roomId is provided, the assistant message is
saved to the database using insertMemories with filtered content.

Changes:
- Import insertMemories, getMessages, filterMessageContentForMemories
- After generateText completes, persist assistant message if roomId exists
- Gracefully handle persistence failures (log but don't fail request)
- Add 4 unit tests for message persistence behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract shared chat completion persistence logic into reusable utility.
Both /api/chat/generate and /api/emails/inbound now use this shared
function instead of duplicating the 3-step process.

- Create lib/chat/saveChatCompletion.ts encapsulating getMessages +
  filterMessageContentForMemories + insertMemories
- Add 6 unit tests for saveChatCompletion
- Update respondToInboundEmail.ts to use saveChatCompletion
- Update handleChatGenerate.ts to use saveChatCompletion
- Update handleChatGenerate.test.ts to mock new utility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Match /api/emails/inbound behavior by automatically creating a room when
roomId is not provided in chat requests. This ensures conversations are
properly tracked even when clients don't specify a roomId upfront.

- Import generateUUID and createNewRoom
- Generate UUID when roomId is missing
- Create room with accountId, generated roomId, artistId, and first message
- Handle both UIMessage format (parts) and simple format (content)
- Add 6 unit tests for auto room creation behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Jan 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
recoup-api Ready Ready Preview Jan 19, 2026 6:17pm

@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Update tests to reflect new behavior where roomId is auto-created
when not provided. The test "does not save message when roomId is
not provided" is now "saves message with auto-generated roomId when
roomId is not provided" since validateChatRequest auto-creates the room.

- Add mocks for generateUUID and createNewRoom
- Update test assertion to expect saveChatCompletion to be called
- Fixes test file failing to load due to missing Supabase env vars

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cursor[bot]

This comment was marked as outdated.

Since validateChatRequest now auto-creates roomId when not provided,
body.roomId is always defined after validation. The conditional check
was dead code that would always evaluate to true.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Return the roomId in the JSON response so clients know which room
was used, especially important when roomId is auto-generated.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When auto-creating a room, the user's initial message was not being
saved to the memories table. This caused incomplete conversation
history where only assistant responses were stored.

Match /api/emails/inbound behavior by calling saveChatCompletion
with role: "user" after creating the new room.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cursor[bot]

This comment was marked as outdated.

Per /api/emails/inbound behavior, user messages should be persisted
for ALL requests, not just when creating new rooms. Updated to use
insertMemories directly with filterMessageContentForMemories (matching
the exact pattern used in validateNewEmailMemory.ts).

Changes:
- Room creation remains conditional (only when roomId not provided)
- User message persistence happens for ALL requests
- Use insertMemories + filterMessageContentForMemories directly
- Remove saveChatCompletion dependency from validateChatRequest

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cursor[bot]

This comment was marked as outdated.

…lity

Extract common room creation and message persistence logic shared between
validateChatRequest.ts (chat API flow) and validateNewEmailMemory.ts
(email inbound flow) into a reusable setupConversation utility.

The utility:
- Auto-creates room if roomId not provided
- Persists user message to memories for ALL requests
- Supports custom memoryId for deduplication (used by email flow)
- Propagates errors for caller handling (e.g., duplicate detection)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When clients send conversation history, the first message is historical
and already exists in the database. Changed to:
- Use the LAST message (newest) instead of first (oldest)
- Use the message's original ID as memoryId to align with
  handleChatCompletion's upsert and prevent duplicate entries

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add convertToUiMessages() utility (similar to AI SDK's convertToModelMessages)
that normalizes messages from various formats into UIMessage format:
- Passes through UIMessage format unchanged
- Converts simple { role, content } format to UIMessage
- Handles mixed arrays of both formats
- Preserves existing message IDs or generates new ones

Refactor validateChatRequest to use:
- convertToUiMessages() for format normalization
- validateMessages() to get the last message

This DRYs up the inline message format handling logic.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move isUiMessage type guard function from convertToUiMessages.ts
to its own file following Single Responsibility Principle.

Also improved the type guard to handle edge cases:
- null/undefined values
- primitive values
- non-array parts property

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace custom SimpleMessage interface with ModelMessage from the AI SDK,
following DRY principle by reusing existing SDK types.

Updated extractTextContent helper to handle ModelMessage.content which
can be either a string or content parts array.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add mocks for setupConversation, validateMessages, convertToUiMessages,
and handleChatCompletion to prevent Supabase serverClient from loading
during tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove unnecessary extractTextContent function since ModelMessage.content
is always a string type. Direct assignment is simpler and clearer.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
sweetmantech and others added 2 commits January 19, 2026 13:14
ModelMessage.content can be either a string or an array of content parts.
Added getTextContent helper to extract text from both formats.

Added test for content parts array case.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move getTextContent function from convertToUiMessages.ts to its own file
following Single Responsibility Principle.

Added 7 tests for the new utility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sweetmantech sweetmantech merged commit 6f8998f into test Jan 19, 2026
5 checks passed
@sweetmantech sweetmantech deleted the sweetmantech/myc-3961-auto-create-roomid-in-validatechatrequestts-if-not-provided branch January 19, 2026 18:20
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

...validatedBody,
accountId,
orgId,
roomId: finalRoomId,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converted messages not returned causing runtime crash

High Severity

When messages are provided in simple format ({ role, content }), convertToUiMessages generates UIMessage format with IDs and parts arrays, but the converted uiMessages is not assigned back to validatedBody.messages. The returned body still contains the original simple format. Later, handleChatCompletion receives these simple format messages and calls validateMessages(messages) which accesses m.parts.find(...), causing a runtime error since simple format messages have content not parts.

Fix in Cursor Fix in Web

promptMessage: lastMessage,
artistId: validatedBody.artistId,
memoryId: lastMessage.id,
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chat flow missing duplicate message error handling

Medium Severity

setupConversation uses insertMemories which throws on duplicate key violations, and its docstring explicitly states callers should handle error code "23505" for duplicates. The email flow in validateNewEmailMemory properly catches and handles this error, but validateChatRequest calls setupConversation without any try-catch. If a client retries a request with the same message ID (common with UIMessage format), the second request fails with a database constraint violation instead of being handled gracefully.

Fix in Cursor Fix in Web

.filter((part): part is { type: "text"; text: string } => part.type === "text")
.map((part) => part.text)
.join("");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check crashes on malformed message content

Medium Severity

The new getTextContent function assumes content is either a string or an array, but doesn't handle undefined or null. Since the schema allows z.array(z.any()) for messages, a malformed message like { role: "user" } (no content property) passes validation. When convertToUiMessages processes it, modelMessage.content is undefined, and getTextContent crashes with "Cannot read property 'filter' of undefined" when it falls through to the array handling branch.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants