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:

  1. opens an isolated session in a throwaway git worktree — the daemon never touches your working copy;
  2. runs with phase gates in auto mode — the design artifact lands in the log even at night;
  3. runs mandatory verification — no escalation when typecheck or tests fail;
  4. 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>
FieldMeaning
--typemaintain (keep an invariant), achieve (reach a state), watch (react to signals).
--scheduleA cron expression, or heartbeat to ride the daemon tick (default every 30 min).
--checkCheap 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.
--budgetUSD/day ceiling for this goal (with configured pricing) plus --runs runs/day.
--escalationnotify · 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.