Imported from 8b-is/8b-is-engine (
entheai/AGENTS.md). Install upstream withnpx skills add 8b-is/8b-is-engine --skill entheai. Copyright stays with the author.
AGENTS.md — entheai
macOS/Apple-Silicon native hybrid coding agent CLI (Rust workspace).
Essential commands
./scripts/check.sh # full CI gate: fmt (--check) + clippy (-D warnings) + tests
cargo build --release # optimized build (fat LTO, single codegen unit, target-cpu=native)
cargo nextest run --workspace --all-targets --all-features # fast parallel tests (preferred)
cargo test --workspace --all-targets --all-features # fallback test runner
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo fmt --all -- --check # NB: `cargo fmt` takes `--all`, not `--workspace`
Run the binary: cargo run -- --help or ./target/release/entheai "your prompt".
Build environment
- Target:
aarch64-apple-darwinonly (Apple Silicon). Pinned Rust toolchain1.96.0(MSRV1.94, required by adk-rust's dependency tree). - Allocator:
mimallocon macOS (wired as#[global_allocator]inbin/entheai/src/main.rs). - Release profile:
opt-level=3,lto="fat",codegen-units=1,panic="unwind"(never abort — sub-agent tokio-task panics must be catchable). .cargo/config.toml: setstarget-cpu=nativeand-Wl,-dead_stripfor macOS; binaries are not portable to older CPUs.scripts/check.shsetsCARGO_PROFILE_DEV_SPLIT_DEBUGINFO=unpacked(and same for test) to speed up dev compilation by skipping dSYM bundling.
Workspace crate map
Cargo.toml # workspace root (resolver=2)
├── bin/entheai/ # CLI binary (clap, tokio, sentry, mimalloc)
├── bin/entheai-worker/ # federation worker (--serve / --dispatch)
├── bin/entheai-launch/ # native .app launcher (Ghostty window)
├── crates/config/ # entheai-config: TOML → Config deserialization
├── crates/core/ # entheai-core: EntheaiAgent (adk-rust-backed agent loop)
├── crates/tools/ # entheai-tools: Tool trait + ToolRegistry + built-in tools
├── crates/permission/ # entheai-permission: Policy (yolo/allowlist/ask) + Prompter
├── crates/router/ # entheai-router: role→model resolution + EntheaiAgent factory
├── crates/orchestrator/ # entheai-orchestrator: fan-out decomposition + worktree isolation
├── crates/mapper/ # entheai-mapper: @{path} input sectioning
├── crates/tui/ # entheai-tui: interactive ratatui chat UI
├── crates/companion/ # entheai-companion: session beacon window (QR + animation)
├── crates/memory/ # entheai-memory: 5-namespace SQLite + vector store
├── crates/memory-pp/ # entheai-memory-pp: prompt-processing, frozen nodes, BrainJudge
├── crates/viz/ # entheai-viz: TUI visualization models (brain ring, swarm)
├── crates/radio/ # entheai-radio: in-TUI music (yt-dlp download → rodio playback)
├── crates/mcp/ # entheai-mcp: MCP client + server supervisor
├── crates/skills/ # entheai-skills: SKILL.md discovery + installer
├── crates/launcher/ # entheai-launcher: native-app window spawn
├── crates/obsidian/ # entheai-obsidian: wiki-sync
├── crates/bus/ # entheai-bus: NATS event bus (federation)
├── crates/federation/ # entheai-federation: remote fleet dispatch
├── crates/sandbox/ # entheai-sandbox: isolated execution
├── crates/ultragraph/ # entheai-ultragraph: graph data structure (Rust port)
├── crates/tantra-agent/ # tantra-agent: tantric board control CLI + adk-rust agent (mlxquantlovefrom.com)
├── crates/kompress-core/ # kompress-core: context-pruning pipeline (vendored from kompress-ultra)
├── crates/relay/ # entheai-relay: post-prompt-processing language chain (hu slang -> Lovari -> English -> Mandarin)
└── crates/vision/ # entheai-vision: image post-processing layer, CLI mode "caligraph" (agy/Gemini, falling back to a vision-capable model)
Crate names use dashes (entheai-core) but Rust module names use underscores (entheai_core). Workspace version: see Cargo.toml's [workspace.package].
Architecture & data flow
crates/core is built on adk-rust (pinned 1.0.0) — there is no hand-rolled Provider/streaming client anymore; model calls go through adk-rust's own Llm implementations (adk_rust::model::openai::OpenAIClient for OpenAI-compatible endpoints).
bin/entheai/src/main.rsparses CLI args (prompt,--config,--model,--yolo,--fanout), loadsentheai.tomlviaentheai_config::Config::from_toml_str, registers built-in tools in aToolRegistry, sets up the permissionPolicy, and builds anEntheaiAgent(viaEntheaiAgent::build_auto, which picks the memory-aware or instruction-only constructor).entheai_core::EntheaiAgent(crates/core/src/entheai_agent.rs) wraps anadk_rust::agent::LlmAgentBuilder+Runner+SessionService. Two entry points:run_to_text()/run()— one fresh message, one fresh session.run_with_history()— seeds prior(role, text)turns into the session viaSessionService::append_eventbefore running the new message (what the interactive TUI uses, since it carries full conversation history forward).crates/core/src/event_bridge.rs'srun_with_events()drives the resultingadk_rust::EventStreamand translates it into the TUI-facingAgentEventenum (Thinking/Token/ToolStarted/ToolFinished/FrozenWoke), and — when memory is enabled — records the final answer's trajectory and raw transcript once the run completes.
entheai_core::model_resolve::resolve_modelparses"<provider>/<model>"into anArc<dyn adk_rust::Llm>using[providers.<name>]config (base_url+ optionalapi_key_env).entheai_tools::ToolRegistrystoresArc<dyn Tool>by name (non-consumingto_tools()lets one registry back multipleEntheaiAgentbuilds — needed since the interactive TUI builds a fresh agent every turn).crates/core/src/adk_tool_adapter.rs'sAdkToolAdapterwraps eachentheai_tools::Tool(+Policy+Prompter) as anadk_rust::Tool. Built-in tools:read_file,write_file,search,run_shell. All are rooted at the canonicalizedcurrent_dir().entheai_permission:Policy.decide(tool_name)returnsAllow(yolo or allowlisted),Deny, orAsk.Askfalls through toPrompter::confirm()which readsy/Nfrom stdin (CLI) or forwards to the TUI's permission modal.entheai-companion(separate binary,crates/companion/src/main.rs): spawned as a child process by the main binary whenever[companion].enabled = true(and--no-companionis not passed). A 180×180 px borderless always-on-top floating window (winit + softbuffer). Shows an animated breathing glow with a QR code encoding{sid, host, port, cwd}. Drives a four-state animation:- idle — slow teal pulse (3s cycle) when TUI is waiting for input
- working — fast teal pulse (1.5s) + orbiting spinner while the agent runs
- permission_pending — magenta pulse (1s) + "?" glyph when a tool is gated
- error — red dim pulse (4s) on errors
State changes arrive over a Unix socket (
$TMPDIR/entheai-<sid>.sock) asStateChangeJSON lines. Clicking copieshttp://<host>.local:9876/session/<sid>to clipboard. On socket close, fades out over 500ms and exits. Full spec:docs/superpowers/specs/2026-07-18-entheai-companion-design.md.
Companion config (entheai.toml)
[companion]
enabled = true # spawn companion window (default: true)
always_on_top = true # float above other windows (default: true)
CLI: --no-companion disables for the session.
Key patterns & conventions
Traits as extension points
All core extension points use #[async_trait]:
adk_rust::Llm— add new model backends (adk-rust ships OpenAI/Anthropic/Gemini/etc.;entheai_core::model_resolveonly wires up OpenAI-compatible)Tool(entheai_tools) — add new built-in tools, wrapped asadk_rust::ToolviaAdkToolAdapterPrompter— swap permission UI (CLI → stdin, TUI → dialog)
Tool implementation recipe
Each tool is a struct with a root/cwd field + new(root) constructor, implementing:
fn name(&self) -> &strfn schema(&self) -> serde_json::Value— OpenAI function-tool JSON withtype,function.name,function.parametersasync fn call(&self, args: serde_json::Value) -> anyhow::Result<String>— execute, return text
Register in main.rs with registry.register(Box::new(MyTool::new(root.clone()))).
Testing patterns
- Inline tests:
#[cfg(test)] mod testsat the bottom of each source file (not in separate/testsdirectories) - Async tests:
#[tokio::test]— tokio runtime is spun per test - HTTP mocking:
wiremock::MockServermocking the OpenAI-compatible/chat/completionsSSE endpoint (seecrates/core/src/entheai_agent.rsandcrates/core/tests/parity.rsfor the request/response fixture shapes) - Filesystem:
tempfile::tempdir()for tool tests of file I/O - Fake
Llm/Toolimpls: implementadk_rust::Llmoradk_rust::Tooldirectly in test modules when a scenario needs more control than an SSE mock gives (seecrates/memory-pp/src/judge.rs'sFakeLlm)
Naming & style
impl Into<String>for public constructors (ergonomic, no string clone at call site)anyhow::Result<T>throughout (no custom error types)serde_json::Valuefor tool args and schemas (dynamic, matches OpenAI wire format)- One-line doc comments
///on public items
Gotchas
cargo fmtuses--all, not--workspace.cargo fmt --workspacewill error. Build/clippy/test all use--workspace.- File-tool path sandboxing:
resolve_in_root()incrates/tools/src/fs.rsblocks both..traversal AND symlink escapes. It canonicalizes the deepest existing ancestor and compares against the canonicalized root. macOS temp dirs are under/var(a symlink to/private/var), so the root must be canonicalized before comparison — callers must pass a canonicalized root (the CLI does this). - run_shell uses
kill_on_drop(true): if the tokio task is aborted (e.g. on timeout), the child process is reaped, not orphaned. - Hard caps exist everywhere:
[router].max_turns(default 200,u32::MAXunder--yolo) tool-dispatch turns perEntheaiAgent, 120s shell timeout, 200 max search results, 100KB max shell output. These prevent runaway API costs and memory blowup. [inference].request_timeout_secs/.retriesare inert. adk-rust 1.0.0'sOpenAIClienthardcodesreqwest::Client::new()with no timeout/retry builder surface — a confirmed gap, not a bug in entheai's wiring.temperature/max_tokensstill work (LlmAgentBuilder::temperature/max_output_tokens).- Sentry DSN is hardcoded in
bin/entheai/src/main.rs. Override viaSENTRY_DSNenv var. No PII is sent (send_default_pii: false). - Model ID format is
<provider>/<model>(e.g.deepseek/deepseek-v4-flash,deepseek/deepseek-v4-pro,gemini/gemini-3.6-flash,openrouter/deepseek/deepseek-v4-pro,osaurus/qwen3-coder,vaked/qwen3-coder:30b). The string is split on the first/inentheai_core::model_resolve::resolve_model.[agents.<role>].modelis a preference-ordered fallback chain: the first entry whose provider is available (declared in[providers]with itsapi_key_envset) wins; if none is, the role falls back to[router].orchestrator→default_model→ the built-in chain (coder/reviewer →deepseek/deepseek-v4-pro→gemini/gemini-3.1-pro-preview→openrouter/deepseek/deepseek-v4-pro; explore/test/docs →deepseek/deepseek-v4-flash→gemini/gemini-3.6-flash→openrouter/deepseek/deepseek-v4-flash; seeentheai_router::DEFAULT_PRO_CHAIN/DEFAULT_FLASH_CHAIN). Only the fan-out path degrades further to the keylessvaked/qwen3-coder:30b(logged); interactive runs error loudly.[fanout].executormust be one ofauto/local/agy/copilot(anything else is a config error). - Config file is
entheai.tomlby default:./entheai.toml(or--config <path>) →~/.config/entheai/entheai.toml→~/.config/entheai/config.toml→ built-in defaults (DeepSeek V4, needsDEEPSEEK_API_KEY). The[providers.<name>]key is used to look up the provider config;deepseek,gemini,openrouterand the keylessvakedare injected into every parsed config byentheai-config(a user block with the same name wins).api_key_envnames an environment variable to read (not the key itself). Full key reference:docs/configuration.md. - Only macOS/Apple Silicon. Hardware-specific tuning (
target-cpu=native,mimalloc,-Wl,-dead_strip) means the binary won't run on Intel Macs or other platforms.
External services
- DeepSeek direct API (default engine):
https://api.deepseek.com/v1,DEEPSEEK_API_KEY—deepseek/deepseek-v4-flash(interactive default, light roles) anddeepseek/deepseek-v4-pro(orchestrator, coder/reviewer,[oracle].model) - Gemini (fallback): native Gemini API via adk-rust (
kind = "gemini",GEMINI_API_KEY; the OpenAI-compatible endpoint breaks Gemini 3.x tool calls) —gemini/gemini-3.6-flash,gemini/gemini-3.1-pro-preview; also the Antigravity CLI target for[fanout] executor = "agy"(agy_model = "gemini-3.6-flash-high") - OpenRouter (third fallback):
https://openrouter.ai/api/v1,OPENROUTER_API_KEY—openrouter/deepseek/deepseek-v4-flash,openrouter/deepseek/deepseek-v4-pro - vaked (keyless free tier):
https://coder.vaked.dev/v1—vaked/qwen3-coder:30b - Osaurus: local inference server on
http://127.0.0.1:1337/v1(OpenAI-compatible) - OpenCode Zen (optional): cloud gateway at
https://opencode.ai/zen/v1(DeepSeek V4 Pro/Flash, Qwen, etc.) - Sentry: crash/error reporting with hardcoded DSN (opt-out via
SENTRY_DSNenv) - 8b.IS Ecosystem:
- Documentation Hub:
https://www.8b.is/documentation - Public Documents:
https://github.com/8b-is/8b-public-documents - MEM|8 Temporal Vector Memory:
https://www.8b.is/waves/mem8 - MAAT Ethical Alignment:
https://www.8b.is/maat
- Documentation Hub: