klanker
Adaptive agent distribution on the Lolaplex stack.
Terminal companion, background care runner, and self-hosted assistant on agents-harness.
Quickstart
pip install klanker
Optional suite extras:
pip install "klanker[browser]" # CDP browser automation
pip install "klanker[keys]" # Ed25519 agent key minting and DID resolution
pip install "klanker[suite]" # Complete Lolaplex suite
Architecture
| Layer | Responsibility | Stack / Package |
|---|---|---|
| Brain | Deterministic loop, LLM streaming, Cordis kernel, Koru schedules | agents-harness |
| Memory & Docs | Persistent local markdown memory, typed facts, header-aware BM25 doc search | agents-memory, agents-docs |
| Observability | Append-only JSONL event logging & turn reconstruction | agents-traces |
| I/O Gateway | Universal inbound/outbound HTTP (/v1/turn, /v1/inject) and Telegram long-poll |
agents-relay |
| Tools & Feelers | Jailed execution, CalDAV calendar, CDP browser, agent DID keys | agents-terminal, agents-calendar, agents-browser, agents-keys |
- Brain (
agents-harness): Deterministic execution loop, LLM completions, Cordis job catalog, and Koru scheduled flows. - Memory & Docs (
agents-memory,agents-docs): Persistent local markdown memory and header-aware documentation search. - Tracing (
agents-traces): Zero-bloat JSONL observability and turn reconstruction. - Relay (
agents-relay): Stdlib HTTP and Telegram gateway for turns, alerts, and notifications. - Calendar & Tools (
agents-calendar,agents-terminal, optionalagents-browser,agents-keys): Pre-wired capabilities for schedules, safe commands, and web interaction.
Capabilities & Roadmap
Core Capabilities (Implemented)
- Adaptive Host Sensing (
klanker sense): Automatic host inspection and runtime discovery. - Dynamic System Prompt (
klanker prompt): Context-aware prompt generation grounded in host capabilities and time. - Multi-Surface Shell: Interactive REPL, one-shot CLI turns, background care worker (
cron), and gateway server (serve). - Full Suite Integration: Core bindings for
agents-harness,agents-relay,agents-memory,agents-docs,agents-traces,agents-terminal, andagents-calendar. - Telegram Reminders: Dynamic reminder scheduling routed to
agents-relay send.
Planned (Roadmap)
- Graphic Architecture Map: Visual system diagram asset hosted on GitHub.
- Autonomous Mesh Coordination: Distributed multi-instance task delegation via A2A.
- Desktop Companion GUI: Native workbench integration with
klanker-desktop.
Commands
| Command | Description |
|---|---|
klanker |
Start interactive multi-turn REPL chat |
klanker "message" |
Run a single turn directly in terminal |
klanker sense [--json] |
Probe host capabilities, suite packages, MCP servers, and skills |
klanker prompt |
Inspect dynamic system prompt generated for current host |
klanker serve [--no-telegram] |
Start HTTP (/v1/turn), Telegram long-poll, and the schedule ticker |
klanker cron [--flow <name>] |
Run scheduled care flows via harness executor |
klanker remind add --user <user> --channel <channel> --at <when> --text <msg> |
Fixed-text reminder on that channel |
klanker routine add|list|remove|run |
LLM routines (prompt + at or cron) |
Schedules
klanker serve runs a ticker every 60 seconds. KLANKER_TICK=0 turns it off. The ticker calls runner.schedule.tick().
A harness that exposes register_routine_handler locks <schedules>/tick.lock inside tick(). Klanker does not lock that file. A second flock on it in the same process deadlocks. On older harness builds (no handler), the ticker takes a non-blocking lock on a different file, ~/.agents/schedules/.tick.lock (or $AGENTS_SCHEDULES_DIR/.tick.lock), so two Klanker processes do not overlap. The scheduler starts each tick() on its own thread so a long job cannot push the next cron slot past the grace window.
Routines are job files with "kind": "routine". The verb is python -m klanker routine run <name> --scheduled, so a verb-only harness tick still runs them. Each job stores the originating channel and user (the turn channel, or KLANKER_CHANNEL, or local). A same-minute duplicate is dropped via <name>.json.last. Replies that are exactly NO_UPDATE (or start with it) are not sent. Telegram chat ids go out through agents-relay send. Any other channel is printed locally, including an HTTP user such as anonymous. klanker routine add without --timezone stores runner.schedule.configured_timezone() (AGENTS_TIMEZONE, then TZ, then ~/.agents/config.json, else UTC). The routine turn subprocess timeout is 30 seconds under the job timeout_sec. The default timeout_sec is the harness approval wait (AGENTS_APPROVAL_TIMEOUT, else the approver --timeout plus 15 seconds) plus 330 seconds, 645 with the default approver, so a turn waiting on an approval is not killed first. Routines added through mcp.schedule.add without a timeout get the same default. When the installed harness accepts --detached-session, routine turns pass it.
klanker serve sets AGENTS_MODULES_DIR to the overlay directory (~/.agents/modules, or /data/.agents/modules in the image) when it is unset. Harness ignores overlay modules, including mcp.schedule.add, unless that variable is set. A custom LOOP_CMD skips Klanker's system prompt and klanker.turn shims; serve logs a warning when it is set.
klanker remind add --user 123456 --channel telegram --at +10m --text "stand up"
klanker remind add --user 123456 --channel telegram --cron "0 8 * * 1" --prompt "Weekly review" --timezone UTC
klanker routine add --name morning --user 123456 --channel telegram --cron "0 8 * * *" --timezone UTC --prompt "Anything new?"
klanker routine list
klanker routine run morning
klanker routine remove morning
The schedule tool mcp.schedule.add accepts text (fixed reminder) or prompt (routine). Passing prompt through the model tool depends on the harness forwarding that argument. klanker.turn appends --prompt when the current harness special-case drops it.
External schedulers (cron, systemd timer, PaaS scheduled task)
klanker serve already ticks. Remove external python -m runner.schedule tick tasks.
If you keep one, it must run as the service user. A root shell (docker exec without a user) creates root-owned lock and state files. It also skips AGENTS_APPROVAL_CMD, which is set in klanker serve, so those tool calls are ungated. Example: setpriv --reuid=<service user> --regid=<service group> --init-groups python -m runner.schedule tick.
Do not flock tick.lock around that command. The harness locks it inside tick(), and a second flock in the same process deadlocks. .tick.lock is only the outer lock for harness builds that have no register_routine_handler.
Approvals
When an approver is available, serve sets these (without overriding values you already exported). That includes a Telegram poll, AGENTS_RELAY_APPROVER plus a bot token, or an AGENTS_APPROVAL_CMD you already exported. A CLI with none of those stays ungated.
AGENTS_APPROVAL_CMD='agents-relay approve --user {user} --timeout 300'
AGENTS_APPROVAL_MODE=ask
The harness substitutes {user}. Mutating tools wait for Approve / Deny. A denial is final.
Telegram refuses to start when TELEGRAM_BOT_TOKEN is set and TELEGRAM_ALLOWED_CHAT_IDS is empty. Opt in to an open bot with KLANKER_TELEGRAM_OPEN=1. That also sets AGENTS_RELAY_ALLOW_ANYONE=1 when it is unset, which is what relay requires before it will poll with an empty allowlist. Routine delivery passes allow_anyone through to send_to_user.
MCP servers and skills
External MCP servers use Claude/Cursor-shaped ~/.agents/mcp.json (override AGENTS_MCP_CONFIG). An example is examples/mcp.json. klanker sense expands ${VAR} in url, command, args, and env the same way the harness does, then lists each server and a lightweight health check (command on PATH, or the URL accepts a connection).
The harness reads ~/.agents/skills/*/SKILL.md (AGENTS_SKILLS_DIR). In the container, HOME=/data, and entrypoint.sh creates /data/.agents/skills on the data volume.
Docker
The image installs agents-harness[mcp] (stdio and HTTP MCP client) and agents-browser (CDP client: mcp + websockets). It does not install Chromium. Set AGENTS_BROWSER_BIN or install a browser on the host if you use it. agents-browser has no --help-json yet; search, read, snapshot, open, and screenshot are hand-written manifests.
Git installs are cached by the image layer. Rebuild with --no-cache, or change CACHE_BUST / SUITE_REF, or the harness and relay commits stay stale. After the image is running, pip freeze | grep agents- should list git commits. AGENTS_VISION=1 makes image attachments vision parts (see .env.example).
Dependency floors: agents-harness[mcp]>=0.1.0, agents-relay>=0.1.0, agents-memory>=1.2.0, agents-traces>=0.1.0, agents-calendar>=0.1.0. The image installs the suite from git main (the latest releases) by default. Set SUITE_REF, or a per-package ref such as HARNESS_REF, to build from another branch or tag.
agents-traces --help-json is a raw argparse dump and does not list subcommands. stats, inspect, sessions, verify, and cleanup are hand-written. audit and seal stay the harness modules. Anything else is mcp.traces.argv after generation.
agents-keys is not in the image. If you install it, only read verbs (did, resolve, ssh-pubkey) are exposed unless KLANKER_KEYS_WRITE=1. vand is exposed only when it is installed.
The entrypoint does not chmod 666 the Docker socket. As root it adds klanker to the socket's group (setpriv --init-groups keeps that group). If that still fails, set the service group_add to the host socket gid (stat -c %g /var/run/docker.sock).
Tests
pytest
License
MIT. See LICENSE.
Metadata
Release files for klanker 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| klanker-0.1.0.tar.gz | 51.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| klanker-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 98.2 kB
Release files / klanker-0.1.0.tar.gz
| Download URL | klanker-0.1.0.tar.gz |
|---|---|
| Size | 51.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca21f86c6333ed3735a9891306514d8ca42d912eac7c8dab04d0e8f76ab28e33
|
|
BLAKE2b-256 checksum How to use checksums |
0492a9e3056a14a07f77286f1c66bffe2292456cf118f0f7136d36eb212893dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / klanker-0.1.0-py3-none-any.whl
| Download URL | klanker-0.1.0-py3-none-any.whl |
|---|---|
| Size | 47.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
02f494d300a6efbac8946be10ad346445ee6393ba221af8b38b9845cf58e0b96
|
|
BLAKE2b-256 checksum How to use checksums |
222e4336d09cf1ced0e7fd17d823e6c56fc7710b97deb1884b56ac9a68428048
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|