createConversationLoop
Creates a complete conversation orchestration system with agent integration and automatic tool resolution.
Overview​
createConversationLoop
is an organism-level primitive that provides full conversation flow management. It integrates an agent, handles tool execution automatically, tracks conversation analytics, and provides lifecycle event handlers. This is the highest-level conversation primitive, ideal for building complete conversational experiences.
Full Orchestration Option: The loop provides complete agent integration and automation. However, you can build equivalent functionality using lower-level primitives if you need custom behavior. The loop is convenient but not mandatory - use it for standard flows or build your own for unique requirements.
Import​
import { createConversationLoop } from "@mrck-labs/grid-core";
Function Signature​
function createConversationLoop(
options: ConversationLoopOptions
): ConversationLoop
Parameters​
options​
- Type:
ConversationLoopOptions
- Required Properties:
agent
: The agent instance to use for conversations
- Optional Properties:
handlers
: Grouped handlers for all services- Type:
GroupedHandlers
- Type:
onMessage
: Callback for each agent response- Type:
(response: AgentResponse) => Promise<void>
- Type:
onError
: Error handler- Type:
(error: Error) => Promise<void>
- Type:
onComplete
: Completion handler- Type:
(summary: any) => Promise<void>
- Type:
onProgress
: Progress callback for real-time updates- Type:
(message: ProgressMessage) => Promise<void>
- Type:
maxTurns
: Optional limit on conversation turns- Type:
number
- Type:
historyMode
: How to handle message history when sending to agent- Type:
HistoryMode
-'full' | 'none' | 'last-n'
- Default:
'full'
- Type:
historyLimit
: Number of messages to include in 'last-n' mode- Type:
number
- Default:
5
- Type:
GroupedHandlers​
The handlers
object can contain:
-
loop
- Loop-level lifecycle handlers:onConversationStarted
:(context: ConversationContext) => Promise<{ initialMessages?: ChatMessage[] } | void>
onMessageSent
:(message: string, context: ConversationContext) => Promise<void>
onResponseReceived
:(response: AgentResponse, context: ConversationContext) => Promise<void>
onConversationEnded
:(summary: ConversationSummary, context: ConversationContext) => Promise<void>
-
manager
- Manager-level handlers (see ConversationManager) -
history
- History-level handlers (see ConversationHistory) -
context
- Context-level handlers (see ConversationContext)
Return Type: ConversationLoop​
Methods​
sendMessage​
sendMessage(message: string): Promise<SendMessageResult>
Send a message and get the agent's response, automatically handling tool calls.
Parameters:
message
: The user's message
Returns: Object containing:
content
: The final response contentrole
: Always "assistant"toolCalls
: Array of tool calls made (if any)metadata
: Additional response metadata
Process:
- Adds user message to history
- Sends to agent with full conversation context
- Automatically executes any tool calls
- Returns final response after all tools complete
getMessages​
getMessages(): ChatMessage[]
Get all messages in the conversation.
Returns: Array of all messages including system prompt
getState​
getState(): Record<string, any>
Get the current conversation state.
Returns: Current state object
updateState​
updateState(key: string, value: any): Promise<void>
Update a state value.
Parameters:
key
: State key (supports dot notation)value
: Value to set
getAnalytics​
getAnalytics(): ConversationAnalytics
Get detailed conversation analytics.
Returns: Object containing:
messageCount
: Total messagesuserMessageCount
: Number of user messagesassistantMessageCount
: Number of assistant messagestoolCallCount
: Total tool calls madeturnCount
: Number of conversation turnsduration
: Total duration in millisecondsavgResponseTime
: Average response timeisActive
: Whether conversation is active
exportConversation​
exportConversation(): ConversationExport
Export the full conversation for persistence.
Returns: Object containing:
messages
: All conversation messagesstate
: Current statemetadata
: Current metadataanalytics
: Conversation analyticsexportedAt
: Export timestamp
importConversation​
importConversation(data: ConversationExport): void
Import a previously exported conversation.
Parameters:
data
: Previously exported conversation data
endConversation​
endConversation(): Promise<void>
End the conversation and trigger cleanup handlers.
Side Effects:
- Marks conversation as ended
- Triggers
onConversationEnded
handler - Prevents further messages
resetConversation​
resetConversation(): Promise<void>
Reset the conversation to start fresh.
Side Effects:
- Clears all messages except system prompt
- Resets state and metrics
- Allows new conversation to begin
setHistoryMode​
setHistoryMode(mode: HistoryMode, limit?: number): void
Set how message history is passed to the agent.
Parameters:
mode
: History mode to use'full'
: Send complete conversation history (default)'none'
: Send only the current message (amnesia mode)'last-n'
: Send only the last N messages
limit
: Number of messages for 'last-n' mode (optional)
Use Cases:
- Testing memory retrieval tools
- Reducing context size for long conversations
- Forcing agents to use external memory systems
getHistoryMode​
getHistoryMode(): { mode: HistoryMode; limit: number }
Get the current history mode configuration.
Returns: Object containing:
mode
: Current history modelimit
: Current limit for 'last-n' mode
Examples​
Basic Usage​
import { createConversationLoop, createConfigurableAgent } from "@mrck-labs/grid-core";
// Create an agent
const agent = createConfigurableAgent({
// ... agent configuration
});
// Create conversation loop
const loop = createConversationLoop({
agent
});
// Send a message and get response
const response = await loop.sendMessage("Hello! What can you help me with?");
console.log(response.content);
// Send another message - maintains context
const response2 = await loop.sendMessage("Can you explain quantum computing?");
console.log(response2.content);
With Tool Execution​
const loop = createConversationLoop({
agent: agentWithTools,
onProgress: (update) => {
console.log(`[${update.type}] ${update.message}`);
}
});
// User asks something requiring tools
const response = await loop.sendMessage("What's the weather in Paris?");
// Progress updates:
// [thinking] Processing your request...
// [tool_execution] Executing tool: get_weather
// [tool_result] Weather data retrieved
// [complete] Response ready
console.log(response.content); // "The weather in Paris is..."
With Lifecycle Handlers​
const loop = createConversationLoop({
agent,
handlers: {
loop: {
onConversationStarted: async (context) => {
console.log("New conversation started");
// Load previous messages if user has history
const userId = context.getUserId();
if (userId) {
const previousMessages = await database.messages.findMany({
where: { userId },
orderBy: { createdAt: 'desc' },
take: 10
});
return { initialMessages: previousMessages.reverse() };
}
},
onMessageSent: async (message, context) => {
await database.audit.create({
data: {
type: "user_message",
content: message,
userId: context.getUserId(),
sessionId: context.getSessionId(),
timestamp: new Date()
}
});
},
onResponseReceived: async (response, context) => {
await database.audit.create({
data: {
type: "agent_response",
content: response.content,
toolCallsCount: response.toolCalls?.length || 0,
sessionId: context.getSessionId(),
timestamp: new Date()
}
});
},
onConversationEnded: async (summary, context) => {
await database.sessions.update({
where: { id: context.getSessionId() },
data: {
endedAt: new Date(),
summary: JSON.stringify(summary),
status: "completed"
}
});
}
}
}
});
Real-time Progress Updates​
const loop = createConversationLoop({
agent,
onProgress: (update) => {
// Send to WebSocket for real-time UI updates
websocket.emit('conversation:progress', {
type: update.type,
message: update.message,
timestamp: update.timestamp,
metadata: update.metadata
});
}
});
// Progress update types:
// - "thinking": LLM is processing
// - "tool_execution": Tool is being executed
// - "tool_result": Tool execution completed
// - "error": An error occurred
// - "complete": Response is ready
State Management​
const loop = createConversationLoop({
agent,
handlers: {
context: {
onStateChanged: async (key, value) => {
await redis.hset(`session:${sessionId}:state`, key, JSON.stringify(value));
}
}
}
});
// Update state during conversation
await loop.updateState("user.name", "Alice");
await loop.updateState("conversation.topic", "technical-support");
// State is available to agent in prompts
const response = await loop.sendMessage("What's my name?");
// Agent can access state and respond: "Your name is Alice"
Export and Import​
// Export conversation for persistence
const conversationData = loop.exportConversation();
await database.conversations.create({
data: {
userId: "user_123",
exportData: JSON.stringify(conversationData),
exportedAt: conversationData.exportedAt
}
});
// Later: Import and continue conversation
const saved = await database.conversations.findFirst({
where: { userId: "user_123" },
orderBy: { exportedAt: 'desc' }
});
const newLoop = await createConversationLoop({ agent });
newLoop.importConversation(JSON.parse(saved.exportData));
// Continue where left off
const response = await newLoop.sendMessage("What were we discussing?");
Analytics and Monitoring​
const loop = createConversationLoop({ agent });
// Have a conversation
await loop.sendMessage("Hello!");
await loop.sendMessage("Can you help me with math?");
await loop.sendMessage("What's 25 * 4?");
// Get analytics
const analytics = loop.getAnalytics();
console.log(analytics);
// {
// messageCount: 6, // Including system and responses
// userMessageCount: 3,
// assistantMessageCount: 3,
// toolCallCount: 1, // Calculator was used
// turnCount: 3,
// duration: 15000, // 15 seconds
// avgResponseTime: 5000, // 5 seconds average
// isActive: true
// }
// End conversation
await loop.endConversation();
History Mode Management​
const loop = createConversationLoop({
agent,
historyMode: 'full' // Default behavior
});
// Have a conversation
await loop.sendMessage("My name is Alice");
await loop.sendMessage("I'm interested in quantum computing");
// Switch to amnesia mode - agent won't see previous messages
loop.setHistoryMode('none');
await loop.sendMessage("What's my name?");
// Agent won't know unless it uses memory tools!
// This is useful for testing memory systems (Beta)
// See: /docs/guides/memory-integration
// Switch to last-n mode
loop.setHistoryMode('last-n', 3);
await loop.sendMessage("What have we discussed?");
// Agent only sees last 3 messages
// Check current mode
const { mode, limit } = loop.getHistoryMode();
console.log(`History mode: ${mode}, limit: ${limit}`);
// Restore full history
loop.setHistoryMode('full');
Testing Memory Tools with History Mode​
import { createMemoryTools } from "@mrck-labs/grid-core";
// Create agent with memory tools
const memoryTools = createMemoryTools(memoryService);
const agent = createConfigurableAgent({
tools: Object.values(memoryTools),
// ... other config
});
const loop = createConversationLoop({
agent,
onProgress: (update) => {
// Log when memory tools are used
if (update.type === 'tool_execution' && update.metadata?.toolName?.includes('memory')) {
console.log('Agent is using memory tools!');
}
}
});
// Normal conversation
await loop.sendMessage("Remember that my favorite color is blue");
// Disable history to test memory retrieval
loop.setHistoryMode('none');
await loop.sendMessage("What's my favorite color?");
// Agent must use memory tools to answer correctly
History Modes​
Full Mode (Default)​
The agent receives the complete conversation history on each turn. This is the standard behavior that provides full context.
Use when:
- You want normal conversational flow
- Context is important for responses
- The conversation is not too long
None Mode (Amnesia)​
The agent receives only the current user message, no history. Forces the agent to rely on external memory systems or tools.
Use when:
- Testing memory retrieval systems
- Simulating stateless interactions
- Forcing tool usage for context
Last-N Mode​
The agent receives only the last N messages from the conversation.
Use when:
- Limiting context size for long conversations
- Balancing context and performance
- Implementing sliding window context
Progress Message Types​
The onProgress
callback receives messages with these types:
"thinking"
- LLM is processing the request"tool_execution"
- A tool is being executed"tool_result"
- Tool execution completed"error"
- An error occurred"complete"
- Final response is ready"info"
- General information update
Best Practices​
- Always handle errors - Wrap sendMessage in try-catch for production
- Use lifecycle handlers - Implement persistence and analytics through handlers
- Monitor progress - Use onProgress for real-time user feedback
- Export long conversations - Periodically export for recovery
- End conversations properly - Call endConversation() for cleanup
- Consider memory integration (Beta) - For agents that need to remember across sessions, see the memory integration guide
TypeScript Types​
type HistoryMode = 'full' | 'none' | 'last-n';
interface ConversationLoopOptions {
agent: Agent;
handlers?: GroupedHandlers;
onMessage?: (response: AgentResponse) => Promise<void>;
onError?: (error: Error) => Promise<void>;
onComplete?: (summary: any) => Promise<void>;
onProgress?: (message: ProgressMessage) => Promise<void>;
maxTurns?: number;
historyMode?: HistoryMode;
historyLimit?: number;
}
interface ConversationAnalytics {
messageCount: number;
userMessageCount: number;
assistantMessageCount: number;
toolCallCount: number;
turnCount: number;
duration: number;
avgResponseTime: number;
isActive: boolean;
}
interface ConversationExport {
messages: ChatMessage[];
state: Record<string, any>;
metadata: Record<string, any>;
analytics: ConversationAnalytics;
exportedAt: number;
}
interface SendMessageResult {
content: string | null;
role: "assistant";
toolCalls?: ToolCall[];
metadata?: Record<string, any>;
}
interface ProgressMessage {
type: "thinking" | "tool_execution" | "tool_result" | "error" | "complete" | "info";
message: string;
timestamp: number;
metadata?: Record<string, any>;
}
Related APIs​
createConversationManager
- Underlying managercreateConfigurableAgent
- Create agentscreateToolExecutor
- Tool execution service