evictl is a local control plane for evi instances: independent agent sessions
that can run in parallel, receive work through messaging channels, and share
distilled memory over time.
An evi is the always-on AI agent identity. Providers are the execution substrate
that can host one or more evi replicas. When there is only one substrate, the
provider and the evi can look identical, but evictl keeps the concepts
separate so the network can clone and supervise replicas across substrates.
The initial providers are:
- OpenClaw
- Hermes Agent
- Claude Code Channels
The intended shape is a replicated evi control plane: create replicas, route work to them, supervise their liveness, collect feedback and observations, then distribute distilled memory back to the replicas with provenance.
Research notes: docs/research-notes.md
bun install
bun test
bun run check
bun run buildRun the CLI directly during development:
bun run src/cli.ts ps--headless is a global flag for non-interactive automation:
evictl --headless status
evictl status --headless
evictl --headless monitor --onceHeadless mode does not imply JSON output or automatic confirmation. It only
removes interactive UI expectations and rejects commands that would wait
indefinitely without an explicit one-shot form. Use command-specific --json
flags where available.
evictl currently targets macOS and requires Bun because the published CLI entry
uses a Bun shebang.
bunx evictl --help
bun install -g evictlFor local development:
bun link
evictl --helpThe repository includes an Agent Skill at skills/evictl.
Install or vendor it with your agent skill manager, then invoke it when managing
local evi instances, routes, runtime handoff, or shared memory with evictl.
evictl ps
evictl discover
evictl import
evictl status
evictl doctor
evictl target add
evictl evi add
evictl evi clone
evictl evi start
evictl evi stop
evictl identity list
evictl identity show
evictl identity add
evictl identity bind
evictl interface list
evictl interface bind
evictl processor list
evictl processor switch
evictl processor launch-plan
evictl spawn
evictl monitor
evictl stop
evictl tail
evictl route list
evictl route set
evictl memory status
evictl memory promote
evictl memory search
evictl memory export
evictl memory import
evictl memory sync
evictl sync
evictl send
evictl feedback
evictl inspectevictl keeps its own inventory of runtime adapters, evi identities, routes, and
memory sync state. Override defaults with:
~/.config/evictl/config.jsonImport the current local setup:
evictl discover
evictl import --dry-run
evictl importManage routes:
evictl route list
evictl route set telegram:main --target evi-claude-code-channels-nukoevi --account default --mode primaryManage swappable interfaces and processors through an identity:
evictl identity add nukoevi --profile nukoevi --memory-scope nukoevi --processor evi-hermes-agent-grok
evictl interface bind telegram:main nukoevi --kind telegram --address main
evictl interface bind discord:main nukoevi --kind discord --address main
evictl interface bind mqtt:nukoevi/inbox nukoevi --kind mqtt --address nukoevi/inbox
evictl processor switch nukoevi evi-hermes-agent-codex
evictl send nukoevi --text "Run from the active processor."Interfaces such as Telegram, MQTT, CLI, LINE, or Web bind to an identity. The identity owns the persona and memory scope, then points at one active processor evi. Switching the processor changes the inner execution engine without changing the external interface bindings.
For Claude Code Channels, processor launch-plan renders the channel plugins
from the identity's active interfaces:
evictl processor switch nukoevi evi-claude-code-channels-nukoevi
evictl processor launch-plan nukoevi
evictl processor launch-plan nukoevi --jsonCreate another runtime target when a replica has its own launchd plist, tmux session, or process pattern:
evictl target add hermes-agent-grok --provider hermes-agent --label ai.hermes.gateway-grok --plist ~/Library/LaunchAgents/ai.hermes.gateway-grok.plist --tmux hermes-agent-grok --process 'hermes_cli.main.*grok'Create another evi identity:
evictl evi add --provider claude-code-channels --id evi-claude-code-channels-research --profile research --workspace /tmp/research --state-dir /tmp/research-state
evictl evi add --provider hermes-agent --id evi-hermes-agent-research --profile research --state-dir ~/.hermes/profiles/research
evictl evi add --provider openclaw --id evi-openclaw-research --profile research --workspace ~/.openclaw/agents/research/agentCreate Hermes Agent replicas with explicit inference providers:
evictl evi add --provider hermes-agent --runtime hermes-agent-grok --id evi-hermes-agent-grok --profile grok --state-dir ~/.hermes/profiles/grok --model-provider grok --model grok-4.3
evictl evi add --provider hermes-agent --id evi-hermes-agent-codex --profile codex --state-dir ~/.hermes/profiles/codex --model-provider codex
evictl evi add --provider hermes-agent --id evi-hermes-agent-llama --profile llama --state-dir ~/.hermes/profiles/llama --model-provider llama.cpp --model local-model --base-url http://127.0.0.1:8080/v1For Hermes Agent, --model-provider records the process-level inference
provider. Aliases such as grok, grok-oauth, and supergrok normalize to
xai-oauth; codex normalizes to openai-codex; llama.cpp normalizes to
Hermes Agent's custom provider. inspect <evi> prints the environment that a launchd
plist, tmux wrapper, or one-shot launcher can use:
evictl inspect evi-hermes-agent-grokspawn <provider> remains as a compatibility alias for evi add. evi clone
creates a new replica entry from an existing evi and records replica_of.
evi start and evi stop operate the configured provider target for an evi.
Fresh runtime-native profile creation is still intentionally adapter-specific:
the inventory records the desired replica, provider, network, workspace,
state dir, agent id, session id, model provider, model, base URL, and runtime
environment, but does not invent provider-specific setup commands.
The importer reads launchd setup for Hermes Agent, Claude Code Channels, and
OpenClaw. Running runtimes are imported as primary routes; stopped runtimes are
imported as standby routes so duplicate Telegram ownership stays visible and
explicit.
route set refuses duplicate primary ownership for the same
channel/account/peer unless --force is passed.
Record feedback into the shared memory event log:
evictl feedback evi-claude-code-channels-nukoevi --verdict remember --text "Prefer explicit route ownership."Feedback is appended as JSONL with the target evi, source, verdict, confidence, subject, and text. This is the first shared-memory sink; later sync commands can compile those events into runtime-native memory stores.
Promote and sync memory:
evictl memory promote
evictl memory search ownership
evictl memory export
evictl memory import
evictl memory sync
evictl syncmemory promote compiles feedback events from the JSONL event log into
compiled_notes/feedback.md.
memory search searches the JSONL event log and compiled memory notes. Use
--json for machine-readable results.
memory export prints the compiled network memory to stdout. memory import
is an alias for memory sync.
memory sync builds compiled_notes/network.md from provider memory sources and
writes a managed evictl:network-memory section back into provider-visible
sinks:
- Hermes Agent:
<state_dir>/memories/MEMORY.mdand<state_dir>/memories/USER.mdare sources;<state_dir>/memories/MEMORY.mdis the managed sink. - OpenClaw:
<workspace>/MEMORY.md,<workspace>/USER.md,<workspace>/IDENTITY.md,<workspace>/SOUL.md,<workspace>/DREAMS.md,<workspace>/dreams.md, and Markdown files under<workspace>/memory/are sources;<workspace>/MEMORY.mdis the managed sink. - Claude Code Channels: Claude Code reads
CLAUDE.mdfiles and the configured appended prompt.evictlwrites<state_dir>/evictl-network-memory.mdand also updates an existing generated prompt file when present.
sync runs both event promotion and network memory sync.
Supervise configured providers:
evictl monitor --once
evictl monitor --interval 60monitor checks all configured targets, starts stopped targets through their
launchd plist when possible, and runs network memory sync after each pass.
Read recent runtime output:
evictl tail claude-code-channels
evictl tail evi-claude-code-channels-nukoevi --lines 120tail reads recent tmux pane output for a configured target or evi.
Send a task:
evictl send evi-claude-code-channels-nukoevi --text "Run the check suite." --queue-only
evictl send evi-claude-code-channels-nukoevi --text "Run the check suite."
evictl send nukoevi --text "Run through the active processor."send records a task event before dispatch. For evi entries with a tmux
session_id, it sends the task into that tmux session. Non-queued sends require
a configured and running tmux session. Identity targets resolve to their active
processor evi before dispatch. --queue-only records the task without
delivering it.
Example:
{
"targets": {
"claude-code-channels": {
"provider": "claude-code-channels",
"label": "com.local.claude-telegram-channel",
"plist": "~/Library/LaunchAgents/com.local.claude-telegram-channel.plist",
"tmux_sessions": ["claude-telegram-channel"],
"process_patterns": ["claude.*plugin:(telegram|discord|fakechat)", "nukoevi-(telegram|discord)", "claude-telegram-channel"],
"health_patterns": ["Listening for channel messages from:"]
}
},
"evis": {
"evi-claude-code-channels-nukoevi": {
"runtime": "claude-code-channels",
"provider": "claude-code-channels",
"profile": "nukoevi",
"agent_id": "",
"session_id": "",
"workspace": "~/Documents/claude-code-channels",
"state_dir": "~/.local/share/claude-telegram-channel",
"model_provider": "",
"model": "",
"base_url": "",
"env": {}
}
},
"identities": {
"nukoevi": {
"profile": "nukoevi",
"memory_scope": "nukoevi",
"active_evi": "evi-claude-code-channels-nukoevi",
"description": ""
}
},
"interfaces": {
"telegram:main": {
"kind": "telegram",
"address": "main",
"identity_id": "nukoevi",
"mode": "primary"
},
"discord:main": {
"kind": "discord",
"address": "main",
"identity_id": "nukoevi",
"mode": "primary"
}
},
"routes": {
"telegram:claude-code-channels:nukoevi": {
"channel": "telegram",
"account_id": "default",
"peer_id": "",
"target_evi": "evi-claude-code-channels-nukoevi",
"mode": "primary"
},
"discord:claude-code-channels:nukoevi": {
"channel": "discord",
"account_id": "default",
"peer_id": "",
"target_evi": "evi-claude-code-channels-nukoevi",
"mode": "primary"
}
},
"memory": {
"event_log": "~/.local/share/evictl/events.jsonl",
"compiled_notes": "~/.local/share/evictl/memory"
}
}evictl prevents accidental duplicate ownership of the same human-facing
channel, account, peer, or session. Multiple evi instances are allowed, but
fanout and mirror routes must be explicit.
Shared memory is compiled from provenance-rich events instead of blindly copying raw transcripts between runtimes.