docs · reference

JSON output#

Every --json flag prints a versioned JSON document on stdout and nothing else there; notes and errors stay on stderr. Each document carries a schema_version, fields are snake_case, token counts are integers, and money is integer micro-dollars (1,000,000 = $1.00) with an estimate_label, never a formatted string. Within a schema version Hekma only adds fields, so parse what you need and ignore the rest.

Documents at a glance#

CommandShapeschema_version
hekma agent list --jsonOne document: totals and instances2
hekma agent show NAME --jsonOne document: instance2
hekma agent usage [NAME] --jsonOne document: instance and usage, or totals2
hekma agent config get NAME --jsonOne document: entries1
hekma agent logs NAME --jsonNDJSON: one object per line1 (on every line)
hekma agent memory attach ... --jsonOne document1
hekma agent memory detach ... --jsonOne document1
Engine events (embedders)transition, budget_breach, usage_update1 (inside each payload)

The version belongs to the kind of content, not the command: list, show and usage share the fleet version (2), and the other families have their own.

Compatibility rule#

These documents, and the exit codes, are a stable interface:

  • Within a schema_version, Hekma only adds fields. An existing field keeps its name, type and meaning.
  • Fields marked optional below are left out when they have no value, rather than printed as null or 0. Treat every optional field as possibly absent.
  • Removing or changing a field raises schema_version. Such a change is announced in the release notes at least one minor release ahead and happens only in a major release.

Check schema_version before you trust the rest of a document, and ignore fields you do not know.

list#

hekma agent list --json prints the whole fleet: fleet totals and one entry per agent.

{
  "schema_version": 2,
  "totals": {
    "total_input_tokens": 2400,
    "total_output_tokens": 600,
    "total_dollars": 14040,
    "dollars_partial": false,
    "unpriced_count": 0,
    "estimate_label": "estimated",
    "total_cached_tokens": 800
  },
  "instances": [
    {
      "name": "worker",
      "kind": "worker",
      "state": "running",
      "restart_count": 0,
      "restart_policy": "on-failure",
      "budget": {
        "cumulative_limit": 6000,
        "cumulative_remaining": 3000,
        "breach_action": "pause"
      },
      "usage": {
        "cumulative_input_tokens": 2400,
        "cumulative_output_tokens": 600,
        "current_run_input_tokens": 1200,
        "current_run_output_tokens": 300,
        "cumulative_dollars": 14040,
        "current_run_dollars": 7020,
        "estimate_label": "estimated",
        "cumulative_cached_tokens": 800,
        "current_run_cached_tokens": 400
      },
      "metering_source": "self-reported",
      "supervision": "detached",
      "agent_home": "/Users/you/Library/Application Support/ktesio/agents/worker"
    }
  ]
}

An empty fleet prints "instances": [] and zero totals.

totals#

FieldTypePresentMeaning
total_input_tokensintegeralwaysCumulative input tokens across all agents (cached tokens included).
total_output_tokensintegeralwaysCumulative output tokens across all agents.
total_cached_tokensintegeroptionalCumulative cached tokens. Absent if any agent's ledger holds usage recorded before Hekma tracked cached tokens.
total_dollarsinteger (micro-dollars)optionalSummed estimated cost of the agents that have a rate. Absent when no agent has one.
dollars_partialbooleanalwaystrue when some agent has usage but no rate, so total_dollars is a lower bound.
unpriced_countintegeralwaysHow many agents with usage were left out of total_dollars; 0 unless dollars_partial is true.
estimate_labelstringoptionalestimated; present when total_dollars is.

Fleet entry#

Each element of instances (and the instance object of show) has these fields.

FieldTypePresentMeaning
namestringalwaysThe agent's name.
kindstringalwaysThe adapter kind, such as acp or a manifest's [adapter] kind.
statestringalwaysregistered, starting, running, paused, stopping, stopped or failed.
restart_countintegeralwaysConsecutive restarts by the restart policy.
restart_policystringalwayson-failure or never.
failed_causestringoptionalWhy the agent failed. Present only when state is failed.
budgetobject or nullalwaysThe budget, or null when no budget is configured.
usageobjectalwaysThe usage totals; all zeros for an agent that never reported any.
metering_sourcestringalwaysself-reported or engine-observed.
acp_context_usageobjectoptionalacp agents only, while a live connection has reported its context window. See ACP fields.
usage_gapobjectoptionalacp agents only, when the ledger holds no usage for them. See ACP fields.
supervisionstringoptionaldetached or session while a live process is recorded; absent otherwise. See supervision.
agent_homestringalwaysAbsolute path of the Agent Home.

budget object#

Each limit field is present only when that budget is set; each dollar field only when a rate is also set.

FieldTypeMeaning
per_run_limit, per_run_remainingintegerToken ceiling for the current run, and what is left.
cumulative_limit, cumulative_remainingintegerLifetime token ceiling, and what is left.
per_run_cost_cap, per_run_dollars_remaininginteger (micro-dollars)Dollar ceiling for the current run, and what is left.
cumulative_cost_cap, cumulative_dollars_remaininginteger (micro-dollars)Lifetime dollar ceiling, and what is left.
estimate_labelstringestimated; present with the dollar fields.
breach_actionstringAlways present: pause, stop or warn.

Remaining values never go below 0.

usage object#

FieldTypePresentMeaning
cumulative_input_tokensintegeralwaysInput tokens over the agent's life, cached tokens included.
cumulative_output_tokensintegeralwaysOutput tokens over the agent's life.
current_run_input_tokensintegeralwaysInput tokens in the current run.
current_run_output_tokensintegeralwaysOutput tokens in the current run.
cumulative_cached_tokensintegeroptionalThe cached part of cumulative_input_tokens. Absent when older usage without a cached count is in scope.
current_run_cached_tokensintegeroptionalThe cached part of current_run_input_tokens.
cumulative_dollarsinteger (micro-dollars)optionalEstimated lifetime cost. Present only with a rate.
current_run_dollarsinteger (micro-dollars)optionalEstimated cost of the current run. Present only with a rate.
estimate_labelstringoptionalestimated; present with the dollar fields.

Cached tokens are a subset of input tokens. Add input and output for the total; never add cached on top. These totals equal the usage ledger exactly.

ACP fields#

Two optional fields appear only on acp agents. Every other kind leaves them out.

acp_context_usage is the agent's own report of its session context window. It is context usage, not billable usage: Hekma never adds it to the ledger or to budgets.

FieldTypeMeaning
usedinteger, optionalTokens currently in the context window.
sizeinteger, optionalThe context window size.
costobject, optionalThe agent's own cost figure: amount (string) and currency (string).

usage_gap appears when an acp agent's ledger holds no usage at all, and says which usage sources Hekma tried:

FieldTypeMeaning
observedstringconfigured or not-configured: whether metering.upstream_base_url is set.
sentinelstringlines-seen or no-lines-seen: whether the agent printed any KTESIO_USAGE lines on stderr.
context_usage_reportedbooleanWhether the agent reported its context window.
noticestringOne sentence a person can read.
{
  "schema_version": 2,
  "instance": {
    "name": "coder",
    "kind": "acp",
    "state": "registered",
    "restart_count": 0,
    "restart_policy": "on-failure",
    "budget": null,
    "usage": {
      "cumulative_input_tokens": 0,
      "cumulative_output_tokens": 0,
      "current_run_input_tokens": 0,
      "current_run_output_tokens": 0,
      "cumulative_cached_tokens": 0,
      "current_run_cached_tokens": 0
    },
    "metering_source": "self-reported",
    "usage_gap": {
      "observed": "not-configured",
      "sentinel": "no-lines-seen",
      "context_usage_reported": false,
      "notice": "no billing-grade usage in the ledger — tiers attempted: observed: not-configured; sentinel: no-lines-seen; no context usage reported (ACP usage_update is context-grain, never billed)"
    },
    "agent_home": "/Users/you/Library/Application Support/ktesio/agents/coder"
  }
}

show#

hekma agent show NAME --json prints one fleet entry under instance, with the same fields and rules as a list entry.

{
  "schema_version": 2,
  "instance": {
    "name": "worker",
    "kind": "worker",
    "state": "running",
    "restart_count": 0,
    "restart_policy": "on-failure",
    "budget": {
      "cumulative_limit": 6000,
      "cumulative_remaining": 3000,
      "breach_action": "pause"
    },
    "usage": {
      "cumulative_input_tokens": 2400,
      "cumulative_output_tokens": 600,
      "current_run_input_tokens": 1200,
      "current_run_output_tokens": 300,
      "cumulative_dollars": 14040,
      "current_run_dollars": 7020,
      "estimate_label": "estimated",
      "cumulative_cached_tokens": 800,
      "current_run_cached_tokens": 400
    },
    "metering_source": "self-reported",
    "supervision": "detached",
    "agent_home": "/Users/you/Library/Application Support/ktesio/agents/worker"
  }
}

The capability declaration that the human show prints is not in this document.

usage#

With a name, hekma agent usage NAME --json prints the agent's usage object:

{
  "schema_version": 2,
  "instance": "worker",
  "usage": {
    "cumulative_input_tokens": 2400,
    "cumulative_output_tokens": 600,
    "current_run_input_tokens": 1200,
    "current_run_output_tokens": 300,
    "cumulative_dollars": 14040,
    "current_run_dollars": 7020,
    "estimate_label": "estimated",
    "cumulative_cached_tokens": 800,
    "current_run_cached_tokens": 400
  }
}

Note that instance is a plain string here, the agent's name, while in show it is an object.

Without a name, hekma agent usage --json prints the fleet totals, the same object list --json carries:

{
  "schema_version": 2,
  "totals": {
    "total_input_tokens": 2400,
    "total_output_tokens": 600,
    "total_dollars": 14040,
    "dollars_partial": false,
    "unpriced_count": 0,
    "estimate_label": "estimated",
    "total_cached_tokens": 800
  }
}

config get#

hekma agent config get NAME --json prints every effective config value; with a key, the entries list holds just that key.

{
  "schema_version": 1,
  "entries": [
    {
      "key": "agent.api_key",
      "value": "secret:****",
      "source": "instance",
      "unvalidated": true
    },
    {
      "key": "budget.tokens.cumulative",
      "value": "6000",
      "source": "instance",
      "unvalidated": false
    },
    {
      "key": "model",
      "value": "gpt-4o-mini",
      "source": "instance",
      "unvalidated": false
    }
  ]
}
FieldTypeMeaning
entries[].keystringThe dotted key. Entries are sorted by key.
entries[].valuestringThe value as text, even for numbers. A secret:NAME value reads secret:**** unless you pass --reveal.
entries[].sourcestringThe layer the value came from: engine-default, kind-default, instance or invocation-override.
entries[].unvalidatedbooleantrue for agent.* keys, which Hekma does not check.

With no config set, entries is an empty list. A key with no value is an error (exit code 2) and prints nothing on stdout.

logs#

hekma agent logs NAME --json prints newline-delimited JSON: one complete object per line, in capture order. The shape is the same with --follow, so you can process each line as it arrives. An empty log prints nothing.

{"schema_version":1,"instance":"worker","stream":"agent-out","at":"2026-10-22T19:07:20Z","text":"worker ready, model=gpt-4o-mini"}
{"schema_version":1,"instance":"worker","stream":"agent-out","at":"2026-10-22T19:07:20Z","text":"KTESIO_USAGE {\"sequence\": 0, \"input_tokens\": 1200, \"output_tokens\": 300, \"cached_tokens\": 400}"}
{"schema_version":1,"instance":"worker","stream":"engine","at":"2026-10-22T19:07:20Z","text":"engine: starting -> running"}
FieldTypeMeaning
schema_versioninteger1.
instancestringThe agent's name.
streamstringagent-out (the agent's stdout), agent-err (its stderr) or engine (a line Hekma wrote).
atstringUTC timestamp with whole seconds. Several lines often share one; keep the line order rather than sorting by time.
textstringThe line, without its trailing newline.

memory attach#

hekma agent memory attach NAME --kind KIND --json:

{
  "schema_version": 1,
  "instance": "worker",
  "kind": "filesystem",
  "guarantee": "managed_dir_byte_durable",
  "dir": "/Users/you/Library/Application Support/ktesio/agents/worker/memory",
  "declared": true
}
FieldTypeMeaning
instancestringThe agent's name.
kindstringfilesystem or native.
guaranteestringmanaged_dir_byte_durable for filesystem (the folder exists and its files survive restarts unchanged), home_persistence_only for native (only the Agent Home is kept).
dirstringThe memory folder's path. For native this is where it would be; nothing is created.
declaredbooleantrue when the backing is filesystem and the agent's adapter maps the memory.dir key, so the agent will receive the path. Always false for native.

A native attach reads "kind": "native", "guarantee": "home_persistence_only" and "declared": false.

memory detach#

hekma agent memory detach NAME --json confirms the detach and nothing more; the folder and its files stay on disk.

{
  "schema_version": 1,
  "instance": "worker"
}

Engine events#

A program that embeds the engine can subscribe to events. Each event is one of three payloads wrapped with a kind tag; the payload carries its own schema_version (1 for all three). The CLI does not print events, but it writes the same transition and breach records, without the kind tag, as JSON Lines to logs/instance.log and logs/breaches.log in the Agent Home. Events, resync and diagnostics covers subscribing and catching up after a gap.

transition#

Sent after every lifecycle change. This one is an agent paused by a token budget:

{
  "kind": "transition",
  "schema_version": 1,
  "instance": "spender",
  "prior_state": "running",
  "new_state": "paused",
  "cause": {
    "kind": "budget-exceeded",
    "scope": "cumulative",
    "dimension": "tokens",
    "limit": 3000,
    "observed": 3600
  },
  "at": "2026-10-22T19:11:09Z"
}
FieldTypeMeaning
instancestringThe agent's name.
prior_state, new_statestringThe states before and after.
causeobjectWhy it changed, tagged by cause.kind (below).
atstringUTC timestamp.
cause.kindExtra fieldsWhen
commandcommand (for example "start")An operator command.
adapter-readynoneThe process is up after a start.
launch-errordetailThe launch failed.
stop-gracefulnoneThe process exited within the stop window.
stop-forceddetailThe process was killed after the window.
pause-best-effort, resume-best-effortdetailA pause or resume on a best-effort declaration, or with no process held.
detached-resumeddetailHekma woke an operator-paused detached agent as the command exited.
crasheddetailThe process exited on its own, or was gone when a command looked for it.
restartedcount, waited_msThe restart policy started it again.
budget-exceededscope, dimension, limit, observed; with a dollar budget also dollar_limit, dollar_observed, estimate_labelA budget breach paused or stopped it.

New cause kinds may be added within version 1; handle unknown ones.

budget_breach#

Sent once per budget per run when a budget is crossed, whatever the breach action:

{
  "kind": "budget_breach",
  "schema_version": 1,
  "instance": "spender",
  "run_id": "run-1792696267061924000-0",
  "scope": "cumulative",
  "dimension": "tokens",
  "limit": 3000,
  "observed": 3600,
  "action": "pause",
  "metering_source": "self-reported",
  "at": "2026-10-22T19:11:09Z"
}
FieldTypePresentMeaning
run_idstringalwaysThe run the breach happened in.
scopestringalwaysper-run or cumulative.
dimensionstringalwaystokens or dollars.
limit, observedintegeralwaysThe ceiling and the value that crossed it, in tokens, or in micro-dollars for a dollar breach.
dollar_limit, dollar_observedinteger (micro-dollars)dollar breachesThe same values, as money.
estimate_labelstringdollar breachesestimated.
actionstringalwaysThe breach action taken: pause, stop or warn.
metering_sourcestringalwaysWhere the usage came from.

usage_update#

Sent each time a usage event is written to the ledger. A replayed event (same run and sequence) is not written again and sends nothing.

{
  "kind": "usage_update",
  "schema_version": 1,
  "event": {
    "instance": "spender",
    "run_id": "run-1792696267061924000-0",
    "input_tokens": 1000,
    "output_tokens": 200,
    "cached_tokens": 100,
    "metering_source": "self-reported",
    "sequence": 2,
    "occurred_at": "2026-10-22T19:11:09Z"
  }
}
FieldTypeMeaning
event.instancestringThe agent's name.
event.run_idstringThe run the usage belongs to.
event.input_tokens, event.output_tokensintegerTokens in this event; input includes cached.
event.cached_tokensinteger or nullThe cached part of the input. null only for usage recorded before Hekma tracked cached tokens.
event.metering_sourcestringself-reported or engine-observed.
event.sequenceintegerThe per-run number used to drop replays. Self-reporting agents supply it; Hekma assigns it for observed usage.
event.occurred_atstringUTC timestamp.

This example was built from a usage row the v0.11.0 binary wrote to its ledger during the same run as the breach above; the field names and order are those of the engine's event type.