What's new
@sinups/ai-kit grows from a set of chat components into a kit for agent products. This release adds the screens around the chat, shared primitives, a theme provider, an embeddable launcher and a Storybook-based test setup. Existing chat components keep their props and gain new optional ones, with one exception: the InputBar question bar changed its callbacks. Installation changes: icons become a peer dependency and the peer ranges move to Mantine 9.4 and React 19.2.
Unreleased
quietPresentationreads the formatters of the host like the cards and rows do: the line of a call takes its outcome fromtoolOutputs, a failure included, and the opened details show the arguments astoolArgswrote them;nullkeeps the summary of the kit. A call thattoolCatalogdescribes goes quiet too, so a local tool of the host without a server reads like an MCP call; built-in tools without an entry keep their cards.- The new messages button shows only while there is content below the viewport, in every
sendScrollmode, and never for a single frame during a send. A message whose id the host replaced — an optimistic question or a placeholder reply saved under its real id — is the same message, not a new one, and a replaced question id no longer counts as a send. sendScroll="prompt-top-hold"puts a sent question at the top likeprompt-topand keeps the list still while the answer grows. The list follows the answer again only after the user scrolls to the bottom with the wheel, touch, keyboard or scrollbar, or presses the new messages button; scrolls of the list itself and growth of the answer never attach it. The button shows once the answer reaches below the viewport and counts the growing answer as one new message.prompt-topkeeps its behavior.partRenderers.textreplaces the Markdown of an answer andpartRenderers.reasoningshows reasoning parts, which stay hidden without a renderer. Planning, the working row and copying keep reading thetextparts.- The folded line of the quiet presentation takes its time from the host for a turn restored from history: from the first reported start to the last end of its calls (
callProviderMetadata.custom.startedAtorstartedAt, plustotalDurationMs,durationorduration_msof the output, elsecallProviderMetadata.custom.durationMsorcustom.endedAtminus the start), or the sum of the reported durations. A run seen live keeps its measured time. - The
ThinkingToolrow of a thought restored from history readsThought for 6sby the same reports of the host instead of a bareThought; a thought seen live keeps its measured time. Since the text of a thought is usually its output, give its time incallProviderMetadata.custom:startedAtwithdurationMsorendedAt.
0.4.0
- MCP calls are read strictly by the MCP specification. A result is the
CallToolResult(or its bare content array):structuredContentby theoutputSchemaof the catalog, else the content blocks by type — text as it is, images and audio as a preview and a player, resources as chips,isErroras a failure. The kit no longer guesses: text is not parsed as JSON, lists are counted only when the schema declares an array, envelope and pagination keys mean nothing, dates are dates only byformat. Arguments followinputSchemaand are no longer unfolded from JSON strings (unfoldToolArgsis deprecated). Annotation badges apply the defaults of the specification: withoutreadOnlyHinta tool may be destructive and reach an open world. - What stays: custom
toolRenderersandtoolOutputsformatters getoutputwith the same value as in 0.3 — for an MCP tool the text of its content, parsed when it holds JSON — andunwrapToolOutput,unwrapMcpOutputandgetToolOutputValuereturn what they returned. New next to it:result, theCallToolResultof the call, andstructuredContenton renderers. Preferresult;outputis kept for existing code. What changes is only what the kit shows by itself, as described above. - The dark theme follows the Mantine dark scale: the page is the host body color (
--mantine-color-body) instead of near-black, the composer, tool cards and code blocks sit one step above it ondark-6, the header of a code block is only a shade lighter than its body, borders are softer, and the switch and progress track is a little darker. White text on accent and danger fills keeps at least 4.5:1. Dark screenshots change accordingly. partRenderersonAgentChatandMessageListrenders your own part types, keyed bypart.type; parts without a renderer stay hidden as before.draftandonDraftChangecontrol the composer text ofAgentChat, for example to ask about a selection; without them the chat keeps its own draft.messageActions.actionsandMessageActionsactionsadd your buttons to the message toolbar;MessageActionButtongives them the look of the built-in ones.sendScroll="prompt-top"puts a sent question at the top and grows the answer under it;streamingCaretshows a caret after the growing text;lazyTurnsskips layout of finished turns out of view. All are off by default.- The transcript is a
logthat is busy while an answer streams, and screen readers hearlabels.answerReady,labels.answerFailedorlabels.answerStoppedonce when it ends. MarkdownLinksProvider(oronLinkClickandlinkSchemesonMarkdown) handles link clicks and allows schemes such asartifact:;javascript:,vbscript:,data:,blob:andfile:links render as text.MediaPart:createMediaPartRenderers()showsfileparts of an answer as an image with a preview, a native audio or video player, or a file chip.ArtifactPanelwithuseArtifactPanelopens an artifact next to the chat or in a drawer, andcreateArtifactPartRendererrendersartifactparts as cards that open it.FileAttachmentshows uploads:status,progress,error,onCancelandonRetry.InputBarpasses them through withonCancelFileandonRetryFileand keeps Send off while a file uploads.filterFilesanduseFileIntakeapply oneaccept,maxFilesandmaxFileSizepolicy to picked, pasted and dropped files;ChatDropZonetakes files dropped anywhere on the chat.- Voice controls that leave audio to you:
MicButton,VoiceLevelandSpeakingIndicator. CommandTogglespins commands to the composer, andStarterCategoriesgroups starter questions inemptyState.content, a new slot for host content in an empty chat.
0.3.0
New props of AgentChat and MessageList, the composer and the mode picker. Some defaults of AgentChat changed as well; see Changed defaults in 0.3.0.
presentation:'cards'by default,rowsPresentationfor the flat transcript of a terminal client,quietPresentationfor muted MCP lines and one folded line per turn such asThought · used 2 tools · 26s. Both are imported values, so they reach the bundle only when used.toolCatalog(tool definitions keyed bymcp__<server>__<tool>:title,description,annotations,inputSchema),toolArgsandtoolOutputsformatters, andlocalefor numbers and dates in tool arguments and results.approvalsattaches Allow/Deny to any call bytoolCallId;ToolApprovalsProviderdoes the same for a standaloneMessageList, andlabels.toolApproval.scopesnames the scope in the settled line.labelscovers the whole chat by section, includingdurationUnits,thinkingTool,messageList.toolRunsandmessageList.planning;ChatLabelsProviderdoes the same for a standalone list.evenSpacing,workingRow,toolActivity,animateAppearanceandframeBatchedcontrol spacing, the working line, the timer of running calls, the fade-in of new parts and how often the streaming answer commits.ThinkingToolshowsThinkingwith a timer while the part streams andThought for 4safter it; both texts are labels.InputBar:contextItemsshows context chips above the text, for example the open document;onRemoveContextadds a remove button to each chip andonRestoreContextshows removed items as a line that brings them back.ModeSelector:labels.titleadds a heading to the open menu, a mode'sbadgemarks it, for exampleDefault, andshortcutspicks a mode by its digit while the menu is open.
Changed defaults in 0.3.0
These change what an existing app renders without code changes. Each one can be undone.
AgentChatnow turns onframeBatched,animateAppearance,workingRowandtoolActivityby default. A standaloneMessageListkeeps all four off. To get the old chat, passframeBatched={false},animateAppearance={false},workingRow={false}andtoolActivity={false}.ToolRowBaseandAgentStatuskeep a status on screen for at least 600ms before a newer one replaces it, so a step that ends in a few frames does not flash.minStatusMs={0}switches back to immediate updates.- Durations from one hour up read
1h 5minstead of65m 3s. The units come fromlabels.durationUnits. --ae-user-message-y, the vertical padding of the user bubble, is 6px instead of 10px, also withoutAiKitProvider. Set the token to10pxon an ancestor to keep the old bubble.- Timers in status rows share one animation frame loop and stop while the tab is hidden; they catch up when it becomes visible again.
- Types:
InputBarLabelshas new required keys (send,stop,closeInfoBar,attach,attachment,question,questionHeader,removeContext,restoreContext), andToolApprovalLabelshasscopes. This only affects code that declares a full labels object; thelabelsprops takePartialobjects and need no change. - Package: type declarations are split into
index.d.mtsforimportandindex.d.tsforrequire.
Overview
- New components follow one contract: controlled props, async callbacks with pending and error states, loading, error and empty states, and layout by their own width. See Architecture.
- Logic is exported as pure functions and hooks. See Hooks and utilities.
- Page recipes for full-page chat, sidebars, inspectors and settings are on Layouts.
Breaking changes
@tabler/icons-reactmoved from dependencies to peer dependencies, so the kit no longer installs its own copy. Add@tabler/icons-reactto your dependencies.- Peer ranges now match what the kit is built on:
@mantine/coreand@mantine/hooks9.4 or later,reactandreact-dom19.2 or later,@tabler/icons-react3. - The package declares
sideEffects, so bundlers keep only the components you import. Keep importing@sinups/ai-kit/styles.cssonce at the app root. InputBar:questionBar.onSubmitnow runs for every answered question and receives the 1-based{ questionIndex }as a second argument. Skipping no longer arrives as an answer withkind: "skip": it callsquestionBar.onSkip({ questionIndex })and closes the panel.
npm install @sinups/ai-kit @mantine/core @mantine/hooks @tabler/icons-react// Before (0.1.x): one callback for answers and for skipping
<InputBar
questionBar={{
id: "deploy",
questions,
onSubmit: (answer) => {
if (answer.kind === "skip") closePanel();
else saveAnswer(answer);
},
}}
/>
// After (0.2.0): onSubmit gets the 1-based question index, skipping has its own callback
<InputBar
questionBar={{
id: "deploy",
questions,
onSubmit: (answer, { questionIndex }) => saveAnswer(questionIndex, answer),
onSkip: ({ questionIndex }) => closePanel(questionIndex),
}}
/>Package
- Styles can be imported per component, like
@mantine/core/styles/Button.css:@sinups/ai-kit/styles/base.cssonce, thenstyles/Wizard.css,styles/AgentChat.cssand so on. Each file includes the styles of the components it renders.styles.csswith everything stays. See Installation and Bundle size. - Projects with
moduleResolution: node16in ESM mode now get the kit types instead ofany. - Source maps are no longer published: the unpacked package is 3.4 MB instead of 13.9 MB, the tarball 0.72 MB instead of 1.77 MB.
Deprecations
AgentChat emptySuggestionsPosition is deprecated. Suggestions always render above the composer, and "bottom" behaves as "top". InputBar places suggestion pills above the field as well. Remove the prop; for a centered start screen use emptyState with layout: "center".
// Before
<AgentChat {...chat} suggestions={suggestions} emptySuggestionsPosition="bottom" />
// After: suggestions render above the composer
<AgentChat {...chat} suggestions={suggestions} />
// Or a centered greeting with pills above the composer
<AgentChat {...chat} emptyState={{ layout: "center", title: "What should we work on?", suggestions }} />ToolApprovalFooter approval.approveLabel and approval.rejectLabel are deprecated. Both still work; the button texts now live with the rest of the footer texts in labels.approve and labels.reject.
// Before
<BashTool {...step} approval={{ onApprove, onReject, approveLabel: "Run it", rejectLabel: "No" }} />
// After: button texts sit with the other footer texts
<BashTool
{...step}
approval={{ onApprove, onReject, labels: { approve: "Run it", reject: "No" } }}
/>New modules
WizardSettingsLayoutSettingsModalMasterDetailEntityListCommandPaletteConfirmDialogKeyValueEditorSchemaViewStatusBadgeShortcutHintValidationErrorsListInvalidSettingsNoticeDomain-neutral building blocks: multi-step flows with validation (WizardModal, useWizard), settings screens (SettingsSection, SettingRow), list and detail panes, searchable lists (EntityListItem), a Mod+K palette (useFuzzySearch) and one status vocabulary.
McpSettingsPanelMcpServerListMcpServerDetailMcpToolDetailMcpServerWizardMcpImportDialogMcpDiscoveredServersMcpConfigWarningsMcpToolAnnotationBadgesMcpTransportIconServers over stdio, HTTP and SSE with status, reconnect and authentication, tools, resources and prompts, an add and edit wizard (McpServerWizardModal), import from another client and approval of servers found in project configuration.
AgentsSettingsPanelAgentListAgentDetailAgentEditorAgentCreateWizardToolSelectorAgentAvatarAgentIdentityFieldsAgent definitions: list, detail, editor, creation wizard and tool selection. The form fields are exported separately (AgentPromptField, AgentModelFields, AgentColorPicker).
SkillsSettingsPanelSkillCatalogSkillDetailSkillEditorSkillPickerSkill catalog by source, detail, an editor with validation and a multi-select picker for forms such as an agent editor.
PermissionRulesPanelAddPermissionRuleWizardPermissionRuleInputPermissionModeSelectorAllow, ask and deny rules with search, counts, a warning for broad rules and a rule wizard.
HooksPanelHookWizardLifecycle hooks grouped by event, with an enable switch per hook, matcher validation and payload examples.
MemoryPanelMemoryFileDetailMemory files by scope with search, a Markdown preview and an inline editor.
SessionListSessionPreviewExportDialogSession history grouped by date with search, pinned and archived filters, per-session actions and export.
MessageActionsEditMessageComposerFeedbackFormPlanApprovalRewindDialogToolResultNoticeMemoryNoticeCommandChipEdit, retry, rewind, branch, copy and feedback under messages. Pass them to AgentChat through messageActions.
BackgroundTasksPanelTaskListTaskDetailAgentTreeTaskStatusPillAgentMessageTaskElapsedRunning and finished tasks and subagents as a list or a tree, with detail, stop and retry. BackgroundTasksDrawer opens the panel in a drawer.
DiffReviewDiffFileListDiffFileViewDiffStatsMulti-file review with unified and split views, word highlighting, viewed marks, j/k navigation and accept or reject per file. DiffReviewModal opens it in a modal.
ModelSettingsPanelEffortSelectorOutputStylePickerUsagePanelStatusPanelCommandsHelpModel, reasoning effort and output style, token and cost usage with plan limits, an environment summary, commands and shortcuts.
New chat components
AgentStatusLive status line with elapsed time, tokens and stall detection (useStalled).ContextUsageContext window ring with warning and danger levels and a compact action.ContextBreakdownWhat fills the context window, group by group, with suggestions to free space.CompactBoundaryMarker where earlier history was replaced with a summary.TurnSummaryEnd-of-turn row: duration, tokens, running background tasks.ContextEventRowFiles, memories, skills or diagnostics pulled into context.HookActivityHooks that ran for an event, and why they blocked or failed.IdleReturnPromptOffers to keep or restart a long conversation after a break.SpendThresholdNoticeTells the user that the session spend passed a threshold.TranscriptSearchConversation search opened with Mod+F.PromptHistorySearchFuzzy search over sent prompts opened with Mod+R.PastedTextAttachmentChip for a long paste collapsed out of the composer.CodeBlockCode with optional syntax highlighting and collapsing.ShellOutputCommand output with ANSI colors, links, JSON and a tail view.MessageList renders the new message parts compaction, turn-summary, context-event and hook-activity with these components.
Changed components
AgentChat:emptyState(welcome and center layouts),emptyStateWidth,statusBar,messageActions,onRetry,onToolAction,withSearch,stickyPrompt,longMessageThreshold,highlighter,collapseToolRuns,alignComposer,topFade,wrapLines,responsiveTables,hideSuggestionsWhenNotEmpty,inputBarProps.contentWidthaccepts any CSS width.MessageList: the same feed options, plussearchOpened,onSearchOpenedChange,commands,onScrollbarWidthChange,labelsand a new-messages button.InputBar:completionsfor/and@triggers, a message queue (onQueue,queuedMessages,onRemoveQueued), collapsed long pastes (pasteCollapseThreshold), prompt history (history,historySearchHotkey,onHistorySearch),labels.ErrorMessage:variant,retrycountdown,resetsAt,collapsible,onRetry.Markdown:streamingre-parses only the growing tail;highlighter,wrapLines,responsiveTables.UserMessage: slash command chips (commands) and collapsing of long messages (longMessageThreshold).QuestionPromptandQuestionTool: option previews, notes, a review step and question progress.ToolApprovalFooter: approval scopes, risk levels, explanations, editable rule suggestions, the matched rule, the requesting agent and rejection with feedback.BashTool: run metadata and formatted output (withOutputMeta,formatOutput,commandSummary). The line budgetmaxOutputLinesis a prop ofBashToolTerminalCard, the card BashTool renders.EditToolandDiffView: word highlighting, line wrapping and syntax highlighting.TodoTool: blockers, owners andmaxVisible.ToolRenderer: custom renderers keyed by the full part type (tool-Bash) override built-in cards and report actions throughonAction.
Theming
AiKitProvider applies the kit theme to a subtree inside your MantineProvider, with accent, radius, density and colorScheme settings, tokens and persistence. AiKitThemeCustomizer is a ready settings panel. createAiKitTheme and mergeAiKitTheme cover a global setup. The kit theme keeps destructive buttons readable (--ae-danger-fill), shows scrollbars on hover, styles field labels, Kbd, code blocks and Stepper, and opens dialogs with focus inside the body. See Theming.
Launcher
ChatLauncher is a floating chat button and panel. mountChatLauncher mounts it on any page in a shadow root. See Embedding the launcher.
Testing
- Stories whose names end with
Flowcarryplayfunctions that drive the component and check callbacks and the visible result.yarn test:storybookruns them together with a render check of every story. yarn test:visual:baselinerecords light and dark screenshots from a Storybook built frommain, andyarn test:visualcompares the branch with them. Baselines are not stored in git.