Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Kerux

Kerux (Greek: κῆρυξ — herald, messenger of the gods) is a fast, self-contained AI agent runtime written in Rust.

It runs a full ReAct agent loop with tool execution, streaming responses, persistent memory, and multi-platform messaging gateways (Telegram, WhatsApp) — all in a single static binary with zero runtime dependencies.

Why Kerux?

  • Single binary — no Python, no Node, no runtime. cargo build and run.
  • Fast — Rust core, ~30K LOC, sub-second startup.
  • Self-contained — sessions, memory, todos, cron jobs all persist to disk as JSON.
  • Multi-platform — Telegram long-polling + WhatsApp (Baileys bridge) adapters built in.
  • Production features — tool approval gates, context compaction, fallback provider chains, voice STT, cron scheduling, subagent delegation.

Crate Layout

CrateDescription
kerux-coreAgent loop, LLM clients, tools, gateway adapters, persistence
kerux-cliCLI/TUI frontend, serve gateway mode, autonomous coding mode

Attribution

Kerux began as a Rust port of hermes-agent by Nous Research. See ATTRIBUTION.md for details.

Quickstart

Build

git clone https://github.com/eikarna/hermes-rs.git
cd hermes-rs
cargo build --release

The binary lands at target/release/kerux.

Configure

Copy the example config and fill in your provider:

cp kerux.example.toml ~/.config/kerux/kerux.toml

Minimal config:

[client]
provider = "openai"
model = "gpt-4o"

[client.auth]
api_key_env = "OPENAI_API_KEY"

Or use environment variables directly:

export KERUX_PROVIDER=openai
export KERUX_MODEL=gpt-4o
export OPENAI_API_KEY=***

Run

Interactive TUI:

kerux

Single-shot:

kerux run "explain this codebase"

Gateway mode (Telegram + WhatsApp):

kerux serve

Verify

cargo fmt --all
cargo test --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings

Configuration

Kerux is TOML-first. Config resolution order:

  1. --config <path> CLI flag
  2. KERUX_HOME environment variable
  3. ~/.config/kerux/kerux.toml (Unix) / %APPDATA%\kerux\kerux.toml (Windows)
  4. ./kerux.toml in the current directory

See kerux.example.toml for the full annotated reference.

Key Sections

[client]

LLM provider settings: provider, model, base_url, timeout_secs, stream.

[gateway]

Messaging gateway settings:

KeyDefaultDescription
telegram_enabledfalseEnable Telegram long-polling adapter
telegram_tokenBot token
whatsapp_enabledfalseEnable WhatsApp adapter (Baileys bridge)
whatsapp_bridge_urlhttp://127.0.0.1:3000Bridge endpoint
streaming_repliesfalseLive-edit token streaming with cursor
tool_approvalfalseRequire Telegram inline-keyboard approval before tool execution
context_compactionfalseSummarize oldest messages near context cap
stt_modelVoice note transcription model (enables STT)

[delegation]

Subagent delegation settings: provider, model, max_concurrent (default 3).

[fallback]

Fallback provider chain (default OFF): ordered list of providers tried on transient failure.

[autonomous]

Autonomous coding mode: todo_path (default TODO.md), status_path (default autonomous-status.toml), test command, git remote/branch.

Environment Variables

All config fields can be overridden via KERUX_* env vars (e.g. KERUX_PROVIDER, KERUX_MODEL, KERUX_HOME, KERUX_LOG_LEVEL, KERUX_SKILLS_DIR).

Architecture Overview

┌─────────────────────────────────────────────────────┐
│                    kerux-cli                         │
│  TUI (ratatui) │ serve (gateway) │ autonomous mode  │
└────────────────────────┬────────────────────────────┘
                         │
┌────────────────────────▼────────────────────────────┐
│                   kerux-core                         │
│                                                      │
│  ┌──────────┐  ┌──────────┐  ┌───────────────────┐  │
│  │  Agent   │  │  Client  │  │      Gateway      │  │
│  │  (ReAct) │  │  (LLM)   │  │ Telegram│WhatsApp │  │
│  └────┬─────┘  └──────────┘  └───────────────────┘  │
│       │                                              │
│  ┌────▼─────────────────────────────────────────┐   │
│  │  Tools: file, patch, terminal, code_exec,    │   │
│  │  web, memory, todo, sub_agent, mcp, skills   │   │
│  └──────────────────────────────────────────────┘   │
│                                                      │
│  ┌──────────────────────────────────────────────┐   │
│  │  Persistence: sessions, memory, todos, cron  │   │
│  │  (~/.kerux/ — atomic JSON writes)            │   │
│  └──────────────────────────────────────────────┘   │
└──────────────────────────────────────────────────────┘

Core Subsystems

ModuleResponsibility
agent.rsReAct loop, streaming, cooperative cancellation, approval gate, context compaction
client.rsLLM provider abstraction (OpenAI-compatible, Anthropic, Gemini), fallback chain
gateway.rsPlatform adapters (Telegram long-polling, WhatsApp bridge), markdown conversion, message chunking
session_store.rsPer-channel conversation persistence (format v2 with summary)
persist.rsShared atomic JSON write helpers (~/.kerux/)
approval.rsTool approval gate (inline keyboard via Telegram callback_query)
scheduler.rsCron-style job scheduler with disk persistence
tools/Built-in tool implementations
platform.rsOS paths (kerux_home(), config/data/sessions dirs)

Agent Loop

The core execution engine lives in kerux-core/src/agent.rs.

ReAct Cycle

  1. Build context — system prompt + conversation history (+ [CONTEXT SUMMARY] if compaction active)
  2. Call LLM — streaming or non-streaming via the configured provider
  3. Parse response — text chunks, reasoning, tool calls (tolerant parsing)
  4. Execute tools — with optional approval gate (F1); results appended as tool messages
  5. Loop — repeat until the model produces a final text response with no tool calls

Cooperative Cancellation

Every iteration checks an Arc<AtomicBool> cancel flag — at loop boundaries, between SSE chunks, and before each tool execution. On cancel, repair_conversation_after_cancel() fixes any dangling assistant/tool message pairs so the conversation stays valid.

Event Emission

The agent emits events (ToolStart, ToolEnd, TextChunk, RunProgress) via a bounded channel using non-blocking try_send() — a slow or dead event pump can never deadlock the ReAct loop.

Context Compaction (F2)

When the conversation approaches the session cap, compact_history() summarizes the oldest messages via a one-shot LLM chat. The summary is stored in the session file (format v2) and injected as a [CONTEXT SUMMARY] marker, keeping recent messages intact.

Gateway & Adapters

The gateway (kerux-core/src/gateway.rs) connects the agent to messaging platforms. Started via kerux serve.

Design

  • Long-polling, not webhooks — no HTTP server dependency (YAGNI). Telegram uses getUpdates with 30s timeout.
  • Parallel adapters — each adapter runs in its own tokio::spawn task so Telegram’s long-poll never starves WhatsApp polling.
  • Interrupt-on-new-message — an incoming message cancels the active run for that channel and starts a fresh one.

Telegram Adapter

  • Markdown → MarkdownV2 conversion (stdlib, custom converter): special-char escaping, fenced code blocks, --- → Unicode separator, tables → bullet lists, blockquotes
  • Message chunking at 3500 chars on line boundaries with code-fence tracking
  • Live message editing for status updates (🤔 Thinking..., 🔧 Tool, heartbeats)
  • Two reply modes: normal (final edit) and streaming (streaming_replies = true, token streaming with cursor, ~900ms throttle)
  • Inline-keyboard tool approval via callback_query
  • Voice note STT via getFile → download → /v1/audio/transcriptions
  • Explicit HTTP timeouts (connect 5s, read 45s) — no half-open hangs

WhatsApp Adapter

  • Talks to a Baileys HTTP bridge (default http://127.0.0.1:3000)
  • Polls GET /messages (drain-queue), sends via POST /send
  • Custom markdown converter (markdown_to_whatsapp) — no regex lookarounds
  • Explicit HTTP timeouts (connect 5s, read 30s)

Persistence

All channel state survives restarts:

DataLocation
Conversations~/.kerux/sessions/<platform>_<channel>.json (format v2)
Memory~/.kerux/memory/memories.json
Todos~/.kerux/todos/todos.json
Cron jobs~/.kerux/cron/jobs.json

All writes are atomic (temp file + rename).

OAuth and provider authentication design

Goal

Add provider authentication that can eventually support official browser login flows while keeping the current OpenAI-compatible API-key path intact.

This is a design checkpoint, not a runtime behavior change.

Provider reality check

  • OpenAI: public OpenAI-compatible API access continues to support API keys. OpenAI also documents ChatGPT/Codex auth for Codex clients, including browser login, device/headless login, access-token injection, and auth-cache reuse. Kerux should model this as a separate OpenAI/Codex account-auth capability, not silently treat Codex tokens as generic OpenAI API keys.
  • Google: Gemini supports API keys and OAuth/Application Default Credentials. Direct desktop OAuth requires a Google OAuth client ID; ADC via gcloud auth application-default login keeps token creation, refresh, and storage outside Kerux.
  • GitHub Copilot: official Copilot CLI authentication supports OAuth device flow, supported GitHub token types (COPILOT_GITHUB_TOKEN, GH_TOKEN, GITHUB_TOKEN), OS keychain storage, and GitHub CLI fallback. Kerux should reference external tokens first and only run Copilot login after provider-specific client behavior is defined.
  • Anthropic: Claude access is not one single OAuth path. Documented routes include Claude.ai / Claude Code account login, Anthropic Console API keys, Team/Enterprise accounts, and cloud-provider routes such as Google Vertex AI, Amazon Bedrock, and Microsoft Foundry. Kerux should keep these as separate capabilities because Vertex/Bedrock/Foundry require provider-specific request/auth behavior, not just a bearer token swap.
  • OpenCode comparison: OpenCode stores provider credentials outside project config and exposes /connect flows. Kerux should copy the credential separation pattern, not vendor-private auth internals.

1. Keep project config non-secret

kerux.toml should continue to describe provider selection, base URLs, and model defaults. It should not become the default storage location for OAuth access tokens or refresh tokens.

Recommended future config shape:

[client]
provider = "openai-compatible"
base_url = "https://api.openai.com/v1"
auth_ref = "openai-default"

auth_ref points to an entry in local credential storage.

2. Store credential metadata locally; store secrets safely

Recommended metadata path:

  • Windows: %APPDATA%/kerux/auth.json
  • macOS: ~/Library/Application Support/kerux/auth.json
  • Linux: ~/.local/share/kerux/auth.json or config-dir equivalent from existing platform helpers

Rules:

  • Never write tokens to repo-local files.
  • Never log token values.
  • Bind credentials to the endpoint stored in the auth profile, and reject repo-local base URL overrides when an auth_ref is active.
  • Require explicit base URLs for non-OpenAI profiles until provider-specific clients own their official endpoints.
  • Prefer OS credential storage for long-lived secrets and refresh tokens.
  • Recommended implementation: use platform credential storage (Windows Credential Manager, macOS Keychain, Linux Secret Service/libsecret) behind a small Kerux abstraction before persisting OAuth refresh tokens. Until that exists, keep tokens in environment variables or provider-managed stores such as Google ADC / GitHub CLI or Copilot CLI keychain.
  • If OS credential storage is not implemented yet, keep long-lived secrets in environment variables or explicit config only; do not silently migrate them into plaintext JSON.
  • If a plaintext fallback is ever added, it must be opt-in, clearly warned, and protected by best-effort owner-only file permissions.
  • Store non-secret provider id, auth type, created/updated timestamps, expiry, and refresh metadata in auth.json.

Sketch:

{
  "version": 1,
  "profiles": {
    "openai-default": {
      "provider": "openai",
      "method": "api_key",
      "base_url": "https://api.openai.com/v1",
      "secret_ref": "env:OPENAI_API_KEY"
    },
    "google-default": {
      "provider": "google-gemini",
      "method": "oauth_pkce",
      "scopes": ["provider-documented scopes for this flow"],
      "expires_at": "2026-01-01T00:00:00Z"
    }
  }
}

3. Add an auth provider boundary

Introduce a small internal provider-auth abstraction before adding provider-specific flows.

#![allow(unused)]
fn main() {
trait AuthProvider {
    fn id(&self) -> &'static str;
    fn supported_methods(&self) -> &'static [AuthMethod];
    async fn resolve_headers(&self, profile: &AuthProfile) -> Result<HeaderMap>;
}
}

Initial implementations should be minimal:

  1. ApiKeyAuthProvider for the current OpenAI-compatible behavior.
  2. BearerTokenAuthProvider for official OAuth/ADC access tokens where the provider accepts bearer tokens.

Provider-specific request formats should stay separate from auth. Kerux currently has an OpenAI-compatible client; OAuth should not imply that every provider can use /v1/chat/completions.

4. CLI/TUI flows

Future commands:

  • kerux auth login <provider>
  • kerux auth set-api-key <provider>
  • kerux auth set-bearer-token <provider> --env <ENV_VAR> --base-url <URL>
  • kerux auth providers
  • kerux auth list
  • kerux auth logout <auth-ref>
  • TUI command/modal equivalent after CLI flow is stable

Login flow order:

  1. Prefer API key where it is the official provider API path.
  2. Prefer provider-managed credentials first: Google ADC, GitHub/Copilot CLI keychain, Claude Code setup wizards, AWS/GCP/Foundry credential chains.
  3. Offer OAuth only for providers with documented third-party, device-code, or ADC flows.
  4. Use loopback PKCE for native desktop OAuth where supported.
  5. Support no-browser mode only through provider-documented flows such as device-code auth or external tools like gcloud auth application-default login --no-browser; do not invent copy/paste auth-code handling.

5. OAuth implementation constraints

Do not add OAuth until these are decided:

  • Token storage format and permission model.
  • Provider allowlist and scopes.
  • Refresh behavior and expiry handling.
  • How non-OpenAI-compatible providers map into OpenAIClient or a new client abstraction.
  • How product-specific account auth maps to runtime endpoints, especially OpenAI Codex/ChatGPT auth and Anthropic Claude account auth.
  • Whether adding OAuth crates is acceptable, or whether to implement PKCE/loopback using existing dependencies.
  • Whether the project will use OS credential storage crates or keep OAuth behind external helper tools until secure storage exists.

Security requirements:

  • Use PKCE for public/native clients.
  • Bind redirect to loopback only (127.0.0.1), random port.
  • Validate state and provider issuer/token endpoint.
  • Loopback callbacks must accept only authorization codes plus validated state; never accept access tokens from query strings.
  • Redact auth headers in logs.

Phased implementation plan

Phase 1: auth profiles, no OAuth

  • Add local auth metadata store module.
  • Add kerux auth set-api-key <provider> to create a profile that references an environment variable or explicitly configured key source; do not silently persist the secret itself.
  • Move current API-key resolution behind auth profile lookup while preserving env/config behavior and precedence.
  • Add kerux auth list and kerux auth logout.
  • Tests: redacted list output, env precedence, missing-secret error, permission best-effort for metadata file.

Phase 2: Google OAuth / ADC-compatible bearer auth

  • Add Google as the first official OAuth-capable provider.
  • Support existing ADC token discovery or explicit token helper before implementing full browser flow.
  • Use provider-documented scopes per Gemini API vs Vertex AI flow; do not hardcode a single scope globally.
  • Tests: expired token rejection/refresh boundary with mocked token provider.

Implemented Phase 2a:

  • kerux auth set-bearer-token <provider> --env <ENV_VAR> --base-url <URL> stores metadata for externally managed OAuth/ADC bearer tokens.
  • Kerux still does not run browser OAuth or refresh tokens itself.
  • Bearer credentials use the same endpoint binding protections as API-key profiles.

Implemented Phase 2b:

  • kerux auth providers reports provider aliases, documented auth methods, Kerux-supported environment sources, and implementation notes for Google, GitHub Copilot, OpenAI, and Anthropic.
  • OpenAI Codex/ChatGPT auth and Anthropic Claude account/cloud-provider auth are documented as distinct capabilities instead of being collapsed into generic API-key or bearer-token auth.
  • kerux auth login <provider> prints provider-specific external setup guidance and intentionally fails without creating credentials until secure token storage and provider-specific runtime clients are available.

Phase 3: browser PKCE flow

  • Add loopback OAuth helper.
  • Add no-browser flow only for providers with a documented device-code or external-tool path.
  • Tests: state validation, callback parsing, token exchange mock server, cleanup of local listener.

Implemented Phase 3a:

  • Added provider-neutral PKCE/state helpers.
  • Authorization URLs require http://127.0.0.1:<port>/... loopback redirects.
  • Callback parsing accepts only authorization codes with matching state and rejects access tokens in query strings or fragments.
  • Added a loopback callback receiver that binds only to 127.0.0.1 on a random local port and accepts one GET callback.
  • Added provider-neutral authorization-code token exchange helper for PKCE flows. Token endpoints must use HTTPS; tests use loopback HTTP only through private test plumbing.
  • Kerux still does not launch browsers or refresh/store OAuth tokens itself.

Phase 4: provider-specific clients

  • Add provider client abstraction only when the first non-OpenAI-compatible provider needs it.
  • Keep OpenAI-compatible behavior unchanged.

Non-goals for the first OAuth PR

  • Reverse-engineered ChatGPT/Codex login beyond documented OpenAI flows.
  • Reusing Claude Code private credential formats without documented support.
  • Storing tokens in repo-local kerux.toml.
  • Supporting every provider in one change.

Tool Approval (F1)

Interactive approval gate for tool execution via Telegram inline keyboards.

How It Works

  1. Agent wants to execute a tool
  2. Gateway sends a prompt with [✅ Approve] [❌ Deny] inline buttons
  3. User taps a button → Telegram sends a callback_query
  4. The query is routed to a per-tool-call oneshot channel
  5. Agent proceeds (approve) or skips with a denial message (deny)
  6. Timeout → treated as denial

Config

[gateway]
tool_approval = true

Implementation

  • kerux-core/src/approval.rs — approval gate manager
  • callback_query handling in TelegramAdapter::poll_updates
  • answerCallbackQuery ack so the button press registers in the Telegram UI

Context Compaction (F2)

Rolling summarization of old conversation turns to stay within context limits.

How It Works

  1. After each run, check conversation length against the session cap
  2. If near cap, compact_history() sends the oldest N messages to the LLM as a one-shot summarization chat
  3. The summary replaces those messages, embedded as a [CONTEXT SUMMARY] marker
  4. Recent messages stay intact — only the tail is compressed

Session Format v2

Compaction summaries persist across restarts via the session file format v2:

{
  "version": 2,
  "summary": "User asked about X; we decided Y...",
  "messages": [...]
}

Format v1 (bare message array) is still readable — backward compatible.

Config

[gateway]
context_compaction = true

Fallback Provider Chain (F3)

Automatic failover to backup LLM providers on transient errors.

How It Works

FallbackChainProvider wraps a primary provider plus an ordered fallback list. On each request:

  1. Try the current provider
  2. If the error is transient (network failure, 429 rate limit, 5xx, interrupted stream) → advance to the next provider
  3. Non-transient errors (auth failure, bad request) propagate immediately — no pointless retries

Config

Default OFF (empty fallback list = primary only). Enable explicitly:

[fallback]
enabled = true

[[fallback.providers]]
provider = "openrouter"
model = "anthropic/claude-sonnet-4"

[[fallback.providers]]
provider = "gemini"
model = "gemini-2.5-pro"

Implementation

  • kerux-core/src/client/fallback.rsFallbackChainProvider + FallbackEntry + is_transient() detection
  • Wired in kerux-cli via wrap_with_fallbacks() around the runtime client

Voice Note STT (F4)

Telegram voice notes are transcribed to text before hitting the agent.

How It Works

  1. Incoming message with a voice attachment detected
  2. getFile API → download the .oga audio via the bot token
  3. POST multipart to /v1/audio/transcriptions (OpenAI-compatible endpoint, same credentials as the primary client)
  4. Transcript injected as the user message text

Config

OFF unless stt_model is set:

[gateway]
stt_model = "gemini/gemini-2.5-flash"

Any model the provider supports on the transcriptions endpoint works.

Cron Scheduler (F5)

Recurring jobs that fire agent prompts on an interval.

Commands

/cron add <name> <interval> <prompt>   — schedule a job
/cron list                             — show all jobs
/cron pause <name>                     — pause
/cron resume <name>                    — resume
/cron remove <name>                    — delete

Interval Syntax

Stdlib-parsed durations: 30m, 2h, 1d, 1h30m.

Behavior

  • Jobs persist to ~/.kerux/cron/jobs.json (atomic writes)
  • Background ticker in Gateway::run() checks due jobs each tick
  • Downtime burst protection: if multiple fires were missed while the process was down, only ONE catch-up run fires
  • Job output is delivered to the channel that created it

Implementation

  • kerux-core/src/scheduler.rsScheduler with atomic JSON persistence

Subagent Delegation (F6)

The agent can delegate focused tasks to isolated child agents.

How It Works

SubAgentTool registers a delegate_to_sub_agent tool. When invoked:

  1. A child KeruxAgent is spawned with a fresh conversation (no parent history)
  2. The child runs its own ReAct loop and returns a final summary
  3. Only the summary enters the parent conversation — intermediate noise stays out

Guardrails

GuardrailValue
DefaultON
Max concurrent children3 (tokio semaphore)
Nesting depth1 (child registry is empty — children cannot delegate further)

Config

[delegation]
provider = "openai"        # optional: separate provider for children
model = "gpt-4o-mini"      # optional: cheaper model for delegation
max_concurrent = 3

Implementation

  • kerux-core/src/tools/sub_agent_tool.rs

Integration Plan: Aider + Kerux

Status: SHIPPED (Phases 1-5 complete as of 0.1.3 main; deltas vs. spec below)

Created: 2026-08-07


Shipped Deltas vs. Plan

Phase-by-phase reality check against the original scope:

  • Phase 1 (provider routing): shipped with native OpenAI, Anthropic, Ollama, OpenRouter, and Gemini adapters, plus per-model capability tables with longest-prefix matching (lookup_capabilities), supports_vision / supports_tool_calls fields, and <edit_format> prompt hints routed from advertised capabilities.
  • Phase 2 (repo map): shipped (tree-sitter C/Python/Rust/TypeScript, personalized PageRank, token-budgeted renderer). Discovery is capped at 500 files before ranking (discover_source_files_with_limit). Format follows Aider’s outline style; incremental file-watcher ranking not ported.
  • Phase 3 (edit blocks): shipped (edit_block tool, atomic multi-edit, exact+fuzzy sharing patch matching, parsed via parse_edit_blocks; model-level routing via EditFormat::SearchReplace/Patch).
  • Phase 4 (git harness): shipped (kerux_core::githarness with snapshot/guard/commit/undo; TUI /undo). commit_transaction runs per tick in autonomous mode and post-run in the TUI when [agent].auto_commit = true.
  • Phase 5 (skill & memory lifecycle): shipped as [curator] policy + non-blocking pass on startup/tick; option [curator].interval_secs enables periodic mid-session passes. Memory pinning (pinned: true) exempts from decay/prune/dedup and is a serialized MEMORY.md header field. Skill staleness keyed off SKILL.md mtime, not usage telemetry. Skill distillation creates tag-clustered distilled-<tag> drafts; [curator].skill_distill_llm_summary = true rewrites the body via the active LLM (falls back to bullet list on error). Drafts route to _pending/ by default ([curator].auto_approve_skills = false) and require TUI approval (a) before loading. Session archiving is idle-time-only. Trajectory compression folds aging low-importance facts into one session_summary per pass ([curator].compression_*; deterministic, no LLM).

Shipped since original plan: skill provenance metadata (SkillOrigin::Agent/User, pinned, use_count, last_activity_at) in SKILL.md front matter, with provenance-gated auto-archive in the curator.



Executive Summary

Integrate Aider’s three core capabilities into Kerux to bridge model-agnostic LLM access, intelligent repo indexing, and robust git-driven development workflows:

  1. Repo Map — AST-based file ranking + Personalized PageRank for token-efficient codebase context
  2. Edit Format — SEARCH/REPLACE blocks for lean code generation (vs. full-file rewrites)
  3. Git Integration — Auto-commit with Conventional Commit messages + /undo rollback command

Outcome: Kerux gains Aider’s productivity patterns (model-agnostic backend, edit-efficient workflows) while retaining Hermes-Agent’s self-learning loop (skills, memory, curator, cron).


Current State Analysis

Kerux (Rust, v0.1.3)

Strengths:

  • Streaming-first ReAct loop with tolerant XML parsing
  • Self-healing LLM error recovery
  • TUI + autonomous modes with state persistence
  • 99+ unit & integration tests

Gaps:

  • LLM client hardcoded to OpenAI (no Anthropic, Ollama, OpenRouter)
  • File context is naive (full reads or line-offset pagination)
  • Edit tools: only patch and file_write (no efficient edit format)

Memory/Skills: Basic memory.rs with no curator, no trajectory compression, no skill lifecycle.

Hermes-Agent (Python reference, ~12k LOC run_agent.py)

Strengths:

  • Self-learning: curator + skill auto-creation
  • Memory: FTS5 session search with LLM summarization
  • Cron/webhook automation + multi-platform gateway
  • Durable skill + memory state across sessions

Not for direct reuse: Monolithic codebase; valuable as architectural reference only.

Aider (Python CLI, paul-gauthier/aider)

Strengths:

  • Repo map: tree-sitter AST + Personalized PageRank ranking
  • Edit format: SEARCH/REPLACE blocks (token-efficient vs. full-file)
  • Git harness: dirty-tree protection, Conventional Commit generation, /undo
  • Model-agnostic: supports 40+ providers via provider abstraction

Not for adoption: We extract subsystem design, not copy code.


Integration Phases

Phase 1: Model-Agnostic Client (Weeks 1-2)

Goal: Swap hardcoded OpenAI client for pluggable provider abstraction.

Scope:

  • Abstract LLMProvider trait over OpenAIClient
  • Implement adapters: OpenAI, Anthropic, Ollama, OpenRouter
  • Config extension: provider selection + per-provider settings
  • Capability negotiation: max_tokens, edit_format, streaming support

Key Files:

  • New: crates/kerux-core/src/client/provider.rs (trait + routing)
  • New: crates/kerux-core/src/client/providers/*.rs (per-provider impl)
  • Modified: crates/kerux-core/src/config.rs (provider config section)
  • Modified: crates/kerux-core/src/agent.rs (use trait, not hardcoded client)

Design Pattern:

#![allow(unused)]
fn main() {
#[async_trait]
pub trait LLMProvider: Send + Sync {
    async fn chat(
        &self,
        model: &str,
        messages: &[Message],
        tools: Option<&[ToolSchema]>,
    ) -> Result<ChatResponse>;

    async fn chat_streaming(
        &self,
        model: &str,
        messages: &[Message],
        tools: Option<&[ToolSchema]>,
    ) -> Result<ChatStreamResponse>;

    fn capabilities(&self, model: &str) -> ProviderCapabilities;
}

#[derive(Clone)]
pub struct ProviderCapabilities {
    pub max_input_tokens: usize,
    pub max_output_tokens: usize,
    pub edit_format: EditFormat,  // search_replace, patch, full_file
    pub supports_streaming: bool,
    pub supports_reasoning: bool,
    pub feature_flags: FeatureFlags,
}
}

Config Extension (TOML):

[client]
provider = "openai"  # openai | anthropic | ollama | openrouter

[client.openai]
base_url = "https://api.openai.com/v1"
api_key = "sk-..."
model_mapping = {}

[client.anthropic]
api_key = "sk-ant-..."
model_mapping = {}

[client.ollama]
base_url = "http://localhost:11434"
model_mapping = {}

[client.openrouter]
base_url = "https://openrouter.ai/api/v1"
api_key = "sk-or-..."
model_mapping = {}

Backward Compat: Default to OpenAI if provider not specified; existing kerux.toml files continue working.

Verification:

  • All existing tests pass with default (OpenAI) provider
  • New provider tests mock HTTP responses (no live API calls)
  • CLI invocation still works: kerux run --query "test" defaults to OpenAI

Phase 2: Repo Map (Weeks 3-4)

Goal: Replace naive file context with Aider-style repo map.

Scope:

  • Symbol extraction via tree-sitter (defs + refs per file)
  • Personalized PageRank scorer (file importance ranking)
  • Token-budget binary search (fit ranked tags to context window)
  • Render concise file tree with line numbers and key signatures

Key Files:

  • New: crates/kerux-core/src/repomap/extractor.rs (tree-sitter symbol extraction)
  • New: crates/kerux-core/src/repomap/scorer.rs (PageRank ranking)
  • New: crates/kerux-core/src/repomap/budgeter.rs (token-budget trimming)
  • New: crates/kerux-core/src/repomap/mod.rs (public API)
  • Modified: crates/kerux-core/src/agent.rs (inject <repo_map> into system prompt)
  • Modified: crates/kerux-core/src/config.rs (add [repomap] section)

Algorithm Summary:

  1. Extract symbols from all files (tree-sitter AST traversal):

    • Tags: (rel_path, line, name, kind) where kind = def / ref
    • Language support: Rust, Python, TypeScript, C (priority order)
    • Cache to disk (sled KV: key = (path, mtime) → serialized tags)
  2. Build ranked graph:

    • Create directed graph: file → file (edges = identifier references)
    • Apply Personalized PageRank with restart bias toward active chat files + mentioned identifiers
    • Per-definition score = aggregated incoming edge rank
    • Sort tags by descending score
  3. Binary-search trim:

    • Target: token budget (default 1024, scales 8x if no active files)
    • Midpoint candidate: render ranked tags slice, estimate tokens
    • Keep reducing until within 15% tolerance
  4. Render output:

    • Concise tree format: file paths + line numbers + key definitions
    • Preserve relative indentation for nested symbols

Phase 3: Edit Format (Weeks 5-6)

Goal: Add SEARCH/REPLACE block parsing and application.

Scope:

  • Parser: extract file path, search block, replace block from LLM stream
  • Applier: exact match strategy, fuzzy fallback (normalized whitespace), diff-based fuzzy fallback
  • Capability routing: inject verbatim format spec into system prompt if model supports SEARCH/REPLACE
  • Fallback: legacy patch tool for models without SEARCH/REPLACE support

Key Files:

  • New: crates/kerux-core/src/tools/edit_parser.rs (SEARCH/REPLACE block parser)
  • New: crates/kerux-core/src/tools/edit_applier.rs (fuzzy diff applier)
  • Modified: crates/kerux-core/src/agent.rs (system prompt format spec injection)
  • Modified: crates/kerux-core/src/tools/builtin.rs (register edit tool)

Verbatim Block Spec:

Every *SEARCH/REPLACE block* must use this format:
1. The *FULL* file path alone on a line, verbatim.
2. The opening fence and code language, eg: ```rust
3. The start of search block: <<<<<<< SEARCH
4. A contiguous chunk of lines to search for in the existing source code
5. The dividing line: =======
6. The lines to replace into the source code
7. The end of the replace block: >>>>>>> REPLACE
8. The closing fence: ```

Application Strategy:

  1. Exact match (first hit): Find literal search string in file content and replace.
  2. Relative Indentation Strategy: If exact match fails, normalize whitespace per line, locate match, apply replacement preserving file’s indentation.
  3. Fuzzy Search Fallback: Use similar crate diff engine to locate near-match line spans and apply edits.
  4. Validation: Reject empty-to-empty blocks; log error if search block not found in target file.

Verification:

  • Unit tests: parser against Aider output formats
  • Integration tests: exact and fuzzy replacements across multi-line blocks
  • E2E tests: full agent turn with block edits on sample codebase

Phase 4: Git Integration (Weeks 7-8)

Goal: Transactional git workflow with auto-commit and rollback.

Scope:

  • Pre-edit dirty tree check (detect unstaged/staged changes before applying edits)
  • Post-edit Conventional Commit generation (call weak model with diff summary)
  • Subcommand /undo (revert last agent commit safely)
  • Integration into autonomous coding loop

Key Files:

  • New: crates/kerux-cli/src/git_harness.rs (git transactions, Conventional Commit generation, undo)
  • Modified: crates/kerux-cli/src/autonomous.rs (delegate git operations to git harness)
  • Modified: crates/kerux-cli/src/main.rs (register /undo subcommand)

Workflow Sequence:

  1. Pre-Edit Check:

    • Run git status --porcelain to detect uncommitted user changes
    • If dirty and auto_commit_dirty enabled: commit dirty files with message "committing dirty files before agent changes"
  2. Edit Application:

    • Apply SEARCH/REPLACE or patch edits
    • Run validation command (cargo test --workspace)
  3. Post-Validation Auto-Commit:

    • If validation passes: stage edited files (git add <files>)
    • Invoke weak LLM model (e.g. gpt-4o-mini or haiku) with git diff --cached to generate Conventional Commit message
    • Commit: git commit -m "<message>"
    • Append commit hash to autonomous-status.toml history ledger
  4. Rollback (/undo):

    • Check if HEAD commit was created by agent (verify hash in ledger)
    • Safety check: reject if commit has multiple parents (merge) or is pushed (HEAD == origin/<branch>)
    • Revert: git checkout HEAD~1 -- <files> + git reset --soft HEAD~1
    • Print confirmation: Removed: <hash> <commit_message>

Verification:

  • Unit tests: git status parser, Conventional Commit prompt construction
  • Integration tests: git harness on temporary git repository (commit, rollback, dirty-tree protection)
  • E2E test: autonomous loop executing multi-step task with rollback on test failure

Phase 5: Skills & Memory Lifecycle (Weeks 9-10)

Goal: Adapt Hermes-Agent self-learning (curator + skill auto-creation) to Rust.

Scope:

  • Extend skill metadata (created_by, usage_count, last_activity, pinned, state)
  • Curator background task (scan agent-created skills, archive stale skills after threshold)
  • Async skill distillation (extract reusable skills from completed agent runs)
  • Skill search & memory integration

Key Files:

  • New: crates/kerux-core/src/curator.rs (curator review loop & skill archiving)
  • Modified: crates/kerux-core/src/skills.rs (extend skill metadata and lifecycle state)
  • Modified: crates/kerux-core/src/distillation.rs (add skill creation pass after run)
  • Modified: crates/kerux-cli/src/main.rs (add kerux curator subcommands)

Curator Rules:

  • Only touch skills with created_by = "agent" provenance
  • Never hard-delete; max destructive action is archive (~/.kerux/skills/.archive/)
  • Pinned skills (pinned = true) are exempt from auto-archiving and review passes
  • Usage tracking: record use_count, last_activity_at per skill invocation

Verification:

  • Unit tests: curator transition rules, skill metadata serialization
  • Integration tests: skill creation from mock conversation history
  • E2E test: agent creates skill → curator archives after simulated time elapsed

Architectural Invariants

  1. Zero Prompt Cache Invalidation: System prompt and tool schemas must remain stable during a conversation. Configuration changes take effect on next session.
  2. Minimal Footprint: No extra dependencies unless stdlib/existing dependencies cannot solve the problem.
  3. Graceful Fallback: If a model doesn’t support SEARCH/REPLACE, degrade to patch tool; if repo map fails, degrade to full-file loading.
  4. Git Safety: Never commit unvalidated edits; never undo user-made commits; always check dirty tree state before modifying files.
  5. Standard Output & Verification: Every phase requires unit tests, integration tests, and explicit verification against cargo check and cargo test.

Kerux Development Progress & Handoff Document

Last Updated: August 10, 2024
Current Version: v0.1.3 (main branch)
Status: ✅ All Aider Phases Complete - Stable Release Candidate


📊 Current State Summary

Implemented Features (Phases 1-5 Complete)

PhaseFeatureStatusCommit HashNotes
Phase 1Model-agnostic provider routing + capability tables✅ Doneaa53ed6OpenAI, Anthropic, Ollama, OpenRouter adapters; per-model metadata
Phase 2Tree-sitter AST repo map extraction + PageRank scoring✅ Doneb141e7d, bf25a0aC/Python/Rust/TypeScript support; capped at 500 files to prevent stalls
Phase 3Aider-style SEARCH/REPLACE (edit_block) + capability routing✅ Donea697180Atomic multi-edit tool with exact+fuzzy matching
Phase 4Transactional git harness✅ Done2e7a75aSnapshots, dirty-tree protection, Conventional Commits, /undo command
Phase 5Skill & memory lifecycle management✅ Doneaa53ed6, a309db0, 2a4485aCurator passes, decay/prune/dedup, distillation, archival, pinning, LLM prose summarization

Test Coverage

  • kerux-core: 235 tests passing (1 pre-existing env failure: MCP stdio requires Python binary)
  • kerux-cli: 104 tests passing
  • Repomap-specific: 7 tests all passing
  • Clippy: Clean (--all-targets --all-features -- -D warnings)

🎯 Next Steps - Priority Order

Based on TODO.md tracking and natural progression after Aider integration, here are recommended follow-ups in priority order:

High Priority (User Impact)

1. Trajectory Compression (SHIPPED, fact-level)

[curator].compression_min_age_days (default 60), compression_max_importance (90), compression_min_count (5). Deterministic fold of old, low-importance, unpinned fact blocks into a single session_summary per curator pass — no LLM, no token spend. Distilled (importance 90) and pinned facts are exempt. Inter-session message compression was scoped out: MemoryManager persists blocks, not transcripts (see memory.rs — sessions carry metadata only); lifting that ceiling is future work.


2. Auto-commit Wiring (SHIPPED, run-level)

[agent].auto_commit = true auto-commits a successful interactive run’s working-tree changes via GitHarness::commit_transaction (Conventional Commit derived from staged diff). Wired at TUI run completion (tui/app.rs finish_run_if_ready) rather than per-tool call: run-level commits respect /undo as the intermediate rollback and batch all of a run’s edits into one commit. Autonomous mode already committed per tick.


3. Gemini Adapter (SHIPPED)

crates/kerux-core/src/client/gemini.rs: generateContent + streamGenerateContent?alt=sse (one-shot replay through shared SSE parser — true token streaming is next), systemInstruction/contents/functionDeclarations translation, functionCalltool_calls mapping. Capability rows for gemini-2.5-pro/flash + generic gemini- fallback in provider.rs; [client.gemini] section + GEMINI_API_KEY / GEMINI_BASE_URL / GEMINI_TIMEOUT_SECS env overrides in config.rs; ProviderKind::Gemini plumbed through resolve_provider_settings, build_provider_for_kind, CLI factory error text. Chose v1beta (current stable surface with function calling + streaming).


Medium Priority (Quality/UX)

4. Skill Approval Flow (SHIPPED)

Distilled drafts default to <skills>/_pending/ ([curator].auto_approve_skills = false), never auto-load, and appear in the TUI Skills panel with a pending badge. a approves (moves to loadable root, refreshes), d discards. Set auto_approve_skills = true to restore immediate load.


5. Per-Model Edit Format Override (SHIPPED)

[agent].edit_format_override (search_replace | patch | full_file) implemented in config.rs + applied at prompt-hint generation in agent.rs; invalid values rejected by TOML parse error via serde.


Low Priority (Nice-to-have)

6. Vision/Image Input Support

Why: Multimodal agents gain significant capabilities (document scanning, screenshot understanding).

Status: ProviderCapabilities added supports_vision field but no implementation exists yet.

Work needed: Minimal framework setup, heavy integration work:

  • Image preprocessing pipeline (resize, encode, base64)
  • Multimodal request construction per-provider
  • Tool-call restrictions (vision-only contexts disable some tools)

ETA: ~8-12 hours per major provider (Anthropic/Ollama first)


🧪 Known Issues & Technical Debt

Pre-existing Environment Failure

  • Issue: mcp::tests::stdio_client_connects_lists_and_calls_tool fails requiring python binary
  • Impact: None in production use case (MCP stdio just optional feature)
  • Fix required: Install Python on test runners or mock python dependency in tests

Performance Edge Cases

  • Huge repos (>6k files): Now protected via file capping but discovery still scans entire tree first. Optimization: early-return scan once cap hit.
  • Tree-sitter parsing on very large files (>1MB): Parser may stall. Recommendation: implement file size threshold warning in extractor.

Missing Documentation

  • TODO: Update AGENTS.md with new features documentation
  • TODO: Add usage examples to README for [curator] configuration
  • TODO: Write RFC for trajectory compression design before implementation

📁 File Locations Reference

ComponentPrimary FilesPurpose
Repo Mapcrates/kerux-core/src/repomap/extractor.rs, scorer.rs, budgeter.rsSymbol extraction, PageRank ranking, token-budgeted rendering
Git Harnesscrates/kerux-core/src/githarness.rs, crates/kerux-cli/src/tui/app.rs (/undo)Snapshot/restore, dirty-tree protection, Conventional Commits
Curatorcrates/kerux-core/src/curator.rsDecay/prune/dedup/archive/distillation loops
Skillscrates/kerux-core/src/skills.rs, crates/kerux-core/src/repomap/budgeter.rsSKILL.md front matter loading, metadata persistence, archive management
Configcrates/kerux-core/src/config.rs, kerux.example.tomlTOML-based settings runtime config resolution
Providerscrates/kerux-core/src/client/anthropic.rs, openai.rs, ollama.rs, openrouter.rsLLM provider implementations with streaming normalization
Agentscrates/kerux-core/src/agent.rs, crates/kerux-cli/src/tui/app.rsReAct loop orchestration, system prompt construction

🚀 Quick Start for New Contributor

  1. Clone repository:

    git clone https://github.com/yourservice/kerux.git
    cd kerux
    
  2. Install dependencies:

    # Rust toolchain (Rustup recommended)
    rustup update stable && cargo update
    
    # Python (for MCP tests only - optional)
    sudo apt install python3  # Ubuntu/Debian
    
  3. Run tests:

    cargo test --workspace
    

    Expected: 235 core tests + 104 CLI tests = 339 total (1 known skip/failure)

  4. Build binary:

    cargo build --release
    target/release/kerux
    
  5. Start coding:

    • Pick task from “Next Steps - Priority Order” above
    • Read relevant files listed under “What to do”
    • Write failing test first (TDD approach)
    • Run cargo clippy during development
    • Submit PR referencing this progress doc

📞 Contact Information

Primary Maintainers: [List maintainers here]
Slack Channel: #channel-name
GitHub Issues: [Link to issues page]

Handoff Instructions: When handing off project maintenance or transitioning team members, share this PROGRESS.md along with:

  • Full test suite results (cargo test --workspace)
  • Recent commit history (git log --oneline -10)
  • Current open pull requests (gh pr list)
  • Access tokens/secrets rotation schedule

Document generated: 2024-08-10
Repository main branch HEAD: bf25a0a
Branch ahead of origin/main by 10 commits