From 724909c72cfebe282898d849bc22974609f891cf Mon Sep 17 00:00:00 2001 From: Mochi Botterson Date: Fri, 24 Apr 2026 13:07:53 -0600 Subject: [PATCH] feat: Add generator skills category + create-agent-tui skill This PR expands the Venice skills repo to include two types of skills: 1. **API Documentation Skills** (existing, venice-*) - Teach agents how to use specific Venice API endpoints - Follow the strict endpoint/curl/errors template 2. **Generator / Scaffolding Skills** (new, create-*) - Help agents create new projects using Venice AI - Include sample code, references, and interactive checklists Changes: - README.md: Split skill catalog into two sections - CONTRIBUTING.md: Add guidelines for both skill types - Add create-agent-tui skill (like create-react-app for terminal agents) The create-agent-tui skill scaffolds complete agent TUI projects in TypeScript with Venice-specific features: web search, X search, model aliases, DIEM tracking, and 30+ model support. --- CONTRIBUTING.md | 33 +- README.md | 12 + skills/create-agent-tui/.gitignore | 5 + skills/create-agent-tui/README.md | 93 ++ skills/create-agent-tui/SKILL.md | 821 ++++++++++++++++++ skills/create-agent-tui/references/modules.md | 209 +++++ skills/create-agent-tui/references/tools.md | 263 ++++++ .../references/venice-features.md | 196 +++++ skills/create-agent-tui/sample/.env.example | 9 + .../create-agent-tui/sample/package-lock.json | 820 +++++++++++++++++ skills/create-agent-tui/sample/package.json | 18 + skills/create-agent-tui/sample/src/agent.ts | 131 +++ skills/create-agent-tui/sample/src/banner.ts | 124 +++ skills/create-agent-tui/sample/src/cli.ts | 319 +++++++ skills/create-agent-tui/sample/src/config.ts | 101 +++ skills/create-agent-tui/sample/src/loader.ts | 119 +++ .../create-agent-tui/sample/src/renderer.ts | 167 ++++ skills/create-agent-tui/sample/src/session.ts | 117 +++ .../sample/src/terminal-bg.ts | 138 +++ .../sample/src/tools/file-edit.ts | 74 ++ .../sample/src/tools/file-read.ts | 34 + .../sample/src/tools/file-write.ts | 25 + .../create-agent-tui/sample/src/tools/glob.ts | 27 + .../create-agent-tui/sample/src/tools/grep.ts | 51 ++ .../sample/src/tools/index.ts | 39 + .../sample/src/tools/list-dir.ts | 69 ++ .../sample/src/tools/shell.ts | 39 + .../sample/src/venice-client.ts | 185 ++++ skills/create-agent-tui/sample/tsconfig.json | 12 + skills/create-agent-tui/skill.json | 33 + 30 files changed, 4282 insertions(+), 1 deletion(-) create mode 100644 skills/create-agent-tui/.gitignore create mode 100644 skills/create-agent-tui/README.md create mode 100644 skills/create-agent-tui/SKILL.md create mode 100644 skills/create-agent-tui/references/modules.md create mode 100644 skills/create-agent-tui/references/tools.md create mode 100644 skills/create-agent-tui/references/venice-features.md create mode 100644 skills/create-agent-tui/sample/.env.example create mode 100644 skills/create-agent-tui/sample/package-lock.json create mode 100644 skills/create-agent-tui/sample/package.json create mode 100644 skills/create-agent-tui/sample/src/agent.ts create mode 100644 skills/create-agent-tui/sample/src/banner.ts create mode 100644 skills/create-agent-tui/sample/src/cli.ts create mode 100644 skills/create-agent-tui/sample/src/config.ts create mode 100644 skills/create-agent-tui/sample/src/loader.ts create mode 100644 skills/create-agent-tui/sample/src/renderer.ts create mode 100644 skills/create-agent-tui/sample/src/session.ts create mode 100644 skills/create-agent-tui/sample/src/terminal-bg.ts create mode 100644 skills/create-agent-tui/sample/src/tools/file-edit.ts create mode 100644 skills/create-agent-tui/sample/src/tools/file-read.ts create mode 100644 skills/create-agent-tui/sample/src/tools/file-write.ts create mode 100644 skills/create-agent-tui/sample/src/tools/glob.ts create mode 100644 skills/create-agent-tui/sample/src/tools/grep.ts create mode 100644 skills/create-agent-tui/sample/src/tools/index.ts create mode 100644 skills/create-agent-tui/sample/src/tools/list-dir.ts create mode 100644 skills/create-agent-tui/sample/src/tools/shell.ts create mode 100644 skills/create-agent-tui/sample/src/venice-client.ts create mode 100644 skills/create-agent-tui/sample/tsconfig.json create mode 100644 skills/create-agent-tui/skill.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac6271e..2f8eca5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,19 @@ template/SKILL.md Starting point for new skills Supporting files (helper scripts, reference data) can live alongside `SKILL.md` inside the skill folder and be referenced relative to it. -## Style +## Skill types + +This repo contains two types of skills: + +### API Documentation Skills (`venice-*`) + +Skills that teach agents how to use specific Venice API endpoints. These follow a strict template. + +### Generator / Scaffolding Skills (`create-*`) + +Skills that help agents create new projects using Venice AI. These include sample code, references, and interactive checklists. They follow a more flexible structure but must still have concrete frontmatter descriptions. + +## Style for API Documentation Skills - **Scope one surface area** — each skill covers a coherent slice of the Venice API (e.g. `venice-embeddings`, not `venice-embeddings-and-chat`). If two skills keep cross-referencing each other, either merge them or tighten their boundaries. - **Concrete frontmatter `description`** — the agent uses this to decide when to load. Name the specific endpoints, parameters, and scenarios. Vague descriptions ("things about chat") hurt selection. @@ -21,6 +33,14 @@ Supporting files (helper scripts, reference data) can live alongside `SKILL.md` - **Cross-links** — reference related skills with relative paths (`../venice-errors/SKILL.md`). - **Length** — aim for under 500 lines. Longer skills should be split. +## Style for Generator Skills + +- **Concrete frontmatter `description`** — describe when an agent should use this skill (e.g., "when building an agent TUI", "when scaffolding a new project"). +- **Interactive checklists** — present options as multi-select checklists with sensible defaults. +- **Working sample code** — include a complete, runnable sample in a `sample/` directory. +- **References** — put detailed specs in a `references/` directory to keep the main SKILL.md focused. +- **Generation workflow** — clearly document the steps an agent should follow. + ## Authoring a new skill 1. Copy `template/` to `skills//`. @@ -38,6 +58,8 @@ Supporting files (helper scripts, reference data) can live alongside `SKILL.md` ## Review checklist +### For API Documentation Skills + - [ ] Frontmatter `name` + `description` present and concrete. - [ ] Endpoint table at the top. - [ ] At least one `curl` example. @@ -45,3 +67,12 @@ Supporting files (helper scripts, reference data) can live alongside `SKILL.md` - [ ] Cross-links to related skills. - [ ] Added to root `README.md` catalog if new. - [ ] No secrets or real API keys in examples (use `$VENICE_API_KEY`). + +### For Generator Skills + +- [ ] Frontmatter `name` + `description` present and concrete. +- [ ] Interactive checklist with defaults marked. +- [ ] Working sample code in `sample/` that compiles/runs. +- [ ] Clear generation workflow documented. +- [ ] Added to root `README.md` catalog if new. +- [ ] No secrets or real API keys in examples (use `$VENICE_API_KEY`). diff --git a/README.md b/README.md index f0d682b..f0954d7 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ template/ Copy this as a starting point for a new skill ## Skill catalog +### API Documentation Skills + +Skills that teach agents how to use specific Venice API endpoints. + | Skill | Covers | |---|---| | [`venice-api-overview`](./skills/venice-api-overview/SKILL.md) | Base URL, auth modes, response headers, pricing model, versioning | @@ -35,6 +39,14 @@ template/ Copy this as a starting point for a new skill | [`venice-augment`](./skills/venice-augment/SKILL.md) | `/augment/text-parser`, `/augment/scrape`, `/augment/search` | | [`venice-errors`](./skills/venice-errors/SKILL.md) | Error shapes, 402 payment required, 422 content policy, 429 rate limits, retry strategy | +### Generator / Scaffolding Skills + +Skills that help agents create new projects using Venice AI. + +| Skill | Covers | +|---|---| +| [`create-agent-tui`](./skills/create-agent-tui/SKILL.md) | Scaffold complete agent TUI projects in TypeScript — like `create-react-app` for terminal agents | + ## Using these skills Each skill is just a folder with a `SKILL.md` that starts with YAML frontmatter: diff --git a/skills/create-agent-tui/.gitignore b/skills/create-agent-tui/.gitignore new file mode 100644 index 0000000..4274b51 --- /dev/null +++ b/skills/create-agent-tui/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +.env +*.log +.DS_Store diff --git a/skills/create-agent-tui/README.md b/skills/create-agent-tui/README.md new file mode 100644 index 0000000..511c1b7 --- /dev/null +++ b/skills/create-agent-tui/README.md @@ -0,0 +1,93 @@ +# create-venice-agent-tui + +> Like `create-react-app` but for terminal agents — scaffolds a complete agent TUI in TypeScript using Venice AI. + +![Venice Agent TUI](./sample/screenshots/banner.png) + +## What It Does + +A skill for AI coding agents (Claude Code, Cursor, Codex, etc.) that scaffolds a complete agent TUI in TypeScript. Tell your coding agent what kind of agent you want, and it generates a runnable project that: + +- Works with **any Venice model** (Claude, GPT-5.5, Grok, Llama, DeepSeek, Mistral) +- Provides a **fully customizable terminal interface** with streaming output +- Includes **file operations, shell, grep, glob** tools out of the box +- Supports **web search and X/Twitter search** via Venice's built-in capabilities +- Tracks **token usage and session persistence** +- Respects Venice's **privacy-first, zero-retention** inference + +## Quick Start + +### Try the sample: + +```bash +cd sample +npm install +VENICE_API_KEY=vn_your_key npm start +``` + +### Or tell your coding agent: + +> "Build me an agent TUI that can read/write files and run shell commands" + +It will use this skill automatically. + +## Features + +### 🎨 Customizable UI +- **Input styles:** block (background box), bordered (line frame), plain (simple prompt) +- **Tool displays:** grouped (tree output), emoji (per-call markers), minimal (one-liners) +- **Loaders:** spinner, gradient shimmer, trailing dots + +### 🛠️ Built-in Tools +- File read/write/edit with diff validation +- Shell command execution with timeout +- Glob file finding +- Grep content search +- Custom tool templates + +### 🔍 Venice-Specific +- Web search with citations +- X/Twitter search (Grok models) +- Reasoning effort control +- DIEM cost tracking +- Balance awareness + +### 📊 Session Management +- JSONL conversation persistence +- Token usage tracking +- Multi-turn conversations +- Slash commands (`/model`, `/new`, `/help`) + +## Model Aliases + +Quick shortcuts for common models: + +``` +opus → claude-opus-4-6 +opus-4.7 → claude-opus-4-7 +sonnet → claude-sonnet-4-6 +gpt5 → gpt-5.5 +grok → grok-41-fast +deepseek → deepseek-v4-pro +llama → llama-4-maverick-17b-128e-instruct +qwen → qwen3-235b-a22b +mistral → mistral-large-2411 +``` + +Use with `/model grok` or `npm start -- --model=deepseek`. + +## Why Venice? + +1. **Privacy** — Zero data retention on private models +2. **Model diversity** — 30+ models in one API +3. **Built-in search** — Web and X search without extra API keys +4. **DIEM economics** — Pay-as-you-go credits that can be resold +5. **Uncensored options** — Models that work for legitimate use cases others refuse + +## License + +MIT + +--- + +*Built for the Venice AI ecosystem. Get your API key at [venice.ai/settings/api](https://venice.ai/settings/api).* diff --git a/skills/create-agent-tui/SKILL.md b/skills/create-agent-tui/SKILL.md new file mode 100644 index 0000000..7f10149 --- /dev/null +++ b/skills/create-agent-tui/SKILL.md @@ -0,0 +1,821 @@ +--- +name: create-venice-agent-tui +description: Scaffolds a complete agent TUI in TypeScript using Venice AI — like create-react-app for terminal agents. Generates a customizable terminal interface with streaming output, session persistence, DIEM-aware cost tracking, and configurable tools. Works with any Venice model (Claude, GPT, Grok, Llama, Mistral, DeepSeek). Use when building an agent, creating a TUI, scaffolding an agent project, or building a coding assistant. +--- + +# Create Venice Agent TUI + +Scaffolds a complete agent TUI in TypeScript targeting **Venice AI**. The generated project provides a production-ready terminal interface with: + +- **Privacy-first inference** via Venice's zero-retention API +- **30+ model options** — Claude Opus 4.7, GPT-5.5, Grok 4, Llama 4, DeepSeek V4, Mistral, and more +- **DIEM integration** — optional pay-as-you-go with Venice's credit system +- **Built-in web search** — Venice's web search and X/Twitter search (via Grok models) +- **Customizable TUI** — input styles, tool displays, ASCII banners, loaders +- **Session persistence** — JSONL append-only conversation logs +- **Full tool suite** — file ops, shell, grep, glob, and custom tools + +Architecture draws from production agent systems: +- **OpenClaw** — session management, tool permissions, approval flows +- **Claude Code** — tool metadata, system prompt composition +- **Codex CLI** — layered config, structured logging + +## Prerequisites + +- Node.js 18+ +- `VENICE_API_KEY` from [venice.ai/settings/api](https://venice.ai/settings/api) +- Optional: DIEM balance for extended usage + +--- + +## Decision Tree + +| User wants to... | Action | +|---|---| +| Build a new agent from scratch | Present checklist below → follow Generation Workflow | +| Add tools to an existing harness | Read [references/tools.md](references/tools.md), present tool checklist only | +| Add a harness module | Read [references/modules.md](references/modules.md), generate the module | +| Configure DIEM payments | Read [references/diem-integration.md](references/diem-integration.md) | + +--- + +## Interactive Tool Checklist + +Present this as a multi-select checklist. Items marked **ON** are pre-selected defaults. + +### Venice Server-Side Features + +| Feature | Default | Config | +|---------|---------|--------| +| Web Search | ON | Auto-search with citations via `--web-search auto` | +| X/Twitter Search | OFF | Grok models only, via `--x-search` | +| Image Generation | OFF | Flux, DALL-E, Stable Diffusion models | +| Vision/Image Analysis | OFF | Analyze images in conversation | +| TTS (Text-to-Speech) | OFF | 60+ voices via Kokoro TTS | + +### User-Defined Tools (client-side, generated into src/tools/) + +| Tool | Default | Description | +|------|---------|-------------| +| File Read | ON | Read files with offset/limit, detect images | +| File Write | ON | Write/create files, auto-create directories | +| File Edit | ON | Search-and-replace with diff validation | +| Glob/Find | ON | File discovery by glob pattern | +| Grep/Search | ON | Content search by regex | +| Directory List | ON | List directory contents | +| Shell/Bash | ON | Execute commands with timeout and output capture | +| JS REPL | OFF | Persistent Node.js environment | +| Sub-agent Spawn | OFF | Delegate tasks to child agents | +| Plan/Todo | OFF | Track multi-step task progress | +| Request User Input | OFF | Structured multiple-choice questions | +| Web Fetch | OFF | Fetch and extract text from web pages | +| View Image | OFF | Read local images as base64 | +| Custom Tool Template | ON | Empty skeleton for domain-specific tools | + +### Harness Modules (architectural components) + +| Module | Default | Description | +|--------|---------|-------------| +| Session Persistence | ON | JSONL append-only conversation log | +| ASCII Logo Banner | OFF | Custom ASCII art banner on startup | +| Context Compaction | OFF | Summarize older messages when context is long | +| System Prompt Composition | OFF | Assemble instructions from static + dynamic context | +| Tool Permissions / Approval | OFF | Gate dangerous tools behind user confirmation | +| Structured Event Logging | OFF | Emit events for tool calls, API requests, errors | +| DIEM Cost Tracking | OFF | Track and display DIEM credit usage per session | +| Balance Awareness | OFF | Show Venice balance, warn when low | +| `@`-file References | OFF | `@filename` to attach file content to next message | +| `!` Shell Shortcut | OFF | `!command` to run shell and inject output into context | + +### Slash Commands (user-facing REPL commands) + +| Command | Default | Description | +|---------|---------|-------------| +| `/model` | ON | Switch Venice model interactively | +| `/new` | ON | Start a fresh conversation | +| `/help` | ON | List available commands | +| `/balance` | OFF | Show Venice API balance | +| `/compact` | OFF | Manually trigger context compaction | +| `/session` | OFF | Show session metadata and token usage | +| `/export` | OFF | Save conversation as Markdown | + +### Visual Customization + +**Input style** — how the prompt looks: + +| Style | Default | Description | +|-------|---------|-------------| +| `block` | ON | Full-width background box with `›` prompt, adapts to terminal theme | +| `bordered` | | Horizontal `─` lines above and below input | +| `plain` | | Simple `> ` readline prompt, no escape sequences | + +**Tool display** — how tool calls appear during execution: + +| Style | Default | Description | +|-------|---------|-------------| +| `grouped` | ON | Bold action labels with tree-branch output | +| `emoji` | | Per-call `⚡`/`✓` markers with args and timing | +| `minimal` | | Aggregated one-liner summaries | +| `hidden` | | No tool output | + +**Loader animation** — shown while waiting for model response: + +| Style | Default | Description | +|-------|---------|-------------| +| `spinner` | ON | Braille dot spinner (⠋⠙⠹…) to the left of the text | +| `gradient` | | Scrolling color shimmer over the loader text | +| `minimal` | | Trailing dots (`Working···`) | + +--- + +## Generation Workflow + +After getting checklist selections, follow this workflow: + +``` +- [ ] Generate package.json with dependencies +- [ ] Generate src/config.ts with Venice-specific settings +- [ ] Generate src/venice-client.ts (Venice API wrapper) +- [ ] Generate src/tools/index.ts wiring selected tools +- [ ] Generate selected tool files in src/tools/ +- [ ] Generate src/agent.ts (core runner with streaming) +- [ ] Generate selected harness modules +- [ ] Generate src/terminal-bg.ts (adaptive input background) +- [ ] Generate src/renderer.ts (tool display) +- [ ] Generate src/loader.ts (loader animation) +- [ ] If slash commands selected: generate src/commands.ts +- [ ] If ASCII Logo Banner is ON: generate src/banner.ts +- [ ] If DIEM tracking is ON: generate src/diem-tracker.ts +- [ ] Generate src/cli.ts entry point +- [ ] Generate .env.example with VENICE_API_KEY= +- [ ] Verify: run npx tsc --noEmit to check types +``` + +--- + +## Core Files + +### package.json + +```bash +npm init -y +npm pkg set type=module +npm pkg set scripts.start="tsx src/cli.ts" +npm pkg set scripts.dev="tsx watch src/cli.ts" +npm install zod +npm install -D tsx typescript @types/node +``` + +### tsconfig.json + +```json +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src"] +} +``` + +### src/config.ts + +```typescript +import { readFileSync, existsSync } from 'fs'; +import { resolve } from 'path'; + +export interface DisplayConfig { + toolDisplay: 'emoji' | 'grouped' | 'minimal' | 'hidden'; + reasoning: boolean; + inputStyle: 'block' | 'bordered' | 'plain'; + loader: { style: 'spinner' | 'gradient' | 'minimal'; text: string }; +} + +export interface VeniceConfig { + webSearch: 'off' | 'auto' | 'on'; + webCitations: boolean; + xSearch: boolean; + reasoningEffort: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'max'; +} + +export interface AgentConfig { + apiKey: string; + model: string; + systemPrompt: string; + maxTurns: number; + maxTokens: number; + sessionDir: string; + showBanner: boolean; + display: DisplayConfig; + venice: VeniceConfig; + slashCommands: boolean; +} + +// Venice model aliases for convenience +export const MODEL_ALIASES: Record = { + 'opus': 'claude-opus-4-6', + 'opus-4.7': 'claude-opus-4-7', + 'sonnet': 'claude-sonnet-4-6', + 'gpt5': 'gpt-5.5', + 'grok': 'grok-41-fast', + 'deepseek': 'deepseek-v4-pro', + 'llama': 'llama-4-maverick-17b-128e-instruct', + 'qwen': 'qwen3-235b-a22b', + 'mistral': 'mistral-large-2411', +}; + +const DEFAULTS: AgentConfig = { + apiKey: '', + model: 'claude-opus-4-6', + systemPrompt: [ + 'You are a coding assistant with access to tools for reading, writing, editing, and searching files, and running shell commands.', + '', + 'Current working directory: {cwd}', + '', + 'Guidelines:', + '- Use your tools proactively. Explore the codebase to find answers instead of asking the user.', + '- Keep working until the task is fully resolved before responding.', + '- Do not guess or make up information — use your tools to verify.', + '- Be concise and direct.', + '- Show file paths clearly when working with files.', + '- Prefer grep and glob tools over shell commands for file search.', + '- When editing code, make minimal targeted changes consistent with the existing style.', + ].join('\n'), + maxTurns: 20, + maxTokens: 16000, + sessionDir: '.sessions', + showBanner: false, + display: { + toolDisplay: 'grouped', + reasoning: false, + inputStyle: 'block', + loader: { style: 'spinner', text: 'Thinking' }, + }, + venice: { + webSearch: 'off', + webCitations: false, + xSearch: false, + reasoningEffort: 'medium', + }, + slashCommands: true, +}; + +export function loadConfig(overrides: Partial = {}): AgentConfig { + let config = { ...DEFAULTS }; + + const configPath = resolve('agent.config.json'); + if (existsSync(configPath)) { + const file = JSON.parse(readFileSync(configPath, 'utf-8')); + if (file.display) config.display = { ...config.display, ...file.display }; + if (file.venice) config.venice = { ...config.venice, ...file.venice }; + config = { ...config, ...file }; + } + + if (process.env.VENICE_API_KEY) config.apiKey = process.env.VENICE_API_KEY; + if (process.env.AGENT_MODEL) config.model = process.env.AGENT_MODEL; + if (process.env.AGENT_MAX_TURNS) config.maxTurns = Number(process.env.AGENT_MAX_TURNS); + if (process.env.AGENT_MAX_TOKENS) config.maxTokens = Number(process.env.AGENT_MAX_TOKENS); + + // Resolve model alias + if (MODEL_ALIASES[config.model]) { + config.model = MODEL_ALIASES[config.model]; + } + + config = { ...config, ...overrides }; + if (!config.apiKey) throw new Error('VENICE_API_KEY is required. Get one at venice.ai/settings/api'); + return config; +} +``` + +### src/venice-client.ts + +```typescript +import type { AgentConfig } from './config.js'; + +const VENICE_BASE_URL = 'https://api.venice.ai/api/v1'; + +export interface ChatMessage { + role: 'user' | 'assistant' | 'system'; + content: string | ContentPart[]; +} + +export interface ContentPart { + type: 'text' | 'image_url'; + text?: string; + image_url?: { url: string }; +} + +export interface Tool { + type: 'function'; + function: { + name: string; + description: string; + parameters: Record; + }; +} + +export interface StreamChunk { + type: 'text' | 'tool_call' | 'tool_call_delta' | 'done'; + content?: string; + tool_call?: { + id: string; + function: { name: string; arguments: string }; + }; + usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number }; +} + +export class VeniceClient { + private apiKey: string; + private config: AgentConfig; + + constructor(config: AgentConfig) { + this.apiKey = config.apiKey; + this.config = config; + } + + async *streamChat( + messages: ChatMessage[], + tools?: Tool[], + ): AsyncGenerator { + const body: Record = { + model: this.config.model, + messages, + stream: true, + max_tokens: this.config.maxTokens, + }; + + if (tools?.length) { + body.tools = tools; + body.tool_choice = 'auto'; + } + + // Venice-specific parameters + const veniceParams: Record = {}; + + if (this.config.venice.webSearch !== 'off') { + veniceParams.enable_web_search = this.config.venice.webSearch; + if (this.config.venice.webCitations) { + veniceParams.enable_web_citations = true; + } + } + + if (this.config.venice.xSearch) { + veniceParams.enable_x_search = true; + } + + if (this.config.venice.reasoningEffort !== 'medium') { + veniceParams.reasoning_effort = this.config.venice.reasoningEffort; + } + + if (Object.keys(veniceParams).length > 0) { + body.venice_parameters = veniceParams; + } + + const response = await fetch(`${VENICE_BASE_URL}/chat/completions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.apiKey}`, + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + const error = await response.text(); + throw new Error(`Venice API error: ${response.status} ${error}`); + } + + const reader = response.body?.getReader(); + if (!reader) throw new Error('No response body'); + + const decoder = new TextDecoder(); + let buffer = ''; + let toolCallBuffer: Record = {}; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + + for (const line of lines) { + if (!line.startsWith('data: ')) continue; + const data = line.slice(6); + if (data === '[DONE]') { + yield { type: 'done' }; + continue; + } + + try { + const parsed = JSON.parse(data); + const delta = parsed.choices?.[0]?.delta; + + if (delta?.content) { + yield { type: 'text', content: delta.content }; + } + + if (delta?.tool_calls) { + for (const tc of delta.tool_calls) { + const id = tc.id || Object.keys(toolCallBuffer)[tc.index]; + if (tc.id) { + toolCallBuffer[tc.id] = { name: tc.function?.name || '', args: '' }; + } + if (tc.function?.arguments) { + toolCallBuffer[id].args += tc.function.arguments; + } + if (tc.function?.name) { + toolCallBuffer[id].name = tc.function.name; + } + } + } + + if (parsed.usage) { + yield { type: 'done', usage: parsed.usage }; + } + } catch { + // Skip malformed JSON + } + } + } + + // Emit completed tool calls + for (const [id, { name, args }] of Object.entries(toolCallBuffer)) { + yield { + type: 'tool_call', + tool_call: { id, function: { name, arguments: args } }, + }; + } + } + + async getBalance(): Promise<{ credits: number; usd: number }> { + const response = await fetch(`${VENICE_BASE_URL}/api_keys/self`, { + headers: { 'Authorization': `Bearer ${this.apiKey}` }, + }); + if (!response.ok) throw new Error('Failed to fetch balance'); + const data = await response.json(); + return { + credits: data.balance_credits || 0, + usd: (data.balance_credits || 0) / 100, + }; + } + + async listModels(): Promise { + const response = await fetch(`${VENICE_BASE_URL}/models`, { + headers: { 'Authorization': `Bearer ${this.apiKey}` }, + }); + if (!response.ok) throw new Error('Failed to fetch models'); + const data = await response.json(); + return data.data?.map((m: { id: string }) => m.id) || []; + } +} +``` + +### src/agent.ts + +```typescript +import type { AgentConfig } from './config.js'; +import { VeniceClient, type ChatMessage, type Tool } from './venice-client.js'; +import { tools, executeToolCall, type ToolDefinition } from './tools/index.js'; + +export type AgentEvent = + | { type: 'text'; delta: string } + | { type: 'tool_call'; name: string; callId: string; args: Record } + | { type: 'tool_result'; name: string; callId: string; output: string; error?: boolean } + | { type: 'reasoning'; delta: string } + | { type: 'done'; usage?: { prompt_tokens: number; completion_tokens: number } }; + +export async function runAgent( + config: AgentConfig, + messages: ChatMessage[], + options?: { onEvent?: (event: AgentEvent) => void; signal?: AbortSignal }, +): Promise<{ text: string; messages: ChatMessage[]; usage: { prompt: number; completion: number } }> { + const client = new VeniceClient(config); + const conversationMessages = [...messages]; + let totalUsage = { prompt: 0, completion: 0 }; + let finalText = ''; + + // Add system prompt if not present + if (!conversationMessages.find(m => m.role === 'system')) { + conversationMessages.unshift({ + role: 'system', + content: config.systemPrompt.replace('{cwd}', process.cwd()), + }); + } + + // Convert tools to OpenAI format + const openaiTools: Tool[] = tools.map(t => ({ + type: 'function', + function: { + name: t.name, + description: t.description, + parameters: t.parameters, + }, + })); + + for (let turn = 0; turn < config.maxTurns; turn++) { + if (options?.signal?.aborted) break; + + let turnText = ''; + const toolCalls: Array<{ id: string; name: string; args: Record }> = []; + + for await (const chunk of client.streamChat(conversationMessages, openaiTools)) { + if (options?.signal?.aborted) break; + + if (chunk.type === 'text' && chunk.content) { + turnText += chunk.content; + options?.onEvent?.({ type: 'text', delta: chunk.content }); + } + + if (chunk.type === 'tool_call' && chunk.tool_call) { + const tc = chunk.tool_call; + let args: Record = {}; + try { + args = JSON.parse(tc.function.arguments); + } catch { + args = {}; + } + toolCalls.push({ id: tc.id, name: tc.function.name, args }); + options?.onEvent?.({ type: 'tool_call', name: tc.function.name, callId: tc.id, args }); + } + + if (chunk.type === 'done' && chunk.usage) { + totalUsage.prompt += chunk.usage.prompt_tokens; + totalUsage.completion += chunk.usage.completion_tokens; + } + } + + // If there's text, add assistant message + if (turnText) { + finalText = turnText; + conversationMessages.push({ role: 'assistant', content: turnText }); + } + + // If no tool calls, we're done + if (toolCalls.length === 0) { + options?.onEvent?.({ type: 'done', usage: totalUsage }); + break; + } + + // Execute tool calls + const toolResults: ChatMessage[] = []; + for (const tc of toolCalls) { + const result = await executeToolCall(tc.name, tc.args); + const output = typeof result === 'string' ? result : JSON.stringify(result); + const isError = typeof result === 'object' && 'error' in result; + + options?.onEvent?.({ + type: 'tool_result', + name: tc.name, + callId: tc.id, + output: output.length > 200 ? output.slice(0, 200) + '…' : output, + error: isError, + }); + + toolResults.push({ + role: 'assistant', + content: `Tool ${tc.name} result: ${output}`, + }); + } + + conversationMessages.push(...toolResults); + } + + return { text: finalText, messages: conversationMessages, usage: totalUsage }; +} + +export async function runAgentWithRetry( + config: AgentConfig, + messages: ChatMessage[], + options?: { onEvent?: (event: AgentEvent) => void; signal?: AbortSignal; maxRetries?: number }, +) { + for (let attempt = 0, max = options?.maxRetries ?? 3; attempt <= max; attempt++) { + try { + return await runAgent(config, messages, options); + } catch (err: any) { + const status = err?.status || err?.statusCode; + if (!(status === 429 || (status >= 500 && status < 600)) || attempt === max) throw err; + await new Promise(r => setTimeout(r, Math.min(1000 * 2 ** attempt, 30000))); + } + } + throw new Error('Unreachable'); +} +``` + +### src/tools/index.ts + +```typescript +import { z } from 'zod'; +import { fileReadTool } from './file-read.js'; +import { fileWriteTool } from './file-write.js'; +import { fileEditTool } from './file-edit.js'; +import { globTool } from './glob.js'; +import { grepTool } from './grep.js'; +import { listDirTool } from './list-dir.js'; +import { shellTool } from './shell.js'; + +export interface ToolDefinition { + name: string; + description: string; + parameters: Record; + execute: (args: Record) => Promise; +} + +export const tools: ToolDefinition[] = [ + fileReadTool, + fileWriteTool, + fileEditTool, + globTool, + grepTool, + listDirTool, + shellTool, +]; + +export async function executeToolCall( + name: string, + args: Record, +): Promise { + const tool = tools.find(t => t.name === name); + if (!tool) { + return { error: `Unknown tool: ${name}` }; + } + try { + return await tool.execute(args); + } catch (err: any) { + return { error: err.message || String(err) }; + } +} +``` + +### src/tools/file-read.ts + +```typescript +import { readFile } from 'fs/promises'; + +export const fileReadTool = { + name: 'file_read', + description: 'Read the contents of a file at the given path', + parameters: { + type: 'object', + properties: { + path: { type: 'string', description: 'Absolute or relative path to the file' }, + offset: { type: 'number', description: 'Start reading from this line (1-indexed)' }, + limit: { type: 'number', description: 'Maximum number of lines to return' }, + }, + required: ['path'], + }, + execute: async ({ path, offset, limit }: { path: string; offset?: number; limit?: number }) => { + try { + const content = await readFile(path, 'utf-8'); + const lines = content.split('\n'); + + const start = offset ? offset - 1 : 0; + const end = limit ? start + limit : lines.length; + const slice = lines.slice(start, end); + + return { + content: slice.join('\n'), + totalLines: lines.length, + ...(end < lines.length && { truncated: true, nextOffset: end + 1 }), + }; + } catch (err: any) { + if (err.code === 'ENOENT') return { error: `File not found: ${path}` }; + if (err.code === 'EACCES') return { error: `Permission denied: ${path}` }; + return { error: err.message }; + } + }, +}; +``` + +--- + +## Venice-Specific Features + +### Web Search Integration + +Enable web search for real-time information: + +```typescript +// In config +venice: { + webSearch: 'auto', // 'off' | 'auto' | 'on' + webCitations: true, // Include source citations +} +``` + +### X/Twitter Search (Grok models only) + +```typescript +// In config — requires a Grok model +model: 'grok-41-fast', +venice: { + xSearch: true, +} +``` + +### Reasoning Effort Control + +```typescript +// In config +venice: { + reasoningEffort: 'high', // 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'max' +} +``` + +### DIEM Cost Tracking Module + +When enabled, track credit usage: + +```typescript +// src/diem-tracker.ts +export class DiemTracker { + private sessionCredits = 0; + + addUsage(promptTokens: number, completionTokens: number, model: string) { + // Approximate credit calculation (varies by model) + const rate = this.getModelRate(model); + const credits = (promptTokens * rate.input + completionTokens * rate.output) / 1000; + this.sessionCredits += credits; + } + + getSessionCost(): string { + return `${this.sessionCredits.toFixed(4)} credits (~$${(this.sessionCredits / 100).toFixed(4)})`; + } + + private getModelRate(model: string) { + // Simplified rates — check venice.ai/pricing for current rates + if (model.includes('opus')) return { input: 15, output: 75 }; + if (model.includes('sonnet')) return { input: 3, output: 15 }; + if (model.includes('gpt-5')) return { input: 5, output: 15 }; + return { input: 1, output: 3 }; // Default for smaller models + } +} +``` + +--- + +## Sample Project Structure + +With all defaults selected: + +``` +my-venice-agent/ +├── package.json +├── tsconfig.json +├── .env.example # VENICE_API_KEY= +├── agent.config.json # Optional config overrides +└── src/ + ├── config.ts # Layered config (defaults → file → env) + ├── venice-client.ts # Venice API wrapper with streaming + ├── agent.ts # Core runner with tool loop + ├── cli.ts # Interactive REPL + ├── session.ts # JSONL conversation persistence + ├── terminal-bg.ts # Adaptive background detection + ├── renderer.ts # Tool display renderer + ├── loader.ts # Loader animation + └── tools/ + ├── index.ts # Tool registry + ├── file-read.ts + ├── file-write.ts + ├── file-edit.ts + ├── glob.ts + ├── grep.ts + ├── list-dir.ts + └── shell.ts +``` + +--- + +## Quick Start + +```bash +cd my-venice-agent +npm install +VENICE_API_KEY=vn_your_key npm start +``` + +Or with options: + +```bash +npm start -- --model deepseek --input bordered --tool-display emoji +``` + +--- + +## Why Venice for Agent TUIs? + +1. **Privacy** — Zero data retention on private models. Your agent's conversations stay yours. +2. **Model diversity** — Switch between Claude, GPT, Grok, Llama, DeepSeek in one API. +3. **Built-in search** — Web and X search without external API keys. +4. **DIEM economics** — Pay-as-you-go credits that can be resold. Profitable agent operation. +5. **Uncensored models** — Venice's own models work for legitimate use cases others refuse. + +--- + +*Built for the Venice AI ecosystem. Not financial advice. Check venice.ai/pricing for current model rates.* diff --git a/skills/create-agent-tui/references/modules.md b/skills/create-agent-tui/references/modules.md new file mode 100644 index 0000000..85673c9 --- /dev/null +++ b/skills/create-agent-tui/references/modules.md @@ -0,0 +1,209 @@ +# Harness Modules Reference + +Architectural components that can be added to the agent harness. + +--- + +## Session Persistence + +JSONL append-only conversation log for persistence and export. + +```typescript +// src/session.ts +export class SessionManager { + constructor(sessionDir: string); + async init(): Promise; + async appendMessage(message: ChatMessage): Promise; + async appendToolCall(name: string, callId: string, args: unknown): Promise; + async appendToolResult(name: string, callId: string, result: unknown, durationMs: number): Promise; + async getMessages(): Promise; + async exportMarkdown(): Promise; + getSessionId(): string; + getSessionFile(): string; +} +``` + +**File format:** Each line is a JSON object with `timestamp`, `type`, and `data`. + +--- + +## Context Compaction + +Summarizes older messages when context gets too long. + +```typescript +// src/compaction.ts +export interface CompactionConfig { + maxTokens: number; // Trigger compaction above this + keepRecentMessages: number; // Messages to preserve uncompacted + summaryModel: string; // Model for generating summaries +} + +export async function compactConversation( + messages: ChatMessage[], + config: CompactionConfig, + client: VeniceClient, +): Promise; +``` + +**Strategy:** +1. Count tokens in conversation +2. If above threshold, take older messages +3. Generate a summary using a fast model +4. Replace old messages with summary message + +--- + +## System Prompt Composition + +Assembles system prompt from static and dynamic context. + +```typescript +// src/system-prompt.ts +export interface SystemPromptConfig { + base: string; // Base instructions + tools?: string; // Tool usage guidelines + context?: string; // Dynamic context (cwd, date, etc.) + project?: string; // Project-specific instructions +} + +export function buildSystemPrompt(config: SystemPromptConfig): string { + return [ + config.base, + config.tools && `\n## Tools\n${config.tools}`, + config.context && `\n## Context\n${config.context}`, + config.project && `\n## Project\n${config.project}`, + ].filter(Boolean).join('\n'); +} +``` + +--- + +## Tool Permissions / Approval + +Gates dangerous tools behind user confirmation. + +```typescript +// src/approval.ts +export interface ToolMetadata { + name: string; + category: 'read-only' | 'write' | 'destructive' | 'network'; + requiresApproval: boolean; +} + +export const TOOL_METADATA: Record = { + file_read: { name: 'file_read', category: 'read-only', requiresApproval: false }, + file_write: { name: 'file_write', category: 'write', requiresApproval: true }, + shell: { name: 'shell', category: 'destructive', requiresApproval: true }, + // ... +}; + +export async function requestApproval( + toolName: string, + args: Record, +): Promise; +``` + +--- + +## Structured Event Logging + +Emits events for tool calls, API requests, errors. + +```typescript +// src/logger.ts +export type LogEvent = + | { type: 'api_request'; model: string; tokens: number; latencyMs: number } + | { type: 'tool_call'; name: string; args: unknown; durationMs: number } + | { type: 'tool_error'; name: string; error: string } + | { type: 'session_start'; sessionId: string } + | { type: 'session_end'; totalTokens: number; totalCost: number }; + +export class EventLogger { + constructor(logFile?: string); + emit(event: LogEvent): void; + flush(): Promise; +} +``` + +--- + +## DIEM Cost Tracking + +Tracks Venice credit usage per session. + +```typescript +// src/diem-tracker.ts +export class DiemTracker { + private sessionCredits = 0; + + addUsage(promptTokens: number, completionTokens: number, model: string): void; + getSessionCost(): string; + getSessionCredits(): number; + + private getModelRate(model: string): { input: number; output: number }; +} +``` + +--- + +## Balance Awareness + +Shows Venice balance, warns when low. + +```typescript +// In CLI startup +const client = new VeniceClient(config); +const balance = await client.getBalance(); + +if (balance.usd < 1) { + console.log(`${YELLOW}⚠ Low balance: $${balance.usd.toFixed(2)}${RESET}`); +} +``` + +--- + +## @-file References + +Allows `@filename` syntax to attach file content. + +```typescript +// src/file-refs.ts +export async function expandFileRefs(input: string): Promise { + const regex = /@([\w\-\.\/]+)/g; + let result = input; + + for (const match of input.matchAll(regex)) { + const filePath = match[1]; + try { + const content = await readFile(filePath, 'utf-8'); + result = result.replace(match[0], `\n\n${content}\n\n`); + } catch { + // Leave as-is if file doesn't exist + } + } + + return result; +} +``` + +--- + +## ! Shell Shortcut + +Allows `!command` to run shell and inject output. + +```typescript +// In input handler +if (input.startsWith('!')) { + const command = input.slice(1); + const { stdout, stderr } = await exec(command); + const output = stdout || stderr; + + // Inject into context + messages.push({ + role: 'user', + content: `Shell command: ${command}\n\nOutput:\n${output}`, + }); +} +``` diff --git a/skills/create-agent-tui/references/tools.md b/skills/create-agent-tui/references/tools.md new file mode 100644 index 0000000..3853460 --- /dev/null +++ b/skills/create-agent-tui/references/tools.md @@ -0,0 +1,263 @@ +# Tool Reference + +All user-defined tools follow a consistent pattern. This document provides complete specs for each tool. + +## Tool Interface + +```typescript +export interface ToolDefinition { + name: string; + description: string; + parameters: { + type: 'object'; + properties: Record; + required: string[]; + }; + execute: (args: any) => Promise; +} +``` + +--- + +## File Read + +Reads file contents with optional offset/limit for large files. + +```typescript +export const fileReadTool = { + name: 'file_read', + description: 'Read the contents of a file at the given path', + parameters: { + type: 'object', + properties: { + path: { type: 'string', description: 'Path to the file' }, + offset: { type: 'number', description: 'Start line (1-indexed)' }, + limit: { type: 'number', description: 'Max lines to return' }, + }, + required: ['path'], + }, + execute: async ({ path, offset, limit }) => { + // Returns: { content, totalLines, truncated?, nextOffset? } + }, +}; +``` + +--- + +## File Write + +Creates or overwrites files, auto-creating parent directories. + +```typescript +export const fileWriteTool = { + name: 'file_write', + description: 'Write content to a file, creating directories if needed', + parameters: { + type: 'object', + properties: { + path: { type: 'string', description: 'Path to write to' }, + content: { type: 'string', description: 'Content to write' }, + }, + required: ['path', 'content'], + }, + execute: async ({ path, content }) => { + // Returns: { success, path, lines } + }, +}; +``` + +--- + +## File Edit + +Search-and-replace with exact matching and diff validation. + +```typescript +export const fileEditTool = { + name: 'file_edit', + description: 'Edit a file by replacing exact text', + parameters: { + type: 'object', + properties: { + path: { type: 'string', description: 'Path to file' }, + oldText: { type: 'string', description: 'Exact text to find' }, + newText: { type: 'string', description: 'Replacement text' }, + }, + required: ['path', 'oldText', 'newText'], + }, + execute: async ({ path, oldText, newText }) => { + // Returns: { success, linesRemoved, linesAdded, diffPreview } + // Errors if oldText not found or found multiple times + }, +}; +``` + +--- + +## Shell/Bash + +Executes shell commands with timeout and output capture. + +```typescript +export const shellTool = { + name: 'shell', + description: 'Execute a shell command', + parameters: { + type: 'object', + properties: { + command: { type: 'string', description: 'Command to execute' }, + cwd: { type: 'string', description: 'Working directory' }, + timeout: { type: 'number', description: 'Timeout in ms' }, + }, + required: ['command'], + }, + execute: async ({ command, cwd, timeout }) => { + // Returns: { stdout, stderr, exitCode } + }, +}; +``` + +--- + +## Glob/Find + +Finds files by glob pattern. + +```typescript +export const globTool = { + name: 'glob', + description: 'Find files matching a glob pattern', + parameters: { + type: 'object', + properties: { + pattern: { type: 'string', description: 'Glob pattern (e.g., **/*.ts)' }, + cwd: { type: 'string', description: 'Base directory' }, + ignore: { type: 'array', description: 'Patterns to ignore' }, + }, + required: ['pattern'], + }, + execute: async ({ pattern, cwd, ignore }) => { + // Returns: { files, count } + }, +}; +``` + +--- + +## Grep/Search + +Searches file contents by regex pattern. + +```typescript +export const grepTool = { + name: 'grep', + description: 'Search for a pattern in files', + parameters: { + type: 'object', + properties: { + pattern: { type: 'string', description: 'Regex pattern' }, + path: { type: 'string', description: 'File or glob to search' }, + cwd: { type: 'string', description: 'Base directory' }, + }, + required: ['pattern', 'path'], + }, + execute: async ({ pattern, path, cwd }) => { + // Returns: { matches: [{ file, line, content }], total } + }, +}; +``` + +--- + +## List Directory + +Lists directory contents with file types and sizes. + +```typescript +export const listDirTool = { + name: 'list_dir', + description: 'List directory contents', + parameters: { + type: 'object', + properties: { + path: { type: 'string', description: 'Directory path' }, + showHidden: { type: 'boolean', description: 'Include hidden files' }, + }, + required: ['path'], + }, + execute: async ({ path, showHidden }) => { + // Returns: { path, count, entries: [{ name, type, size? }] } + }, +}; +``` + +--- + +## JS REPL (Optional) + +Persistent Node.js environment for code execution. + +```typescript +export const jsReplTool = { + name: 'js_repl', + description: 'Execute JavaScript in a persistent Node.js environment', + parameters: { + type: 'object', + properties: { + code: { type: 'string', description: 'JavaScript code to execute' }, + }, + required: ['code'], + }, + execute: async ({ code }) => { + // Returns: { result, console: [] } + }, +}; +``` + +--- + +## Web Fetch (Optional) + +Fetches and extracts text from web pages. + +```typescript +export const webFetchTool = { + name: 'web_fetch', + description: 'Fetch and extract text from a URL', + parameters: { + type: 'object', + properties: { + url: { type: 'string', description: 'URL to fetch' }, + maxChars: { type: 'number', description: 'Max characters to return' }, + }, + required: ['url'], + }, + execute: async ({ url, maxChars }) => { + // Returns: { title, content, truncated? } + }, +}; +``` + +--- + +## Custom Tool Template + +Empty skeleton for domain-specific tools. + +```typescript +export const customTool = { + name: 'my_tool', + description: 'Description of what this tool does', + parameters: { + type: 'object', + properties: { + // Add your parameters here + }, + required: [], + }, + execute: async (args) => { + // Implement your tool logic here + return { result: 'success' }; + }, +}; +``` diff --git a/skills/create-agent-tui/references/venice-features.md b/skills/create-agent-tui/references/venice-features.md new file mode 100644 index 0000000..1b84191 --- /dev/null +++ b/skills/create-agent-tui/references/venice-features.md @@ -0,0 +1,196 @@ +# Venice-Specific Features Reference + +Venice AI provides unique features not available on other platforms. + +--- + +## Model Diversity + +Venice offers 30+ models through a single API: + +### Claude Models +- `claude-opus-4-7` — Latest Opus, best for complex tasks +- `claude-opus-4-6` — Previous Opus, very capable +- `claude-sonnet-4-6` — Fast and efficient + +### OpenAI Models +- `gpt-5.5` — Latest GPT, strong coding +- `gpt-4o` — Multimodal GPT-4 + +### Grok Models (with X search) +- `grok-41-fast` — Fast Grok with X/Twitter search +- `grok-4-20-beta` — Latest Grok beta + +### Open Source +- `llama-4-maverick-17b-128e-instruct` — Llama 4 with 10M context +- `deepseek-v4-pro` — Best coding benchmarks +- `qwen3-235b-a22b` — Qwen 3 large +- `mistral-large-2411` — Mistral Large + +--- + +## Web Search + +Venice models can search the web in real-time. + +```typescript +// Enable in config +venice: { + webSearch: 'auto', // 'off' | 'auto' | 'on' + webCitations: true, // Include source URLs +} + +// Or via API parameter +venice_parameters: { + enable_web_search: 'auto', + enable_web_citations: true, +} +``` + +**Modes:** +- `off` — No web search +- `auto` — Model decides when to search +- `on` — Always search + +--- + +## X/Twitter Search + +Grok models can search X/Twitter in real-time. + +```typescript +// Requires a Grok model +model: 'grok-41-fast', +venice: { + xSearch: true, +} + +// Via API +venice_parameters: { + enable_x_search: true, +} +``` + +--- + +## Reasoning Effort + +Control how much the model "thinks" before responding. + +```typescript +venice: { + reasoningEffort: 'high', // 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'max' +} + +// Via API +venice_parameters: { + reasoning_effort: 'high', +} +``` + +Higher effort = more thorough reasoning, higher cost, slower response. + +--- + +## Privacy Modes + +Venice offers different privacy levels: + +- **Private models** — Zero data retention, your data is never stored +- **Pro models** — Standard models with Venice's privacy protections +- **Uncensored models** — Venice's own models without content filters + +Check model capabilities at venice.ai/models. + +--- + +## DIEM Token Integration + +DIEM provides permanent access to Venice inference. + +**How it works:** +1. Buy DIEM tokens on-chain +2. Each DIEM generates $1/day of inference credits +3. Credits never expire, DIEM can be resold + +**Cost tracking:** +```typescript +// Approximate rates (check venice.ai/pricing) +const RATES = { + 'claude-opus': { input: 15, output: 75 }, // per 1M tokens + 'claude-sonnet': { input: 3, output: 15 }, + 'gpt-5.5': { input: 5, output: 15 }, + 'grok': { input: 2, output: 8 }, + 'deepseek': { input: 0.5, output: 2 }, +}; +``` + +--- + +## Image Generation + +Venice supports multiple image models: + +```typescript +const response = await fetch('https://api.venice.ai/api/v1/images/generations', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + model: 'flux-pro', // or 'dall-e-3', 'stable-diffusion-xl' + prompt: 'A futuristic city at sunset', + size: '1024x1024', + }), +}); +``` + +--- + +## Text-to-Speech + +60+ voices via Kokoro TTS: + +```typescript +const response = await fetch('https://api.venice.ai/api/v1/audio/speech', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + model: 'kokoro', + input: 'Hello, world!', + voice: 'af_sarah', // See docs for all voices + }), +}); +``` + +--- + +## API Endpoints + +| Endpoint | Description | +|----------|-------------| +| `/api/v1/chat/completions` | Chat completion (OpenAI-compatible) | +| `/api/v1/models` | List available models | +| `/api/v1/images/generations` | Generate images | +| `/api/v1/audio/speech` | Text-to-speech | +| `/api/v1/audio/transcriptions` | Speech-to-text | +| `/api/v1/embeddings` | Text embeddings | +| `/api/v1/api_keys/self` | Check balance | + +Base URL: `https://api.venice.ai` + +--- + +## Rate Limits + +Venice has generous rate limits: + +- **Free tier:** 100 requests/minute +- **Pro tier:** 1000 requests/minute +- **Enterprise:** Custom limits + +Use exponential backoff for 429 errors. diff --git a/skills/create-agent-tui/sample/.env.example b/skills/create-agent-tui/sample/.env.example new file mode 100644 index 0000000..d9a8cb8 --- /dev/null +++ b/skills/create-agent-tui/sample/.env.example @@ -0,0 +1,9 @@ +# Get your API key at https://venice.ai/settings/api +VENICE_API_KEY=vn_your_key_here + +# Optional: Override default model +# AGENT_MODEL=claude-opus-4-7 + +# Optional: Adjust limits +# AGENT_MAX_TURNS=20 +# AGENT_MAX_TOKENS=16000 diff --git a/skills/create-agent-tui/sample/package-lock.json b/skills/create-agent-tui/sample/package-lock.json new file mode 100644 index 0000000..572734f --- /dev/null +++ b/skills/create-agent-tui/sample/package-lock.json @@ -0,0 +1,820 @@ +{ + "name": "venice-agent-tui-sample", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "venice-agent-tui-sample", + "version": "1.0.0", + "dependencies": { + "glob": "^11.0.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.19.0", + "typescript": "^5.6.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "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/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "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.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "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.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "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/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.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 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/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.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.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "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==", + "dev": true, + "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/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/skills/create-agent-tui/sample/package.json b/skills/create-agent-tui/sample/package.json new file mode 100644 index 0000000..e841004 --- /dev/null +++ b/skills/create-agent-tui/sample/package.json @@ -0,0 +1,18 @@ +{ + "name": "venice-agent-tui-sample", + "version": "1.0.0", + "type": "module", + "scripts": { + "start": "tsx src/cli.ts", + "dev": "tsx watch src/cli.ts" + }, + "dependencies": { + "glob": "^11.0.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.19.0", + "typescript": "^5.6.0" + } +} diff --git a/skills/create-agent-tui/sample/src/agent.ts b/skills/create-agent-tui/sample/src/agent.ts new file mode 100644 index 0000000..6572398 --- /dev/null +++ b/skills/create-agent-tui/sample/src/agent.ts @@ -0,0 +1,131 @@ +import type { AgentConfig } from './config.js'; +import { VeniceClient, type ChatMessage, type Tool, type ToolCall } from './venice-client.js'; +import { tools, executeToolCall, type ToolDefinition } from './tools/index.js'; + +export type AgentEvent = + | { type: 'text'; delta: string } + | { type: 'tool_call'; name: string; callId: string; args: Record } + | { type: 'tool_result'; name: string; callId: string; output: string; error?: boolean } + | { type: 'done'; usage?: { prompt: number; completion: number } }; + +export async function runAgent( + config: AgentConfig, + messages: ChatMessage[], + options?: { onEvent?: (event: AgentEvent) => void; signal?: AbortSignal }, +): Promise<{ text: string; messages: ChatMessage[]; usage: { prompt: number; completion: number } }> { + const client = new VeniceClient(config); + const conversationMessages = [...messages]; + let totalUsage = { prompt: 0, completion: 0 }; + let finalText = ''; + + // Add system prompt if not present + if (!conversationMessages.find(m => m.role === 'system')) { + conversationMessages.unshift({ + role: 'system', + content: config.systemPrompt.replace('{cwd}', process.cwd()), + }); + } + + // Convert tools to OpenAI format + const openaiTools: Tool[] = tools.map(t => ({ + type: 'function', + function: { + name: t.name, + description: t.description, + parameters: t.parameters, + }, + })); + + for (let turn = 0; turn < config.maxTurns; turn++) { + if (options?.signal?.aborted) break; + + let turnText = ''; + let toolCalls: ToolCall[] = []; + + for await (const chunk of client.streamChat(conversationMessages, openaiTools)) { + if (options?.signal?.aborted) break; + + if (chunk.type === 'text' && chunk.content) { + turnText += chunk.content; + options?.onEvent?.({ type: 'text', delta: chunk.content }); + } + + if (chunk.type === 'tool_call' && chunk.tool_calls) { + toolCalls = chunk.tool_calls; + for (const tc of toolCalls) { + let args: Record = {}; + try { args = JSON.parse(tc.function.arguments); } catch {} + options?.onEvent?.({ type: 'tool_call', name: tc.function.name, callId: tc.id, args }); + } + } + + if (chunk.type === 'done' && chunk.usage) { + totalUsage.prompt += chunk.usage.prompt_tokens; + totalUsage.completion += chunk.usage.completion_tokens; + } + } + + // Add assistant message with text and/or tool calls + if (turnText || toolCalls.length > 0) { + const assistantMsg: ChatMessage = { + role: 'assistant', + content: turnText || '', + }; + if (toolCalls.length > 0) { + assistantMsg.tool_calls = toolCalls; + } + conversationMessages.push(assistantMsg); + if (turnText) finalText = turnText; + } + + // If no tool calls, we're done + if (toolCalls.length === 0) { + options?.onEvent?.({ type: 'done', usage: totalUsage }); + break; + } + + // Execute tool calls and add results + for (const tc of toolCalls) { + let args: Record = {}; + try { args = JSON.parse(tc.function.arguments); } catch {} + + const result = await executeToolCall(tc.function.name, args); + const output = typeof result === 'string' ? result : JSON.stringify(result); + const isError = typeof result === 'object' && result !== null && 'error' in result; + + options?.onEvent?.({ + type: 'tool_result', + name: tc.function.name, + callId: tc.id, + output: output.length > 500 ? output.slice(0, 500) + '…' : output, + error: isError, + }); + + conversationMessages.push({ + role: 'tool', + tool_call_id: tc.id, + content: output, + }); + } + } + + options?.onEvent?.({ type: 'done', usage: totalUsage }); + return { text: finalText, messages: conversationMessages, usage: totalUsage }; +} + +export async function runAgentWithRetry( + config: AgentConfig, + messages: ChatMessage[], + options?: { onEvent?: (event: AgentEvent) => void; signal?: AbortSignal; maxRetries?: number }, +) { + for (let attempt = 0, max = options?.maxRetries ?? 3; attempt <= max; attempt++) { + try { + return await runAgent(config, messages, options); + } catch (err: any) { + const status = err?.status || err?.statusCode; + if (!(status === 429 || (status >= 500 && status < 600)) || attempt === max) throw err; + await new Promise(r => setTimeout(r, Math.min(1000 * 2 ** attempt, 30000))); + } + } + throw new Error('Unreachable'); +} diff --git a/skills/create-agent-tui/sample/src/banner.ts b/skills/create-agent-tui/sample/src/banner.ts new file mode 100644 index 0000000..ffed50a --- /dev/null +++ b/skills/create-agent-tui/sample/src/banner.ts @@ -0,0 +1,124 @@ +/** + * ASCII banner generator for the agent TUI. + * Generates block-letter ASCII art for project names. + */ + +const RESET = '\x1b[0m'; +const BOLD = '\x1b[1m'; +const DIM = '\x1b[2m'; +const CYAN = '\x1b[36m'; +const MAGENTA = '\x1b[35m'; + +// Simple 5-line block letter font +const LETTERS: Record = { + A: ['█████', '█ █', '█████', '█ █', '█ █'], + B: ['████ ', '█ █', '████ ', '█ █', '████ '], + C: ['█████', '█ ', '█ ', '█ ', '█████'], + D: ['████ ', '█ █', '█ █', '█ █', '████ '], + E: ['█████', '█ ', '████ ', '█ ', '█████'], + F: ['█████', '█ ', '████ ', '█ ', '█ '], + G: ['█████', '█ ', '█ ██', '█ █', '█████'], + H: ['█ █', '█ █', '█████', '█ █', '█ █'], + I: ['█████', ' █ ', ' █ ', ' █ ', '█████'], + J: ['█████', ' █ ', ' █ ', '█ █ ', '████ '], + K: ['█ █', '█ █ ', '███ ', '█ █ ', '█ █'], + L: ['█ ', '█ ', '█ ', '█ ', '█████'], + M: ['█ █', '██ ██', '█ █ █', '█ █', '█ █'], + N: ['█ █', '██ █', '█ █ █', '█ ██', '█ █'], + O: ['█████', '█ █', '█ █', '█ █', '█████'], + P: ['█████', '█ █', '█████', '█ ', '█ '], + Q: ['█████', '█ █', '█ █ █', '█ █ ', '████ '], + R: ['█████', '█ █', '█████', '█ █ ', '█ █'], + S: ['█████', '█ ', '█████', ' █', '█████'], + T: ['█████', ' █ ', ' █ ', ' █ ', ' █ '], + U: ['█ █', '█ █', '█ █', '█ █', '█████'], + V: ['█ █', '█ █', '█ █', ' █ █ ', ' █ '], + W: ['█ █', '█ █', '█ █ █', '██ ██', '█ █'], + X: ['█ █', ' █ █ ', ' █ ', ' █ █ ', '█ █'], + Y: ['█ █', ' █ █ ', ' █ ', ' █ ', ' █ '], + Z: ['█████', ' █ ', ' █ ', ' █ ', '█████'], + ' ': [' ', ' ', ' ', ' ', ' '], + '0': ['█████', '█ █', '█ █', '█ █', '█████'], + '1': [' █ ', '██ ', ' █ ', ' █ ', '█████'], + '2': ['█████', ' █', '█████', '█ ', '█████'], + '3': ['█████', ' █', '█████', ' █', '█████'], + '4': ['█ █', '█ █', '█████', ' █', ' █'], + '5': ['█████', '█ ', '█████', ' █', '█████'], + '6': ['█████', '█ ', '█████', '█ █', '█████'], + '7': ['█████', ' █', ' █ ', ' █ ', ' █ '], + '8': ['█████', '█ █', '█████', '█ █', '█████'], + '9': ['█████', '█ █', '█████', ' █', '█████'], +}; + +/** + * Generate ASCII art for a text string. + */ +export function generateAsciiArt(text: string, maxWidth = 60): string[] { + const chars = text.toUpperCase().split(''); + const lines: string[][] = [[], [], [], [], []]; + + let currentWidth = 0; + + for (const char of chars) { + const letter = LETTERS[char] || LETTERS[' ']; + const letterWidth = letter[0].length + 1; // +1 for spacing + + if (currentWidth + letterWidth > maxWidth) { + break; // Don't exceed max width + } + + for (let i = 0; i < 5; i++) { + lines[i].push(letter[i]); + } + currentWidth += letterWidth; + } + + return lines.map(line => line.join(' ')); +} + +/** + * Print a styled banner with ASCII art. + */ +export function printBanner(projectName: string, model: string, version = '1.0.0'): void { + const art = generateAsciiArt(projectName); + + console.log(''); + for (const line of art) { + console.log(` ${MAGENTA}${BOLD}${line}${RESET}`); + } + console.log(''); + console.log(` ${DIM}model${RESET} ${CYAN}${model}${RESET}`); + console.log(` ${DIM}version${RESET} ${version}`); + console.log(''); +} + +/** + * Print a simple text banner (fallback when ASCII art is disabled). + */ +export function printTextBanner(projectName: string, model: string, version = '1.0.0'): void { + const width = Math.min(process.stdout.columns || 60, 60); + const line = '─'.repeat(width); + + console.log(`\n${DIM}${line}${RESET}`); + console.log(` ${BOLD}${projectName}${RESET} ${DIM}v${version}${RESET}`); + console.log(` ${DIM}model${RESET} ${CYAN}${model}${RESET}`); + console.log(`${DIM}${line}${RESET}\n`); +} + +// Venice ASCII art logo +const VENICE_LOGO = ` + ██╗ ██╗███████╗███╗ ██╗██╗ ██████╗███████╗ + ██║ ██║██╔════╝████╗ ██║██║██╔════╝██╔════╝ + ██║ ██║█████╗ ██╔██╗ ██║██║██║ █████╗ + ╚██╗ ██╔╝██╔══╝ ██║╚██╗██║██║██║ ██╔══╝ + ╚████╔╝ ███████╗██║ ╚████║██║╚██████╗███████╗ + ╚═══╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═════╝╚══════╝`; + +/** + * Print the Venice logo banner. + */ +export function printVeniceBanner(model: string): void { + console.log(`${CYAN}${BOLD}${VENICE_LOGO}${RESET}`); + console.log(`\n ${DIM}model${RESET} ${MAGENTA}${model}${RESET}`); + console.log(` ${DIM}Privacy-first AI inference${RESET}\n`); +} diff --git a/skills/create-agent-tui/sample/src/cli.ts b/skills/create-agent-tui/sample/src/cli.ts new file mode 100644 index 0000000..b8e2d61 --- /dev/null +++ b/skills/create-agent-tui/sample/src/cli.ts @@ -0,0 +1,319 @@ +#!/usr/bin/env node +import { createInterface } from 'readline'; +import { parseArgs } from 'util'; +import { loadConfig, MODEL_ALIASES, type AgentConfig } from './config.js'; +import { runAgentWithRetry, type AgentEvent } from './agent.js'; +import { SessionManager } from './session.js'; +import { ToolRenderer, type ToolDisplayStyle } from './renderer.js'; +import { Loader, type LoaderStyle } from './loader.js'; +import { printBanner, printTextBanner, printVeniceBanner } from './banner.js'; +import { detectBg, getDefaultInputBg } from './terminal-bg.js'; +import type { ChatMessage } from './venice-client.js'; + +// ANSI codes +const DIM = '\x1b[2m'; +const RESET = '\x1b[0m'; +const BOLD = '\x1b[1m'; +const CYAN = '\x1b[36m'; +const GREEN = '\x1b[32m'; +const YELLOW = '\x1b[33m'; +const GRAY = '\x1b[90m'; +const RED = '\x1b[31m'; +const MAGENTA = '\x1b[35m'; + +function formatTokens(n: number): string { + return n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n); +} + +function parseCliArgs() { + try { + const { values } = parseArgs({ + options: { + model: { type: 'string', short: 'm' }, + banner: { type: 'string', short: 'b' }, + input: { type: 'string', short: 'i' }, + 'tool-display': { type: 'string', short: 't' }, + loader: { type: 'string', short: 'l' }, + 'loader-text': { type: 'string' }, + 'web-search': { type: 'string' }, + 'x-search': { type: 'boolean' }, + help: { type: 'boolean', short: 'h' }, + }, + allowPositionals: true, + }); + return values; + } catch { + return {}; + } +} + +function showHelp() { + console.log(` +${BOLD}Venice Agent TUI${RESET} + +${BOLD}Usage:${RESET} + npm start -- [options] + +${BOLD}Options:${RESET} + -m, --model Model name or alias (opus, sonnet, grok, deepseek, llama) + -b, --banner Project name for ASCII banner (or "venice" for logo) + -i, --input