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

  • quietPresentation reads the formatters of the host like the cards and rows do: the line of a call takes its outcome from toolOutputs, a failure included, and the opened details show the arguments as toolArgs wrote them; null keeps the summary of the kit. A call that toolCatalog describes 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 sendScroll mode, 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 like prompt-top and 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-top keeps its behavior.
  • partRenderers.text replaces the Markdown of an answer and partRenderers.reasoning shows reasoning parts, which stay hidden without a renderer. Planning, the working row and copying keep reading the text parts.
  • 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.startedAt or startedAt, plus totalDurationMs, duration or duration_ms of the output, else callProviderMetadata.custom.durationMs or custom.endedAt minus the start), or the sum of the reported durations. A run seen live keeps its measured time.
  • The ThinkingTool row of a thought restored from history reads Thought for 6s by the same reports of the host instead of a bare Thought; a thought seen live keeps its measured time. Since the text of a thought is usually its output, give its time in callProviderMetadata.custom: startedAt with durationMs or endedAt.

0.4.0

  • MCP calls are read strictly by the MCP specification. A result is the CallToolResult (or its bare content array): structuredContent by the outputSchema of the catalog, else the content blocks by type — text as it is, images and audio as a preview and a player, resources as chips, isError as 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 by format. Arguments follow inputSchema and are no longer unfolded from JSON strings (unfoldToolArgs is deprecated). Annotation badges apply the defaults of the specification: without readOnlyHint a tool may be destructive and reach an open world.
  • What stays: custom toolRenderers and toolOutputs formatters get output with the same value as in 0.3 — for an MCP tool the text of its content, parsed when it holds JSON — and unwrapToolOutput, unwrapMcpOutput and getToolOutputValue return what they returned. New next to it: result, the CallToolResult of the call, and structuredContent on renderers. Prefer result; output is 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 on dark-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.
  • partRenderers on AgentChat and MessageList renders your own part types, keyed by part.type; parts without a renderer stay hidden as before.
  • draft and onDraftChange control the composer text of AgentChat, for example to ask about a selection; without them the chat keeps its own draft.
  • messageActions.actions and MessageActions actions add your buttons to the message toolbar; MessageActionButton gives them the look of the built-in ones.
  • sendScroll="prompt-top" puts a sent question at the top and grows the answer under it; streamingCaret shows a caret after the growing text; lazyTurns skips layout of finished turns out of view. All are off by default.
  • The transcript is a log that is busy while an answer streams, and screen readers hear labels.answerReady, labels.answerFailed or labels.answerStopped once when it ends.
  • MarkdownLinksProvider (or onLinkClick and linkSchemes on Markdown) handles link clicks and allows schemes such as artifact:; javascript:, vbscript:, data:, blob: and file: links render as text.
  • MediaPart: createMediaPartRenderers() shows file parts of an answer as an image with a preview, a native audio or video player, or a file chip.
  • ArtifactPanel with useArtifactPanel opens an artifact next to the chat or in a drawer, and createArtifactPartRenderer renders artifact parts as cards that open it.
  • FileAttachment shows uploads: status, progress, error, onCancel and onRetry. InputBar passes them through with onCancelFile and onRetryFile and keeps Send off while a file uploads.
  • filterFiles and useFileIntake apply one accept, maxFiles and maxFileSize policy to picked, pasted and dropped files; ChatDropZone takes files dropped anywhere on the chat.
  • Voice controls that leave audio to you: MicButton, VoiceLevel and SpeakingIndicator.
  • CommandToggles pins commands to the composer, and StarterCategories groups starter questions in emptyState.content, a new slot for host content in an empty chat.

Changed defaults in 0.3.0

These change what an existing app renders without code changes. Each one can be undone.

  • AgentChat now turns on frameBatched, animateAppearance, workingRow and toolActivity by default. A standalone MessageList keeps all four off. To get the old chat, pass frameBatched={false}, animateAppearance={false}, workingRow={false} and toolActivity={false}.
  • ToolRowBase and AgentStatus keep 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 5m instead of 65m 3s. The units come from labels.durationUnits.
  • --ae-user-message-y, the vertical padding of the user bubble, is 6px instead of 10px, also without AiKitProvider. Set the token to 10px on 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: InputBarLabels has new required keys (send, stop, closeInfoBar, attach, attachment, question, questionHeader, removeContext, restoreContext), and ToolApprovalLabels has scopes. This only affects code that declares a full labels object; the labels props take Partial objects and need no change.
  • Package: type declarations are split into index.d.mts for import and index.d.ts for require.

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-react moved from dependencies to peer dependencies, so the kit no longer installs its own copy. Add @tabler/icons-react to your dependencies.
  • Peer ranges now match what the kit is built on: @mantine/core and @mantine/hooks 9.4 or later, react and react-dom 19.2 or later, @tabler/icons-react 3.
  • The package declares sideEffects, so bundlers keep only the components you import. Keep importing @sinups/ai-kit/styles.css once at the app root.
  • InputBar: questionBar.onSubmit now runs for every answered question and receives the 1-based { questionIndex } as a second argument. Skipping no longer arrives as an answer with kind: "skip": it calls questionBar.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.css once, then styles/Wizard.css, styles/AgentChat.css and so on. Each file includes the styles of the components it renders. styles.css with everything stays. See Installation and Bundle size.
  • Projects with moduleResolution: node16 in ESM mode now get the kit types instead of any.
  • 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

Primitives
WizardSettingsLayoutSettingsModalMasterDetailEntityListCommandPaletteConfirmDialogKeyValueEditorSchemaViewStatusBadgeShortcutHintValidationErrorsListInvalidSettingsNotice

Domain-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.

MCP
McpSettingsPanelMcpServerListMcpServerDetailMcpToolDetailMcpServerWizardMcpImportDialogMcpDiscoveredServersMcpConfigWarningsMcpToolAnnotationBadgesMcpTransportIcon

Servers 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.

Agents
AgentsSettingsPanelAgentListAgentDetailAgentEditorAgentCreateWizardToolSelectorAgentAvatarAgentIdentityFields

Agent definitions: list, detail, editor, creation wizard and tool selection. The form fields are exported separately (AgentPromptField, AgentModelFields, AgentColorPicker).

Skills
SkillsSettingsPanelSkillCatalogSkillDetailSkillEditorSkillPicker

Skill catalog by source, detail, an editor with validation and a multi-select picker for forms such as an agent editor.

Permissions
PermissionRulesPanelAddPermissionRuleWizardPermissionRuleInputPermissionModeSelector

Allow, ask and deny rules with search, counts, a warning for broad rules and a rule wizard.

Hooks
HooksPanelHookWizard

Lifecycle hooks grouped by event, with an enable switch per hook, matcher validation and payload examples.

Memory
MemoryPanelMemoryFileDetail

Memory files by scope with search, a Markdown preview and an inline editor.

Sessions
SessionListSessionPreviewExportDialog

Session history grouped by date with search, pinned and archived filters, per-session actions and export.

Message actions
MessageActionsEditMessageComposerFeedbackFormPlanApprovalRewindDialogToolResultNoticeMemoryNoticeCommandChip

Edit, retry, rewind, branch, copy and feedback under messages. Pass them to AgentChat through messageActions.

Background tasks
BackgroundTasksPanelTaskListTaskDetailAgentTreeTaskStatusPillAgentMessageTaskElapsed

Running and finished tasks and subagents as a list or a tree, with detail, stop and retry. BackgroundTasksDrawer opens the panel in a drawer.

Diff review
DiffReviewDiffFileListDiffFileViewDiffStats

Multi-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.

Model settings and help
ModelSettingsPanelEffortSelectorOutputStylePickerUsagePanelStatusPanelCommandsHelp

Model, reasoning effort and output style, token and cost usage with plan limits, an environment summary, commands and shortcuts.

Elicitation
ElicitationForm

Forms built from MCP elicitation schemas, including URL requests.

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. contentWidth accepts any CSS width.
  • MessageList: the same feed options, plus searchOpened, onSearchOpenedChange, commands, onScrollbarWidthChange, labels and a new-messages button.
  • InputBar: completions for / and @ triggers, a message queue (onQueue, queuedMessages, onRemoveQueued), collapsed long pastes (pasteCollapseThreshold), prompt history (history, historySearchHotkey, onHistorySearch), labels.
  • ErrorMessage: variant, retry countdown, resetsAt, collapsible, onRetry.
  • Markdown: streaming re-parses only the growing tail; highlighter, wrapLines, responsiveTables.
  • UserMessage: slash command chips (commands) and collapsing of long messages (longMessageThreshold).
  • QuestionPrompt and QuestionTool: 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 budget maxOutputLines is a prop of BashToolTerminalCard, the card BashTool renders.
  • EditTool and DiffView: word highlighting, line wrapping and syntax highlighting.
  • TodoTool: blockers, owners and maxVisible.
  • ToolRenderer: custom renderers keyed by the full part type (tool-Bash) override built-in cards and report actions through onAction.

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 Flow carry play functions that drive the component and check callbacks and the visible result. yarn test:storybook runs them together with a render check of every story.
  • yarn test:visual:baseline records light and dark screenshots from a Storybook built from main, and yarn test:visual compares the branch with them. Baselines are not stored in git.