@happyvertical/smrt-chat
Chat rooms, DMs, threads, and agent conversations with tool whitelisting for the SMRT framework.
v0.20.44RoomsThreadsAgent Sessions
Overview
smrt-chat provides multi-tenant chat infrastructure with support for public and private rooms, direct messages, threaded conversations, reactions, and AI agent sessions with configurable tool access.
Installation
bash
npm install @happyvertical/smrt-chatQuick Start
typescript
import { ChatService } from '@happyvertical/smrt-chat';
const chat = await ChatService.create({
persistence: { type: 'sql', url: 'chat.db' },
});
// Create a public room (creator added as owner)
const room = await chat.createRoom({
tenantId: 'tenant-1',
name: 'General',
roomType: 'public',
createdByProfileId: 'profile-1',
});
// Send a message
await chat.sendMessage({
tenantId: 'tenant-1',
roomId: room.id,
senderProfileId: 'profile-1',
content: 'Hello, world!',
});
// Start a threaded conversation
const thread = await chat.startThread({
tenantId: 'tenant-1',
roomId: room.id,
rootMessageId: message.id,
title: 'Follow-up discussion',
});
// Reply within the thread
await chat.sendMessage({
tenantId: 'tenant-1',
roomId: room.id,
senderProfileId: 'profile-2',
content: 'Great point!',
threadId: thread.id,
});
// Get or create a DM room between two profiles
const dmRoom = await chat.getOrCreateDM({
tenantId: 'tenant-1',
profileId1: 'profile-1',
profileId2: 'profile-2',
});Core Models
ChatRoom
typescript
class ChatRoom extends SmrtObject {
name: string
roomType: 'public' | 'private' | 'dm' | 'agent'
status: string
topic?: string
maxParticipants?: number
lastMessageAt?: Date
// Tenant-scoped (required)
}ChatMessage
typescript
class ChatMessage extends SmrtObject {
roomId: string
senderProfileId: string
content: string
role: 'user' | 'assistant' | 'system' | 'tool'
messageType: 'text' | 'system' | 'action' | 'file' | 'tool_call' | 'tool_result'
threadId?: string
replyToMessageId?: string
toolCallData?: string // JSON for tool interactions
}AgentSession
typescript
class AgentSession extends SmrtObject {
agentId: string // String ref (not FK)
roomId: string
allowedTools: string // JSON string array
sessionContext: string // JSON for multi-turn memory
systemPrompt?: string
maxTokens?: number
maxMessages?: number
expiresAt?: Date
status: string
isActive(): boolean
getAllowedTools(): string[]
getSessionContext(): Record<string, any>
updateSessionContext(updates: Record<string, any>): void
}Agent Sessions
typescript
// Create an agent session (auto-creates an agent-type room)
const { session, room } = await chat.createAgentSession({
tenantId: 'tenant-1',
agentId: 'agent-summarizer',
participantProfileId: 'profile-1',
allowedTools: ['web-search', 'summarize'],
systemPrompt: 'You are a research assistant.',
maxMessages: 100,
});
// Send a message within the agent session
await chat.sendAgentMessage({
tenantId: 'tenant-1',
agentSessionId: session.id,
senderProfileId: 'profile-1',
content: 'Summarize the latest news',
role: 'user',
});
// Check session limits before allowing more messages
if (session.isActive()) {
// Session has not expired or hit token/message limits
}
// Tool whitelisting is app-controlled:
// The framework stores the allowedTools list but
// the consuming app validates before executionBest Practices
DOs
- Use
ChatServicefacade for room creation and messaging - Check
session.isActive()before sending agent messages - Use
getSessionContext()/updateSessionContext()for agent memory - Validate tool calls against
allowedToolsin your app logic - Use
getOrCreateDM()for direct message rooms
DON'Ts
- Don't use
contextfield directly (reserved for slug scoping -- usesessionContext) - Don't skip session expiry checks (expiresAt or limit-based)
- Don't rely on framework for tool validation (app responsibility)
- Don't forget tenant context (ChatRoom requires tenant scoping)
- Don't create agent rooms manually (use
createAgentSessionwhich auto-creates them)