Skip to content

feat(ai-chat): complete phase 2 client sweep - #149

Merged
olliethedev merged 7 commits into
v3from
feat/ai-chat-phase2-sweep
Aug 20, 2026
Merged

feat(ai-chat): complete phase 2 client sweep#149
olliethedev merged 7 commits into
v3from
feat/ai-chat-phase2-sweep

Conversation

@olliethedev

@olliethedev olliethedev commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • standardize AI conversation query keys and resources, including cache-preserving rename updates and exact cache removal on delete
  • add permission-aware, localized chat UI with provider notifications, the rename form hook, and route lifecycle/SSR loader parity
  • update exports, docs, the shadcn registry artifact, unit coverage, and cross-framework chat E2E coverage

Verification

  • pnpm build
  • pnpm typecheck
  • pnpm test (591 tests)
  • docs production build (56 static pages)
  • pnpm -F @btst/stack test-registry
  • changed-file Biome check
  • Playwright chat render smoke on Next.js, TanStack Start, and React Router

The new streaming rename/delete E2E requires a real OPENAI_API_KEY; it is committed for keyed CI coverage. The existing local pnpm-lock.yaml change was intentionally excluded.


Note

Medium Risk
Touches conversation cache, client permission gates, and public-mode bypass behavior. Backward-compatible when no auth provider is set, but auth and cache invalidation need review.

Overview
Finishes the AI Chat phase-2 client sweep: conversations use a shared aiChatResources / createAiChatQueryKeys declaration (stable conversations.list / detail keys), rename merges into the detail cache, and delete removes that cache entry. Drops @lukemorales/query-key-factory and exports @btst/stack/plugins/ai-chat/query-keys.

Authenticated UI now gates ai-chat:conversation read/create/update/delete via StackProvider auth; public mode stays permissive. Copy goes through the i18n catalog (aiChat.*), with legacy localization still winning. Rename/delete/upload feedback uses notify. Adds useRenameConversationForm (trim, field errors, toasts).

Docs list the catalog keys, /chat/conversations APIs, permission map, and query-key import. E2E adds sidebar rename/delete coverage and more reliable chat navigation.

Reviewed by Cursor Bugbot for commit 18167b8. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
better-stack-docs Ready Ready Preview Aug 20, 2026 5:33pm
better-stack-playground Ready Ready Preview Aug 20, 2026 5:33pm

Request Review

@cursor cursor 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.

Security Review — feat(ai-chat): complete phase 2 client sweep

Summary

No high-confidence vulnerabilities were found. The PR refactors the AI Chat client plugin to use a new resource-factory pattern, adds i18n/localization support, and introduces client-side permission gating (useCan / CanAccess). One low-severity observation is noted below.


Threat-area findings

Area Finding Severity
Authn/authz boundary New canWrite / CanAccess / ComposedRoute permission= guards are UI-only — server enforcement must exist independently ℹ️ Note (existing arch)
Info disclosure Raw error.message from API calls surfaced to the user in two places ⚠️ Low
XSS / injection toolName from AI response interpolated into UI strings ✅ Safe (JSX text, not HTML)
Secrets / logging No credentials or secrets added or logged ✅ Safe
Dependencies Only new export paths added to package.json; no new third-party packages ✅ Safe
Deserialization / SSRF No new network calls or deserialization paths introduced ✅ Safe

[LOW] Raw API error messages surfaced to end users

Two call sites pass error.message directly from a caught API response into a user-visible notification:

1. chat-sidebar.tsx — delete failure

catch (error) {
  notify.error(
    error instanceof Error
      ? error.message          // ← raw server error string
      : tr("CONVERSATION_DELETE_FAILURE", ...)
  );
}

2. chat-hooks.tsx — rename form

errorMessage: (error) =>
  error.message ||             // ← raw server error string
  localization?.CONVERSATION_RENAME_FAILURE ||
  t("aiChat.toasts.renameFailure", ...),

Risk: If the backend ever emits verbose error messages (database query details, stack traces, internal resource IDs), those strings will be shown verbatim in the toast to the authenticated user who made the request. There is no XSS risk because React renders the string as text content. Impact is limited to the session owner seeing their own server-side error details.

Recommendation: Sanitize or normalise API errors before surfacing them. A simple guard is sufficient:

// Only use error.message when it looks like a safe user-facing string
const safeMessage = isUserFacingError(error)
  ? error.message
  : t("aiChat.toasts.deleteFailure", "Failed to delete conversation");

Alternatively, standardise the backend to return structured { userMessage: string } error shapes and only read that field on the client.


Note on client-side permission gating

The new useCan / CanAccess / ComposedRoute permission= wrappers are UI-only; they do not replace server-side enforcement. This is the standard pattern for this codebase and the backend plugin endpoints (unchanged here) must independently validate ownership before executing mutations. The canWrite expression correctly defaults to false while the permission check is pending (fail-closed), which is the correct secure default.

Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

Comment thread packages/stack/src/plugins/ai-chat/client/hooks/chat-hooks.tsx

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fce2b2f. Configure here.

Comment thread packages/stack/src/plugins/ai-chat/query-keys.ts Outdated
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Shadcn registry updated — registry JSON files were rebuilt and committed to this branch.

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