# cx **Repository Path**: umb/cx ## Basic Information - **Project Name**: cx - **Description**: AgentRunner = LLM + Tools + Memory + Loop + Policy - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-11 - **Last Updated**: 2026-08-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # GenericAgent-Rust A Rust port / reimplementation of [GenericAgent](https://github.com/lsdefine/GenericAgent), focused on the minimal seed core. ## Phase 1 Goal A command-line agent that can: - Read files / patch files / write files - Run code (Python / shell via subprocess) - Talk to an OpenAI-compatible LLM API - Execute a multi-turn ReAct-style loop Deliberately **not** in phase 1: - Browser control (TMWebDriver) - Multi-model mixing / fallback - Long-term skill / memory evolution - Rich TUI ## Quick Start ### For end users (download the binary) 1. Download `cx-.zip` from Releases (or build locally per the [Building](#building--distributing) section below). 2. Unzip anywhere. Inside you'll see: - `cx.exe` (or `cx` on Linux/macOS) — the agent. - `assets/` — system prompts and tool schemas (required). - `mykey.example.toml` — copy this to `mykey.toml` and fill in: ```toml api_key = "sk-..." base_url = "http://your-llm-endpoint/v1" model = "your-model-name" ``` 3. Run it: ```bash ./cx "List all rust files here and summarize them" ``` ### For developers ```bash cargo build --release export GA_API_KEY="sk-..." export GA_BASE_URL="https://api.openai.com/v1" export GA_MODEL="gpt-4o-mini" ./target/release/cx "List all rust files here and summarize them" ``` ## Building & Distributing End users don't need a Rust toolchain — the release `.exe` is statically linked and only needs `assets/` plus a populated `mykey.toml` to run. Two helper scripts are included: ```bash # Windows / PowerShell .\build-release.ps1 # builds + assembles dist/ RELEASE_ZIP=1 .\build-release.ps1 # also zips dist/ into a release archive # Linux / macOS ./build-release.sh RELEASE_ZIP=1 ./build-release.sh ``` The resulting `dist/` directory contains everything a colleague needs to run the agent. Copy it to their machine; no install step required. ## Features - **Tools**: `file_read`, `file_write`, `file_patch`, `code_run` (python/bash subprocess with merged stdout/stderr), `update_working_checkpoint`, `list_sessions`, `read_session`, `ask_user`, `web_fetch` (fetch a public URL and return the text content with HTML tags stripped, with optional `max_chars` and `start_line` chunking). - **Interactive REPL**: run with `--repl` (or just `cx` with no prompt) to enter an interactive session. The agent keeps its memory across turns and processes each line as a new prompt. Commands start with `:`: `:help`, `:exit`, `:reset`, `:log-level `, `:sessions`, `:history`, `:continue `. Command history is persisted via `rustyline` at `temp/repl_history.txt`. The REPL prints an ASCII welcome banner showing the active model + endpoint, mirrors each user prompt as `>>> 💬 …`, and prints `⏳ / 🔧 / ✅ / ❌` status lines before / after each LLM / tool event. Pass `--no-banner` to suppress this decoration for scripted usage. - **Parallel tool dispatch**: when the LLM emits multiple `tool_calls` in one turn, they run concurrently via `futures::join_all`. `tool_call_id` is preserved on each `tool_result`. - **History trimming**: when the estimated token count exceeds the threshold, older messages are collapsed into a summary message. By default a procedural summary (tool names + assistant text snippets + working memory) is used. For larger compressed regions (>= 4 messages and >= `$GA_LLM_SUMMARY_MIN_TOKENS`, default 800 estimated tokens), an LLM-generated summary is invoked instead. The trim log shows whether `LLM` or `procedural` summary was used. The recent `$GA_KEEP_RECENT` (default 8) messages are always preserved. - **Token usage**: per-turn and cumulative session totals via `stream_options.include_usage`. Saturating addition. - **Sessions**: every run writes `temp/sessions//events.jsonl` and `meta.json`. `--continue ` resumes a prior session with its working memory and final answer replayed into the prompt. - **Tool output streaming**: large tool results (>4K chars) are auto-saved to `temp/tool_outputs/__.txt`; the LLM receives a compact `saved_to` reference and uses `file_read` to view the full content. - **Tool output folding** + **log levels**: long `ToolEnd` results are collapsed into a one-line summary by default (e.g. `exit 0; 391 chars of output`). Use `-v` / `--verbose` or `--log-level=verbose` to expand the full body. The `--log-level=quiet|default|verbose` flag controls CLI density globally: `quiet` shows only final `Done`/`Error` and critical tool events; `default` shows tool summaries, plan/reflect, and token stats; `verbose` is equivalent to the `-v` flag. Errors always show their body regardless of level. - **Retry with exponential backoff**: transient HTTP errors (408/409/425/429/500/502/503/504/529) trigger `max_retries = $GA_LLM_MAX_RETRIES` retries with `base = $GA_LLM_RETRY_BASE_MS` doubled each attempt. Defaults: 3 retries, 1000ms base. - **Session continuation**: `read_session` returns compact single-line text per event (not nested JSON) so the LLM can ingest long sessions without triggering HTTP 400. ## Tests ```bash cargo test # unit + integration tests (no network) GA_LIVE_ENDPOINT=https://... cargo test --test live # optional live LLM test ``` Verified against: - `claude-opus-5` via OpenAI-compatible gateway - local `vLLM` endpoints (tool-calling requires `--enable-auto-tool-choice`) ## Design Notes - **Sync Iterator API on the outside, tokio async on the inside.** Each `Agent::run()` spawns a task on a long-lived tokio runtime and exposes events through a `crossbeam_channel::Receiver` wrapped in a plain `Iterator`. This mirrors GA's Python generator model 1:1. - Tools implement a small `Tool` trait. Tool bodies are mostly synchronous except for `code_run` (subprocess streaming). - The system prompt and tool schema are loaded from `assets/` at startup so they stay editable without recompiling. - SSE streaming from the LLM is parsed incrementally. ## P8: LLM-generated history summary When the compressed region is large enough (>= 4 messages and >= 800 estimated tokens by default), `maybe_trim` invokes the LLM with a "summarizer" system prompt that preserves the task goal, file paths, tool outcomes, errors, and durable working memory. The LLM call is cheap (no tools, short prompt) and falls back to the procedural summary on any error. The trim status line shows which path was used, e.g.: ``` [history trimmed] 1603 ~ 598 tokens (14 ~ 4 messages, LLM summary) ``` Tunable via `GA_LLM_SUMMARY_MIN_TOKENS`, `GA_KEEP_RECENT`, and `GA_TRIM_THRESHOLD`. ## License MIT