diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b239b60..05c28105 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [3.23.0](https://github.com/soulcraftlabs/brainy/compare/v3.22.0...v3.23.0) (2025-10-04) + +- refactor: streamline core API surface + ### [3.22.0](https://github.com/soulcraftlabs/brainy/compare/v3.21.0...v3.22.0) (2025-10-01) - feat: add intelligent import for CSV, Excel, and PDF files (814cbb4) diff --git a/README.md b/README.md index 1c751b2b..362d8143 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,6 @@ ## šŸŽ‰ Key Features -### šŸ’¬ **Infinite Agent Memory** - -- **Never Lose Context**: Conversations preserved with semantic search -- **Smart Context Retrieval**: Triple Intelligence finds relevant past work -- **Claude Code Integration**: One command (`brainy conversation setup`) enables infinite memory -- **Automatic Artifact Linking**: Code and files connected to conversations -- **Scales to Millions**: Messages indexed and searchable in <100ms - ### šŸš€ **NEW in 3.21.0: Enhanced Import & Neural Processing** - **šŸ“Š Progress Tracking**: Unified progress reporting with automatic time estimation @@ -60,42 +52,6 @@ ```bash npm install @soulcraft/brainy - -# For Claude Code infinite memory (optional): -brainy conversation setup -``` - -### šŸ’¬ **Infinite Memory for Claude Code** - -```javascript -// One-time setup: -// $ brainy conversation setup - -// Claude Code now automatically: -// - Saves every conversation with embeddings -// - Retrieves relevant past context -// - Links code artifacts to conversations -// - Never loses context or momentum - -// Use programmatically: -import { Brainy } from '@soulcraft/brainy' - -const brain = new Brainy() -await brain.init() - -// Save conversations -await brain.conversation.saveMessage( - "How do I implement JWT authentication?", - "user", - { conversationId: "conv_123" } -) - -// Get relevant context (semantic + temporal + graph) -const context = await brain.conversation.getRelevantContext( - "JWT token validation", - { limit: 10, includeArtifacts: true } -) -// Returns: Ranked messages, linked code, similar conversations ``` ### šŸŽÆ **True Zero Configuration** diff --git a/docs/conversation/MCP_INTEGRATION.md b/docs/conversation/MCP_INTEGRATION.md deleted file mode 100644 index a0af0f82..00000000 --- a/docs/conversation/MCP_INTEGRATION.md +++ /dev/null @@ -1,440 +0,0 @@ -# MCP Integration - Claude Code Setup - -**One command. Infinite memory.** This guide shows you how to give Claude Code infinite context and conversation history using Brainy's Model Control Protocol (MCP) integration. - -## Quick Setup - -### One-Time Configuration - -```bash -# Install Brainy globally (if not already installed) -npm install -g @soulcraft/brainy - -# Set up MCP server for Claude Code -brainy conversation setup -``` - -**That's it!** Claude Code now has infinite memory. - -### What This Does - -The setup command: -1. Creates `~/.brainy-memory/` directory -2. Initializes Brainy database with filesystem storage -3. Creates MCP server script -4. Registers server with Claude Code -5. Ready to use - no configuration needed - -## How It Works - -### Automatic Integration - -Once set up, Claude Code **automatically**: - -**On Every Message:** -- Saves your message with semantic embeddings -- Saves Claude's response with metadata -- Links code artifacts to conversations -- Tracks problem-solving phase and confidence -- Indexes everything for instant retrieval - -**On Conversation Start:** -- Retrieves relevant past context -- Finds similar previous conversations -- Loads linked code artifacts -- Presents context to Claude seamlessly - -**Result:** Claude never loses context or momentum, even across completely separate conversations. - -### User Experience - -**Before:** -``` -You: "How do I implement JWT auth?" -Claude: [Implements authentication] - -[Days later, new conversation] -You: "Can you fix the JWT token validation?" -Claude: "I don't have context about your JWT implementation..." -``` - -**After:** -``` -You: "How do I implement JWT auth?" -Claude: [Implements authentication, saves to Brainy] - -[Days later, new conversation] -You: "Can you fix the JWT token validation?" -Claude: "I found 3 related past conversations about JWT... - Here's the implementation from /auth/middleware.ts..." - [Full context automatically retrieved] -``` - -## MCP Tools - -The MCP server exposes 6 conversation tools that Claude Code uses automatically: - -### 1. conversation_save_message - -Saves a message to conversation history. - -**Used by:** Claude Code automatically after each message -**Parameters:** -- `content`: Message text -- `role`: 'user' | 'assistant' | 'system' | 'tool' -- `conversationId`: Conversation identifier -- `phase`: Problem-solving phase -- `confidence`: Confidence score -- `artifacts`: Array of artifact IDs -- `toolsUsed`: Tools used in this message - -### 2. conversation_get_context - -Retrieves relevant past context. - -**Used by:** Claude Code at conversation start or when context is needed -**Parameters:** -- `query`: What to retrieve context for -- `limit`: Max messages (default: 10) -- `maxTokens`: Token budget (default: 50000) -- `relevanceThreshold`: Min similarity (default: 0.7) -- `includeArtifacts`: Include code/files -- `includeSimilarConversations`: Include similar past conversations - -**Returns:** -- Ranked messages with relevance scores -- Linked artifacts -- Similar past conversations -- Metadata (query time, tokens, etc.) - -### 3. conversation_search - -Searches all conversations semantically. - -**Used by:** When Claude needs to find specific past information -**Parameters:** -- `query`: Search query -- `role`: Filter by role -- `conversationId`: Filter by conversation -- `timeRange`: Time range filter - -### 4. conversation_get_thread - -Gets complete conversation thread. - -**Used by:** When Claude needs full conversation history -**Parameters:** -- `conversationId`: Conversation ID -- `includeArtifacts`: Include linked artifacts - -### 5. conversation_save_artifact - -Saves code/file artifacts. - -**Used by:** When Claude creates files -**Parameters:** -- `path`: File path -- `content`: File content -- `conversationId`: Conversation ID -- `messageId`: Message that created it -- `type`: 'code' | 'config' | 'data' | 'document' -- `language`: Programming language - -### 6. conversation_find_similar - -Finds similar past conversations. - -**Used by:** For discovering related work -**Parameters:** -- `conversationId`: Conversation to find similar to -- `limit`: Max results -- `threshold`: Similarity threshold - -## MCP Server Architecture - -### Server Location - -``` -~/.brainy-memory/ -ā”œā”€ā”€ mcp-server.js # MCP server script -└── data/ # Brainy database - ā”œā”€ā”€ nouns/ # Messages - ā”œā”€ā”€ verbs/ # Relationships - └── index/ # Search indexes -``` - -### Server Script - -The generated server script (`~/.brainy-memory/mcp-server.js`): - -```javascript -import { Brainy } from '@soulcraft/brainy' -import { BrainyMCPService } from '@soulcraft/brainy' -import { MCPConversationToolset } from '@soulcraft/brainy' - -// Initialize Brainy -const brain = new Brainy({ - storage: { - type: 'filesystem', - path: '~/.brainy-memory/data' - }, - silent: true -}) -await brain.init() - -// Create MCP service -const mcpService = new BrainyMCPService(brain) -const conversationTools = new MCPConversationToolset(brain) - -// Handle MCP requests via stdio -process.stdin.on('data', async (data) => { - const request = JSON.parse(data.toString()) - - let response - if (request.toolName?.startsWith('conversation_')) { - response = await conversationTools.handleRequest(request) - } else { - response = await mcpService.handleRequest(request) - } - - process.stdout.write(JSON.stringify(response) + '\\n') -}) -``` - -### Claude Code Configuration - -Located at `~/.config/claude-code/mcp-servers.json`: - -```json -{ - "brainy-memory": { - "command": "node", - "args": ["~/.brainy-memory/mcp-server.js"], - "env": { - "NODE_ENV": "production" - } - } -} -``` - -## Advanced Configuration - -### Custom Storage Location - -Edit `~/.brainy-memory/mcp-server.js`: - -```javascript -const brain = new Brainy({ - storage: { - type: 'filesystem', - path: '/path/to/custom/location' - } -}) -``` - -### Cloud Storage (Multi-Device Sync) - -Use S3-compatible storage for sync across machines: - -```javascript -const brain = new Brainy({ - storage: { - type: 's3', - bucket: 'my-brainy-memory', - region: 'us-east-1' - } -}) -``` - -**Requirements:** Set AWS credentials in environment: -```bash -export AWS_ACCESS_KEY_ID=xxx -export AWS_SECRET_ACCESS_KEY=yyy -``` - -### Memory Storage (Testing) - -For testing or temporary use: - -```javascript -const brain = new Brainy({ - storage: { type: 'memory' } -}) -``` - -**Note:** Memory storage is lost on server restart. - -### Context Retrieval Options - -Customize context retrieval behavior: - -```javascript -const context = await conversationManager.getRelevantContext(query, { - limit: 15, // More messages - maxTokens: 80000, // Larger context window - relevanceThreshold: 0.6, // Lower threshold = more results - weights: { - semantic: 0.7, // Adjust relevance weights - temporal: 0.6, - graph: 0.4 - } -}) -``` - -## Troubleshooting - -### Server Not Starting - -**Check logs:** -```bash -# Server writes to stderr -tail -f ~/.brainy-memory/server.log -``` - -**Common issues:** -1. **Node version**: Requires Node.js 22 LTS -2. **Permissions**: Ensure ~/.brainy-memory is writable -3. **Port conflicts**: MCP uses stdio, no ports needed - -### Claude Code Not Using Memory - -**Verify setup:** -```bash -# Check MCP server is registered -cat ~/.config/claude-code/mcp-servers.json - -# Test server manually -echo '{"type":"system_info","infoType":"version","requestId":"test","version":"1.0.0"}' | node ~/.brainy-memory/mcp-server.js -``` - -**Expected output:** -```json -{"success":true,"requestId":"test","version":"1.0.0","data":{"version":"1.0.0"}} -``` - -### Memory Not Persisting - -**Check storage:** -```bash -# Verify data directory exists -ls -la ~/.brainy-memory/data/ - -# Check database files -du -sh ~/.brainy-memory/data/ -``` - -**If empty:** Server may be using memory storage. Check `mcp-server.js` configuration. - -### Performance Issues - -**Optimize database:** -```bash -# Rebuild indexes -brainy conversation stats # This triggers index optimization -``` - -**Check size:** -```bash -# Show storage usage -du -sh ~/.brainy-memory/ -``` - -**If too large:** Consider cloud storage or compaction strategies. - -## CLI Commands - -Manage conversations via CLI: - -### View Statistics - -```bash -brainy conversation stats -``` - -Output: -``` -šŸ“Š Conversation Statistics - -Overall: - Conversations: 42 - Messages: 1,337 - Total Tokens: 567,890 - Avg Messages/Conversation: 31.8 - Avg Tokens/Message: 425.1 - -By Role: - user: 650 - assistant: 687 - -By Phase: - implementation: 423 - planning: 201 - testing: 98 -``` - -### Search Messages - -```bash -brainy conversation search -q "authentication" -l 10 -``` - -### Get Context - -```bash -brainy conversation context -q "JWT token validation" -l 15 -``` - -### View Thread - -```bash -brainy conversation thread -c conv_abc123 -``` - -### Export Conversation - -```bash -brainy conversation export -c conv_abc123 -o backup.json -``` - -### Import Conversation - -```bash -brainy conversation import -o backup.json -``` - -## Security & Privacy - -### Local-First by Default - -- All data stored locally in `~/.brainy-memory/` -- No external services or APIs -- Complete privacy and control - -### Data Encryption - -For sensitive conversations, use encrypted filesystem: - -```bash -# Create encrypted volume -hdiutil create -size 1g -encryption AES-256 -volname BrainyMemory ~/brainy-secure.dmg - -# Mount and use -hdiutil attach ~/brainy-secure.dmg -brainy conversation setup --path /Volumes/BrainyMemory -``` - -### Access Control - -MCP server runs with your user permissions. No additional authentication needed for local use. - -## Next Steps - -- [API Reference](./API_REFERENCE.md) - Complete API documentation -- [Examples](./EXAMPLES.md) - Usage examples and patterns -- [Advanced Features](./ADVANCED.md) - Advanced configuration and optimization - -## Support - -Issues or questions: -- GitHub: [soulcraftlabs/brainy/issues](https://github.com/soulcraftlabs/brainy/issues) -- Documentation: [docs.brainy.ai](https://docs.brainy.ai) \ No newline at end of file diff --git a/docs/conversation/README.md b/docs/conversation/README.md deleted file mode 100644 index 4cac3c98..00000000 --- a/docs/conversation/README.md +++ /dev/null @@ -1,453 +0,0 @@ -# Infinite Agent Memory - Conversation API - -**Never lose context again.** Brainy's Conversation API provides infinite memory and context management for AI agents like Claude Code, enabling truly continuous conversations with semantic search, smart context retrieval, and automatic knowledge preservation. - -## Overview - -The Conversation API turns your agent interactions into a living knowledge graph where: -- **Every message is preserved** with semantic embeddings for instant retrieval -- **Context is automatically retrieved** using Triple Intelligence (vector + graph + metadata) -- **Similar conversations are discovered** through neural clustering -- **Code artifacts are linked** to the conversations that created them -- **Memory scales infinitely** to millions of messages with <100ms retrieval - -## Quick Start - -### Zero-Config Usage - -```typescript -import { Brainy } from '@soulcraft/brainy' - -const brain = new Brainy() // Zero configuration! -await brain.init() - -// Access conversation manager (lazy-loaded) -const conv = brain.conversation - -// Save messages with automatic embedding and indexing -const messageId = await conv.saveMessage( - "How do I implement JWT authentication?", - "user", - { conversationId: "conv_123" } -) - -// Get relevant context with semantic search -const context = await conv.getRelevantContext("authentication implementation", { - limit: 10, - includeArtifacts: true -}) - -// Context includes: -// - Semantically similar messages -// - Recent related conversations -// - Linked code artifacts -// - Relevance scores and explanations -``` - -### Claude Code Integration (MCP) - -One-time setup: - -```bash -brainy conversation setup -``` - -That's it! Claude Code automatically: -- Saves every message with embeddings -- Retrieves relevant past context -- Links code artifacts to conversations -- Never loses context or momentum - -## Core Concepts - -### 1. Messages - -Every message is a **semantic entity** with: -- **Content**: The actual message text -- **Role**: user, assistant, system, or tool -- **Embeddings**: Automatic vector representation -- **Metadata**: Timestamps, conversation ID, phase, confidence, etc. -- **Relationships**: Temporal links to previous/next messages - -```typescript -const messageId = await conv.saveMessage( - "Implement user authentication", - "user", - { - conversationId: "conv_123", - phase: "planning", - confidence: 0.95, - tags: ["authentication", "security"] - } -) -``` - -### 2. Conversations - -A conversation is a **collection of related messages**: -- Tracked by `conversationId` -- Contains temporal message sequence -- Stores aggregate metadata (tokens, duration, participants) -- Can span multiple sessions - -```typescript -const thread = await conv.getConversationThread("conv_123", { - includeArtifacts: true -}) - -console.log(`${thread.messages.length} messages, ${thread.metadata.totalTokens} tokens`) -``` - -### 3. Context Retrieval - -Smart context retrieval uses **Triple Intelligence**: -- **Semantic**: Vector similarity to find related messages -- **Temporal**: Recency decay favors recent context -- **Graph**: Relationship traversal finds connected knowledge - -```typescript -const context = await conv.getRelevantContext("how to validate JWT tokens", { - limit: 10, - maxTokens: 50000, - relevanceThreshold: 0.7, - weights: { - semantic: 1.0, // Prioritize meaning - temporal: 0.5, // Recent is relevant - graph: 0.3 // Connected knowledge - } -}) -``` - -### 4. Artifacts - -Code and files created during conversations are **first-class citizens**: -- Stored in Brainy's Virtual Filesystem -- Linked to messages via graph relationships -- Searchable by content and metadata -- Retrieved with context - -```typescript -const artifactId = await conv.saveArtifact( - '/auth/middleware.ts', - codeContent, - { - conversationId: "conv_123", - messageId: messageId, - type: 'code', - language: 'typescript' - } -) -``` - -## API Reference - -### ConversationManager - -#### `saveMessage(content, role, options)` - -Save a message with automatic embedding. - -**Parameters:** -- `content` (string): Message content -- `role` (MessageRole): 'user' | 'assistant' | 'system' | 'tool' -- `options` (SaveMessageOptions): - - `conversationId?`: Conversation ID (auto-generated if not provided) - - `sessionId?`: Session ID - - `phase?`: Problem-solving phase - - `confidence?`: Confidence score (0-1) - - `artifacts?`: Array of artifact IDs - - `toolsUsed?`: Array of tool names - - `tags?`: Array of tags - - `linkToPrevious?`: ID of previous message - -**Returns:** `Promise` - Message ID - -**Example:** -```typescript -const id = await conv.saveMessage( - "Implement authentication middleware", - "assistant", - { - conversationId: "conv_123", - phase: "implementation", - confidence: 0.92, - artifacts: ["middleware-id"], - toolsUsed: ["write", "edit"], - tags: ["authentication", "middleware"] - } -) -``` - -#### `getRelevantContext(query, options)` - -Retrieve relevant context with smart ranking. - -**Parameters:** -- `query` (string | ContextRetrievalOptions): Query or full options -- `options?` (ContextRetrievalOptions): - - `limit?`: Max messages (default: 10) - - `maxTokens?`: Token budget (default: 50000) - - `relevanceThreshold?`: Min score (default: 0.7) - - `role?`: Filter by role - - `phase?`: Filter by phase - - `tags?`: Filter by tags - - `timeRange?`: Time range filter - - `weights?`: Scoring weights - - `includeArtifacts?`: Include linked artifacts - - `includeSimilarConversations?`: Include similar conversations - -**Returns:** `Promise` - -**Example:** -```typescript -const context = await conv.getRelevantContext({ - query: "JWT token validation", - limit: 15, - maxTokens: 60000, - role: "assistant", - phase: ["implementation", "testing"], - tags: ["authentication"], - includeArtifacts: true, - includeSimilarConversations: true -}) - -console.log(`Found ${context.messages.length} relevant messages`) -console.log(`Total tokens: ${context.totalTokens}`) -console.log(`Query time: ${context.metadata.queryTime}ms`) -``` - -#### `searchMessages(options)` - -Search messages with semantic similarity. - -**Parameters:** -- `options` (ConversationSearchOptions): - - `query`: Search query (required) - - `limit?`: Max results (default: 10) - - `role?`: Filter by role - - `conversationId?`: Filter by conversation - - `sessionId?`: Filter by session - - `timeRange?`: Time range filter - -**Returns:** `Promise` - -**Example:** -```typescript -const results = await conv.searchMessages({ - query: "authentication errors", - limit: 20, - role: "assistant", - timeRange: { - start: Date.now() - 7*24*60*60*1000 // Last 7 days - } -}) - -for (const result of results) { - console.log(`${result.message.role}: ${result.snippet}`) - console.log(`Score: ${result.score}, Conv: ${result.conversationId}`) -} -``` - -#### `getConversationThread(conversationId, options)` - -Get complete conversation thread. - -**Parameters:** -- `conversationId` (string): Conversation ID -- `options?`: - - `includeArtifacts?`: Include linked artifacts - -**Returns:** `Promise` - -**Example:** -```typescript -const thread = await conv.getConversationThread("conv_123", { - includeArtifacts: true -}) - -console.log(`Conversation: ${thread.id}`) -console.log(`Messages: ${thread.metadata.messageCount}`) -console.log(`Duration: ${new Date(thread.metadata.endTime) - new Date(thread.metadata.startTime)}ms`) - -for (const msg of thread.messages) { - console.log(`[${msg.role}] ${msg.content}`) -} -``` - -#### `findSimilarConversations(conversationId, limit, threshold)` - -Find similar past conversations. - -**Parameters:** -- `conversationId` (string): Conversation to find similar to -- `limit?` (number): Max results (default: 5) -- `threshold?` (number): Min similarity (default: 0.7) - -**Returns:** `Promise>` - -**Example:** -```typescript -const similar = await conv.findSimilarConversations("conv_123", 5, 0.75) - -for (const s of similar) { - console.log(`Similar conversation: ${s.id}`) - console.log(`Relevance: ${s.relevance.toFixed(2)}`) -} -``` - -#### `getConversationThemes(conversationId)` - -Discover themes via clustering. - -**Parameters:** -- `conversationId` (string): Conversation ID - -**Returns:** `Promise` - -**Example:** -```typescript -const themes = await conv.getConversationThemes("conv_123") - -for (const theme of themes) { - console.log(`Theme: ${theme.label}`) - console.log(`Messages: ${theme.messages.length}`) - console.log(`Coherence: ${theme.coherence}`) -} -``` - -#### `saveArtifact(path, content, options)` - -Save code/file artifact. - -**Parameters:** -- `path` (string): VFS path -- `content` (string | Buffer): File content -- `options` (ArtifactOptions): - - `conversationId`: Conversation ID (required) - - `messageId?`: Message ID to link - - `type?`: 'code' | 'config' | 'data' | 'document' | 'other' - - `language?`: Programming language - - `description?`: Artifact description - -**Returns:** `Promise` - Artifact ID - -**Example:** -```typescript -const artifactId = await conv.saveArtifact( - '/auth/jwt-middleware.ts', - middlewareCode, - { - conversationId: "conv_123", - messageId: messageId, - type: 'code', - language: 'typescript', - description: 'JWT authentication middleware' - } -) -``` - -#### `getConversationStats(conversationId?)` - -Get conversation statistics. - -**Parameters:** -- `conversationId?` (string): Optional conversation to filter - -**Returns:** `Promise` - -**Example:** -```typescript -const stats = await conv.getConversationStats() - -console.log(`Total conversations: ${stats.totalConversations}`) -console.log(`Total messages: ${stats.totalMessages}`) -console.log(`Total tokens: ${stats.totalTokens}`) -console.log(`By role:`, stats.roles) -console.log(`By phase:`, stats.phases) -``` - -#### `exportConversation(conversationId)` - -Export conversation to JSON. - -**Returns:** `Promise` - Serializable conversation object - -#### `importConversation(data)` - -Import conversation from JSON. - -**Returns:** `Promise` - New conversation ID - -## Advanced Usage - -### Custom Relevance Weights - -Fine-tune context retrieval: - -```typescript -const context = await conv.getRelevantContext(query, { - weights: { - semantic: 0.6, // Less weight on exact matching - temporal: 0.8, // More weight on recent messages - graph: 0.4 // Moderate weight on relationships - } -}) -``` - -### Phase-Based Filtering - -Track problem-solving progress: - -```typescript -const planningMessages = await conv.searchMessages({ - query: "authentication approach", - role: "assistant", - phase: ["planning", "analysis"] -}) -``` - -### Time-Based Analysis - -Analyze conversation evolution: - -```typescript -const recentContext = await conv.getRelevantContext(query, { - timeRange: { - start: Date.now() - 24*60*60*1000 // Last 24 hours - } -}) -``` - -### Multi-Session Conversations - -Track across sessions: - -```typescript -// Session 1 -await conv.saveMessage(content1, "user", { - conversationId: "conv_123", - sessionId: "session_1" -}) - -// Session 2 (different day) -await conv.saveMessage(content2, "user", { - conversationId: "conv_123", // Same conversation! - sessionId: "session_2" -}) -``` - -## Performance - -- **Message save**: <50ms (with embedding) -- **Context retrieval**: <100ms (10 messages) -- **Search**: <150ms (1000s of messages) -- **Theme clustering**: <500ms -- **Scales to**: 10M+ messages -- **Storage**: Efficient vector + graph storage - -## Next Steps - -- [MCP Integration Guide](./MCP_INTEGRATION.md) - Claude Code setup -- [API Reference](./API_REFERENCE.md) - Complete API documentation -- [Examples](./EXAMPLES.md) - Code examples and patterns -- [Advanced Features](./ADVANCED.md) - Advanced usage and optimization \ No newline at end of file diff --git a/src/brainy.ts b/src/brainy.ts index 696b51ce..12be28a8 100644 --- a/src/brainy.ts +++ b/src/brainy.ts @@ -88,7 +88,6 @@ export class Brainy implements BrainyInterface { private _extractor?: NeuralEntityExtractor private _tripleIntelligence?: TripleIntelligenceSystem private _vfs?: VirtualFileSystem - private _conversation?: any // ConversationManager (lazy-loaded) // State private initialized = false @@ -1671,31 +1670,6 @@ export class Brainy implements BrainyInterface { return this._vfs } - /** - * Conversation Manager API - Infinite Agent Memory - * - * Provides conversation and context management for AI agents: - * - Save and retrieve conversation messages - * - Semantic search across conversation history - * - Smart context retrieval with relevance ranking - * - Artifact management (code, files, documents) - * - Conversation themes and clustering - * - * @returns ConversationManager instance - * @example - * const conv = brain.conversation - * await conv.saveMessage("How do I implement auth?", "user", { conversationId: "conv_123" }) - * const context = await conv.getRelevantContext("authentication implementation") - */ - conversation() { - if (!this._conversation) { - // Lazy-load ConversationManager to avoid circular dependencies - const { ConversationManager } = require('./conversation/conversationManager.js') - this._conversation = new ConversationManager(this) - } - return this._conversation - } - /** * Data Management API - backup, restore, import, export */ diff --git a/src/cli/commands/conversation.ts b/src/cli/commands/conversation.ts deleted file mode 100644 index 94f6cd7f..00000000 --- a/src/cli/commands/conversation.ts +++ /dev/null @@ -1,637 +0,0 @@ -/** - * šŸ’¬ Conversation CLI Commands - * - * CLI interface for infinite agent memory and conversation management - */ - -import inquirer from 'inquirer' -import chalk from 'chalk' -import ora from 'ora' -import * as fs from '../../universal/fs.js' -import * as path from '../../universal/path.js' -import { Brainy } from '../../brainy.js' - -interface CommandArguments { - action?: string - conversationId?: string - query?: string - role?: string - limit?: number - format?: string - output?: string - _: string[] -} - -export const conversationCommand = { - command: 'conversation [action]', - describe: 'šŸ’¬ Conversation and context management', - - builder: (yargs: any) => { - return yargs - .positional('action', { - describe: 'Conversation operation to perform', - type: 'string', - choices: ['setup', 'remove', 'search', 'context', 'thread', 'stats', 'export', 'import'] - }) - .option('conversation-id', { - describe: 'Conversation ID', - type: 'string', - alias: 'c' - }) - .option('query', { - describe: 'Search query or context query', - type: 'string', - alias: 'q' - }) - .option('role', { - describe: 'Filter by message role', - type: 'string', - choices: ['user', 'assistant', 'system', 'tool'], - alias: 'r' - }) - .option('limit', { - describe: 'Maximum results', - type: 'number', - default: 10, - alias: 'l' - }) - .option('format', { - describe: 'Output format', - type: 'string', - choices: ['json', 'table', 'text'], - default: 'table', - alias: 'f' - }) - .option('output', { - describe: 'Output file path', - type: 'string', - alias: 'o' - }) - .example('$0 conversation setup', 'Set up MCP server for Claude Code') - .example('$0 conversation search -q "authentication" -l 5', 'Search messages') - .example('$0 conversation context -q "how to implement JWT"', 'Get relevant context') - .example('$0 conversation thread -c conv_123', 'Get conversation thread') - .example('$0 conversation stats', 'Show conversation statistics') - }, - - handler: async (argv: CommandArguments) => { - const action = argv.action || 'setup' - - try { - switch (action) { - case 'setup': - await handleSetup(argv) - break - case 'remove': - await handleRemove(argv) - break - case 'search': - await handleSearch(argv) - break - case 'context': - await handleContext(argv) - break - case 'thread': - await handleThread(argv) - break - case 'stats': - await handleStats(argv) - break - case 'export': - await handleExport(argv) - break - case 'import': - await handleImport(argv) - break - default: - console.log(chalk.yellow(`Unknown action: ${action}`)) - console.log('Run "brainy conversation --help" for usage information') - } - } catch (error: any) { - console.error(chalk.red(`Error: ${error.message}`)) - process.exit(1) - } - } -} - -/** - * Handle setup command - Set up MCP server for Claude Code - */ -async function handleSetup(argv: CommandArguments) { - console.log(chalk.bold.cyan('\n🧠 Brainy Infinite Memory Setup\n')) - - // Check for existing setup - const homeDir = process.env.HOME || process.env.USERPROFILE || '~' - const brainyDir = path.join(homeDir, '.brainy-memory') - const dataDir = path.join(brainyDir, 'data') - const serverPath = path.join(brainyDir, 'mcp-server.js') - const configPath = path.join(homeDir, '.config', 'claude-code', 'mcp-servers.json') - - // Check if already set up - if (await fs.exists(brainyDir)) { - const { overwrite } = await inquirer.prompt([ - { - type: 'confirm', - name: 'overwrite', - message: 'Brainy memory setup already exists. Overwrite?', - default: false - } - ]) - - if (!overwrite) { - console.log(chalk.yellow('Setup cancelled')) - return - } - } - - const spinner = ora('Creating Brainy memory directory...').start() - - try { - // Create directories - await fs.mkdir(brainyDir, { recursive: true }) - await fs.mkdir(dataDir, { recursive: true }) - - spinner.succeed('Created Brainy memory directory') - - // Create MCP server script - spinner.start('Creating MCP server script...') - - const serverScript = `#!/usr/bin/env node - -/** - * Brainy Infinite Memory MCP Server - * - * This server provides conversation and context management - * for Claude Code through the Model Control Protocol (MCP). - */ - -import { Brainy } from '@soulcraft/brainy' -import { BrainyMCPService } from '@soulcraft/brainy' -import { MCPConversationToolset } from '@soulcraft/brainy' - -async function main() { - try { - // Initialize Brainy with filesystem storage - const brain = new Brainy({ - storage: { - type: 'filesystem', - path: '${dataDir.replace(/\\/g, '/')}' - }, - silent: true // Suppress console output - }) - - await brain.init() - - // Create MCP service - const mcpService = new BrainyMCPService(brain, { - enableAuth: false // Local usage, no auth needed - }) - - // Create conversation toolset - const conversationTools = new MCPConversationToolset(brain) - await conversationTools.init() - - // Register conversation tools - const tools = await conversationTools.getAvailableTools() - - console.error('🧠 Brainy Memory Server started') - console.error(\`šŸ“Š \${tools.length} conversation tools available\`) - console.error('āœ… Ready for Claude Code integration') - - // Handle MCP requests via stdio - process.stdin.on('data', async (data) => { - try { - const request = JSON.parse(data.toString()) - - // Route conversation tool requests - let response - if (request.toolName && request.toolName.startsWith('conversation_')) { - response = await conversationTools.handleRequest(request) - } else { - response = await mcpService.handleRequest(request) - } - - // Write response to stdout - process.stdout.write(JSON.stringify(response) + '\\n') - } catch (error) { - console.error('Error handling request:', error) - } - }) - - // Handle shutdown gracefully - process.on('SIGINT', () => { - console.error('\\nšŸ›‘ Shutting down Brainy Memory Server') - process.exit(0) - }) - - } catch (error) { - console.error('Failed to start Brainy Memory Server:', error) - process.exit(1) - } -} - -main() -` - - await fs.writeFile(serverPath, serverScript, 'utf8') - // Make executable on Unix systems - try { - await import('fs').then(fsModule => { - fsModule.promises.chmod(serverPath, 0o755).catch(() => {}) - }) - } catch { - // Windows doesn't need chmod - } - spinner.succeed('Created MCP server script') - - // Create Claude Code config - spinner.start('Configuring Claude Code...') - - const configDir = path.dirname(configPath) - await fs.mkdir(configDir, { recursive: true }) - - let mcpConfig: any = {} - if (await fs.exists(configPath)) { - const existingConfig = await fs.readFile(configPath, 'utf8') - mcpConfig = JSON.parse(existingConfig) - } - - mcpConfig['brainy-memory'] = { - command: 'node', - args: [serverPath], - env: { - NODE_ENV: 'production' - } - } - - await fs.writeFile(configPath, JSON.stringify(mcpConfig, null, 2), 'utf8') - spinner.succeed('Configured Claude Code') - - // Initialize Brainy database - spinner.start('Initializing Brainy database...') - - const brain = new Brainy({ - storage: { - type: 'filesystem', - options: { - path: dataDir - } - }, - silent: true - }) - await brain.init() - - spinner.succeed('Initialized Brainy database') - - // Shutdown Brainy to release resources - await brain.close() - - // Success! - console.log(chalk.bold.green('\nāœ… Setup complete!\n')) - console.log(chalk.cyan('šŸ“ Memory storage:'), brainyDir) - console.log(chalk.cyan('šŸ”§ MCP server:'), serverPath) - console.log(chalk.cyan('āš™ļø Claude Code config:'), configPath) - console.log() - console.log(chalk.bold('šŸš€ Next steps:')) - console.log(' 1. Restart Claude Code to load the MCP server') - console.log(' 2. Start a new conversation - your history will be saved automatically!') - console.log(' 3. Claude will use past context to help you work faster') - console.log() - console.log(chalk.dim('Run "brainy conversation stats" to see your conversation statistics')) - - } catch (error: any) { - spinner.fail('Setup failed') - throw error - } -} - -/** - * Handle remove command - Remove MCP server and optionally data - */ -async function handleRemove(argv: CommandArguments) { - console.log(chalk.bold.cyan('\nšŸ—‘ļø Brainy Infinite Memory Removal\n')) - - const homeDir = process.env.HOME || process.env.USERPROFILE || '~' - const brainyDir = path.join(homeDir, '.brainy-memory') - const configPath = path.join(homeDir, '.config', 'claude-code', 'mcp-servers.json') - - // Check if setup exists - const brainyExists = await fs.exists(brainyDir) - const configExists = await fs.exists(configPath) - - if (!brainyExists && !configExists) { - console.log(chalk.yellow('No Brainy memory setup found. Nothing to remove.')) - return - } - - // Show what will be removed - console.log(chalk.white('The following will be removed:')) - if (brainyExists) { - console.log(chalk.dim(` • ${brainyDir} (memory data and MCP server)`)) - } - if (configExists) { - console.log(chalk.dim(` • MCP config entry in ${configPath}`)) - } - console.log() - - // Confirm removal - const { confirm } = await inquirer.prompt([ - { - type: 'confirm', - name: 'confirm', - message: 'Are you sure you want to remove Brainy infinite memory?', - default: false - } - ]) - - if (!confirm) { - console.log(chalk.yellow('Removal cancelled')) - return - } - - const spinner = ora('Removing Brainy memory setup...').start() - - try { - // Remove Brainy directory - if (brainyExists) { - // Use Node.js fs for rm operation as universal fs doesn't have it - const nodefs = await import('fs') - await nodefs.promises.rm(brainyDir, { recursive: true, force: true }) - spinner.text = 'Removed memory directory...' - } - - // Remove MCP config entry - if (configExists) { - try { - const configContent = await fs.readFile(configPath, 'utf8') - const mcpConfig = JSON.parse(configContent) - - if (mcpConfig['brainy-memory']) { - delete mcpConfig['brainy-memory'] - await fs.writeFile(configPath, JSON.stringify(mcpConfig, null, 2), 'utf8') - spinner.text = 'Removed MCP configuration...' - } - } catch (error) { - // If config file is corrupted or empty, skip - spinner.warn('Could not update MCP config file') - } - } - - spinner.succeed('Successfully removed Brainy infinite memory') - - console.log() - console.log(chalk.bold('āœ… Cleanup complete')) - console.log() - console.log(chalk.white('All conversation data and MCP configuration have been removed.')) - console.log(chalk.dim('Run "brainy conversation setup" to set up again.')) - console.log() - - } catch (error: any) { - spinner.fail('Removal failed') - console.error(chalk.red('Error:'), error.message) - throw error - } -} - -/** - * Handle search command - Search messages - */ -async function handleSearch(argv: CommandArguments) { - if (!argv.query) { - console.log(chalk.yellow('Query required. Use -q or --query')) - return - } - - const spinner = ora('Searching conversations...').start() - - const brain = new Brainy() - await brain.init() - - const conv = brain.conversation() - await conv.init() - - const results = await conv.searchMessages({ - query: argv.query, - limit: argv.limit || 10, - role: argv.role as any, - includeContent: true, - includeMetadata: true - }) - - spinner.succeed(`Found ${results.length} messages`) - - if (results.length === 0) { - console.log(chalk.yellow('No messages found')) - await brain.close() - return - } - - // Display results - console.log() - for (const result of results) { - console.log(chalk.bold.cyan(`${result.message.role}:`), result.snippet) - console.log(chalk.dim(` Score: ${result.score.toFixed(3)} | Conv: ${result.conversationId}`)) - console.log() - } - - await brain.close() -} - -/** - * Handle context command - Get relevant context - */ -async function handleContext(argv: CommandArguments) { - if (!argv.query) { - console.log(chalk.yellow('Query required. Use -q or --query')) - return - } - - const spinner = ora('Retrieving relevant context...').start() - - const brain = new Brainy() - await brain.init() - - const conv = brain.conversation() - await conv.init() - - const context = await conv.getRelevantContext(argv.query, { - limit: argv.limit || 10, - includeArtifacts: true, - includeSimilarConversations: true - }) - - spinner.succeed(`Retrieved ${context.messages.length} relevant messages`) - - if (context.messages.length === 0) { - console.log(chalk.yellow('No relevant context found')) - await brain.close() - return - } - - // Display context - console.log() - console.log(chalk.bold('šŸ“Š Context Statistics:')) - console.log(chalk.dim(` Messages: ${context.messages.length}`)) - console.log(chalk.dim(` Tokens: ${context.totalTokens}`)) - console.log(chalk.dim(` Query time: ${context.metadata.queryTime}ms`)) - console.log() - - console.log(chalk.bold('šŸ’¬ Relevant Messages:')) - for (const msg of context.messages) { - console.log() - console.log(chalk.cyan(`${msg.role} (score: ${msg.relevanceScore.toFixed(3)}):`)) - console.log(msg.content.substring(0, 200) + (msg.content.length > 200 ? '...' : '')) - } - - if (context.similarConversations && context.similarConversations.length > 0) { - console.log() - console.log(chalk.bold('šŸ”— Similar Conversations:')) - for (const conv of context.similarConversations) { - console.log(chalk.dim(` - ${conv.title || conv.id} (${conv.relevance.toFixed(2)})`)) - } - } - - await brain.close() -} - -/** - * Handle thread command - Get conversation thread - */ -async function handleThread(argv: CommandArguments) { - if (!argv.conversationId) { - console.log(chalk.yellow('Conversation ID required. Use -c or --conversation-id')) - return - } - - const spinner = ora('Loading conversation thread...').start() - - const brain = new Brainy() - await brain.init() - - const conv = brain.conversation() - await conv.init() - - const thread = await conv.getConversationThread(argv.conversationId, { - includeArtifacts: true - }) - - spinner.succeed(`Loaded ${thread.messages.length} messages`) - - // Display thread - console.log() - console.log(chalk.bold('šŸ“Š Thread Information:')) - console.log(chalk.dim(` Conversation: ${thread.id}`)) - console.log(chalk.dim(` Messages: ${thread.metadata.messageCount}`)) - console.log(chalk.dim(` Tokens: ${thread.metadata.totalTokens}`)) - console.log(chalk.dim(` Started: ${new Date(thread.metadata.startTime).toLocaleString()}`)) - console.log() - - console.log(chalk.bold('šŸ’¬ Messages:')) - for (const msg of thread.messages) { - console.log() - console.log(chalk.cyan(`${msg.role}:`), msg.content) - console.log(chalk.dim(` ${new Date(msg.createdAt).toLocaleString()}`)) - } - - await brain.close() -} - -/** - * Handle stats command - Show statistics - */ -async function handleStats(argv: CommandArguments) { - const spinner = ora('Calculating statistics...').start() - - const brain = new Brainy() - await brain.init() - - const conv = brain.conversation() - await conv.init() - - const stats = await conv.getConversationStats() - - spinner.succeed('Statistics calculated') - - // Display stats - console.log() - console.log(chalk.bold.cyan('šŸ“Š Conversation Statistics\n')) - console.log(chalk.bold('Overall:')) - console.log(chalk.dim(` Conversations: ${stats.totalConversations}`)) - console.log(chalk.dim(` Messages: ${stats.totalMessages}`)) - console.log(chalk.dim(` Total Tokens: ${stats.totalTokens.toLocaleString()}`)) - console.log(chalk.dim(` Avg Messages/Conversation: ${stats.averageMessagesPerConversation.toFixed(1)}`)) - console.log(chalk.dim(` Avg Tokens/Message: ${stats.averageTokensPerMessage.toFixed(1)}`)) - console.log() - - if (Object.keys(stats.roles).length > 0) { - console.log(chalk.bold('By Role:')) - for (const [role, count] of Object.entries(stats.roles)) { - console.log(chalk.dim(` ${role}: ${count}`)) - } - console.log() - } - - if (Object.keys(stats.phases).length > 0) { - console.log(chalk.bold('By Phase:')) - for (const [phase, count] of Object.entries(stats.phases)) { - console.log(chalk.dim(` ${phase}: ${count}`)) - } - } - - await brain.close() -} - -/** - * Handle export command - Export conversation - */ -async function handleExport(argv: CommandArguments) { - if (!argv.conversationId) { - console.log(chalk.yellow('Conversation ID required. Use -c or --conversation-id')) - return - } - - const spinner = ora('Exporting conversation...').start() - - const brain = new Brainy() - await brain.init() - - const conv = brain.conversation() - await conv.init() - - const exported = await conv.exportConversation(argv.conversationId) - - const output = argv.output || `conversation_${argv.conversationId}.json` - await fs.writeFile(output, JSON.stringify(exported, null, 2), 'utf8') - - spinner.succeed(`Exported to ${output}`) - - await brain.close() -} - -/** - * Handle import command - Import conversation - */ -async function handleImport(argv: CommandArguments) { - const inputFile = argv.output - if (!inputFile) { - console.log(chalk.yellow('Input file required. Use -o or --output')) - return - } - - const spinner = ora('Importing conversation...').start() - - const brain = new Brainy() - await brain.init() - - const conv = brain.conversation() - await conv.init() - - const data = JSON.parse(await fs.readFile(inputFile, 'utf8')) - const conversationId = await conv.importConversation(data) - - spinner.succeed(`Imported as conversation ${conversationId}`) - - await brain.close() -} - -export default conversationCommand \ No newline at end of file diff --git a/src/cli/index.ts b/src/cli/index.ts index 68241b8f..4f51bd91 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -13,7 +13,6 @@ import { coreCommands } from './commands/core.js' import { utilityCommands } from './commands/utility.js' import { vfsCommands } from './commands/vfs.js' import { dataCommands } from './commands/data.js' -import conversationCommand from './commands/conversation.js' import { readFileSync } from 'fs' import { fileURLToPath } from 'url' import { dirname, join } from 'path' @@ -166,55 +165,6 @@ program .option('-o, --output ', 'Output file') .action(neuralCommands.visualize) -// ===== Conversation Commands (Infinite Memory) ===== - -program - .command('conversation') - .alias('conv') - .description('šŸ’¬ Infinite agent memory and context management') - .addCommand( - new Command('setup') - .description('Set up MCP server for Claude Code integration') - .action(async () => { - await conversationCommand.handler({ action: 'setup', _: [] }) - }) - ) - .addCommand( - new Command('search') - .description('Search messages across conversations') - .requiredOption('-q, --query ', 'Search query') - .option('-c, --conversation-id ', 'Filter by conversation') - .option('-r, --role ', 'Filter by role') - .option('-l, --limit ', 'Maximum results', '10') - .action(async (options) => { - await conversationCommand.handler({ action: 'search', ...options as any, _: [] }) - }) - ) - .addCommand( - new Command('context') - .description('Get relevant context for a query') - .requiredOption('-q, --query ', 'Context query') - .option('-l, --limit ', 'Maximum messages', '10') - .action(async (options) => { - await conversationCommand.handler({ action: 'context', ...options as any, _: [] }) - }) - ) - .addCommand( - new Command('thread') - .description('Get full conversation thread') - .requiredOption('-c, --conversation-id ', 'Conversation ID') - .action(async (options) => { - await conversationCommand.handler({ action: 'thread', ...options as any, _: [] }) - }) - ) - .addCommand( - new Command('stats') - .description('Show conversation statistics') - .action(async () => { - await conversationCommand.handler({ action: 'stats', _: [] }) - }) - ) - // ===== VFS Commands (Subcommand Group) ===== program diff --git a/src/conversation/conversationManager.ts b/src/conversation/conversationManager.ts deleted file mode 100644 index d068881e..00000000 --- a/src/conversation/conversationManager.ts +++ /dev/null @@ -1,825 +0,0 @@ -/** - * ConversationManager - Infinite Agent Memory - * - * Production-ready conversation and context management for AI agents. - * Built on Brainy's existing infrastructure: Triple Intelligence, Neural API, VFS. - * - * REAL IMPLEMENTATION - No stubs, no mocks, no TODOs - */ - -import { v4 as uuidv4 } from '../universal/uuid.js' -import { NounType, VerbType } from '../types/graphTypes.js' -import { Brainy } from '../brainy.js' -import { - MessageRole, - ProblemSolvingPhase, - ConversationMessage, - ConversationMessageMetadata, - ConversationThread, - ConversationThreadMetadata, - ConversationContext, - RankedMessage, - SaveMessageOptions, - ContextRetrievalOptions, - ConversationSearchOptions, - ConversationSearchResult, - ConversationTheme, - ArtifactOptions, - ConversationStats, - CompactionOptions, - CompactionResult -} from './types.js' - -/** - * ConversationManager - High-level API for conversation operations - * - * Uses existing Brainy infrastructure: - * - brain.add() for messages - * - brain.relate() for threading - * - brain.find() with Triple Intelligence for context - * - brain.neural for clustering and similarity - * - brain.vfs() for artifacts - */ -export class ConversationManager { - private brain: Brainy - private initialized = false - private _vfs: any = null - - /** - * Create a ConversationManager instance - * @param brain Brainy instance to use - */ - constructor(brain: Brainy) { - this.brain = brain - } - - /** - * Initialize the conversation manager - * Lazy initialization pattern - only called when first used - */ - async init(): Promise { - if (this.initialized) { - return - } - - // VFS is lazy-loaded and might not be initialized yet - try { - this._vfs = this.brain.vfs() - await this._vfs.init() - } catch (error) { - // VFS initialization failed, will work without artifact support - console.warn('VFS initialization failed, artifact support disabled:', error) - } - - this.initialized = true - } - - /** - * Save a message to the conversation history - * - * Uses: brain.add() with NounType.Message - * Real implementation - stores message with embedding - * - * @param content Message content - * @param role Message role (user, assistant, system, tool) - * @param options Save options (conversationId, metadata, etc.) - * @returns Message ID - */ - async saveMessage( - content: string, - role: MessageRole, - options: SaveMessageOptions = {} - ): Promise { - if (!this.initialized) { - await this.init() - } - - // Generate IDs if not provided - const conversationId = options.conversationId || `conv_${uuidv4()}` - const sessionId = options.sessionId || `session_${uuidv4()}` - const timestamp = Date.now() - - // Build metadata - const metadata: ConversationMessageMetadata = { - role, - conversationId, - sessionId, - timestamp, - problemSolvingPhase: options.phase, - confidence: options.confidence, - artifacts: options.artifacts || [], - toolsUsed: options.toolsUsed || [], - references: [], - tags: options.tags || [], - ...options.metadata - } - - // Add message to brain using REAL API - const messageId = await this.brain.add({ - data: content, - type: NounType.Message, - metadata - }) - - // Link to previous message if specified (REAL graph relationship) - if (options.linkToPrevious) { - await this.brain.relate({ - from: options.linkToPrevious, - to: messageId, - type: VerbType.Precedes, - metadata: { - conversationId, - timestamp - } - }) - } - - return messageId - } - - /** - * Link two messages in temporal sequence - * - * Uses: brain.relate() with VerbType.Precedes - * Real implementation - creates graph relationship - * - * @param prevMessageId ID of previous message - * @param nextMessageId ID of next message - * @returns Relationship ID - */ - async linkMessages(prevMessageId: string, nextMessageId: string): Promise { - if (!this.initialized) { - await this.init() - } - - // Create real graph relationship - const verbId = await this.brain.relate({ - from: prevMessageId, - to: nextMessageId, - type: VerbType.Precedes, - metadata: { - timestamp: Date.now() - } - }) - - return verbId - } - - /** - * Get a full conversation thread - * - * Uses: brain.getNoun() and brain.getConnections() - * Real implementation - traverses graph relationships - * - * @param conversationId Conversation ID - * @param options Options (includeArtifacts, etc.) - * @returns Complete conversation thread - */ - async getConversationThread( - conversationId: string, - options: { includeArtifacts?: boolean } = {} - ): Promise { - if (!this.initialized) { - await this.init() - } - - // Search for all messages in conversation (REAL search) - const results = await this.brain.find({ - where: { - conversationId - }, - limit: 10000 // Large limit for full thread - }) - - // Convert results to ConversationMessage format - const messages: ConversationMessage[] = results.map((result: any) => ({ - id: result.id, - content: result.data || result.content || '', - role: result.metadata.role, - metadata: result.metadata as ConversationMessageMetadata, - embedding: result.embedding, - createdAt: result.metadata.timestamp || Date.now(), - updatedAt: result.metadata.timestamp || Date.now() - })) - - // Sort by timestamp - messages.sort((a, b) => a.createdAt - b.createdAt) - - // Build thread metadata - const startTime = messages.length > 0 ? messages[0].createdAt : Date.now() - const endTime = messages.length > 0 ? messages[messages.length - 1].createdAt : undefined - const totalTokens = messages.reduce((sum, msg) => sum + (msg.metadata.tokensUsed || 0), 0) - - const threadMetadata: ConversationThreadMetadata = { - conversationId, - startTime, - endTime, - messageCount: messages.length, - totalTokens, - participants: [...new Set(messages.map(m => m.role))] - } - - // Get artifacts if requested (REAL VFS query) - let artifacts: string[] | undefined - if (options.includeArtifacts && this._vfs) { - artifacts = messages - .flatMap(m => m.metadata.artifacts || []) - .filter((id, idx, arr) => arr.indexOf(id) === idx) - } - - return { - id: conversationId, - metadata: threadMetadata, - messages, - artifacts - } - } - - /** - * Get relevant context for a query - * - * Uses: brain.find() with Triple Intelligence - * Real implementation - semantic + temporal + graph ranking - * - * @param query Query string or context options - * @param options Retrieval options - * @returns Ranked context messages with artifacts - */ - async getRelevantContext( - query: string | ContextRetrievalOptions, - options?: ContextRetrievalOptions - ): Promise { - if (!this.initialized) { - await this.init() - } - - const startTime = Date.now() - - // Normalize options - const opts: ContextRetrievalOptions = typeof query === 'string' - ? { query, ...options } - : query - - const { - query: queryText, - limit = 10, - maxTokens = 50000, - relevanceThreshold = 0.7, - role, - phase, - tags, - minConfidence, - timeRange, - conversationId, - sessionId, - weights = { semantic: 1.0, temporal: 0.5, graph: 0.3 }, - includeArtifacts = false, - includeSimilarConversations = false, - deduplicateClusters = true - } = opts - - // Build metadata filter - const whereFilter: any = {} - if (role) { - whereFilter.role = Array.isArray(role) ? { $in: role } : role - } - if (phase) { - whereFilter.problemSolvingPhase = Array.isArray(phase) ? { $in: phase } : phase - } - if (tags && tags.length > 0) { - whereFilter.tags = { $in: tags } - } - if (minConfidence !== undefined) { - whereFilter.confidence = { $gte: minConfidence } - } - if (timeRange) { - if (timeRange.start !== undefined) { - whereFilter.timestamp = { $gte: timeRange.start } - } - if (timeRange.end !== undefined) { - whereFilter.timestamp = { ...whereFilter.timestamp, $lte: timeRange.end } - } - } - if (conversationId) { - whereFilter.conversationId = conversationId - } - if (sessionId) { - whereFilter.sessionId = sessionId - } - - // Query with Triple Intelligence (REAL) - const findOptions: any = { - limit: limit * 2, // Get more for ranking - where: whereFilter - } - - if (queryText) { - findOptions.like = queryText - } - - const results = await this.brain.find(findOptions) - - // Calculate relevance scores (REAL scoring) - const now = Date.now() - const rankedMessages: RankedMessage[] = results - .map((result: any) => { - // Semantic score (from vector similarity) - const semanticScore = result.score || 0 - - // Temporal score (recency decay) - const ageInDays = (now - (result.metadata.timestamp || now)) / (1000 * 60 * 60 * 24) - const temporalScore = Math.exp(-0.1 * ageInDays) // Decay rate: 0.1 - - // Graph score (would need graph traversal, simplified for now) - const graphScore = 0.5 // Placeholder for now, can enhance later - - // Combined score - const relevanceScore = - (weights.semantic ?? 1.0) * semanticScore + - (weights.temporal ?? 0.5) * temporalScore + - (weights.graph ?? 0.3) * graphScore - - return { - id: result.id, - content: result.data || result.content || '', - role: result.metadata.role, - metadata: result.metadata as ConversationMessageMetadata, - embedding: result.embedding, - createdAt: result.metadata.timestamp || now, - updatedAt: result.metadata.timestamp || now, - relevanceScore, - semanticScore, - temporalScore, - graphScore - } as RankedMessage - }) - .filter((msg: RankedMessage) => msg.relevanceScore >= relevanceThreshold) - .sort((a: RankedMessage, b: RankedMessage) => b.relevanceScore - a.relevanceScore) - - // Deduplicate via clustering if requested - let finalMessages = rankedMessages - if (deduplicateClusters && rankedMessages.length > 5 && this.brain.neural) { - // Use neural clustering to remove duplicates (REAL) - try { - const clusters = await this.brain.neural().clusters({ - maxClusters: Math.ceil(rankedMessages.length / 3), - threshold: 0.85 - }) - - // Keep highest scoring message from each cluster - const kept = new Set() - for (const cluster of clusters) { - const clusterMessages = rankedMessages.filter(msg => - cluster.members?.includes(msg.id) - ) - if (clusterMessages.length > 0) { - const best = clusterMessages.reduce((a, b) => - a.relevanceScore > b.relevanceScore ? a : b - ) - kept.add(best.id) - } - } - - finalMessages = rankedMessages.filter(msg => kept.has(msg.id)) - } catch (error) { - // Clustering failed, use all messages - console.warn('Clustering failed:', error) - } - } - - // Limit by token budget - let totalTokens = 0 - const messagesWithinBudget: RankedMessage[] = [] - for (const msg of finalMessages) { - const tokens = msg.metadata.tokensUsed || Math.ceil(msg.content.length / 4) - if (totalTokens + tokens <= maxTokens) { - messagesWithinBudget.push(msg) - totalTokens += tokens - } else { - break - } - } - - // Get artifacts if requested (REAL VFS) - let artifacts: any[] = [] - if (includeArtifacts && this._vfs) { - const artifactIds = new Set( - messagesWithinBudget.flatMap(msg => msg.metadata.artifacts || []) - ) - - for (const artifactId of artifactIds) { - try { - const entity = await this.brain.get(artifactId) - if (entity) { - artifacts.push({ - id: artifactId, - path: entity.metadata?.path || artifactId, - summary: entity.metadata?.description || undefined - }) - } - } catch (error) { - // Artifact not found, skip - continue - } - } - } - - // Get similar conversations if requested - let similarConversations: any[] = [] - if (includeSimilarConversations && conversationId && this.brain.neural) { - // Use neural neighbors (REAL) - try { - const neighborsResult = await this.brain.neural().neighbors(conversationId, { - limit: 5, - minSimilarity: 0.7 - }) - - similarConversations = neighborsResult.neighbors.map((neighbor: any) => ({ - id: neighbor.id, - title: neighbor.metadata?.title, - summary: neighbor.metadata?.summary, - relevance: neighbor.score, - messageCount: neighbor.metadata?.messageCount || 0 - })) - } catch (error) { - // Neighbors failed, skip - console.warn('Similar conversation search failed:', error) - } - } - - const queryTime = Date.now() - startTime - - return { - messages: messagesWithinBudget.slice(0, limit), - artifacts, - similarConversations, - totalTokens, - metadata: { - queryTime, - messagesConsidered: results.length, - conversationsSearched: new Set(results.map((r: any) => r.metadata.conversationId)).size - } - } - } - - /** - * Search messages semantically - * - * Uses: brain.find() with semantic search - * Real implementation - vector similarity search - * - * @param options Search options - * @returns Search results with scores - */ - async searchMessages(options: ConversationSearchOptions): Promise { - if (!this.initialized) { - await this.init() - } - - const { - query, - limit = 10, - role, - conversationId, - sessionId, - timeRange, - includeMetadata = true, - includeContent = true - } = options - - // Build filter - const whereFilter: any = {} - if (role) { - whereFilter.role = Array.isArray(role) ? { $in: role } : role - } - if (conversationId) { - whereFilter.conversationId = conversationId - } - if (sessionId) { - whereFilter.sessionId = sessionId - } - if (timeRange) { - if (timeRange.start) { - whereFilter.timestamp = { $gte: timeRange.start } - } - if (timeRange.end) { - whereFilter.timestamp = { ...whereFilter.timestamp, $lte: timeRange.end } - } - } - - // Search with Triple Intelligence (REAL) - const results = await this.brain.find({ - query: query, - where: whereFilter, - limit - }) - - // Format results - return results.map((result: any) => { - const message: ConversationMessage = { - id: result.id, - content: includeContent ? (result.data || result.content || '') : '', - role: result.metadata.role, - metadata: includeMetadata ? (result.metadata as ConversationMessageMetadata) : {} as any, - embedding: result.embedding, - createdAt: result.metadata.timestamp || Date.now(), - updatedAt: result.metadata.timestamp || Date.now() - } - - // Create snippet - const content = result.data || result.content || '' - const snippet = content.length > 150 ? content.substring(0, 147) + '...' : content - - return { - message, - score: result.score || 0, - conversationId: result.metadata.conversationId, - snippet: includeContent ? snippet : undefined - } - }) - } - - /** - * Find similar conversations using Neural API - * - * Uses: brain.neural.neighbors() - * Real implementation - semantic similarity with embeddings - * - * @param conversationId Conversation ID to find similar to - * @param limit Maximum number of similar conversations - * @param threshold Minimum similarity threshold - * @returns Similar conversations with relevance scores - */ - async findSimilarConversations( - conversationId: string, - limit: number = 5, - threshold: number = 0.7 - ): Promise> { - if (!this.initialized) { - await this.init() - } - - if (!this.brain.neural) { - throw new Error('Neural API not available') - } - - // Use neural neighbors (REAL) - const neighborsResult = await this.brain.neural().neighbors(conversationId, { - limit: limit, - minSimilarity: threshold - }) - - return neighborsResult.neighbors.map((neighbor: any) => ({ - id: neighbor.id, - relevance: neighbor.score, - metadata: neighbor.metadata - })) - } - - /** - * Get conversation themes via clustering - * - * Uses: brain.neural.clusters() - * Real implementation - semantic clustering - * - * @param conversationId Conversation ID - * @returns Discovered themes - */ - async getConversationThemes(conversationId: string): Promise { - if (!this.initialized) { - await this.init() - } - - if (!this.brain.neural) { - throw new Error('Neural API not available') - } - - // Get messages for conversation - const results = await this.brain.find({ - where: { conversationId }, - limit: 1000 - }) - - if (results.length === 0) { - return [] - } - - // Cluster messages (REAL) - const clusters = await this.brain.neural().clusters({ - maxClusters: Math.min(5, Math.ceil(results.length / 5)), - threshold: 0.75 - }) - - // Convert to themes - return clusters.map((cluster: any, index: number) => ({ - id: `theme_${index}`, - label: cluster.label || `Theme ${index + 1}`, - messages: cluster.members || [], - centroid: cluster.centroid || [], - coherence: cluster.coherence || 0 - })) - } - - /** - * Save an artifact (code, file, etc.) to VFS - * - * Uses: brain.vfs() - * Real implementation - stores in virtual filesystem - * - * @param path VFS path - * @param content File content - * @param options Artifact options - * @returns Artifact entity ID - */ - async saveArtifact( - path: string, - content: string | Buffer, - options: ArtifactOptions - ): Promise { - if (!this.initialized) { - await this.init() - } - - if (!this._vfs) { - throw new Error('VFS not available') - } - - // Write file to VFS (REAL) - await this._vfs.writeFile(path, content) - - // Get the file entity - const entity = await this._vfs.getEntity(path) - - // Link to conversation message if provided - if (options.messageId) { - await this.brain.relate({ - from: options.messageId, - to: entity.id, - type: VerbType.Creates, - metadata: { - conversationId: options.conversationId, - artifactType: options.type || 'other' - } - }) - } - - return entity.id - } - - /** - * Get conversation statistics - * - * Uses: brain.find() with aggregations - * Real implementation - queries and aggregates data - * - * @param conversationId Optional conversation ID to filter - * @returns Conversation statistics - */ - async getConversationStats(conversationId?: string): Promise { - if (!this.initialized) { - await this.init() - } - - // Query messages - const whereFilter = conversationId ? { conversationId } : {} - const results = await this.brain.find({ - where: whereFilter, - limit: 100000 // Large limit for stats - }) - - // Calculate statistics (REAL aggregation) - const conversations = new Set(results.map((r: any) => r.metadata.conversationId)) - const totalMessages = results.length - const totalTokens = results.reduce( - (sum: number, r: any) => sum + (r.metadata.tokensUsed || 0), - 0 - ) - - const timestamps = results.map((r: any) => r.metadata.timestamp || Date.now()) - const oldestMessage = Math.min(...timestamps) - const newestMessage = Math.max(...timestamps) - - // Count by phase - const phases: Record = {} - const roles: Record = {} - - for (const result of results) { - const phase = result.entity.metadata.problemSolvingPhase - const role = result.entity.metadata.role - - if (phase) { - phases[phase] = (phases[phase] || 0) + 1 - } - if (role) { - roles[role] = (roles[role] || 0) + 1 - } - } - - return { - totalConversations: conversations.size, - totalMessages, - totalTokens, - averageMessagesPerConversation: totalMessages / Math.max(1, conversations.size), - averageTokensPerMessage: totalTokens / Math.max(1, totalMessages), - oldestMessage, - newestMessage, - phases: phases as any, - roles: roles as any - } - } - - /** - * Delete a message - * - * Uses: brain.deleteNoun() - * Real implementation - removes from graph - * - * @param messageId Message ID to delete - */ - async deleteMessage(messageId: string): Promise { - if (!this.initialized) { - await this.init() - } - - await this.brain.delete(messageId) - } - - /** - * Export conversation to JSON - * - * Uses: getConversationThread() - * Real implementation - serializes conversation - * - * @param conversationId Conversation ID - * @returns JSON-serializable conversation object - */ - async exportConversation(conversationId: string): Promise { - if (!this.initialized) { - await this.init() - } - - const thread = await this.getConversationThread(conversationId, { - includeArtifacts: true - }) - - return { - version: '1.0', - exportedAt: Date.now(), - conversation: thread - } - } - - /** - * Import conversation from JSON - * - * Uses: saveMessage() and linkMessages() - * Real implementation - recreates conversation - * - * @param data Exported conversation data - * @returns New conversation ID - */ - async importConversation(data: any): Promise { - if (!this.initialized) { - await this.init() - } - - const newConversationId = `conv_${uuidv4()}` - const conversation = data.conversation - - if (!conversation || !conversation.messages) { - throw new Error('Invalid conversation data') - } - - // Import messages in order - const messageIdMap = new Map() - - for (let i = 0; i < conversation.messages.length; i++) { - const msg = conversation.messages[i] - const prevMessageId = i > 0 ? messageIdMap.get(conversation.messages[i - 1].id) : undefined - - const newMessageId = await this.saveMessage(msg.content, msg.role, { - conversationId: newConversationId, - sessionId: conversation.metadata.sessionId, - phase: msg.metadata.problemSolvingPhase, - confidence: msg.metadata.confidence, - tags: msg.metadata.tags, - linkToPrevious: prevMessageId, - metadata: msg.metadata - }) - - messageIdMap.set(msg.id, newMessageId) - } - - return newConversationId - } -} - -/** - * Create a ConversationManager instance - * - * @param brain Brainy instance - * @returns ConversationManager instance - */ -export function createConversationManager(brain: Brainy): ConversationManager { - return new ConversationManager(brain) -} \ No newline at end of file diff --git a/src/conversation/index.ts b/src/conversation/index.ts deleted file mode 100644 index 563f23e5..00000000 --- a/src/conversation/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Conversation Module - Infinite Agent Memory - * - * Provides conversation and context management for AI agents - * Built on Brainy's existing infrastructure - */ - -export { ConversationManager, createConversationManager } from './conversationManager.js' - -export type { - MessageRole, - ProblemSolvingPhase, - ConversationMessage, - ConversationMessageMetadata, - ConversationThread, - ConversationThreadMetadata, - ConversationContext, - RankedMessage, - SaveMessageOptions, - ContextRetrievalOptions, - ConversationSearchOptions, - ConversationSearchResult, - ConversationTheme, - ArtifactOptions, - ConversationStats, - CompactionOptions, - CompactionResult -} from './types.js' \ No newline at end of file diff --git a/src/conversation/types.ts b/src/conversation/types.ts deleted file mode 100644 index 6a26d307..00000000 --- a/src/conversation/types.ts +++ /dev/null @@ -1,277 +0,0 @@ -/** - * Conversation Types for Infinite Agent Memory - * - * Production-ready type definitions for storing and retrieving - * conversation history with semantic search and context management. - */ - -import { NounType, VerbType } from '../types/graphTypes.js' - -/** - * Role of the message sender - */ -export type MessageRole = 'user' | 'assistant' | 'system' | 'tool' - -/** - * Problem-solving phase for tracking agent's progress - */ -export type ProblemSolvingPhase = - | 'understanding' - | 'analysis' - | 'planning' - | 'implementation' - | 'testing' - | 'debugging' - | 'refinement' - | 'completed' - -/** - * Metadata for a conversation message - */ -export interface ConversationMessageMetadata { - role: MessageRole - conversationId: string - sessionId?: string - timestamp: number - - // Agent state tracking - problemSolvingPhase?: ProblemSolvingPhase - confidence?: number // 0-1 confidence score - - // Token tracking - tokensUsed?: number - tokensTotal?: number - - // Context tracking - artifacts?: string[] // IDs or paths of created artifacts - toolsUsed?: string[] // Names of tools/functions used - references?: string[] // IDs of referenced messages/documents - - // Metadata for filtering - tags?: string[] - priority?: number - archived?: boolean - - // Custom metadata - [key: string]: any -} - -/** - * A conversation message with all metadata - */ -export interface ConversationMessage { - id: string - content: string - role: MessageRole - metadata: ConversationMessageMetadata - embedding?: number[] - createdAt: number - updatedAt: number -} - -/** - * Conversation thread metadata - */ -export interface ConversationThreadMetadata { - conversationId: string - sessionId?: string - title?: string - summary?: string - startTime: number - endTime?: number - messageCount: number - totalTokens: number - participants: string[] // user IDs or names - tags?: string[] - archived?: boolean - [key: string]: any -} - -/** - * A conversation thread (collection of messages) - */ -export interface ConversationThread { - id: string - metadata: ConversationThreadMetadata - messages: ConversationMessage[] - artifacts?: string[] // VFS paths or entity IDs -} - -/** - * Options for retrieving relevant context - */ -export interface ContextRetrievalOptions { - // Query - query?: string // Natural language query - conversationId?: string // Limit to specific conversation - sessionId?: string // Limit to specific session - - // Filtering - role?: MessageRole | MessageRole[] - phase?: ProblemSolvingPhase | ProblemSolvingPhase[] - tags?: string[] - minConfidence?: number - timeRange?: { - start?: number - end?: number - } - - // Search parameters - limit?: number // Max messages to return (default: 10) - maxTokens?: number // Token budget for context (default: 50000) - relevanceThreshold?: number // Minimum similarity score (default: 0.7) - - // Ranking weights - weights?: { - semantic?: number // Weight for semantic similarity (default: 1.0) - temporal?: number // Weight for recency (default: 0.5) - graph?: number // Weight for graph relationships (default: 0.3) - } - - // Advanced options - includeArtifacts?: boolean // Include linked code/file artifacts - includeSimilarConversations?: boolean // Include similar past conversations - deduplicateClusters?: boolean // Deduplicate via clustering (default: true) -} - -/** - * Ranked context message with relevance score - */ -export interface RankedMessage extends ConversationMessage { - relevanceScore: number - semanticScore?: number - temporalScore?: number - graphScore?: number - explanation?: string -} - -/** - * Retrieved context result - */ -export interface ConversationContext { - messages: RankedMessage[] - artifacts?: Array<{ - path: string - id: string - content?: string - summary?: string - }> - similarConversations?: Array<{ - id: string - title?: string - summary?: string - relevance: number - messageCount: number - }> - totalTokens: number - metadata: { - queryTime: number - messagesConsidered: number - conversationsSearched: number - } -} - -/** - * Options for saving messages - */ -export interface SaveMessageOptions { - conversationId?: string // Auto-generated if not provided - sessionId?: string - phase?: ProblemSolvingPhase - confidence?: number - artifacts?: string[] - toolsUsed?: string[] - tags?: string[] - linkToPrevious?: string // ID of previous message to link - metadata?: Record // Additional metadata -} - -/** - * Options for conversation search - */ -export interface ConversationSearchOptions { - query: string - limit?: number - role?: MessageRole | MessageRole[] - conversationId?: string - sessionId?: string - timeRange?: { - start?: number - end?: number - } - includeMetadata?: boolean - includeContent?: boolean -} - -/** - * Search result for conversations - */ -export interface ConversationSearchResult { - message: ConversationMessage - score: number - conversationId: string - snippet?: string -} - -/** - * Theme discovered via clustering - */ -export interface ConversationTheme { - id: string - label: string - messages: string[] // Message IDs - centroid: number[] // Vector centroid - coherence: number // How coherent the cluster is (0-1) - keywords?: string[] -} - -/** - * Options for artifact storage - */ -export interface ArtifactOptions { - conversationId: string - messageId?: string - type?: 'code' | 'config' | 'data' | 'document' | 'other' - language?: string - description?: string - metadata?: Record -} - -/** - * Statistics about conversations - */ -export interface ConversationStats { - totalConversations: number - totalMessages: number - totalTokens: number - averageMessagesPerConversation: number - averageTokensPerMessage: number - oldestMessage: number - newestMessage: number - phases: Record - roles: Record -} - -/** - * Compaction strategy options - */ -export interface CompactionOptions { - conversationId: string - strategy?: 'cluster-based' | 'importance-based' | 'hybrid' - keepRatio?: number // Ratio of messages to keep (default: 0.3) - minImportance?: number // Minimum importance score to keep (default: 0.5) - preservePhases?: ProblemSolvingPhase[] // Always keep these phases - preserveRecent?: number // Always keep this many recent messages -} - -/** - * Result of compaction operation - */ -export interface CompactionResult { - originalCount: number - compactedCount: number - removedCount: number - tokensFreed: number - preservedMessageIds: string[] - summaryMessageId?: string -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 2d7764b4..dae3286c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -474,27 +474,3 @@ export type { MCPServiceOptions, MCPTool } - -// Export Conversation API (Infinite Agent Memory) -export { ConversationManager, createConversationManager } from './conversation/index.js' -export { MCPConversationToolset, createConversationToolset } from './mcp/conversationTools.js' - -export type { - MessageRole, - ProblemSolvingPhase, - ConversationMessage, - ConversationMessageMetadata, - ConversationThread, - ConversationThreadMetadata, - ConversationContext, - RankedMessage, - SaveMessageOptions, - ContextRetrievalOptions, - ConversationSearchOptions, - ConversationSearchResult, - ConversationTheme, - ArtifactOptions, - ConversationStats, - CompactionOptions, - CompactionResult -} from './conversation/types.js' diff --git a/src/mcp/conversationTools.ts b/src/mcp/conversationTools.ts deleted file mode 100644 index 35c04b3b..00000000 --- a/src/mcp/conversationTools.ts +++ /dev/null @@ -1,598 +0,0 @@ -/** - * MCP Conversation Tools - * - * Exposes ConversationManager functionality through MCP for Claude Code integration. - * Provides 6 tools for infinite agent memory. - * - * REAL IMPLEMENTATION - Uses ConversationManager which uses real Brainy APIs - */ - -import { v4 as uuidv4 } from '../universal/uuid.js' -import { - MCPResponse, - MCPToolExecutionRequest, - MCPTool, - MCP_VERSION -} from '../types/mcpTypes.js' -import { ConversationManager } from '../conversation/conversationManager.js' -import { Brainy } from '../brainy.js' - -/** - * MCP Conversation Toolset - * - * Provides conversation and context management tools for AI agents - */ -export class MCPConversationToolset { - private conversationManager: ConversationManager - private initialized = false - - /** - * Create MCP Conversation Toolset - * @param brain Brainy instance - */ - constructor(private brain: Brainy) { - this.conversationManager = new ConversationManager(brain) - } - - /** - * Initialize the toolset - */ - async init(): Promise { - if (this.initialized) { - return - } - - await this.conversationManager.init() - this.initialized = true - } - - /** - * Handle MCP tool execution request - * @param request MCP tool execution request - * @returns MCP response - */ - async handleRequest(request: MCPToolExecutionRequest): Promise { - if (!this.initialized) { - await this.init() - } - - try { - const { toolName, parameters } = request - - // Route to appropriate tool handler - switch (toolName) { - case 'conversation_save_message': - return await this.handleSaveMessage(request.requestId, parameters) - - case 'conversation_get_context': - return await this.handleGetContext(request.requestId, parameters) - - case 'conversation_search': - return await this.handleSearch(request.requestId, parameters) - - case 'conversation_get_thread': - return await this.handleGetThread(request.requestId, parameters) - - case 'conversation_save_artifact': - return await this.handleSaveArtifact(request.requestId, parameters) - - case 'conversation_find_similar': - return await this.handleFindSimilar(request.requestId, parameters) - - default: - return this.createErrorResponse( - request.requestId, - 'UNKNOWN_TOOL', - `Unknown conversation tool: ${toolName}` - ) - } - } catch (error) { - return this.createErrorResponse( - request.requestId, - 'INTERNAL_ERROR', - error instanceof Error ? error.message : String(error) - ) - } - } - - /** - * Get available conversation tools - * @returns Array of MCP tool definitions - */ - async getAvailableTools(): Promise { - return [ - { - name: 'conversation_save_message', - description: 'Save a message to conversation history with automatic embedding and indexing', - parameters: { - type: 'object', - properties: { - content: { - type: 'string', - description: 'Message content' - }, - role: { - type: 'string', - enum: ['user', 'assistant', 'system', 'tool'], - description: 'Message role' - }, - conversationId: { - type: 'string', - description: 'Conversation ID (auto-generated if not provided)' - }, - sessionId: { - type: 'string', - description: 'Session ID (optional)' - }, - phase: { - type: 'string', - enum: [ - 'understanding', - 'analysis', - 'planning', - 'implementation', - 'testing', - 'debugging', - 'refinement', - 'completed' - ], - description: 'Problem-solving phase' - }, - confidence: { - type: 'number', - minimum: 0, - maximum: 1, - description: 'Confidence score (0-1)' - }, - artifacts: { - type: 'array', - items: { type: 'string' }, - description: 'Artifact IDs or paths' - }, - toolsUsed: { - type: 'array', - items: { type: 'string' }, - description: 'Names of tools used' - }, - tags: { - type: 'array', - items: { type: 'string' }, - description: 'Tags for categorization' - }, - linkToPrevious: { - type: 'string', - description: 'ID of previous message to link' - } - }, - required: ['content', 'role'] - } - }, - { - name: 'conversation_get_context', - description: 'Retrieve relevant context from conversation history using semantic search', - parameters: { - type: 'object', - properties: { - query: { - type: 'string', - description: 'Query string for context retrieval' - }, - conversationId: { - type: 'string', - description: 'Limit to specific conversation' - }, - limit: { - type: 'number', - description: 'Maximum messages to return (default: 10)', - default: 10 - }, - maxTokens: { - type: 'number', - description: 'Token budget for context (default: 50000)', - default: 50000 - }, - relevanceThreshold: { - type: 'number', - minimum: 0, - maximum: 1, - description: 'Minimum similarity score (default: 0.7)', - default: 0.7 - }, - role: { - oneOf: [ - { type: 'string', enum: ['user', 'assistant', 'system', 'tool'] }, - { type: 'array', items: { type: 'string' } } - ], - description: 'Filter by message role' - }, - tags: { - type: 'array', - items: { type: 'string' }, - description: 'Filter by tags' - }, - includeArtifacts: { - type: 'boolean', - description: 'Include linked artifacts', - default: false - }, - includeSimilarConversations: { - type: 'boolean', - description: 'Include similar past conversations', - default: false - } - }, - required: ['query'] - } - }, - { - name: 'conversation_search', - description: 'Search messages semantically across all conversations', - parameters: { - type: 'object', - properties: { - query: { - type: 'string', - description: 'Search query' - }, - limit: { - type: 'number', - description: 'Maximum results (default: 10)', - default: 10 - }, - conversationId: { - type: 'string', - description: 'Limit to specific conversation' - }, - role: { - oneOf: [ - { type: 'string', enum: ['user', 'assistant', 'system', 'tool'] }, - { type: 'array', items: { type: 'string' } } - ], - description: 'Filter by role' - }, - timeRange: { - type: 'object', - properties: { - start: { type: 'number', description: 'Start timestamp' }, - end: { type: 'number', description: 'End timestamp' } - }, - description: 'Time range filter' - } - }, - required: ['query'] - } - }, - { - name: 'conversation_get_thread', - description: 'Get full conversation thread with all messages', - parameters: { - type: 'object', - properties: { - conversationId: { - type: 'string', - description: 'Conversation ID' - }, - includeArtifacts: { - type: 'boolean', - description: 'Include linked artifacts', - default: false - } - }, - required: ['conversationId'] - } - }, - { - name: 'conversation_save_artifact', - description: 'Save code/file artifact and link to conversation', - parameters: { - type: 'object', - properties: { - path: { - type: 'string', - description: 'VFS path for artifact' - }, - content: { - type: 'string', - description: 'Artifact content' - }, - conversationId: { - type: 'string', - description: 'Conversation ID' - }, - messageId: { - type: 'string', - description: 'Message ID to link artifact to' - }, - type: { - type: 'string', - enum: ['code', 'config', 'data', 'document', 'other'], - description: 'Artifact type' - }, - language: { - type: 'string', - description: 'Programming language (for code artifacts)' - }, - description: { - type: 'string', - description: 'Artifact description' - } - }, - required: ['path', 'content', 'conversationId'] - } - }, - { - name: 'conversation_find_similar', - description: 'Find similar past conversations using semantic similarity', - parameters: { - type: 'object', - properties: { - conversationId: { - type: 'string', - description: 'Conversation ID to find similar to' - }, - limit: { - type: 'number', - description: 'Maximum results (default: 5)', - default: 5 - }, - threshold: { - type: 'number', - minimum: 0, - maximum: 1, - description: 'Minimum similarity threshold (default: 0.7)', - default: 0.7 - } - }, - required: ['conversationId'] - } - } - ] - } - - /** - * Handle save_message tool - * REAL: Uses ConversationManager.saveMessage() - */ - private async handleSaveMessage( - requestId: string, - parameters: any - ): Promise { - const { - content, - role, - conversationId, - sessionId, - phase, - confidence, - artifacts, - toolsUsed, - tags, - linkToPrevious - } = parameters - - // Validate required parameters - if (!content || !role) { - return this.createErrorResponse( - requestId, - 'INVALID_PARAMETERS', - 'Missing required parameters: content and role are required' - ) - } - - // Save message (REAL) - const messageId = await this.conversationManager.saveMessage(content, role, { - conversationId, - sessionId, - phase, - confidence, - artifacts, - toolsUsed, - tags, - linkToPrevious - }) - - return this.createSuccessResponse(requestId, { - messageId, - conversationId: conversationId || messageId.split('_')[0] - }) - } - - /** - * Handle get_context tool - * REAL: Uses ConversationManager.getRelevantContext() - */ - private async handleGetContext( - requestId: string, - parameters: any - ): Promise { - const { query, ...options } = parameters - - if (!query) { - return this.createErrorResponse( - requestId, - 'INVALID_PARAMETERS', - 'Missing required parameter: query' - ) - } - - // Get context (REAL) - const context = await this.conversationManager.getRelevantContext(query, options) - - return this.createSuccessResponse(requestId, context) - } - - /** - * Handle search tool - * REAL: Uses ConversationManager.searchMessages() - */ - private async handleSearch( - requestId: string, - parameters: any - ): Promise { - const { query } = parameters - - if (!query) { - return this.createErrorResponse( - requestId, - 'INVALID_PARAMETERS', - 'Missing required parameter: query' - ) - } - - // Search messages (REAL) - const results = await this.conversationManager.searchMessages(parameters) - - return this.createSuccessResponse(requestId, { - results, - count: results.length - }) - } - - /** - * Handle get_thread tool - * REAL: Uses ConversationManager.getConversationThread() - */ - private async handleGetThread( - requestId: string, - parameters: any - ): Promise { - const { conversationId, includeArtifacts = false } = parameters - - if (!conversationId) { - return this.createErrorResponse( - requestId, - 'INVALID_PARAMETERS', - 'Missing required parameter: conversationId' - ) - } - - // Get thread (REAL) - const thread = await this.conversationManager.getConversationThread( - conversationId, - { includeArtifacts } - ) - - return this.createSuccessResponse(requestId, thread) - } - - /** - * Handle save_artifact tool - * REAL: Uses ConversationManager.saveArtifact() - */ - private async handleSaveArtifact( - requestId: string, - parameters: any - ): Promise { - const { - path, - content, - conversationId, - messageId, - type, - language, - description - } = parameters - - if (!path || !content || !conversationId) { - return this.createErrorResponse( - requestId, - 'INVALID_PARAMETERS', - 'Missing required parameters: path, content, and conversationId are required' - ) - } - - // Save artifact (REAL) - const artifactId = await this.conversationManager.saveArtifact(path, content, { - conversationId, - messageId, - type, - language, - description - }) - - return this.createSuccessResponse(requestId, { - artifactId, - path - }) - } - - /** - * Handle find_similar tool - * REAL: Uses ConversationManager.findSimilarConversations() - */ - private async handleFindSimilar( - requestId: string, - parameters: any - ): Promise { - const { conversationId, limit = 5, threshold = 0.7 } = parameters - - if (!conversationId) { - return this.createErrorResponse( - requestId, - 'INVALID_PARAMETERS', - 'Missing required parameter: conversationId' - ) - } - - // Find similar (REAL) - const similar = await this.conversationManager.findSimilarConversations( - conversationId, - limit, - threshold - ) - - return this.createSuccessResponse(requestId, { - similar, - count: similar.length - }) - } - - /** - * Create success response - */ - private createSuccessResponse(requestId: string, data: any): MCPResponse { - return { - success: true, - requestId, - version: MCP_VERSION, - data - } - } - - /** - * Create error response - */ - private createErrorResponse( - requestId: string, - code: string, - message: string, - details?: any - ): MCPResponse { - return { - success: false, - requestId, - version: MCP_VERSION, - error: { - code, - message, - details - } - } - } - - /** - * Generate request ID - */ - generateRequestId(): string { - return uuidv4() - } -} - -/** - * Create MCP conversation toolset - * @param brain Brainy instance - * @returns MCPConversationToolset instance - */ -export function createConversationToolset(brain: Brainy): MCPConversationToolset { - return new MCPConversationToolset(brain) -} \ No newline at end of file