API Reference
Complete API reference for Code's tRPC endpoints and SDK.
Overview
Code exposes a fully type-safe API through tRPC. All endpoints have automatic TypeScript types without code generation.
Base URL (HTTP mode): http://localhost:3000
Transport Options:
- In-process: Direct function calls (TUI)
- HTTP/SSE: Network-based (Web UI, daemon)
Client Creation
In-Process Client
import { createClient } from '@sylphx/code-client'
const client = createClient({
transport: 'in-process',
appContext: getAppContext()
})HTTP Client
import { createClient } from '@sylphx/code-client'
const client = createClient({
transport: 'http',
serverUrl: 'http://localhost:3000'
})Session API
session.list
List all sessions.
Type: Query
Input: None
Output:
interface Session {
id: string
provider: string
model: string
title?: string
createdAt: number
updatedAt: number
}
type Output = Session[]Example:
const sessions = await client.session.list.query()
// [{ id: 'abc', provider: 'openrouter', model: 'claude-3.5-sonnet', ... }]session.get
Get a single session by ID.
Type: Query
Input:
{ sessionId: string }Output:
interface SessionWithMessages extends Session {
messages: Message[]
}
type Output = SessionWithMessages | nullExample:
const session = await client.session.get.query({ sessionId: 'abc' })
// { id: 'abc', messages: [...], ... }session.create
Create a new session.
Type: Mutation
Input:
{
provider: string // e.g., 'openrouter', 'anthropic'
model: string // e.g., 'claude-3.5-sonnet'
title?: string // Optional title
}Output:
type Output = SessionExample:
const session = await client.session.create.mutate({
provider: 'openrouter',
model: 'anthropic/claude-3.5-sonnet',
title: 'My Chat'
})
// { id: 'new-id', provider: 'openrouter', ... }Events Emitted:
session-createdonsession-eventschannel
session.delete
Delete a session.
Type: Mutation
Input:
{ sessionId: string }Output:
{ success: boolean }Example:
await client.session.delete.mutate({ sessionId: 'abc' })
// { success: true }Events Emitted:
session-deletedonsession-eventschannel
session.updateTitle
Update session title.
Type: Mutation
Input:
{
sessionId: string
title: string
}Output:
{ success: boolean }Example:
await client.session.updateTitle.mutate({
sessionId: 'abc',
title: 'New Title'
})Events Emitted:
session-title-updatedonsession-eventschannel
session.updateModel
Update session AI model.
Type: Mutation
Input:
{
sessionId: string
provider: string
model: string
}Output:
{ success: boolean }Example:
await client.session.updateModel.mutate({
sessionId: 'abc',
provider: 'anthropic',
model: 'claude-3-opus'
})Events Emitted:
session-model-updatedonsession-eventschannel
session.compact
Compact session history with AI summary.
Type: Mutation
Input:
{ sessionId: string }Output:
{
success: boolean
oldSessionId: string
newSessionId: string
summary: string
}Example:
const result = await client.session.compact.mutate({ sessionId: 'abc' })
// {
// success: true,
// oldSessionId: 'abc',
// newSessionId: 'xyz',
// summary: 'Summary of conversation...'
// }Events Emitted:
session-createdonsession-eventschannel- AI streaming events on
session:${newSessionId}channel (auto-triggered)
Notes:
- Server automatically triggers AI response after compact
- Client should switch to new session
- Original session is preserved (not deleted)
Message API
message.streamResponse
Stream AI response to user message.
Type: Subscription
Input:
{
sessionId: string
content: ParsedContentPart[]
}
interface ParsedContentPart {
type: 'text' | 'image'
content?: string // For text
data?: string // For image (base64)
}Output: Observable stream of events
type StreamEvent =
| { type: 'user-message-created', messageId: string, content: ParsedContentPart[] }
| { type: 'assistant-message-created', messageId: string }
| { type: 'text-start' }
| { type: 'text-delta', text: string }
| { type: 'text-end' }
| { type: 'reasoning-start' }
| { type: 'reasoning-delta', text: string }
| { type: 'reasoning-end', duration: number }
| { type: 'tool-call', toolCallId: string, toolName: string, args: unknown }
| { type: 'tool-result', toolCallId: string, toolName: string, result: unknown, duration: number }
| { type: 'tool-error', toolCallId: string, toolName: string, error: string, duration: number }
| { type: 'complete', usage: TokenUsage, finishReason: string }
| { type: 'error', error: string }
| { type: 'abort' }Example:
const subscription = client.message.streamResponse.subscribe(
{
sessionId: 'abc',
content: [{ type: 'text', content: 'Hello!' }]
},
{
onData: (event) => {
if (event.type === 'text-delta') {
console.log('Token:', event.text)
} else if (event.type === 'complete') {
console.log('Done! Usage:', event.usage)
}
},
onError: (error) => console.error('Error:', error),
onComplete: () => console.log('Stream complete')
}
)
// Cleanup
subscription.unsubscribe()Events Emitted:
- All streaming events on both:
- Direct subscription (Path A)
session:${sessionId}channel (Path B, for multi-client sync)
Events API
events.subscribeToSession
Subscribe to session-specific events (streaming, tools, etc.).
Type: Subscription
Input:
{
sessionId: string
replayLast?: number // Number of events to replay (default: 0)
}Output: Observable stream of StreamEvent
Example:
const subscription = client.events.subscribeToSession.subscribe(
{ sessionId: 'abc', replayLast: 0 },
{
onData: (event) => {
console.log('Session event:', event)
}
}
)
subscription.unsubscribe()Use Cases:
- Multi-client synchronization
- Resuming ongoing streams
- Background event monitoring
events.subscribeToAllSessions
Subscribe to global session lifecycle events.
Type: Subscription
Input: None
Output: Observable stream of session events
type SessionEvent =
| { type: 'session-created', sessionId: string, provider: string, model: string }
| { type: 'session-deleted', sessionId: string }
| { type: 'session-title-updated', sessionId: string, title: string }
| { type: 'session-model-updated', sessionId: string, provider: string, model: string }
| { type: 'session-compacted', oldSessionId: string, newSessionId: string, summary: string }Example:
const subscription = client.events.subscribeToAllSessions.subscribe(
undefined,
{
onData: (event) => {
if (event.type === 'session-created') {
console.log('New session:', event.sessionId)
}
}
}
)Use Cases:
- Sidebar synchronization
- Dashboard updates
- Session list refresh
Config API
config.get
Get current configuration.
Type: Query
Input: None
Output:
interface Config {
ai: {
provider: string
model: string
temperature: number
maxTokens: number
}
// ... other config
}Example:
const config = await client.config.get.query()
// { ai: { provider: 'openrouter', model: '...', ... }, ... }config.update
Update configuration.
Type: Mutation
Input:
Partial<Config>Output:
{ success: boolean }Example:
await client.config.update.mutate({
ai: {
model: 'claude-3-opus'
}
})Stats API
stats.get
Get usage statistics.
Type: Query
Input:
{ sessionId?: string } // Optional: stats for specific sessionOutput:
interface Stats {
totalSessions: number
totalMessages: number
totalTokens: number
tokensIn: number
tokensOut: number
sessionStats?: {
messageCount: number
tokenCount: number
createdAt: number
}
}Example:
// Global stats
const stats = await client.stats.get.query()
// { totalSessions: 10, totalMessages: 100, ... }
// Session-specific stats
const sessionStats = await client.stats.get.query({ sessionId: 'abc' })
// { sessionStats: { messageCount: 20, tokenCount: 5000, ... } }Error Handling
Error Types
All errors use tRPC error codes:
type ErrorCode =
| 'BAD_REQUEST' // Invalid input
| 'UNAUTHORIZED' // Auth required
| 'FORBIDDEN' // No permission
| 'NOT_FOUND' // Resource not found
| 'TIMEOUT' // Request timeout
| 'INTERNAL_SERVER_ERROR' // Server error
| 'TOO_MANY_REQUESTS' // Rate limitedError Handling Example
import { TRPCClientError } from '@trpc/client'
try {
const session = await client.session.get.query({ sessionId: 'invalid' })
} catch (error) {
if (error instanceof TRPCClientError) {
console.error('Error code:', error.data?.code)
console.error('Message:', error.message)
if (error.data?.code === 'NOT_FOUND') {
// Handle not found
} else if (error.data?.code === 'INTERNAL_SERVER_ERROR') {
// Handle server error
}
}
}Type Definitions
Message
interface Message {
id: string
role: 'user' | 'assistant' | 'system'
content: ParsedContentPart[]
createdAt: number
usage?: TokenUsage
}TokenUsage
interface TokenUsage {
inputTokens: number
outputTokens: number
totalTokens: number
}ParsedContentPart
type ParsedContentPart =
| { type: 'text', content: string }
| { type: 'image', data: string, mediaType: string }
| { type: 'tool-use', toolCallId: string, toolName: string, args: unknown }
| { type: 'tool-result', toolCallId: string, result: unknown }Rate Limiting
Some operations have rate limits:
message.streamResponse: 10 requests per minute per sessionsession.compact: 5 requests per hour per session
Rate limit errors return:
{
code: 'TOO_MANY_REQUESTS',
message: 'Rate limit exceeded',
retryAfter: 60 // seconds
}Best Practices
Subscription Management
Always unsubscribe when done:
useEffect(() => {
const subscription = client.message.streamResponse.subscribe(...)
return () => subscription.unsubscribe()
}, [sessionId])Error Handling
Handle all error cases:
try {
await client.session.delete.mutate({ sessionId })
} catch (error) {
if (error instanceof TRPCClientError) {
switch (error.data?.code) {
case 'NOT_FOUND':
showError('Session not found')
break
case 'FORBIDDEN':
showError('Permission denied')
break
default:
showError('An error occurred')
}
}
}Type Safety
Let TypeScript infer types:
// ✅ Good - TypeScript infers Session type
const session = await client.session.get.query({ sessionId: 'abc' })
// ❌ Avoid - Unnecessary type cast
const session = await client.session.get.query({ sessionId: 'abc' }) as SessionRelated Documentation
- Architecture Overview - System design
- tRPC Communication - tRPC details
- Usage Guide - Practical examples