Skip to content

fix: point prod API base to api.recoupable.dev (domain cutover)#1820

Merged
sweetmantech merged 1 commit into
testfrom
fix/api-base-dot-dev
Jun 29, 2026
Merged

fix: point prod API base to api.recoupable.dev (domain cutover)#1820
sweetmantech merged 1 commit into
testfrom
fix/api-base-dot-dev

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

What

One-line change in lib/consts.ts: the production branch of NEW_API_BASE_URL https://api.recoupable.comhttps://api.recoupable.dev. Non-prod base (test-recoup-api.vercel.app) unchanged.

Why

Part of the recoupable.com.dev domain cutover (#1819, item 3). api.recoupable.com was retired with the .com apex and no longer resolves, so every client API call from the live chat.recoupable.dev failed:

GET https://api.recoupable.com/api/ai/models → net::ERR_NAME_NOT_RESOLVED

(model selector stuck on default, etc.). The new api.recoupable.dev custom domain is already live — GET /api/ai/models returns 200 — so this is a host swap.

Verification

  • pnpm exec vitest run on the suites that consume NEW_API_BASE_URL (lib/chat, lib/composio, lib/tasks, lib/api): 41 passed / 11 files. Tests build their expected URLs from the constant and run with IS_PROD=false, so they're unaffected by the prod-only literal.
  • tsc --noEmit: no errors implicating consts.ts (the only failing file, lib/emails/__tests__/extractSendEmailResults.test.ts, is pre-existing UIMessage type drift from the in-flight ai-sdk upgrade on main, unrelated to this change).
  • Will confirm on the Vercel preview that /api/ai/models resolves 200 and the model selector populates.

Merge note

Targets test per repo flow. No docs/contract change (host swap only), so no docs PR.

🤖 Generated with Claude Code


Summary by cubic

Updated the production value of NEW_API_BASE_URL from https://api.recoupable.com to https://api.recoupable.dev to complete the domain cutover and fix failed requests from chat.recoupable.dev (e.g., /api/ai/models). Non-prod base stays https://test-recoup-api.vercel.app.

Written for commit 69d9045. Summary will update on new commits.

Review in cubic

NEW_API_BASE_URL resolved to https://api.recoupable.com in production, which no
longer resolves after the .com apex was retired — every client API call from
chat.recoupable.dev failed with ERR_NAME_NOT_RESOLVED (e.g. /api/ai/models, so
the model selector never populated). Point prod at the live api.recoupable.dev
domain. Non-prod base (test-recoup-api.vercel.app) is unchanged.

Refs #1819.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
chat Ready Ready Preview Jun 29, 2026 10:20pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@sweetmantech, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7c79dda6-2d8c-4908-adc6-5dd46322efd3

📥 Commits

Reviewing files that changed from the base of the PR and between 134dc41 and 69d9045.

📒 Files selected for processing (1)
  • lib/consts.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/api-base-dot-dev

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 1 file

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: Updates production API URL to new domain. Single-line change in a constant file, no logic or risk.

Re-trigger cubic

@sweetmantech sweetmantech merged commit f317014 into test Jun 29, 2026
4 checks passed
sweetmantech added a commit that referenced this pull request Jun 29, 2026
* refactor(chat): update chat components to use chatId instead of roomId (#1765)

* refactor(chat): update chat components to use chatId instead of roomId

This commit removes the InstantChatRoom component and updates various hooks and components to replace references of roomId with chatId. The changes ensure that chat functionality is aligned with the new session-scoped architecture, improving consistency across the chat system. Additionally, the sessionId is now required in several components to streamline the chat transport process.

* refactor(chat): integrate sessionId and update chat path utilities

This commit enhances the chat components by introducing the `sessionId` in various hooks and updating the URL handling to utilize new utility functions for generating chat paths. The changes improve the consistency and maintainability of chat navigation, ensuring that the application correctly reflects the session-scoped architecture.

* fix(chat): ensure newRoomId is treated as a string in URL handling

This commit updates the `useCreateArtistTool` hook to explicitly cast `result.newRoomId` to a string when generating the chat path. This change prevents potential type-related issues and ensures consistent URL formatting. Additionally, it adds an import statement for testing utilities in the chat paths test file, enhancing test coverage and maintainability.

* refactor(Header, OrganizationProvider): remove usePathname and replace with window.location.pathname

This commit removes the usePathname hook from both Artist.tsx and OrganizationProvider.tsx, replacing it with direct access to window.location.pathname. This change simplifies the code and maintains the intended navigation behavior without relying on Next.js's router for pathname management.

* refactor(chat): split chatPaths per SRP, drop dead legacy branch, simplify useParams

- Remove dead `/chat/` branch from isActiveChatRoomPath — this PR makes
  /chat/{id} 404, so treating it as an active chat room contradicted the
  PR's own intent and could never fire in production.
- Split lib/chat/chatPaths.ts into one-function-per-file (getChatPath,
  getChatUrl, isActiveChatRoomPath) per repo SRP convention; mirror tests.
- Simplify useVercelChat to `const { chatId } = useParams<{ chatId?: string }>()`
  (KISS), consistent with chat.tsx.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): defer new-chat bootstrap wait from spinner to send button (#1767) (#1774)

* feat(chat): defer new-chat bootstrap wait from spinner to send button (#1767)

New chats landing on `/` or `/chat` blocked the whole view on a
full-screen spinner while `POST /api/sessions` + `POST /api/sandbox`
resolved (~12s cold, dominated by sandbox provisioning). Render `<Chat>`
immediately with a typeable input instead and provision in parallel; the
Send button stays disabled with a "Preparing your workspace…" cue until
the api-minted `sessionId` + `chatId` land, so the workflow transport
never fires without the ids its validator requires.

- NewChatBootstrap: render <Chat> eagerly with a stable placeholder chat
  id (survives the preparing → ready transition); drop the spinner.
- Thread `sessionId?`, `workflowChatId`, `isBootstrapPreparing` through
  Chat → VercelChatProvider → useVercelChat (provider kept inline).
- useVercelChat: `transportChatId = workflowChatId ?? id` drives the
  transport, optimistic conversation, and URL; only load persisted
  history on a canonical route so no spinner flashes over the input
  during the ready transition.
- ChatInput: gate Send on `isBootstrapPreparing`; input stays typeable.
- useChatTransport / useMessageLoader: sessionId optional + guard.
- useCreateArtistTool: guard the one context sessionId consumer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): send live sessionId/chatId from workflow transport (#1767)

The new-chat flow mounts <Chat> (and useChat) during provisioning with
sessionId undefined + a placeholder chatId. useChat captures the transport
from that mount render and does not swap to the rebuilt instance when the
ids later resolve — so the first send POSTed `sessionId: undefined` to
/api/chat/workflow and got a 400 (the URL rewrote correctly because it
reads the live prop, not the transport).

Read the ids from refs inside transport.body() so a single stable
transport instance always sends the values current as of the request,
regardless of useChat's stale capture. Removed chatId/sessionId from the
memo deps since rebuilding the instance never helped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): replace preparing-text cue with a workspace status dot (#1767)

Swap the flashing "Preparing your workspace…" toolbar text for a
stoplight status dot in the top-right of the chat input: red = off
(provisioning failed / unavailable), yellow = provisioning, green =
ready. Hover shows context via the shadcn tooltip.

- New `WorkspaceStatusIndicator` component in its own file (SRP) — a pure
  display component driven by a `status` prop, built on the existing
  shadcn-based `common/Tooltip`, so it's open for reuse without change.
- Thread `workspaceStatus: "off" | "provisioning" | "ready"` through
  Chat → VercelChatProvider → context (replacing the `isBootstrapPreparing`
  boolean); Send is gated while it's not `ready`.
- NewChatBootstrap now derives the status and renders `<Chat>` even on a
  provisioning error (red dot + disabled Send, input still visible)
  instead of swapping in a full-screen error view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(chat): simplify provisioning tooltip copy (#1767)

Shorten the yellow/provisioning workspace-status tooltip to
"Preparing your workspace".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): drop legacy GET /api/chats/{id}/artist caller (#1767) (#1768)

* fix(chat): drop legacy GET /api/chats/{id}/artist caller (#1767)

* fix(chat): reactive conversations cache + user-scoped session query

* refactor(chat): drop dead /chat/:id artist cache fallback (post-#1765)

The conversations-cache fallback in useArtistFromChat only existed for the
legacy /chat/:id deep link (chatId but no sessionId). That route was
removed in #1765 — every chat is now reached via the canonical
/sessions/{sessionId}/chats/{chatId} URL, so sessionId is always present.

Reduce to the session path: GET /api/sessions/{sessionId} -> session.artistId
-> select. Removes useSyncExternalStore reactive-cache machinery,
findArtistIdInConversationsCache (+ its test), and the unused chatId param.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): validate getSessionById response with zod (PR #1768 review)

Replace type-casting with boundary zod validation, matching the
getConversations convention. Drops the `as GetSessionByIdResponse`/
`as GetSessionByIdErrorResponse` casts in favor of safeParse/parse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): extract shared safeJsonParse into lib/api (PR #1768 review)

Replace the locally-defined safeJsonParse in getSessionById with a
shared helper in lib/api/, alongside the other HTTP helpers. DRY/SRP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): replace chatId with transportChatId in message editor and hooks

This commit updates the MessageEditor component and the useVercelChat hook to utilize transportChatId instead of the previous chatId. This change enhances the consistency of chat-related API calls and aligns with the new session-scoped architecture, ensuring that the correct chat identifier is used for operations like deleting trailing messages.

* fix(chat): persist selected model before send so the workflow bills it (#1781)

* fix(chat): persist selected model before send so the workflow bills it

The chat-workflow path bills the model it reads from chats.model_id at
request time. The picker selection was never written there, so every new
chat (and any model change) billed the chats.model_id default instead of
the chosen model.

Before sending, await a PATCH of the selected model to
chats.model_id when it changed for the active chat (shouldPersistChatModel
guards redundant writes). The chat row already exists from new-chat
bootstrap, so this covers the first turn race-free without an api change.

Reuses buildPatchChatBody + updateChat(modelId) (originally from #1779).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): extract model-persist-before-send into usePersistSelectedModel

Addresses OCP/SRP review feedback on #1781: instead of inlining the
persist-before-send block in useVercelChat, encapsulate it in a dedicated
usePersistSelectedModel hook (owns the lastPersisted ref + guard + PATCH).
useVercelChat now just calls `await persistSelectedModel()` before send.

Behavior unchanged; pure decision logic still covered by
shouldPersistChatModel tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): persist selected model on retry/edit + append paths

Addresses review feedback on #1781:
- Retry/Edit call reload() → regenerate (and append() calls sendMessage)
  bypassing handleSubmit, so the selected model was not persisted before
  those sends — the regenerated turn could bill the previous/default model.
  Now await persistSelectedModel() in handleReload and append too.
- Clarify updateChat JSDoc: it patches title and/or modelId (not just rename).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): repoint chat transport to canonical POST /api/chat (#1783)

Step 3 of the /api/chat/workflow → /api/chat rename (chat#1767), against
the merged docs contract (docs#235) and the api endpoint (api#645, on test).

useChatTransport now POSTs to ${baseUrl}/api/chat instead of
/api/chat/workflow. Comment-only refs in VercelChatProvider updated too.

⚠️ Deploy coordination: requires api#645 (which removes /api/chat/workflow)
to be live in the same environment. Must deploy together with the api
promotion and the open-agents repoint (step 4), or chat 404s.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Revert "refactor(chat): replace chatId with transportChatId in message editor and hooks"

This reverts commit 9f34b11.

* chore: upgrade @privy-io/react-auth v1.88.4 → v3.31.0 (#1802)

* chore: upgrade @privy-io/react-auth v1.88.4 -> v3.31.0

Prerequisite for cross-subdomain (cookie) auth in #1801:
matches marketing's ^3.31.0 so both surfaces run a cookie-capable SDK.

Migration surface is minimal — every @privy-io import in chat is usePrivy
or the PrivyProvider component (audited all 48 sites); none of the
v3-renamed/removed hooks (useLoginToFrame, useSignAuthorization,
useSolanaWallets, useWallets, useLogin/useLogout callbacks) are used, and
usePrivy()'s return shape (login/logout/authenticated/user/getAccessToken/
ready) plus user.email.address / user.wallet.address are unchanged in v3.

Only code change: embeddedWallets.createOnLogin moved under a per-chain key
(v3 breaking change) -> embeddedWallets.ethereum.createOnLogin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update providers/PrivyProvider.tsx

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show X (Twitter) + LinkedIn in artist Connectors tab (chat#1793) (#1805)

* feat: show X (Twitter) in artist Connectors tab; keep LinkedIn label-only (chat#1793)

Mirror the api ALLOWED_ARTIST_CONNECTORS change on the frontend: add
`twitter` to the artist-settings connector allow-list. `linkedin` stays
excluded (label/owner-only).

TDD: new allowedArtistConnectors.test.ts asserts twitter included +
linkedin excluded, RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show LinkedIn in artist Connectors tab too (chat#1793)

Reversal of the earlier label-only call: per owner decision 2026-06-18,
LinkedIn is now an artist-facing connector. Add `linkedin` to the chat
ALLOWED_ARTIST_CONNECTORS, mirroring api#680.

TDD: flipped the linkedin assertion (now included), RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: update artist allow-list comment — chat is the source of truth (chat#1793)

The api's ALLOWED_ARTIST_CONNECTORS is being deleted (no runtime consumer;
api#680). Reword the comment to drop the stale "separate from the API's
ALLOWED_ARTIST_CONNECTORS" reference and state that this list is the source
of truth, applied by filtering the unopinionated GET /api/connectors response.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: branded icons + display metadata for X, LinkedIn, YouTube connectors (chat#1793)

The artist Connectors tab showed X/LinkedIn (and YouTube) with the generic
Link2 fallback icon, a default description, and a mangled fallback name.
Add proper display entries:
- getConnectorIcon: SiX (X), SiYoutube (YouTube), lucide Linkedin (LinkedIn,
  since Simple Icons dropped the LinkedIn logo on brand request)
- connectorMetadata: descriptions for twitter + linkedin
- formatConnectorName: "X (Twitter)" + "LinkedIn"

TDD: connectorDisplay.test.ts asserts branded (non-fallback) icons + clean
names + real descriptions for the three slugs, RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: point prod API base to api.recoupable.dev (domain cutover) (#1820)

NEW_API_BASE_URL resolved to https://api.recoupable.com in production, which no
longer resolves after the .com apex was retired — every client API call from
chat.recoupable.dev failed with ERR_NAME_NOT_RESOLVED (e.g. /api/ai/models, so
the model selector never populated). Point prod at the live api.recoupable.dev
domain. Non-prod base (test-recoup-api.vercel.app) is unchanged.

Refs #1819.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: ahmednahima0-beep <ahmednahima0@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: pradipthaadhi <yulius.upwork@gmail.com>
sweetmantech added a commit that referenced this pull request Jun 29, 2026
* refactor(chat): update chat components to use chatId instead of roomId (#1765)

* refactor(chat): update chat components to use chatId instead of roomId

This commit removes the InstantChatRoom component and updates various hooks and components to replace references of roomId with chatId. The changes ensure that chat functionality is aligned with the new session-scoped architecture, improving consistency across the chat system. Additionally, the sessionId is now required in several components to streamline the chat transport process.

* refactor(chat): integrate sessionId and update chat path utilities

This commit enhances the chat components by introducing the `sessionId` in various hooks and updating the URL handling to utilize new utility functions for generating chat paths. The changes improve the consistency and maintainability of chat navigation, ensuring that the application correctly reflects the session-scoped architecture.

* fix(chat): ensure newRoomId is treated as a string in URL handling

This commit updates the `useCreateArtistTool` hook to explicitly cast `result.newRoomId` to a string when generating the chat path. This change prevents potential type-related issues and ensures consistent URL formatting. Additionally, it adds an import statement for testing utilities in the chat paths test file, enhancing test coverage and maintainability.

* refactor(Header, OrganizationProvider): remove usePathname and replace with window.location.pathname

This commit removes the usePathname hook from both Artist.tsx and OrganizationProvider.tsx, replacing it with direct access to window.location.pathname. This change simplifies the code and maintains the intended navigation behavior without relying on Next.js's router for pathname management.

* refactor(chat): split chatPaths per SRP, drop dead legacy branch, simplify useParams

- Remove dead `/chat/` branch from isActiveChatRoomPath — this PR makes
  /chat/{id} 404, so treating it as an active chat room contradicted the
  PR's own intent and could never fire in production.
- Split lib/chat/chatPaths.ts into one-function-per-file (getChatPath,
  getChatUrl, isActiveChatRoomPath) per repo SRP convention; mirror tests.
- Simplify useVercelChat to `const { chatId } = useParams<{ chatId?: string }>()`
  (KISS), consistent with chat.tsx.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): defer new-chat bootstrap wait from spinner to send button (#1767) (#1774)

* feat(chat): defer new-chat bootstrap wait from spinner to send button (#1767)

New chats landing on `/` or `/chat` blocked the whole view on a
full-screen spinner while `POST /api/sessions` + `POST /api/sandbox`
resolved (~12s cold, dominated by sandbox provisioning). Render `<Chat>`
immediately with a typeable input instead and provision in parallel; the
Send button stays disabled with a "Preparing your workspace…" cue until
the api-minted `sessionId` + `chatId` land, so the workflow transport
never fires without the ids its validator requires.

- NewChatBootstrap: render <Chat> eagerly with a stable placeholder chat
  id (survives the preparing → ready transition); drop the spinner.
- Thread `sessionId?`, `workflowChatId`, `isBootstrapPreparing` through
  Chat → VercelChatProvider → useVercelChat (provider kept inline).
- useVercelChat: `transportChatId = workflowChatId ?? id` drives the
  transport, optimistic conversation, and URL; only load persisted
  history on a canonical route so no spinner flashes over the input
  during the ready transition.
- ChatInput: gate Send on `isBootstrapPreparing`; input stays typeable.
- useChatTransport / useMessageLoader: sessionId optional + guard.
- useCreateArtistTool: guard the one context sessionId consumer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): send live sessionId/chatId from workflow transport (#1767)

The new-chat flow mounts <Chat> (and useChat) during provisioning with
sessionId undefined + a placeholder chatId. useChat captures the transport
from that mount render and does not swap to the rebuilt instance when the
ids later resolve — so the first send POSTed `sessionId: undefined` to
/api/chat/workflow and got a 400 (the URL rewrote correctly because it
reads the live prop, not the transport).

Read the ids from refs inside transport.body() so a single stable
transport instance always sends the values current as of the request,
regardless of useChat's stale capture. Removed chatId/sessionId from the
memo deps since rebuilding the instance never helped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): replace preparing-text cue with a workspace status dot (#1767)

Swap the flashing "Preparing your workspace…" toolbar text for a
stoplight status dot in the top-right of the chat input: red = off
(provisioning failed / unavailable), yellow = provisioning, green =
ready. Hover shows context via the shadcn tooltip.

- New `WorkspaceStatusIndicator` component in its own file (SRP) — a pure
  display component driven by a `status` prop, built on the existing
  shadcn-based `common/Tooltip`, so it's open for reuse without change.
- Thread `workspaceStatus: "off" | "provisioning" | "ready"` through
  Chat → VercelChatProvider → context (replacing the `isBootstrapPreparing`
  boolean); Send is gated while it's not `ready`.
- NewChatBootstrap now derives the status and renders `<Chat>` even on a
  provisioning error (red dot + disabled Send, input still visible)
  instead of swapping in a full-screen error view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(chat): simplify provisioning tooltip copy (#1767)

Shorten the yellow/provisioning workspace-status tooltip to
"Preparing your workspace".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): drop legacy GET /api/chats/{id}/artist caller (#1767) (#1768)

* fix(chat): drop legacy GET /api/chats/{id}/artist caller (#1767)

* fix(chat): reactive conversations cache + user-scoped session query

* refactor(chat): drop dead /chat/:id artist cache fallback (post-#1765)

The conversations-cache fallback in useArtistFromChat only existed for the
legacy /chat/:id deep link (chatId but no sessionId). That route was
removed in #1765 — every chat is now reached via the canonical
/sessions/{sessionId}/chats/{chatId} URL, so sessionId is always present.

Reduce to the session path: GET /api/sessions/{sessionId} -> session.artistId
-> select. Removes useSyncExternalStore reactive-cache machinery,
findArtistIdInConversationsCache (+ its test), and the unused chatId param.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): validate getSessionById response with zod (PR #1768 review)

Replace type-casting with boundary zod validation, matching the
getConversations convention. Drops the `as GetSessionByIdResponse`/
`as GetSessionByIdErrorResponse` casts in favor of safeParse/parse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): extract shared safeJsonParse into lib/api (PR #1768 review)

Replace the locally-defined safeJsonParse in getSessionById with a
shared helper in lib/api/, alongside the other HTTP helpers. DRY/SRP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): replace chatId with transportChatId in message editor and hooks

This commit updates the MessageEditor component and the useVercelChat hook to utilize transportChatId instead of the previous chatId. This change enhances the consistency of chat-related API calls and aligns with the new session-scoped architecture, ensuring that the correct chat identifier is used for operations like deleting trailing messages.

* fix(chat): persist selected model before send so the workflow bills it (#1781)

* fix(chat): persist selected model before send so the workflow bills it

The chat-workflow path bills the model it reads from chats.model_id at
request time. The picker selection was never written there, so every new
chat (and any model change) billed the chats.model_id default instead of
the chosen model.

Before sending, await a PATCH of the selected model to
chats.model_id when it changed for the active chat (shouldPersistChatModel
guards redundant writes). The chat row already exists from new-chat
bootstrap, so this covers the first turn race-free without an api change.

Reuses buildPatchChatBody + updateChat(modelId) (originally from #1779).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): extract model-persist-before-send into usePersistSelectedModel

Addresses OCP/SRP review feedback on #1781: instead of inlining the
persist-before-send block in useVercelChat, encapsulate it in a dedicated
usePersistSelectedModel hook (owns the lastPersisted ref + guard + PATCH).
useVercelChat now just calls `await persistSelectedModel()` before send.

Behavior unchanged; pure decision logic still covered by
shouldPersistChatModel tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): persist selected model on retry/edit + append paths

Addresses review feedback on #1781:
- Retry/Edit call reload() → regenerate (and append() calls sendMessage)
  bypassing handleSubmit, so the selected model was not persisted before
  those sends — the regenerated turn could bill the previous/default model.
  Now await persistSelectedModel() in handleReload and append too.
- Clarify updateChat JSDoc: it patches title and/or modelId (not just rename).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): repoint chat transport to canonical POST /api/chat (#1783)

Step 3 of the /api/chat/workflow → /api/chat rename (chat#1767), against
the merged docs contract (docs#235) and the api endpoint (api#645, on test).

useChatTransport now POSTs to ${baseUrl}/api/chat instead of
/api/chat/workflow. Comment-only refs in VercelChatProvider updated too.

⚠️ Deploy coordination: requires api#645 (which removes /api/chat/workflow)
to be live in the same environment. Must deploy together with the api
promotion and the open-agents repoint (step 4), or chat 404s.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Revert "refactor(chat): replace chatId with transportChatId in message editor and hooks"

This reverts commit 9f34b11.

* chore: upgrade @privy-io/react-auth v1.88.4 → v3.31.0 (#1802)

* chore: upgrade @privy-io/react-auth v1.88.4 -> v3.31.0

Prerequisite for cross-subdomain (cookie) auth in #1801:
matches marketing's ^3.31.0 so both surfaces run a cookie-capable SDK.

Migration surface is minimal — every @privy-io import in chat is usePrivy
or the PrivyProvider component (audited all 48 sites); none of the
v3-renamed/removed hooks (useLoginToFrame, useSignAuthorization,
useSolanaWallets, useWallets, useLogin/useLogout callbacks) are used, and
usePrivy()'s return shape (login/logout/authenticated/user/getAccessToken/
ready) plus user.email.address / user.wallet.address are unchanged in v3.

Only code change: embeddedWallets.createOnLogin moved under a per-chain key
(v3 breaking change) -> embeddedWallets.ethereum.createOnLogin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update providers/PrivyProvider.tsx

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show X (Twitter) + LinkedIn in artist Connectors tab (chat#1793) (#1805)

* feat: show X (Twitter) in artist Connectors tab; keep LinkedIn label-only (chat#1793)

Mirror the api ALLOWED_ARTIST_CONNECTORS change on the frontend: add
`twitter` to the artist-settings connector allow-list. `linkedin` stays
excluded (label/owner-only).

TDD: new allowedArtistConnectors.test.ts asserts twitter included +
linkedin excluded, RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show LinkedIn in artist Connectors tab too (chat#1793)

Reversal of the earlier label-only call: per owner decision 2026-06-18,
LinkedIn is now an artist-facing connector. Add `linkedin` to the chat
ALLOWED_ARTIST_CONNECTORS, mirroring api#680.

TDD: flipped the linkedin assertion (now included), RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: update artist allow-list comment — chat is the source of truth (chat#1793)

The api's ALLOWED_ARTIST_CONNECTORS is being deleted (no runtime consumer;
api#680). Reword the comment to drop the stale "separate from the API's
ALLOWED_ARTIST_CONNECTORS" reference and state that this list is the source
of truth, applied by filtering the unopinionated GET /api/connectors response.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: branded icons + display metadata for X, LinkedIn, YouTube connectors (chat#1793)

The artist Connectors tab showed X/LinkedIn (and YouTube) with the generic
Link2 fallback icon, a default description, and a mangled fallback name.
Add proper display entries:
- getConnectorIcon: SiX (X), SiYoutube (YouTube), lucide Linkedin (LinkedIn,
  since Simple Icons dropped the LinkedIn logo on brand request)
- connectorMetadata: descriptions for twitter + linkedin
- formatConnectorName: "X (Twitter)" + "LinkedIn"

TDD: connectorDisplay.test.ts asserts branded (non-fallback) icons + clean
names + real descriptions for the three slugs, RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: point prod API base to api.recoupable.dev (domain cutover) (#1820)

NEW_API_BASE_URL resolved to https://api.recoupable.com in production, which no
longer resolves after the .com apex was retired — every client API call from
chat.recoupable.dev failed with ERR_NAME_NOT_RESOLVED (e.g. /api/ai/models, so
the model selector never populated). Point prod at the live api.recoupable.dev
domain. Non-prod base (test-recoup-api.vercel.app) is unchanged.

Refs #1819.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: repoint dead .com hosts to live .dev (#1819 §A+§B) (#1823)

Repoint dead recoupable.com hosts to their live .dev equivalents and DRY
the URLs through shared constants in lib/consts.ts.

- Add APP_BASE_URL, DOCS_BASE_URL, API_PUBLIC_BASE_URL constants
- getChatUrl: chat.recoupable.com -> APP_BASE_URL (chat.recoupable.dev)
- ApiKeyManager / ExternalLinksGroup docs links -> DOCS_BASE_URL
  (docs.recoupable.dev); apex recoupable.com link left intact (still live)
- In-app API doc examples (account/fans/posts): api.recoupable.com ->
  api.recoupable.dev, via API_PUBLIC_BASE_URL where interpolatable
- JSDoc @see hosts -> docs.recoupable.dev
- Update getChatUrl test assertion to match new origin

Emails (agent@recoupable.com) and apex links left untouched.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: ahmednahima0-beep <ahmednahima0@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: pradipthaadhi <yulius.upwork@gmail.com>
sweetmantech added a commit that referenced this pull request Jun 30, 2026
* refactor(chat): update chat components to use chatId instead of roomId (#1765)

* refactor(chat): update chat components to use chatId instead of roomId

This commit removes the InstantChatRoom component and updates various hooks and components to replace references of roomId with chatId. The changes ensure that chat functionality is aligned with the new session-scoped architecture, improving consistency across the chat system. Additionally, the sessionId is now required in several components to streamline the chat transport process.

* refactor(chat): integrate sessionId and update chat path utilities

This commit enhances the chat components by introducing the `sessionId` in various hooks and updating the URL handling to utilize new utility functions for generating chat paths. The changes improve the consistency and maintainability of chat navigation, ensuring that the application correctly reflects the session-scoped architecture.

* fix(chat): ensure newRoomId is treated as a string in URL handling

This commit updates the `useCreateArtistTool` hook to explicitly cast `result.newRoomId` to a string when generating the chat path. This change prevents potential type-related issues and ensures consistent URL formatting. Additionally, it adds an import statement for testing utilities in the chat paths test file, enhancing test coverage and maintainability.

* refactor(Header, OrganizationProvider): remove usePathname and replace with window.location.pathname

This commit removes the usePathname hook from both Artist.tsx and OrganizationProvider.tsx, replacing it with direct access to window.location.pathname. This change simplifies the code and maintains the intended navigation behavior without relying on Next.js's router for pathname management.

* refactor(chat): split chatPaths per SRP, drop dead legacy branch, simplify useParams

- Remove dead `/chat/` branch from isActiveChatRoomPath — this PR makes
  /chat/{id} 404, so treating it as an active chat room contradicted the
  PR's own intent and could never fire in production.
- Split lib/chat/chatPaths.ts into one-function-per-file (getChatPath,
  getChatUrl, isActiveChatRoomPath) per repo SRP convention; mirror tests.
- Simplify useVercelChat to `const { chatId } = useParams<{ chatId?: string }>()`
  (KISS), consistent with chat.tsx.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): defer new-chat bootstrap wait from spinner to send button (#1767) (#1774)

* feat(chat): defer new-chat bootstrap wait from spinner to send button (#1767)

New chats landing on `/` or `/chat` blocked the whole view on a
full-screen spinner while `POST /api/sessions` + `POST /api/sandbox`
resolved (~12s cold, dominated by sandbox provisioning). Render `<Chat>`
immediately with a typeable input instead and provision in parallel; the
Send button stays disabled with a "Preparing your workspace…" cue until
the api-minted `sessionId` + `chatId` land, so the workflow transport
never fires without the ids its validator requires.

- NewChatBootstrap: render <Chat> eagerly with a stable placeholder chat
  id (survives the preparing → ready transition); drop the spinner.
- Thread `sessionId?`, `workflowChatId`, `isBootstrapPreparing` through
  Chat → VercelChatProvider → useVercelChat (provider kept inline).
- useVercelChat: `transportChatId = workflowChatId ?? id` drives the
  transport, optimistic conversation, and URL; only load persisted
  history on a canonical route so no spinner flashes over the input
  during the ready transition.
- ChatInput: gate Send on `isBootstrapPreparing`; input stays typeable.
- useChatTransport / useMessageLoader: sessionId optional + guard.
- useCreateArtistTool: guard the one context sessionId consumer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): send live sessionId/chatId from workflow transport (#1767)

The new-chat flow mounts <Chat> (and useChat) during provisioning with
sessionId undefined + a placeholder chatId. useChat captures the transport
from that mount render and does not swap to the rebuilt instance when the
ids later resolve — so the first send POSTed `sessionId: undefined` to
/api/chat/workflow and got a 400 (the URL rewrote correctly because it
reads the live prop, not the transport).

Read the ids from refs inside transport.body() so a single stable
transport instance always sends the values current as of the request,
regardless of useChat's stale capture. Removed chatId/sessionId from the
memo deps since rebuilding the instance never helped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): replace preparing-text cue with a workspace status dot (#1767)

Swap the flashing "Preparing your workspace…" toolbar text for a
stoplight status dot in the top-right of the chat input: red = off
(provisioning failed / unavailable), yellow = provisioning, green =
ready. Hover shows context via the shadcn tooltip.

- New `WorkspaceStatusIndicator` component in its own file (SRP) — a pure
  display component driven by a `status` prop, built on the existing
  shadcn-based `common/Tooltip`, so it's open for reuse without change.
- Thread `workspaceStatus: "off" | "provisioning" | "ready"` through
  Chat → VercelChatProvider → context (replacing the `isBootstrapPreparing`
  boolean); Send is gated while it's not `ready`.
- NewChatBootstrap now derives the status and renders `<Chat>` even on a
  provisioning error (red dot + disabled Send, input still visible)
  instead of swapping in a full-screen error view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(chat): simplify provisioning tooltip copy (#1767)

Shorten the yellow/provisioning workspace-status tooltip to
"Preparing your workspace".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): drop legacy GET /api/chats/{id}/artist caller (#1767) (#1768)

* fix(chat): drop legacy GET /api/chats/{id}/artist caller (#1767)

* fix(chat): reactive conversations cache + user-scoped session query

* refactor(chat): drop dead /chat/:id artist cache fallback (post-#1765)

The conversations-cache fallback in useArtistFromChat only existed for the
legacy /chat/:id deep link (chatId but no sessionId). That route was
removed in #1765 — every chat is now reached via the canonical
/sessions/{sessionId}/chats/{chatId} URL, so sessionId is always present.

Reduce to the session path: GET /api/sessions/{sessionId} -> session.artistId
-> select. Removes useSyncExternalStore reactive-cache machinery,
findArtistIdInConversationsCache (+ its test), and the unused chatId param.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): validate getSessionById response with zod (PR #1768 review)

Replace type-casting with boundary zod validation, matching the
getConversations convention. Drops the `as GetSessionByIdResponse`/
`as GetSessionByIdErrorResponse` casts in favor of safeParse/parse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): extract shared safeJsonParse into lib/api (PR #1768 review)

Replace the locally-defined safeJsonParse in getSessionById with a
shared helper in lib/api/, alongside the other HTTP helpers. DRY/SRP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): replace chatId with transportChatId in message editor and hooks

This commit updates the MessageEditor component and the useVercelChat hook to utilize transportChatId instead of the previous chatId. This change enhances the consistency of chat-related API calls and aligns with the new session-scoped architecture, ensuring that the correct chat identifier is used for operations like deleting trailing messages.

* fix(chat): persist selected model before send so the workflow bills it (#1781)

* fix(chat): persist selected model before send so the workflow bills it

The chat-workflow path bills the model it reads from chats.model_id at
request time. The picker selection was never written there, so every new
chat (and any model change) billed the chats.model_id default instead of
the chosen model.

Before sending, await a PATCH of the selected model to
chats.model_id when it changed for the active chat (shouldPersistChatModel
guards redundant writes). The chat row already exists from new-chat
bootstrap, so this covers the first turn race-free without an api change.

Reuses buildPatchChatBody + updateChat(modelId) (originally from #1779).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): extract model-persist-before-send into usePersistSelectedModel

Addresses OCP/SRP review feedback on #1781: instead of inlining the
persist-before-send block in useVercelChat, encapsulate it in a dedicated
usePersistSelectedModel hook (owns the lastPersisted ref + guard + PATCH).
useVercelChat now just calls `await persistSelectedModel()` before send.

Behavior unchanged; pure decision logic still covered by
shouldPersistChatModel tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): persist selected model on retry/edit + append paths

Addresses review feedback on #1781:
- Retry/Edit call reload() → regenerate (and append() calls sendMessage)
  bypassing handleSubmit, so the selected model was not persisted before
  those sends — the regenerated turn could bill the previous/default model.
  Now await persistSelectedModel() in handleReload and append too.
- Clarify updateChat JSDoc: it patches title and/or modelId (not just rename).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): repoint chat transport to canonical POST /api/chat (#1783)

Step 3 of the /api/chat/workflow → /api/chat rename (chat#1767), against
the merged docs contract (docs#235) and the api endpoint (api#645, on test).

useChatTransport now POSTs to ${baseUrl}/api/chat instead of
/api/chat/workflow. Comment-only refs in VercelChatProvider updated too.

⚠️ Deploy coordination: requires api#645 (which removes /api/chat/workflow)
to be live in the same environment. Must deploy together with the api
promotion and the open-agents repoint (step 4), or chat 404s.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Revert "refactor(chat): replace chatId with transportChatId in message editor and hooks"

This reverts commit 9f34b11.

* chore: upgrade @privy-io/react-auth v1.88.4 → v3.31.0 (#1802)

* chore: upgrade @privy-io/react-auth v1.88.4 -> v3.31.0

Prerequisite for cross-subdomain (cookie) auth in #1801:
matches marketing's ^3.31.0 so both surfaces run a cookie-capable SDK.

Migration surface is minimal — every @privy-io import in chat is usePrivy
or the PrivyProvider component (audited all 48 sites); none of the
v3-renamed/removed hooks (useLoginToFrame, useSignAuthorization,
useSolanaWallets, useWallets, useLogin/useLogout callbacks) are used, and
usePrivy()'s return shape (login/logout/authenticated/user/getAccessToken/
ready) plus user.email.address / user.wallet.address are unchanged in v3.

Only code change: embeddedWallets.createOnLogin moved under a per-chain key
(v3 breaking change) -> embeddedWallets.ethereum.createOnLogin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update providers/PrivyProvider.tsx

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show X (Twitter) + LinkedIn in artist Connectors tab (chat#1793) (#1805)

* feat: show X (Twitter) in artist Connectors tab; keep LinkedIn label-only (chat#1793)

Mirror the api ALLOWED_ARTIST_CONNECTORS change on the frontend: add
`twitter` to the artist-settings connector allow-list. `linkedin` stays
excluded (label/owner-only).

TDD: new allowedArtistConnectors.test.ts asserts twitter included +
linkedin excluded, RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show LinkedIn in artist Connectors tab too (chat#1793)

Reversal of the earlier label-only call: per owner decision 2026-06-18,
LinkedIn is now an artist-facing connector. Add `linkedin` to the chat
ALLOWED_ARTIST_CONNECTORS, mirroring api#680.

TDD: flipped the linkedin assertion (now included), RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: update artist allow-list comment — chat is the source of truth (chat#1793)

The api's ALLOWED_ARTIST_CONNECTORS is being deleted (no runtime consumer;
api#680). Reword the comment to drop the stale "separate from the API's
ALLOWED_ARTIST_CONNECTORS" reference and state that this list is the source
of truth, applied by filtering the unopinionated GET /api/connectors response.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: branded icons + display metadata for X, LinkedIn, YouTube connectors (chat#1793)

The artist Connectors tab showed X/LinkedIn (and YouTube) with the generic
Link2 fallback icon, a default description, and a mangled fallback name.
Add proper display entries:
- getConnectorIcon: SiX (X), SiYoutube (YouTube), lucide Linkedin (LinkedIn,
  since Simple Icons dropped the LinkedIn logo on brand request)
- connectorMetadata: descriptions for twitter + linkedin
- formatConnectorName: "X (Twitter)" + "LinkedIn"

TDD: connectorDisplay.test.ts asserts branded (non-fallback) icons + clean
names + real descriptions for the three slugs, RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: point prod API base to api.recoupable.dev (domain cutover) (#1820)

NEW_API_BASE_URL resolved to https://api.recoupable.com in production, which no
longer resolves after the .com apex was retired — every client API call from
chat.recoupable.dev failed with ERR_NAME_NOT_RESOLVED (e.g. /api/ai/models, so
the model selector never populated). Point prod at the live api.recoupable.dev
domain. Non-prod base (test-recoup-api.vercel.app) is unchanged.

Refs #1819.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: repoint dead .com hosts to live .dev (#1819 §A+§B) (#1823)

Repoint dead recoupable.com hosts to their live .dev equivalents and DRY
the URLs through shared constants in lib/consts.ts.

- Add APP_BASE_URL, DOCS_BASE_URL, API_PUBLIC_BASE_URL constants
- getChatUrl: chat.recoupable.com -> APP_BASE_URL (chat.recoupable.dev)
- ApiKeyManager / ExternalLinksGroup docs links -> DOCS_BASE_URL
  (docs.recoupable.dev); apex recoupable.com link left intact (still live)
- In-app API doc examples (account/fans/posts): api.recoupable.com ->
  api.recoupable.dev, via API_PUBLIC_BASE_URL where interpolatable
- JSDoc @see hosts -> docs.recoupable.dev
- Update getChatUrl test assertion to match new origin

Emails (agent@recoupable.com) and apex links left untouched.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* HOLD MERGE: move brand email to @recoupable.dev (#1826)

RECOUP_FROM_EMAIL agent@recoupable.com → @recoupable.dev (the agent's outbound
from-address); global-error page "Get help" support mailto → @recoupable.dev.

DO NOT MERGE until recoupable.dev is a verified sending domain in Resend —
the agent sends from this address, so an unverified domain bounces every email.
Per #1819 decision 2026-06-29.

Refs #1819.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: ahmednahima0-beep <ahmednahima0@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: pradipthaadhi <yulius.upwork@gmail.com>
sweetmantech added a commit that referenced this pull request Jun 30, 2026
* refactor(chat): update chat components to use chatId instead of roomId (#1765)

* refactor(chat): update chat components to use chatId instead of roomId

This commit removes the InstantChatRoom component and updates various hooks and components to replace references of roomId with chatId. The changes ensure that chat functionality is aligned with the new session-scoped architecture, improving consistency across the chat system. Additionally, the sessionId is now required in several components to streamline the chat transport process.

* refactor(chat): integrate sessionId and update chat path utilities

This commit enhances the chat components by introducing the `sessionId` in various hooks and updating the URL handling to utilize new utility functions for generating chat paths. The changes improve the consistency and maintainability of chat navigation, ensuring that the application correctly reflects the session-scoped architecture.

* fix(chat): ensure newRoomId is treated as a string in URL handling

This commit updates the `useCreateArtistTool` hook to explicitly cast `result.newRoomId` to a string when generating the chat path. This change prevents potential type-related issues and ensures consistent URL formatting. Additionally, it adds an import statement for testing utilities in the chat paths test file, enhancing test coverage and maintainability.

* refactor(Header, OrganizationProvider): remove usePathname and replace with window.location.pathname

This commit removes the usePathname hook from both Artist.tsx and OrganizationProvider.tsx, replacing it with direct access to window.location.pathname. This change simplifies the code and maintains the intended navigation behavior without relying on Next.js's router for pathname management.

* refactor(chat): split chatPaths per SRP, drop dead legacy branch, simplify useParams

- Remove dead `/chat/` branch from isActiveChatRoomPath — this PR makes
  /chat/{id} 404, so treating it as an active chat room contradicted the
  PR's own intent and could never fire in production.
- Split lib/chat/chatPaths.ts into one-function-per-file (getChatPath,
  getChatUrl, isActiveChatRoomPath) per repo SRP convention; mirror tests.
- Simplify useVercelChat to `const { chatId } = useParams<{ chatId?: string }>()`
  (KISS), consistent with chat.tsx.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): defer new-chat bootstrap wait from spinner to send button (#1767) (#1774)

* feat(chat): defer new-chat bootstrap wait from spinner to send button (#1767)

New chats landing on `/` or `/chat` blocked the whole view on a
full-screen spinner while `POST /api/sessions` + `POST /api/sandbox`
resolved (~12s cold, dominated by sandbox provisioning). Render `<Chat>`
immediately with a typeable input instead and provision in parallel; the
Send button stays disabled with a "Preparing your workspace…" cue until
the api-minted `sessionId` + `chatId` land, so the workflow transport
never fires without the ids its validator requires.

- NewChatBootstrap: render <Chat> eagerly with a stable placeholder chat
  id (survives the preparing → ready transition); drop the spinner.
- Thread `sessionId?`, `workflowChatId`, `isBootstrapPreparing` through
  Chat → VercelChatProvider → useVercelChat (provider kept inline).
- useVercelChat: `transportChatId = workflowChatId ?? id` drives the
  transport, optimistic conversation, and URL; only load persisted
  history on a canonical route so no spinner flashes over the input
  during the ready transition.
- ChatInput: gate Send on `isBootstrapPreparing`; input stays typeable.
- useChatTransport / useMessageLoader: sessionId optional + guard.
- useCreateArtistTool: guard the one context sessionId consumer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): send live sessionId/chatId from workflow transport (#1767)

The new-chat flow mounts <Chat> (and useChat) during provisioning with
sessionId undefined + a placeholder chatId. useChat captures the transport
from that mount render and does not swap to the rebuilt instance when the
ids later resolve — so the first send POSTed `sessionId: undefined` to
/api/chat/workflow and got a 400 (the URL rewrote correctly because it
reads the live prop, not the transport).

Read the ids from refs inside transport.body() so a single stable
transport instance always sends the values current as of the request,
regardless of useChat's stale capture. Removed chatId/sessionId from the
memo deps since rebuilding the instance never helped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): replace preparing-text cue with a workspace status dot (#1767)

Swap the flashing "Preparing your workspace…" toolbar text for a
stoplight status dot in the top-right of the chat input: red = off
(provisioning failed / unavailable), yellow = provisioning, green =
ready. Hover shows context via the shadcn tooltip.

- New `WorkspaceStatusIndicator` component in its own file (SRP) — a pure
  display component driven by a `status` prop, built on the existing
  shadcn-based `common/Tooltip`, so it's open for reuse without change.
- Thread `workspaceStatus: "off" | "provisioning" | "ready"` through
  Chat → VercelChatProvider → context (replacing the `isBootstrapPreparing`
  boolean); Send is gated while it's not `ready`.
- NewChatBootstrap now derives the status and renders `<Chat>` even on a
  provisioning error (red dot + disabled Send, input still visible)
  instead of swapping in a full-screen error view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(chat): simplify provisioning tooltip copy (#1767)

Shorten the yellow/provisioning workspace-status tooltip to
"Preparing your workspace".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): drop legacy GET /api/chats/{id}/artist caller (#1767) (#1768)

* fix(chat): drop legacy GET /api/chats/{id}/artist caller (#1767)

* fix(chat): reactive conversations cache + user-scoped session query

* refactor(chat): drop dead /chat/:id artist cache fallback (post-#1765)

The conversations-cache fallback in useArtistFromChat only existed for the
legacy /chat/:id deep link (chatId but no sessionId). That route was
removed in #1765 — every chat is now reached via the canonical
/sessions/{sessionId}/chats/{chatId} URL, so sessionId is always present.

Reduce to the session path: GET /api/sessions/{sessionId} -> session.artistId
-> select. Removes useSyncExternalStore reactive-cache machinery,
findArtistIdInConversationsCache (+ its test), and the unused chatId param.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): validate getSessionById response with zod (PR #1768 review)

Replace type-casting with boundary zod validation, matching the
getConversations convention. Drops the `as GetSessionByIdResponse`/
`as GetSessionByIdErrorResponse` casts in favor of safeParse/parse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): extract shared safeJsonParse into lib/api (PR #1768 review)

Replace the locally-defined safeJsonParse in getSessionById with a
shared helper in lib/api/, alongside the other HTTP helpers. DRY/SRP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): replace chatId with transportChatId in message editor and hooks

This commit updates the MessageEditor component and the useVercelChat hook to utilize transportChatId instead of the previous chatId. This change enhances the consistency of chat-related API calls and aligns with the new session-scoped architecture, ensuring that the correct chat identifier is used for operations like deleting trailing messages.

* fix(chat): persist selected model before send so the workflow bills it (#1781)

* fix(chat): persist selected model before send so the workflow bills it

The chat-workflow path bills the model it reads from chats.model_id at
request time. The picker selection was never written there, so every new
chat (and any model change) billed the chats.model_id default instead of
the chosen model.

Before sending, await a PATCH of the selected model to
chats.model_id when it changed for the active chat (shouldPersistChatModel
guards redundant writes). The chat row already exists from new-chat
bootstrap, so this covers the first turn race-free without an api change.

Reuses buildPatchChatBody + updateChat(modelId) (originally from #1779).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(chat): extract model-persist-before-send into usePersistSelectedModel

Addresses OCP/SRP review feedback on #1781: instead of inlining the
persist-before-send block in useVercelChat, encapsulate it in a dedicated
usePersistSelectedModel hook (owns the lastPersisted ref + guard + PATCH).
useVercelChat now just calls `await persistSelectedModel()` before send.

Behavior unchanged; pure decision logic still covered by
shouldPersistChatModel tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(chat): persist selected model on retry/edit + append paths

Addresses review feedback on #1781:
- Retry/Edit call reload() → regenerate (and append() calls sendMessage)
  bypassing handleSubmit, so the selected model was not persisted before
  those sends — the regenerated turn could bill the previous/default model.
  Now await persistSelectedModel() in handleReload and append too.
- Clarify updateChat JSDoc: it patches title and/or modelId (not just rename).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(chat): repoint chat transport to canonical POST /api/chat (#1783)

Step 3 of the /api/chat/workflow → /api/chat rename (chat#1767), against
the merged docs contract (docs#235) and the api endpoint (api#645, on test).

useChatTransport now POSTs to ${baseUrl}/api/chat instead of
/api/chat/workflow. Comment-only refs in VercelChatProvider updated too.

⚠️ Deploy coordination: requires api#645 (which removes /api/chat/workflow)
to be live in the same environment. Must deploy together with the api
promotion and the open-agents repoint (step 4), or chat 404s.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Revert "refactor(chat): replace chatId with transportChatId in message editor and hooks"

This reverts commit 9f34b11.

* chore: upgrade @privy-io/react-auth v1.88.4 → v3.31.0 (#1802)

* chore: upgrade @privy-io/react-auth v1.88.4 -> v3.31.0

Prerequisite for cross-subdomain (cookie) auth in #1801:
matches marketing's ^3.31.0 so both surfaces run a cookie-capable SDK.

Migration surface is minimal — every @privy-io import in chat is usePrivy
or the PrivyProvider component (audited all 48 sites); none of the
v3-renamed/removed hooks (useLoginToFrame, useSignAuthorization,
useSolanaWallets, useWallets, useLogin/useLogout callbacks) are used, and
usePrivy()'s return shape (login/logout/authenticated/user/getAccessToken/
ready) plus user.email.address / user.wallet.address are unchanged in v3.

Only code change: embeddedWallets.createOnLogin moved under a per-chain key
(v3 breaking change) -> embeddedWallets.ethereum.createOnLogin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update providers/PrivyProvider.tsx

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show X (Twitter) + LinkedIn in artist Connectors tab (chat#1793) (#1805)

* feat: show X (Twitter) in artist Connectors tab; keep LinkedIn label-only (chat#1793)

Mirror the api ALLOWED_ARTIST_CONNECTORS change on the frontend: add
`twitter` to the artist-settings connector allow-list. `linkedin` stays
excluded (label/owner-only).

TDD: new allowedArtistConnectors.test.ts asserts twitter included +
linkedin excluded, RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: show LinkedIn in artist Connectors tab too (chat#1793)

Reversal of the earlier label-only call: per owner decision 2026-06-18,
LinkedIn is now an artist-facing connector. Add `linkedin` to the chat
ALLOWED_ARTIST_CONNECTORS, mirroring api#680.

TDD: flipped the linkedin assertion (now included), RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: update artist allow-list comment — chat is the source of truth (chat#1793)

The api's ALLOWED_ARTIST_CONNECTORS is being deleted (no runtime consumer;
api#680). Reword the comment to drop the stale "separate from the API's
ALLOWED_ARTIST_CONNECTORS" reference and state that this list is the source
of truth, applied by filtering the unopinionated GET /api/connectors response.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: branded icons + display metadata for X, LinkedIn, YouTube connectors (chat#1793)

The artist Connectors tab showed X/LinkedIn (and YouTube) with the generic
Link2 fallback icon, a default description, and a mangled fallback name.
Add proper display entries:
- getConnectorIcon: SiX (X), SiYoutube (YouTube), lucide Linkedin (LinkedIn,
  since Simple Icons dropped the LinkedIn logo on brand request)
- connectorMetadata: descriptions for twitter + linkedin
- formatConnectorName: "X (Twitter)" + "LinkedIn"

TDD: connectorDisplay.test.ts asserts branded (non-fallback) icons + clean
names + real descriptions for the three slugs, RED before GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: point prod API base to api.recoupable.dev (domain cutover) (#1820)

NEW_API_BASE_URL resolved to https://api.recoupable.com in production, which no
longer resolves after the .com apex was retired — every client API call from
chat.recoupable.dev failed with ERR_NAME_NOT_RESOLVED (e.g. /api/ai/models, so
the model selector never populated). Point prod at the live api.recoupable.dev
domain. Non-prod base (test-recoup-api.vercel.app) is unchanged.

Refs #1819.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: repoint dead .com hosts to live .dev (#1819 §A+§B) (#1823)

Repoint dead recoupable.com hosts to their live .dev equivalents and DRY
the URLs through shared constants in lib/consts.ts.

- Add APP_BASE_URL, DOCS_BASE_URL, API_PUBLIC_BASE_URL constants
- getChatUrl: chat.recoupable.com -> APP_BASE_URL (chat.recoupable.dev)
- ApiKeyManager / ExternalLinksGroup docs links -> DOCS_BASE_URL
  (docs.recoupable.dev); apex recoupable.com link left intact (still live)
- In-app API doc examples (account/fans/posts): api.recoupable.com ->
  api.recoupable.dev, via API_PUBLIC_BASE_URL where interpolatable
- JSDoc @see hosts -> docs.recoupable.dev
- Update getChatUrl test assertion to match new origin

Emails (agent@recoupable.com) and apex links left untouched.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* HOLD MERGE: move brand email to @recoupable.dev (#1826)

RECOUP_FROM_EMAIL agent@recoupable.com → @recoupable.dev (the agent's outbound
from-address); global-error page "Get help" support mailto → @recoupable.dev.

DO NOT MERGE until recoupable.dev is a verified sending domain in Resend —
the agent sends from this address, so an unverified domain bounces every email.
Per #1819 decision 2026-06-29.

Refs #1819.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: move apex recoupable.com → recoupable.dev (chat Website + legal redirects) (#1825)

Update the Website link in the user profile dropdown and the privacy/terms
redirects from recoupable.com to the live recoupable.dev apex.

- ExternalLinksGroup: Website href + label → recoupable.dev
- /privacy → https://recoupable.dev/privacy-policy
- /terms → https://recoupable.dev/terms-of-use (path renamed from terms-of-service)

Refs #1819

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: ahmednahima0-beep <ahmednahima0@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: pradipthaadhi <yulius.upwork@gmail.com>
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.

1 participant