From 92bad8619c29930f1c8243027153ccabe0045895 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Mon, 10 Nov 2025 21:55:01 +0100 Subject: [PATCH] Removed agent-old --- packages/agent-old/README.md | 432 ------ packages/agent-old/package-lock.json | 1343 ----------------- packages/agent-old/package.json | 47 - packages/agent-old/src/agent.ts | 741 --------- packages/agent-old/src/args.ts | 204 --- packages/agent-old/src/cli.ts | 9 - packages/agent-old/src/index.ts | 15 - packages/agent-old/src/main.ts | 285 ---- .../src/renderers/console-renderer.ts | 176 --- .../agent-old/src/renderers/json-renderer.ts | 7 - .../agent-old/src/renderers/tui-renderer.ts | 422 ------ packages/agent-old/src/session-manager.ts | 187 --- packages/agent-old/src/tools/tools.ts | 264 ---- packages/agent-old/tsconfig.build.json | 9 - packages/pods/src/commands/prompt.ts | 3 +- 15 files changed, 1 insertion(+), 4143 deletions(-) delete mode 100644 packages/agent-old/README.md delete mode 100644 packages/agent-old/package-lock.json delete mode 100644 packages/agent-old/package.json delete mode 100644 packages/agent-old/src/agent.ts delete mode 100644 packages/agent-old/src/args.ts delete mode 100644 packages/agent-old/src/cli.ts delete mode 100644 packages/agent-old/src/index.ts delete mode 100644 packages/agent-old/src/main.ts delete mode 100644 packages/agent-old/src/renderers/console-renderer.ts delete mode 100644 packages/agent-old/src/renderers/json-renderer.ts delete mode 100644 packages/agent-old/src/renderers/tui-renderer.ts delete mode 100644 packages/agent-old/src/session-manager.ts delete mode 100644 packages/agent-old/src/tools/tools.ts delete mode 100644 packages/agent-old/tsconfig.build.json diff --git a/packages/agent-old/README.md b/packages/agent-old/README.md deleted file mode 100644 index 4b2f2d16..00000000 --- a/packages/agent-old/README.md +++ /dev/null @@ -1,432 +0,0 @@ -# pi-agent - -A general-purpose agent with tool calling and session persistence, modeled after Claude Code but extremely hackable and minimal. It comes with a built-in TUI (also modeled after Claude Code) for interactive use. - -Everything is designed to be easy: -- Writing custom UIs on top of it (via JSON mode in any language or the TypeScript API) -- Using it for inference steps in deterministic programs (via JSON mode in any language or the TypeScript API) -- Providing your own system prompts and tools -- Working with various LLM providers or self-hosted LLMs - -## Installation - -```bash -npm install -g @mariozechner/pi-agent -``` - -This installs the `pi-agent` command globally. - -## Quick Start - -By default, pi-agent uses OpenAI's API with model `gpt-5-mini` and authenticates using the `OPENAI_API_KEY` environment variable. Any OpenAI-compatible endpoint works, including Ollama, vLLM, OpenRouter, Groq, Anthropic, etc. - -```bash -# Single message -pi-agent "What is 2+2?" - -# Multiple messages processed sequentially -pi-agent "What is 2+2?" "What about 3+3?" - -# Interactive chat mode (no messages = interactive) -pi-agent - -# Continue most recently modified session in current directory -pi-agent --continue "Follow up question" - -# GPT-OSS via Groq -pi-agent --base-url https://api.groq.com/openai/v1 --api-key $GROQ_API_KEY --model openai/gpt-oss-120b - -# GLM 4.5 via OpenRouter -pi-agent --base-url https://openrouter.ai/api/v1 --api-key $OPENROUTER_API_KEY --model z-ai/glm-4.5 - -# Claude via Anthropic's OpenAI compatibility layer. See: https://docs.anthropic.com/en/api/openai-sdk -pi-agent --base-url https://api.anthropic.com/v1 --api-key $ANTHROPIC_API_KEY --model claude-opus-4-1-20250805 - -# Gemini via Google AI -pi-agent --base-url https://generativelanguage.googleapis.com/v1beta/openai/ --api-key $GEMINI_API_KEY --model gemini-2.5-flash -``` - -## Usage Modes - -### Single-Shot Mode -Process one or more messages and exit: -```bash -pi-agent "First question" "Second question" -``` - -### Interactive Mode -Start an interactive chat session: -```bash -pi-agent -``` -- Type messages and press Enter to send -- Type `exit` or `quit` to end session -- Press Escape to interrupt while processing -- Press CTRL+C to clear the text editor -- Press CTRL+C twice quickly to exit - -### JSON Mode -JSON mode enables programmatic integration by outputting events as JSONL (JSON Lines). - -**Single-shot mode:** Outputs a stream of JSON events for each message, then exits. -```bash -pi-agent --json "What is 2+2?" "And the meaning of life?" -# Outputs: {"type":"session_start","sessionId":"bb6f0acb-80cf-4729-9593-bcf804431a53","model":"gpt-5-mini","api":"completions","baseURL":"https://api.openai.com/v1","systemPrompt":"You are a helpful assistant."} {"type":"user_message","text":"What is 2+2?"} {"type":"assistant_start"} {"type":"token_usage","inputTokens":314,"outputTokens":16,"totalTokens":330,"cacheReadTokens":0,"cacheWriteTokens":0} {"type":"assistant_message","text":"2 + 2 = 4"} {"type":"user_message","text":"And the meaning of life?"} {"type":"assistant_start"} {"type":"token_usage","inputTokens":337,"outputTokens":331,"totalTokens":668,"cacheReadTokens":0,"cacheWriteTokens":0} {"type":"assistant_message","text":"Short answer (pop-culture): 42.\n\nMore useful answers:\n- Philosophical... -``` - -**Interactive mode:** Accepts JSON commands via stdin and outputs JSON events to stdout. -```bash -# Start interactive JSON mode -pi-agent --json -# Now send commands via stdin - -# Pipe one or more initial messages in -(echo '{"type": "message", "content": "What is 2+2?"}'; cat) | pi-agent --json -# Outputs: {"type":"session_start","sessionId":"bb64cfbe-dd52-4662-bd4a-0d921c332fd1","model":"gpt-5-mini","api":"completions","baseURL":"https://api.openai.com/v1","systemPrompt":"You are a helpful assistant."} {"type":"user_message","text":"What is 2+2?"} {"type":"assistant_start"} {"type":"token_usage","inputTokens":314,"outputTokens":16,"totalTokens":330,"cacheReadTokens":0,"cacheWriteTokens":0} {"type":"assistant_message","text":"2 + 2 = 4"} -``` - -Commands you can send via stdin in interactive JSON mode: -```json -{"type": "message", "content": "Your message here"} // Send a message to the agent -{"type": "interrupt"} // Interrupt current processing -``` - -## Configuration - -### Command Line Options -``` ---base-url API base URL (default: https://api.openai.com/v1) ---api-key API key (or set OPENAI_API_KEY env var) ---model Model name (default: gpt-4o-mini) ---api API type: "completions" or "responses" (default: completions) ---system-prompt System prompt (default: "You are a helpful assistant.") ---continue Continue previous session ---json JSON mode ---help, -h Show help message -``` - -## Session Persistence - -Sessions are automatically saved to `~/.pi/sessions/` and include: -- Complete conversation history -- Tool call results -- Token usage statistics - -Use `--continue` to resume the last session: -```bash -pi-agent "Start a story about a robot" -# ... later ... -pi-agent --continue "Continue the story" -``` - -## Tools - -The agent includes built-in tools for file system operations: -- **read_file** - Read file contents -- **list_directory** - List directory contents -- **bash** - Execute shell commands -- **glob** - Find files by pattern -- **ripgrep** - Search file contents - -These tools are automatically available when using the agent through the `pi` command for code navigation tasks. - -## JSON Mode Events - -When using `--json`, the agent outputs these event types: -- `session_start` - New session started with metadata -- `user_message` - User input -- `assistant_start` - Assistant begins responding -- `assistant_message` - Assistant's response -- `reasoning` - Reasoning/thinking (for models that support it) -- `tool_call` - Tool being called -- `tool_result` - Result from tool -- `token_usage` - Token usage statistics (includes `reasoningTokens` for models with reasoning) -- `error` - Error occurred -- `interrupted` - Processing was interrupted - -The complete TypeScript type definition for `AgentEvent` can be found in [`src/agent.ts`](src/agent.ts#L6). - -## Build an Interactive UI with JSON Mode -Build custom UIs in any language by spawning pi-agent in JSON mode and communicating via stdin/stdout. - -```javascript -import { spawn } from 'child_process'; -import { createInterface } from 'readline'; - -// Start the agent in JSON mode -const agent = spawn('pi-agent', ['--json']); - -// Create readline interface for parsing JSONL output from agent -const agentOutput = createInterface({input: agent.stdout, crlfDelay: Infinity}); - -// Create readline interface for user input -const userInput = createInterface({input: process.stdin, output: process.stdout}); - -// State tracking -let isProcessing = false, lastUsage, isExiting = false; - -// Handle each line of JSON output from agent -agentOutput.on('line', (line) => { - try { - const event = JSON.parse(line); - - // Handle all event types - switch (event.type) { - case 'session_start': - console.log(`Session started (${event.model}, ${event.api}, ${event.baseURL})`); - console.log('Press CTRL + C to exit'); - promptUser(); - break; - - case 'user_message': - // Already shown in prompt, skip - break; - - case 'assistant_start': - isProcessing = true; - console.log('\n[assistant]'); - break; - - case 'thinking': - console.log(`[thinking]\n${event.text}\n`); - break; - - case 'tool_call': - console.log(`[tool] ${event.name}(${event.args.substring(0, 50)})\n`); - break; - - case 'tool_result': - const lines = event.result.split('\n'); - const truncated = lines.length - 5 > 0 ? `\n. ... (${lines.length - 5} more lines truncated)` : ''; - console.log(`[tool result]\n${lines.slice(0, 5).join('\n')}${truncated}\n`); - break; - - case 'assistant_message': - console.log(event.text.trim()); - isProcessing = false; - promptUser(); - break; - - case 'token_usage': - lastUsage = event; - break; - - case 'error': - console.error('\n❌ Error:', event.message); - isProcessing = false; - promptUser(); - break; - - case 'interrupted': - console.log('\n⚠️ Interrupted by user'); - isProcessing = false; - promptUser(); - break; - } - } catch (e) { - console.error('Failed to parse JSON:', line, e); - } -}); - -// Send a message to the agent -function sendMessage(content) { - agent.stdin.write(`${JSON.stringify({type: 'message', content: content})}\n`); -} - -// Send interrupt signal -function interrupt() { - agent.stdin.write(`${JSON.stringify({type: 'interrupt'})}\n`); -} - -// Prompt for user input -function promptUser() { - if (isExiting) return; - - if (lastUsage) { - console.log(`\nin: ${lastUsage.inputTokens}, out: ${lastUsage.outputTokens}, cache read: ${lastUsage.cacheReadTokens}, cache write: ${lastUsage.cacheWriteTokens}`); - } - - userInput.question('\n[user]\n> ', (answer) => { - answer = answer.trim(); - if (answer) { - sendMessage(answer); - } else { - promptUser(); - } - }); -} - -// Handle Ctrl+C -process.on('SIGINT', () => { - if (isProcessing) { - interrupt(); - } else { - agent.kill(); - process.exit(0); - } -}); - -// Handle agent exit -agent.on('close', (code) => { - isExiting = true; - userInput.close(); - console.log(`\nAgent exited with code ${code}`); - process.exit(code); -}); - -// Handle errors -agent.on('error', (err) => { - console.error('Failed to start agent:', err); - process.exit(1); -}); - -// Start the conversation -console.log('Pi Agent Interactive Chat'); -``` - -### Usage Examples - -```bash -# OpenAI o1/o3 - see thinking content with Responses API -pi-agent --api responses --model o1-mini "Explain quantum computing" - -# Groq gpt-oss - reasoning with Chat Completions -pi-agent --base-url https://api.groq.com/openai/v1 --api-key $GROQ_API_KEY \ - --model openai/gpt-oss-120b "Complex math problem" - -# Gemini 2.5 - thinking content automatically configured -pi-agent --base-url https://generativelanguage.googleapis.com/v1beta/openai/ \ - --api-key $GEMINI_API_KEY --model gemini-2.5-flash "Think step by step" - -# OpenRouter - supports various reasoning models -pi-agent --base-url https://openrouter.ai/api/v1 --api-key $OPENROUTER_API_KEY \ - --model "qwen/qwen3-235b-a22b-thinking-2507" "Complex reasoning task" -``` - -### JSON Mode Events - -When reasoning is active, you'll see: -- `reasoning` events with thinking text (when available) -- `token_usage` events include `reasoningTokens` field -- Console/TUI show reasoning tokens with ⚡ symbol - -## Reasoning - -Pi-agent supports reasoning/thinking tokens for models that provide this capability: - -### Supported Providers - -| Provider | Model | API | Thinking Content | Notes | -|----------|-------|-----|------------------|-------| -| **OpenAI** | o1, o3 | Responses | ✅ Full | Thinking events + token counts | -| | o1, o3 | Chat Completions | ❌ | Token counts only | -| | gpt-5 | Both APIs | ❌ | Model limitation (empty summaries) | -| **Groq** | gpt-oss | Chat Completions | ✅ Full | Via `reasoning_format: "parsed"` | -| | gpt-oss | Responses | ❌ | API doesn't support reasoning.summary | -| **Gemini** | 2.5 models | Chat Completions | ✅ Full | Auto-configured via extra_body | -| **Anthropic** | Claude | OpenAI Compat | ❌ | Use native API for thinking | -| **OpenRouter** | Various | Both APIs | Varies | Depends on underlying model | - - -### Technical Details - -The agent automatically: -- Detects provider from base URL -- Tests model reasoning support on first use (cached) -- Adjusts request parameters per provider: - - OpenAI: `reasoning_effort` (minimal/low) - - Groq: `reasoning_format: "parsed"` - - Gemini: `extra_body.google.thinking_config` - - OpenRouter: `reasoning` object with `effort` field -- Parses provider-specific response formats: - - Gemini: Extracts from `` tags - - Groq: Uses `message.reasoning` field - - OpenRouter: Uses `message.reasoning` field - - OpenAI: Uses standard `reasoning` events - -## Architecture - -The agent is built with: -- **agent.ts** - Core Agent class and API functions -- **cli.ts** - CLI entry point, argument parsing, and JSON mode handler -- **args.ts** - Custom typed argument parser -- **session-manager.ts** - Session persistence -- **tools/** - Tool implementations -- **renderers/** - Output formatters (console, TUI, JSON) - -## Development - -### Running from Source - -```bash -# Run directly with npx tsx - no build needed -npx tsx src/cli.ts "What is 2+2?" - -# Interactive TUI mode -npx tsx src/cli.ts - -# JSON mode for programmatic use -echo '{"type":"message","content":"list files"}' | npx tsx src/cli.ts --json -``` - -### Testing - -The agent supports three testing modes: - -#### 1. Test UI/Renderers (non-interactive mode) -```bash -# Test console renderer output and metrics -npx tsx src/cli.ts "list files in /tmp" 2>&1 | tail -5 -# Verify: ↑609 ↓610 ⚒ 1 (tokens and tool count) - -# Test TUI renderer (with stdin) -echo "list files" | npx tsx src/cli.ts 2>&1 | grep "⚒" -``` - -#### 2. Test Model Behavior (JSON mode) -```bash -# Extract metrics for model comparison -echo '{"type":"message","content":"write fibonacci in Python"}' | \ - npx tsx src/cli.ts --json --model gpt-4o-mini 2>&1 | \ - jq -s '[.[] | select(.type=="token_usage")] | last' - -# Compare models: tokens used, tool calls made, quality -for model in "gpt-4o-mini" "gpt-4o"; do - echo "Testing $model:" - echo '{"type":"message","content":"fix syntax errors in: prnt(hello)"}' | \ - npx tsx src/cli.ts --json --model $model 2>&1 | \ - jq -r 'select(.type=="token_usage" or .type=="tool_call" or .type=="assistant_message")' -done -``` - -#### 3. LLM-as-Judge Testing -```bash -# Capture output from different models and evaluate with another LLM -TASK="write a Python function to check if a number is prime" - -# Get response from model A -RESPONSE_A=$(echo "{\"type\":\"message\",\"content\":\"$TASK\"}" | \ - npx tsx src/cli.ts --json --model gpt-4o-mini 2>&1 | \ - jq -r '.[] | select(.type=="assistant_message") | .text') - -# Judge the response -echo "{\"type\":\"message\",\"content\":\"Rate this code (1-10): $RESPONSE_A\"}" | \ - npx tsx src/cli.ts --json --model gpt-4o 2>&1 | \ - jq -r '.[] | select(.type=="assistant_message") | .text' -``` - -## Use as a Library - -```typescript -import { Agent, ConsoleRenderer } from '@mariozechner/pi-agent'; - -const agent = new Agent({ - apiKey: process.env.OPENAI_API_KEY, - baseURL: 'https://api.openai.com/v1', - model: 'gpt-5-mini', - api: 'completions', - systemPrompt: 'You are a helpful assistant.' -}, new ConsoleRenderer()); - -await agent.ask('What is 2+2?'); -``` \ No newline at end of file diff --git a/packages/agent-old/package-lock.json b/packages/agent-old/package-lock.json deleted file mode 100644 index a7c66b50..00000000 --- a/packages/agent-old/package-lock.json +++ /dev/null @@ -1,1343 +0,0 @@ -{ - "name": "@mariozechner/pi-agent", - "version": "0.5.48", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@mariozechner/pi-agent", - "version": "0.5.48", - "license": "MIT", - "dependencies": { - "@mariozechner/tui": "^0.1.1", - "@types/glob": "^8.1.0", - "chalk": "^5.3.0", - "glob": "^11.0.3", - "openai": "^5.12.2" - }, - "bin": { - "pi-agent": "dist/cli.js" - }, - "devDependencies": { - "@biomejs/biome": "^2.1.4", - "@types/node": "^22.10.5", - "tsx": "^4.20.3", - "typescript": "^5.9.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@biomejs/biome": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.4.tgz", - "integrity": "sha512-QWlrqyxsU0FCebuMnkvBIkxvPqH89afiJzjMl+z67ybutse590jgeaFdDurE9XYtzpjRGTI1tlUZPGWmbKsElA==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.1.4", - "@biomejs/cli-darwin-x64": "2.1.4", - "@biomejs/cli-linux-arm64": "2.1.4", - "@biomejs/cli-linux-arm64-musl": "2.1.4", - "@biomejs/cli-linux-x64": "2.1.4", - "@biomejs/cli-linux-x64-musl": "2.1.4", - "@biomejs/cli-win32-arm64": "2.1.4", - "@biomejs/cli-win32-x64": "2.1.4" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.4.tgz", - "integrity": "sha512-sCrNENE74I9MV090Wq/9Dg7EhPudx3+5OiSoQOkIe3DLPzFARuL1dOwCWhKCpA3I5RHmbrsbNSRfZwCabwd8Qg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.4.tgz", - "integrity": "sha512-gOEICJbTCy6iruBywBDcG4X5rHMbqCPs3clh3UQ+hRKlgvJTk4NHWQAyHOXvaLe+AxD1/TNX1jbZeffBJzcrOw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.4.tgz", - "integrity": "sha512-juhEkdkKR4nbUi5k/KRp1ocGPNWLgFRD4NrHZSveYrD6i98pyvuzmS9yFYgOZa5JhaVqo0HPnci0+YuzSwT2fw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.4.tgz", - "integrity": "sha512-nYr7H0CyAJPaLupFE2cH16KZmRC5Z9PEftiA2vWxk+CsFkPZQ6dBRdcC6RuS+zJlPc/JOd8xw3uCCt9Pv41WvQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.4.tgz", - "integrity": "sha512-Eoy9ycbhpJVYuR+LskV9s3uyaIkp89+qqgqhGQsWnp/I02Uqg2fXFblHJOpGZR8AxdB9ADy87oFVxn9MpFKUrw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.4.tgz", - "integrity": "sha512-lvwvb2SQQHctHUKvBKptR6PLFCM7JfRjpCCrDaTmvB7EeZ5/dQJPhTYBf36BE/B4CRWR2ZiBLRYhK7hhXBCZAg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.4.tgz", - "integrity": "sha512-3WRYte7orvyi6TRfIZkDN9Jzoogbv+gSvR+b9VOXUg1We1XrjBg6WljADeVEaKTvOcpVdH0a90TwyOQ6ue4fGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.4.tgz", - "integrity": "sha512-tBc+W7anBPSFXGAoQW+f/+svkpt8/uXfRwDzN1DvnatkRMt16KIYpEi/iw8u9GahJlFv98kgHcIrSsZHZTR0sw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@mariozechner/tui": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@mariozechner/tui/-/tui-0.1.1.tgz", - "integrity": "sha512-OQJB9j2d4FMqXij7utfC0rcrrFkNVR9QMpDmLT4RGasZzEUU2ritRDXe5WH4jEjRT6gTUe3LEetAWkTpOaxLZQ==", - "license": "MIT", - "dependencies": { - "@types/mime-types": "^2.1.4", - "chalk": "^5.4.1", - "marked": "^15.0.12", - "mime-types": "^3.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "license": "MIT", - "dependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, - "node_modules/@types/mime-types": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", - "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", - "license": "MIT" - }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.17.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz", - "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", - "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/marked": { - "version": "15.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", - "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/openai": { - "version": "5.12.2", - "resolved": "https://registry.npmjs.org/openai/-/openai-5.12.2.tgz", - "integrity": "sha512-xqzHHQch5Tws5PcKR2xsZGX9xtch+JQFz5zb14dGqlshmmDAFBFEWmeIpf7wVqWV+w7Emj7jRgkNJakyKE0tYQ==", - "license": "Apache-2.0", - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tsx": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", - "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - } - } -} diff --git a/packages/agent-old/package.json b/packages/agent-old/package.json deleted file mode 100644 index 3a4ce3fb..00000000 --- a/packages/agent-old/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@mariozechner/pi-agent-old", - "version": "0.5.48", - "description": "General-purpose agent with tool calling and session persistence", - "type": "module", - "bin": { - "pi-agent": "dist/cli.js" - }, - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "clean": "rm -rf dist", - "build": "tsc -p tsconfig.build.json && chmod +x dist/cli.js", - "check": "biome check --write .", - "prepublishOnly": "npm run clean && npm run build" - }, - "dependencies": { - "@mariozechner/pi-tui": "^0.5.44", - "@types/glob": "^8.1.0", - "chalk": "^5.5.0", - "glob": "^11.0.3", - "openai": "^5.12.2" - }, - "devDependencies": {}, - "keywords": [ - "agent", - "ai", - "llm", - "openai", - "claude", - "cli", - "tui" - ], - "author": "Mario Zechner", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/badlogic/pi-mono.git", - "directory": "packages/agent" - }, - "engines": { - "node": ">=20.0.0" - } -} diff --git a/packages/agent-old/src/agent.ts b/packages/agent-old/src/agent.ts deleted file mode 100644 index 6ceba4b2..00000000 --- a/packages/agent-old/src/agent.ts +++ /dev/null @@ -1,741 +0,0 @@ -import OpenAI from "openai"; -import type { ResponseFunctionToolCallOutputItem } from "openai/resources/responses/responses.mjs"; -import type { SessionManager } from "./session-manager.js"; -import { executeTool, toolsForChat, toolsForResponses } from "./tools/tools.js"; - -export type AgentEvent = - | { type: "session_start"; sessionId: string; model: string; api: string; baseURL: string; systemPrompt: string } - | { type: "assistant_start" } - | { type: "reasoning"; text: string } - | { type: "tool_call"; toolCallId: string; name: string; args: string } - | { type: "tool_result"; toolCallId: string; result: string; isError: boolean } - | { type: "assistant_message"; text: string } - | { type: "error"; message: string } - | { type: "user_message"; text: string } - | { type: "interrupted" } - | { - type: "token_usage"; - inputTokens: number; - outputTokens: number; - totalTokens: number; - cacheReadTokens: number; - cacheWriteTokens: number; - reasoningTokens: number; - }; - -export interface AgentEventReceiver { - on(event: AgentEvent): Promise; -} - -export interface AgentConfig { - apiKey: string; - baseURL: string; - model: string; - api: "completions" | "responses"; - systemPrompt: string; -} - -export interface ToolCall { - name: string; - arguments: string; - id: string; -} - -// Cache for model reasoning support detection per API type -const modelReasoningSupport = new Map(); - -// Provider detection based on base URL -function detectProvider(baseURL?: string): "openai" | "gemini" | "groq" | "anthropic" | "openrouter" | "other" { - if (!baseURL) return "openai"; - if (baseURL.includes("api.openai.com")) return "openai"; - if (baseURL.includes("generativelanguage.googleapis.com")) return "gemini"; - if (baseURL.includes("api.groq.com")) return "groq"; - if (baseURL.includes("api.anthropic.com")) return "anthropic"; - if (baseURL.includes("openrouter.ai")) return "openrouter"; - return "other"; -} - -// Parse provider-specific reasoning from message content -function parseReasoningFromMessage(message: any, baseURL?: string): { cleanContent: string; reasoningTexts: string[] } { - const provider = detectProvider(baseURL); - const reasoningTexts: string[] = []; - let cleanContent = message.content || ""; - - switch (provider) { - case "gemini": - // Gemini returns thinking in tags - if (cleanContent.includes("")) { - const thoughtMatches = cleanContent.matchAll(/([\s\S]*?)<\/thought>/g); - for (const match of thoughtMatches) { - reasoningTexts.push(match[1].trim()); - } - // Remove all thought tags from the response - cleanContent = cleanContent.replace(/[\s\S]*?<\/thought>/g, "").trim(); - } - break; - - case "groq": - // Groq returns reasoning in a separate field when reasoning_format is "parsed" - if (message.reasoning) { - reasoningTexts.push(message.reasoning); - } - break; - - case "openrouter": - // OpenRouter returns reasoning in message.reasoning field - if (message.reasoning) { - reasoningTexts.push(message.reasoning); - } - break; - - default: - // Other providers don't embed reasoning in message content - break; - } - - return { cleanContent, reasoningTexts }; -} - -// Adjust request options based on provider-specific requirements -function adjustRequestForProvider( - requestOptions: any, - api: "completions" | "responses", - baseURL?: string, - supportsReasoning?: boolean, -): any { - const provider = detectProvider(baseURL); - - // Handle provider-specific adjustments - switch (provider) { - case "gemini": - if (api === "completions" && supportsReasoning && requestOptions.reasoning_effort) { - // Gemini needs extra_body for thinking content - // Can't use both reasoning_effort and thinking_config - const budget = - requestOptions.reasoning_effort === "low" - ? 1024 - : requestOptions.reasoning_effort === "medium" - ? 8192 - : 24576; - - requestOptions.extra_body = { - google: { - thinking_config: { - thinking_budget: budget, - include_thoughts: true, - }, - }, - }; - // Remove reasoning_effort when using thinking_config - delete requestOptions.reasoning_effort; - } - break; - - case "groq": - if (api === "responses" && requestOptions.reasoning) { - // Groq responses API doesn't support reasoning.summary - delete requestOptions.reasoning.summary; - } else if (api === "completions" && supportsReasoning && requestOptions.reasoning_effort) { - // Groq Chat Completions uses reasoning_format instead of reasoning_effort alone - requestOptions.reasoning_format = "parsed"; - // Keep reasoning_effort for Groq - } - break; - - case "anthropic": - // Anthropic's OpenAI compatibility has its own quirks - // But thinking content isn't available via OpenAI compat layer - break; - - case "openrouter": - // OpenRouter uses a unified reasoning parameter format - if (api === "completions" && supportsReasoning && requestOptions.reasoning_effort) { - // Convert reasoning_effort to OpenRouter's reasoning format - requestOptions.reasoning = { - effort: - requestOptions.reasoning_effort === "low" - ? "low" - : requestOptions.reasoning_effort === "minimal" - ? "low" - : requestOptions.reasoning_effort === "medium" - ? "medium" - : "high", - }; - delete requestOptions.reasoning_effort; - } - break; - - default: - // OpenAI and others use standard format - break; - } - - return requestOptions; -} - -async function checkReasoningSupport( - client: OpenAI, - model: string, - api: "completions" | "responses", - baseURL?: string, - signal?: AbortSignal, -): Promise { - // Check if already aborted - if (signal?.aborted) { - throw new Error("Interrupted"); - } - - // Check cache first - const cacheKey = model; - const cached = modelReasoningSupport.get(cacheKey); - if (cached && cached[api] !== undefined) { - return cached[api]!; - } - - let supportsReasoning = false; - const provider = detectProvider(baseURL); - - if (api === "responses") { - // Try a minimal request with reasoning parameter for Responses API - try { - const testRequest: any = { - model, - input: "test", - max_output_tokens: 1024, - reasoning: { - effort: "low", // Use low instead of minimal to ensure we get summaries - }, - }; - await client.responses.create(testRequest, { signal }); - supportsReasoning = true; - } catch (error) { - supportsReasoning = false; - } - } else { - // For Chat Completions API, try with reasoning parameter - try { - const testRequest: any = { - model, - messages: [{ role: "user", content: "test" }], - max_completion_tokens: 1024, - }; - - // Add provider-specific reasoning parameters - if (provider === "gemini") { - // Gemini uses extra_body for thinking - testRequest.extra_body = { - google: { - thinking_config: { - thinking_budget: 100, // Minimum viable budget for test - include_thoughts: true, - }, - }, - }; - } else if (provider === "groq") { - // Groq uses both reasoning_format and reasoning_effort - testRequest.reasoning_format = "parsed"; - testRequest.reasoning_effort = "low"; - } else { - // Others use reasoning_effort - testRequest.reasoning_effort = "minimal"; - } - - await client.chat.completions.create(testRequest, { signal }); - supportsReasoning = true; - } catch (error) { - supportsReasoning = false; - } - } - - // Update cache - const existing = modelReasoningSupport.get(cacheKey) || {}; - existing[api] = supportsReasoning; - modelReasoningSupport.set(cacheKey, existing); - - return supportsReasoning; -} - -export async function callModelResponsesApi( - client: OpenAI, - model: string, - messages: any[], - signal?: AbortSignal, - eventReceiver?: AgentEventReceiver, - supportsReasoning?: boolean, - baseURL?: string, -): Promise { - let conversationDone = false; - - while (!conversationDone) { - // Check if we've been interrupted - if (signal?.aborted) { - throw new Error("Interrupted"); - } - - // Build request options - let requestOptions: any = { - model, - input: messages, - tools: toolsForResponses as any, - tool_choice: "auto", - parallel_tool_calls: true, - max_output_tokens: 2000, // TODO make configurable - ...(supportsReasoning && { - reasoning: { - effort: "minimal", // Use minimal effort for responses API - summary: "detailed", // Request detailed reasoning summaries - }, - }), - }; - - // Apply provider-specific adjustments - requestOptions = adjustRequestForProvider(requestOptions, "responses", baseURL, supportsReasoning); - - const response = await client.responses.create(requestOptions, { signal }); - - // Report token usage if available (responses API format) - if (response.usage) { - const usage = response.usage; - eventReceiver?.on({ - type: "token_usage", - inputTokens: usage.input_tokens || 0, - outputTokens: usage.output_tokens || 0, - totalTokens: usage.total_tokens || 0, - cacheReadTokens: usage.input_tokens_details?.cached_tokens || 0, - cacheWriteTokens: 0, // Not available in API - reasoningTokens: usage.output_tokens_details?.reasoning_tokens || 0, - }); - } - - const output = response.output; - if (!output) break; - - for (const item of output) { - // gpt-oss vLLM quirk: need to remove type from "message" events - if (item.id === "message") { - const { type, ...message } = item; - messages.push(item); - } else { - messages.push(item); - } - - switch (item.type) { - case "reasoning": { - // Handle both content (o1/o3) and summary (gpt-5) formats - const reasoningItems = item.content || item.summary || []; - for (const content of reasoningItems) { - if (content.type === "reasoning_text" || content.type === "summary_text") { - await eventReceiver?.on({ type: "reasoning", text: content.text }); - } - } - break; - } - - case "message": { - for (const content of item.content || []) { - if (content.type === "output_text") { - await eventReceiver?.on({ type: "assistant_message", text: content.text }); - } else if (content.type === "refusal") { - await eventReceiver?.on({ type: "error", message: `Refusal: ${content.refusal}` }); - } - conversationDone = true; - } - break; - } - - case "function_call": { - if (signal?.aborted) { - throw new Error("Interrupted"); - } - - try { - await eventReceiver?.on({ - type: "tool_call", - toolCallId: item.call_id || "", - name: item.name, - args: item.arguments, - }); - const result = await executeTool(item.name, item.arguments, signal); - await eventReceiver?.on({ - type: "tool_result", - toolCallId: item.call_id || "", - result, - isError: false, - }); - - // Add tool result to messages - const toolResultMsg = { - type: "function_call_output", - call_id: item.call_id, - output: result, - } as ResponseFunctionToolCallOutputItem; - messages.push(toolResultMsg); - } catch (e: any) { - await eventReceiver?.on({ - type: "tool_result", - toolCallId: item.call_id || "", - result: e.message, - isError: true, - }); - const errorMsg = { - type: "function_call_output", - call_id: item.id, - output: e.message, - isError: true, - }; - messages.push(errorMsg); - } - break; - } - - default: { - eventReceiver?.on({ type: "error", message: `Unknown output type in LLM response: ${item.type}` }); - break; - } - } - } - } -} - -export async function callModelChatCompletionsApi( - client: OpenAI, - model: string, - messages: any[], - signal?: AbortSignal, - eventReceiver?: AgentEventReceiver, - supportsReasoning?: boolean, - baseURL?: string, -): Promise { - let assistantResponded = false; - - while (!assistantResponded) { - if (signal?.aborted) { - throw new Error("Interrupted"); - } - - // Build request options - let requestOptions: any = { - model, - messages, - tools: toolsForChat, - tool_choice: "auto", - max_completion_tokens: 2000, // TODO make configurable - ...(supportsReasoning && { - reasoning_effort: "low", // Use low effort for completions API - }), - }; - - // Apply provider-specific adjustments - requestOptions = adjustRequestForProvider(requestOptions, "completions", baseURL, supportsReasoning); - - const response = await client.chat.completions.create(requestOptions, { signal }); - - const message = response.choices[0].message; - - // Report token usage if available - if (response.usage) { - const usage = response.usage; - await eventReceiver?.on({ - type: "token_usage", - inputTokens: usage.prompt_tokens || 0, - outputTokens: usage.completion_tokens || 0, - totalTokens: usage.total_tokens || 0, - cacheReadTokens: usage.prompt_tokens_details?.cached_tokens || 0, - cacheWriteTokens: 0, // Not available in API - reasoningTokens: usage.completion_tokens_details?.reasoning_tokens || 0, - }); - } - - if (message.tool_calls && message.tool_calls.length > 0) { - // Add assistant message with tool calls to history - const assistantMsg: any = { - role: "assistant", - content: message.content || null, - tool_calls: message.tool_calls, - }; - messages.push(assistantMsg); - - // Display and execute each tool call - for (const toolCall of message.tool_calls) { - // Check if interrupted before executing tool - if (signal?.aborted) { - throw new Error("Interrupted"); - } - - try { - const funcName = toolCall.type === "function" ? toolCall.function.name : toolCall.custom.name; - const funcArgs = toolCall.type === "function" ? toolCall.function.arguments : toolCall.custom.input; - - await eventReceiver?.on({ type: "tool_call", toolCallId: toolCall.id, name: funcName, args: funcArgs }); - const result = await executeTool(funcName, funcArgs, signal); - await eventReceiver?.on({ type: "tool_result", toolCallId: toolCall.id, result, isError: false }); - - // Add tool result to messages - const toolMsg = { - role: "tool", - tool_call_id: toolCall.id, - content: result, - }; - messages.push(toolMsg); - } catch (e: any) { - eventReceiver?.on({ type: "tool_result", toolCallId: toolCall.id, result: e.message, isError: true }); - const errorMsg = { - role: "tool", - tool_call_id: toolCall.id, - content: e.message, - }; - messages.push(errorMsg); - } - } - } else if (message.content) { - // Parse provider-specific reasoning from message - const { cleanContent, reasoningTexts } = parseReasoningFromMessage(message, baseURL); - - // Emit reasoning events if any - for (const reasoning of reasoningTexts) { - await eventReceiver?.on({ type: "reasoning", text: reasoning }); - } - - // Emit the cleaned assistant message - await eventReceiver?.on({ type: "assistant_message", text: cleanContent }); - const finalMsg = { role: "assistant", content: cleanContent }; - messages.push(finalMsg); - assistantResponded = true; - } - } -} - -export class Agent { - private client: OpenAI; - public readonly config: AgentConfig; - private messages: any[] = []; - private renderer?: AgentEventReceiver; - private sessionManager?: SessionManager; - private comboReceiver: AgentEventReceiver; - private abortController: AbortController | null = null; - private supportsReasoning: boolean | null = null; - - constructor(config: AgentConfig, renderer?: AgentEventReceiver, sessionManager?: SessionManager) { - this.config = config; - this.client = new OpenAI({ - apiKey: config.apiKey, - baseURL: config.baseURL, - }); - - // Use provided renderer or default to console - this.renderer = renderer; - this.sessionManager = sessionManager; - - this.comboReceiver = { - on: async (event: AgentEvent): Promise => { - await this.renderer?.on(event); - await this.sessionManager?.on(event); - }, - }; - - // Initialize with system prompt if provided - if (config.systemPrompt) { - this.messages.push({ - role: "developer", - content: config.systemPrompt, - }); - } - - // Start session logging if we have a session manager - if (sessionManager) { - sessionManager.startSession(this.config); - - // Emit session_start event - this.comboReceiver.on({ - type: "session_start", - sessionId: sessionManager.getSessionId(), - model: config.model, - api: config.api, - baseURL: config.baseURL, - systemPrompt: config.systemPrompt, - }); - } - } - - async ask(userMessage: string): Promise { - // Render user message through the event system - this.comboReceiver.on({ type: "user_message", text: userMessage }); - - // Add user message - const userMsg = { role: "user", content: userMessage }; - this.messages.push(userMsg); - - // Create a new AbortController for this chat session - this.abortController = new AbortController(); - - try { - await this.comboReceiver.on({ type: "assistant_start" }); - - // Check reasoning support only once per agent instance - if (this.supportsReasoning === null) { - this.supportsReasoning = await checkReasoningSupport( - this.client, - this.config.model, - this.config.api, - this.config.baseURL, - this.abortController.signal, - ); - } - - if (this.config.api === "responses") { - await callModelResponsesApi( - this.client, - this.config.model, - this.messages, - this.abortController.signal, - this.comboReceiver, - this.supportsReasoning, - this.config.baseURL, - ); - } else { - await callModelChatCompletionsApi( - this.client, - this.config.model, - this.messages, - this.abortController.signal, - this.comboReceiver, - this.supportsReasoning, - this.config.baseURL, - ); - } - } catch (e) { - // Check if this was an interruption by checking the abort signal - if (this.abortController.signal.aborted) { - // Emit interrupted event so UI can clean up properly - await this.comboReceiver?.on({ type: "interrupted" }); - return; - } - throw e; - } finally { - this.abortController = null; - } - } - - interrupt(): void { - this.abortController?.abort(); - } - - setEvents(events: AgentEvent[]): void { - // Reconstruct messages from events based on API type - this.messages = []; - - if (this.config.api === "responses") { - // Responses API format - if (this.config.systemPrompt) { - this.messages.push({ - role: "developer", - content: this.config.systemPrompt, - }); - } - - for (const event of events) { - switch (event.type) { - case "user_message": - this.messages.push({ - role: "user", - content: [{ type: "input_text", text: event.text }], - }); - break; - - case "reasoning": - // Add reasoning message - this.messages.push({ - type: "reasoning", - content: [{ type: "reasoning_text", text: event.text }], - }); - break; - - case "tool_call": - // Add function call - this.messages.push({ - type: "function_call", - id: event.toolCallId, - name: event.name, - arguments: event.args, - }); - break; - - case "tool_result": - // Add function result - this.messages.push({ - type: "function_call_output", - call_id: event.toolCallId, - output: event.result, - }); - break; - - case "assistant_message": - // Add final message - this.messages.push({ - type: "message", - content: [{ type: "output_text", text: event.text }], - }); - break; - } - } - } else { - // Chat Completions API format - if (this.config.systemPrompt) { - this.messages.push({ role: "system", content: this.config.systemPrompt }); - } - - // Track tool calls in progress - let pendingToolCalls: any[] = []; - - for (const event of events) { - switch (event.type) { - case "user_message": - this.messages.push({ role: "user", content: event.text }); - break; - - case "assistant_start": - // Reset pending tool calls for new assistant response - pendingToolCalls = []; - break; - - case "tool_call": - // Accumulate tool calls - pendingToolCalls.push({ - id: event.toolCallId, - type: "function", - function: { - name: event.name, - arguments: event.args, - }, - }); - break; - - case "tool_result": - // When we see the first tool result, add the assistant message with all tool calls - if (pendingToolCalls.length > 0) { - this.messages.push({ - role: "assistant", - content: null, - tool_calls: pendingToolCalls, - }); - pendingToolCalls = []; - } - // Add the tool result - this.messages.push({ - role: "tool", - tool_call_id: event.toolCallId, - content: event.result, - }); - break; - - case "assistant_message": - // Final assistant response (no tool calls) - this.messages.push({ role: "assistant", content: event.text }); - break; - - // Skip other event types (thinking, error, interrupted, token_usage) - } - } - } - } -} diff --git a/packages/agent-old/src/args.ts b/packages/agent-old/src/args.ts deleted file mode 100644 index f1dd9d7c..00000000 --- a/packages/agent-old/src/args.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { homedir } from "os"; -import { resolve } from "path"; - -export type Choice = { - value: T; - description?: string; -}; - -export type ArgDef = { - type: "flag" | "boolean" | "int" | "float" | "string" | "file"; - alias?: string; - default?: any; - description?: string; - choices?: Choice[] | string[]; // Can be simple strings or objects with descriptions - showDefault?: boolean | string; // false to hide, true to show value, string to show custom text -}; - -export type ArgDefs = Record; - -export type ParsedArgs = { - [K in keyof T]: T[K]["type"] extends "flag" - ? boolean - : T[K]["type"] extends "boolean" - ? boolean - : T[K]["type"] extends "int" - ? number - : T[K]["type"] extends "float" - ? number - : T[K]["type"] extends "string" - ? string - : T[K]["type"] extends "file" - ? string - : never; -} & { - _: string[]; // Positional arguments -}; - -export function parseArgs(defs: T, args: string[]): ParsedArgs { - const result: any = { _: [] }; - const aliasMap: Record = {}; - - // Build alias map and set defaults - for (const [key, def] of Object.entries(defs)) { - if (def.alias) { - aliasMap[def.alias] = key; - } - if (def.default !== undefined) { - result[key] = def.default; - } else if (def.type === "flag" || def.type === "boolean") { - result[key] = false; - } - } - - // Parse arguments - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - - // Check if it's a flag - if (arg.startsWith("--")) { - const flagName = arg.slice(2); - const key = aliasMap[flagName] || flagName; - const def = defs[key]; - - if (!def) { - // Unknown flag, add to positional args - result._.push(arg); - continue; - } - - if (def.type === "flag") { - // Simple on/off flag - result[key] = true; - } else if (i + 1 < args.length) { - // Flag with value - const value = args[++i]; - - let parsedValue: any; - - switch (def.type) { - case "boolean": - parsedValue = value === "true" || value === "1" || value === "yes"; - break; - case "int": - parsedValue = parseInt(value, 10); - if (Number.isNaN(parsedValue)) { - throw new Error(`Invalid integer value for --${key}: ${value}`); - } - break; - case "float": - parsedValue = parseFloat(value); - if (Number.isNaN(parsedValue)) { - throw new Error(`Invalid float value for --${key}: ${value}`); - } - break; - case "string": - parsedValue = value; - break; - case "file": { - // Resolve ~ to home directory and make absolute - let path = value; - if (path.startsWith("~")) { - path = path.replace("~", homedir()); - } - parsedValue = resolve(path); - break; - } - } - - // Validate against choices if specified - if (def.choices) { - const validValues = def.choices.map((c) => (typeof c === "string" ? c : c.value)); - if (!validValues.includes(parsedValue)) { - throw new Error( - `Invalid value for --${key}: "${parsedValue}". Valid choices: ${validValues.join(", ")}`, - ); - } - } - - result[key] = parsedValue; - } else { - throw new Error(`Flag --${key} requires a value`); - } - } else if (arg.startsWith("-") && arg.length === 2) { - // Short flag like -h - const flagChar = arg[1]; - const key = aliasMap[flagChar] || flagChar; - const def = defs[key]; - - if (!def) { - result._.push(arg); - continue; - } - - if (def.type === "flag") { - result[key] = true; - } else { - throw new Error(`Short flag -${flagChar} cannot have a value`); - } - } else { - // Positional argument - result._.push(arg); - } - } - - return result as ParsedArgs; -} - -export function printHelp(defs: T, usage: string): void { - console.log(usage); - console.log("\nOptions:"); - - for (const [key, def] of Object.entries(defs)) { - let line = ` --${key}`; - if (def.alias) { - line += `, -${def.alias}`; - } - - if (def.type !== "flag") { - if (def.choices) { - // Show choices instead of type - const simpleChoices = def.choices.filter((c) => typeof c === "string"); - if (simpleChoices.length === def.choices.length) { - // All choices are simple strings - line += ` <${simpleChoices.join("|")}>`; - } else { - // Has descriptions, just show the type - const typeStr = def.type === "file" ? "path" : def.type; - line += ` <${typeStr}>`; - } - } else { - const typeStr = def.type === "file" ? "path" : def.type; - line += ` <${typeStr}>`; - } - } - - if (def.description) { - // Pad to align descriptions - line = line.padEnd(30) + def.description; - } - - if (def.default !== undefined && def.type !== "flag" && def.showDefault !== false) { - if (typeof def.showDefault === "string") { - line += ` (default: ${def.showDefault})`; - } else { - line += ` (default: ${def.default})`; - } - } - - console.log(line); - - // Print choices with descriptions if available - if (def.choices) { - const hasDescriptions = def.choices.some((c) => typeof c === "object" && c.description); - if (hasDescriptions) { - for (const choice of def.choices) { - if (typeof choice === "object") { - const choiceLine = ` ${choice.value}`.padEnd(30) + (choice.description || ""); - console.log(choiceLine); - } - } - } - } - } -} diff --git a/packages/agent-old/src/cli.ts b/packages/agent-old/src/cli.ts deleted file mode 100644 index dbfa9060..00000000 --- a/packages/agent-old/src/cli.ts +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env node - -import { main } from "./main.js"; - -// Run as CLI - this file should always be executed, not imported -main(process.argv.slice(2)).catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/packages/agent-old/src/index.ts b/packages/agent-old/src/index.ts deleted file mode 100644 index 558555f8..00000000 --- a/packages/agent-old/src/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Main exports for pi-agent package - -export type { AgentConfig, AgentEvent, AgentEventReceiver } from "./agent.js"; -export { Agent } from "./agent.js"; -export type { ArgDef, ArgDefs, ParsedArgs } from "./args.js"; -// CLI utilities -export { parseArgs, printHelp } from "./args.js"; -// CLI main function -export { main } from "./main.js"; -// Renderers -export { ConsoleRenderer } from "./renderers/console-renderer.js"; -export { JsonRenderer } from "./renderers/json-renderer.js"; -export { TuiRenderer } from "./renderers/tui-renderer.js"; -export type { SessionData, SessionEvent, SessionHeader } from "./session-manager.js"; -export { SessionManager } from "./session-manager.js"; diff --git a/packages/agent-old/src/main.ts b/packages/agent-old/src/main.ts deleted file mode 100644 index 9980cc64..00000000 --- a/packages/agent-old/src/main.ts +++ /dev/null @@ -1,285 +0,0 @@ -import chalk from "chalk"; -import { createInterface } from "readline"; -import type { AgentConfig } from "./agent.js"; -import { Agent } from "./agent.js"; -import { parseArgs, printHelp as printHelpArgs } from "./args.js"; -import { ConsoleRenderer } from "./renderers/console-renderer.js"; -import { JsonRenderer } from "./renderers/json-renderer.js"; -import { TuiRenderer } from "./renderers/tui-renderer.js"; -import { SessionManager } from "./session-manager.js"; - -// Define argument structure -const argDefs = { - "base-url": { - type: "string" as const, - default: "https://api.openai.com/v1", - description: "API base URL", - }, - "api-key": { - type: "string" as const, - default: process.env.OPENAI_API_KEY || "", - description: "API key", - showDefault: "$OPENAI_API_KEY", - }, - model: { - type: "string" as const, - default: "gpt-5-mini", - description: "Model name", - }, - api: { - type: "string" as const, - default: "completions", - description: "API type", - choices: [ - { value: "completions", description: "OpenAI Chat Completions API (most models)" }, - { value: "responses", description: "OpenAI Responses API (GPT-OSS models)" }, - ], - }, - "system-prompt": { - type: "string" as const, - default: "You are a helpful assistant.", - description: "System prompt", - }, - continue: { - type: "flag" as const, - alias: "c", - description: "Continue previous session", - }, - json: { - type: "flag" as const, - description: "Output as JSONL", - }, - help: { - type: "flag" as const, - alias: "h", - description: "Show this help message", - }, -}; - -interface JsonCommand { - type: "message" | "interrupt"; - content?: string; -} - -function printHelp(): void { - const usage = `Usage: pi-agent [options] [messages...] - -Examples: -# Single message (default OpenAI, GPT-5 Mini, OPENAI_API_KEY env var) -pi-agent "What is 2+2?" - -# Multiple messages processed sequentially -pi-agent "What is 2+2?" "What about 3+3?" - -# Interactive chat mode (no messages = interactive) -pi-agent - -# Continue most recently modified session in current directory -pi-agent --continue "Follow up question" - -# GPT-OSS via Groq -pi-agent --base-url https://api.groq.com/openai/v1 --api-key $GROQ_API_KEY --model openai/gpt-oss-120b - -# GLM 4.5 via OpenRouter -pi-agent --base-url https://openrouter.ai/api/v1 --api-key $OPENROUTER_API_KEY --model z-ai/glm-4.5 - -# Claude via Anthropic (no prompt caching support - see https://docs.anthropic.com/en/api/openai-sdk) -pi-agent --base-url https://api.anthropic.com/v1 --api-key $ANTHROPIC_API_KEY --model claude-opus-4-1-20250805`; - printHelpArgs(argDefs, usage); -} - -async function runJsonInteractiveMode(config: AgentConfig, sessionManager: SessionManager): Promise { - const rl = createInterface({ - input: process.stdin, - output: process.stdout, - terminal: false, // Don't interpret control characters - }); - - const renderer = new JsonRenderer(); - const agent = new Agent(config, renderer, sessionManager); - let isProcessing = false; - let pendingMessage: string | null = null; - - const processMessage = async (content: string): Promise => { - isProcessing = true; - - try { - await agent.ask(content); - } catch (e: any) { - await renderer.on({ type: "error", message: e.message }); - } finally { - isProcessing = false; - - // Process any pending message - if (pendingMessage) { - const msg = pendingMessage; - pendingMessage = null; - await processMessage(msg); - } - } - }; - - // Listen for lines from stdin - rl.on("line", (line) => { - try { - const command = JSON.parse(line) as JsonCommand; - - switch (command.type) { - case "interrupt": - agent.interrupt(); - isProcessing = false; - break; - - case "message": - if (!command.content) { - renderer.on({ type: "error", message: "Message content is required" }); - return; - } - - if (isProcessing) { - // Queue the message for when the agent is done - pendingMessage = command.content; - } else { - processMessage(command.content); - } - break; - - default: - renderer.on({ type: "error", message: `Unknown command type: ${(command as any).type}` }); - } - } catch (e) { - renderer.on({ type: "error", message: `Invalid JSON: ${e}` }); - } - }); - - // Wait for stdin to close - await new Promise((resolve) => { - rl.on("close", () => { - resolve(); - }); - }); -} - -async function runTuiInteractiveMode(agentConfig: AgentConfig, sessionManager: SessionManager): Promise { - const sessionData = sessionManager.getSessionData(); - if (sessionData) { - console.log(chalk.dim(`Resuming session with ${sessionData.events.length} events`)); - } - const renderer = new TuiRenderer(); - - // Initialize TUI BEFORE creating the agent to prevent double init - await renderer.init(); - - const agent = new Agent(agentConfig, renderer, sessionManager); - renderer.setInterruptCallback(() => { - agent.interrupt(); - }); - - if (sessionData) { - agent.setEvents(sessionData ? sessionData.events.map((e) => e.event) : []); - for (const sessionEvent of sessionData.events) { - const event = sessionEvent.event; - if (event.type === "assistant_start") { - renderer.renderAssistantLabel(); - } else { - await renderer.on(event); - } - } - } - - while (true) { - const userInput = await renderer.getUserInput(); - try { - await agent.ask(userInput); - } catch (e: any) { - await renderer.on({ type: "error", message: e.message }); - } - } -} - -async function runSingleShotMode( - agentConfig: AgentConfig, - sessionManager: SessionManager, - messages: string[], - jsonOutput: boolean, -): Promise { - const sessionData = sessionManager.getSessionData(); - const renderer = jsonOutput ? new JsonRenderer() : new ConsoleRenderer(); - const agent = new Agent(agentConfig, renderer, sessionManager); - if (sessionData) { - if (!jsonOutput) { - console.log(chalk.dim(`Resuming session with ${sessionData.events.length} events`)); - } - agent.setEvents(sessionData ? sessionData.events.map((e) => e.event) : []); - } - - for (const msg of messages) { - try { - await agent.ask(msg); - } catch (e: any) { - await renderer.on({ type: "error", message: e.message }); - } - } -} - -// Main function to use Agent as standalone CLI -export async function main(args: string[]): Promise { - // Parse arguments - const parsed = parseArgs(argDefs, args); - - // Show help if requested - if (parsed.help) { - printHelp(); - return; - } - - // Extract configuration from parsed args - const baseURL = parsed["base-url"]; - const apiKey = parsed["api-key"]; - const model = parsed.model; - const continueSession = parsed.continue; - const api = parsed.api as "completions" | "responses"; - const systemPrompt = parsed["system-prompt"]; - const jsonOutput = parsed.json; - const messages = parsed._; // Positional arguments - - if (!apiKey) { - throw new Error("API key required (use --api-key or set OPENAI_API_KEY)"); - } - - // Determine mode: interactive if no messages provided - const isInteractive = messages.length === 0; - - // Create session manager - const sessionManager = new SessionManager(continueSession); - - // Create or restore agent - let agentConfig: AgentConfig = { - apiKey, - baseURL, - model, - api, - systemPrompt, - }; - - if (continueSession) { - const sessionData = sessionManager.getSessionData(); - if (sessionData) { - agentConfig = { - ...sessionData.config, - apiKey, // Allow overriding API key - }; - } - } - - // Run in appropriate mode - if (isInteractive) { - if (jsonOutput) { - await runJsonInteractiveMode(agentConfig, sessionManager); - } else { - await runTuiInteractiveMode(agentConfig, sessionManager); - } - } else { - await runSingleShotMode(agentConfig, sessionManager, messages, jsonOutput); - } -} diff --git a/packages/agent-old/src/renderers/console-renderer.ts b/packages/agent-old/src/renderers/console-renderer.ts deleted file mode 100644 index bcdf275f..00000000 --- a/packages/agent-old/src/renderers/console-renderer.ts +++ /dev/null @@ -1,176 +0,0 @@ -import chalk from "chalk"; -import type { AgentEvent, AgentEventReceiver } from "../agent.js"; - -export class ConsoleRenderer implements AgentEventReceiver { - private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; - private currentFrame = 0; - private animationInterval: NodeJS.Timeout | null = null; - private isAnimating = false; - private animationLine = ""; - private isTTY = process.stdout.isTTY; - private toolCallCount = 0; - private lastInputTokens = 0; - private lastOutputTokens = 0; - private lastCacheReadTokens = 0; - private lastCacheWriteTokens = 0; - private lastReasoningTokens = 0; - - private startAnimation(text: string = "Thinking"): void { - if (this.isAnimating || !this.isTTY) return; - this.isAnimating = true; - this.currentFrame = 0; - - // Write initial frame - this.animationLine = `${chalk.cyan(this.frames[this.currentFrame])} ${chalk.dim(text)}`; - process.stdout.write(this.animationLine); - - this.animationInterval = setInterval(() => { - // Clear current line - process.stdout.write(`\r${" ".repeat(this.animationLine.length)}\r`); - - // Update frame - this.currentFrame = (this.currentFrame + 1) % this.frames.length; - this.animationLine = `${chalk.cyan(this.frames[this.currentFrame])} ${chalk.dim(text)}`; - process.stdout.write(this.animationLine); - }, 80); - } - - private stopAnimation(): void { - if (!this.isAnimating) return; - - if (this.animationInterval) { - clearInterval(this.animationInterval); - this.animationInterval = null; - } - - // Clear the animation line - process.stdout.write(`\r${" ".repeat(this.animationLine.length)}\r`); - this.isAnimating = false; - this.animationLine = ""; - } - - private displayMetrics(): void { - // Build metrics display - let metricsText = chalk.dim( - `↑${this.lastInputTokens.toLocaleString()} ↓${this.lastOutputTokens.toLocaleString()}`, - ); - - // Add reasoning tokens if present - if (this.lastReasoningTokens > 0) { - metricsText += chalk.dim(` ⚡${this.lastReasoningTokens.toLocaleString()}`); - } - - // Add cache info if available - if (this.lastCacheReadTokens > 0 || this.lastCacheWriteTokens > 0) { - const cacheText: string[] = []; - if (this.lastCacheReadTokens > 0) { - cacheText.push(`⟲${this.lastCacheReadTokens.toLocaleString()}`); - } - if (this.lastCacheWriteTokens > 0) { - cacheText.push(`⟳${this.lastCacheWriteTokens.toLocaleString()}`); - } - metricsText += chalk.dim(` (${cacheText.join(" ")})`); - } - - // Add tool call count - if (this.toolCallCount > 0) { - metricsText += chalk.dim(` ⚒ ${this.toolCallCount}`); - } - - console.log(metricsText); - console.log(); - } - - async on(event: AgentEvent): Promise { - // Stop animation for any new event except token_usage - if (event.type !== "token_usage" && this.isAnimating) { - this.stopAnimation(); - } - - switch (event.type) { - case "session_start": - console.log( - chalk.blue( - `[Session started] ID: ${event.sessionId}, Model: ${event.model}, API: ${event.api}, Base URL: ${event.baseURL}`, - ), - ); - console.log(chalk.dim(`System Prompt: ${event.systemPrompt}\n`)); - break; - - case "assistant_start": - console.log(chalk.hex("#FFA500")("[assistant]")); - this.startAnimation(); - break; - - case "reasoning": - this.stopAnimation(); - console.log(chalk.dim("[thinking]")); - console.log(chalk.dim(event.text)); - console.log(); - // Resume animation after showing thinking - this.startAnimation("Processing"); - break; - - case "tool_call": - this.stopAnimation(); - this.toolCallCount++; - console.log(chalk.yellow(`[tool] ${event.name}(${event.args})`)); - // Resume animation while tool executes - this.startAnimation(`Running ${event.name}`); - break; - - case "tool_result": { - this.stopAnimation(); - const lines = event.result.split("\n"); - const maxLines = 10; - const truncated = lines.length > maxLines; - const toShow = truncated ? lines.slice(0, maxLines) : lines; - - const text = toShow.join("\n"); - console.log(event.isError ? chalk.red(text) : chalk.gray(text)); - - if (truncated) { - console.log(chalk.dim(`... (${lines.length - maxLines} more lines)`)); - } - console.log(); - // Resume animation after tool result - this.startAnimation("Thinking"); - break; - } - - case "assistant_message": - this.stopAnimation(); - console.log(event.text); - console.log(); - // Display metrics after assistant message - this.displayMetrics(); - break; - - case "error": - this.stopAnimation(); - console.error(chalk.red(`[error] ${event.message}\n`)); - break; - - case "user_message": - console.log(chalk.green("[user]")); - console.log(event.text); - console.log(); - break; - - case "interrupted": - this.stopAnimation(); - console.log(chalk.red("[Interrupted by user]\n")); - break; - - case "token_usage": - // Store token usage for display after assistant message - this.lastInputTokens = event.inputTokens; - this.lastOutputTokens = event.outputTokens; - this.lastCacheReadTokens = event.cacheReadTokens; - this.lastCacheWriteTokens = event.cacheWriteTokens; - this.lastReasoningTokens = event.reasoningTokens; - // Don't stop animation for this event - break; - } - } -} diff --git a/packages/agent-old/src/renderers/json-renderer.ts b/packages/agent-old/src/renderers/json-renderer.ts deleted file mode 100644 index b90454d9..00000000 --- a/packages/agent-old/src/renderers/json-renderer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { AgentEvent, AgentEventReceiver } from "../agent.js"; - -export class JsonRenderer implements AgentEventReceiver { - async on(event: AgentEvent): Promise { - console.log(JSON.stringify(event)); - } -} diff --git a/packages/agent-old/src/renderers/tui-renderer.ts b/packages/agent-old/src/renderers/tui-renderer.ts deleted file mode 100644 index 5a4fc59d..00000000 --- a/packages/agent-old/src/renderers/tui-renderer.ts +++ /dev/null @@ -1,422 +0,0 @@ -import { - CombinedAutocompleteProvider, - Container, - MarkdownComponent, - TextComponent, - TextEditor, - TUI, - WhitespaceComponent, -} from "@mariozechner/pi-tui"; -import chalk from "chalk"; -import type { AgentEvent, AgentEventReceiver } from "../agent.js"; - -class LoadingAnimation extends TextComponent { - private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; - private currentFrame = 0; - private intervalId: NodeJS.Timeout | null = null; - private ui: TUI | null = null; - - constructor(ui: TUI) { - super("", { bottom: 1 }); - this.ui = ui; - this.start(); - } - - start() { - this.updateDisplay(); - this.intervalId = setInterval(() => { - this.currentFrame = (this.currentFrame + 1) % this.frames.length; - this.updateDisplay(); - }, 80); - } - - stop() { - if (this.intervalId) { - clearInterval(this.intervalId); - this.intervalId = null; - } - } - - private updateDisplay() { - const frame = this.frames[this.currentFrame]; - this.setText(`${chalk.cyan(frame)} ${chalk.dim("Thinking...")}`); - if (this.ui) { - this.ui.requestRender(); - } - } -} - -export class TuiRenderer implements AgentEventReceiver { - private ui: TUI; - private chatContainer: Container; - private statusContainer: Container; - private editor: TextEditor; - private tokenContainer: Container; - private isInitialized = false; - private onInputCallback?: (text: string) => void; - private currentLoadingAnimation: LoadingAnimation | null = null; - private onInterruptCallback?: () => void; - private lastSigintTime = 0; - private lastInputTokens = 0; - private lastOutputTokens = 0; - private lastCacheReadTokens = 0; - private lastCacheWriteTokens = 0; - private lastReasoningTokens = 0; - private toolCallCount = 0; - // Cumulative token tracking - private cumulativeInputTokens = 0; - private cumulativeOutputTokens = 0; - private cumulativeCacheReadTokens = 0; - private cumulativeCacheWriteTokens = 0; - private cumulativeReasoningTokens = 0; - private cumulativeToolCallCount = 0; - private tokenStatusComponent: TextComponent | null = null; - - constructor() { - this.ui = new TUI(); - this.chatContainer = new Container(); - this.statusContainer = new Container(); - this.editor = new TextEditor(); - this.tokenContainer = new Container(); - - // Setup autocomplete for file paths and slash commands - const autocompleteProvider = new CombinedAutocompleteProvider( - [ - { - name: "tokens", - description: "Show cumulative token usage for this session", - }, - ], - process.cwd(), // Base directory for file path completion - ); - this.editor.setAutocompleteProvider(autocompleteProvider); - } - - async init(): Promise { - if (this.isInitialized) return; - - // Add header with instructions - const header = new TextComponent( - chalk.gray(chalk.blueBright(">> pi interactive chat <<<")) + - "\n" + - chalk.dim("Press Escape to interrupt while processing") + - "\n" + - chalk.dim("Press CTRL+C to clear the text editor") + - "\n" + - chalk.dim("Press CTRL+C twice quickly to exit"), - { bottom: 1 }, - ); - - // Setup UI layout - this.ui.addChild(header); - this.ui.addChild(this.chatContainer); - this.ui.addChild(this.statusContainer); - this.ui.addChild(new WhitespaceComponent(1)); - this.ui.addChild(this.editor); - this.ui.addChild(this.tokenContainer); - this.ui.setFocus(this.editor); - - // Set up global key handler for Escape and Ctrl+C - this.ui.onGlobalKeyPress = (data: string): boolean => { - // Intercept Escape key when processing - if (data === "\x1b" && this.currentLoadingAnimation) { - // Call interrupt callback if set - if (this.onInterruptCallback) { - this.onInterruptCallback(); - } - - // Don't do any UI cleanup here - let the interrupted event handle it - // This avoids race conditions and ensures the interrupted message is shown - - // Don't forward to editor - return false; - } - - // Handle Ctrl+C (raw mode sends \x03) - if (data === "\x03") { - const now = Date.now(); - const timeSinceLastCtrlC = now - this.lastSigintTime; - - if (timeSinceLastCtrlC < 500) { - // Second Ctrl+C within 500ms - exit - this.stop(); - process.exit(0); - } else { - // First Ctrl+C - clear the editor - this.clearEditor(); - this.lastSigintTime = now; - } - - // Don't forward to editor - return false; - } - - // Forward all other keys - return true; - }; - - // Handle editor submission - this.editor.onSubmit = (text: string) => { - text = text.trim(); - if (!text) return; - - // Handle slash commands - if (text.startsWith("/")) { - const [command, ...args] = text.slice(1).split(" "); - if (command === "tokens") { - this.showTokenUsage(); - return; - } - // Unknown slash command, ignore - return; - } - - if (this.onInputCallback) { - this.onInputCallback(text); - } - }; - - // Start the UI - await this.ui.start(); - this.isInitialized = true; - } - - async on(event: AgentEvent): Promise { - // Ensure UI is initialized - if (!this.isInitialized) { - await this.init(); - } - - switch (event.type) { - case "assistant_start": - this.chatContainer.addChild(new TextComponent(chalk.hex("#FFA500")("[assistant]"))); - // Disable editor submission while processing - this.editor.disableSubmit = true; - // Start loading animation in the status container - this.statusContainer.clear(); - this.currentLoadingAnimation = new LoadingAnimation(this.ui); - this.statusContainer.addChild(this.currentLoadingAnimation); - break; - - case "reasoning": { - // Show thinking in dim text - const thinkingContainer = new Container(); - thinkingContainer.addChild(new TextComponent(chalk.dim("[thinking]"))); - - // Split thinking text into lines for better display - const thinkingLines = event.text.split("\n"); - for (const line of thinkingLines) { - thinkingContainer.addChild(new TextComponent(chalk.dim(line))); - } - thinkingContainer.addChild(new WhitespaceComponent(1)); - this.chatContainer.addChild(thinkingContainer); - break; - } - - case "tool_call": - this.toolCallCount++; - this.cumulativeToolCallCount++; - this.updateTokenDisplay(); - this.chatContainer.addChild(new TextComponent(chalk.yellow(`[tool] ${event.name}(${event.args})`))); - break; - - case "tool_result": { - // Show tool result with truncation - const lines = event.result.split("\n"); - const maxLines = 10; - const truncated = lines.length > maxLines; - const toShow = truncated ? lines.slice(0, maxLines) : lines; - - const resultContainer = new Container(); - for (const line of toShow) { - resultContainer.addChild(new TextComponent(event.isError ? chalk.red(line) : chalk.gray(line))); - } - - if (truncated) { - resultContainer.addChild(new TextComponent(chalk.dim(`... (${lines.length - maxLines} more lines)`))); - } - resultContainer.addChild(new WhitespaceComponent(1)); - this.chatContainer.addChild(resultContainer); - break; - } - - case "assistant_message": - // Stop loading animation when assistant responds - if (this.currentLoadingAnimation) { - this.currentLoadingAnimation.stop(); - this.currentLoadingAnimation = null; - this.statusContainer.clear(); - } - // Re-enable editor submission - this.editor.disableSubmit = false; - // Use MarkdownComponent for rich formatting - this.chatContainer.addChild(new MarkdownComponent(event.text)); - this.chatContainer.addChild(new WhitespaceComponent(1)); - break; - - case "error": - // Stop loading animation on error - if (this.currentLoadingAnimation) { - this.currentLoadingAnimation.stop(); - this.currentLoadingAnimation = null; - this.statusContainer.clear(); - } - // Re-enable editor submission - this.editor.disableSubmit = false; - this.chatContainer.addChild(new TextComponent(chalk.red(`[error] ${event.message}`), { bottom: 1 })); - break; - - case "user_message": - // Render user message - this.chatContainer.addChild(new TextComponent(chalk.green("[user]"))); - this.chatContainer.addChild(new TextComponent(event.text, { bottom: 1 })); - break; - - case "token_usage": - // Store the latest token counts (not cumulative since prompt includes full context) - this.lastInputTokens = event.inputTokens; - this.lastOutputTokens = event.outputTokens; - this.lastCacheReadTokens = event.cacheReadTokens; - this.lastCacheWriteTokens = event.cacheWriteTokens; - this.lastReasoningTokens = event.reasoningTokens; - - // Accumulate cumulative totals - this.cumulativeInputTokens += event.inputTokens; - this.cumulativeOutputTokens += event.outputTokens; - this.cumulativeCacheReadTokens += event.cacheReadTokens; - this.cumulativeCacheWriteTokens += event.cacheWriteTokens; - this.cumulativeReasoningTokens += event.reasoningTokens; - - this.updateTokenDisplay(); - break; - - case "interrupted": - // Stop the loading animation - if (this.currentLoadingAnimation) { - this.currentLoadingAnimation.stop(); - this.currentLoadingAnimation = null; - this.statusContainer.clear(); - } - // Show interrupted message - this.chatContainer.addChild(new TextComponent(chalk.red("[Interrupted by user]"), { bottom: 1 })); - // Re-enable editor submission - this.editor.disableSubmit = false; - // Explicitly request render to ensure message is displayed - this.ui.requestRender(); - break; - } - - this.ui.requestRender(); - } - - private updateTokenDisplay(): void { - // Clear and update token display - this.tokenContainer.clear(); - - // Build token display text - let tokenText = chalk.dim( - `↑ ${this.lastInputTokens.toLocaleString()} ↓ ${this.lastOutputTokens.toLocaleString()}`, - ); - - // Add reasoning tokens if present - if (this.lastReasoningTokens > 0) { - tokenText += chalk.dim(` ⚡ ${this.lastReasoningTokens.toLocaleString()}`); - } - - // Add cache info if available - if (this.lastCacheReadTokens > 0 || this.lastCacheWriteTokens > 0) { - const cacheText: string[] = []; - if (this.lastCacheReadTokens > 0) { - cacheText.push(` cache read: ${this.lastCacheReadTokens.toLocaleString()}`); - } - if (this.lastCacheWriteTokens > 0) { - cacheText.push(` cache write: ${this.lastCacheWriteTokens.toLocaleString()}`); - } - tokenText += chalk.dim(` (${cacheText.join(" ")})`); - } - - // Add tool call count - if (this.toolCallCount > 0) { - tokenText += chalk.dim(` ⚒ ${this.toolCallCount}`); - } - - this.tokenStatusComponent = new TextComponent(tokenText); - this.tokenContainer.addChild(this.tokenStatusComponent); - } - - async getUserInput(): Promise { - return new Promise((resolve) => { - this.onInputCallback = (text: string) => { - this.onInputCallback = undefined; // Clear callback - resolve(text); - }; - }); - } - - setInterruptCallback(callback: () => void): void { - this.onInterruptCallback = callback; - } - - clearEditor(): void { - this.editor.setText(""); - - // Show hint in status container - this.statusContainer.clear(); - const hint = new TextComponent(chalk.dim("Press Ctrl+C again to exit")); - this.statusContainer.addChild(hint); - this.ui.requestRender(); - - // Clear the hint after 500ms - setTimeout(() => { - this.statusContainer.clear(); - this.ui.requestRender(); - }, 500); - } - - renderAssistantLabel(): void { - // Just render the assistant label without starting animations - // Used for restored session history - this.chatContainer.addChild(new TextComponent(chalk.hex("#FFA500")("[assistant]"))); - this.ui.requestRender(); - } - - private showTokenUsage(): void { - let tokenText = chalk.dim( - `Total usage\n input: ${this.cumulativeInputTokens.toLocaleString()}\n output: ${this.cumulativeOutputTokens.toLocaleString()}`, - ); - - if (this.cumulativeReasoningTokens > 0) { - tokenText += chalk.dim(`\n reasoning: ${this.cumulativeReasoningTokens.toLocaleString()}`); - } - - if (this.cumulativeCacheReadTokens > 0 || this.cumulativeCacheWriteTokens > 0) { - const cacheText: string[] = []; - if (this.cumulativeCacheReadTokens > 0) { - cacheText.push(`\n cache read: ${this.cumulativeCacheReadTokens.toLocaleString()}`); - } - if (this.cumulativeCacheWriteTokens > 0) { - cacheText.push(`\n cache right: ${this.cumulativeCacheWriteTokens.toLocaleString()}`); - } - tokenText += chalk.dim(` ${cacheText.join(" ")}`); - } - - if (this.cumulativeToolCallCount > 0) { - tokenText += chalk.dim(`\n tool calls: ${this.cumulativeToolCallCount}`); - } - - const tokenSummary = new TextComponent(chalk.italic(tokenText), { bottom: 1 }); - this.chatContainer.addChild(tokenSummary); - this.ui.requestRender(); - } - - stop(): void { - if (this.currentLoadingAnimation) { - this.currentLoadingAnimation.stop(); - this.currentLoadingAnimation = null; - } - if (this.isInitialized) { - this.ui.stop(); - this.isInitialized = false; - } - } -} diff --git a/packages/agent-old/src/session-manager.ts b/packages/agent-old/src/session-manager.ts deleted file mode 100644 index 6c392f9f..00000000 --- a/packages/agent-old/src/session-manager.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { randomBytes } from "crypto"; -import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync } from "fs"; -import { homedir } from "os"; -import { join, resolve } from "path"; -import type { AgentConfig, AgentEvent, AgentEventReceiver } from "./agent.js"; - -// Simple UUID v4 generator -function uuidv4(): string { - const bytes = randomBytes(16); - bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4 - bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 10 - const hex = bytes.toString("hex"); - return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`; -} - -export interface SessionHeader { - type: "session"; - id: string; - timestamp: string; - cwd: string; - config: AgentConfig; -} - -export interface SessionEvent { - type: "event"; - timestamp: string; - event: AgentEvent; -} - -export interface SessionData { - config: AgentConfig; - events: SessionEvent[]; - totalUsage: Extract; -} - -export class SessionManager implements AgentEventReceiver { - private sessionId!: string; - private sessionFile!: string; - private sessionDir: string; - - constructor(continueSession: boolean = false) { - this.sessionDir = this.getSessionDirectory(); - - if (continueSession) { - const mostRecent = this.findMostRecentlyModifiedSession(); - if (mostRecent) { - this.sessionFile = mostRecent; - // Load session ID from file - this.loadSessionId(); - } else { - // No existing session, create new - this.initNewSession(); - } - } else { - this.initNewSession(); - } - } - - private getSessionDirectory(): string { - const cwd = process.cwd(); - const safePath = "--" + cwd.replace(/^\//, "").replace(/\//g, "-") + "--"; - - const piConfigDir = resolve(process.env.PI_CONFIG_DIR || join(homedir(), ".pi")); - const sessionDir = join(piConfigDir, "sessions", safePath); - if (!existsSync(sessionDir)) { - mkdirSync(sessionDir, { recursive: true }); - } - return sessionDir; - } - - private initNewSession(): void { - this.sessionId = uuidv4(); - const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); - this.sessionFile = join(this.sessionDir, `${timestamp}_${this.sessionId}.jsonl`); - } - - private findMostRecentlyModifiedSession(): string | null { - try { - const files = readdirSync(this.sessionDir) - .filter((f) => f.endsWith(".jsonl")) - .map((f) => ({ - name: f, - path: join(this.sessionDir, f), - mtime: statSync(join(this.sessionDir, f)).mtime, - })) - .sort((a, b) => b.mtime.getTime() - a.mtime.getTime()); - - return files[0]?.path || null; - } catch { - return null; - } - } - - private loadSessionId(): void { - if (!existsSync(this.sessionFile)) return; - - const lines = readFileSync(this.sessionFile, "utf8").trim().split("\n"); - for (const line of lines) { - try { - const entry = JSON.parse(line); - if (entry.type === "session") { - this.sessionId = entry.id; - return; - } - } catch { - // Skip malformed lines - } - } - // If no session entry found, create new ID - this.sessionId = uuidv4(); - } - - startSession(config: AgentConfig): void { - const entry: SessionHeader = { - type: "session", - id: this.sessionId, - timestamp: new Date().toISOString(), - cwd: process.cwd(), - config, - }; - appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n"); - } - - async on(event: AgentEvent): Promise { - const entry: SessionEvent = { - type: "event", - timestamp: new Date().toISOString(), - event: event, - }; - appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n"); - } - - getSessionData(): SessionData | null { - if (!existsSync(this.sessionFile)) return null; - - let config: AgentConfig | null = null; - const events: SessionEvent[] = []; - let totalUsage: Extract = { - type: "token_usage", - inputTokens: 0, - outputTokens: 0, - totalTokens: 0, - cacheReadTokens: 0, - cacheWriteTokens: 0, - reasoningTokens: 0, - }; - - const lines = readFileSync(this.sessionFile, "utf8").trim().split("\n"); - for (const line of lines) { - try { - const entry = JSON.parse(line); - if (entry.type === "session") { - config = entry.config; - this.sessionId = entry.id; - } else if (entry.type === "event") { - const eventEntry: SessionEvent = entry as SessionEvent; - events.push(eventEntry); - if (eventEntry.event.type === "token_usage") { - const usage = entry.event as Extract; - if (!totalUsage) { - totalUsage = { ...usage }; - } else { - totalUsage.inputTokens += usage.inputTokens; - totalUsage.outputTokens += usage.outputTokens; - totalUsage.totalTokens += usage.totalTokens; - totalUsage.cacheReadTokens += usage.cacheReadTokens; - totalUsage.cacheWriteTokens += usage.cacheWriteTokens; - totalUsage.reasoningTokens += usage.reasoningTokens; - } - } - } - } catch { - // Skip malformed lines - } - } - - return config ? { config, events, totalUsage } : null; - } - - getSessionId(): string { - return this.sessionId; - } - - getSessionFile(): string { - return this.sessionFile; - } -} diff --git a/packages/agent-old/src/tools/tools.ts b/packages/agent-old/src/tools/tools.ts deleted file mode 100644 index 1d09c2b6..00000000 --- a/packages/agent-old/src/tools/tools.ts +++ /dev/null @@ -1,264 +0,0 @@ -import { spawn } from "node:child_process"; -import { closeSync, existsSync, openSync, readdirSync, readFileSync, readSync, statSync } from "node:fs"; -import { resolve } from "node:path"; -import { glob } from "glob"; -import type { ChatCompletionTool } from "openai/resources"; - -// For GPT-OSS models via responses API -export const toolsForResponses = [ - { - type: "function" as const, - name: "read", - description: "Read contents of a file", - parameters: { - type: "object", - properties: { - path: { - type: "string", - description: "Path to the file to read", - }, - }, - required: ["path"], - }, - }, - { - type: "function" as const, - name: "list", - description: "List contents of a directory", - parameters: { - type: "object", - properties: { - path: { - type: "string", - description: "Path to the directory (default: current directory)", - }, - }, - }, - }, - { - type: "function" as const, - name: "bash", - description: "Execute a command in Bash", - parameters: { - type: "object", - properties: { - command: { - type: "string", - description: "Command to execute", - }, - }, - required: ["command"], - }, - }, - { - type: "function" as const, - name: "glob", - description: "Find files matching a glob pattern", - parameters: { - type: "object", - properties: { - pattern: { - type: "string", - description: "Glob pattern to match files (e.g., '**/*.ts', 'src/**/*.json')", - }, - path: { - type: "string", - description: "Directory to search in (default: current directory)", - }, - }, - required: ["pattern"], - }, - }, - { - type: "function" as const, - name: "rg", - description: "Search using ripgrep.", - parameters: { - type: "object", - properties: { - args: { - type: "string", - description: - 'Arguments to pass directly to ripgrep. Examples: "-l prompt" or "-i TODO" or "--type ts className" or "functionName src/". Never add quotes around the search pattern.', - }, - }, - required: ["args"], - }, - }, -]; - -// For standard chat API (OpenAI format) -export const toolsForChat: ChatCompletionTool[] = toolsForResponses.map((tool) => ({ - type: "function" as const, - function: { - name: tool.name, - description: tool.description, - parameters: tool.parameters, - }, -})); - -// Helper to execute commands with abort support -async function execWithAbort(command: string, signal?: AbortSignal): Promise { - return new Promise((resolve, reject) => { - const child = spawn(command, { - shell: true, - signal, - }); - - let stdout = ""; - let stderr = ""; - const MAX_OUTPUT_SIZE = 1024 * 1024; // 1MB limit - let outputTruncated = false; - - child.stdout?.on("data", (data) => { - const chunk = data.toString(); - if (stdout.length + chunk.length > MAX_OUTPUT_SIZE) { - if (!outputTruncated) { - stdout += "\n... [Output truncated - exceeded 1MB limit] ..."; - outputTruncated = true; - } - } else { - stdout += chunk; - } - }); - - child.stderr?.on("data", (data) => { - const chunk = data.toString(); - if (stderr.length + chunk.length > MAX_OUTPUT_SIZE) { - if (!outputTruncated) { - stderr += "\n... [Output truncated - exceeded 1MB limit] ..."; - outputTruncated = true; - } - } else { - stderr += chunk; - } - }); - - child.on("error", (error) => { - reject(error); - }); - - child.on("close", (code) => { - if (signal?.aborted) { - reject(new Error("Interrupted")); - } else if (code !== 0 && code !== null) { - // For some commands like ripgrep, exit code 1 is normal (no matches) - if (code === 1 && command.includes("rg")) { - resolve(""); // No matches for ripgrep - } else if (stderr && !stdout) { - reject(new Error(stderr)); - } else { - resolve(stdout || ""); - } - } else { - resolve(stdout || stderr || ""); - } - }); - - // Kill the process if signal is aborted - if (signal) { - signal.addEventListener( - "abort", - () => { - child.kill("SIGTERM"); - }, - { once: true }, - ); - } - }); -} - -export async function executeTool(name: string, args: string, signal?: AbortSignal): Promise { - const parsed = JSON.parse(args); - - switch (name) { - case "read": { - const path = parsed.path; - if (!path) return "Error: path parameter is required"; - const file = resolve(path); - if (!existsSync(file)) return `File not found: ${file}`; - - // Check file size before reading - const stats = statSync(file); - const MAX_FILE_SIZE = 1024 * 1024; // 1MB limit - if (stats.size > MAX_FILE_SIZE) { - // Read only the first 1MB - const fd = openSync(file, "r"); - const buffer = Buffer.alloc(MAX_FILE_SIZE); - readSync(fd, buffer, 0, MAX_FILE_SIZE, 0); - closeSync(fd); - return buffer.toString("utf8") + "\n\n... [File truncated - exceeded 1MB limit] ..."; - } - - const data = readFileSync(file, "utf8"); - return data; - } - - case "list": { - const path = parsed.path || "."; - const dir = resolve(path); - if (!existsSync(dir)) return `Directory not found: ${dir}`; - const entries = readdirSync(dir, { withFileTypes: true }); - return entries.map((entry) => (entry.isDirectory() ? entry.name + "/" : entry.name)).join("\n"); - } - - case "bash": { - const command = parsed.command; - if (!command) return "Error: command parameter is required"; - try { - const output = await execWithAbort(command, signal); - return output || "Command executed successfully"; - } catch (e: any) { - if (e.message === "Interrupted") { - throw e; // Re-throw interruption - } - throw new Error(`Command failed: ${e.message}`); - } - } - - case "glob": { - const pattern = parsed.pattern; - if (!pattern) return "Error: pattern parameter is required"; - const searchPath = parsed.path || process.cwd(); - - try { - const matches = await glob(pattern, { - cwd: searchPath, - dot: true, - nodir: false, - mark: true, // Add / to directories - }); - - if (matches.length === 0) { - return "No files found matching the pattern"; - } - - // Sort by modification time (most recent first) if possible - return matches.sort().join("\n"); - } catch (e: any) { - return `Glob error: ${e.message}`; - } - } - - case "rg": { - const args = parsed.args; - if (!args) return "Error: args parameter is required"; - - // Force ripgrep to never read from stdin by redirecting stdin from /dev/null - const cmd = `rg ${args} < /dev/null`; - - try { - const output = await execWithAbort(cmd, signal); - return output.trim() || "No matches found"; - } catch (e: any) { - if (e.message === "Interrupted") { - throw e; // Re-throw interruption - } - return `ripgrep error: ${e.message}`; - } - } - - default: - return `Unknown tool: ${name}`; - } -} diff --git a/packages/agent-old/tsconfig.build.json b/packages/agent-old/tsconfig.build.json deleted file mode 100644 index 5ce43029..00000000 --- a/packages/agent-old/tsconfig.build.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} \ No newline at end of file diff --git a/packages/pods/src/commands/prompt.ts b/packages/pods/src/commands/prompt.ts index 5c1c6308..09793710 100644 --- a/packages/pods/src/commands/prompt.ts +++ b/packages/pods/src/commands/prompt.ts @@ -1,4 +1,3 @@ -import { main as agentMain } from "@mariozechner/pi-agent-old"; import chalk from "chalk"; import { getActivePod, loadConfig } from "../config.js"; @@ -77,7 +76,7 @@ Current working directory: ${process.cwd()}`; // Call agent main function directly try { - await agentMain(args); + throw new Error("Not implemented"); } catch (err: any) { console.error(chalk.red(`Agent error: ${err.message}`)); process.exit(1);