Agent Panel

Pro

Mention an agent in a conversation and open it beside the chat: what it is doing right now, its plan, and everything it has done in the thread.

launch-week

3 people, 3 agents
Priya Shah
Trial churn jumped last month. Can someone dig in before Thursday's review?
You
can you take this? Trials from the last 30 days.
On it. I'll post the summary here when it's ready.
Found 9 duplicate charges from the billing change. The refund batch is ready and needs an approval.
Moved the 6 Lisbon shipments to Monday and let the customers know.
Daniel Roth
Thanks . who signs off on refunds this week?

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add agent-panel

Usage#

The panel fetches nothing. Keep each agent's state where you keep the conversation, render mentions with splitAgentMentions, and give every agent a view. Pair it with PromptInputMentions so typing @ offers the agents in the composer.

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

On a narrow screen#

The split reads its own width, not the window's. Under 44rem the panel slides over the chat and the close button brings the thread back, with focus on the mention you tapped.

launch-week

3 people, 3 agents
Priya Shah
Trial churn jumped last month. Can someone dig in before Thursday's review?
You
can you take this? Trials from the last 30 days.
On it. I'll post the summary here when it's ready.
Found 9 duplicate charges from the billing change. The refund batch is ready and needs an approval.
Moved the 6 Lisbon shipments to Monday and let the customers know.
Daniel Roth
Thanks . who signs off on refunds this week?

Anatomy#

import {  AgentSplit,  AgentSplitMain,  AgentMention,  AgentPanel,  AgentPanelView,  AgentPanelHeader,  AgentPanelHeading,  AgentPanelTitle,  AgentPanelDescription,  AgentPanelClose,  AgentPanelBody,  AgentPanelNow,  AgentPanelRequest,  AgentPanelSection,  AgentPanelSectionTitle,  AgentPanelLog,  AgentPanelLogItem,  AgentPanelFooter,} from "@/components/ui/agent-panel"<AgentSplit>  <AgentSplitMain>    <AgentMention agent="scout" />  </AgentSplitMain>  <AgentPanel>    <AgentPanelView value="scout">      <AgentPanelHeader>        <AgentPanelHeading>          <AgentPanelTitle />          <AgentPanelDescription />        </AgentPanelHeading>        <AgentPanelClose />      </AgentPanelHeader>      <AgentPanelBody>        <AgentPanelSection>          <AgentPanelSectionTitle />          <AgentPanelRequest />          <AgentPanelNow />        </AgentPanelSection>        <AgentPanelSection>          <AgentPanelSectionTitle />          <AgentPanelLog>            <AgentPanelLogItem />          </AgentPanelLog>        </AgentPanelSection>      </AgentPanelBody>      <AgentPanelFooter />    </AgentPanelView>  </AgentPanel></AgentSplit>

API reference#

AgentSplit

The split and the open agent every part reads. Two columns: the main view and the panel. The panel's column grows from nothing, so the chat narrows instead of being covered. Under 44rem of its own width the panel covers the main view and slides in from the right. Every part accepts className, merged last.

PropTypeDefaultDescription
valuestring | null-The open agent, when you control it. null closes the panel.
defaultValuestring | nullnullThe agent open on the first render.
onValueChange(agent: string | null) => void-An agent was opened, switched or closed.
panelWidthstring"24rem"The panel's width when there is room for both columns, any CSS length.
Data attributeDescription
data-openPresent while an agent is open.

AgentSplitMain

The main column, a flex column that can shrink. Drop the conversation inside.

AgentMention

A mention of an agent, a <button> that opens it in the panel, and closes it when it is already open. It returns focus here when the panel closes. Children are the name.

PropTypeDefaultDescription
agent*string-The agent's id, the value of its AgentPanelView.
variant"chip" | "plain""chip"chip sits inside message text, with the agent's mascot. plain is a bare name, for an author line.
markReact.ReactNode-The agent's face before the name, sized to the line: pass its AgentMascot with the agent's status, so the face says how it is doing. Chip only.
Data attributeDescription
data-state="open" | "closed"open while this agent is the one in the panel: the chip lifts onto the page.

AgentPanel

The panel, an <aside> that stays mounted so it can animate. It goes inert while closed, takes focus when it opens and closes on Escape.

PropTypeDefaultDescription
aria-labelstring"Agent"Names the region.
Data attributeDescription
data-state="open" | "closed"On the panel and on its sheet.

AgentPanelView

One agent's contents. Only the open agent's view renders. Switching agents with the panel open plays the new view in; closing keeps the last view on screen while it slides out.

PropTypeDefaultDescription
value*string-The agent this view belongs to.
Data attributeDescription
data-swapPresent when this view replaced another one while the panel was open.

AgentPanelHeader

The top row with a hairline under it, 44px like ChannelChatHeader so both headers line up across the split: the agent's AgentMascot (small, contained), the heading and the close button.

AgentPanelHeading

One line in the header: the name, then the role and status.

AgentPanelTitle

The agent's name, an <h2> that truncates.

AgentPanelDescription

The muted text after the name, for the role and the status. Truncates before the name does.

AgentPanelClose

A ghost icon button that closes the panel. Takes every Button prop.

PropTypeDefaultDescription
labelstring"Close"The accessible name.

AgentPanelBody

The only scrolling region, a column of sections on whitespace. Lead it with the agent's properties (a DescriptionList split 50/50: who asked, when it started, what it can reach), then one section for the job that matters now, then its earlier work.

AgentPanelNow

What the agent is doing this second, as one flat row: the status dot, the line, and the elapsed time on the right in tabular figures while it works. Announced politely; a new line rises in when the text changes. Use it for the outcome too, with status="done".

PropTypeDefaultDescription
status"working" | "waiting" | "idle" | "done" | "failed" | "cancelled""working"Sets the dot.
sincenumber-When the current task started, in epoch ms. Turns the timer on.
untilnumber-Freezes the timer at this time.
labelReact.ReactNode-A muted word before the line, when the section title does not already say it.
activityKeyReact.Key-Replays the line's entrance when it changes. Defaults to the text itself.

AgentPanelRequest

What the agent was asked, quoted: the panel's one grey block. Put it first in the job section (Working on, Just finished) so the work always reads against the ask.

PropTypeDefaultDescription
authorReact.ReactNode-Who asked, in a small muted line ("You asked").
avatarReact.ReactNode-A 16px avatar before the author.
onSelect() => void-Makes the quote a button, to scroll the chat to the message it came from.

AgentPanelSection

A titled group in the body. Drop a Plan, an AgentPanelLog, files or anything else inside.

AgentPanelSectionTitle

A section's small muted title.

PropTypeDefaultDescription
countnumber-A tabular count after the title.

AgentPanelLog

What the agent has done, an <ol> that bleeds 8px so its rows' hover fill lines up with the text.

AgentPanelLogItem

One thing the agent did, or one job it handed back. Children are the title.

PropTypeDefaultDescription
descriptionReact.ReactNode-The outcome under the title. With it the row reads as a piece of work and drops the marker.
timeReact.ReactNode-When, in tabular figures at the end of the row.
iconReact.ReactNode-Replaces the small marker.
onSelect() => void-Makes the row a button, for instance to scroll to the message it produced.

AgentPanelFooter

Optional actions pinned under the body, sharing the width. Most panels need none: keep a decision (Approve) inside the section it belongs to.

splitAgentMentions

splitAgentMentions(text, agents, trigger?) cuts a message into plain strings and { agent, text } mentions of the agents you pass ({ id, name }[]), longest name first, so you can render each mention as an AgentMention.

useAgentSplit

{ agent, open, toggle, close } for a trigger of your own, a roster row or a menu item. toggle(agent, element?) remembers the element to return focus to.

agentPanelVariants

The tv recipe behind every part.

FAQ#

So the mention you clicked stays in view next to what the agent is doing. You read the thread and the agent side by side, and switching to another agent swaps the panel without moving the chat.