chore(sdk): remove orphaned @hanzo/ai AgentKit copy from the extension

The extension's packages/ai was a duplicate 'AgentKit with MCP' (canonical home:
hanzoai/ai) that squatted the @hanzo/ai name and collided with the published AI
client. Nothing consumes it anymore (all 16 adapters resolve @hanzo/ai to the
published 0.2.0 client from npm; no @hanzo/ai/server|react subpath imports; not
in the release publish). Removing it makes @hanzo/ai in the workspace
unambiguously the AI client.

Taxonomy (per direction): @hanzo/ai = AI client · @hanzo/agent = Agent SDK ·
@hanzo/ui = all v8 components (hanzoai/ui, agent UI → @hanzo/ui/agent) ·
@hanzo/gui = v8 framework (hanzoai/gui, Tauri+Tamagui unified). Removed the
@hanzo/ai CI test step. github 84 + slack 68 still green.
This commit is contained in:
Hanzo Dev
2026-07-04 00:19:31 -07:00
parent 6baf19e04c
commit e37b8e7a07
43 changed files with 0 additions and 4898 deletions
-2
View File
@@ -32,8 +32,6 @@ jobs:
- name: Test ACI package
run: pnpm --filter @hanzo/aci test
- name: Test AI package
run: pnpm --filter @hanzo/ai test
- name: Test auth core
run: pnpm --filter @hanzo/auth test
-12
View File
@@ -1,12 +0,0 @@
Copyright 2025 Hanzo Industries Inc
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-236
View File
@@ -1,236 +0,0 @@
# @hanzo/ai
The AI Toolkit for TypeScript - AgentKit with Model Context Protocol (MCP) support.
## Features
- 🤖 **Multi-Provider Support**: OpenAI, Anthropic, Google, Mistral, Bedrock, Vertex AI, Cohere, and more
- 🔧 **MCP Integration**: Full Model Context Protocol support for tool calling
- 🌐 **Agent Networks**: Create and orchestrate multiple AI agents
- 📊 **Observability**: Built-in telemetry with OpenTelemetry support
- 🔄 **Streaming**: First-class streaming support for all providers
- 📝 **Type Safety**: Full TypeScript support with comprehensive types
- 🎯 **Unified API**: Consistent interface across all providers
## Installation
```bash
npm install @hanzo/ai
```
## Quick Start
### Basic Text Generation
```typescript
import { generateText, openai } from '@hanzo/ai';
const result = await generateText({
model: openai({ apiKey: process.env.OPENAI_API_KEY })('gpt-4'),
prompt: 'What is the meaning of life?',
maxTokens: 500
});
console.log(result.text);
```
### Creating an Agent
```typescript
import { createAgent, anthropic } from '@hanzo/ai';
const agent = createAgent({
name: 'Assistant',
model: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })('claude-3-sonnet'),
instructions: 'You are a helpful assistant.',
tools: [
{
name: 'calculate',
description: 'Perform mathematical calculations',
parameters: {
type: 'object',
properties: {
expression: { type: 'string' }
}
},
handler: async ({ expression }) => {
return { result: eval(expression) };
}
}
]
});
const response = await agent.run('What is 2 + 2?');
```
### Agent Networks
```typescript
import { createNetwork, createAgent, openai, anthropic } from '@hanzo/ai';
const network = createNetwork({
agents: [
createAgent({
name: 'researcher',
model: openai({ apiKey: process.env.OPENAI_API_KEY })('gpt-4'),
instructions: 'You are a research specialist.'
}),
createAgent({
name: 'writer',
model: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })('claude-3-sonnet'),
instructions: 'You are a creative writer.'
})
]
});
// Agents can collaborate
const result = await network.run('Research and write a short story about AI');
```
### Streaming
```typescript
import { generateStream, google } from '@hanzo/ai';
const stream = await generateStream({
model: google({ apiKey: process.env.GOOGLE_API_KEY })('gemini-pro'),
prompt: 'Tell me a story',
maxTokens: 1000
});
for await (const chunk of stream) {
process.stdout.write(chunk.text);
}
```
### Object Generation
```typescript
import { generateObject, openai } from '@hanzo/ai';
import { z } from 'zod';
const schema = z.object({
name: z.string(),
age: z.number(),
interests: z.array(z.string())
});
const result = await generateObject({
model: openai({ apiKey: process.env.OPENAI_API_KEY })('gpt-4'),
prompt: 'Generate a random person profile',
schema
});
console.log(result.object); // Typed as { name: string, age: number, interests: string[] }
```
## MCP Support
@hanzo/ai includes full Model Context Protocol support:
```typescript
import { createAgent } from '@hanzo/ai';
import { createMCPClient } from '@hanzo/ai/mcp';
// Connect to an MCP server
const mcpClient = await createMCPClient({
command: 'node',
args: ['path/to/mcp-server.js']
});
// Use MCP tools with an agent
const agent = createAgent({
name: 'mcp-agent',
model: openai({ apiKey: process.env.OPENAI_API_KEY })('gpt-4'),
tools: await mcpClient.getTools()
});
```
## Providers
Supported providers with their respective models:
- **OpenAI**: GPT-4, GPT-3.5, o1-preview, o1-mini
- **Anthropic**: Claude 3 (Opus, Sonnet, Haiku), Claude 2
- **Google**: Gemini Pro, Gemini Ultra, PaLM
- **Mistral**: Mistral Large, Medium, Small, Mixtral
- **AWS Bedrock**: Access to multiple models
- **Google Vertex AI**: Enterprise Google AI
- **Cohere**: Command, Generate, Embed
- **Hanzo**: Custom Hanzo models
## Telemetry
Built-in telemetry support with OpenTelemetry:
```typescript
import { createHanzoCloudTelemetry } from '@hanzo/ai/telemetry';
// Initialize telemetry
const telemetry = createHanzoCloudTelemetry({
apiKey: process.env.HANZO_API_KEY,
serviceName: 'my-ai-app'
});
// All AI operations are automatically traced
```
## Advanced Usage
### Custom Providers
```typescript
import { createProvider } from '@hanzo/ai';
const customProvider = createProvider({
name: 'custom',
generateText: async ({ prompt, maxTokens }) => {
// Your implementation
return { text: 'Response' };
}
});
```
### Tool Creation
```typescript
import { createTool } from '@hanzo/ai';
const weatherTool = createTool({
name: 'get_weather',
description: 'Get current weather for a location',
parameters: {
type: 'object',
properties: {
location: { type: 'string' }
},
required: ['location']
},
handler: async ({ location }) => {
// Implementation
return { temperature: 72, condition: 'sunny' };
}
});
```
### State Management
```typescript
import { createState } from '@hanzo/ai';
const state = createState({
messages: [],
context: {}
});
// Use with agents for conversation memory
const agent = createAgent({
name: 'stateful-agent',
model: openai({ apiKey: process.env.OPENAI_API_KEY })('gpt-4'),
state
});
```
## License
MIT © Hanzo AI
-369
View File
@@ -1,369 +0,0 @@
# Telemetry & Observability
@hanzo/ai includes comprehensive telemetry and observability features that integrate seamlessly with Hanzo Cloud's monitoring platform.
## Overview
The telemetry system provides:
- Distributed tracing with OpenTelemetry
- Structured logging with Winston
- Metrics collection (counters, gauges, histograms)
- Session tracking
- Error tracking and reporting
- Performance monitoring
## Basic Usage
### Local Telemetry
```typescript
import { createAgent, Telemetry } from '@hanzo/ai';
// Create a basic telemetry instance
const telemetry = new Telemetry({
serviceName: 'my-ai-service',
enabled: true
});
// Use with agents
const agent = createAgent({
name: 'my-agent',
// ... other config
});
const result = await agent.run({
messages: [{ role: 'user', content: 'Hello' }],
context: { telemetry }
});
```
### Hanzo Cloud Integration
```typescript
import { createHanzoCloudTelemetry } from '@hanzo/ai/telemetry/hanzo-cloud';
// Initialize Hanzo Cloud telemetry
const telemetry = createHanzoCloudTelemetry({
cloudUrl: 'https://cloud.hanzo.ai',
apiKey: process.env.HANZO_CLOUD_API_KEY!,
projectId: process.env.HANZO_PROJECT_ID!,
environment: 'production',
serviceName: 'customer-support-ai',
serviceVersion: '1.0.0'
});
```
## Features
### Distributed Tracing
Track execution flow across agents and networks:
```typescript
// Trace a custom operation
const result = await telemetry.trace(
'process_order',
async (span) => {
span.setAttributes({
'order.id': orderId,
'order.amount': amount
});
// Your logic here
return processOrder(orderId);
},
{
kind: SpanKind.CLIENT,
attributes: {
'service.operation': 'order_processing'
}
}
);
```
### Structured Logging
Log with automatic trace context:
```typescript
telemetry.log('info', 'Processing customer request', {
customerId: '12345',
requestType: 'support'
});
telemetry.log('error', 'Failed to process request', {
error: error.message,
stack: error.stack
});
```
### Metrics Collection
Track key performance indicators:
```typescript
// Counters
telemetry.increment('api.requests', 1, {
endpoint: '/chat',
method: 'POST'
});
// Gauges
telemetry.gauge('queue.size', queueLength, {
queue: 'processing'
});
// Histograms
telemetry.histogram('response.time', responseTime, {
endpoint: '/chat'
});
```
### Agent & Network Metrics
Automatic metrics for agent executions:
- `agent.executions` - Count of agent runs
- `agent.execution.duration` - Agent execution time
- `agent.tokens.used` - Token usage per agent
- `tool.executions` - Tool usage statistics
- `network.iterations` - Network iteration count
- `network.execution.duration` - Total network execution time
### Session Tracking
Track related executions:
```typescript
// Create a session
const sessionId = telemetry.createSession();
// All subsequent operations will be linked to this session
const result = await network.run({
messages: [...],
telemetry
});
```
### Error Tracking
Automatic error capture with context:
```typescript
try {
await agent.run({ messages, telemetry });
} catch (error) {
// Errors are automatically recorded with span context
telemetry.recordException(error);
}
```
## Network Telemetry
Networks provide additional telemetry:
```typescript
const network = createNetwork({
name: 'support_network',
agents: [classifier, techSupport, billing],
// ...
});
// Automatic tracking of:
// - Router decisions
// - Agent handoffs
// - State changes
// - Iteration count
// - Total execution time
const result = await network.run({
messages: [...],
telemetry
});
```
## MCP Server Telemetry
Track MCP server connections:
```typescript
// Automatic tracking when MCP servers are connected
const agent = createAgent({
name: 'mcp-agent',
mcpServers: [{
name: 'file-system',
transport: { type: 'stdio', command: 'mcp-fs' }
}]
});
// Metrics tracked:
// - mcp.connections
// - mcp.servers.active
// - mcp.tool.calls
```
## Best Practices
### 1. Use Structured Attributes
```typescript
span.setAttributes({
'user.id': userId,
'user.tier': 'premium',
'request.type': 'chat',
'request.model': 'gpt-4'
});
```
### 2. Create Meaningful Spans
```typescript
await telemetry.trace('user_request', async () => {
await telemetry.trace('validate_input', validateInput);
await telemetry.trace('process_with_ai', processAI);
await telemetry.trace('format_response', formatResponse);
});
```
### 3. Track Business Metrics
```typescript
// Track business-relevant metrics
telemetry.increment('revenue.processed', amount, {
currency: 'USD',
paymentMethod: 'stripe'
});
telemetry.gauge('customer.satisfaction', score, {
surveyType: 'nps'
});
```
### 4. Use Session Context
```typescript
// Link related operations
const sessionId = telemetry.createSession();
for (const message of conversation) {
await handleMessage(message, telemetry);
}
```
### 5. Proper Cleanup
```typescript
// Always flush telemetry before shutdown
process.on('SIGTERM', async () => {
await telemetry.shutdown();
process.exit(0);
});
```
## Viewing Telemetry Data
### Hanzo Cloud Console
Access your telemetry data at:
- Traces: `https://cloud.hanzo.ai/projects/{projectId}/traces`
- Metrics: `https://cloud.hanzo.ai/projects/{projectId}/metrics`
- Logs: `https://cloud.hanzo.ai/projects/{projectId}/logs`
### Local Development
In development, telemetry outputs to console with structured JSON:
```json
{
"timestamp": "2024-01-15T10:30:45.123Z",
"level": "info",
"message": "Agent executed",
"trace_id": "abc123",
"span_id": "def456",
"agent": "classifier",
"duration": 245,
"success": true
}
```
## Performance Impact
The telemetry system is designed for minimal overhead:
- Async span processing
- Batched metric collection
- Configurable sampling
- Automatic span pruning
- Efficient memory usage
Disable in performance-critical paths:
```typescript
const telemetry = new Telemetry({
enabled: process.env.NODE_ENV === 'production'
});
```
## Troubleshooting
### Missing Traces
Check:
1. Telemetry is enabled
2. API key is valid
3. Network connectivity to Hanzo Cloud
4. Proper span nesting
### High Memory Usage
- Reduce span attributes
- Enable sampling
- Decrease batch size
- Check for span leaks
### Debug Mode
Enable debug logging:
```typescript
const telemetry = createHanzoCloudTelemetry({
// ...
logLevel: 'debug'
});
```
## Advanced Features
### Custom Span Processors
```typescript
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
// Add custom processing
const processor = new BatchSpanProcessor(customExporter);
telemetry.addSpanProcessor(processor);
```
### Context Propagation
```typescript
// Get trace context for external services
const headers = telemetry.getTraceContext();
// Make external request with trace context
await fetch(url, {
headers: {
...headers,
'Content-Type': 'application/json'
}
});
```
### Multi-Region Support
```typescript
const telemetry = createHanzoCloudTelemetry({
cloudUrl: process.env.HANZO_CLOUD_URL || 'https://cloud.hanzo.ai',
// Auto-detected from environment
region: process.env.HANZO_CLOUD_REGION
});
```
-263
View File
@@ -1,263 +0,0 @@
/**
* Example: Using Hanzo AI with Cloud Telemetry
*
* This example shows how to integrate agents and networks with Hanzo Cloud's
* observability platform for comprehensive monitoring and debugging.
*/
import { createAgent, createNetwork } from '@hanzo/ai';
import { createHanzoCloudTelemetry } from '@hanzo/ai/telemetry/hanzo-cloud';
import { commonTools } from '@hanzo/ai/tools';
import { OpenAIProvider } from '@hanzo/ai/providers/openai';
// Initialize Hanzo Cloud telemetry
const telemetry = createHanzoCloudTelemetry({
cloudUrl: process.env.HANZO_CLOUD_URL || 'https://cloud.hanzo.ai',
apiKey: process.env.HANZO_CLOUD_API_KEY!,
projectId: process.env.HANZO_PROJECT_ID!,
environment: process.env.NODE_ENV || 'development',
serviceName: 'customer-support-ai',
serviceVersion: '1.0.0',
logLevel: 'info'
});
// Create a session for tracking related executions
const sessionId = telemetry.createSession();
console.log(`Started telemetry session: ${sessionId}`);
// Create agents with telemetry integration
const classifierAgent = createAgent({
name: 'classifier',
description: 'Classifies customer inquiries',
system: `You are a customer inquiry classifier. Analyze the customer's message and classify it into one of these categories:
- technical_support
- billing
- product_info
- complaint
- other`,
tools: [
commonTools.done(),
commonTools.handoff()
]
});
const techSupportAgent = createAgent({
name: 'tech_support',
description: 'Handles technical support issues',
system: 'You are a technical support specialist. Help customers resolve technical issues with our products.',
tools: [
commonTools.done(),
commonTools.askUser()
]
});
const billingAgent = createAgent({
name: 'billing',
description: 'Handles billing inquiries',
system: 'You are a billing specialist. Help customers with payment, subscription, and invoice questions.',
tools: [
commonTools.done(),
commonTools.remember(),
commonTools.recall()
]
});
// Create network with telemetry
const supportNetwork = createNetwork({
name: 'customer_support',
agents: [classifierAgent, techSupportAgent, billingAgent],
defaultModel: new OpenAIProvider({
apiKey: process.env.OPENAI_API_KEY!,
model: 'gpt-4'
}),
router: (context) => {
// First iteration: always start with classifier
if (context.iteration === 0) {
return context.network.getAgent('classifier');
}
// Check if classifier has determined the category
const category = context.state.get('category');
const nextAgent = context.state.get('nextAgent');
if (nextAgent) {
context.state.delete('nextAgent'); // Clear for next iteration
return context.network.getAgent(nextAgent);
}
if (category === 'technical_support') {
return context.network.getAgent('tech_support');
} else if (category === 'billing') {
return context.network.getAgent('billing');
}
return undefined; // No more agents to run
}
});
// Example: Process customer inquiry with full telemetry
async function handleCustomerInquiry(message: string) {
// Create a span for the entire operation
return telemetry.trace(
'customer_inquiry',
async (span) => {
// Add customer context
span.setAttributes({
'customer.message.length': message.length,
'customer.session.id': sessionId
});
try {
// Log the inquiry
telemetry.log('info', 'Processing customer inquiry', {
messagePreview: message.substring(0, 100)
});
// Run the support network
const result = await supportNetwork.run({
messages: [
{ role: 'user', content: message }
],
telemetry // Pass telemetry instance
});
// Record success metrics
telemetry.increment('customer.inquiries.processed', 1, {
status: 'success',
category: result.state.category || 'unknown'
});
// Log the resolution
telemetry.log('info', 'Customer inquiry resolved', {
iterations: result.iterations,
finalAgent: result.history[result.history.length - 1]?.agent,
category: result.state.category
});
return result;
} catch (error) {
// Record failure metrics
telemetry.increment('customer.inquiries.processed', 1, {
status: 'error'
});
telemetry.log('error', 'Failed to process customer inquiry', {
error: error instanceof Error ? error.message : String(error)
});
throw error;
}
},
{
attributes: {
'inquiry.type': 'customer_support'
}
}
);
}
// Example: Monitor streaming responses
async function handleStreamingInquiry(message: string) {
const stream = supportNetwork.stream({
messages: [
{ role: 'user', content: message }
],
telemetry
});
let totalTokens = 0;
for await (const chunk of stream) {
// Track streaming metrics
if (chunk.type === 'content') {
totalTokens += chunk.content.length;
telemetry.gauge('streaming.tokens.current', totalTokens, {
agent: chunk.agent
});
} else if (chunk.type === 'agent:start') {
telemetry.log('debug', `Agent ${chunk.agent} started at iteration ${chunk.iteration}`);
} else if (chunk.type === 'agent:complete') {
telemetry.histogram('agent.streaming.duration', chunk.duration, {
agent: chunk.agent
});
}
// Process chunk...
console.log(chunk);
}
}
// Example: Batch processing with telemetry
async function processBatchInquiries(inquiries: string[]) {
telemetry.log('info', `Starting batch processing of ${inquiries.length} inquiries`);
const results = await Promise.allSettled(
inquiries.map((inquiry, index) =>
telemetry.trace(
`batch_inquiry_${index}`,
() => handleCustomerInquiry(inquiry),
{
attributes: {
'batch.index': index,
'batch.total': inquiries.length
}
}
)
)
);
// Analyze results
const successful = results.filter(r => r.status === 'fulfilled').length;
const failed = results.filter(r => r.status === 'rejected').length;
telemetry.gauge('batch.success.rate', successful / inquiries.length, {
batchSize: inquiries.length
});
telemetry.log('info', 'Batch processing complete', {
total: inquiries.length,
successful,
failed
});
return results;
}
// Example usage
async function main() {
try {
// Single inquiry
const result = await handleCustomerInquiry(
"I'm having trouble logging into my account. It says my password is incorrect but I'm sure it's right."
);
console.log('Result:', result);
// Streaming inquiry
await handleStreamingInquiry(
"My last bill seems higher than usual. Can you explain the charges?"
);
// Batch processing
const batchResults = await processBatchInquiries([
"How do I reset my password?",
"What are your business hours?",
"I want to cancel my subscription",
"The app keeps crashing on startup"
]);
console.log(`Processed ${batchResults.length} inquiries`);
} finally {
// Ensure telemetry is flushed before exit
await telemetry.shutdown();
}
}
// Run the example
if (require.main === module) {
main().catch(console.error);
}
// Export for testing
export { handleCustomerInquiry, supportNetwork, telemetry };
-83
View File
@@ -1,83 +0,0 @@
{
"name": "@hanzo/ai",
"version": "0.1.1",
"description": "The AI Toolkit for TypeScript - AgentKit with MCP support",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
"README.md",
"LICENSE"
],
"publishConfig": {
"access": "public"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./server": {
"types": "./dist/server/index.d.ts",
"import": "./dist/server/index.mjs",
"require": "./dist/server/index.js"
},
"./react": {
"types": "./dist/react/index.d.ts",
"import": "./dist/react/index.mjs",
"require": "./dist/react/index.js"
}
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"test": "vitest run",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.22.0",
"eventsource-parser": "^1.0.0",
"nanoid": "^5.0.0",
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/resources": "^2.9.0",
"@opentelemetry/semantic-conventions": "^1.41.1",
"@opentelemetry/instrumentation": "^0.220.0",
"@opentelemetry/sdk-trace-node": "^2.9.0",
"@opentelemetry/sdk-trace-base": "^2.9.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.220.0",
"winston": "^3.11.0"
},
"peerDependencies": {
"react": "^18.0.0",
"zod": "^3.0.0 || ^4.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
}
},
"devDependencies": {
"@types/node": "^20.0.0",
"@types/react": "^18.0.0",
"tsup": "^8.0.0",
"typescript": "^5.3.0",
"vitest": "^3.2.6"
},
"keywords": [
"ai",
"llm",
"agents",
"mcp",
"typescript",
"hanzo"
],
"author": "Hanzo AI",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/hanzoai/ai.git"
}
}
-429
View File
@@ -1,429 +0,0 @@
/**
* Agent implementation with MCP support
*/
import { z } from 'zod';
import { Tool } from './tool';
import { MCPServer } from '../mcp/types';
import { ModelInterface } from '../types';
import { Telemetry, SpanKind } from '../telemetry';
import { nanoid } from 'nanoid';
export interface AgentConfig {
name: string;
description?: string;
system?: string;
tools?: Tool[];
mcpServers?: MCPServer[];
model?: ModelInterface;
temperature?: number;
maxTokens?: number;
metadata?: Record<string, any>;
}
export interface AgentContext {
agent: Agent;
network?: any; // Will be Network type
state?: any;
telemetry: Telemetry;
}
export interface AgentRunOptions {
messages: any[];
model?: ModelInterface;
context?: Partial<AgentContext>;
stream?: boolean;
}
export class Agent {
readonly id: string;
readonly name: string;
readonly description?: string;
readonly system?: string;
readonly tools: Map<string, Tool>;
readonly mcpServers: MCPServer[];
readonly model?: ModelInterface;
readonly temperature?: number;
readonly maxTokens?: number;
readonly metadata: Record<string, any>;
private mcpTools: Map<string, any> = new Map();
private initialized = false;
constructor(config: AgentConfig) {
this.id = nanoid();
this.name = config.name;
this.description = config.description;
this.system = config.system;
this.tools = new Map();
this.mcpServers = config.mcpServers || [];
this.model = config.model;
this.temperature = config.temperature;
this.maxTokens = config.maxTokens;
this.metadata = config.metadata || {};
// Register tools
if (config.tools) {
for (const tool of config.tools) {
this.tools.set(tool.name, tool);
}
}
}
async initialize(): Promise<void> {
if (this.initialized) return;
// Initialize MCP servers
for (const server of this.mcpServers) {
await this.connectMCPServer(server);
}
this.initialized = true;
}
private async connectMCPServer(server: MCPServer): Promise<void> {
// Import MCP client dynamically
const { MCPClient } = await import('../mcp/client');
const client = new MCPClient();
await client.connect({
name: server.name,
transport: server.transport
});
// Get available tools from MCP server
const tools = await client.listTools();
// Register MCP tools
for (const tool of tools) {
this.mcpTools.set(`${server.name}:${tool.name}`, {
server,
client,
tool
});
}
}
async run(options: AgentRunOptions): Promise<any> {
await this.initialize();
const model = options.model || this.model;
if (!model) {
throw new Error(`No model specified for agent ${this.name}`);
}
// Build context
const context: AgentContext = {
agent: this,
network: options.context?.network,
state: options.context?.state,
telemetry: options.context?.telemetry || new Telemetry()
};
// Prepare tools for the model
const availableTools = this.getAllTools();
// Add system message if specified
const messages = [...options.messages];
if (this.system) {
messages.unshift({
role: 'system',
content: this.system
});
}
// Execute with telemetry
return context.telemetry.trace(
`agent.${this.name}`,
async (span) => {
// Add agent metadata to span
span.setAttributes({
'agent.name': this.name,
'agent.id': this.id,
'agent.model': model.name || 'unknown',
'agent.tools.count': this.getAllTools().size,
'agent.mcp.servers': this.mcpServers.length,
'agent.messages.count': messages.length
});
const startTime = Date.now();
try {
let result;
if (options.stream) {
result = await this.runStream({ ...options, messages, model, context });
} else {
result = await this.runComplete({ ...options, messages, model, context });
}
const duration = Date.now() - startTime;
// Record metrics
if (context.telemetry && 'recordAgentExecution' in context.telemetry) {
(context.telemetry as any).recordAgentExecution(
this.name,
duration,
true,
{
model: model.name,
messageCount: messages.length,
toolsUsed: result.toolCalls?.length || 0
}
);
}
return result;
} catch (error) {
const duration = Date.now() - startTime;
// Record failed execution
if (context.telemetry && 'recordAgentExecution' in context.telemetry) {
(context.telemetry as any).recordAgentExecution(
this.name,
duration,
false,
{
model: model.name,
error: error instanceof Error ? error.message : String(error)
}
);
}
throw error;
}
},
{
kind: SpanKind.CLIENT,
attributes: {
'agent.type': 'llm'
}
}
);
}
private async runComplete(options: any): Promise<any> {
const { messages, model, context } = options;
const response = await model.complete({
messages,
tools: Array.from(this.getAllTools().values()),
temperature: this.temperature,
maxTokens: this.maxTokens
});
// Handle tool calls
if (response.toolCalls && response.toolCalls.length > 0) {
const toolResults = await this.executeToolCalls(response.toolCalls, context);
// Add tool results to messages
messages.push({
role: 'assistant',
content: response.content,
toolCalls: response.toolCalls
});
messages.push({
role: 'tool',
toolResults
});
// Recursively call for the next response
return this.runComplete({ ...options, messages });
}
return response;
}
private async *runStream(options: any): AsyncIterableIterator<any> {
const { messages, model, context } = options;
const stream = await model.stream({
messages,
tools: Array.from(this.getAllTools().values()),
temperature: this.temperature,
maxTokens: this.maxTokens
});
// Handle streaming with tool calls
return this.handleStreamWithTools(stream, messages, context, options);
}
private async *handleStreamWithTools(
stream: AsyncIterableIterator<any>,
messages: any[],
context: AgentContext,
options: any
): AsyncIterableIterator<any> {
let content = '';
const toolCalls: any[] = [];
for await (const chunk of stream) {
yield chunk;
if (chunk.type === 'content') {
content += chunk.content;
} else if (chunk.type === 'tool_call') {
toolCalls.push(chunk.toolCall);
} else if (chunk.type === 'done' && toolCalls.length > 0) {
// Execute tool calls
const toolResults = await this.executeToolCalls(toolCalls, context);
// Add to messages
messages.push({
role: 'assistant',
content,
toolCalls
});
messages.push({
role: 'tool',
toolResults
});
// Continue with next iteration
const nextStream = await this.runStream({ ...options, messages });
for await (const nextChunk of nextStream) {
yield nextChunk;
}
}
}
}
private async executeToolCalls(toolCalls: any[], context: AgentContext): Promise<any[]> {
const results = [];
for (const call of toolCalls) {
try {
const result = await this.executeTool(call.name, call.arguments, context);
results.push({
id: call.id,
result
});
} catch (error) {
results.push({
id: call.id,
error: String(error)
});
}
}
return results;
}
private async executeTool(name: string, args: any, context: AgentContext): Promise<any> {
const startTime = Date.now();
try {
let result;
let toolType: 'local' | 'mcp' = 'local';
// Check local tools first
if (this.tools.has(name)) {
const tool = this.tools.get(name)!;
result = await tool.handler(args, context);
} else {
// Check MCP tools
let found = false;
for (const [key, mcpTool] of this.mcpTools) {
const [serverName, toolName] = key.split(':');
if (toolName === name || key === name) {
toolType = 'mcp';
result = await mcpTool.client.callTool({
name: toolName,
arguments: args
});
found = true;
break;
}
}
if (!found) {
throw new Error(`Tool '${name}' not found`);
}
}
const duration = Date.now() - startTime;
// Record tool usage metric
if (context.telemetry && 'recordToolUsage' in context.telemetry) {
(context.telemetry as any).recordToolUsage(
name,
this.name,
duration,
true
);
}
// Add telemetry event
context.telemetry.recordEvent({
name: 'tool.executed',
attributes: {
'tool.name': name,
'tool.type': toolType,
'tool.duration': duration,
'agent.name': this.name
}
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
// Record failed tool execution
if (context.telemetry && 'recordToolUsage' in context.telemetry) {
(context.telemetry as any).recordToolUsage(
name,
this.name,
duration,
false
);
}
throw error;
}
}
private getAllTools(): Map<string, any> {
const allTools = new Map();
// Add local tools
for (const [name, tool] of this.tools) {
allTools.set(name, {
name: tool.name,
description: tool.description,
parameters: tool.parameters
});
}
// Add MCP tools
for (const [key, mcpTool] of this.mcpTools) {
const [serverName, toolName] = key.split(':');
allTools.set(toolName, {
name: toolName,
description: mcpTool.tool.description,
parameters: mcpTool.tool.inputSchema
});
}
return allTools;
}
clone(overrides?: Partial<AgentConfig>): Agent {
return new Agent({
name: this.name,
description: this.description,
system: this.system,
tools: Array.from(this.tools.values()),
mcpServers: this.mcpServers,
model: this.model,
temperature: this.temperature,
maxTokens: this.maxTokens,
metadata: this.metadata,
...overrides
});
}
}
export function createAgent(config: AgentConfig): Agent {
return new Agent(config);
}
-1
View File
@@ -1 +0,0 @@
export async function embed(params: any): Promise<any> { return { embeddings: [] }; }
-1
View File
@@ -1 +0,0 @@
export async function generateObject(params: any): Promise<any> { return {}; }
-1
View File
@@ -1 +0,0 @@
export async function* generateStream(params: any): AsyncIterableIterator<any> { yield { type: 'done' }; }
-1
View File
@@ -1 +0,0 @@
export async function generateText(params: any): Promise<any> { return { text: '' }; }
-418
View File
@@ -1,418 +0,0 @@
/**
* Network implementation for agent collaboration
*/
import { Agent } from './agent';
import { State } from './state';
import { Router } from './router';
import { ModelInterface } from '../types';
import { Telemetry, SpanKind } from '../telemetry';
import { nanoid } from 'nanoid';
import { EventEmitter } from 'events';
export interface NetworkConfig {
name: string;
agents: Agent[];
defaultModel?: ModelInterface;
router?: Router | ((context: RouterContext) => Agent | undefined);
state?: State;
maxIterations?: number;
metadata?: Record<string, any>;
}
export interface RouterContext {
network: Network;
state: State;
messages: any[];
iteration: number;
history: ExecutionHistory[];
}
export interface ExecutionHistory {
agent: string;
input: any;
output: any;
timestamp: number;
duration: number;
}
export interface NetworkRunOptions {
messages: any[];
stream?: boolean;
onIteration?: (context: RouterContext) => void;
telemetry?: Telemetry;
}
export class Network extends EventEmitter {
readonly id: string;
readonly name: string;
readonly agents: Map<string, Agent>;
readonly defaultModel?: ModelInterface;
readonly router: Router;
readonly state: State;
readonly maxIterations: number;
readonly metadata: Record<string, any>;
private initialized = false;
constructor(config: NetworkConfig) {
super();
this.id = nanoid();
this.name = config.name;
this.agents = new Map();
this.defaultModel = config.defaultModel;
this.state = config.state || new State();
this.maxIterations = config.maxIterations || 10;
this.metadata = config.metadata || {};
// Register agents
for (const agent of config.agents) {
this.agents.set(agent.name, agent);
}
// Setup router
if (typeof config.router === 'function') {
this.router = new Router({ handler: config.router });
} else if (config.router) {
this.router = config.router;
} else {
// Default router - just use first agent
this.router = new Router({
handler: () => config.agents[0]
});
}
}
async initialize(): Promise<void> {
if (this.initialized) return;
// Initialize all agents
const initPromises = Array.from(this.agents.values()).map(agent =>
agent.initialize()
);
await Promise.all(initPromises);
this.initialized = true;
this.emit('initialized');
}
async run(options: NetworkRunOptions): Promise<any> {
await this.initialize();
const telemetry = options.telemetry || new Telemetry();
const history: ExecutionHistory[] = [];
let messages = [...options.messages];
return telemetry.trace(
`network.${this.name}`,
async (span) => {
const networkStartTime = Date.now();
const agentExecutions = new Map<string, number>();
// Add network metadata to span
span.setAttributes({
'network.name': this.name,
'network.id': this.id,
'network.agents.count': this.agents.size,
'network.maxIterations': this.maxIterations,
'network.messages.initial': messages.length
});
try {
for (let iteration = 0; iteration < this.maxIterations; iteration++) {
// Build router context
const context: RouterContext = {
network: this,
state: this.state,
messages,
iteration,
history
};
// Call iteration callback if provided
if (options.onIteration) {
options.onIteration(context);
}
// Get next agent from router with telemetry
const nextAgent = await telemetry.trace(
`router.${this.name}`,
async () => this.router.route(context),
{
kind: SpanKind.INTERNAL,
attributes: { 'router.iteration': iteration }
}
);
if (!nextAgent) {
// No more agents to run
this.emit('complete', { history, state: this.state });
telemetry.recordEvent({
name: 'network.complete',
attributes: {
'network.name': this.name,
'network.iterations': iteration,
'network.reason': 'no_next_agent'
}
});
break;
}
// Track agent executions
agentExecutions.set(
nextAgent.name,
(agentExecutions.get(nextAgent.name) || 0) + 1
);
this.emit('agent:start', { agent: nextAgent.name, iteration });
// Run agent
const startTime = Date.now();
try {
const agentContext = {
network: this,
state: this.state,
telemetry
};
const result = await nextAgent.run({
messages,
model: this.defaultModel,
context: agentContext,
stream: options.stream
});
const duration = Date.now() - startTime;
// Add to history
const historyEntry: ExecutionHistory = {
agent: nextAgent.name,
input: messages[messages.length - 1],
output: result,
timestamp: Date.now(),
duration
};
history.push(historyEntry);
// Update messages
if (result.content) {
messages.push({
role: 'assistant',
content: result.content,
metadata: {
agent: nextAgent.name
}
});
}
this.emit('agent:complete', {
agent: nextAgent.name,
iteration,
result,
duration
});
// Check for completion
if (this.state.kv.get('complete') === true) {
this.emit('complete', { history, state: this.state });
telemetry.recordEvent({
name: 'network.complete',
attributes: {
'network.name': this.name,
'network.iterations': iteration + 1,
'network.reason': 'state_complete'
}
});
break;
}
} catch (error) {
this.emit('agent:error', {
agent: nextAgent.name,
iteration,
error
});
throw error;
}
}
const networkDuration = Date.now() - networkStartTime;
// Record network execution metrics
if (telemetry && 'recordNetworkExecution' in telemetry) {
(telemetry as any).recordNetworkExecution(
this.name,
history.length,
networkDuration,
agentExecutions
);
}
// Return final result
return {
messages,
history,
state: this.state.toJSON(),
iterations: history.length
};
} catch (error) {
const networkDuration = Date.now() - networkStartTime;
// Record failed network execution
telemetry.recordEvent({
name: 'network.error',
attributes: {
'network.name': this.name,
'network.duration': networkDuration,
'network.iterations': history.length,
'error.message': error instanceof Error ? error.message : String(error)
}
});
throw error;
}
},
{
kind: SpanKind.SERVER,
attributes: {
'network.type': 'agent_network'
}
}
);
}
async *stream(options: NetworkRunOptions): AsyncIterableIterator<any> {
await this.initialize();
const telemetry = options.telemetry || new Telemetry();
const history: ExecutionHistory[] = [];
let messages = [...options.messages];
for (let iteration = 0; iteration < this.maxIterations; iteration++) {
// Build router context
const context: RouterContext = {
network: this,
state: this.state,
messages,
iteration,
history
};
// Get next agent from router
const nextAgent = await this.router.route(context);
if (!nextAgent) {
// No more agents to run
yield { type: 'complete', history, state: this.state.toJSON() };
break;
}
yield { type: 'agent:start', agent: nextAgent.name, iteration };
// Run agent
const startTime = Date.now();
const agentContext = {
network: this,
state: this.state,
telemetry
};
// Stream from agent
const stream = await nextAgent.run({
messages,
model: this.defaultModel,
context: agentContext,
stream: true
});
let content = '';
for await (const chunk of stream) {
yield { ...chunk, agent: nextAgent.name };
if (chunk.type === 'content') {
content += chunk.content;
}
}
const duration = Date.now() - startTime;
// Add to history
const historyEntry: ExecutionHistory = {
agent: nextAgent.name,
input: messages[messages.length - 1],
output: { content },
timestamp: Date.now(),
duration
};
history.push(historyEntry);
// Update messages
if (content) {
messages.push({
role: 'assistant',
content,
metadata: {
agent: nextAgent.name
}
});
}
yield {
type: 'agent:complete',
agent: nextAgent.name,
iteration,
duration
};
// Check for completion
if (this.state.kv.get('complete') === true) {
yield { type: 'complete', history, state: this.state.toJSON() };
break;
}
}
}
getAgent(name: string): Agent | undefined {
return this.agents.get(name);
}
addAgent(agent: Agent): void {
this.agents.set(agent.name, agent);
this.emit('agent:added', { agent: agent.name });
}
removeAgent(name: string): void {
if (this.agents.delete(name)) {
this.emit('agent:removed', { agent: name });
}
}
reset(): void {
this.state.reset();
this.emit('reset');
}
getMetrics(): {
totalIterations: number;
agentExecutions: Map<string, number>;
averageDuration: number;
errors: number;
} {
// This would be populated from telemetry in a real implementation
return {
totalIterations: 0,
agentExecutions: new Map(),
averageDuration: 0,
errors: 0
};
}
}
export function createNetwork(config: NetworkConfig): Network {
return new Network(config);
}
-38
View File
@@ -1,38 +0,0 @@
/**
* Router implementation for agent networks
*/
import { Agent } from './agent';
import { Network } from './network';
import { State } from './state';
export interface RouterConfig {
handler: (context: RouterContext) => Agent | undefined | Promise<Agent | undefined>;
metadata?: Record<string, any>;
}
export interface RouterContext {
network: Network;
state: State;
messages: any[];
iteration: number;
history: any[];
}
export class Router {
private handler: RouterConfig['handler'];
readonly metadata: Record<string, any>;
constructor(config: RouterConfig) {
this.handler = config.handler;
this.metadata = config.metadata || {};
}
async route(context: RouterContext): Promise<Agent | undefined> {
return Promise.resolve(this.handler(context));
}
}
export function createRouter(config: RouterConfig): Router {
return new Router(config);
}
-257
View File
@@ -1,257 +0,0 @@
/**
* State management for agent networks
*/
import { EventEmitter } from 'events';
import { z } from 'zod';
export interface StateConfig {
schema?: z.ZodSchema;
initial?: Record<string, any>;
persistent?: boolean;
}
export interface StateChange {
key: string;
oldValue: any;
newValue: any;
timestamp: number;
}
export class State extends EventEmitter {
readonly kv: Map<string, any>;
readonly history: StateChange[];
readonly schema?: z.ZodSchema;
private readonly persistent: boolean;
constructor(config: StateConfig = {}) {
super();
this.kv = new Map();
this.history = [];
this.schema = config.schema;
this.persistent = config.persistent || false;
// Initialize with initial values
if (config.initial) {
for (const [key, value] of Object.entries(config.initial)) {
this.set(key, value);
}
}
// Load from persistence if enabled
if (this.persistent) {
this.load();
}
}
set(key: string, value: any): void {
// Validate against schema if provided
if (this.schema) {
const result = this.schema.safeParse({ ...this.toJSON(), [key]: value });
if (!result.success) {
throw new Error(`State validation failed: ${result.error.message}`);
}
}
const oldValue = this.kv.get(key);
this.kv.set(key, value);
// Record change
const change: StateChange = {
key,
oldValue,
newValue: value,
timestamp: Date.now()
};
this.history.push(change);
// Emit change event
this.emit('change', change);
this.emit(`change:${key}`, { oldValue, newValue: value });
// Persist if enabled
if (this.persistent) {
this.save();
}
}
get(key: string): any {
return this.kv.get(key);
}
has(key: string): boolean {
return this.kv.has(key);
}
delete(key: string): boolean {
const oldValue = this.kv.get(key);
const deleted = this.kv.delete(key);
if (deleted) {
const change: StateChange = {
key,
oldValue,
newValue: undefined,
timestamp: Date.now()
};
this.history.push(change);
this.emit('change', change);
this.emit(`change:${key}`, { oldValue, newValue: undefined });
if (this.persistent) {
this.save();
}
}
return deleted;
}
clear(): void {
const oldState = this.toJSON();
this.kv.clear();
// Record all deletions
for (const key of Object.keys(oldState)) {
const change: StateChange = {
key,
oldValue: oldState[key],
newValue: undefined,
timestamp: Date.now()
};
this.history.push(change);
}
this.emit('clear', { oldState });
if (this.persistent) {
this.save();
}
}
reset(): void {
this.clear();
this.history.length = 0;
this.emit('reset');
}
toJSON(): Record<string, any> {
const obj: Record<string, any> = {};
for (const [key, value] of this.kv) {
obj[key] = value;
}
return obj;
}
fromJSON(data: Record<string, any>): void {
this.clear();
for (const [key, value] of Object.entries(data)) {
this.set(key, value);
}
}
getHistory(key?: string): StateChange[] {
if (key) {
return this.history.filter(change => change.key === key);
}
return [...this.history];
}
rollback(steps: number = 1): void {
if (steps > this.history.length) {
throw new Error('Cannot rollback more steps than history length');
}
// Get changes to rollback
const changesToRollback = this.history.slice(-steps);
// Apply rollback
for (const change of changesToRollback.reverse()) {
if (change.oldValue === undefined) {
this.kv.delete(change.key);
} else {
this.kv.set(change.key, change.oldValue);
}
}
// Remove rolled back changes from history
this.history.length = this.history.length - steps;
this.emit('rollback', { steps, changes: changesToRollback });
if (this.persistent) {
this.save();
}
}
// Persistence methods
private save(): void {
// Only available in browser environments
if (typeof globalThis !== 'undefined' && 'localStorage' in globalThis) {
const data = {
state: this.toJSON(),
history: this.history
};
(globalThis as any).localStorage.setItem('hanzo-ai-state', JSON.stringify(data));
}
}
private load(): void {
// Only available in browser environments
if (typeof globalThis !== 'undefined' && 'localStorage' in globalThis) {
const stored = (globalThis as any).localStorage.getItem('hanzo-ai-state');
if (stored) {
try {
const data = JSON.parse(stored);
this.fromJSON(data.state);
this.history.push(...(data.history || []));
} catch (error) {
console.error('Failed to load state from storage:', error);
}
}
}
}
// Computed properties
compute<T>(key: string, fn: (state: Record<string, any>) => T): T {
const state = this.toJSON();
const result = fn(state);
// Cache computed value
this.set(`_computed_${key}`, result);
return result;
}
watch(key: string, callback: (value: any) => void): () => void {
const handler = ({ newValue }: any) => callback(newValue);
this.on(`change:${key}`, handler);
// Return unsubscribe function
return () => {
this.off(`change:${key}`, handler);
};
}
// State machine helpers
transition(from: string, to: string, stateKey: string = 'state'): boolean {
const current = this.get(stateKey);
if (current === from) {
this.set(stateKey, to);
this.emit('transition', { from, to, stateKey });
return true;
}
return false;
}
inState(state: string, stateKey: string = 'state'): boolean {
return this.get(stateKey) === state;
}
}
export function createState(config?: StateConfig): State {
return new State(config);
}
-235
View File
@@ -1,235 +0,0 @@
/**
* Tool implementation for agents
*/
import { z } from 'zod';
import { AgentContext } from './agent';
export interface ToolConfig<TParams = any, TResult = any> {
name: string;
description: string;
parameters: z.ZodSchema<TParams>;
handler: (params: TParams, context: AgentContext) => Promise<TResult> | TResult;
examples?: Array<{
input: TParams;
output: TResult;
description?: string;
}>;
metadata?: Record<string, any>;
}
export class Tool<TParams = any, TResult = any> {
readonly name: string;
readonly description: string;
readonly parameters: z.ZodSchema<TParams>;
readonly handler: (params: TParams, context: AgentContext) => Promise<TResult> | TResult;
readonly examples?: ToolConfig<TParams, TResult>['examples'];
readonly metadata: Record<string, any>;
constructor(config: ToolConfig<TParams, TResult>) {
this.name = config.name;
this.description = config.description;
this.parameters = config.parameters;
this.handler = config.handler;
this.examples = config.examples;
this.metadata = config.metadata || {};
}
async execute(params: any, context: AgentContext): Promise<TResult> {
// Validate parameters
const result = this.parameters.safeParse(params);
if (!result.success) {
throw new Error(`Invalid parameters for tool '${this.name}': ${result.error.message}`);
}
// Execute handler
return Promise.resolve(this.handler(result.data, context));
}
getSchema(): any {
return {
name: this.name,
description: this.description,
parameters: this.zodToJsonSchema(this.parameters)
};
}
private zodToJsonSchema(schema: z.ZodSchema): any {
// This is a simplified version - in production you'd use a proper converter
if (schema instanceof z.ZodObject) {
const shape = schema.shape;
const properties: any = {};
const required: string[] = [];
for (const [key, value] of Object.entries(shape)) {
properties[key] = this.zodToJsonSchema(value as z.ZodSchema);
// Check if field is required
if (!(value as any).isOptional()) {
required.push(key);
}
}
return {
type: 'object',
properties,
required: required.length > 0 ? required : undefined
};
} else if (schema instanceof z.ZodString) {
return { type: 'string' };
} else if (schema instanceof z.ZodNumber) {
return { type: 'number' };
} else if (schema instanceof z.ZodBoolean) {
return { type: 'boolean' };
} else if (schema instanceof z.ZodArray) {
return {
type: 'array',
items: this.zodToJsonSchema((schema as any)._def.type)
};
}
// Fallback
return { type: 'any' };
}
}
export function createTool<TParams = any, TResult = any>(
config: ToolConfig<TParams, TResult>
): Tool<TParams, TResult> {
return new Tool(config);
}
// Common tool patterns
export const commonTools = {
done: (onDone?: (result: any, context: AgentContext) => void) =>
createTool({
name: 'done',
description: 'Call this tool when you are finished with the task.',
parameters: z.object({
answer: z.string().describe("Final answer or result"),
summary: z.string().optional().describe("Brief summary of what was accomplished")
}),
handler: async (params, context) => {
context.network?.state.set('complete', true);
context.network?.state.set('answer', params.answer);
if (params.summary) {
context.network?.state.set('summary', params.summary);
}
if (onDone) {
onDone(params, context);
}
return { success: true };
}
}),
handoff: () =>
createTool({
name: 'handoff',
description: 'Hand off the conversation to another agent.',
parameters: z.object({
agent: z.string().describe("Name of the agent to hand off to"),
context: z.string().describe("Context to provide to the next agent"),
priority: z.boolean().optional().describe("Whether this is a priority handoff")
}),
handler: async (params, context) => {
if (!context.network) {
throw new Error('Handoff requires a network context');
}
context.network.state.set('nextAgent', params.agent);
context.network.state.set('handoffContext', params.context);
return {
success: true,
agent: params.agent
};
}
}),
askUser: () =>
createTool({
name: 'ask_user',
description: 'Ask the user for additional information or clarification.',
parameters: z.object({
question: z.string().describe("The question to ask the user"),
context: z.string().optional().describe("Additional context for the question"),
options: z.array(z.string()).optional().describe("Multiple choice options if applicable")
}),
handler: async (params, context) => {
context.network?.state.set('waitingForUser', true);
context.network?.state.set('userQuestion', params);
return {
success: true,
waiting: true
};
}
}),
remember: () =>
createTool({
name: 'remember',
description: 'Store information in long-term memory.',
parameters: z.object({
key: z.string().describe("Memory key"),
value: z.any().describe("Value to remember"),
category: z.string().optional().describe("Category for organization"),
ttl: z.number().optional().describe("Time to live in seconds")
}),
handler: async (params, context) => {
const memory = context.network?.state.get('memory') || {};
memory[params.key] = {
value: params.value,
category: params.category,
timestamp: Date.now(),
ttl: params.ttl
};
context.network?.state.set('memory', memory);
return { success: true };
}
}),
recall: () =>
createTool({
name: 'recall',
description: 'Retrieve information from long-term memory.',
parameters: z.object({
key: z.string().optional().describe("Specific memory key"),
category: z.string().optional().describe("Category to search"),
query: z.string().optional().describe("Search query")
}),
handler: async (params, context) => {
const memory = context.network?.state.get('memory') || {};
if (params.key) {
return memory[params.key]?.value || null;
}
// Search by category or query
const results: any[] = [];
for (const [key, item] of Object.entries(memory)) {
const memItem = item as any;
// Check TTL
if (memItem.ttl && Date.now() - memItem.timestamp > memItem.ttl * 1000) {
continue;
}
if (params.category && memItem.category === params.category) {
results.push({ key, ...memItem });
} else if (params.query) {
const searchStr = JSON.stringify(memItem.value).toLowerCase();
if (searchStr.includes(params.query.toLowerCase())) {
results.push({ key, ...memItem });
}
}
}
return results;
}
})
};
-45
View File
@@ -1,45 +0,0 @@
/**
* Error types for @hanzo/ai
*/
export class HanzoAIError extends Error {
constructor(message: string, public code?: string) {
super(message);
this.name = 'HanzoAIError';
}
}
export class AgentError extends HanzoAIError {
constructor(message: string, public agentName: string) {
super(message, 'AGENT_ERROR');
this.name = 'AgentError';
}
}
export class NetworkError extends HanzoAIError {
constructor(message: string, public networkName: string) {
super(message, 'NETWORK_ERROR');
this.name = 'NetworkError';
}
}
export class ToolError extends HanzoAIError {
constructor(message: string, public toolName: string) {
super(message, 'TOOL_ERROR');
this.name = 'ToolError';
}
}
export class MCPError extends HanzoAIError {
constructor(message: string, public serverName?: string) {
super(message, 'MCP_ERROR');
this.name = 'MCPError';
}
}
export class ValidationError extends HanzoAIError {
constructor(message: string, public field?: string) {
super(message, 'VALIDATION_ERROR');
this.name = 'ValidationError';
}
}
-45
View File
@@ -1,45 +0,0 @@
/**
* @hanzo/ai - The AI Toolkit for TypeScript
* AgentKit with MCP support and multi-provider integration
*/
// Core exports
export { createAgent } from './core/agent';
export { createNetwork } from './core/network';
export { createTool } from './core/tool';
export { createRouter } from './core/router';
export { createState } from './core/state';
// Provider exports
export { anthropic } from './providers/anthropic';
export { openai } from './providers/openai';
export { google } from './providers/google';
export { mistral } from './providers/mistral';
export { bedrock } from './providers/bedrock';
export { vertex } from './providers/vertex';
export { cohere } from './providers/cohere';
export { hanzo } from './providers/hanzo';
// Core functionality
export { generateText } from './core/generate/text';
export { generateStream } from './core/generate/stream';
export { generateObject } from './core/generate/object';
export { embed } from './core/embed';
// Types
export * from './types';
// Utilities
export { createCompletionId } from './utils/id';
export { parseStreamPart } from './utils/stream';
export { validateSchema } from './utils/schema';
// Errors
export * from './errors';
// MCP exports
export * from './mcp';
// Tracing
export * from './telemetry';
export { createHanzoCloudTelemetry, HanzoCloudTelemetry } from './telemetry/hanzo-cloud';
-461
View File
@@ -1,461 +0,0 @@
/**
* MCP Server wrapper for Agents and Networks
* Allows any agent or network to be exposed as an MCP server
*/
import { Server as MCPServer } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { Agent } from '../core/agent';
import { Network } from '../core/network';
import { Tool } from '../core/tool';
import { z } from 'zod';
export interface AgentServerConfig {
agent?: Agent;
network?: Network;
name?: string;
version?: string;
metadata?: Record<string, any>;
}
export class AgentMCPServer {
private server: MCPServer;
private agent?: Agent;
private network?: Network;
constructor(config: AgentServerConfig) {
if (!config.agent && !config.network) {
throw new Error('Either agent or network must be provided');
}
this.agent = config.agent;
this.network = config.network;
const name = config.name || this.agent?.name || this.network?.name || 'hanzo-agent';
const version = config.version || '1.0.0';
this.server = new MCPServer({
name,
version,
metadata: config.metadata
});
this.setupTools();
this.setupResources();
this.setupPrompts();
}
private setupTools(): void {
if (this.agent) {
this.setupAgentTools();
} else if (this.network) {
this.setupNetworkTools();
}
}
private setupAgentTools(): void {
if (!this.agent) return;
// Expose agent's run method as a tool
this.server.setRequestHandler('tools/list', async () => ({
tools: [
{
name: `${this.agent.name}_chat`,
description: `Chat with ${this.agent.name} agent`,
inputSchema: {
type: 'object',
properties: {
message: {
type: 'string',
description: 'Message to send to the agent'
},
context: {
type: 'object',
description: 'Optional context',
properties: {
history: {
type: 'array',
items: { type: 'object' },
description: 'Previous messages'
}
}
}
},
required: ['message']
}
},
// Expose all agent tools
...Array.from(this.agent.tools.values()).map(tool => ({
name: `${this.agent!.name}_${tool.name}`,
description: tool.description,
inputSchema: this.toolToJsonSchema(tool)
}))
]
}));
// Handle tool calls
this.server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params as any;
if (name === `${this.agent!.name}_chat`) {
const result = await this.agent!.run({
messages: [
{ role: 'user', content: args.message }
],
context: args.context
});
return {
content: [
{
type: 'text',
text: result.content || JSON.stringify(result)
}
]
};
}
// Check if it's one of the agent's tools
const toolName = name.replace(`${this.agent!.name}_`, '');
const tool = this.agent!.tools.get(toolName);
if (tool) {
const result = await tool.execute(args, {
agent: this.agent!,
telemetry: {} as any
});
return {
content: [
{
type: 'text',
text: JSON.stringify(result)
}
]
};
}
throw new Error(`Tool '${name}' not found`);
});
}
private setupNetworkTools(): void {
if (!this.network) return;
this.server.setRequestHandler('tools/list', async () => ({
tools: [
{
name: `${this.network.name}_run`,
description: `Run a task through the ${this.network.name} network`,
inputSchema: {
type: 'object',
properties: {
message: {
type: 'string',
description: 'Task or message for the network'
},
stream: {
type: 'boolean',
description: 'Whether to stream responses'
}
},
required: ['message']
}
},
{
name: `${this.network.name}_state_get`,
description: `Get a value from the network's state`,
inputSchema: {
type: 'object',
properties: {
key: {
type: 'string',
description: 'State key to retrieve'
}
},
required: ['key']
}
},
{
name: `${this.network.name}_state_set`,
description: `Set a value in the network's state`,
inputSchema: {
type: 'object',
properties: {
key: {
type: 'string',
description: 'State key'
},
value: {
description: 'Value to set'
}
},
required: ['key', 'value']
}
},
// Expose individual agents as tools
...Array.from(this.network.agents.values()).map(agent => ({
name: `${this.network!.name}_agent_${agent.name}`,
description: `Run task through ${agent.name} agent in the network`,
inputSchema: {
type: 'object',
properties: {
message: {
type: 'string',
description: 'Message for the agent'
}
},
required: ['message']
}
}))
]
}));
this.server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params as any;
if (name === `${this.network!.name}_run`) {
const result = await this.network!.run({
messages: [
{ role: 'user', content: args.message }
],
stream: args.stream
});
return {
content: [
{
type: 'text',
text: JSON.stringify(result)
}
]
};
}
if (name === `${this.network!.name}_state_get`) {
const value = this.network!.state.get(args.key);
return {
content: [
{
type: 'text',
text: JSON.stringify(value)
}
]
};
}
if (name === `${this.network!.name}_state_set`) {
this.network!.state.set(args.key, args.value);
return {
content: [
{
type: 'text',
text: 'State updated'
}
]
};
}
// Check for agent-specific tools
const agentMatch = name.match(new RegExp(`^${this.network!.name}_agent_(.+)$`));
if (agentMatch) {
const agentName = agentMatch[1];
const agent = this.network!.getAgent(agentName);
if (agent) {
const result = await agent.run({
messages: [
{ role: 'user', content: args.message }
]
});
return {
content: [
{
type: 'text',
text: result.content || JSON.stringify(result)
}
]
};
}
}
throw new Error(`Tool '${name}' not found`);
});
}
private setupResources(): void {
this.server.setRequestHandler('resources/list', async () => ({
resources: [
{
uri: `agent://${this.agent?.name || this.network?.name}/state`,
name: 'State',
description: 'Current state of the agent/network',
mimeType: 'application/json'
},
{
uri: `agent://${this.agent?.name || this.network?.name}/metrics`,
name: 'Metrics',
description: 'Performance metrics',
mimeType: 'application/json'
}
]
}));
this.server.setRequestHandler('resources/read', async (request) => {
const { uri } = request.params as any;
if (uri.endsWith('/state')) {
const state = this.network?.state.toJSON() || {
agent: this.agent?.name,
metadata: this.agent?.metadata
};
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(state, null, 2)
}
]
};
}
if (uri.endsWith('/metrics')) {
const metrics = this.network?.getMetrics() || {
agent: this.agent?.name,
calls: 0 // Would be tracked in real implementation
};
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(metrics, null, 2)
}
]
};
}
throw new Error(`Resource '${uri}' not found`);
});
}
private setupPrompts(): void {
const prompts = [];
if (this.agent && this.agent.system) {
prompts.push({
name: `${this.agent.name}_system`,
description: `System prompt for ${this.agent.name}`,
arguments: []
});
}
this.server.setRequestHandler('prompts/list', async () => ({
prompts
}));
this.server.setRequestHandler('prompts/get', async (request) => {
const { name } = request.params as any;
if (name === `${this.agent?.name}_system`) {
return {
description: `System prompt for ${this.agent.name}`,
messages: [
{
role: 'system',
content: {
type: 'text',
text: this.agent!.system!
}
}
]
};
}
throw new Error(`Prompt '${name}' not found`);
});
}
private toolToJsonSchema(tool: Tool): any {
// Convert Zod schema to JSON Schema
// This is simplified - would use a proper converter in production
return {
type: 'object',
properties: {},
required: []
};
}
async start(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
}
async startHttp(port: number = 3000): Promise<void> {
// HTTP transport implementation
const { createServer } = await import('http');
const server = createServer(async (req, res) => {
if (req.method === 'POST') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', async () => {
try {
const request = JSON.parse(body);
const response = await this.handleHttpRequest(request);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(response));
} catch (error) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: String(error) }));
}
});
}
});
server.listen(port);
console.log(`Agent MCP server listening on http://localhost:${port}`);
}
private async handleHttpRequest(request: any): Promise<any> {
// Route to appropriate handler based on request method
const [namespace, method] = request.method.split('/');
if (namespace === 'tools') {
if (method === 'list') {
return this.server.handleRequest({ ...request, method: 'tools/list' });
} else if (method === 'call') {
return this.server.handleRequest({ ...request, method: 'tools/call' });
}
}
throw new Error(`Unknown method: ${request.method}`);
}
}
// Helper to create and start an MCP server for an agent/network
export function exposeAsMCP(
agentOrNetwork: Agent | Network,
options?: {
transport?: 'stdio' | 'http';
port?: number;
name?: string;
}
): AgentMCPServer {
const server = new AgentMCPServer({
agent: agentOrNetwork instanceof Agent ? agentOrNetwork : undefined,
network: agentOrNetwork instanceof Network ? agentOrNetwork : undefined,
name: options?.name
});
if (options?.transport === 'http') {
server.startHttp(options.port);
} else {
server.start();
}
return server;
}
-57
View File
@@ -1,57 +0,0 @@
/**
* MCP Client implementation
*/
import { MCPTransport, MCPTool, MCPResource, MCPPrompt } from './types';
export interface MCPClientConfig {
name: string;
transport: MCPTransport;
}
export class MCPClient {
private config: MCPClientConfig;
constructor(config?: MCPClientConfig) {
this.config = config || { name: 'default', transport: { type: 'stdio' } };
}
async connect(config: MCPClientConfig): Promise<void> {
this.config = config;
// Implementation would connect to MCP server
}
async listTools(): Promise<MCPTool[]> {
// Implementation would fetch tools from server
return [];
}
async listResources(): Promise<MCPResource[]> {
// Implementation would fetch resources from server
return [];
}
async listPrompts(): Promise<MCPPrompt[]> {
// Implementation would fetch prompts from server
return [];
}
async callTool(params: { name: string; arguments: any }): Promise<any> {
// Implementation would call tool on server
return {};
}
async readResource(uri: string): Promise<any> {
// Implementation would read resource from server
return {};
}
async getPrompt(name: string, args?: any): Promise<any> {
// Implementation would get prompt from server
return {};
}
async disconnect(): Promise<void> {
// Implementation would disconnect from server
}
}
-7
View File
@@ -1,7 +0,0 @@
/**
* MCP exports
*/
export * from './types';
export * from './client';
export * from './agent-server';
-36
View File
@@ -1,36 +0,0 @@
/**
* MCP types for @hanzo/ai
*/
export interface MCPServer {
name: string;
transport: MCPTransport;
metadata?: Record<string, any>;
}
export interface MCPTransport {
type: 'stdio' | 'http' | 'websocket';
command?: string;
args?: string[];
url?: string;
headers?: Record<string, string>;
}
export interface MCPTool {
name: string;
description: string;
inputSchema: any; // JSON Schema
}
export interface MCPResource {
uri: string;
name: string;
description?: string;
mimeType?: string;
}
export interface MCPPrompt {
name: string;
description: string;
arguments?: any[];
}
-20
View File
@@ -1,20 +0,0 @@
/**
* Anthropic provider
*/
import { ModelInterface } from '../types';
export const anthropic = (config: { apiKey: string; model?: string }): ModelInterface => {
return {
name: config.model || 'claude-3-opus-20240229',
async complete(params) {
// Implementation would call Anthropic API
return { content: 'Claude response' };
},
async *stream(params) {
// Implementation would stream from Anthropic
yield { type: 'content', content: 'Claude' };
yield { type: 'done' };
}
};
};
-1
View File
@@ -1 +0,0 @@
export const bedrock = (config: any) => ({ name: 'bedrock', async complete(p: any) { return { content: '' }; }, async *stream(p: any) { yield { type: 'done' as const }; } });
-1
View File
@@ -1 +0,0 @@
export const cohere = (config: any) => ({ name: 'cohere', async complete(p: any) { return { content: '' }; }, async *stream(p: any) { yield { type: 'done' as const }; } });
-1
View File
@@ -1 +0,0 @@
export const google = (config: any) => ({ name: 'gemini', async complete(p: any) { return { content: '' }; }, async *stream(p: any) { yield { type: 'done' as const }; } });
-1
View File
@@ -1 +0,0 @@
export const hanzo = (config: any) => ({ name: 'hanzo', async complete(p: any) { return { content: '' }; }, async *stream(p: any) { yield { type: 'done' as const }; } });
-1
View File
@@ -1 +0,0 @@
export const mistral = (config: any) => ({ name: 'mistral', async complete(p: any) { return { content: '' }; }, async *stream(p: any) { yield { type: 'done' as const }; } });
-20
View File
@@ -1,20 +0,0 @@
/**
* OpenAI provider
*/
import { ModelInterface } from '../types';
export const openai = (config: { apiKey: string; model?: string }): ModelInterface => {
return {
name: config.model || 'gpt-4',
async complete(params) {
// Implementation would call OpenAI API
return { content: 'OpenAI response' };
},
async *stream(params) {
// Implementation would stream from OpenAI
yield { type: 'content', content: 'OpenAI' };
yield { type: 'done' };
}
};
};
-1
View File
@@ -1 +0,0 @@
export const vertex = (config: any) => ({ name: 'vertex', async complete(p: any) { return { content: '' }; }, async *stream(p: any) { yield { type: 'done' as const }; } });
-18
View File
@@ -1,18 +0,0 @@
// Server exports for @hanzo/ai
// This module provides server-side specific functionality
export { Agent } from '../core/agent'
export { Network } from '../core/network'
export { Router, type RouterContext } from '../core/router'
export { State } from '../core/state'
export { AgentMCPServer } from '../mcp/agent-server'
// Re-export providers for server use
export * from '../providers/anthropic'
export * from '../providers/openai'
export * from '../providers/google'
export * from '../providers/bedrock'
export * from '../providers/vertex'
export * from '../providers/cohere'
export * from '../providers/mistral'
export * from '../providers/hanzo'
@@ -1,319 +0,0 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { HanzoCloudTelemetry } from '../hanzo-cloud';
// Mock fetch
global.fetch = vi.fn();
describe('HanzoCloudTelemetry', () => {
let telemetry: HanzoCloudTelemetry;
const mockConfig = {
cloudUrl: 'https://test.hanzo.ai',
apiKey: 'test-api-key',
projectId: 'test-project',
environment: 'test',
serviceName: 'test-service'
};
beforeEach(() => {
vi.clearAllMocks();
(global.fetch as any).mockResolvedValue({
ok: true,
status: 200,
json: async () => ({})
});
telemetry = new HanzoCloudTelemetry(mockConfig);
});
afterEach(async () => {
await telemetry.shutdown();
});
describe('initialization', () => {
it('should initialize with config', () => {
expect(telemetry).toBeInstanceOf(HanzoCloudTelemetry);
});
it('should log initialization', async () => {
// Just verify that telemetry initializes without errors
// The log output is visible in stdout which confirms it works
const newTelemetry = new HanzoCloudTelemetry(mockConfig);
expect(newTelemetry).toBeInstanceOf(HanzoCloudTelemetry);
await newTelemetry.shutdown();
});
});
describe('logging', () => {
it('should log with trace context', async () => {
await telemetry.trace('test-operation', async () => {
telemetry.log('info', 'Test message', { custom: 'data' });
});
});
it('should support all log levels', () => {
telemetry.log('debug', 'Debug message');
telemetry.log('info', 'Info message');
telemetry.log('warn', 'Warning message');
telemetry.log('error', 'Error message');
});
});
describe('agent metrics', () => {
it('should record agent execution', () => {
const incrementSpy = vi.spyOn(telemetry, 'increment');
const histogramSpy = vi.spyOn(telemetry, 'histogram');
const logSpy = vi.spyOn(telemetry, 'log');
telemetry.recordAgentExecution('test-agent', 1000, true, {
model: 'gpt-4',
tokens: 150
});
expect(incrementSpy).toHaveBeenCalledWith('agent.executions', 1, {
agent: 'test-agent',
success: 'true'
});
expect(histogramSpy).toHaveBeenCalledWith('agent.execution.duration', 1000, {
agent: 'test-agent'
});
expect(incrementSpy).toHaveBeenCalledWith('agent.tokens.used', 150, {
agent: 'test-agent',
model: 'gpt-4'
});
expect(logSpy).toHaveBeenCalledWith(
'info',
'Agent test-agent executed',
expect.objectContaining({
agent: 'test-agent',
duration: 1000,
success: true,
model: 'gpt-4',
tokens: 150
})
);
});
it('should record failed agent execution', () => {
const incrementSpy = vi.spyOn(telemetry, 'increment');
telemetry.recordAgentExecution('test-agent', 500, false, {
error: 'Test error'
});
expect(incrementSpy).toHaveBeenCalledWith('agent.executions', 1, {
agent: 'test-agent',
success: 'false'
});
});
});
describe('network metrics', () => {
it('should record network execution', () => {
const incrementSpy = vi.spyOn(telemetry, 'increment');
const histogramSpy = vi.spyOn(telemetry, 'histogram');
const logSpy = vi.spyOn(telemetry, 'log');
const agentExecutions = new Map([
['agent1', 3],
['agent2', 2]
]);
telemetry.recordNetworkExecution('test-network', 5, 3000, agentExecutions);
expect(incrementSpy).toHaveBeenCalledWith('network.executions', 1, {
network: 'test-network'
});
expect(histogramSpy).toHaveBeenCalledWith('network.iterations', 5, {
network: 'test-network'
});
expect(histogramSpy).toHaveBeenCalledWith('network.execution.duration', 3000, {
network: 'test-network'
});
expect(incrementSpy).toHaveBeenCalledWith('network.agent.executions', 3, {
network: 'test-network',
agent: 'agent1'
});
expect(incrementSpy).toHaveBeenCalledWith('network.agent.executions', 2, {
network: 'test-network',
agent: 'agent2'
});
expect(logSpy).toHaveBeenCalledWith(
'info',
'Network test-network completed',
expect.objectContaining({
network: 'test-network',
iterations: 5,
duration: 3000,
agents: { agent1: 3, agent2: 2 }
})
);
});
});
describe('tool metrics', () => {
it('should record tool usage', () => {
const incrementSpy = vi.spyOn(telemetry, 'increment');
const histogramSpy = vi.spyOn(telemetry, 'histogram');
telemetry.recordToolUsage('search', 'agent1', 100, true);
expect(incrementSpy).toHaveBeenCalledWith('tool.executions', 1, {
tool: 'search',
agent: 'agent1',
success: 'true'
});
expect(histogramSpy).toHaveBeenCalledWith('tool.execution.duration', 100, {
tool: 'search',
agent: 'agent1'
});
});
});
describe('MCP metrics', () => {
it('should record MCP connection', () => {
const incrementSpy = vi.spyOn(telemetry, 'increment');
const gaugeSpy = vi.spyOn(telemetry, 'gauge');
const logSpy = vi.spyOn(telemetry, 'log');
telemetry.recordMCPConnection('file-system', true, {
transport: 'stdio'
});
expect(incrementSpy).toHaveBeenCalledWith('mcp.connections', 1, {
server: 'file-system',
success: 'true'
});
expect(gaugeSpy).toHaveBeenCalledWith('mcp.servers.active', 1, {
server: 'file-system'
});
expect(logSpy).toHaveBeenCalledWith(
'info',
'MCP server file-system connection established',
expect.objectContaining({
server: 'file-system',
transport: 'stdio'
})
);
});
it('should record failed MCP connection', () => {
const incrementSpy = vi.spyOn(telemetry, 'increment');
const gaugeSpy = vi.spyOn(telemetry, 'gauge');
telemetry.recordMCPConnection('file-system', false);
expect(incrementSpy).toHaveBeenCalledWith('mcp.connections', 1, {
server: 'file-system',
success: 'false'
});
expect(gaugeSpy).not.toHaveBeenCalled();
});
});
describe('sessions', () => {
it('should create sessions', () => {
const setAttributesSpy = vi.spyOn(telemetry, 'setAttributes');
const sessionId = telemetry.createSession();
expect(sessionId).toMatch(/^session_\d+_[a-z0-9]+$/);
expect(setAttributesSpy).toHaveBeenCalledWith({
'hanzo.session.id': sessionId
});
});
it('should use provided session ID', () => {
const setAttributesSpy = vi.spyOn(telemetry, 'setAttributes');
const sessionId = telemetry.createSession('custom-session-123');
expect(sessionId).toBe('custom-session-123');
expect(setAttributesSpy).toHaveBeenCalledWith({
'hanzo.session.id': 'custom-session-123'
});
});
});
describe('metrics flushing', () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
it('should buffer and flush metrics', async () => {
// Record some metrics
telemetry.increment('test.counter', 1);
telemetry.increment('test.counter', 2);
telemetry.gauge('test.gauge', 10);
telemetry.gauge('test.gauge', 20);
telemetry.histogram('test.histogram', 100);
telemetry.histogram('test.histogram', 200);
// Manually trigger flush
await (telemetry as any).flushMetrics(mockConfig);
expect(global.fetch).toHaveBeenCalledWith(
'https://test.hanzo.ai/v1/metrics',
expect.objectContaining({
method: 'POST',
headers: expect.objectContaining({
'Content-Type': 'application/json',
'x-api-key': 'test-api-key',
'x-project-id': 'test-project'
}),
body: expect.stringContaining('metrics')
})
);
});
});
describe('error handling', () => {
it('should handle metrics send failure', async () => {
(global.fetch as any).mockResolvedValueOnce({
ok: false,
status: 500,
statusText: 'Internal Server Error'
});
// Add some metrics to flush
telemetry.increment('test.counter', 1);
// Trigger metrics flush - should not throw
await expect(
(telemetry as any).flushMetrics(mockConfig)
).resolves.not.toThrow();
// The error log is visible in stdout which confirms error handling works
});
it('should handle network errors', async () => {
(global.fetch as any).mockRejectedValueOnce(new Error('Network error'));
// Add some metrics to flush
telemetry.increment('test.counter', 1);
// Trigger metrics flush - should not throw
await expect(
(telemetry as any).flushMetrics(mockConfig)
).resolves.not.toThrow();
// The error log is visible in stdout which confirms error handling works
});
});
});
@@ -1,225 +0,0 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { Telemetry } from '../index';
import { SpanKind, SpanStatusCode } from '@opentelemetry/api';
describe('Telemetry', () => {
let telemetry: Telemetry;
beforeEach(() => {
telemetry = new Telemetry({
serviceName: 'test-service',
enabled: true
});
});
afterEach(async () => {
await telemetry.shutdown();
});
describe('span operations', () => {
it('should create and end spans', () => {
const span = telemetry.startSpan('test-span', {
kind: SpanKind.INTERNAL,
attributes: { 'test.attr': 'value' }
});
expect(span).toBeDefined();
expect(span.end).toBeInstanceOf(Function);
telemetry.endSpan('test-span', { code: SpanStatusCode.OK });
});
it('should trace async operations', async () => {
const result = await telemetry.trace(
'async-operation',
async (span) => {
span.setAttribute('operation.type', 'test');
return 'success';
}
);
expect(result).toBe('success');
});
it('should trace sync operations', () => {
const result = telemetry.traceSync(
'sync-operation',
(span) => {
span.setAttribute('operation.type', 'test');
return 42;
}
);
expect(result).toBe(42);
});
it('should handle errors in traced operations', async () => {
const error = new Error('Test error');
await expect(
telemetry.trace('failing-operation', async () => {
throw error;
})
).rejects.toThrow('Test error');
});
});
describe('events and attributes', () => {
it('should record events', () => {
const span = telemetry.startSpan('test-span');
telemetry.recordEvent({
name: 'test.event',
attributes: { 'event.type': 'test' },
timestamp: Date.now()
}, span);
telemetry.endSpan('test-span');
});
it('should set attributes', () => {
const span = telemetry.startSpan('test-span');
telemetry.setAttributes({
'attr1': 'value1',
'attr2': 42,
'attr3': true
}, span);
telemetry.endSpan('test-span');
});
it('should record exceptions', () => {
const span = telemetry.startSpan('test-span');
const error = new Error('Test exception');
telemetry.recordException(error, span);
telemetry.endSpan('test-span');
});
});
describe('metrics', () => {
it('should emit metric events', () => {
const metricHandler = vi.fn();
telemetry.on('metric', metricHandler);
telemetry.increment('test.counter', 1, { tag: 'value' });
expect(metricHandler).toHaveBeenCalledWith({
name: 'test.counter',
value: 1,
type: 'counter',
tags: expect.objectContaining({ tag: 'value' }),
timestamp: expect.any(Number)
});
});
it('should record gauges', () => {
const metricHandler = vi.fn();
telemetry.on('metric', metricHandler);
telemetry.gauge('test.gauge', 42);
expect(metricHandler).toHaveBeenCalledWith(
expect.objectContaining({
name: 'test.gauge',
value: 42,
type: 'gauge'
})
);
});
it('should record histograms', () => {
const metricHandler = vi.fn();
telemetry.on('metric', metricHandler);
telemetry.histogram('test.histogram', 123);
expect(metricHandler).toHaveBeenCalledWith(
expect.objectContaining({
name: 'test.histogram',
value: 123,
type: 'histogram'
})
);
});
});
describe('context propagation', () => {
it('should get trace context', async () => {
await telemetry.trace('test-op', async () => {
const context = telemetry.getTraceContext();
expect(context).toBeInstanceOf(Object);
// In test environment with noop tracer, context might be empty
// This is expected behavior
});
});
it('should create child telemetry instances', () => {
const child = telemetry.createChild('child-service', {
'child.attr': 'value'
});
expect(child).toBeInstanceOf(Telemetry);
expect(child).not.toBe(telemetry);
});
});
describe('lifecycle', () => {
it('should flush pending data', async () => {
const flushHandler = vi.fn();
telemetry.on('flush', flushHandler);
// Create some spans
telemetry.startSpan('span1');
telemetry.startSpan('span2');
await telemetry.flush();
expect(flushHandler).toHaveBeenCalled();
});
it('should shutdown cleanly', async () => {
const span = telemetry.startSpan('test-span');
await telemetry.shutdown();
// Should not throw
telemetry.endSpan('test-span');
});
});
describe('disabled telemetry', () => {
it('should not create real spans when disabled', () => {
const disabledTelemetry = new Telemetry({
serviceName: 'test',
enabled: false
});
const span = disabledTelemetry.startSpan('test-span');
// Should return a no-op span
expect(span).toBeDefined();
expect(span.end).toBeInstanceOf(Function);
// Should not throw
span.end();
});
it('should not emit metrics when disabled', () => {
const disabledTelemetry = new Telemetry({
serviceName: 'test',
enabled: false
});
const metricHandler = vi.fn();
disabledTelemetry.on('metric', metricHandler);
disabledTelemetry.increment('test.counter');
expect(metricHandler).not.toHaveBeenCalled();
});
});
});
-363
View File
@@ -1,363 +0,0 @@
/**
* Hanzo Cloud telemetry integration
* Connects to Hanzo Cloud's observability platform for centralized monitoring
*/
import { Telemetry, TelemetryConfig } from './index';
import * as opentelemetry from '@opentelemetry/api';
import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import winston from 'winston';
export interface HanzoCloudConfig extends TelemetryConfig {
cloudUrl: string;
apiKey: string;
projectId: string;
environment?: string;
serviceName?: string;
serviceVersion?: string;
logLevel?: 'debug' | 'info' | 'warn' | 'error';
}
export class HanzoCloudTelemetry extends Telemetry {
private logger: winston.Logger;
private exporter?: OTLPTraceExporter;
private provider?: NodeTracerProvider;
private metricsBuffer: Map<string, any[]> = new Map();
private flushInterval?: NodeJS.Timeout;
constructor(config: HanzoCloudConfig) {
super(config);
// Setup Winston logger with Hanzo Cloud format
this.logger = this.createLogger(config);
// Initialize OpenTelemetry
this.initializeOpenTelemetry(config);
// Start metrics flush interval
this.startMetricsFlush(config);
// Log initialization
this.logger.info('Hanzo Cloud telemetry initialized', {
projectId: config.projectId,
environment: config.environment || 'development',
serviceName: config.serviceName || 'hanzo-ai'
});
}
private createLogger(config: HanzoCloudConfig): winston.Logger {
// Tracing format that adds trace context to logs
const tracingFormat = winston.format((info) => {
const span = opentelemetry.trace.getActiveSpan();
if (span) {
const { spanId, traceId } = span.spanContext();
info['trace_id'] = traceId;
info['span_id'] = spanId;
info['project_id'] = config.projectId;
info['service.name'] = config.serviceName || 'hanzo-ai';
}
return info;
});
return winston.createLogger({
level: config.logLevel || 'info',
format: winston.format.combine(
winston.format.errors({ stack: true }),
winston.format.timestamp(),
tracingFormat(),
winston.format.json()
),
defaultMeta: {
service: config.serviceName || 'hanzo-ai',
environment: config.environment || 'development',
projectId: config.projectId
},
transports: [
new winston.transports.Console(),
// Could add HTTP transport to send logs to Hanzo Cloud
]
});
}
private initializeOpenTelemetry(config: HanzoCloudConfig): void {
// Enable OpenTelemetry debugging in development
if (config.environment === 'development') {
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
}
// Create resource
const resource = resourceFromAttributes({
[ATTR_SERVICE_NAME]: config.serviceName || 'hanzo-ai',
[ATTR_SERVICE_VERSION]: config.serviceVersion || '1.0.0',
// stable semconv dropped deployment.environment; keep the raw key
'deployment.environment': config.environment || 'development',
'hanzo.project.id': config.projectId,
'hanzo.cloud.region': process.env.HANZO_CLOUD_REGION || 'us-east-1'
});
// Create OTLP exporter
this.exporter = new OTLPTraceExporter({
url: `${config.cloudUrl}/v1/traces`,
headers: {
'x-api-key': config.apiKey,
'x-project-id': config.projectId
}
});
// Create provider with batch processor (SDK 2.x: processors are
// constructor-only; addSpanProcessor was removed)
this.provider = new NodeTracerProvider({
resource,
spanProcessors: [
new BatchSpanProcessor(this.exporter, {
maxQueueSize: 1000,
maxExportBatchSize: 512,
scheduledDelayMillis: 5000,
exportTimeoutMillis: 30000
})
]
});
// Register as global provider
this.provider.register();
// Register instrumentations for automatic tracing
registerInstrumentations({
instrumentations: [
// Add instrumentations as needed
]
});
}
private startMetricsFlush(config: HanzoCloudConfig): void {
// Flush metrics every 30 seconds
this.flushInterval = setInterval(() => {
this.flushMetrics(config);
}, 30000);
// Listen for metric events
this.on('metric', (metric) => {
const key = `${metric.name}:${metric.type}`;
if (!this.metricsBuffer.has(key)) {
this.metricsBuffer.set(key, []);
}
this.metricsBuffer.get(key)!.push(metric);
});
}
private async flushMetrics(config: HanzoCloudConfig): Promise<void> {
if (this.metricsBuffer.size === 0) return;
const metrics = Array.from(this.metricsBuffer.entries()).map(([key, values]) => {
const [name, type] = key.split(':');
// Aggregate metrics
if (type === 'counter') {
const sum = values.reduce((acc, m) => acc + m.value, 0);
return { name, type, value: sum, tags: values[0].tags };
} else if (type === 'gauge') {
// Use latest value for gauges
return values[values.length - 1];
} else if (type === 'histogram') {
// Calculate percentiles for histograms
const sorted = values.map(m => m.value).sort((a, b) => a - b);
return {
name,
type,
value: {
count: sorted.length,
min: sorted[0],
max: sorted[sorted.length - 1],
p50: sorted[Math.floor(sorted.length * 0.5)],
p95: sorted[Math.floor(sorted.length * 0.95)],
p99: sorted[Math.floor(sorted.length * 0.99)]
},
tags: values[0].tags
};
}
return values[0];
});
// Send to Hanzo Cloud
try {
const response = await fetch(`${config.cloudUrl}/v1/metrics`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': config.apiKey,
'x-project-id': config.projectId
},
body: JSON.stringify({
metrics,
timestamp: Date.now()
})
});
if (!response.ok) {
this.logger.error('Failed to send metrics to Hanzo Cloud', {
status: response.status,
statusText: response.statusText
});
}
// Clear buffer after successful send
this.metricsBuffer.clear();
} catch (error) {
this.logger.error('Error sending metrics to Hanzo Cloud', { error });
}
}
/**
* Log a message with trace context
*/
log(level: 'debug' | 'info' | 'warn' | 'error', message: string, meta?: any): void {
this.logger[level](message, meta);
}
/**
* Record an agent execution
*/
recordAgentExecution(agentName: string, duration: number, success: boolean, metadata?: any): void {
this.increment('agent.executions', 1, {
agent: agentName,
success: success ? 'true' : 'false'
});
this.histogram('agent.execution.duration', duration, {
agent: agentName
});
if (metadata?.tokens) {
this.increment('agent.tokens.used', metadata.tokens, {
agent: agentName,
model: metadata.model || 'unknown'
});
}
this.log('info', `Agent ${agentName} executed`, {
agent: agentName,
duration,
success,
...metadata
});
}
/**
* Record a network execution
*/
recordNetworkExecution(
networkName: string,
iterations: number,
duration: number,
agentExecutions: Map<string, number>
): void {
this.increment('network.executions', 1, {
network: networkName
});
this.histogram('network.iterations', iterations, {
network: networkName
});
this.histogram('network.execution.duration', duration, {
network: networkName
});
// Record per-agent metrics within the network
for (const [agent, count] of agentExecutions) {
this.increment('network.agent.executions', count, {
network: networkName,
agent
});
}
this.log('info', `Network ${networkName} completed`, {
network: networkName,
iterations,
duration,
agents: Object.fromEntries(agentExecutions)
});
}
/**
* Record tool usage
*/
recordToolUsage(toolName: string, agentName: string, duration: number, success: boolean): void {
this.increment('tool.executions', 1, {
tool: toolName,
agent: agentName,
success: success ? 'true' : 'false'
});
this.histogram('tool.execution.duration', duration, {
tool: toolName,
agent: agentName
});
}
/**
* Record MCP server connection
*/
recordMCPConnection(serverName: string, success: boolean, metadata?: any): void {
this.increment('mcp.connections', 1, {
server: serverName,
success: success ? 'true' : 'false'
});
if (success) {
this.gauge('mcp.servers.active', 1, {
server: serverName
});
}
this.log('info', `MCP server ${serverName} connection ${success ? 'established' : 'failed'}`, {
server: serverName,
...metadata
});
}
/**
* Create a session for tracking related executions
*/
createSession(sessionId?: string): string {
const id = sessionId || `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
this.setAttributes({ 'hanzo.session.id': id });
return id;
}
/**
* Enhanced shutdown with cleanup
*/
async shutdown(): Promise<void> {
// Clear intervals
if (this.flushInterval) {
clearInterval(this.flushInterval);
}
// Flush remaining metrics
await this.flushMetrics(this.config as HanzoCloudConfig);
// Shutdown OpenTelemetry
if (this.provider) {
await this.provider.shutdown();
}
// Call parent shutdown
await super.shutdown();
this.logger.info('Hanzo Cloud telemetry shut down');
}
}
/**
* Create a Hanzo Cloud telemetry instance
*/
export function createHanzoCloudTelemetry(config: HanzoCloudConfig): HanzoCloudTelemetry {
return new HanzoCloudTelemetry(config);
}
-352
View File
@@ -1,352 +0,0 @@
/**
* Telemetry integration for Hanzo AI
* Provides distributed tracing, logging, and metrics for agent executions
*/
import * as opentelemetry from '@opentelemetry/api';
import { Span, SpanStatusCode, SpanKind } from '@opentelemetry/api';
import { EventEmitter } from 'events';
export interface TelemetryConfig {
serviceName?: string;
enabled?: boolean;
cloudUrl?: string;
apiKey?: string;
projectId?: string;
sessionId?: string;
metadata?: Record<string, any>;
}
export interface SpanContext {
traceId: string;
spanId: string;
traceFlags?: number;
}
export interface TelemetryEvent {
name: string;
attributes?: Record<string, any>;
timestamp?: number;
}
export class Telemetry extends EventEmitter {
private tracer: opentelemetry.Tracer;
private enabled: boolean;
private config: TelemetryConfig;
private activeSpans: Map<string, Span> = new Map();
constructor(config: TelemetryConfig = {}) {
super();
this.config = {
serviceName: 'hanzo-ai',
enabled: true,
...config
};
this.enabled = this.config.enabled ?? true;
this.tracer = opentelemetry.trace.getTracer(
this.config.serviceName || 'hanzo-ai',
'1.0.0'
);
}
/**
* Start a new span for tracing
*/
startSpan(
name: string,
options?: {
kind?: SpanKind;
attributes?: Record<string, any>;
parent?: Span | SpanContext;
}
): Span {
if (!this.enabled) {
return opentelemetry.trace.getTracer('noop').startSpan('noop');
}
const spanOptions: opentelemetry.SpanOptions = {
kind: options?.kind || SpanKind.INTERNAL,
attributes: {
'service.name': this.config.serviceName,
'hanzo.ai.version': '1.0.0',
...this.config.metadata,
...options?.attributes
}
};
// Handle parent span
let context = opentelemetry.context.active();
if (options?.parent) {
if ('spanContext' in options.parent) {
// It's a Span
context = opentelemetry.trace.setSpan(context, options.parent);
} else {
// It's a SpanContext - need to create a parent span
const parentSpan = this.tracer.startSpan('parent', {
context: opentelemetry.trace.setSpanContext(
opentelemetry.ROOT_CONTEXT,
options.parent as SpanContext
)
});
context = opentelemetry.trace.setSpan(context, parentSpan);
}
}
const span = this.tracer.startSpan(name, spanOptions, context);
this.activeSpans.set(name, span);
// Add default attributes
if (this.config.projectId) {
span.setAttribute('hanzo.project.id', this.config.projectId);
}
if (this.config.sessionId) {
span.setAttribute('hanzo.session.id', this.config.sessionId);
}
return span;
}
/**
* End a span
*/
endSpan(name: string, status?: { code: SpanStatusCode; message?: string }): void {
const span = this.activeSpans.get(name);
if (span) {
if (status) {
span.setStatus(status);
}
span.end();
this.activeSpans.delete(name);
}
}
/**
* Trace an async operation
*/
async trace<T>(
name: string,
fn: (span: Span) => Promise<T>,
options?: {
kind?: SpanKind;
attributes?: Record<string, any>;
}
): Promise<T> {
const span = this.startSpan(name, options);
try {
const result = await fn(span);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
this.recordException(error as Error, span);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : String(error)
});
throw error;
} finally {
span.end();
this.activeSpans.delete(name);
}
}
/**
* Trace a sync operation
*/
traceSync<T>(
name: string,
fn: (span: Span) => T,
options?: {
kind?: SpanKind;
attributes?: Record<string, any>;
}
): T {
const span = this.startSpan(name, options);
try {
const result = fn(span);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
this.recordException(error as Error, span);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : String(error)
});
throw error;
} finally {
span.end();
this.activeSpans.delete(name);
}
}
/**
* Record an exception in the current or specified span
*/
recordException(error: Error, span?: Span): void {
const activeSpan = span || this.getCurrentSpan();
if (!activeSpan) return;
activeSpan.recordException({
name: error.name,
message: error.message,
stack: error.stack
});
// Add error attributes for observability platforms
activeSpan.setAttributes({
'error.type': error.name,
'error.message': error.message,
'error.stack': error.stack
});
}
/**
* Record an event in the current or specified span
*/
recordEvent(event: TelemetryEvent, span?: Span): void {
const activeSpan = span || this.getCurrentSpan();
if (!activeSpan) return;
activeSpan.addEvent(event.name, event.attributes, event.timestamp);
}
/**
* Set attributes on the current or specified span
*/
setAttributes(attributes: Record<string, any>, span?: Span): void {
const activeSpan = span || this.getCurrentSpan();
if (!activeSpan) return;
activeSpan.setAttributes(attributes);
}
/**
* Get the currently active span
*/
getCurrentSpan(): Span | undefined {
return opentelemetry.trace.getActiveSpan();
}
/**
* Get the current trace context for propagation
*/
getTraceContext(): Record<string, string> {
const span = this.getCurrentSpan();
if (!span) return {};
const carrier: Record<string, string> = {};
opentelemetry.propagation.inject(
opentelemetry.trace.setSpan(opentelemetry.context.active(), span),
carrier
);
return carrier;
}
/**
* Create a child telemetry instance with inherited context
*/
createChild(name: string, attributes?: Record<string, any>): Telemetry {
return new Telemetry({
...this.config,
metadata: {
...this.config.metadata,
...attributes,
parent: name
}
});
}
/**
* Record metrics
*/
recordMetric(
name: string,
value: number,
type: 'counter' | 'gauge' | 'histogram' = 'gauge',
tags?: Record<string, string | number>
): void {
if (!this.enabled) return;
// Emit metric event for collection
this.emit('metric', {
name,
value,
type,
tags: {
...this.config.metadata,
...tags
},
timestamp: Date.now()
});
}
/**
* Increment a counter metric
*/
increment(name: string, value: number = 1, tags?: Record<string, string | number>): void {
this.recordMetric(name, value, 'counter', tags);
}
/**
* Record a gauge metric
*/
gauge(name: string, value: number, tags?: Record<string, string | number>): void {
this.recordMetric(name, value, 'gauge', tags);
}
/**
* Record a histogram metric
*/
histogram(name: string, value: number, tags?: Record<string, string | number>): void {
this.recordMetric(name, value, 'histogram', tags);
}
/**
* Flush all pending telemetry data
*/
async flush(): Promise<void> {
// End all active spans
for (const [name, span] of this.activeSpans) {
span.setStatus({ code: SpanStatusCode.OK });
span.end();
}
this.activeSpans.clear();
// Emit flush event for collectors
this.emit('flush');
}
/**
* Shutdown telemetry
*/
async shutdown(): Promise<void> {
await this.flush();
this.removeAllListeners();
}
}
// Global telemetry instance
let globalTelemetry: Telemetry | undefined;
/**
* Get or create the global telemetry instance
*/
export function getTelemetry(config?: TelemetryConfig): Telemetry {
if (!globalTelemetry) {
globalTelemetry = new Telemetry(config);
}
return globalTelemetry;
}
/**
* Set the global telemetry instance
*/
export function setTelemetry(telemetry: Telemetry): void {
globalTelemetry = telemetry;
}
// Export types
export { Span, SpanStatusCode, SpanKind } from '@opentelemetry/api';
-58
View File
@@ -1,58 +0,0 @@
/**
* Core types for @hanzo/ai
*/
export interface ModelInterface {
name?: string;
complete(params: CompletionParams): Promise<CompletionResponse>;
stream(params: CompletionParams): AsyncIterableIterator<StreamChunk>;
}
export interface CompletionParams {
messages: Message[];
tools?: Tool[];
temperature?: number;
maxTokens?: number;
}
export interface CompletionResponse {
content?: string;
toolCalls?: ToolCall[];
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
}
export interface StreamChunk {
type: 'content' | 'tool_call' | 'done';
content?: string;
toolCall?: ToolCall;
}
export interface Message {
role: 'system' | 'user' | 'assistant' | 'tool';
content?: string;
toolCalls?: ToolCall[];
toolResults?: ToolResult[];
metadata?: Record<string, any>;
}
export interface Tool {
name: string;
description: string;
parameters: any; // JSON Schema
}
export interface ToolCall {
id: string;
name: string;
arguments: any;
}
export interface ToolResult {
id: string;
result?: any;
error?: string;
}
-8
View File
@@ -1,8 +0,0 @@
import { nanoid } from 'nanoid';
/**
* Creates a unique completion ID
*/
export function createCompletionId(): string {
return `cmpl-${nanoid()}`;
}
-16
View File
@@ -1,16 +0,0 @@
import { z, ZodSchema } from 'zod';
/**
* Validates data against a schema
*/
export function validateSchema<T>(
schema: ZodSchema<T>,
data: unknown
): { success: true; data: T } | { success: false; error: z.ZodError } {
const result = schema.safeParse(data);
if (result.success) {
return { success: true, data: result.data };
} else {
return { success: false, error: result.error };
}
}
-25
View File
@@ -1,25 +0,0 @@
import { createParser, ParsedEvent } from 'eventsource-parser';
export interface StreamPart {
type: 'text' | 'function_call' | 'tool_calls' | 'data' | 'error' | 'done';
value: any;
}
/**
* Parses a stream part from a server-sent event
*/
export function parseStreamPart(data: string): StreamPart | null {
try {
const parsed = JSON.parse(data);
return parsed;
} catch {
return null;
}
}
/**
* Creates an event source parser
*/
export function createEventSourceParser(onParse: (event: ParsedEvent) => void) {
return createParser(onParse);
}
-21
View File
@@ -1,21 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
}
-14
View File
@@ -1,14 +0,0 @@
import { defineConfig } from 'tsup'
export default defineConfig({
entry: {
index: 'src/index.ts',
'server/index': 'src/server/index.ts',
},
format: ['cjs', 'esm'],
dts: false, // Temporarily disable type generation - needs fixing
splitting: false,
sourcemap: true,
clean: true,
external: ['react'],
})
-26
View File
@@ -1,26 +0,0 @@
import { defineConfig } from 'vitest/config';
import path from 'path';
export default defineConfig({
test: {
globals: true,
environment: 'node',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'dist/',
'**/*.d.ts',
'**/*.config.*',
'**/mockData.ts',
'**/__tests__/**'
]
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
}
});
-409
View File
@@ -136,64 +136,6 @@ importers:
specifier: ^3.2.6
version: 3.2.6(@types/node@20.19.9)(jsdom@26.1.0)(sass@1.60.0)(terser@5.43.1)
packages/ai:
dependencies:
'@modelcontextprotocol/sdk':
specifier: 1.26.0
version: 1.26.0(zod@3.25.71)
'@opentelemetry/api':
specifier: ^1.9.1
version: 1.9.1
'@opentelemetry/exporter-trace-otlp-http':
specifier: ^0.220.0
version: 0.220.0(@opentelemetry/api@1.9.1)
'@opentelemetry/instrumentation':
specifier: ^0.220.0
version: 0.220.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources':
specifier: ^2.9.0
version: 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace-base':
specifier: ^2.9.0
version: 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace-node':
specifier: ^2.9.0
version: 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions':
specifier: ^1.41.1
version: 1.41.1
eventsource-parser:
specifier: ^1.0.0
version: 1.1.2
nanoid:
specifier: ^5.0.0
version: 5.1.5
react:
specifier: ^18.0.0
version: 18.3.1
winston:
specifier: ^3.11.0
version: 3.17.0
zod:
specifier: ^3.22.0
version: 3.25.71
devDependencies:
'@types/node':
specifier: ^20.0.0
version: 20.19.9
'@types/react':
specifier: ^18.0.0
version: 18.3.23
tsup:
specifier: ^8.0.0
version: 8.5.0(postcss@8.5.10)(typescript@5.8.3)
typescript:
specifier: ^5.3.0
version: 5.8.3
vitest:
specifier: ^3.2.6
version: 3.2.6(@types/node@20.19.9)(jsdom@26.1.0)(sass@1.60.0)(terser@5.43.1)
packages/auth:
devDependencies:
typescript:
@@ -1969,10 +1911,6 @@ packages:
'@codemirror/view@6.43.4':
resolution: {integrity: sha512-YImu23iyKfncJzT7sRy+rEqEhSc8RhOHqDxwy4WzXRKJwYm6iwf/9OJk5ctCAdZ6yi2ZqaGEvmf55fSVqMDrgg==}
'@colors/colors@1.6.0':
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
engines: {node: '>=0.1.90'}
'@csstools/color-helpers@5.0.2':
resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
engines: {node: '>=18'}
@@ -2001,9 +1939,6 @@ packages:
resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
engines: {node: '>=18'}
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
'@discoveryjs/json-ext@0.5.7':
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
engines: {node: '>=10.0.0'}
@@ -4018,90 +3953,6 @@ packages:
resolution: {integrity: sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==}
engines: {node: '>= 20'}
'@opentelemetry/api-logs@0.220.0':
resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==}
engines: {node: '>=8.0.0'}
'@opentelemetry/api@1.9.1':
resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==}
engines: {node: '>=8.0.0'}
'@opentelemetry/context-async-hooks@2.9.0':
resolution: {integrity: sha512-OQ0vzvbZBiUhjqLnUaoNfYmP8553Crr3aggB4y0ZUi815mZ7idpdJXQmoKdeBKJelYttoBlLSSHubmyw3wvX4w==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/core@2.9.0':
resolution: {integrity: sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/exporter-trace-otlp-http@0.220.0':
resolution: {integrity: sha512-/+ExB3lRkf+erv4PnoywyL7RHKITidxtUpUTS55k7OQ0dB42S7gEF1gry7swb9MSm1hYLUhJg4QQh9W8SpwwqA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/instrumentation@0.220.0':
resolution: {integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/otlp-exporter-base@0.220.0':
resolution: {integrity: sha512-CXYo8UD5Mn9YbgebO2EL4wejtA+gxLmLiu6HCk2KH2BR7XhFN6/6p1UlCb23DYCjeYkndevLHuejCCN1yx4+OQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/otlp-transformer@0.220.0':
resolution: {integrity: sha512-lXGrv7KXZ0gNH9SVNUaa6vv6phVYGvJxfXAlMbzbakiXru75f5MZl8Z7oqiMMQD77riVHJCFlQvbZs/VVN2/4A==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
'@opentelemetry/resources@2.9.0':
resolution: {integrity: sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
'@opentelemetry/sdk-logs@0.220.0':
resolution: {integrity: sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.4.0 <1.10.0'
'@opentelemetry/sdk-metrics@2.9.0':
resolution: {integrity: sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.9.0 <1.10.0'
'@opentelemetry/sdk-trace-base@2.9.0':
resolution: {integrity: sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
'@opentelemetry/sdk-trace-node@2.9.0':
resolution: {integrity: sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/sdk-trace@2.9.0':
resolution: {integrity: sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
'@opentelemetry/semantic-conventions@1.41.1':
resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==}
engines: {node: '>=14'}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -4947,9 +4798,6 @@ packages:
'@types/tough-cookie@4.0.5':
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
'@types/triple-beam@1.3.5':
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
@@ -5349,11 +5197,6 @@ packages:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
acorn-import-attributes@1.9.5:
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
peerDependencies:
acorn: ^8
acorn-import-phases@1.0.4:
resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==}
engines: {node: '>=10.13.0'}
@@ -5987,9 +5830,6 @@ packages:
cjs-module-lexer@1.4.3:
resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
cjs-module-lexer@2.2.0:
resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==}
classnames@2.5.1:
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
@@ -6087,9 +5927,6 @@ packages:
color-string@1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
color@3.2.1:
resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
color@4.2.3:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
@@ -6097,9 +5934,6 @@ packages:
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
colorspace@1.1.4:
resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
@@ -6759,9 +6593,6 @@ packages:
resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
engines: {node: '>= 4'}
enabled@2.0.0:
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
@@ -7002,10 +6833,6 @@ packages:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
eventsource-parser@1.1.2:
resolution: {integrity: sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==}
engines: {node: '>=14.18'}
eventsource-parser@3.0.3:
resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==}
engines: {node: '>=20.0.0'}
@@ -7153,9 +6980,6 @@ packages:
picomatch:
optional: true
fecha@4.2.3:
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
@@ -7256,9 +7080,6 @@ packages:
flatted@3.4.2:
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
fn.name@1.1.0:
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
follow-redirects@1.16.0:
resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==}
engines: {node: '>=4.0'}
@@ -7707,10 +7528,6 @@ packages:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
import-in-the-middle@3.2.0:
resolution: {integrity: sha512-vR2B6HKIhaBjcZr2bLpFiJ1VbzOlRQ7aby4/gw5WPIzToLjqpfWw3VJ4sk1uDchoOODEirvO2jyrSPtUSL5CrQ==}
engines: {node: '>=18'}
import-local@3.2.0:
resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==}
engines: {node: '>=8'}
@@ -8428,9 +8245,6 @@ packages:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
kuler@2.0.0:
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
layout-base@1.0.2:
resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
@@ -8601,10 +8415,6 @@ packages:
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
engines: {node: '>=18'}
logform@2.7.0:
resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==}
engines: {node: '>= 12.0.0'}
long@4.0.0:
resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==}
@@ -8945,9 +8755,6 @@ packages:
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
module-details-from-path@1.0.4:
resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
mongodb-connection-string-url@3.0.2:
resolution: {integrity: sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==}
@@ -9014,11 +8821,6 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nanoid@5.1.5:
resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
engines: {node: ^18 || >=20}
hasBin: true
napi-build-utils@1.0.2:
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
@@ -9223,9 +9025,6 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
one-time@1.0.0:
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
@@ -9898,10 +9697,6 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
require-in-the-middle@8.0.1:
resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==}
engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'}
requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
@@ -10360,9 +10155,6 @@ packages:
resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==}
engines: {node: ^18.17.0 || >=20.5.0}
stack-trace@0.0.10:
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
@@ -10639,9 +10431,6 @@ packages:
text-decoder@1.2.3:
resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==}
text-hex@1.0.0:
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@@ -10765,10 +10554,6 @@ packages:
tree-sitter@0.21.1:
resolution: {integrity: sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==}
triple-beam@1.4.1:
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
engines: {node: '>= 14.0.0'}
ts-api-utils@1.4.3:
resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==}
engines: {node: '>=16'}
@@ -11417,14 +11202,6 @@ packages:
wildcard@2.0.1:
resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==}
winston-transport@4.9.0:
resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==}
engines: {node: '>= 12.0.0'}
winston@3.17.0:
resolution: {integrity: sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==}
engines: {node: '>= 12.0.0'}
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@@ -12754,8 +12531,6 @@ snapshots:
style-mod: 4.1.3
w3c-keyname: 2.2.8
'@colors/colors@1.6.0': {}
'@csstools/color-helpers@5.0.2': {}
'@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
@@ -12776,12 +12551,6 @@ snapshots:
'@csstools/css-tokenizer@3.0.4': {}
'@dabh/diagnostics@2.0.3':
dependencies:
colorspace: 1.1.4
enabled: 2.0.0
kuler: 2.0.0
'@discoveryjs/json-ext@0.5.7': {}
'@emnapi/runtime@1.4.5':
@@ -15620,99 +15389,6 @@ snapshots:
'@octokit/request-error': 7.1.0
'@octokit/webhooks-methods': 6.0.0
'@opentelemetry/api-logs@0.220.0':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/api@1.9.1': {}
'@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/semantic-conventions': 1.41.1
'@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.9.1)
'@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/api-logs': 0.220.0
import-in-the-middle: 3.2.0
require-in-the-middle: 8.0.1
transitivePeerDependencies:
- supports-color
'@opentelemetry/otlp-exporter-base@0.220.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.9.1)
'@opentelemetry/otlp-transformer@0.220.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/api-logs': 0.220.0
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.41.1
'@opentelemetry/sdk-logs@0.220.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/api-logs': 0.220.0
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.41.1
'@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.41.1
'@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-trace@2.9.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.41.1
'@opentelemetry/semantic-conventions@1.41.1': {}
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -16624,8 +16300,6 @@ snapshots:
'@types/tough-cookie@4.0.5': {}
'@types/triple-beam@1.3.5': {}
'@types/trusted-types@2.0.7':
optional: true
@@ -17307,10 +16981,6 @@ snapshots:
mime-types: 3.0.1
negotiator: 1.0.0
acorn-import-attributes@1.9.5(acorn@8.15.0):
dependencies:
acorn: 8.15.0
acorn-import-phases@1.0.4(acorn@8.17.0):
dependencies:
acorn: 8.17.0
@@ -18114,8 +17784,6 @@ snapshots:
cjs-module-lexer@1.4.3: {}
cjs-module-lexer@2.2.0: {}
classnames@2.5.1: {}
clean-stack@2.2.0: {}
@@ -18204,11 +17872,6 @@ snapshots:
color-name: 1.1.4
simple-swizzle: 0.2.2
color@3.2.1:
dependencies:
color-convert: 1.9.3
color-string: 1.9.1
color@4.2.3:
dependencies:
color-convert: 2.0.1
@@ -18216,11 +17879,6 @@ snapshots:
colorette@2.0.20: {}
colorspace@1.1.4:
dependencies:
color: 3.2.1
text-hex: 1.0.0
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
@@ -18867,8 +18525,6 @@ snapshots:
emojis-list@3.0.0: {}
enabled@2.0.0: {}
encodeurl@1.0.2: {}
encodeurl@2.0.0: {}
@@ -19268,8 +18924,6 @@ snapshots:
events@3.3.0: {}
eventsource-parser@1.1.2: {}
eventsource-parser@3.0.3: {}
eventsource@3.0.7:
@@ -19475,8 +19129,6 @@ snapshots:
optionalDependencies:
picomatch: 4.0.4
fecha@4.2.3: {}
fetch-blob@3.2.0:
dependencies:
node-domexception: 1.0.0
@@ -19630,8 +19282,6 @@ snapshots:
flatted@3.4.2: {}
fn.name@1.1.0: {}
follow-redirects@1.16.0: {}
for-each@0.3.5:
@@ -20164,13 +19814,6 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
import-in-the-middle@3.2.0:
dependencies:
acorn: 8.15.0
acorn-import-attributes: 1.9.5(acorn@8.15.0)
cjs-module-lexer: 2.2.0
module-details-from-path: 1.0.4
import-local@3.2.0:
dependencies:
pkg-dir: 4.2.0
@@ -21183,8 +20826,6 @@ snapshots:
kleur@3.0.3: {}
kuler@2.0.0: {}
layout-base@1.0.2: {}
layout-base@2.0.1: {}
@@ -21339,15 +20980,6 @@ snapshots:
chalk: 5.4.1
is-unicode-supported: 1.3.0
logform@2.7.0:
dependencies:
'@colors/colors': 1.6.0
'@types/triple-beam': 1.3.5
fecha: 4.2.3
ms: 2.1.3
safe-stable-stringify: 2.5.0
triple-beam: 1.4.1
long@4.0.0:
optional: true
@@ -21653,8 +21285,6 @@ snapshots:
yargs-parser: 21.1.1
yargs-unparser: 2.0.0
module-details-from-path@1.0.4: {}
mongodb-connection-string-url@3.0.2:
dependencies:
'@types/whatwg-url': 11.0.5
@@ -21706,8 +21336,6 @@ snapshots:
nanoid@3.3.11: {}
nanoid@5.1.5: {}
napi-build-utils@1.0.2: {}
napi-build-utils@2.0.0: {}
@@ -21894,10 +21522,6 @@ snapshots:
dependencies:
wrappy: 1.0.2
one-time@1.0.0:
dependencies:
fn.name: 1.1.0
onetime@5.1.2:
dependencies:
mimic-fn: 2.1.0
@@ -22630,13 +22254,6 @@ snapshots:
require-from-string@2.0.2: {}
require-in-the-middle@8.0.1:
dependencies:
debug: 4.4.3(supports-color@8.1.1)
module-details-from-path: 1.0.4
transitivePeerDependencies:
- supports-color
requires-port@1.0.0: {}
resize-observer-polyfill@1.5.1: {}
@@ -23315,8 +22932,6 @@ snapshots:
dependencies:
minipass: 7.1.2
stack-trace@0.0.10: {}
stack-utils@2.0.6:
dependencies:
escape-string-regexp: 2.0.0
@@ -23601,8 +23216,6 @@ snapshots:
dependencies:
b4a: 1.6.7
text-hex@1.0.0: {}
text-table@0.2.0: {}
thenify-all@1.6.0:
@@ -23709,8 +23322,6 @@ snapshots:
node-addon-api: 8.4.0
node-gyp-build: 4.8.4
triple-beam@1.4.1: {}
ts-api-utils@1.4.3(typescript@5.8.3):
dependencies:
typescript: 5.8.3
@@ -24647,26 +24258,6 @@ snapshots:
wildcard@2.0.1: {}
winston-transport@4.9.0:
dependencies:
logform: 2.7.0
readable-stream: 3.6.2
triple-beam: 1.4.1
winston@3.17.0:
dependencies:
'@colors/colors': 1.6.0
'@dabh/diagnostics': 2.0.3
async: 3.2.6
is-stream: 2.0.1
logform: 2.7.0
one-time: 1.0.0
readable-stream: 3.6.2
safe-stable-stringify: 2.5.0
stack-trace: 0.0.10
triple-beam: 1.4.1
winston-transport: 4.9.0
word-wrap@1.2.5: {}
wordwrap@1.0.0: {}
-1
View File
@@ -1,5 +1,4 @@
packages:
- 'packages/ai'
- 'packages/aci'
- 'packages/auth'
- 'packages/browser'