@frontera-sdk/chat-ui

中文文档
1.53.54发布于 2026/11/15Apache-2.0TypeScriptES Module近 30 天下载 1.5万

Ready-made React components for embedding a Frontera agent conversation — message rendering, tool calls, artifacts and composer.

原始使用文档
文档目录20 节
  1. Install
  2. Quick start
  3. Credential
  4. Upgrading
  5. modes , modelMode and defaultMode are gone — use modelSelec…
  6. Props
  7. Composer
  8. Conversation and agent
  9. Layout and appearance
  10. Connect flow
  11. Events
  12. Tokens
  13. Which key
  14. Capabilities
  15. Other entry points
  16. Connecting a user's own account
  17. Mobile
  18. Hazards
  19. Troubleshooting
  20. What the browser calls

@frontera-sdk/chat-ui

A Frontera agent's conversation, as a React component in your own application. Streamed replies, tool activity, attachments, the files the agent produces, knowledge citations, approvals and personal-account connections.

It is a component, not an iframe: it renders in your DOM, uses your React, and your CSS can reach it (see Hazards).


Install

npm i @frontera-sdk/chat-ui react react-dom

React 19, ESM only. Installing needs Node.js 22 or later. react and react-dom are the only packages the component needs from you — two copies of React in one page break hooks. Everything else it renders with installs as a dependency of this package.

npm ls react   # expect one version

If your token endpoint lives in the same project, the server side needs two more, because your own code imports them directly (see Tokens):

npm i @frontera-sdk/chat @frontera-sdk/core

Both already arrive with this package, so the install is a no-op in practice. Declare them anyway: a package your source imports belongs in your manifest, or the day this package stops depending on them your build breaks.


Quick start

Two things are required: both stylesheets, and a container with a height.

'use client'
import { FronteraChat } from '@frontera-sdk/chat-ui/chat'
import '@frontera-sdk/chat-ui/platform.css'
import '@frontera-sdk/chat-ui/styles.css'

export function Support() {
  return (
    <div className="h-dvh">
      <FronteraChat tokenEndpoint="/api/chat-token" />
    </div>
  )
}

tokenEndpoint is a route on your server returning { token: string, expiresAt: string } for the signed-in person. Building that route is the other half of the integration — see Tokens.


Credential

Pass exactly one. All four are mutually exclusive in the type.

PropTypeNotes
tokenEndpointstringRecommended. URL on your server. Renewal handled for you. Return apiBaseUrl alongside the token, or pass it as a prop — the exchange does not include it.
token + apiBaseUrlstring + stringYou own replacing it before it expires. The chat stops when it does.
getToken() => Promise<{ token, expiresAt, apiBaseUrl? }>Called again on renewal.
clientFronteraClient | (() => FronteraClient | null)You built one yourself, e.g. from useChatClient.

Upgrading

modes, modelMode and defaultMode are gone — use modelSelector

The model is bound to the Agent on the deployment. A list supplied by the host could only ever disagree with what actually runs, and the disagreement was invisible: the picker showed one model and the reply came from another.

What remains is modelSelector?: boolean — whether the picker appears at all. Which modes it offers, and which one starts selected, now come from the agent.

BeforeAfter
modes={[...]}delete it — the agent's own modes are used
modes={[]} to hide the pickermodelSelector={false}
modelMode="fast" to pin a modelconfigure that mode as the agent's default; the prop has no replacement
defaultMode="fast"same — it is the agent's default mode now

TypeScript reports these as unknown props. JavaScript does not — an ignored modelMode leaves the chat running the agent's default mode, and a modes={[]} that used to hide the picker now shows it, since modelSelector defaults to true. Search your integration for all three names.

Ship this with a deployment whose /v1/chat-agents projects agent modes. An older deployment answers without them and the picker renders empty.

Props

Everything below is optional.

Composer

PropTypeDefaultWhat it does
approvalSelectorbooleantrueApproval-mode picker. full ("run without approval") only appears if the token carries the approval-mode capability.
fileUploadbooleantruePaperclip and drop-anywhere-on-the-conversation. false makes the whole surface inert.
appsButtonbooleanfalsePlugins button — connect accounts before asking. Requires connectRedirectUri.
shortcutsShortcut[]noneStarter cards on an empty conversation. Off by default: each default card needs a computer-equipped agent.
modelSelectorbooleantrueModel picker. What it offers comes from the agent; it hides itself when there is one mode and no effort levels.
placeholderstring"Assign a task or ask anything..."Composer placeholder.
interface Shortcut {
  id: string
  label: string
  description: string
  prompt: string   // dropped into the composer, not sent
}

Conversation and agent

PropTypeDefaultWhat it does
conversationIdstringnew each mountResume a thread. Key the component by it if you own a thread list.
agentNamestringperson choosesSelects the agent by slug or id, and hides the picker. Ignored when the credential already fixes the agent.
agentLabelstringagent's own nameDisplay name above replies. The cosmetic one.
agentIdstring—Agent UUID. Lets the composer offer that agent's apps and @ mentions.
agentPickerbooleantrueShown only when the credential reaches more than one agent.
bodyPartial<StartChatRequest>—Extra fields on every send, read at send time. messages and conversationId excluded.

Layout and appearance

PropTypeDefaultWhat it does
headerbooleantrueThe conversation's own header.
sidePanelbooleantrueArtifact pane (desktop) / full-screen sheet (mobile), and the control that opens it.
themeFronteraChatThemeinheritsDesign tokens, applied to the shell and the portal root.
darkbooleanOS preferencePin the scheme. Pass it if your app has a theme toggle, or the two disagree.
classNamestring—Added to the shell element.
emptyReactNodeplatform greetingReplaces the new-session screen.
mobileMenuReactNodenoneLeading control in the mobile header — your session list. Nothing renders if omitted. Ignored ≥768px.
interface FronteraChatTheme {
  background?: string; surface?: string; border?: string
  text?: string; muted?: string
  accent?: string; accentText?: string; danger?: string
  radius?: string; fontFamily?: string
}

Connect flow

PropTypeWhat it does
connectRedirectUristringYour OAuth callback page. Without it the connect card explains what is missing instead of offering a dead button.
connectOriginstringOrigin the callback posts from, when it differs from the redirect's.

Events

PropTypeWhen
onStart(info: { conversationId: string; runId: string }) => voidA run starts. The hook for your own thread list.
onError(error: Error) => voidEvery transport and stream error. Wire this — without it failures are visible to the user and nowhere in your logs.

Tokens

The browser never holds your agent key. Your server exchanges it for a short-lived, per-user cht_ token.

Server-side only. Needs @frontera-sdk/chat and @frontera-sdk/core in your manifest — see Install.

// POST /api/chat-token — your server
import { FronteraClient } from '@frontera-sdk/core/client'
import { exchangeChatToken } from '@frontera-sdk/chat/auth'

const frontera = new FronteraClient({
  apiBaseUrl: process.env.FRONTERA_API_URL!,
  // Either key kind goes here, same shape — see "Which key" below.
  credential: { kind: 'apiKey', key: process.env.FRONTERA_WORKSPACE_KEY! },
})

export async function POST(req: Request) {
  const user = await currentUser(req)                // YOUR session
  if (!user) return new Response('unauthorized', { status: 401 })

  const token = await exchangeChatToken(frontera, {
    externalUserId: user.id,                         // never from the body
    ttlSeconds: 1800,                                // 60–86400, default 3600
    capabilities: ['plugin-connect'],
  })

  // `apiBaseUrl` is yours to add — the exchange returns only the token and its
  // expiry, and the component has no other way to learn which deployment the
  // token belongs to. Without it: "No apiBaseUrl: pass one, or return it from
  // your token endpoint". It is the origin the browser is about to call, so
  // there is nothing secret in it.
  return Response.json({ ...token, apiBaseUrl: process.env.FRONTERA_API_URL })
}

Which key

Agent key sak_…Workspace key sk-ws-…
Reachesexactly one agent, alwaysevery chat agent in the workspace
Per-user choicenone — agentId naming a different agent is 403, agentIds is 403agentId for one, agentIds for a subset (max 50)
Agent pickernever shown, nothing to pickshown, limited to what you minted
If leakedone agentevery chat agent
Extra requirement—create it with the Embedded chat purpose (Workspace settings → API Keys); a platform-access key is refused, naming its scope

One surface, one agent → agent key, and stop there. A picker, or different agents for different customers → workspace key.

// Workspace key only — narrowing what this person's token may reach.
await exchangeChatToken(frontera, {
  externalUserId: user.id,
  agentId: 'support-bot',                     // exactly one
  // or
  agentIds: ['support-bot', 'billing-bot'],   // a subset; the picker shows these
})

agentId and agentName both accept the agent's UUID or its slug; an unknown one is 404, never a silent fallback. Narrowing is enforced server-side — filtering a picker in the browser looks identical and protects nothing.

Capabilities

CapabilityDefaultOpens
(base)alwaysChat, history, own conversations, answering approvals
plugin-connectonConnecting their own account for a plugin
scheduleoffThe agent's scheduled tasks
agent-modeoffWork items, exchanges, handoffs
approval-modeoffChoosing "run without approval"

externalUserId, capabilities, agentId and ttlSeconds are privilege decisions. Take each from your own session — if the caller can name the user, any signed-in person can read someone else's conversations.

Renewal is automatic, up to a 12-hour session ceiling; after that your server mints again.


Other entry points

ImportGives you
@frontera-sdk/chat-ui/chatFronteraChat — the whole surface
@frontera-sdk/chat-ui/clientuseChatClient, ChatClient, public types — for your own sidebar
@frontera-sdk/chat-ui/oauth-callbackFronteraOAuthCallback — the connect callback page
@frontera-sdk/chat-ui/new-sessionbuildDefaultShortcuts, Shortcut
@frontera-sdk/chat-ui/message, /composer, /parts, /headerCompose your own layout
@frontera-sdk/chat-ui/themeFronteraChatTheme

Reading the conversation from your own chrome:

import { useChatClient, type Session } from '@frontera-sdk/chat-ui/client'

const { client, getClient } = useChatClient({ tokenEndpoint: '/api/chat-token' })
useEffect(() => {
  client?.listSessions({ limit: 20 }).then(p => setThreads(p.data))
}, [client])

// Share the credential instead of minting a second token for the same person:
<FronteraChat client={getClient} conversationId={openId} />

Connecting a user's own account

For plugins that act as the person (their mail, their files). Four prerequisites, all required:

  1. Register your callback URL with the provider (Google, Microsoft…).
  2. Add your own OAuth client in Frontera → Settings → Secrets. A provider only authorises redirects on a domain its owner verified, so it cannot be ours.
  3. Add the callback URL and your app's origin to the embed allowlist (POST /v1/apps/embed-allowlist, needs a workspace member with app:configure). No default, no wildcard.
  4. Mint tokens with plugin-connect.
// app/oauth/callback/page.tsx
'use client'
import { FronteraOAuthCallback } from '@frontera-sdk/chat-ui/oauth-callback'
export default function Page() { return <FronteraOAuthCallback /> }
<FronteraChat
  tokenEndpoint="/api/chat-token"
  connectRedirectUri="https://app.example.com/oauth/callback"
  appsButton
/>

Popup by default, full-page redirect when popups are blocked.


Mobile

Under 768px the component follows the platform rather than shrinking the desktop layout:

  • the side panel becomes a full-screen sheet, not a column;
  • the header is solid and in the flow, with the title, a summary sheet and a files list;
  • mobileMenu renders at the head of that bar — your session list, your component;
  • the bar stays on a new session, captioned "New Session".

Hazards

Your CSS reaches inA rule like button { padding: 8px } restyles every control in the chat. Scope yours: button:not(.fr-shell *). Portalled dialogs carry .fr-shell too.
One ReactTwo copies break hooks — Cannot read properties of null (reading 'useState'). Check npm ls react.
Height.fr-shell is height: 100%. A container without height collapses it.
CSPAllow your Frontera API origin in connect-src.

Troubleshooting

SymptomCause
Blank chat, no erroronError not wired
Nothing renders / zero heightContainer has no height
401 immediatelyToken endpoint returned the wrong shape — must be { token, expiresAt }
Chat stops after an hourUsing token without refresh; use tokenEndpoint
useState of nullTwo Reacts
Controls look wrongHost CSS styling bare elements
Connect button does nothingconnectRedirectUri missing, or URL not on the allowlist
Conversation stops with nothing to clickA parked approval — GET /v1/chat/approvals?conversationId=… with the same token

What the browser calls

For a CSP allowlist or a proxy. All against your Frontera API origin, with the cht_ token:

POST   /v1/chat                                    conversation stream (SSE)
POST   /v1/chat-tokens/renew                       refresh
GET    /v1/chat-agents                             agent picker
GET    /v1/sessions, /v1/sessions/:id              history
PATCH  /v1/sessions/:id                            title, approval mode
GET    /v1/chat/approvals?conversationId=…         parked decisions
POST   /v1/chat/approvals/:id                      answer one
POST   /v1/chat/app-connections/start|exchange|resume
POST   /v1/chat-uploads                            attachments
GET    /v1/chat-artifacts/:id[/file|/preview|/versions…]
PATCH  /v1/chat-artifacts/:id                      save an edit

版本与发布记录

@frontera-sdk/chat-ui 共发布 151 个版本,首次发布于 2026/09/15;过去 12 个月发布了 151 个版本。

版本发布日期距今
1.53.54latest2026/11/15今天
1.53.532026/11/151 天前
1.53.522026/11/141 天前
1.53.512026/11/141 天前
1.53.502026/11/141 天前
1.53.492026/11/141 天前

关于 @frontera-sdk/chat-ui 的常见问题

@frontera-sdk/chat-ui 是什么?

Ready-made React components for embedding a Frontera agent conversation — message rendering, tool calls, artifacts and composer.

如何安装 @frontera-sdk/chat-ui?

在项目目录运行 npm install @frontera-sdk/chat-ui,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^19.0.0)、react-dom(^19.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@frontera-sdk/chat-ui 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@frontera-sdk/chat-ui 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@frontera-sdk/chat-ui 可以商用吗?

@frontera-sdk/chat-ui 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@frontera-sdk/chat-ui 的下载量有多少?

根据 npm 官方下载统计,@frontera-sdk/chat-ui 在 2026/10/16 – 2026/11/14 这 30 天内被下载 15,377 次,日均约 513 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@frontera-sdk/chat-ui 还在维护吗?

最新版本 1.53.54 发布于 2026/11/15(今天);至今共发布 151 个版本,其中过去 12 个月发布了 151 个;首次发布于 2026/09/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。