
Research
2025 Report: Destructive Malware in Open Source Packages
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.
@automattic/agenttic-ui
Advanced tools
React UI components for AI agent chat interfaces. A pure UI layer designed to work seamlessly with @automattic/agenttic-client hooks or any agent communication system.
npm install @automattic/agenttic-ui
import { useAgentChat } from '@automattic/agenttic-client';
import { AgentUI } from '@automattic/agenttic-ui';
function ChatApplication() {
const {
messages,
isProcessing,
error,
onSubmit,
abortCurrentRequest,
suggestions,
clearSuggestions,
messageRenderer,
} = useAgentChat( {
agentId: 'big-sky',
sessionId: 'my-session',
} );
return (
<AgentUI
messages={ messages }
isProcessing={ isProcessing }
error={ error }
onSubmit={ onSubmit }
onStop={ abortCurrentRequest }
suggestions={ suggestions }
clearSuggestions={ clearSuggestions }
messageRenderer={ messageRenderer }
variant="floating"
placeholder="Ask me anything..."
/>
);
}
For complete control over layout and component ordering:
import { AgentUI } from '@automattic/agenttic-ui';
function CustomChatLayout() {
return (
<AgentUI.Container
messages={ messages }
isProcessing={ isProcessing }
error={ error }
onSubmit={ onSubmit }
onStop={ abortCurrentRequest }
variant="embedded"
>
<AgentUI.ConversationView>
<AgentUI.Header />
<AgentUI.Messages />
<AgentUI.Footer>
<AgentUI.Notice />
<AgentUI.Input />
</AgentUI.Footer>
<AgentUI.Suggestions />
</AgentUI.ConversationView>
</AgentUI.Container>
);
}
External control of input value and changes:
function ExternallyControlledChat() {
const [ inputValue, setInputValue ] = useState( '' );
return (
<AgentUI
messages={ messages }
isProcessing={ isProcessing }
onSubmit={ onSubmit }
inputValue={ inputValue }
onInputChange={ setInputValue }
variant="embedded"
/>
);
}
AgentUI - Convenience wrapper with default layout AgentUI.Container - Root container with state management and context AgentUI.ConversationView - Conversation layout wrapper AgentUI.Header - Chat header with close/expand buttons AgentUI.Messages - Message history display AgentUI.Footer - Footer wrapper for input and notice AgentUI.Input - Text input with auto-resize AgentUI.Notice - Error and notification display AgentUI.Suggestions - Quick action suggestions
interface AgentUIProps {
// Core chat data
messages: Message[];
isProcessing: boolean;
error?: string | null;
onSubmit: ( message: string ) => void;
onStop?: () => void;
// UI configuration
variant?: 'floating' | 'embedded';
placeholder?: string | string[];
triggerIcon?: React.ReactNode;
notice?: NoticeConfig;
emptyView?: React.ReactNode;
// Chat state management (floating variant)
floatingChatState?: ChatState;
onOpen?: () => void;
onExpand?: () => void;
onClose?: () => void;
// Suggestions
suggestions?: Suggestion[];
clearSuggestions?: () => void;
// Message rendering
messageRenderer?: ComponentType< { children: string } >;
// Controlled input (optional)
inputValue?: string;
onInputChange?: ( value: string ) => void;
// Styling
className?: string;
style?: React.CSSProperties;
}
Place suggestions anywhere in your layout:
<AgentUI.Container {...props}>
<AgentUI.ConversationView>
<AgentUI.Messages />
<AgentUI.Suggestions /> {/* Above input */}
<AgentUI.Footer>
<AgentUI.Input />
</AgentUI.Footer>
</AgentUI.ConversationView>
</AgentUI.Container>
// Or below input:
<AgentUI.Container {...props}>
<AgentUI.ConversationView>
<AgentUI.Messages />
<AgentUI.Footer>
<AgentUI.Input />
</AgentUI.Footer>
<AgentUI.Suggestions /> {/* Below input */}
</AgentUI.ConversationView>
</AgentUI.Container>
Use individual components for complete customization:
import {
Messages,
Message,
ChatInput,
Suggestions,
} from '@automattic/agenttic-ui';
function FullyCustomChat() {
return (
<div className="my-chat-container">
<Messages
messages={ messages }
messageRenderer={ messageRenderer }
/>
<ChatInput
value={ inputValue }
onChange={ setInputValue }
onSubmit={ onSubmit }
placeholder="Type a message..."
isProcessing={ isProcessing }
/>
<Suggestions
suggestions={ suggestions }
onSuggestionClick={ onSubmit }
onClear={ clearSuggestions }
/>
</div>
);
}
Stop button appears automatically during processing:
<AgentUI
isProcessing={ isProcessing }
onStop={ abortCurrentRequest }
// Submit button becomes stop button when processing
/>
Content items can have different types that determine how they're displayed:
type: 'text' - Normal text content (visible)type: 'image_url' - Image content (visible)type: 'component' - React component (visible)type: 'context' - Context information sent as text to the agent but hidden from UI// Example: Mixing visible and context content
const messages = [
{
id: '1',
role: 'user',
content: [ { type: 'text', text: 'Take me to the dashboard' } ],
timestamp: Date.now(),
archived: false,
showIcon: true,
},
{
id: '2',
role: 'user',
content: [
{
type: 'context', // Hidden from UI, sent to agent for context
text: 'Navigation completed. Dashboard loaded successfully.',
},
],
timestamp: Date.now(),
archived: false,
showIcon: true,
},
{
id: '3',
role: 'agent',
content: [
{ type: 'text', text: "I've taken you to the dashboard." },
],
timestamp: Date.now(),
archived: false,
showIcon: true,
},
];
<AgentUI messages={ messages } />;
// Only messages 1 and 3 will be visible (message 2 has only context content)
// Example: Message with both visible and context content
const mixedMessage = {
id: '4',
role: 'user',
content: [
{ type: 'text', text: 'Here are your analytics' },
{ type: 'context', text: 'page: /analytics, loaded: true' },
],
timestamp: Date.now(),
archived: false,
showIcon: true,
};
// The context content will be filtered out, only "Here are your analytics" is visible
import { ReactMarkdown } from 'react-markdown';
const customRenderer = ( { children }: { children: string } ) => (
<ReactMarkdown remarkPlugins={ [ remarkGfm ] }>{ children }</ReactMarkdown>
);
<AgentUI messageRenderer={ customRenderer } />;
For floating variant, control state externally:
const [ chatState, setChatState ] = useState< ChatState >( 'collapsed' );
<AgentUI
variant="floating"
floatingChatState={ chatState }
onOpen={ () => setChatState( 'compact' ) }
onExpand={ () => setChatState( 'expanded' ) }
onClose={ () => setChatState( 'collapsed' ) }
/>;
Manages floating chat state:
const {
state, // 'collapsed' | 'compact' | 'expanded'
setState,
isOpen, // boolean
open, // () => void
close, // () => void
toggle, // () => void
} = useChat( initialState );
Manages input state with auto-resize:
const { value, setValue, clear, textareaRef, handleKeyDown, adjustHeight } =
useInput( {
value: inputValue,
setValue: setInputValue,
onSubmit: handleSubmit,
isProcessing: false,
} );
interface Message {
id: string;
role: 'user' | 'agent';
content: Array< {
type: 'text' | 'image_url' | 'component' | 'context';
text?: string;
image_url?: string;
component?: React.ComponentType;
componentProps?: any;
} >;
timestamp: number;
archived: boolean;
showIcon: boolean;
icon?: string;
actions?: MessageAction[];
disabled?: boolean;
}
interface MessageAction {
id: string;
icon?: React.ReactNode;
label: string;
onClick: ( message: Message ) => void | Promise< void >;
tooltip?: string;
disabled?: boolean;
pressed?: boolean;
showLabel?: boolean;
}
interface Suggestion {
id: string;
label: string;
prompt: string;
}
interface NoticeConfig {
icon?: React.ReactNode | null | false;
message: string;
action?: {
label: string;
onClick: () => void;
};
dismissible?: boolean;
onDismiss?: () => void;
}
type ChatState = 'collapsed' | 'compact' | 'expanded';
import '@automattic/agenttic-ui/index.css';
All styles are scoped to .agenttic class to prevent conflicts.
Override CSS custom properties:
.agenttic {
--color-primary: #your-brand-color;
--color-background: #ffffff;
--color-foreground: #000000;
}
.agenttic [data-slot='chat-footer'] {
--color-background: oklch( 1 0 0 );
--color-primary: #your-brand-color;
}
Pre-built icon components:
import {
ThumbsUpIcon,
ThumbsDownIcon,
CopyIcon,
StopIcon,
ArrowUpIcon,
XIcon,
BigSkyIcon,
StylesIcon,
} from '@automattic/agenttic-ui';
# Build the package
pnpm build
# Run in development mode
pnpm dev
# Run tests
pnpm test
# Type checking
pnpm type-check
# Start Storybook
pnpm storybook
import { useAgentChat } from '@automattic/agenttic-client';
import { AgentUI } from '@automattic/agenttic-ui';
function App() {
const agentProps = useAgentChat( {
agentId: 'big-sky',
} );
return <AgentUI { ...agentProps } variant="floating" />;
}
The useAgentChat hook returns props that match the AgentUI interface.
FAQs
UI components for the Agenttic framework
The npm package @automattic/agenttic-ui receives a total of 1,061 weekly downloads. As such, @automattic/agenttic-ui popularity was classified as popular.
We found that @automattic/agenttic-ui demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 52 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.

Research
/Security News
A five-month operation turned 27 npm packages into durable hosting for browser-run lures that mimic document-sharing portals and Microsoft sign-in, targeting 25 organizations across manufacturing, industrial automation, plastics, and healthcare for credential theft.