My Mac does a lot of AI work now: a local 27B model for everyday prompts, and hosted coding agents for the jobs a local model can't do. For a while that meant a handful of terminals, each running something, and a vague memory of which one was doing what. When the Mac restarted, or I closed the wrong window, work vanished.
eki is my answer: one queue on the Mac. I ask once — eki ask "fix the failing test" — and eki decides where it goes, runs it, and keeps it running. It's still in progress and not public yet, but the design has settled enough to write about.
Rule one: a restart at any moment loses nothing
This is the rule every other decision is checked against, and it shapes the whole architecture.
- The engine holds no state. Everything lives in one SQLite file (in WAL mode, with a busy timeout, so several processes can write safely). The engine is a manager, not a parent: on every tick it reaps dead workers, routes queued runs, checks whether the machine has room, and spawns workers.
- The work happens in detached workers, one per run. Each worker starts the program — Claude Code, Codex, or a local model — reads its output, and writes events straight to the database. Killing the engine leaves every worker running.
- A dead worker means interrupted, never failed. The worker records the program's session id as soon as it has one, so when the engine notices a worker has died, the run goes back in the queue and resumes in the same session. After three interruptions in a row, it does fail — something is wrong and retrying forever won't fix it.
A rule like this is only real if it's tested, so there's a test for it — the drill — that kills things at awkward moments and checks nothing was lost. It runs on every change.
Rule two: everything is a run
Every request becomes a row in the database before it starts: queued, running, done, failed, cancelled, handed off, or interrupted. Everything that happens during it is an event. That makes the whole system inspectable from the command line — eki runs, eki follow <run>, eki show <run> — and it means the question "what is my Mac doing right now?" always has an answer.
Rule three: routing explains itself
The most useful feature turned out to be a small one. Every run stores why it went where it went, as a readable chain:
rule: names a path → code → claude (codex last: five_hour 82%)
A request passes through five layers in order. First, a thread stays with the provider that's been answering it, unless that provider can't take the new request. Second, constraints: pure code that compares what the request needs (text, tools, web, vision) against what each provider can do, plus whether it's available or cooling down after hitting a limit. Third, intent: simple rules catch the obvious cases — a folder path means code work, an attached picture means vision — and only when no rule fires does a small model classify the request. Fourth, preference: each kind of work has an order of targets, and one near its usage limit moves to the back. Fifth, failover down that list.
When nothing else decides, the default is cheapest first: the local model, then subscriptions, then pay-per-token APIs. eki route "…" prints the chain without running anything, which is how I tune the rules.
Rule four: background work waits for room
A run has a priority: now (I'm waiting for it) or background. Background runs are gated by the machine itself — memory pressure, load, and whether the Mac is plugged in. A 27B model uses around 14 GB of memory, so this matters: a background job shouldn't make the Mac crawl while I'm using it,.
Rule five: integrate, don't rebuild
eki doesn't build its own agent harness or tools. Models come from providers, harnesses from their makers, tools from MCP servers, instructions from skills. eki's job is to hand each run the same set of skills and tools in whatever form each program expects. And credentials stay mine: subscriptions are reached only by running the official command-line tools as me. eki never reads or copies a token.
Small files, on purpose
One last rule sounds cosmetic but isn't: no module over 400 lines, enforced by a test. The previous version of eki had a few very large files, and when several agents worked on it in parallel, they constantly collided in the same file. Small files with one job each make parallel changes merge cleanly — which matters, because more and more of eki's changes are proposed by agents working on eki itself.
I'll write more about that part — eki planning and building changes to itself, with me as the reviewer — once it's further along.