A TypeScript-first, streaming-only AI agent framework for building autonomous agents with sophisticated tool execution capabilities.
pnpm install @continue-reasoning/mini-agent
- Gemini - Google Gemini 2.0 Flash with native tool calling
- OpenAI - GPT-4o with function calling and response caching
- Anthropic Claude (coming soon)
- Vercel AI SDK integration (planned)
- Session Management - Multi-session conversation handling with isolation
- Event Stream - Comprehensive real-time event system (20+ event types)
- Streaming-Only - All responses are streamed for optimal UX
- Tool Scheduler - Sophisticated tool execution with approval workflows
- Token Tracking - Real-time usage monitoring with automatic history management
- BaseTool System - Extensible tool creation with built-in validation and lifecycle
- Type Safety - Full TypeScript support with comprehensive interfaces
- Tool Confirmation - User approval workflows for destructive operations
- Parallel Execution - Concurrent tool execution with state management
- Abort Control - Comprehensive cancellation and timeout support
- Error Recovery - Graceful error handling with detailed error events
- History Management - Automatic context window management
- Output Streaming - Real-time tool output updates during execution
- MCP Support - Model Context Protocol integration (in development)
- Plugin System - External tool discovery and loading
- Streaming-First: All responses are streamed by default - non-streaming is implemented by collecting stream chunks
- Interface-Driven: Clean TypeScript interfaces with flexible implementations (BaseAgent, StandardAgent)
- Platform-Agnostic: Provider-agnostic design that works with any LLM (Gemini, OpenAI, etc.)
- Event-Based: Rich event system with 20+ event types for comprehensive monitoring
- Type-Safe: Full TypeScript support with generics for tools and comprehensive interface definitions
- Session-Aware: Built-in multi-session management with isolated conversation contexts

StandardAgent (Session-Aware Agent)
βββ BaseAgent (Core Implementation)
β βββ IChat (LLM Interface)
β β βββ GeminiChat (Google Gemini Implementation)
β β βββ OpenAIChat (OpenAI GPT Implementation)
β βββ IToolScheduler (Tool Execution)
β β βββ CoreToolScheduler (Parallel execution with approval workflows)
β βββ ITokenTracker (Token Monitoring)
β βββ TokenTracker (Real-time usage tracking)
βββ SessionManager (Multi-session Management)
βββ AgentEvent (Event System - 20+ event types)
- StandardAgent: Session-aware agent with multi-conversation management
- BaseAgent: Core orchestrator implementing the main agent processing loop
- IChat: Streaming-first chat interface supporting multiple LLM providers
- IToolScheduler: Advanced tool execution with parallel processing and user confirmation
- ITokenTracker: Real-time token usage monitoring with automatic history management
- SessionManager: Isolated conversation contexts with persistence support
- AgentEvent: Comprehensive event system for real-time monitoring and integration
import { StandardAgent, AgentEventType, AllConfig } from '@continue-reasoning/mini-agent';
import { BaseTool, DefaultToolResult, Type } from '@continue-reasoning/mini-agent';
// 1. Create a custom tool using BaseTool
export class WeatherTool extends BaseTool<
{ latitude: number; longitude: number },
{ temperature: number; location: string }
> {
constructor() {
super(
'get_weather', // Tool name
'Weather Tool', // Display name
'Get current weather temperature', // Description
{
type: Type.OBJECT,
properties: {
latitude: { type: Type.NUMBER, description: 'Latitude coordinate' },
longitude: { type: Type.NUMBER, description: 'Longitude coordinate' }
},
required: ['latitude', 'longitude']
},
true, // isOutputMarkdown
true // canUpdateOutput for real-time updates
);
}
override validateToolParams(params: { latitude: number; longitude: number }): string | null {
if (params.latitude < -90 || params.latitude > 90) {
return 'Latitude must be between -90 and 90';
}
if (params.longitude < -180 || params.longitude > 180) {
return 'Longitude must be between -180 and 180';
}
return null;
}
async execute(
params: { latitude: number; longitude: number },
signal: AbortSignal,
updateOutput?: (output: string) => void
): Promise<DefaultToolResult<{ temperature: number; location: string }>> {
try {
// Check for cancellation
this.checkAbortSignal(signal, 'Weather fetch');
// Update progress in real-time
if (updateOutput) {
updateOutput(this.formatProgress('Fetching weather', 'Connecting to API...', 'π€οΈ'));
}
// Simulate API call
const temperature = Math.round(Math.random() * 35 + 5); // 5-40Β°C
if (updateOutput) {
updateOutput(this.formatProgress('Weather retrieved', `${temperature}Β°C`, 'β
'));
}
const result = { temperature, location: `${params.latitude},${params.longitude}` };
return new DefaultToolResult(this.createResult(
`Weather: ${temperature}Β°C at coordinates ${params.latitude}, ${params.longitude}`,
`π€οΈ Temperature: **${temperature}Β°C**`,
`Retrieved weather: ${temperature}Β°C`
));
} catch (error) {
return new DefaultToolResult(this.createErrorResult(error, 'Weather fetch'));
}
}
}
// 2. Configure and create the agent
const config: AllConfig = {
agentConfig: {
model: 'gpt-4o', // or 'gemini-2.0-flash'
workingDirectory: process.cwd(),
apiKey: process.env.OPENAI_API_KEY, // or GEMINI_API_KEY
maxHistoryTokens: 100000,
},
chatConfig: {
apiKey: process.env.OPENAI_API_KEY, // or GEMINI_API_KEY
modelName: 'gpt-4o', // or 'gemini-2.0-flash'
tokenLimit: 128000,
systemPrompt: 'You are a helpful assistant with weather capabilities.',
},
toolSchedulerConfig: {
approvalMode: 'yolo', // Auto-approve for demo
},
};
// 3. Create agent with tools
const agent = new StandardAgent([new WeatherTool()], config);
// 4. Process user input with streaming
const userInput = 'What is the weather like in Tokyo (latitude: 35.6762, longitude: 139.6503)?';
for await (const event of agent.processWithSession(userInput)) {
switch (event.type) {
case AgentEventType.ResponseChunkTextDelta:
// Real-time text streaming
process.stdout.write(event.data.content.text_delta);
break;
case AgentEventType.ToolExecutionStart:
console.log(`π§ Executing: ${event.data.toolName}`);
break;
case AgentEventType.ToolExecutionDone:
console.log(`β
Completed: ${event.data.toolName}`);
break;
case AgentEventType.ResponseComplete:
console.log('\n⨠Response complete');
break;
}
}
import { StandardAgent } from '@continue-reasoning/mini-agent';
// Create agent with session management
const agent = new StandardAgent(tools, config);
// Create multiple conversation sessions
const session1 = agent.createNewSession('Weather Analysis');
const session2 = agent.createNewSession('Math Calculations');
// Use different sessions for different conversations
for await (const event of agent.processWithSession('What is the weather in Tokyo?', session1)) {
// Handle weather conversation in session1
}
for await (const event of agent.processWithSession('Calculate 15 + 27', session2)) {
// Handle math conversation in session2
}
// Switch between sessions
agent.switchToSession(session1);
for await (const event of agent.processWithSession('How about the weather in London?', session1)) {
// Continue weather conversation in session1
}
// Get session information
const sessions = agent.getSessions();
sessions.forEach(session => {
console.log(`Session: ${session.title}`);
console.log(`Messages: ${session.messageHistory.length}`);
console.log(`Tokens: ${session.tokenUsage.totalTokens}`);
});
MiniAgent provides a comprehensive event system with 20+ event types for real-time monitoring:
Event Type | Description | Data Structure |
---|---|---|
LLM Response Events | ||
ResponseChunkTextDelta |
Real-time text streaming | { content: { text_delta: string } } |
ResponseChunkTextDone |
Text complete | { content: { text: string } } |
ResponseChunkThinkingDelta |
Thinking process streaming | { content: { thinking_delta: string } } |
ResponseChunkThinkingDone |
Thinking process complete | { content: { thinking: string } } |
ResponseChunkFunctionCallDelta |
Function call parameters streaming | { content: { functionCall: { name: string, args: string } } } |
ResponseChunkFunctionCallDone |
Function call parameters complete | { content: { functionCall: { id: string, call_id: string, name: string, args: string } } } |
ResponseComplete |
Response finished | { response_id: string, usage: TokenUsage } |
Tool Execution Events | ||
ToolExecutionStart |
Tool begins execution | { toolName: string, callId: string, args: Record<string, unknown>, sessionId: string, turn: number } |
ToolExecutionDone |
Tool completes | { toolName: string, callId: string, result?: unknown, error?: string, duration?: number, sessionId: string, turn: number } |
Session Events | ||
UserMessage |
User input processed | { type: string, content: string, sessionId: string, turn: number, metadata?: any } |
TurnComplete |
Conversation turn done | { type: string, sessionId: string, turn: number, hasToolCalls: boolean } |
Error Events | ||
Error |
General errors | { message: string, timestamp: number, turn: number } |
ResponseFailed |
LLM response failed | { response_id: string, error: { code?: string, message?: string } } |
ResponseIncomplete |
Response not complete | { response_id: string, incomplete_details: { reason: string } } |
-
Choose between Delta and Done events - Don't handle both
*Delta
and*Done
events for the same content type- Delta events (
ResponseChunkTextDelta
,ResponseChunkThinkingDelta
,ResponseChunkFunctionCallDelta
) - Use only when real-time streaming UX is critical - Done events (
ResponseChunkTextDone
,ResponseChunkThinkingDone
,ResponseChunkFunctionCallDone
) - Recommended for most cases - contains complete content - Done events contain the full aggregated content from all corresponding delta events
- Delta events (
-
Recommended event handling pattern:
// β Good - Handle complete content case AgentEventType.ResponseChunkTextDone: console.log('Complete response:', event.data.content.text); break; // β Avoid - Don't handle both delta and done case AgentEventType.ResponseChunkTextDelta: // Only use when real-time streaming is essential case AgentEventType.ResponseChunkTextDone: // Don't handle both - creates duplicate content
-
Tool execution monitoring - Recommended: Use
ToolExecutionStart/Done
events for tool trackingToolExecutionStart/Done
- Best practice - High-level tool execution lifecycleResponseChunkFunctionCallDelta/Done
- Only when needed - Low-level function call parameter streaming- Function call events show LLM preparing tool calls, execution events show actual tool running
-
Use AbortController - Implement timeout and cancellation control
-
Error handling - Listen for
Error
andResponseFailed
events -
Token monitoring - Track usage with
ResponseComplete
event data
import { AgentEventType } from '@continue-reasoning/mini-agent';
// β
Recommended - Handle complete content with Done events
const abortController = new AbortController();
setTimeout(() => abortController.abort(), 30000);
try {
for await (const event of agent.processWithSession(userInput, sessionId, abortController.signal)) {
switch (event.type) {
case AgentEventType.UserMessage:
// β
Type-safe access to user message data
const userMsgData = event.data as {
content: string;
sessionId: string;
turn: number;
type: string;
};
console.log(`π€ Processing user input (Turn ${userMsgData.turn}): ${userMsgData.content}`);
break;
case AgentEventType.ResponseChunkTextDone:
// β
Get complete text content - recommended approach
const textData = event.data as { content: { text: string } };
console.log('π€ Assistant:', textData.content.text);
break;
case AgentEventType.ResponseChunkThinkingDone:
// β
Get complete thinking process if needed
const thinkingData = event.data as { content: { thinking: string } };
console.log('π§ Reasoning:', thinkingData.content.thinking);
break;
case AgentEventType.ResponseChunkFunctionCallDone:
// β
Get complete function call parameters - useful for debugging
const funcCallData = event.data as {
content: {
functionCall: {
id: string;
call_id: string;
name: string;
args: string;
}
}
};
const funcCall = funcCallData.content.functionCall;
console.log(`π§ LLM prepared tool: ${funcCall.name} with args: ${funcCall.args}`);
break;
case AgentEventType.ToolExecutionStart:
// β
Recommended - Track actual tool execution with full context
const toolStartData = event.data as {
toolName: string;
callId: string;
args: Record<string, unknown>;
sessionId: string;
turn: number;
};
console.log(`βοΈ Tool executing: ${toolStartData.toolName} (Turn ${toolStartData.turn})`);
console.log(` Args:`, toolStartData.args);
break;
case AgentEventType.ToolExecutionDone:
// β
Recommended - Track tool completion with full context
const toolDoneData = event.data as {
toolName: string;
callId: string;
result?: unknown;
error?: string;
duration?: number;
sessionId: string;
turn: number;
};
if (toolDoneData.error) {
console.log(`β Tool failed: ${toolDoneData.toolName} - ${toolDoneData.error}`);
} else {
console.log(`β
Tool completed: ${toolDoneData.toolName} (${toolDoneData.duration}ms)`);
}
break;
case AgentEventType.ResponseComplete:
// β
Access complete response with token usage
const completeData = event.data as {
response_id: string;
usage?: {
inputTokens: number;
outputTokens: number;
totalTokens: number;
}
};
console.log(`π Response complete - Tokens: ${completeData.usage?.totalTokens || 0}`);
break;
case AgentEventType.TurnComplete:
// β
Turn completion with context
const turnData = event.data as {
type: string;
sessionId: string;
turn: number;
hasToolCalls: boolean;
};
console.log(`π Turn ${turnData.turn} completed ${turnData.hasToolCalls ? 'with' : 'without'} tool calls`);
break;
case AgentEventType.Error:
// β
Structured error handling
const errorData = event.data as {
message: string;
timestamp: number;
turn: number;
};
console.error(`β Error (Turn ${errorData.turn}): ${errorData.message}`);
break;
case AgentEventType.ResponseFailed:
// β
Handle response failures
const failedData = event.data as {
response_id: string;
error: { code?: string; message?: string };
};
console.error(`β Response failed: ${failedData.error.message || 'Unknown error'}`);
break;
}
}
} catch (error) {
if (abortController.signal.aborted) {
console.log('β° Operation timed out');
} else {
console.error('π₯ Unexpected error:', error);
}
}
import { AgentEventType } from '@continue-reasoning/mini-agent';
// π― Only use when real-time streaming UX is essential
let assistantResponse = '';
for await (const event of agent.processWithSession(userInput)) {
switch (event.type) {
case AgentEventType.ResponseChunkTextDelta:
// β‘ Real-time character-by-character streaming
const deltaData = event.data as { content: { text_delta: string } };
const delta = deltaData.content.text_delta;
process.stdout.write(delta);
assistantResponse += delta;
break;
case AgentEventType.ResponseChunkFunctionCallDelta:
// π― Real-time function call parameter streaming (if needed for UX)
const funcDeltaData = event.data as {
content: { functionCall: { name: string; args: string } }
};
const funcDelta = funcDeltaData.content.functionCall;
console.log(`\nπ§ LLM preparing: ${funcDelta.name}...`);
break;
// β Don't handle corresponding Done events when using Delta events
// case AgentEventType.ResponseChunkTextDone:
// case AgentEventType.ResponseChunkFunctionCallDone:
// // These would duplicate content from delta events
case AgentEventType.ToolExecutionStart:
// β
Type-safe tool execution tracking
const toolStartData = event.data as {
toolName: string;
callId: string;
args: Record<string, unknown>;
sessionId: string;
turn: number;
};
console.log(`\nβοΈ Tool executing: ${toolStartData.toolName} (Turn ${toolStartData.turn})`);
break;
case AgentEventType.ToolExecutionDone:
// β
Type-safe tool completion tracking
const toolDoneData = event.data as {
toolName: string;
callId: string;
result?: unknown;
error?: string;
duration?: number;
sessionId: string;
turn: number;
};
const status = toolDoneData.error ? 'β Failed' : 'β
Completed';
console.log(`\n${status}: ${toolDoneData.toolName} (${toolDoneData.duration || 0}ms)`);
break;
case AgentEventType.ResponseComplete:
// β
Final response with token usage
const completeData = event.data as {
response_id: string;
usage?: { totalTokens: number }
};
console.log(`\nπ Final response complete (${completeData.usage?.totalTokens || 0} tokens)`);
console.log(`Full response: ${assistantResponse}`);
break;
case AgentEventType.Error:
// β
Handle streaming errors
const errorData = event.data as {
message: string;
timestamp: number;
turn: number;
};
console.error(`\nβ Streaming error (Turn ${errorData.turn}): ${errorData.message}`);
break;
}
}
The framework provides sophisticated tool execution with approval workflows and real-time monitoring:
Configure callbacks to monitor tool execution lifecycle:
const config: AllConfig = {
toolSchedulerConfig: {
approvalMode: 'default', // 'yolo' | 'always' | 'default'
// 1. Real-time tool output streaming
outputUpdateHandler: (callId: string, output: string) => {
console.log(`[${callId}] ${output}`);
// Stream to UI in real-time
},
// 2. Tool state change notifications
onToolCallsUpdate: (toolCalls: IToolCall[]) => {
toolCalls.forEach(call => {
console.log(`Tool ${call.request.name}: ${call.status}`);
if (call.status === 'awaiting_approval') {
// Handle confirmation UI
handleToolConfirmation(call);
}
});
},
// 3. Batch completion handler
onAllToolCallsComplete: (completed: ICompletedToolCall[]) => {
const successful = completed.filter(tc => tc.status === 'success').length;
const failed = completed.filter(tc => tc.status === 'error').length;
console.log(`Batch complete: ${successful} successful, ${failed} failed`);
}
}
};
// Auto-approve all tools (good for demos)
approvalMode: 'yolo'
// Always require user confirmation
approvalMode: 'always'
// Let each tool decide (based on tool.shouldConfirmExecute)
approvalMode: 'default'
import { ToolConfirmationOutcome } from '@continue-reasoning/mini-agent';
async function handleToolConfirmation(toolCall: IWaitingToolCall) {
const { confirmationDetails } = toolCall;
// Show confirmation UI based on tool type
const userChoice = await showConfirmationDialog({
title: confirmationDetails.title,
message: confirmationDetails.prompt,
toolName: toolCall.request.name,
args: toolCall.request.args
});
// Send response back to scheduler
await agent.getToolScheduler().handleConfirmationResponse(
toolCall.request.callId,
userChoice ? ToolConfirmationOutcome.ProceedOnce : ToolConfirmationOutcome.Cancel
);
}
# Install MiniAgent
pnpm install @continue-reasoning/mini-agent
# Set up environment variables
echo "OPENAI_API_KEY=your_openai_key" >> .env
echo "GEMINI_API_KEY=your_gemini_key" >> .env
import { StandardAgent, BaseTool, AllConfig } from '@continue-reasoning/mini-agent';
// 1. Create a simple tool
class GreetingTool extends BaseTool<{ name: string }, { greeting: string }> {
constructor() {
super('greet', 'Greeting Tool', 'Generate a personalized greeting', {
type: 'object',
properties: { name: { type: 'string', description: 'Name to greet' } },
required: ['name']
});
}
async execute(params, signal) {
return new DefaultToolResult(this.createResult(
`Hello, ${params.name}! Nice to meet you.`,
`π Hello, **${params.name}**!`,
`Greeted ${params.name}`
));
}
}
// 2. Configure agent
const config: AllConfig = {
agentConfig: {
model: 'gpt-4o',
workingDirectory: process.cwd(),
apiKey: process.env.OPENAI_API_KEY
},
chatConfig: {
apiKey: process.env.OPENAI_API_KEY,
modelName: 'gpt-4o',
tokenLimit: 128000,
systemPrompt: 'You are a helpful assistant with greeting capabilities.'
},
toolSchedulerConfig: { approvalMode: 'yolo' }
};
// 3. Create and use agent
const agent = new StandardAgent([new GreetingTool()], config);
for await (const event of agent.processWithSession('Please greet Alice')) {
if (event.type === 'response.chunk.text.delta') {
process.stdout.write(event.data.content.text_delta);
}
}
- Basic Example - Simple agent setup with weather tools
- Session Management - Multi-session conversation handling
- Tool Creation - Custom tool implementation examples
- Provider Comparison - OpenAI vs Gemini comparison
- Integration Guide - Complete integration guide for coding agents
- API Reference - Detailed API documentation
- Tool Development - Guide for creating custom tools
- Architecture Overview - Framework design and principles
# Clone and setup
git clone https://github.com/your-org/miniagent.git
cd miniagent
pnpm install
# Build and test
pnpm build
pnpm test
pnpm lint
# Run examples
pnpm example:basic
pnpm example:session
MIT License - see LICENSE for details.
src/
βββ interfaces.ts # Core TypeScript interfaces
βββ baseAgent.ts # Core agent implementation
βββ standardAgent.ts # Session-aware agent
βββ sessionManager.ts # Multi-session management
βββ chat/
β βββ geminiChat.ts # Google Gemini provider
β βββ openaiChat.ts # OpenAI provider
βββ coreToolScheduler.ts # Tool execution engine
βββ baseTool.ts # Tool base class
βββ tokenTracker.ts # Token usage monitoring
βββ agentEvent.ts # Event system
βββ examples/ # Working examples
βββ basicExample.ts # Simple agent usage
βββ sessionManagerExample.ts # Multi-session demo
βββ tools.ts # Tool implementation examples