AI agent interaction components for SpaceUI.
bun add @spacedrive/ai @spacedrive/primitives
# or
npm install @spacedrive/ai @spacedrive/primitivesPeer dependencies:
react^18.0.0 || ^19.0.0react-dom^18.0.0 || ^19.0.0@tanstack/react-query^5.0.0@tanstack/react-virtual^3.0.0
Optional dependencies:
@react-sigma/core,sigma,graphology(for MemoryGraph)@dnd-kit/core,@dnd-kit/sortable(for TaskBoard drag-and-drop)
import { ToolCall, Markdown, ChatComposer, TaskCard, ConnectionStatus } from '@spacedrive/ai';
import type { ToolCallPair, TaskInfo } from '@spacedrive/ai';
function AgentInterface() {
const toolCall: ToolCallPair = {
id: '1',
name: 'file_read',
argsRaw: '{"path": "/docs/readme.md"}',
args: { path: '/docs/readme.md' },
resultRaw: '{"content": "# Hello"}',
result: { content: '# Hello' },
status: 'completed',
};
const task: TaskInfo = {
id: '1',
title: 'Review code',
status: 'in_progress',
priority: 'high',
assignees: ['agent-1'],
};
return (
<div className="space-y-4">
<ConnectionStatus status="connected" />
<ToolCall toolCall={toolCall} expanded />
<TaskCard task={task} />
</div>
);
}Display tool invocations with arguments and results:
import { ToolCall } from '@spacedrive/ai';
import type { ToolCallPair } from '@spacedrive/ai';
const toolCall: ToolCallPair = {
id: '1',
name: 'search_files',
argsRaw: '{"query": "*.ts"}',
args: { query: '*.ts' },
resultRaw: '{"count": 42}',
result: { count: 42 },
status: 'completed', // 'running' | 'completed' | 'error'
};
<ToolCall
toolCall={toolCall}
expanded={true} // Show full details
onToggle={() => {}} // Toggle expansion
/>Render agent responses with proper styling:
import { Markdown } from '@spacedrive/ai';
<Markdown content={`# Response
Here's what I found:
- Item 1
- Item 2
\`\`\`typescript
const x = 1;
\`\`\`
`} />Features:
- GitHub Flavored Markdown
- Syntax highlighting ready
- Semantic color styling
- Raw HTML support
Message input with model selection:
import { useState } from 'react';
import { ChatComposer, ModelSelect } from '@spacedrive/ai';
import type { ModelOption } from '@spacedrive/ai';
const models: ModelOption[] = [
{
id: 'gpt-4',
name: 'GPT-4',
provider: 'OpenAI',
context_window: 8192,
capabilities: ['tools', 'reasoning'],
},
];
function Chat() {
const [message, setMessage] = useState('');
const [model, setModel] = useState('gpt-4');
return (
<ChatComposer
value={message}
onChange={setMessage}
onSend={() => console.log('Send:', message)}
models={models}
selectedModel={model}
onModelChange={setModel}
onVoiceClick={() => console.log('Voice input')}
/>
);
}LLM model picker with search:
<ModelSelect
models={models}
value={selectedModel}
onChange={setSelectedModel}
placeholder="Select a model..."
/>Individual task display:
import { TaskCard } from '@spacedrive/ai';
import type { TaskInfo } from '@spacedrive/ai';
const task: TaskInfo = {
id: '1',
title: 'Implement feature',
status: 'in_progress',
priority: 'high',
assignees: ['agent-1', 'agent-2'],
conversation_id: 'conv-123',
};
<TaskCard
task={task}
onClick={() => console.log('Open task')}
/>Kanban board with columns:
import { TaskBoard } from '@spacedrive/ai';
const tasks: TaskInfo[] = [
{ id: '1', title: 'Task 1', status: 'backlog', priority: 'medium', assignees: [] },
{ id: '2', title: 'Task 2', status: 'in_progress', priority: 'high', assignees: [] },
];
<TaskBoard
tasks={tasks}
onTaskMove={(taskId, newStatus) => {
console.log(`Move ${taskId} to ${newStatus}`);
}}
onTaskClick={(task) => {
console.log('Clicked:', task);
}}
/>List view of agent memories:
import { MemoryList } from '@spacedrive/ai';
import type { MemoryInfo } from '@spacedrive/ai';
const memories: MemoryInfo[] = [
{
id: '1',
type: 'conversation',
content: 'Discussed architecture...',
source: 'Slack',
edges: [{ target: '2', relation: 'related' }],
},
];
<MemoryList
memories={memories}
onMemoryClick={(memory) => console.log(memory)}
onMemoryDelete={(memory) => console.log('Delete:', memory.id)}
/>Graph visualization (lazy-loaded):
import { MemoryGraph } from '@spacedrive/ai';
<MemoryGraph
memories={memories}
onNodeClick={(memory) => console.log(memory)}
/>Agent switching dropdown:
import { AgentSelector } from '@spacedrive/ai';
import type { AgentInfo } from '@spacedrive/ai';
const agents: AgentInfo[] = [
{ id: '1', name: 'Dev Assistant', detail: 'Code review', status: 'online' },
{ id: '2', name: 'Doc Writer', detail: 'Documentation', status: 'busy' },
];
<AgentSelector
agents={agents}
value={selectedAgent}
onChange={setSelectedAgent}
/>Deterministic avatar from seed:
import { ProfileAvatar } from '@spacedrive/ai';
// Generates gradient based on seed
<ProfileAvatar seed="user123" name="John Doe" size="md" />
// With image
<ProfileAvatar
seed="user456"
name="Jane Smith"
imageUrl="/avatars/jane.jpg"
size="lg"
/>Connection state indicator:
import { ConnectionStatus } from '@spacedrive/ai';
<ConnectionStatus status="connected" />
<ConnectionStatus status="connecting" />
<ConnectionStatus status="offline" />
<ConnectionStatus status="error" />Cron job management:
import { CronJobList } from '@spacedrive/ai';
import type { CronJobInfo } from '@spacedrive/ai';
const jobs: CronJobInfo[] = [
{
id: '1',
name: 'Daily backup',
schedule: '0 2 * * *',
last_run: '2024-03-24T02:00:00Z',
next_run: '2024-03-25T02:00:00Z',
status: 'active',
},
];
<CronJobList
jobs={jobs}
onCreateJob={() => console.log('Create job')}
onToggleJob={(id, enabled) => console.log(id, enabled)}
/>Autonomy level control:
import { AutonomyPanel } from '@spacedrive/ai';
<AutonomyPanel
currentLevel="assisted"
onLevelChange={(level) => console.log('New level:', level)}
pendingApprovals={[
{ id: '1', action: 'Delete file', reason: 'User request', timestamp: '2024-03-25T10:00:00Z' },
]}
onApprove={(id) => console.log('Approve:', id)}
onDeny={(id) => console.log('Deny:', id)}
/>All components export their prop types:
import type {
ToolCallPair,
ToolCallStatus,
TranscriptStep,
TaskInfo,
MemoryInfo,
CronJobInfo,
AgentInfo,
ModelOption,
} from '@spacedrive/ai';Pairs action and result steps for display:
import { pairTranscriptSteps } from '@spacedrive/ai';
import type { TranscriptStep } from '@spacedrive/ai';
const steps: TranscriptStep[] = [
{ type: 'action', call_id: '1', name: 'read', content: [], text: '' },
{ type: 'tool_result', call_id: '1', name: 'read', content: [], text: 'Done' },
];
const pairs = pairTranscriptSteps(steps);
// [[action, result], [null, null], ...]- Data via props - No internal data fetching
- Callbacks for events -
onSend,onCancel, etc. - Layout-agnostic - Use flex/grid, container constrains
- Lazy-loadable - Heavy components code-split
- Types co-located - Export prop interfaces
MIT © Spacedrive