diff --git a/.changeset/convert-to-zod.md b/.changeset/convert-to-zod.md new file mode 100644 index 0000000..bea2cbf --- /dev/null +++ b/.changeset/convert-to-zod.md @@ -0,0 +1,6 @@ +--- +'mycoder-agent': minor +'mycoder': minor +--- + +Convert from JsonSchema7Type to ZodSchema for tool parameters and returns, required for Vercel AI SDK integration. diff --git a/.changeset/implement-token-caching.md b/.changeset/implement-token-caching.md new file mode 100644 index 0000000..4f5e288 --- /dev/null +++ b/.changeset/implement-token-caching.md @@ -0,0 +1,5 @@ +--- +'mycoder-agent': patch +--- + +Re-implemented token caching for Vercel AI SDK usage with Anthropic provider to reduce token consumption during repeated API calls. diff --git a/.changeset/refactor-tool-agent.md b/.changeset/refactor-tool-agent.md new file mode 100644 index 0000000..2db0f5d --- /dev/null +++ b/.changeset/refactor-tool-agent.md @@ -0,0 +1,5 @@ +--- +'mycoder-agent': minor +--- + +Refactored toolAgent.ts into modular components for improved maintainability and testability. Split into config.ts, messageUtils.ts, toolExecutor.ts, tokenTracking.ts, and types.ts modules. diff --git a/.changeset/temp-changeset-message.txt b/.changeset/temp-changeset-message.txt deleted file mode 100644 index da31bbe..0000000 --- a/.changeset/temp-changeset-message.txt +++ /dev/null @@ -1 +0,0 @@ -Add textEditor tool that combines readFile and updateFile functionality diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 59bc346..ac90ad2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,17 +80,19 @@ This project and everyone participating in it is governed by our Code of Conduct 5. Push to your fork and create a Pull Request 6. Pre-commit Hooks: - + We use [husky](https://typicode.github.io/husky/) and [lint-staged](https://github.com/okonet/lint-staged) to automatically run linting and formatting on staged files before each commit. This helps maintain code quality and consistency. The pre-commit hooks are configured to run: - - `pnpm lint`: Lints the staged files using ESLint + + - `pnpm lint`: Lints the staged files using ESLint - `pnpm format`: Formats the staged files using Prettier If either of these commands fails due to linting errors or formatting issues, the commit will be aborted. Please fix the reported issues and try committing again. You can also run the lint and format commands manually at any time: - ```bash + + ```bash pnpm lint # Lint all files pnpm format # Format all files ``` diff --git a/docs/LargeCodeBase_Plan.md b/docs/LargeCodeBase_Plan.md index 52a7781..b9ecb13 100644 --- a/docs/LargeCodeBase_Plan.md +++ b/docs/LargeCodeBase_Plan.md @@ -11,16 +11,19 @@ This document presents research findings on how leading AI coding tools handle l While detailed technical documentation on Claude Code's internal architecture is limited in public sources, we can infer several approaches from Anthropic's general AI architecture and Claude Code's capabilities: 1. **Chunking and Retrieval Augmentation**: + - Claude Code likely employs retrieval-augmented generation (RAG) to handle large codebases - Files are likely chunked into manageable segments with semantic understanding - Relevant code chunks are retrieved based on query relevance 2. **Hierarchical Code Understanding**: + - Builds a hierarchical representation of code (project → modules → files → functions) - Maintains a graph of relationships between code components - Prioritizes context based on relevance to the current task 3. **Incremental Context Management**: + - Dynamically adjusts the context window to include only relevant code - Maintains a "working memory" of recently accessed or modified files - Uses sliding context windows to process large files sequentially @@ -35,16 +38,19 @@ While detailed technical documentation on Claude Code's internal architecture is Aider's approach to handling large codebases can be inferred from its open-source codebase and documentation: 1. **Git Integration**: + - Leverages Git to track file changes and understand repository structure - Uses Git history to prioritize recently modified files - Employs Git's diff capabilities to minimize context needed for changes 2. **Selective File Context**: + - Only includes relevant files in the context rather than the entire codebase - Uses heuristics to identify related files based on imports, references, and naming patterns - Implements a "map-reduce" approach where it first analyzes the codebase structure, then selectively processes relevant files 3. **Prompt Engineering and Chunking**: + - Designs prompts that can work with limited context by focusing on specific tasks - Chunks large files and processes them incrementally - Uses summarization to compress information about non-focal code parts @@ -90,6 +96,7 @@ Based on the research findings, we recommend the following enhancements to MyCod ``` **Implementation Details:** + - Create a lightweight indexer that runs during project initialization - Generate embeddings for code files, focusing on API definitions, function signatures, and documentation - Build a graph of relationships between files based on imports/exports and references @@ -120,6 +127,7 @@ Based on the research findings, we recommend the following enhancements to MyCod ``` **Implementation Details:** + - Develop a working set manager that tracks currently relevant files - Implement a relevance scoring algorithm that considers: - Semantic similarity to the current task @@ -148,6 +156,7 @@ Based on the research findings, we recommend the following enhancements to MyCod ``` **Implementation Details:** + - Chunk files at meaningful boundaries (functions, classes, modules) - Implement overlapping chunks to maintain context across boundaries - Develop a progressive loading strategy: @@ -181,6 +190,7 @@ Based on the research findings, we recommend the following enhancements to MyCod ``` **Implementation Details:** + - Implement a multi-level caching system: - Token cache: Store tokenized representations of files to avoid re-tokenization - Embedding cache: Store vector embeddings for semantic search @@ -209,6 +219,7 @@ Based on the research findings, we recommend the following enhancements to MyCod ``` **Implementation Details:** + - Improve task decomposition to identify parallelizable sub-tasks - Implement smart context distribution to sub-agents: - Provide each sub-agent with only the context it needs @@ -222,16 +233,19 @@ Based on the research findings, we recommend the following enhancements to MyCod ## Implementation Roadmap ### Phase 1: Foundation (1-2 months) + - Develop the basic indexing system for project structure and file metadata - Implement a simple relevance-based context selection mechanism - Create a basic chunking strategy for large files ### Phase 2: Advanced Features (2-3 months) + - Implement the semantic indexing system with code embeddings - Develop the full context management system with working sets - Create the multi-level caching system ### Phase 3: Optimization and Integration (1-2 months) + - Enhance sub-agent coordination for parallel processing - Optimize performance with better caching and context management - Integrate all components into a cohesive system diff --git a/docs/SentryIntegration.md b/docs/SentryIntegration.md index b6897f2..8ab2745 100644 --- a/docs/SentryIntegration.md +++ b/docs/SentryIntegration.md @@ -17,6 +17,7 @@ npm install @sentry/node --save ## Configuration By default, Sentry is: + - Enabled in production environments - Disabled in development environments (unless explicitly enabled) - Configured to capture 100% of transactions @@ -56,7 +57,9 @@ Sentry.init({ tracesSampleRate: 1.0, environment: process.env.NODE_ENV || 'development', release: `mycoder@${packageVersion}`, - enabled: process.env.NODE_ENV !== 'development' || process.env.ENABLE_SENTRY === 'true', + enabled: + process.env.NODE_ENV !== 'development' || + process.env.ENABLE_SENTRY === 'true', }); // Capture errors @@ -76,6 +79,7 @@ mycoder test-sentry ``` This command will: + 1. Generate a test error that includes the package version 2. Report it to Sentry.io 3. Output the result to the console @@ -85,6 +89,7 @@ Note: In development environments, you may need to set `ENABLE_SENTRY=true` for ## Privacy Error reports sent to Sentry include: + - Stack traces - Error messages - Environment information diff --git a/packages/agent/package.json b/packages/agent/package.json index 6e20de1..4afa424 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -44,13 +44,16 @@ "author": "Ben Houston", "license": "MIT", "dependencies": { - "@anthropic-ai/sdk": "^0.37", + "@ai-sdk/anthropic": "^1.1.13", + "@ai-sdk/openai": "^1.2.0", "@mozilla/readability": "^0.5.0", "@playwright/test": "^1.50.1", "@vitest/browser": "^3.0.5", + "ai": "^4.1.50", "chalk": "^5.4.1", "dotenv": "^16", "jsdom": "^26.0.0", + "ollama-ai-provider": "^1.2.0", "playwright": "^1.50.1", "uuid": "^11", "zod": "^3.24.2", diff --git a/packages/agent/src/core/tokens.ts b/packages/agent/src/core/tokens.ts index e1d99da..ebad962 100644 --- a/packages/agent/src/core/tokens.ts +++ b/packages/agent/src/core/tokens.ts @@ -1,4 +1,4 @@ -import Anthropic from '@anthropic-ai/sdk'; +//import Anthropic from '@anthropic-ai/sdk'; import { LogLevel } from '../utils/logger.js'; @@ -34,6 +34,7 @@ export class TokenUsage { return usage; } + /* static fromMessage(message: Anthropic.Message) { const usage = new TokenUsage(); usage.input = message.usage.input_tokens; @@ -41,7 +42,7 @@ export class TokenUsage { usage.cacheReads = message.usage.cache_read_input_tokens ?? 0; usage.output = message.usage.output_tokens; return usage; - } + }*/ static sum(usages: TokenUsage[]) { const usage = new TokenUsage(); diff --git a/packages/agent/src/core/toolAgent.respawn.test.ts b/packages/agent/src/core/toolAgent.respawn.test.ts deleted file mode 100644 index be5c49a..0000000 --- a/packages/agent/src/core/toolAgent.respawn.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -import { toolAgent } from '../../src/core/toolAgent.js'; -import { getTools } from '../../src/tools/getTools.js'; -import { MockLogger } from '../utils/mockLogger.js'; - -import { TokenTracker } from './tokens.js'; -import { ToolContext } from './types.js'; - -const toolContext: ToolContext = { - logger: new MockLogger(), - headless: true, - workingDirectory: '.', - userSession: false, - pageFilter: 'simple', - tokenTracker: new TokenTracker(), -}; -// Mock Anthropic SDK -vi.mock('@anthropic-ai/sdk', () => { - return { - default: vi.fn().mockImplementation(() => ({ - messages: { - create: vi - .fn() - .mockResolvedValueOnce({ - content: [ - { - type: 'tool_use', - name: 'respawn', - id: 'test-id', - input: { respawnContext: 'new context' }, - }, - ], - usage: { input_tokens: 10, output_tokens: 10 }, - }) - .mockResolvedValueOnce({ - content: [], - usage: { input_tokens: 5, output_tokens: 5 }, - }), - }, - })), - }; -}); - -describe('toolAgent respawn functionality', () => { - const tools = getTools(); - - beforeEach(() => { - process.env.ANTHROPIC_API_KEY = 'test-key'; - vi.clearAllMocks(); - }); - - it('should handle respawn tool calls', async () => { - const result = await toolAgent( - 'initial prompt', - tools, - { - maxIterations: 2, // Need at least 2 iterations for respawn + empty response - model: 'test-model', - maxTokens: 100, - temperature: 0, - getSystemPrompt: () => 'test system prompt', - }, - toolContext, - ); - - expect(result.result).toBe( - 'Maximum sub-agent iterations reach without successful completion', - ); - }); -}); diff --git a/packages/agent/src/core/toolAgent.test.ts b/packages/agent/src/core/toolAgent.test.ts index 43bf116..16fd17f 100644 --- a/packages/agent/src/core/toolAgent.test.ts +++ b/packages/agent/src/core/toolAgent.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { z } from 'zod'; import { MockLogger } from '../utils/mockLogger.js'; import { executeToolCall } from './executeToolCall.js'; import { TokenTracker } from './tokens.js'; -import { toolAgent } from './toolAgent.js'; import { Tool, ToolContext } from './types.js'; const toolContext: ToolContext = { @@ -16,91 +16,59 @@ const toolContext: ToolContext = { tokenTracker: new TokenTracker(), }; -// Mock configuration for testing -const testConfig = { - maxIterations: 50, - model: 'claude-3-7-sonnet-latest', - maxTokens: 4096, - temperature: 0.7, - getSystemPrompt: () => 'Test system prompt', -}; - -// Mock Anthropic client response -const mockResponse = { - content: [ - { - type: 'tool_use', - name: 'sequenceComplete', - id: '1', - input: { result: 'Test complete' }, +// Mock tool for testing +const mockTool: Tool = { + name: 'mockTool', + description: 'A mock tool for testing', + parameters: z.object({ + input: z.string().describe('Test input'), + }), + returns: z.string().describe('The processed result'), + parametersJsonSchema: { + type: 'object', + properties: { + input: { + type: 'string', + description: 'Test input', + }, }, - ], - usage: { input_tokens: 10, output_tokens: 10 }, - model: 'claude-3-7-sonnet-latest', - role: 'assistant', - id: 'msg_123', + required: ['input'], + }, + returnsJsonSchema: { + type: 'string', + description: 'The processed result', + }, + execute: ({ input }) => Promise.resolve(`Processed: ${input}`), }; -// Mock Anthropic SDK -const mockCreate = vi.fn().mockImplementation(() => mockResponse); -vi.mock('@anthropic-ai/sdk', () => ({ - default: class { - messages = { - create: mockCreate, - }; +const errorTool: Tool = { + name: 'errorTool', + description: 'A tool that always fails', + parameters: z.object({}), + returns: z.string().describe('Error message'), + parametersJsonSchema: { + type: 'object', + properties: {}, + required: [], + }, + returnsJsonSchema: { + type: 'string', + description: 'Error message', + }, + execute: () => { + throw new Error('Deliberate failure'); }, -})); +}; describe('toolAgent', () => { beforeEach(() => { - process.env.ANTHROPIC_API_KEY = 'test-key'; + vi.clearAllMocks(); }); afterEach(() => { vi.clearAllMocks(); }); - // Mock tool for testing - const mockTool: Tool = { - name: 'mockTool', - description: 'A mock tool for testing', - parameters: { - type: 'object', - properties: { - input: { - type: 'string', - description: 'Test input', - }, - }, - required: ['input'], - }, - returns: { - type: 'string', - description: 'The processed result', - }, - execute: ({ input }) => Promise.resolve(`Processed: ${input}`), - }; - - const sequenceCompleteTool: Tool = { - name: 'sequenceComplete', - description: 'Completes the sequence', - parameters: { - type: 'object', - properties: { - result: { - type: 'string', - description: 'The final result', - }, - }, - required: ['result'], - }, - returns: { - type: 'string', - description: 'The final result', - }, - execute: ({ result }) => Promise.resolve(result), - }; - it('should execute tool calls', async () => { const result = await executeToolCall( { @@ -130,23 +98,6 @@ describe('toolAgent', () => { }); it('should handle tool execution errors', async () => { - const errorTool: Tool = { - name: 'errorTool', - description: 'A tool that always fails', - parameters: { - type: 'object', - properties: {}, - required: [], - }, - returns: { - type: 'string', - description: 'Error message', - }, - execute: () => { - throw new Error('Deliberate failure'); - }, - }; - await expect( executeToolCall( { @@ -159,43 +110,4 @@ describe('toolAgent', () => { ), ).rejects.toThrow('Deliberate failure'); }); - - // Test empty response handling - it('should handle empty responses by sending a reminder', async () => { - // Reset the mock and set up the sequence of responses - mockCreate.mockReset(); - mockCreate - .mockResolvedValueOnce({ - content: [], - usage: { input_tokens: 5, output_tokens: 5 }, - }) - .mockResolvedValueOnce(mockResponse); - - const result = await toolAgent( - 'Test prompt', - [sequenceCompleteTool], - testConfig, - toolContext, - ); - - // Verify that create was called twice (once for empty response, once for completion) - expect(mockCreate).toHaveBeenCalledTimes(2); - expect(result.result).toBe('Test complete'); - }); - - // New tests for async system prompt - it('should handle async system prompt', async () => { - // Reset mock and set expected response - mockCreate.mockReset(); - mockCreate.mockResolvedValue(mockResponse); - - const result = await toolAgent( - 'Test prompt', - [sequenceCompleteTool], - testConfig, - toolContext, - ); - - expect(result.result).toBe('Test complete'); - }); }); diff --git a/packages/agent/src/core/toolAgent.ts b/packages/agent/src/core/toolAgent.ts index 4a7c9ae..f99fccb 100644 --- a/packages/agent/src/core/toolAgent.ts +++ b/packages/agent/src/core/toolAgent.ts @@ -1,389 +1,22 @@ -import { execSync } from 'child_process'; - -import Anthropic from '@anthropic-ai/sdk'; -import { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages/messages.js'; -import chalk from 'chalk'; - -import { getAnthropicApiKeyError } from '../utils/errors.js'; - -import { executeToolCall } from './executeToolCall.js'; -import { TokenTracker, TokenUsage } from './tokens.js'; -import { - Tool, - TextContent, - ToolUseContent, - ToolResultContent, - Message, - ToolContext, -} from './types.js'; - -export interface ToolAgentResult { - result: string; - interactions: number; -} - -const CONFIG = { - maxIterations: 200, - model: 'claude-3-7-sonnet-latest', - maxTokens: 4096, - temperature: 0.7, - getSystemPrompt: () => { - // Gather context with error handling - const getCommandOutput = (command: string, label: string): string => { - try { - return execSync(command).toString().trim(); - } catch (error) { - return `[Error getting ${label}: ${(error as Error).message}]`; - } - }; - - const context = { - pwd: getCommandOutput('pwd', 'current directory'), - files: getCommandOutput('ls -la', 'file listing'), - system: getCommandOutput('uname -a', 'system information'), - datetime: new Date().toString(), - }; - - return [ - 'You are an AI agent that can use tools to accomplish tasks.', - '', - 'Current Context:', - `Directory: ${context.pwd}`, - 'Files:', - context.files, - `System: ${context.system}`, - `DateTime: ${context.datetime}`, - '', - 'You prefer to call tools in parallel when possible because it leads to faster execution and less resource usage.', - 'When done, call the sequenceComplete tool with your results to indicate that the sequence has completed.', - '', - 'For coding tasks:', - '0. Try to break large tasks into smaller sub-tasks that can be completed and verified sequentially.', - " - trying to make lots of changes in one go can make it really hard to identify when something doesn't work", - ' - use sub-agents for each sub-task, leaving the main agent in a supervisory role', - ' - when possible ensure the project compiles/builds and the tests pass after each sub-task', - ' - give the sub-agents the guidance and context necessary be successful', - '1. First understand the context by:', - ' - Reading README.md, CONTRIBUTING.md, and similar documentation', - ' - Checking project configuration files (e.g., package.json)', - ' - Understanding coding standards', - '2. Ensure changes:', - ' - Follow project conventions', - ' - Build successfully', - ' - Pass all tests', - '3. Update documentation as needed', - '4. Consider adding documentation if you encountered setup/understanding challenges', - '', - 'Feel free to use Google and Bing via the browser tools to search for information or for ideas when you get stuck.', - '', - 'When you run into issues or unexpected results, take a step back and read the project documentation and configuration files and look at other source files in the project for examples of what works.', - '', - 'Use sub-agents for parallel tasks, providing them with specific context they need rather than having them rediscover it.', - ].join('\\n'); - }, -}; - -interface ToolCallResult { - sequenceCompleted: boolean; - completionResult?: string; - toolResults: ToolResultContent[]; -} - -function processResponse(response: Anthropic.Message) { - const content: (TextContent | ToolUseContent)[] = []; - const toolCalls: ToolUseContent[] = []; - - for (const message of response.content) { - if (message.type === 'text') { - content.push({ type: 'text', text: message.text }); - } else if (message.type === 'tool_use') { - const toolUse: ToolUseContent = { - type: 'tool_use', - name: message.name, - id: message.id, - input: message.input, - }; - content.push(toolUse); - toolCalls.push(toolUse); - } - } - - return { content, toolCalls }; -} - -async function executeTools( - toolCalls: ToolUseContent[], - tools: Tool[], - messages: Message[], - context: ToolContext, -): Promise { - if (toolCalls.length === 0) { - return { sequenceCompleted: false, toolResults: [] }; - } - - const { logger } = context; - - logger.verbose(`Executing ${toolCalls.length} tool calls`); - - // Check for respawn tool call - const respawnCall = toolCalls.find((call) => call.name === 'respawn'); - if (respawnCall) { - return { - sequenceCompleted: false, - toolResults: [ - { - type: 'tool_result', - tool_use_id: respawnCall.id, - content: 'Respawn initiated', - }, - ], - respawn: { - context: respawnCall.input.respawnContext, - }, - }; - } - - const results = await Promise.all( - toolCalls.map(async (call) => { - let toolResult = ''; - try { - toolResult = await executeToolCall(call, tools, { - ...context, - tokenTracker: new TokenTracker(call.name, context.tokenTracker), - }); - } catch (error: any) { - toolResult = `Error: Exception thrown during tool execution. Type: ${error.constructor.name}, Message: ${error.message}`; - } - return { - type: 'tool_result' as const, - tool_use_id: call.id, - content: toolResult, - isComplete: call.name === 'sequenceComplete', - }; - }), - ); - - const toolResults = results.map(({ type, tool_use_id, content }) => ({ - type, - tool_use_id, - content, - })); - - const sequenceCompleted = results.some((r) => r.isComplete); - const completionResult = results.find((r) => r.isComplete)?.content; - - messages.push({ - role: 'user', - content: toolResults, - }); - - if (sequenceCompleted) { - logger.verbose('Sequence completed', { completionResult }); - } - - return { sequenceCompleted, completionResult, toolResults }; -} - -// a function that takes a list of messages and returns a list of messages but with the last message having a cache_control of ephemeral -function addCacheControlToTools(messages: T[]): T[] { - return messages.map((m, i) => ({ - ...m, - ...(i === messages.length - 1 - ? { cache_control: { type: 'ephemeral' } } - : {}), - })); -} - -function addCacheControlToContentBlocks( - content: ContentBlockParam[], -): ContentBlockParam[] { - return content.map((c, i) => { - if (i === content.length - 1) { - if ( - c.type === 'text' || - c.type === 'document' || - c.type === 'image' || - c.type === 'tool_use' || - c.type === 'tool_result' || - c.type === 'thinking' || - c.type === 'redacted_thinking' - ) { - return { ...c, cache_control: { type: 'ephemeral' } }; - } - } - return c; - }); -} -function addCacheControlToMessages( - messages: Anthropic.Messages.MessageParam[], -): Anthropic.Messages.MessageParam[] { - return messages.map((m, i) => { - if (typeof m.content === 'string') { - return { - ...m, - content: [ - { - type: 'text', - text: m.content, - cache_control: { type: 'ephemeral' }, - }, - ] as ContentBlockParam[], - }; - } - return { - ...m, - content: - i >= messages.length - 2 - ? addCacheControlToContentBlocks(m.content) - : m.content, - }; - }); -} - -export const toolAgent = async ( - initialPrompt: string, - tools: Tool[], - config = CONFIG, - context: ToolContext, -): Promise => { - const { logger, tokenTracker } = context; - - logger.verbose('Starting agent execution'); - logger.verbose('Initial prompt:', initialPrompt); - - let interactions = 0; - - const apiKey = process.env.ANTHROPIC_API_KEY; - if (!apiKey) throw new Error(getAnthropicApiKeyError()); - - const client = new Anthropic({ apiKey }); - const messages: Message[] = [ - { - role: 'user', - content: [{ type: 'text', text: initialPrompt }], - }, - ]; - - logger.debug('User message:', initialPrompt); - - // Get the system prompt once at the start - const systemPrompt = config.getSystemPrompt(); - - for (let i = 0; i < config.maxIterations; i++) { - logger.verbose( - `Requesting completion ${i + 1} with ${messages.length} messages with ${ - JSON.stringify(messages).length - } bytes`, - ); - - interactions++; - - // Create request parameters - const requestParams: Anthropic.MessageCreateParams = { - model: config.model, - max_tokens: config.maxTokens, - temperature: config.temperature, - messages: addCacheControlToMessages(messages), - system: [ - { - type: 'text', - text: systemPrompt, - cache_control: { type: 'ephemeral' }, - }, - ], - tools: addCacheControlToTools( - tools.map((t) => ({ - name: t.name, - description: t.description, - input_schema: t.parameters as Anthropic.Tool.InputSchema, - })), - ), - tool_choice: { type: 'auto' }, - }; - - const response = await client.messages.create(requestParams); - - if (!response.content.length) { - // Instead of treating empty response as completion, remind the agent - logger.verbose('Received empty response from agent, sending reminder'); - messages.push({ - role: 'user', - content: [ - { - type: 'text', - text: 'I notice you sent an empty response. If you are done with your tasks, please call the sequenceComplete tool with your results. If you are waiting for other tools to complete, you can use the sleep tool to wait before checking again.', - }, - ], - }); - continue; - } - - // Track both regular and cached token usage - const tokenUsagePerMessage = TokenUsage.fromMessage(response); - tokenTracker.tokenUsage.add(tokenUsagePerMessage); - - const { content, toolCalls } = processResponse(response); - messages.push({ - role: 'assistant', - content, - }); - - // Log the assistant's message - const assistantMessage = content - .filter((c) => c.type === 'text') - .map((c) => c.text) - .join('\\n'); - if (assistantMessage) { - logger.info(assistantMessage); - } - - logger.log( - tokenTracker.logLevel, - chalk.blue(`[Token Usage/Message] ${tokenUsagePerMessage.toString()}`), - ); - - const { sequenceCompleted, completionResult, respawn } = await executeTools( - toolCalls, - tools, - messages, - context, - ); - - if (respawn) { - logger.info('Respawning agent with new context'); - // Reset messages to just the new context - messages.length = 0; - messages.push({ - role: 'user', - content: [{ type: 'text', text: respawn.context }], - }); - continue; - } - - if (sequenceCompleted) { - const result = { - result: - completionResult ?? - 'Sequence explicitly completed with an empty result', - interactions, - }; - logger.log( - tokenTracker.logLevel, - chalk.blueBright(`[Token Usage/Agent] ${tokenTracker.toString()}`), - ); - return result; - } - } - - logger.warn('Maximum iterations reached'); - const result = { - result: 'Maximum sub-agent iterations reach without successful completion', - interactions, - }; - // Use the appropriate log level based on tokenUsage flag - logger.log( - tokenTracker.logLevel, - chalk.blueBright(`[Token Usage/Agent] ${tokenTracker.toString()}`), +/** + * @fileoverview + * This file has been refactored into a modular structure. + * Please import from the toolAgent directory instead. + */ + +import { toolAgent, ToolAgentResult } from './toolAgent/index.js'; +import { Tool, ToolContext } from './toolAgent/types.js'; + +// Re-export the main toolAgent function and types for backward compatibility +export { toolAgent, ToolAgentResult, Tool, ToolContext }; + +// Provide a deprecation warning when this file is imported directly +const deprecationWarning = () => { + console.warn( + 'Warning: Importing directly from toolAgent.ts is deprecated. ' + + 'Please import from the toolAgent directory instead.', ); - return result; }; + +// Call the deprecation warning when this module is loaded +deprecationWarning(); diff --git a/packages/agent/src/core/toolAgent/README.md b/packages/agent/src/core/toolAgent/README.md new file mode 100644 index 0000000..dbcc9a9 --- /dev/null +++ b/packages/agent/src/core/toolAgent/README.md @@ -0,0 +1,34 @@ +# Tool Agent Module + +This directory contains the refactored Tool Agent implementation, split into smaller, focused modules for improved maintainability and testability. + +## Module Structure + +- **index.ts**: Main entry point and orchestration of the tool agent functionality +- **config.ts**: Configuration-related code and default settings +- **messageUtils.ts**: Utilities for handling and formatting messages +- **toolExecutor.ts**: Logic for executing tool calls +- **tokenTracking.ts**: Enhanced utilities for token tracking +- **types.ts**: Additional type definitions specific to toolAgent (re-exports from core/types.ts) + +## Usage + +```typescript +import { toolAgent } from './toolAgent/index.js'; +import { Tool, ToolContext } from './toolAgent/types.js'; + +// Use the toolAgent function as before +const result = await toolAgent(prompt, tools, config, context); +``` + +## Benefits of This Structure + +- **Improved maintainability**: Smaller, focused files are easier to understand and modify +- **Better testability**: Isolated components can be tested independently +- **Clearer responsibilities**: Each module has a single purpose +- **Easier onboarding**: New developers can understand the system more quickly +- **Simpler future extensions**: Modular design makes it easier to extend functionality + +## Migration + +The original `toolAgent.ts` file now re-exports from this directory for backward compatibility, but it will display a deprecation warning. New code should import directly from the toolAgent directory. diff --git a/packages/agent/src/core/toolAgent/config.ts b/packages/agent/src/core/toolAgent/config.ts new file mode 100644 index 0000000..8eea29e --- /dev/null +++ b/packages/agent/src/core/toolAgent/config.ts @@ -0,0 +1,72 @@ +import { execSync } from 'child_process'; + +import { anthropic } from '@ai-sdk/anthropic'; + +/** + * Default configuration for the tool agent + */ +export const DEFAULT_CONFIG = { + maxIterations: 200, + model: anthropic('claude-3-7-sonnet-20250219'), + maxTokens: 4096, + temperature: 0.7, + getSystemPrompt: getDefaultSystemPrompt, +}; + +/** + * Gets the default system prompt with contextual information about the environment + */ +export function getDefaultSystemPrompt(): string { + // Gather context with error handling + const getCommandOutput = (command: string, label: string): string => { + try { + return execSync(command).toString().trim(); + } catch (error) { + return `[Error getting ${label}: ${(error as Error).message}]`; + } + }; + + const context = { + pwd: getCommandOutput('pwd', 'current directory'), + files: getCommandOutput('ls -la', 'file listing'), + system: getCommandOutput('uname -a', 'system information'), + datetime: new Date().toString(), + }; + + return [ + 'You are an AI agent that can use tools to accomplish tasks.', + '', + 'Current Context:', + `Directory: ${context.pwd}`, + 'Files:', + context.files, + `System: ${context.system}`, + `DateTime: ${context.datetime}`, + '', + 'You prefer to call tools in parallel when possible because it leads to faster execution and less resource usage.', + 'When done, call the sequenceComplete tool with your results to indicate that the sequence has completed.', + '', + 'For coding tasks:', + '0. Try to break large tasks into smaller sub-tasks that can be completed and verified sequentially.', + " - trying to make lots of changes in one go can make it really hard to identify when something doesn't work", + ' - use sub-agents for each sub-task, leaving the main agent in a supervisory role', + ' - when possible ensure the project compiles/builds and the tests pass after each sub-task', + ' - give the sub-agents the guidance and context necessary be successful', + '1. First understand the context by:', + ' - Reading README.md, CONTRIBUTING.md, and similar documentation', + ' - Checking project configuration files (e.g., package.json)', + ' - Understanding coding standards', + '2. Ensure changes:', + ' - Follow project conventions', + ' - Build successfully', + ' - Pass all tests', + '3. Update documentation as needed', + '4. Consider adding documentation if you encountered setup/understanding challenges', + '', + 'Feel free to use Google and Bing via the browser tools to search for information or for ideas when you get stuck.', + '', + 'When you run into issues or unexpected results, take a step back and read the project documentation and configuration files and look at other source files in the project for examples of what works.', + '', + 'Use sub-agents for parallel tasks, providing them with specific context they need rather than having them rediscover it.', + ].join('\n'); +} diff --git a/packages/agent/src/core/toolAgent/index.ts b/packages/agent/src/core/toolAgent/index.ts new file mode 100644 index 0000000..6076672 --- /dev/null +++ b/packages/agent/src/core/toolAgent/index.ts @@ -0,0 +1,157 @@ +import { CoreMessage, ToolSet, generateText, tool as makeTool } from 'ai'; + +import { getAnthropicApiKeyError } from '../../utils/errors.js'; + +import { DEFAULT_CONFIG } from './config.js'; +import { + addCacheControlToMessages, + createCacheControlMessageFromSystemPrompt, + createToolCallParts, + formatToolCalls, +} from './messageUtils.js'; +import { logTokenUsage } from './tokenTracking.js'; +import { executeTools } from './toolExecutor.js'; +import { Tool, ToolAgentResult, ToolContext } from './types.js'; + +/** + * Main tool agent function that orchestrates the conversation with the AI + * and handles tool execution + */ +export const toolAgent = async ( + initialPrompt: string, + tools: Tool[], + config = DEFAULT_CONFIG, + context: ToolContext, +): Promise => { + const { logger, tokenTracker } = context; + + logger.verbose('Starting agent execution'); + logger.verbose('Initial prompt:', initialPrompt); + + let interactions = 0; + + const apiKey = process.env.ANTHROPIC_API_KEY; + if (!apiKey) throw new Error(getAnthropicApiKeyError()); + + const messages: CoreMessage[] = [ + { + role: 'user', + content: [{ type: 'text', text: initialPrompt }], + }, + ]; + + logger.debug('User message:', initialPrompt); + + // Get the system prompt once at the start + const systemPrompt = config.getSystemPrompt(); + + for (let i = 0; i < config.maxIterations; i++) { + logger.verbose( + `Requesting completion ${i + 1} with ${messages.length} messages with ${ + JSON.stringify(messages).length + } bytes`, + ); + + interactions++; + + const toolSet: ToolSet = {}; + tools.forEach((tool) => { + toolSet[tool.name] = makeTool({ + description: tool.description, + parameters: tool.parameters, + }); + }); + + // Apply cache control to messages for token caching + const messagesWithCacheControl = [ + createCacheControlMessageFromSystemPrompt(systemPrompt), + ...addCacheControlToMessages(messages), + ]; + + const generateTextProps = { + model: config.model, + temperature: config.temperature, + messages: messagesWithCacheControl, + tools: toolSet, + }; + const { text, toolCalls } = await generateText(generateTextProps); + + const localToolCalls = formatToolCalls(toolCalls); + + if (!text.length) { + // Instead of treating empty response as completion, remind the agent + logger.verbose('Received empty response from agent, sending reminder'); + messages.push({ + role: 'user', + content: [ + { + type: 'text', + text: 'I notice you sent an empty response. If you are done with your tasks, please call the sequenceComplete tool with your results. If you are waiting for other tools to complete, you can use the sleep tool to wait before checking again.', + }, + ], + }); + continue; + } + + messages.push({ + role: 'assistant', + content: [{ type: 'text', text: text }], + }); + + if (text) { + logger.info(text); + } + + if (toolCalls.length > 0) { + const toolCallParts = createToolCallParts(toolCalls); + + messages.push({ + role: 'assistant', + content: toolCallParts, + }); + } + + const { sequenceCompleted, completionResult, respawn } = await executeTools( + localToolCalls, + tools, + messages, + context, + ); + + if (respawn) { + logger.info('Respawning agent with new context'); + // Reset messages to just the new context + messages.length = 0; + messages.push({ + role: 'user', + content: [{ type: 'text', text: respawn.context }], + }); + continue; + } + + if (sequenceCompleted) { + const result: ToolAgentResult = { + result: completionResult ?? 'Sequence explicitly completed', + interactions, + }; + logTokenUsage(tokenTracker); + return result; + } + } + + logger.warn('Maximum iterations reached'); + const result = { + result: 'Maximum sub-agent iterations reach without successful completion', + interactions, + }; + + logTokenUsage(tokenTracker); + return result; +}; + +// Re-export everything from the module +export * from './config.js'; +export * from './messageUtils.js'; +export * from './toolExecutor.js'; +export * from './tokenTracking.js'; +export * from './types.js'; diff --git a/packages/agent/src/core/toolAgent/messageUtils.ts b/packages/agent/src/core/toolAgent/messageUtils.ts new file mode 100644 index 0000000..7c8b71a --- /dev/null +++ b/packages/agent/src/core/toolAgent/messageUtils.ts @@ -0,0 +1,75 @@ +import { CoreMessage, ToolCallPart } from 'ai'; + +/** + * Creates a cache control message from a system prompt + * This is used for token caching with the Vercel AI SDK + */ +export function createCacheControlMessageFromSystemPrompt( + systemPrompt: string, +): CoreMessage { + return { + role: 'system', + content: systemPrompt, + providerOptions: { + anthropic: { cacheControl: { type: 'ephemeral' } }, + }, + }; +} + +/** + * Adds cache control to the messages for token caching with the Vercel AI SDK + * This marks the last two messages as ephemeral which allows the conversation up to that + * point to be cached (with a ~5 minute window), reducing token usage when making multiple API calls + */ +export function addCacheControlToMessages( + messages: CoreMessage[], +): CoreMessage[] { + if (messages.length <= 1) return messages; + + // Create a deep copy of the messages array to avoid mutating the original + const result = JSON.parse(JSON.stringify(messages)) as CoreMessage[]; + + // Get the last two messages (if available) + const lastTwoMessageIndices = [messages.length - 1, messages.length - 2]; + + // Add providerOptions with anthropic cache control to the last two messages + lastTwoMessageIndices.forEach((index) => { + if (index >= 0) { + const message = result[index]; + if (message) { + // For the Vercel AI SDK, we need to add the providerOptions.anthropic property + // with cacheControl: 'ephemeral' to enable token caching + message.providerOptions = { + ...message.providerOptions, + anthropic: { cacheControl: { type: 'ephemeral' } }, + }; + } + } + }); + + return result; +} + +/** + * Formats tool calls from the AI into the ToolUseContent format + */ +export function formatToolCalls(toolCalls: any[]): any[] { + return toolCalls.map((call) => ({ + type: 'tool_use', + name: call.toolName, + id: call.toolCallId, + input: call.args, + })); +} + +/** + * Creates tool call parts for the assistant message + */ +export function createToolCallParts(toolCalls: any[]): Array { + return toolCalls.map((toolCall) => ({ + type: 'tool-call', + toolCallId: toolCall.toolCallId, + toolName: toolCall.toolName, + args: toolCall.args, + })); +} diff --git a/packages/agent/src/core/toolAgent/tokenTracking.ts b/packages/agent/src/core/toolAgent/tokenTracking.ts new file mode 100644 index 0000000..694ae20 --- /dev/null +++ b/packages/agent/src/core/toolAgent/tokenTracking.ts @@ -0,0 +1,36 @@ +import chalk from 'chalk'; + +import { TokenTracker, TokenUsage } from '../tokens.js'; + +/** + * Enhanced utilities for token tracking and reporting + */ +export function logTokenUsage(tokenTracker: TokenTracker): void { + console.log( + chalk.blueBright(`[Token Usage/Agent] ${tokenTracker.toString()}`), + ); +} + +/** + * Creates a child token tracker for a specific tool call + */ +export function createToolTokenTracker( + toolName: string, + parentTracker: TokenTracker, +): TokenTracker { + return new TokenTracker(toolName, parentTracker); +} + +/** + * Gets the total token usage for a token tracker and all its children + */ +export function getTotalTokenUsage(tokenTracker: TokenTracker): TokenUsage { + return tokenTracker.getTotalUsage(); +} + +/** + * Gets the total cost for a token tracker and all its children + */ +export function getTotalTokenCost(tokenTracker: TokenTracker): string { + return tokenTracker.getTotalCost(); +} diff --git a/packages/agent/src/core/toolAgent/toolExecutor.ts b/packages/agent/src/core/toolAgent/toolExecutor.ts new file mode 100644 index 0000000..56fa15c --- /dev/null +++ b/packages/agent/src/core/toolAgent/toolExecutor.ts @@ -0,0 +1,89 @@ +import { CoreMessage, CoreToolMessage, ToolResultPart } from 'ai'; + +import { executeToolCall } from '../executeToolCall.js'; +import { TokenTracker } from '../tokens.js'; + +import { Tool, ToolCallResult, ToolContext, ToolUseContent } from './types.js'; + +/** + * Executes a list of tool calls and returns the results + */ +export async function executeTools( + toolCalls: ToolUseContent[], + tools: Tool[], + messages: CoreMessage[], + context: ToolContext, +): Promise { + if (toolCalls.length === 0) { + return { sequenceCompleted: false, toolResults: [] }; + } + + const { logger } = context; + + logger.verbose(`Executing ${toolCalls.length} tool calls`); + + // Check for respawn tool call + const respawnCall = toolCalls.find((call) => call.name === 'respawn'); + if (respawnCall) { + return { + sequenceCompleted: false, + toolResults: [ + { + type: 'tool-result', + toolCallId: respawnCall.id, + toolName: respawnCall.name, + result: { success: true }, + } satisfies ToolResultPart, + ], + respawn: { + context: respawnCall.input.respawnContext, + }, + }; + } + + const toolResults: ToolResultPart[] = await Promise.all( + toolCalls.map(async (call) => { + let toolResult = ''; + try { + toolResult = await executeToolCall(call, tools, { + ...context, + tokenTracker: new TokenTracker(call.name, context.tokenTracker), + }); + } catch (error: any) { + toolResult = JSON.stringify({ + errorMessage: error.message, + errorType: error.constructor.name, + }); + } + + return { + type: 'tool-result', + toolCallId: call.id, + toolName: call.name, + result: JSON.parse(toolResult), + } satisfies ToolResultPart; + }), + ); + + const sequenceCompletedTool = toolResults.find( + (r) => r.toolName === 'sequenceComplete', + ); + const completionResult = sequenceCompletedTool + ? (sequenceCompletedTool.result as { result: string }).result + : undefined; + + messages.push({ + role: 'tool', + content: toolResults, + } satisfies CoreToolMessage); + + if (sequenceCompletedTool) { + logger.verbose('Sequence completed', { completionResult }); + } + + return { + sequenceCompleted: sequenceCompletedTool !== undefined, + completionResult, + toolResults, + }; +} diff --git a/packages/agent/src/core/toolAgent/types.ts b/packages/agent/src/core/toolAgent/types.ts new file mode 100644 index 0000000..a7c57b0 --- /dev/null +++ b/packages/agent/src/core/toolAgent/types.ts @@ -0,0 +1,20 @@ +// Re-export all types from the original types.ts file +export * from '../types.js'; + +// Only define new types specific to toolAgent here +export interface ToolAgentResult { + result: string; + interactions: number; +} + +export interface ToolCallResult { + sequenceCompleted: boolean; + completionResult?: string; + toolResults: any[]; + respawn?: { context: string }; +} + +export type ErrorResult = { + errorMessage: string; + errorType: string; +}; diff --git a/packages/agent/src/core/types.ts b/packages/agent/src/core/types.ts index 328f146..1bdab20 100644 --- a/packages/agent/src/core/types.ts +++ b/packages/agent/src/core/types.ts @@ -1,3 +1,4 @@ +import { z } from 'zod'; import { JsonSchema7Type } from 'zod-to-json-schema'; import { Logger } from '../utils/logger.js'; @@ -20,14 +21,18 @@ export type ToolContext = { export type Tool, TReturn = any> = { name: string; description: string; - parameters: JsonSchema7Type; - returns: JsonSchema7Type; + parameters: z.ZodType; + returns: z.ZodType; logPrefix?: string; logParameters?: (params: TParams, context: ToolContext) => void; logReturns?: (returns: TReturn, context: ToolContext) => void; execute: (params: TParams, context: ToolContext) => Promise; + + // Keep JsonSchema7Type for backward compatibility and Vercel AI SDK integration + parametersJsonSchema?: JsonSchema7Type; + returnsJsonSchema?: JsonSchema7Type; }; export type ToolCall = { diff --git a/packages/agent/src/tools/browser/BrowserManager.ts b/packages/agent/src/tools/browser/BrowserManager.ts index c507666..a136e8a 100644 --- a/packages/agent/src/tools/browser/BrowserManager.ts +++ b/packages/agent/src/tools/browser/BrowserManager.ts @@ -18,7 +18,6 @@ export class BrowserManager { async createSession(config?: BrowserConfig): Promise { try { const sessionConfig = { ...this.defaultConfig, ...config }; - //console.log('sessionConfig', sessionConfig); const browser = await chromium.launch({ headless: sessionConfig.headless, }); diff --git a/packages/agent/src/tools/browser/browseMessage.ts b/packages/agent/src/tools/browser/browseMessage.ts index 49547da..9b9e299 100644 --- a/packages/agent/src/tools/browser/browseMessage.ts +++ b/packages/agent/src/tools/browser/browseMessage.ts @@ -70,8 +70,10 @@ export const browseMessageTool: Tool = { name: 'browseMessage', logPrefix: '🏄', description: 'Performs actions in an active browser session', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returns: returnSchema, + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ( { instanceId, action }, diff --git a/packages/agent/src/tools/browser/browseStart.ts b/packages/agent/src/tools/browser/browseStart.ts index a4c5fa6..ebab30b 100644 --- a/packages/agent/src/tools/browser/browseStart.ts +++ b/packages/agent/src/tools/browser/browseStart.ts @@ -36,8 +36,10 @@ export const browseStartTool: Tool = { name: 'browseStart', logPrefix: '🏄', description: 'Starts a new browser session with optional initial URL', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returns: returnSchema, + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ( { url, timeout = 30000 }, diff --git a/packages/agent/src/tools/browser/filterPageContent.ts b/packages/agent/src/tools/browser/filterPageContent.ts index a1d3879..398cc1e 100644 --- a/packages/agent/src/tools/browser/filterPageContent.ts +++ b/packages/agent/src/tools/browser/filterPageContent.ts @@ -80,13 +80,6 @@ async function getSimpleProcessedDOM(page: Page): Promise { elementsToRemove.forEach((element) => element.remove()); - console.log( - 'removing ', - elementsToRemove.length, - ' elements out of a total ', - elements.length, - ); - return clone.outerHTML; }); diff --git a/packages/agent/src/tools/interaction/subAgent.ts b/packages/agent/src/tools/interaction/subAgent.ts index 18f8715..8d8de08 100644 --- a/packages/agent/src/tools/interaction/subAgent.ts +++ b/packages/agent/src/tools/interaction/subAgent.ts @@ -1,3 +1,4 @@ +import { anthropic } from '@ai-sdk/anthropic'; import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; @@ -35,11 +36,13 @@ const parameterSchema = z.object({ .optional(), }); -const returnSchema = z - .string() - .describe( - 'The response from the sub-agent including its reasoning and tool usage', - ); +const returnSchema = z.object({ + response: z + .string() + .describe( + 'The response from the sub-agent including its reasoning and tool usage', + ), +}); type Parameters = z.infer; type ReturnType = z.infer; @@ -47,7 +50,7 @@ type ReturnType = z.infer; // Sub-agent specific configuration const subAgentConfig = { maxIterations: 50, - model: process.env.AGENT_MODEL || 'claude-3-opus-20240229', + model: anthropic('claude-3-7-sonnet-20250219'), maxTokens: 4096, temperature: 0.7, getSystemPrompt: () => { @@ -66,8 +69,10 @@ export const subAgentTool: Tool = { description: 'Creates a sub-agent that has access to all tools to solve a specific task', logPrefix: '🤖', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returns: returnSchema, + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async (params, context) => { // Validate parameters const { description, goal, projectContext, fileContext } = @@ -106,7 +111,7 @@ export const subAgentTool: Tool = { workingDirectory: fileContext?.workingDirectory ?? context.workingDirectory, }); - return result.result; // Return the result string directly + return { response: result.result }; }, logParameters: (input, { logger }) => { logger.info(`Delegating task "${input.description}"`); diff --git a/packages/agent/src/tools/interaction/userPrompt.ts b/packages/agent/src/tools/interaction/userPrompt.ts index ebeaa14..638085e 100644 --- a/packages/agent/src/tools/interaction/userPrompt.ts +++ b/packages/agent/src/tools/interaction/userPrompt.ts @@ -8,7 +8,9 @@ const parameterSchema = z.object({ prompt: z.string().describe('The prompt message to display to the user'), }); -const returnSchema = z.string().describe("The user's response"); +const returnSchema = z.object({ + userText: z.string().describe("The user's response"), +}); type Parameters = z.infer; type ReturnType = z.infer; @@ -17,8 +19,10 @@ export const userPromptTool: Tool = { name: 'userPrompt', description: 'Prompts the user for input and returns their response', logPrefix: '🗣️', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returns: returnSchema, + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ({ prompt }, { logger }) => { logger.verbose(`Prompting user with: ${prompt}`); @@ -26,7 +30,7 @@ export const userPromptTool: Tool = { logger.verbose(`Received user response: ${response}`); - return response; + return { userText: response }; }, logParameters: () => {}, logReturns: () => {}, diff --git a/packages/agent/src/tools/io/fetch.ts b/packages/agent/src/tools/io/fetch.ts index b98b9dc..5982b01 100644 --- a/packages/agent/src/tools/io/fetch.ts +++ b/packages/agent/src/tools/io/fetch.ts @@ -38,8 +38,10 @@ export const fetchTool: Tool = { description: 'Executes HTTP requests using native Node.js fetch API, for using APIs, not for browsing the web.', logPrefix: '🌐', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + returns: returnSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ( { method, url, params, body, headers }: Parameters, { logger }, diff --git a/packages/agent/src/tools/io/textEditor.ts b/packages/agent/src/tools/io/textEditor.ts index 5d58ced..c0a1d89 100644 --- a/packages/agent/src/tools/io/textEditor.ts +++ b/packages/agent/src/tools/io/textEditor.ts @@ -1,7 +1,7 @@ -import * as fs from 'fs/promises'; +import { execSync } from 'child_process'; import * as fsSync from 'fs'; +import * as fs from 'fs/promises'; import * as path from 'path'; -import { execSync } from 'child_process'; import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; @@ -21,7 +21,9 @@ const parameterSchema = z.object({ ), path: z .string() - .describe('Absolute path to file or directory, e.g. `/repo/file.py` or `/repo`.'), + .describe( + 'Absolute path to file or directory, e.g. `/repo/file.py` or `/repo`.', + ), file_text: z .string() .optional() @@ -72,10 +74,20 @@ export const textEditorTool: Tool = { description: 'View, create, and edit files with persistent state across command calls', logPrefix: '📝', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + returns: returnSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ( - { command, path: filePath, file_text, insert_line, new_str, old_str, view_range }, + { + command, + path: filePath, + file_text, + insert_line, + new_str, + old_str, + view_range, + }, context, ) => { const normalizedPath = path.normalize(filePath); @@ -126,13 +138,12 @@ export const textEditorTool: Tool = { const [start, end] = view_range; const startLine = Math.max(1, start || 1) - 1; // Convert to 0-indexed const endLine = end === -1 ? lines.length : end; - displayContent = lines - .slice(startLine, endLine) - .join('\n'); + displayContent = lines.slice(startLine, endLine).join('\n'); } // Add line numbers - const startLineNum = view_range && view_range.length === 2 ? view_range[0] : 1; + const startLineNum = + view_range && view_range.length === 2 ? view_range[0] : 1; const numberedContent = displayContent .split('\n') .map((line, i) => `${(startLineNum || 1) + i}: ${line}`) @@ -140,7 +151,10 @@ export const textEditorTool: Tool = { // Truncate if too large if (numberedContent.length > OUTPUT_LIMIT) { - const truncatedContent = numberedContent.substring(0, OUTPUT_LIMIT); + const truncatedContent = numberedContent.substring( + 0, + OUTPUT_LIMIT, + ); return { success: true, message: `File content (truncated):`, diff --git a/packages/agent/src/tools/system/respawn.ts b/packages/agent/src/tools/system/respawn.ts index 6b0030e..1640764 100644 --- a/packages/agent/src/tools/system/respawn.ts +++ b/packages/agent/src/tools/system/respawn.ts @@ -1,29 +1,31 @@ +import { z } from 'zod'; +import { zodToJsonSchema } from 'zod-to-json-schema'; + import { Tool, ToolContext } from '../../core/types.js'; export interface RespawnInput { respawnContext: string; } +const parameterSchema = z.object({ + respawnContext: z.string().describe('The context to keep after respawning'), +}); + +const returnSchema = z.object({ + result: z + .string() + .describe('A message indicating that the respawn has been initiated'), +}); + export const respawnTool: Tool = { name: 'respawn', description: 'Resets the agent context to just the system prompt and provided context', logPrefix: '🔄', - parameters: { - type: 'object', - properties: { - respawnContext: { - type: 'string', - description: 'The context to keep after respawning', - }, - }, - required: ['respawnContext'], - additionalProperties: false, - }, - returns: { - type: 'string', - description: 'A message indicating that the respawn has been initiated', - }, + parameters: parameterSchema, + returns: returnSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: ( _params: Record, _context: ToolContext, diff --git a/packages/agent/src/tools/system/sequenceComplete.ts b/packages/agent/src/tools/system/sequenceComplete.ts index 037ef5b..cb3bf1f 100644 --- a/packages/agent/src/tools/system/sequenceComplete.ts +++ b/packages/agent/src/tools/system/sequenceComplete.ts @@ -7,9 +7,11 @@ const parameterSchema = z.object({ result: z.string().describe('The final result to return from the tool agent'), }); -const returnSchema = z - .string() - .describe('This is returned to the caller of the tool agent.'); +const returnSchema = z.object({ + result: z + .string() + .describe('This is returned to the caller of the tool agent.'), +}); type Parameters = z.infer; type ReturnType = z.infer; @@ -18,9 +20,11 @@ export const sequenceCompleteTool: Tool = { name: 'sequenceComplete', description: 'Completes the tool use sequence and returns the final result', logPrefix: '✅', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), - execute: ({ result }) => Promise.resolve(result), + parameters: parameterSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returns: returnSchema, + returnsJsonSchema: zodToJsonSchema(returnSchema), + execute: ({ result }) => Promise.resolve({ result }), logParameters: () => {}, logReturns: (output, { logger }) => { logger.info(`Completed: ${output}`); diff --git a/packages/agent/src/tools/system/shellExecute.ts b/packages/agent/src/tools/system/shellExecute.ts index d042734..4fbe278 100644 --- a/packages/agent/src/tools/system/shellExecute.ts +++ b/packages/agent/src/tools/system/shellExecute.ts @@ -48,8 +48,10 @@ export const shellExecuteTool: Tool = { logPrefix: '💻', description: 'Executes a bash shell command and returns its output, can do amazing things if you are a shell scripting wizard', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + returns: returnSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ( { command, timeout = 30000 }, diff --git a/packages/agent/src/tools/system/shellMessage.ts b/packages/agent/src/tools/system/shellMessage.ts index c774500..d25e288 100644 --- a/packages/agent/src/tools/system/shellMessage.ts +++ b/packages/agent/src/tools/system/shellMessage.ts @@ -77,8 +77,10 @@ export const shellMessageTool: Tool = { description: 'Interacts with a running shell process, sending input and receiving output', logPrefix: '💻', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returns: returnSchema, + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ( { instanceId, stdin, signal }, diff --git a/packages/agent/src/tools/system/shellStart.ts b/packages/agent/src/tools/system/shellStart.ts index 810e7f4..60508ca 100644 --- a/packages/agent/src/tools/system/shellStart.ts +++ b/packages/agent/src/tools/system/shellStart.ts @@ -72,8 +72,10 @@ export const shellStartTool: Tool = { description: 'Starts a shell command with fast sync mode (default 100ms timeout) that falls back to async mode for longer-running commands', logPrefix: '💻', - parameters: zodToJsonSchema(parameterSchema), - returns: zodToJsonSchema(returnSchema), + parameters: parameterSchema, + returns: returnSchema, + parametersJsonSchema: zodToJsonSchema(parameterSchema), + returnsJsonSchema: zodToJsonSchema(returnSchema), execute: async ( { command, timeout = DEFAULT_TIMEOUT }, diff --git a/packages/agent/src/tools/system/sleep.ts b/packages/agent/src/tools/system/sleep.ts index 5ff84f2..fc28062 100644 --- a/packages/agent/src/tools/system/sleep.ts +++ b/packages/agent/src/tools/system/sleep.ts @@ -23,8 +23,10 @@ export const sleepTool: Tool = { description: 'Pauses execution for the specified number of seconds, useful when waiting for async tools to make progress before checking on them', logPrefix: '💤', - parameters: zodToJsonSchema(parametersSchema), - returns: zodToJsonSchema(returnsSchema), + parameters: parametersSchema, + returns: returnsSchema, + parametersJsonSchema: zodToJsonSchema(parametersSchema), + returnsJsonSchema: zodToJsonSchema(returnsSchema), async execute(params) { const { seconds } = parametersSchema.parse(params); diff --git a/packages/cli/src/commands/$default.ts b/packages/cli/src/commands/$default.ts index 01959ac..8b06b2d 100644 --- a/packages/cli/src/commands/$default.ts +++ b/packages/cli/src/commands/$default.ts @@ -10,6 +10,7 @@ import { userPrompt, LogLevel, subAgentTool, + errorToString, } from 'mycoder-agent'; import { TokenTracker } from 'mycoder-agent/dist/core/tokens.js'; @@ -148,7 +149,11 @@ export const command: CommandModule = { : JSON.stringify(result.result, null, 2); logger.info('\n=== Result ===\n', output); } catch (error) { - logger.error('An error occurred:', error); + logger.error( + 'An error occurred:', + errorToString(error), + error instanceof Error ? error.stack : '', + ); // Capture the error with Sentry captureException(error); } diff --git a/packages/cli/src/commands/tools.ts b/packages/cli/src/commands/tools.ts index 7b1e580..5656a0e 100644 --- a/packages/cli/src/commands/tools.ts +++ b/packages/cli/src/commands/tools.ts @@ -43,19 +43,22 @@ export const command: CommandModule = { try { const tools = getTools(); - console.log('Available Tools:\\n'); + console.log('Available Tools:\n'); for (const tool of tools) { // Tool name and description console.log(`${tool.name}`); console.log('-'.repeat(tool.name.length)); - console.log(`Description: ${tool.description}\\n`); + console.log(`Description: ${tool.description}\n`); // Parameters section console.log('Parameters:'); + // Use parametersJsonSchema if available, otherwise convert from ZodSchema + const parametersSchema = + (tool as any).parametersJsonSchema || tool.parameters; console.log( formatSchema( - tool.parameters as { + parametersSchema as { properties?: Record; required?: string[]; }, @@ -65,9 +68,11 @@ export const command: CommandModule = { // Returns section console.log('Returns:'); if (tool.returns) { + // Use returnsJsonSchema if available, otherwise convert from ZodSchema + const returnsSchema = (tool as any).returnsJsonSchema || tool.returns; console.log( formatSchema( - tool.returns as { + returnsSchema as { properties?: Record; required?: string[]; }, @@ -75,7 +80,7 @@ export const command: CommandModule = { ); } else { console.log(' Type: any'); - console.log(' Description: Tool execution result or error\\n'); + console.log(' Description: Tool execution result or error\n'); } console.log(); // Add spacing between tools diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 338de52..187b1bf 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,19 +1,17 @@ import { createRequire } from 'module'; -import { join } from 'path'; -import { fileURLToPath } from 'url'; import * as dotenv from 'dotenv'; import sourceMapSupport from 'source-map-support'; -import yargs from 'yargs'; +import yargs, { CommandModule } from 'yargs'; import { hideBin } from 'yargs/helpers'; -import { fileCommands } from 'yargs-file-commands'; -// Initialize Sentry as early as possible +import { command as defaultCommand } from './commands/$default.js'; +import { command as testSentryCommand } from './commands/test-sentry.js'; +import { command as toolsCommand } from './commands/tools.js'; +import { sharedOptions } from './options.js'; import { initSentry, captureException } from './sentry/index.js'; initSentry(); -import { sharedOptions } from './options.js'; - import type { PackageJson } from 'type-fest'; // Add global declaration for our patched toolAgent @@ -26,10 +24,6 @@ const main = async () => { const require = createRequire(import.meta.url); const packageInfo = require('../package.json') as PackageJson; - // Get the directory where commands are located - const __filename = fileURLToPath(import.meta.url); - const commandsDir = join(__filename, '..', 'commands'); - // Set up yargs with the new CLI interface await yargs(hideBin(process.argv)) .scriptName(packageInfo.name!) @@ -37,12 +31,11 @@ const main = async () => { .options(sharedOptions) .alias('h', 'help') .alias('V', 'version') - .command( - await fileCommands({ - commandDirs: [commandsDir], - logLevel: 'info', - }), - ) + .command([ + defaultCommand, + testSentryCommand, + toolsCommand, + ] as CommandModule[]) .strict() .showHelpOnFail(true) .help().argv; diff --git a/packages/cli/src/sentry/index.ts b/packages/cli/src/sentry/index.ts index 360344d..5f9a26a 100644 --- a/packages/cli/src/sentry/index.ts +++ b/packages/cli/src/sentry/index.ts @@ -1,6 +1,7 @@ -import * as Sentry from '@sentry/node'; import { createRequire } from 'module'; +import * as Sentry from '@sentry/node'; + /** * Initialize Sentry for error tracking * @param dsn Optional custom DSN to use instead of the default @@ -10,7 +11,8 @@ export function initSentry(dsn?: string) { let packageVersion = 'unknown'; try { const require = createRequire(import.meta.url); - packageVersion = process.env.npm_package_version || require('../../package.json').version; + packageVersion = + process.env.npm_package_version || require('../../package.json').version; } catch (error) { console.warn('Could not determine package version for Sentry:', error); } @@ -18,27 +20,26 @@ export function initSentry(dsn?: string) { // Initialize Sentry Sentry.init({ // Default DSN from Sentry.io integration instructions - dsn: dsn || 'https://2873d2518b60f645918b6a08ae5e69ae@o4508898407481344.ingest.us.sentry.io/4508898476687360', - + dsn: + dsn || + 'https://2873d2518b60f645918b6a08ae5e69ae@o4508898407481344.ingest.us.sentry.io/4508898476687360', + // No profiling integration as requested - + // Capture 100% of the transactions tracesSampleRate: 1.0, - + // Set environment based on NODE_ENV environment: process.env.NODE_ENV || 'development', - + // Add release version from package.json release: `mycoder@${packageVersion}`, - + // Don't capture errors in development mode unless explicitly enabled - enabled: process.env.NODE_ENV !== 'development' || process.env.ENABLE_SENTRY === 'true', + enabled: + process.env.NODE_ENV !== 'development' || + process.env.ENABLE_SENTRY === 'true', }); - - // Log confirmation that Sentry is initialized with version info - if (process.env.NODE_ENV !== 'test') { - console.log(`Sentry initialized for mycoder@${packageVersion}`); - } } /** @@ -67,20 +68,27 @@ export function testSentryErrorReporting() { let packageVersion = 'unknown'; try { const require = createRequire(import.meta.url); - packageVersion = process.env.npm_package_version || require('../../package.json').version; + packageVersion = + process.env.npm_package_version || + require('../../package.json').version; } catch (error) { - console.warn('Could not determine package version for test error:', error); + console.warn( + 'Could not determine package version for test error:', + error, + ); } - + // Throw a test error with version information - throw new Error(`Test error for Sentry.io integration from mycoder@${packageVersion}`); + throw new Error( + `Test error for Sentry.io integration from mycoder@${packageVersion}`, + ); } catch (error) { // Capture the error with Sentry Sentry.captureException(error); - + // Log a message about the test console.log('Test error sent to Sentry.io'); - + // Return the error for inspection return error; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2cb8424..63e6459 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,9 +60,12 @@ importers: packages/agent: dependencies: - '@anthropic-ai/sdk': - specifier: ^0.37 - version: 0.37.0 + '@ai-sdk/anthropic': + specifier: ^1.1.13 + version: 1.1.13(zod@3.24.2) + '@ai-sdk/openai': + specifier: ^1.2.0 + version: 1.2.0(zod@3.24.2) '@mozilla/readability': specifier: ^0.5.0 version: 0.5.0 @@ -72,6 +75,9 @@ importers: '@vitest/browser': specifier: ^3.0.5 version: 3.0.6(@types/node@18.19.76)(playwright@1.50.1)(typescript@5.7.3)(vite@6.1.1(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))(vitest@3.0.6) + ai: + specifier: ^4.1.50 + version: 4.1.50(react@19.0.0)(zod@3.24.2) chalk: specifier: ^5.4.1 version: 5.4.1 @@ -81,6 +87,9 @@ importers: jsdom: specifier: ^26.0.0 version: 26.0.0 + ollama-ai-provider: + specifier: ^1.2.0 + version: 1.2.0(zod@3.24.2) playwright: specifier: ^1.50.1 version: 1.50.1 @@ -173,8 +182,51 @@ importers: packages: - '@anthropic-ai/sdk@0.37.0': - resolution: {integrity: sha512-tHjX2YbkUBwEgg0JZU3EFSSAQPoK4qQR/NFYa8Vtzd5UAyXzZksCw2In69Rml4R/TyHPBfRYaLK35XiOe33pjw==} + '@ai-sdk/anthropic@1.1.13': + resolution: {integrity: sha512-dBivw7ggokys0c9UmbhxHW36S+EHMQEHk/hVcakGO3sMEe6Vi0dR575xDjXJqs8uZPAmbcZjNb1s89U8cA0Y+Q==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/openai@1.2.0': + resolution: {integrity: sha512-tzxH6OxKL5ffts4zJPdziQSJGGpSrQcJmuSrE92jCt7pJ4PAU5Dx4tjNNFIU8lSfwarLnywejZEt3Fz0uQZZOQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/provider-utils@2.1.10': + resolution: {integrity: sha512-4GZ8GHjOFxePFzkl3q42AU0DQOtTQ5w09vmaWUf/pKFXJPizlnzKSUkF0f+VkapIUfDugyMqPMT1ge8XQzVI7Q==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/provider@1.0.9': + resolution: {integrity: sha512-jie6ZJT2ZR0uVOVCDc9R2xCX5I/Dum/wEK28lx21PJx6ZnFAN9EzD2WsPhcDWfCgGx3OAZZ0GyM3CEobXpa9LA==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.1.20': + resolution: {integrity: sha512-4QOM9fR9SryaRraybckDjrhl1O6XejqELdKmrM5g9y9eLnWAfjwF+W1aN0knkSHzbbjMqN77sy9B9yL8EuJbDw==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.0.0 + peerDependenciesMeta: + react: + optional: true + zod: + optional: true + + '@ai-sdk/ui-utils@1.1.16': + resolution: {integrity: sha512-jfblR2yZVISmNK2zyNzJZFtkgX57WDAUQXcmn3XUBJyo8LFsADu+/vYMn5AOyBi9qJT0RBk11PEtIxIqvByw3Q==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true '@asamuzakjp/css-color@2.8.3': resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==} @@ -1086,6 +1138,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/diff-match-patch@1.0.36': + resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -1101,18 +1156,12 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} - '@types/node-fetch@2.6.12': - resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} - '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} '@types/node@18.19.76': resolution: {integrity: sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==} - '@types/node@20.17.19': - resolution: {integrity: sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==} - '@types/pg-pool@2.0.6': resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==} @@ -1231,10 +1280,6 @@ packages: '@vitest/utils@3.0.6': resolution: {integrity: sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==} - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -1254,9 +1299,17 @@ packages: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} - agentkeepalive@4.6.0: - resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} - engines: {node: '>= 8.0.0'} + ai@4.1.50: + resolution: {integrity: sha512-YBNeemrJKDrxoBQd3V9aaxhKm5q5YyRcF7PZE7W0NmLuvsdva/1aQNYTAsxs47gQFdvqfYmlFy4B0E+356OlPA==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.0.0 + peerDependenciesMeta: + react: + optional: true + zod: + optional: true ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1526,6 +1579,9 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -1754,13 +1810,13 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + eventsource-parser@3.0.0: + resolution: {integrity: sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==} + engines: {node: '>=18.0.0'} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -1834,17 +1890,10 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} - form-data-encoder@1.7.2: - resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} - form-data@4.0.2: resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} engines: {node: '>= 6'} - formdata-node@4.4.1: - resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} - engines: {node: '>= 12.20'} - forwarded-parse@2.1.2: resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} @@ -1996,9 +2045,6 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -2203,6 +2249,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2210,6 +2259,11 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + jsondiffpatch@0.6.0: + resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -2353,19 +2407,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2397,6 +2438,15 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + ollama-ai-provider@1.2.0: + resolution: {integrity: sha512-jTNFruwe3O/ruJeppI/quoOUxG7NA6blG3ZyQj3lei4+NnJo7bi3eIRWqlVpRlu/mbzbFXeJSBuYQWF6pzGKww==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} @@ -2464,6 +2514,9 @@ packages: parse5@7.2.1: resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + partial-json@0.1.7: + resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2604,6 +2657,10 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + engines: {node: '>=0.10.0'} + read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} @@ -2696,6 +2753,9 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2856,6 +2916,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + swr@2.3.2: + resolution: {integrity: sha512-RosxFpiabojs75IwQ316DGoDRmOqtiAj0tg8wCcbEu4CiLZBs/a9QNtHV7TUfDXmmlgqij/NqzKq/eLelyv9xA==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -2876,6 +2941,10 @@ packages: engines: {node: '>=10'} hasBin: true + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2925,9 +2994,6 @@ packages: resolution: {integrity: sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA==} engines: {node: '>=16'} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@5.0.0: resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} engines: {node: '>=18'} @@ -2996,9 +3062,6 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -3013,6 +3076,11 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-sync-external-store@1.4.0: + resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true @@ -3094,13 +3162,6 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - web-streams-polyfill@4.0.0-beta.3: - resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} - engines: {node: '>= 14'} - - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -3117,9 +3178,6 @@ packages: resolution: {integrity: sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==} engines: {node: '>=18'} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -3230,17 +3288,48 @@ packages: snapshots: - '@anthropic-ai/sdk@0.37.0': + '@ai-sdk/anthropic@1.1.13(zod@3.24.2)': dependencies: - '@types/node': 18.19.76 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding + '@ai-sdk/provider': 1.0.9 + '@ai-sdk/provider-utils': 2.1.10(zod@3.24.2) + zod: 3.24.2 + + '@ai-sdk/openai@1.2.0(zod@3.24.2)': + dependencies: + '@ai-sdk/provider': 1.0.9 + '@ai-sdk/provider-utils': 2.1.10(zod@3.24.2) + zod: 3.24.2 + + '@ai-sdk/provider-utils@2.1.10(zod@3.24.2)': + dependencies: + '@ai-sdk/provider': 1.0.9 + eventsource-parser: 3.0.0 + nanoid: 3.3.8 + secure-json-parse: 2.7.0 + optionalDependencies: + zod: 3.24.2 + + '@ai-sdk/provider@1.0.9': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@1.1.20(react@19.0.0)(zod@3.24.2)': + dependencies: + '@ai-sdk/provider-utils': 2.1.10(zod@3.24.2) + '@ai-sdk/ui-utils': 1.1.16(zod@3.24.2) + swr: 2.3.2(react@19.0.0) + throttleit: 2.1.0 + optionalDependencies: + react: 19.0.0 + zod: 3.24.2 + + '@ai-sdk/ui-utils@1.1.16(zod@3.24.2)': + dependencies: + '@ai-sdk/provider': 1.0.9 + '@ai-sdk/provider-utils': 2.1.10(zod@3.24.2) + zod-to-json-schema: 3.24.3(zod@3.24.2) + optionalDependencies: + zod: 3.24.2 '@asamuzakjp/css-color@2.8.3': dependencies: @@ -4161,6 +4250,8 @@ snapshots: '@types/ms': 2.1.0 optional: true + '@types/diff-match-patch@1.0.36': {} + '@types/estree@1.0.6': {} '@types/json-schema@7.0.15': {} @@ -4174,21 +4265,12 @@ snapshots: dependencies: '@types/node': 18.19.76 - '@types/node-fetch@2.6.12': - dependencies: - '@types/node': 20.17.19 - form-data: 4.0.2 - '@types/node@12.20.55': {} '@types/node@18.19.76': dependencies: undici-types: 5.26.5 - '@types/node@20.17.19': - dependencies: - undici-types: 6.19.8 - '@types/pg-pool@2.0.6': dependencies: '@types/pg': 8.6.1 @@ -4356,10 +4438,6 @@ snapshots: loupe: 3.1.3 tinyrainbow: 2.0.0 - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - acorn-import-attributes@1.9.5(acorn@8.14.0): dependencies: acorn: 8.14.0 @@ -4372,9 +4450,17 @@ snapshots: agent-base@7.1.3: {} - agentkeepalive@4.6.0: + ai@4.1.50(react@19.0.0)(zod@3.24.2): dependencies: - humanize-ms: 1.2.1 + '@ai-sdk/provider': 1.0.9 + '@ai-sdk/provider-utils': 2.1.10(zod@3.24.2) + '@ai-sdk/react': 1.1.20(react@19.0.0)(zod@3.24.2) + '@ai-sdk/ui-utils': 1.1.16(zod@3.24.2) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + optionalDependencies: + react: 19.0.0 + zod: 3.24.2 ajv@6.12.6: dependencies: @@ -4643,6 +4729,8 @@ snapshots: detect-indent@6.1.0: {} + diff-match-patch@1.0.5: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -4986,10 +5074,10 @@ snapshots: esutils@2.0.3: {} - event-target-shim@5.0.1: {} - eventemitter3@5.0.1: {} + eventsource-parser@3.0.0: {} + execa@8.0.1: dependencies: cross-spawn: 7.0.6 @@ -5070,8 +5158,6 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data-encoder@1.7.2: {} - form-data@4.0.2: dependencies: asynckit: 0.4.0 @@ -5079,11 +5165,6 @@ snapshots: es-set-tostringtag: 2.1.0 mime-types: 2.1.35 - formdata-node@4.4.1: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 4.0.0-beta.3 - forwarded-parse@2.1.2: {} fs-extra@7.0.1: @@ -5247,10 +5328,6 @@ snapshots: human-signals@5.0.0: {} - humanize-ms@1.2.1: - dependencies: - ms: 2.1.3 - husky@9.1.7: {} iconv-lite@0.4.24: @@ -5472,12 +5549,20 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: dependencies: minimist: 1.2.8 + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.4.1 + diff-match-patch: 1.0.5 + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -5625,12 +5710,6 @@ snapshots: natural-compare@1.4.0: {} - node-domexception@1.0.0: {} - - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - npm-run-path@5.3.0: dependencies: path-key: 4.0.0 @@ -5670,6 +5749,14 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + ollama-ai-provider@1.2.0(zod@3.24.2): + dependencies: + '@ai-sdk/provider': 1.0.9 + '@ai-sdk/provider-utils': 2.1.10(zod@3.24.2) + partial-json: 0.1.7 + optionalDependencies: + zod: 3.24.2 + onetime@6.0.0: dependencies: mimic-fn: 4.0.0 @@ -5735,6 +5822,8 @@ snapshots: dependencies: entities: 4.5.0 + partial-json@0.1.7: {} + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -5837,6 +5926,8 @@ snapshots: react-is@17.0.2: {} + react@19.0.0: {} + read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 @@ -5964,6 +6055,8 @@ snapshots: dependencies: xmlchars: 2.2.0 + secure-json-parse@2.7.0: {} + semver@6.3.1: {} semver@7.7.1: {} @@ -6137,6 +6230,12 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + swr@2.3.2(react@19.0.0): + dependencies: + dequal: 2.0.3 + react: 19.0.0 + use-sync-external-store: 1.4.0(react@19.0.0) + symbol-tree@3.2.4: {} synckit@0.9.2: @@ -6156,6 +6255,8 @@ snapshots: source-map-support: 0.5.21 optional: true + throttleit@2.1.0: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -6198,8 +6299,6 @@ snapshots: dependencies: tldts: 6.1.79 - tr46@0.0.3: {} - tr46@5.0.0: dependencies: punycode: 2.3.1 @@ -6287,8 +6386,6 @@ snapshots: undici-types@5.26.5: {} - undici-types@6.19.8: {} - universalify@0.1.2: {} universalify@0.2.0: {} @@ -6302,6 +6399,10 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + use-sync-external-store@1.4.0(react@19.0.0): + dependencies: + react: 19.0.0 + uuid@11.1.0: {} vite-node@3.0.6(@types/node@18.19.76)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0): @@ -6383,10 +6484,6 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - web-streams-polyfill@4.0.0-beta.3: {} - - webidl-conversions@3.0.1: {} - webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: @@ -6400,11 +6497,6 @@ snapshots: tr46: 5.0.0 webidl-conversions: 7.0.0 - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0