Skip to content

feat(king): King Leon, a coordinator agent for every project - #61

Merged
viktorvillalobos merged 46 commits into
mainfrom
feature/coordinator/init
Oct 21, 2026
Merged

viktorvillalobos merged 46 commits into
mainfrom
feature/coordinator/init

Conversation

@viktorvillalobos

Copy link
Copy Markdown
Contributor

King Leon: a principal engineer for every project

King is one agent that answers for every project Leon knows. You talk to it the way you would talk to a principal engineer: how something works, what state a project is in, what to build next, or to build it. Short questions it answers itself; long investigations and code changes go to workers, other agents (Claude Code, Codex, opencode, Grok, Cursor) on the cheapest model that can do the job, each in a worktree of its own. King commits, pushes and opens pull requests. It never merges, never pushes to a default branch and never forces a push; the tools refuse it, not only the prompt.

Full description: docs/KING.md.

What is in it

  • leon-king (new crate, pure): the coordinator as a state machine (King's conversation, its workers, the wake that gives King its workers' news so it never polls), the router (tiers, job rules, usage windows, daily budget), the transcript, King's instructions, the control protocol, the model catalogue (talents, benchmarks with source and date) and the report under every answer.
  • The service (leon king serve): runs headless, without the window, on a private Unix socket (same-uid peer check). The window, the command line and King's own MCP tools are its clients. State and past conversations survive restarts.
  • King's tools (leon king mcp): projects, usage, models, workers (start, tell, answer, interrupt, stop, transcript), worktree diff, pr_open with guards, notify_user. King also gets Leon's memory and task boards (leon task mcp --anywhere, with a project argument).
  • leon-turn: a turn can be given MCP servers and instructions (Claude, Codex app-server, ACP), and every adapter reports the turn's tokens.
  • The window: "The King" in the sidebar (Ctrl+Shift+Alt+K):
    • King's conversation on the left, with a report strip under each answer (tokens, cache, tok/s, cost, the day's spend, a bar per usage window).
    • Workers as cards on the right; a card opens the worker's conversation live, to write to it, stop it, or take it over into the sidebar as a normal session.
    • Questions from King or a worker are answered with buttons or Alt+number.
    • Chips under the field change King's agent, model, effort and permission mode.
    • NEW and HISTORY keep, read and resume past conversations.
    • A task card is given to King with a.
  • Configuration: every choice is in king.toml over Leon's defaults (crates/leon-king/src/defaults.toml), merged key by key; leon king config [--defaults] prints it.

Defaults worth a look

  • King runs on Claude Code in auto mode. Workers start in a mode that edits their own worktree without asking (codex auto, claude acceptEdits), because every question a worker asks costs one of King's turns.
  • Plans are a strict job of the planning tier (best models); research goes to cheap, code to standard.

Not in this PR

King on the phone, leon king service install, workers on SSH and relay machines, terminal-only agents as workers.

Testing

  • scripts/check.sh passes, after merging main (one conflict in ui/shell.rs: both sides' commands kept).
  • Unit tests:
    • the coordinator, the router, the configuration and the history;
    • the protocol, the MCP tools and their guards;
    • the window's King view, with a fake service: connecting, the conversation, cards, taking over, answering by key, changing model and mode, past conversations, giving a task to King, the watching signal.
  • Run for real:
    • in the nested Hyprland rig, with King on Claude Haiku delegating to opencode workers on free models;
    • locally against real projects: King on Opus with a Codex worker restyling a project in its worktree.

TurnSpec gains mcp_servers and instructions. Claude Code is told them
with --mcp-config and --append-system-prompt, Codex in the thread's
config and developerInstructions, and the Agent Client Protocol's agents
in the session's mcpServers. Nothing sets them yet: the coordinator
will.
leon-king holds what can be decided about King Leon without touching a
disk: king.toml, the state of King's conversation and its workers and
the effects each change asks for, the wake with news of the workers,
the router that picks an agent by tier, usage and budget, the short
transcripts, King's instructions and the control socket's messages.
leon king serve keeps King's conversation and its workers without a
window: it runs their turns on pipes, saves its state after every
change, wakes King with its workers' news and listens on a control
socket only the user may open. King's agent runs in <data dir>/king with
instructions listing every project, and is given leon king mcp (status,
projects, usage, workers, diffs, pull requests, notifications) beside
the shared memory and the board. Workers get a worktree of their own,
never the default branch; pull requests are pushed without force and
never merged. leon king say|status|log|start|tell|answer|stop|release|
brain talk to it, starting the service when nothing answers.

See docs/KING.md.
A new event, Usage, says what a turn read and wrote so far: Claude Code's
result usage, the usage of an Agent Client Protocol prompt's answer, and
what Codex's thread added since the turn began. The phone and the window
do not show it yet; the coordinator reads it.
…nder every answer

King is told every model the agents of this computer offer, asked the way
the window's conversations ask, with what is known of each: talents from 1
to 5 and benchmarks with their sources. A built-in catalogue holds Leon's
starting estimate and no benchmark; king-models.toml in the data folder,
which King writes with model_note, wins over it.

A plan job always runs on the planning tier (Opus at high effort, then
Codex at high effort by default) and cheaper models are refused for it.

Every turn is measured from the agents' own counts: tokens read, cached
and written, tokens per second, cost. Under each answer of King's Leon
writes that, what was spent and used today, and what is left of each
agent's usage windows, and King reads the same before every turn.
… a file

Leon's defaults for King are defaults.toml, commented key by key, and the
person's king.toml is read over it key by key: a table named changes only
the keys written, a tier named replaces that tier alone, and [] removes
one. Where each kind of job goes is [jobs] (plans: the planning tier,
strict), and the branches nothing is pushed to are [guards]. King's
instructions list the jobs' rules from the configuration. leon king
config prints the configuration in force, --defaults the defaults.
King Leon is a row of the sidebar's navigation and a view of the main
pane (Ctrl+Shift+Alt+K): King's conversation with its field on the left,
the workers it runs as cards on the right. A card opens the worker's
conversation live, where the person can write to it, stop its turn, stop
it or take it over; a worker taken over opens as a session of the window
that continues its agent's conversation. Workers are never in the sidebar
while they are King's.

The window is one more client of King's service: a link opens the
control socket (starting the service when nothing answers), asks for the
status and for the entries it lacks, and is told of every change. Tests
play the service through a fake link.

The status now carries how each question may be answered, Leon's news to
King is a transcript entry of its own, and notices say whether the
conversation shows them already, so the window does not say them twice.
Alt and an answer's number answer the question in view (the open
worker's, else King's), and each button says its key. The chip under
King's field, or the palette's "Change the model King runs on…", asks
for the agent, one of the models the service found, and the effort, and
moves King there. The window tells the service while King's view is open
in the window in front, and while anybody looks, what the conversation
shows already is not also sent to the desktop.
* How much King asks is a chip beside its model: the permission modes its
  agent lists, kept like the model (`[brain] mode`).
* "The King" sits above The Den; its answers get more line height and
  room, and the report under each is a strip: the turn's numbers, the
  day's, and a bar per usage window. The report's facts now travel with
  its entry.
* King puts tasks on the board of the project it names (`task mcp
  --anywhere`, a `project` argument), and a card is given to King with
  `a` or from its details.
* NEW puts King's conversation away; HISTORY lists the past ones, to read
  and to take up again (`leon king new`, `history`, `resume`).
* King runs in its agent's auto mode by default, and workers edit in
  their own worktree without asking (codex `auto`, claude `acceptEdits`):
  every question cost one of King's turns to answer. `""` is the agent's
  own mode.
* What a worker asks is framed in King's view with its answers, without
  opening the worker; Alt and a number answer it.
* King always names the model as well as the agent, and is told where to
  read the model a worker's agent chose by itself.
An image pasted into King's field is kept as a file in King's folder and
its path written, for King and its workers to open; on Linux, where the
toolkit sees no images, it is read with wl-paste or xclip. The paperclip
attaches files as their paths, and dictation writes into King's field (or
an open worker's) with the conversations' microphone and speech model.
…r result

* A permission's choices are named by what they do (`d.2 = deny`), and
  King answers with allow, always or deny: a guessed Codex id denied what
  was meant to be allowed, and a denial ends a Codex turn.
* A worker whose turn was stopped (a denial, an interrupt) waits, paused,
  instead of being marked failed.
* A turn is over once the agent said its result, even while its process
  runs on for a program it left behind (a dev server); RUNNING lists those
  programs, with STOP. A turn in flight shows a spinner and its time.
* Pasted images and attached files are chips over the field, sent as
  paths with the words.
…versations go on fresh

* Leon answers a worker's routine permission itself, without waking King:
  an edit in its own worktree, a command that only reads, and tests,
  builds and servers run in the worktree. A push, a write out of the
  worktree, or a command it cannot read for sure still goes to King. The
  rules are `[permissions]`.
* A turn of King's that read `compact_at_tokens` or more (600k) puts the
  conversation in the history; King goes on in a new one, told where it
  left off.
* The field's key is part of its button: SEND ⏎, STOP ESC.
…its CI

A worker saying it is done is not evidence. `worktree_check` runs the
project's own check (scripts/check.sh, make check, cargo test, the
package's check or test script, go test, pytest, or `[checks]` in
king.toml) in the worker's worktree on its last commit, on a thread of
the service's; King hears how it ended as news, with the end of what it
printed, and the whole log stays in king/checks/<worker>.log.

`pr_open` now needs a passed check of the branch's last commit
(`[guards] verify_before_pr`), or a reason it cannot be checked, and
writes the evidence in the description. On a branch whose pull request
is open it pushes to it. The service then watches the pull request's CI
with `gh` every `ci_poll_secs`, without King's turns, and wakes King when
it settles: which checks failed, or that it passed, which also tells the
person it is theirs to review. Each worker's card shows its check and
its CI, and opens the pull request on a click.

Also fixes what CI found on this branch: `king::Paths::new` used the
Unix-only keeper on Windows, and the take-over test ended while the
thread typing the agent's line still wrote to the terminal (macOS).
… reads are routine

A request to read or resume a past conversation named it `id`, the
envelope's own field once flattened: the service read the envelope's
number instead, and refused. It is `conversation` now, with a test that
every request survives the round trip.

Each worker keeps the conversation that started it. WORKERS shows those
of the conversation on screen, a past one's while it is read, and the
earlier ones' that still work or ask apart; a conversation that goes on
in a new one takes its workers along. Workers saved before are given the
conversation that ran when they started.

Leon also answers a worker's question about an MCP server's tool when the
tool's name only reads (`[permissions] mcp_reading`); a name with a word
of `mcp_writes` (create, delete, send, pay, generate…) still goes to King.
@viktorvillalobos
viktorvillalobos force-pushed the feature/coordinator/init branch from 4971026 to be9f7db Compare October 19, 2026 10:54
opencode 2.0.32 lists every provider's models in its answer to
session/new and sends no config_option_update after it. A turn that
asked for a model waited for that update forever and never sent its
prompt: every opencode worker of King's hung on its first turn.
A task can be a step of another (its parent) and need other steps done
first; the store refuses needs across boards and in a circle. A run of
a task starts each step once what it needs is done and checked, at most
run_parallel at once: in a worktree whose branch starts from what it
needs, with a worker from its tier. When the worker finishes, Leon
commits what it left and runs the project's check; a failed check goes
back to the worker run_fixes times. King hears only when the run is
done or blocked, and goes on with run_continue or calls it off with
run_cancel.

Started from the board (r, or Run its steps in the drawer, which lists
the steps and how the run goes), from King (run_start) or from
`leon king run`. Steps' tasks move across the board as they go.
Every answer drew its report with a bar for each usage window, so two
answers in a row stacked two tall boxes. An older report is now its line
of numbers, and one of a turn that used no tokens is left out.
leon king service install | uninstall | status | logs is leon host
service for King: a systemd user unit or a launchd agent of its own that
runs leon --data-dir <dir> king serve with the installing session's PATH,
so King and its workers go on with the window closed and come back after
a failure.
The wire gains King and KingClosed: lines of King's control protocol that
the host passes through, both ways, to King's service on that computer, one
connection per session opened by its first line. The host reads none of
them and says it offers them after the Den in its ShareState answer. Both
the window's host and leon host have the door, which starts the service
when nothing answers, as leon king does.

leon-link carries the lines (king_send, king_events) and leon-mobile
offers king_ask, one request and its reply, and king_watch, King's notices,
subscribing again after the line closed.
The phone reads King of the computer it paired with: its conversation, a
field to say something, its workers and runs, and the notices King sends,
followed while the screen is open. The computer's screen shows a King row
when its host says it can. The bindings are generated again for king_ask
and king_watch.
…e Den

open_terminal asks the window to run a command in a session of its own,
where the person sees its output: a dev server, a watcher. The service keeps
the request until a window takes it, and only the first that asks gets it.

Each worker that is still King's is a lion of the Den, as King's status
says it stands and, while it works, as its transcript says; opening it
opens its card. The window joins King's service whenever it runs, without
starting it, so both follow King with its view closed.
TerminalTaken named its terminal id, which the envelope flattens over and
reads as its own number, so no window could take one. It is terminal now,
and the envelope test covers both new requests. The window also says, in
its log, when it is on the line to King's service and why a terminal was
not its to open.
Every worker leaves an outcome: each run of its check and whether it
passed, its pull request's CI, a failed turn, what it cost and took. They
are summed per agent, model and kind of job into a record kept with King's
state. King reads it in models_list, and a tier passes over a model that
keeps doing poorly at a kind of job while another candidate has room
(learn_after, learn_below), saying so under passed_over.
A project on another machine gets its workers there: the worktree is made
there, the agent runs there through the window's Reach, and the check, the
diff, the pull request and the project's status run there. King's tools ask
the service to run git and gh on another machine's folder; worker_start and
open_terminal take the machine of an existing folder.
King can name an agent of Leon's catalogue it cannot drive on a pipe (Kimi,
ZCode, Antigravity...): its worktree is made as for any worker and a window
opens the agent there, on its prompt when the catalogue says how it takes
one, else with the prompt shown above it. Leon runs no turn of it, so it
cannot be told more, and its cost is recorded as not known rather than free.
The palette that picks King's model says, beside each model, what its record
says per kind of job. A terminal worker's test gives the test computer Kimi
instead of every test.
A conversation's foot says what the last turn read and wrote and how fast,
as the agent counts it. The phone's turns now carry the same count.
The King screens answer the questions King and its workers wait on, tell a
worker more, interrupt or stop it, and start, go on with or cancel runs. A
worker on another machine or in a terminal says so.
The bindings carry the turn's Usage, and the chat shows the last turn's
tokens and speed under its last answer, as the window does.
Leon's memory tools do not run on the other machine, so its worker is given
the global notes and its project's with its instructions, and says what is
worth keeping for King to keep.
A phone that asks gives King's service its Expo push token. When King tells
the person something and no client shows King, the title and body are sent
to Expo's push service; a phone Expo says is gone is forgotten.
A setting, off until turned on, gives each paired computer with King this
phone's Expo push token, and takes it back when turned off; a computer that
is offline is told when it next connects. Tapping such a notice opens King.
agy --help: --prompt-interactive runs an initial prompt interactively and
continues the session. King's terminal workers on Antigravity now start
on their task instead of waiting for the person to type it.
King's service waits for the commands of a worker on another computer.
When the paired computer was off, each git command waited for it to
connect (15 seconds) and a start took a minute, holding every other
client of the service up. A computer the connection already found away
now fails at once with why, and an answer that never comes is not waited
for past the command's limit.
A line typed from a thread of its own woke the window's tasks from
outside gpui's test scheduler, which failed King's terminal tests now
and then under load (the gate of 486171c). The tests' readiness never
looks, and a readiness that never looks types the line at once.
King's service is Unix's: on Windows what serves it is not used, and
clippy refused it. On macOS the temporary folder is reached through a
link, and `pwd` says the folder itself.
A new line or a lone & started another command that was judged as the
first one's words; 1> was not a redirection; sed -Ei, --in-place and a
script that writes or runs read as reading; a bare cd or cd - kept the
worktree; git branch -D and -f, git -c and git diff --output read as
reading; and a camelCase tool of an MCP server (sendMessage) hid its
words. All of these now go to King.
…taken from another goal

A task server that is not King's refused nothing when a call named
another project's board. King's names it by its root without the
separators at its end. task_create of a step whose title an open task
already has re-parented that task; a task that is another's step, or
waits for others, is now left alone and the call says so.
…kout, and the longest line

A worker's folder is told apart by git: in a project's main checkout,
the checkout or a folder of it, it is refused, and a worktree anywhere
finds its project. King's worktrees follow worktree_location. A line as
long as the wire allows is read: the new line that ends it is not
counted.
@viktorvillalobos
viktorvillalobos merged commit 4cfdba4 into main Oct 21, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant