An agentic CLI runner in Rust with unified support for Abliteration AI, Cerebras, OpenAI, Azure OpenAI, Gemini, Claude, Ollama, and local models.
15,495 lines of code - 12.8 MiB binary - Emphasizing "sophisticated simplicity".
Homepage: longrunningagents.com
Named after the AI character in William Gibson's novel Agency (2020). In the book, Eunice is a hyper-intelligent AI who chose her own name, derived from the military acronym UNISS (Untethered Neuromorphic Intra-System Support) - reflecting her independence from central servers, brain-inspired architecture, and distributed nature.
- Multi-Provider Support: Abliteration AI, Cerebras, OpenAI, Azure OpenAI, Google Gemini, Anthropic Claude, Ollama, and local models
- Astra and OpenAI Agents API: Native Astra Responses support plus an optional managed runtime for CLI, webapp, and scheduled runs
- 4 Built-in Tools: Bash, Read, Write, and Skill - always available, no configuration needed
- Skills System: User-defined prompts in
~/.eunice/skills/for reusable capabilities - Startup Instructions: Automatically applies user-wide
~/.eunice/AGENTS.mdand current-directoryAGENTS.md - Smart Defaults: Automatically selects the best available model (prefers Gemini)
- Interactive Chat: TUI mode with command history and autocomplete
- Webapp Mode: Browser-based interface with real-time streaming
- Zero Configuration: Works out of the box with just an API key
# Astra with Eunice's local agent loop
eunice --model astra "Review this repository"
# OpenAI manages the agent loop; Eunice runs the local tools
eunice --runtime openai-agents --model astra "Review this repository"
eunice --webapp --runtime openai-agents --model astraBoth use OPENAI_API_KEY; the managed runtime requires Agents API access.
--runtime defaults to eunice. With openai-agents, the default model is Astra.
--agents still selects a schedule file. A scheduled entry can set
runtime = "openai-agents" or runtime = "eunice"; omitting it inherits the
server runtime. The web editor and --install preserve this setting.
Managed tools run on the Eunice host. Completed messages appear as the remote
session progresses. SQLite sessions recover pending work after restart and
journal tool results to prevent automatic re-execution after acknowledgement
loss. CLI and --no-persist state lasts only for the current process. Start a new
session to change a managed session's runtime/model. See the
integration guide for recovery details,
retention, test coverage and the opt-in live smoke test.
cargo install --git ssh://git@github.com/xeb/eunice.gitgit clone git@github.com:xeb/eunice.git
cd eunice
cargo install --path .# Set your API key
export GEMINI_API_KEY=your_key_here
# or
export OPENAI_API_KEY=your_key_here
# or
export ANTHROPIC_API_KEY=your_key_here
# or
export ABLIT_KEY=your_key_here
# or
export CEREBRAS_API_KEY=your_key_here
# Run with a prompt
eunice "List all Rust files in this directory"
# Interactive chat mode
eunice --chat
# Use a specific model
eunice --model gpt-5.6-terra "Explain this code"
eunice --model sonnet "Review main.rs"
eunice --model abliterated-model-large-v2 "Inspect this project and run the tests"
eunice --hax "Inspect this project and run the tests"
# Start webapp
eunice --webappInstall a recent CPU-compatible llama-server (on Arch: sudo pacman -S llama-cpp), then:
eunice --model hf:qwen3.5:2b --chat
# Smaller alternative:
eunice --model hf:qwen3.5:0.8b --chatEunice downloads Q4_K_M weights on first use, starts a localhost-only llama.cpp server, and runs its normal Bash/Read/Write/Skill agent loop in the terminal. Responses stream as they arrive. Tools execute only after their arguments and the stream are complete. No API key or Ollama service is required. Exiting Eunice stops its inference process and releases the model memory.
The 2B weights are about 1.28 GB; runtime memory also includes context and working
buffers. Defaults use a 4096-token context, one slot, non-thinking mode, and a
1024-token generation limit per turn. A token-limited tool call is rejected rather
than executed partially. Unknown hf: aliases are errors, never a substitute model.
Settings for local Qwen runs:
| Environment variable | Purpose |
|---|---|
EUNICE_LLAMA_SERVER |
Explicit path to the inference executable |
EUNICE_LOCAL_THREADS |
CPU generation threads |
EUNICE_LOCAL_BATCH_THREADS |
Prompt-processing threads; defaults to generation threads |
EUNICE_LOCAL_CTX |
Context size; default 4096 |
EUNICE_LOCAL_PREDICT |
Maximum generated tokens per turn; default 1024 |
Weights are cached under ~/.eunice/models/; diagnostics are written to
~/.eunice/llama-server-18921.log. Port conflicts and early server exits are
reported. Existing Gemma aliases and the specialized MTP runtime remain available.
Choose local weights when starting Eunice; in-session local model switching is
not supported yet.
Eunice comes with 4 built-in tools that are always available:
| Tool | Description |
|---|---|
| Bash | Execute shell commands with full system access |
| Read | Read file contents, with binary file detection |
| Write | Write content to files, creates parent directories |
| Skill | Discover and use skills from ~/.eunice/skills/ |
Skills are reusable prompts stored in ~/.eunice/skills/<skill-name>/SKILL.md.
Four skills are auto-installed on first run:
- image_analysis: Analyze images using multimodal AI
- web_search: Search the web for information
- git_helper: Git operations and best practices
- pdf_analysis: Extract and analyze PDF content
mkdir -p ~/.eunice/skills/my_skill
cat > ~/.eunice/skills/my_skill/SKILL.md << 'EOF'
# My Custom Skill
## Description
A skill that helps with specific tasks.
## Instructions
When invoked, follow these steps...
EOFThe Skill tool searches these directories to find relevant skills for a task.
Local Qwen receives a terminal-agent system instruction whenever tools are supplied.
Bash, Read, Write, and Skill are exposed automatically; no yolo setting is needed.
For example, “what directory is this” can invoke Bash pwd and report its result.
Tool selection remains automatic, so ordinary conversation does not require a tool call.
The TUI shows the completed turn's stats above the input and before the next response, without buffering streamed text:
last turn · 11.5 tok/s · 36.2s · 2 calls · session 4.2 KiB · compact 1
- tok/s: weighted output generation rate from llama.cpp's native timings. Other providers show eff tok/s (reported output tokens / wall-clock turn duration), or a dash when token usage is unavailable.
- Duration: the previous user turn, including prompt processing, tool execution, retries, and automatic compaction; excludes typing and initial model startup.
- Calls: agent model-request rounds, including tool follow-ups and context-error retries, plus successful model-assisted compaction requests. Internal HTTP transport retries are not separate rounds.
- Session: retained JSON conversation bytes plus full tool-output payloads (including temporary-file payloads). Excludes model weights, schemas, and runtime allocation overhead. This is storage size, not the model's context token count.
- Compact: successful context reductions since
/clear, manual and automatic.
Use /compact to reduce retained context. It first tries shortening tool results,
then a model-generated summary. Explicit project instructions are preserved.
History is replaced only if smaller; errors, cancellation, and non-reducing
summaries leave it intact. Full tool outputs remain available for retrieval, so
session storage may shrink less than model context. /clear resets both history
and stored tool outputs. /compact is for Eunice-managed conversation history;
remote managed sessions are not compacted through this command.
Set a per-user default in ~/.eunice/config.toml:
default_model = "hf:qwen3.5:2b"Now eunice opens the terminal UI with that model, and eunice "your task"
uses it for a single task. This works with local aliases and cloud model IDs.
Explicit --model, --hax, --gemma, or --gemmad choices take precedence.
Without this file or default_model, the existing automatic selection remains.
Malformed configuration is reported with its path instead of silently selecting
another provider. Explicit model flags bypass the default file.
--install snapshots the configured default into the service's model argument.
The selected model must support the requested runtime; --runtime openai-agents
still requires a compatible OpenAI model. API keys and local runtime installation
requirements are unchanged.
| Provider | API Key Variable | Default Model |
|---|---|---|
| Abliteration AI | ABLIT_KEY or ~/.config/ablit/key |
abliterated-model-large-v2 |
| Cerebras | CEREBRAS_API_KEY or ~/.config/cerebras.env |
live account catalog |
| Google Gemini | GEMINI_API_KEY |
gemini-3.8-flash |
| OpenAI | OPENAI_API_KEY |
gpt-5.6 |
| Anthropic | ANTHROPIC_API_KEY |
claude-sonnet-5 |
| Azure OpenAI | AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY |
(deployment-specific) |
| Ollama | (no key needed) | live models from the local Ollama server |
abliterated-model-large-v2 is the default; the API also currently serves
abliterated-model-large and abliterated-model. All use Abliteration AI's
OpenAI-compatible API with Eunice's full
Bash, Read, Write, and Skill tool loop. Set ABLIT_KEY, or save the key in
~/.config/ablit/key with mode 0600:
chmod 600 ~/.config/ablit/key
eunice --model abliterated-model-large-v2 "Inspect this project and run its tests"Set ABLIT_BASE_URL to override the default https://api.abliteration.ai/v1 endpoint.
Cerebras uses its OpenAI-compatible API with Eunice's full Bash, Read, Write, and Skill tool loop. Model names are explicitly namespaced so they cannot collide with identically named Ollama models:
eunice --model cerebras:gpt-oss-120b "Inspect this project"
eunice --model cerebras:gemma-4-31b "Summarize README.md"Set CEREBRAS_API_KEY, or store a shell-style assignment such as
CEREBRAS_API_KEY=... in ~/.config/cerebras.env with mode 0600. Eunice
loads that file automatically. CEREBRAS_BASE_URL overrides the default
https://api.cerebras.ai/v1 endpoint. --list-models queries the authenticated
catalog so every model enabled for the account appears automatically.
For convenience, these aliases work:
eunice --model fable "..." # claude-fable-5-1
eunice --model sonnet "..." # claude-sonnet-5
eunice --model opus "..." # claude-opus-5
eunice --model haiku "..." # claude-haiku-4-5-20251001
eunice --model flash "..." # gemini-3.8-flash (default)
eunice --model cyber "..." # gemini-3.8-flash-cyber (Fairwind access required)
eunice --model pro "..." # gemini-3.1-pro-preview
eunice --hax "..." # abliterated-model-large-v2eunice --list-models shows Eunice's current cloud-model tiers, the live
Cerebras account catalog, and models reported live by Ollama, with providers
and models alphabetized. The curated
cloud list tracks the provider catalogs:
OpenAI,
Gemini, and
Claude.
Azure OpenAI uses the azure:<deployment-name> format:
# Set up Azure OpenAI environment
export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com"
export AZURE_OPENAI_API_KEY="your-api-key"
# Use your deployment name after azure:
eunice --model azure:gpt-5.6-terra "Hello"
eunice --model azure:my-custom-deployment "Explain this code"Eunice uses Azure's current /openai/v1/ route with implicit versioning. Set
AZURE_OPENAI_API_VERSION only for a legacy dated deployment endpoint.
eunice [OPTIONS] [PROMPT_POSITIONAL]
Arguments:
[PROMPT_POSITIONAL] Positional prompt argument
Options:
--model <MODEL> AI model to use
--hax Shorthand for --model=abliterated-model-large-v2
--gemma Shorthand for --model=gemma4:31b (local Gemma 4 31B + MTP)
--gemmad Use the already-running gemmad daemon and its live model
--no-gemmad No-op; kept for compatibility (gemmad is never used implicitly)
--prompt <TEXT> System prompt (inline text or file path)
--chat Interactive chat mode
--webapp Start web server interface
--port <PORT> Port for webapp server [default: 8811]
--host <HOST> Host for webapp server [default: 0.0.0.0]
--no-persist Disable webapp session persistence (sessions.db)
--agents <FILE> Path to an agents.toml of scheduled agents (webapp mode)
--install Install eunice --webapp as a systemd user service
--uninstall-service Remove the systemd user service installed by --install
--list-models List available AI models
--list-tools List the 4 built-in tools
--list-skills List available skills from ~/.eunice/skills/
--llms-txt Output full LLM context documentation
--update Update to the latest version
-f, --force Force reinstall even if already up to date (with --update)
--uninstall Uninstall eunice
--debug Enable debug output for API calls
--download <MODEL> Download a local model (e.g., hf:gemma4:e4b)
--local-models List downloaded local models
--remove-model <MODEL> Remove a downloaded local model
--serve <MODEL> Start gemma4-server for a local model
--rebuild-gemma4-mtp Force a clean rebuild of the gemma4-mtp server binary
-h, --help Print help
-V, --version Print version
A gemmad daemon (an OpenAI-compatible server for
Gemma 4 — gemma-4-26b-a4b by default — on 127.0.0.1:18082) is selected with
--gemmad. A running daemon is not picked up automatically; the smart default
(gemini-3.8-flash) stays the default even when it is reachable:
eunice "Summarize this file" # smart-default (gemini-3.8-flash), daemon or not
eunice --gemmad "..." # use the daemon; errors if it is not reachable
eunice --no-gemmad "..." # accepted but now a no-op; gemmad is never implicit- Only
--gemmadprobes the daemon (a fast/livezcheck), so a bare invocation never pays the round-trip. - The Bearer token comes from
$GEMMAD_API_KEY, else~/.config/gemmad/keys.toml. - Host/port are overridable via
GEMMAD_HOST/GEMMAD_PORT. The live model id is read from the daemon's/v1/models(overridable fallback:GEMMAD_MODEL_ID). - Tools work: the daemon returns standard OpenAI
tool_calls, so the full Bash/Read/Write/Skill tool set is available.
This is distinct from --gemma, which builds and starts a local 31B + MTP
server (and needs the GPU's VRAM free).
On every agentic startup, Eunice loads optional user-wide instructions from
~/.eunice/AGENTS.md, followed by AGENTS.md in the current working directory.
Their contents are included as system instructions on the first turn in
single-shot, chat, webapp, and scheduled-agent runs. Parent directories are not
searched. In webapp mode, these instructions precede those supplied by --prompt.
To share a home-directory instruction file everywhere, run
ln -s ../AGENTS.md ~/.eunice/AGENTS.md (create ~/.eunice first if needed).
When both paths resolve to the same file, Eunice includes it only once.
Changes take effect in newly started processes, including after compaction.
If no prompt is provided, eunice auto-discovers prompt files in the current directory:
prompt.txt,prompt.mdinstruction.txt,instruction.mdinstructions.txt,instructions.md
Start a web server for browser-based interaction:
eunice --webapp
# Opens at http://localhost:8811When --prompt supplies system instructions, restored conversations show them
in a compact disclosure that is collapsed by default. The user message remains
separate and visible; expand System instructions only when you need them.
Features:
- Real-time SSE streaming
- Session persistence
- Multi-turn conversations
- Tool execution display
- Scheduled long-running agents (see below)
Agents are prompts that run on a cron schedule inside the webapp server. Define them in a
plain-text agents.toml and pass it with --agents:
[[agent]]
name = "daily-digest" # required, lowercase kebab-case, unique
schedule = "0 9 * * *" # required, standard 5-field cron, server local time
prompt = "Summarize yesterday's commits in ~/p/myrepo and write digest.md"
[[agent]]
name = "repo-watch"
schedule = "*/30 * * * *"
prompt_file = "prompts/repo-watch.md" # alternative to prompt; relative to agents.toml
model = "flash" # optional; defaults to the server's model
working_dir = "/home/me/p/myrepo" # optional; cwd for this agent's tools
timeout_secs = 900 # optional, default 600
enabled = true # optional, default trueeunice --webapp --agents agents.tomlEach run creates a normal session, so the full transcript — prompts, tool calls, output — is readable in the web UI, and you can watch a run live while it happens. The hamburger drawer gains an AGENTS tab showing each agent's schedule, next and last run, status, and recent runs — and lets you create, edit, enable and delete agents directly.
Changes apply without a restart. The server watches agents.toml and any prompt_file it
references, and reloads a few seconds after a change, however you made it. Browser edits rewrite the
file in place, preserving your comments and formatting.
The config is validated at startup and the server refuses to start if anything is wrong, so a typo fails immediately rather than silently never firing. Once it is running the rule inverts: an invalid edit is rejected and the previous config keeps running, so a typo can never take the daemon down.
Note the webapp has no authentication of its own, so anyone who can reach the port can edit agents —
and agents run shell commands. Bind to --host 127.0.0.1 unless it sits behind an authenticating
proxy.
Schedules use standard 5-field Unix cron (minute hour day-of-month month day-of-week), with
day-of-week 0/7 = Sunday, and names like MON-FRI accepted. Missed schedules are not backfilled:
if the server was down at 09:00, that run is skipped rather than replayed. If a run is still going
when its next tick arrives, that tick is skipped rather than queued.
See HOWTO_SCHEDULED_AGENTS.md for the full guide: every field, schedule recipes, run semantics, service management, troubleshooting, and worked examples.
--install installs the webapp as a systemd user service — no sudo, no root:
eunice --install --port 8811 --agents /home/me/agents/agents.tomlThis validates the agents file, writes ~/.config/systemd/user/eunice.service bound to the port you
chose, enables and starts it, and turns on lingering so it survives logout and starts at boot.
Because systemd user services do not inherit your shell environment, the installer snapshots your
API keys and provider settings (ABLIT_KEY, ABLIT_BASE_URL, CEREBRAS_API_KEY, CEREBRAS_BASE_URL, OPENAI_API_KEY,
ANTHROPIC_API_KEY, GEMINI_API_KEY, GOOGLE_API_KEY, OLLAMA_HOST, and others) into
~/.eunice/eunice.env with mode 0600. Re-run --install after rotating a key.
systemctl --user status eunice # check it
journalctl --user -u eunice -f # follow logs
eunice --uninstall-service # stop, disable, and remove the unit--uninstall-service leaves ~/.eunice/eunice.env, sessions.db, and lingering alone.
Eunice v1.0.15 follows a "sophisticated simplicity" design:
- Optional configuration - per-user model defaults; environment variables for API keys
- No external MCP servers - 4 built-in tools cover most use cases
- No multi-agent orchestration - one agent, focused execution
- Skills for extensibility - user prompts, not complex plugins
The agent loop is simple:
- Send user prompt + conversation history
- If LLM returns tool calls, execute them
- Send results back to LLM
- Repeat until LLM has no more tool calls
MIT License
- v1.2.1: User-wide startup instructions with symlink deduplication.
- v1.2.0: TUI turn telemetry, native local generation speed, session payload size, successful compaction counters, and
/compact. - v1.1.1: Give local Qwen an explicit terminal-agent role so ordinary filesystem questions use available tools; preserve user instructions and text-only requests.
- v1.1.0: Optional per-user default model in
~/.eunice/config.toml; explicit model flags override it. - v1.0.15: Qwen3.5 through CPU llama.cpp, streamed terminal responses and validated function calls, explicit model resolution, and owned inference-process cleanup
- v1.0.14: Automatic per-project
AGENTS.mdsystem instructions - v1.0.13: Cerebras support and collapsible webapp system instructions
- v1.0.12: Current model catalogs/defaults for every provider and Azure OpenAI v1 endpoint support
- v1.0.11: Abliteration AI
abliterated-model-large-v2provider with full tool calling - v1.0.1: Azure OpenAI support, GLM model support, --debug flag
- v1.0.0: Major simplification - 4 built-in tools, skills system, no MCP/orchestrator
- v0.3.x: Full-featured with MCP servers, multi-agent, DMN mode, research mode
Use /model <id> [effort] to switch providers/models without discarding the
conversation, or /effort low (also medium/high/xhigh where supported) to change
reasoning. /effort default restores the provider default. /model and
/effort show the current selection; /model --json lists available choices.
Unsupported efforts are rejected before replacing the client. Local models
requiring server startup still launch in a new session.
The tmux-terminal model badge uses /model --tmux and pane-local metadata to
read choices without adding a catalog to the transcript. The bridge checks a
per-request confirmation, and new input invalidates an open picker. Existing
Eunice processes must be relaunched once after installing this update.