The daemon — khaelord
The long-term autonomous mode: an engineer that lives in your project, whose every decision — even at 3 AM — is an event you can replay the next morning.
The model#
Where other always-on agents keep a prose checklist, KHAELOR has structured, event-sourced goals. Each goal run:
- opens an isolated session in a throwaway git worktree — the daemon never touches your working copy;
- runs with phase gates in auto mode — the design artifact lands in the log even at night;
- runs mandatory verification — no escalation when typecheck or tests fail;
- appends every event to
.khaelor/daemon/goals/<id>.events.jsonl— replayable, auditable.
Goals#
khaelord goal add "watch GitHub issues labeled 'bug', reproduce, propose a fix" \
--type watch --schedule "*/30 * * * *" --budget 5 --escalation draft-pr
khaelord goal add "npm deps stay fresh without breaking changes" \
--type maintain --schedule "0 6 * * 1" --check "npm outdated --json | grep -q ." \
--escalation draft-pr
khaelord goal list
khaelord goal pause <id> · khaelord goal resume <id>
| Field | Meaning |
|---|---|
--type | maintain (keep an invariant), achieve (reach a state), watch (react to signals). |
--schedule | A cron expression, or heartbeat to ride the daemon tick (default every 30 min). |
--check | Cheap triage: a command whose exit code decides if there is anything to do. Exit 0 → the run is skipped and logged as such. This is the two-tier cost routing: the check is (nearly) free, the strong model only runs when there is real work. |
--budget | USD/day ceiling for this goal (with configured pricing) plus --runs runs/day. |
--escalation | notify · draft-pr (branch left for review) · auto-merge-if-verified (--no-ff merge only when every check passed). |
Running it#
khaelord start # foreground; use nohup / launchd / systemd to detach
khaelord status # pid, active runs, spent today
khaelord stop
03:12 ● goal deps-fresh check failed → run r7f2 in worktree khaelor/goal-r7f2
03:14 ◑ design recorded · 3 files
03:17 ✓ verified · branch khaelor/goal-r7f2 left for review (escalation: draft-pr)
03:17 spent today: $1.84 / $20 · runs 1/8
Budget guard#
// .khaelor/daemon/config.json
{
"budget": { "maxUsdPerDay": 20, "maxUsdPerRun": 3, "hardStop": true },
"activeHours": "07:00-23:00",
"heartbeatMinutes": 30,
"model": { "runs": "claude-sonnet-4-5" },
"channels": { "webhook": "https://…", "command": "./notify.sh" },
"pricing": { "inputPerMTok": 3, "outputPerMTok": 15, "cacheReadPerMTok": 0.3, "cacheWritePerMTok": 3.75 }
}
Ceilings are hard: once a goal or the daemon hits its daily budget, runs are skipped and the skip
is logged. Costs are computed from real API usage and only when pricing is
configured — KHAELOR never invents a dollar figure. Without pricing, maxRunsPerDay
is the binding limit.
Asynchronous approvals#
When a run needs a permission at 3 AM, it does not block and does not burn tokens waiting: the request lands in a persisted queue, the run checkpoints (a paused run is just a paused JSONL — it costs zero), and the daemon moves to the next goal.
khaelord approvals # list pending requests with context
khaelord approve <id> # or: khaelord deny <id>
Channels#
V1 ships two adapters behind one ChannelAdapter seam — the same philosophy as the
isolated ModelClient:
- webhook — every notification POSTed as JSON;
- command — notifications piped to a script's stdin: plug in mail, Slack, ntfy, whatever you like.
Auditability#
In the TUI, /goals shows every goal, its schedule, and today's spend. Each run
references its child session id — resume it, /fork it, or /sdiff it
against another run. Autonomy without auditability is scary; KHAELOR's autonomy is auditable
by construction.