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#
| Command | Shape | schema_version |
|---|---|---|
hekma agent list --json | One document: totals and instances | 2 |
hekma agent show NAME --json | One document: instance | 2 |
hekma agent usage [NAME] --json | One document: instance and usage, or totals | 2 |
hekma agent config get NAME --json | One document: entries | 1 |
hekma agent logs NAME --json | NDJSON: one object per line | 1 (on every line) |
hekma agent memory attach ... --json | One document | 1 |
hekma agent memory detach ... --json | One document | 1 |
| Engine events (embedders) | transition, budget_breach, usage_update | 1 (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
nullor0. 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#
| Field | Type | Present | Meaning |
|---|---|---|---|
total_input_tokens | integer | always | Cumulative input tokens across all agents (cached tokens included). |
total_output_tokens | integer | always | Cumulative output tokens across all agents. |
total_cached_tokens | integer | optional | Cumulative cached tokens. Absent if any agent's ledger holds usage recorded before Hekma tracked cached tokens. |
total_dollars | integer (micro-dollars) | optional | Summed estimated cost of the agents that have a rate. Absent when no agent has one. |
dollars_partial | boolean | always | true when some agent has usage but no rate, so total_dollars is a lower bound. |
unpriced_count | integer | always | How many agents with usage were left out of total_dollars; 0 unless dollars_partial is true. |
estimate_label | string | optional | estimated; present when total_dollars is. |
Fleet entry#
Each element of instances (and the instance object of show) has these fields.
| Field | Type | Present | Meaning |
|---|---|---|---|
name | string | always | The agent's name. |
kind | string | always | The adapter kind, such as acp or a manifest's [adapter] kind. |
state | string | always | registered, starting, running, paused, stopping, stopped or failed. |
restart_count | integer | always | Consecutive restarts by the restart policy. |
restart_policy | string | always | on-failure or never. |
failed_cause | string | optional | Why the agent failed. Present only when state is failed. |
budget | object or null | always | The budget, or null when no budget is configured. |
usage | object | always | The usage totals; all zeros for an agent that never reported any. |
metering_source | string | always | self-reported or engine-observed. |
acp_context_usage | object | optional | acp agents only, while a live connection has reported its context window. See ACP fields. |
usage_gap | object | optional | acp agents only, when the ledger holds no usage for them. See ACP fields. |
supervision | string | optional | detached or session while a live process is recorded; absent otherwise. See supervision. |
agent_home | string | always | Absolute 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.
| Field | Type | Meaning |
|---|---|---|
per_run_limit, per_run_remaining | integer | Token ceiling for the current run, and what is left. |
cumulative_limit, cumulative_remaining | integer | Lifetime token ceiling, and what is left. |
per_run_cost_cap, per_run_dollars_remaining | integer (micro-dollars) | Dollar ceiling for the current run, and what is left. |
cumulative_cost_cap, cumulative_dollars_remaining | integer (micro-dollars) | Lifetime dollar ceiling, and what is left. |
estimate_label | string | estimated; present with the dollar fields. |
breach_action | string | Always present: pause, stop or warn. |
Remaining values never go below 0.
usage object#
| Field | Type | Present | Meaning |
|---|---|---|---|
cumulative_input_tokens | integer | always | Input tokens over the agent's life, cached tokens included. |
cumulative_output_tokens | integer | always | Output tokens over the agent's life. |
current_run_input_tokens | integer | always | Input tokens in the current run. |
current_run_output_tokens | integer | always | Output tokens in the current run. |
cumulative_cached_tokens | integer | optional | The cached part of cumulative_input_tokens. Absent when older usage without a cached count is in scope. |
current_run_cached_tokens | integer | optional | The cached part of current_run_input_tokens. |
cumulative_dollars | integer (micro-dollars) | optional | Estimated lifetime cost. Present only with a rate. |
current_run_dollars | integer (micro-dollars) | optional | Estimated cost of the current run. Present only with a rate. |
estimate_label | string | optional | estimated; 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.
| Field | Type | Meaning |
|---|---|---|
used | integer, optional | Tokens currently in the context window. |
size | integer, optional | The context window size. |
cost | object, optional | The 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:
| Field | Type | Meaning |
|---|---|---|
observed | string | configured or not-configured: whether metering.upstream_base_url is set. |
sentinel | string | lines-seen or no-lines-seen: whether the agent printed any KTESIO_USAGE lines on stderr. |
context_usage_reported | boolean | Whether the agent reported its context window. |
notice | string | One 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
}
]
}| Field | Type | Meaning |
|---|---|---|
entries[].key | string | The dotted key. Entries are sorted by key. |
entries[].value | string | The value as text, even for numbers. A secret:NAME value reads secret:**** unless you pass --reveal. |
entries[].source | string | The layer the value came from: engine-default, kind-default, instance or invocation-override. |
entries[].unvalidated | boolean | true 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"}| Field | Type | Meaning |
|---|---|---|
schema_version | integer | 1. |
instance | string | The agent's name. |
stream | string | agent-out (the agent's stdout), agent-err (its stderr) or engine (a line Hekma wrote). |
at | string | UTC timestamp with whole seconds. Several lines often share one; keep the line order rather than sorting by time. |
text | string | The 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
}| Field | Type | Meaning |
|---|---|---|
instance | string | The agent's name. |
kind | string | filesystem or native. |
guarantee | string | managed_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). |
dir | string | The memory folder's path. For native this is where it would be; nothing is created. |
declared | boolean | true 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"
}| Field | Type | Meaning |
|---|---|---|
instance | string | The agent's name. |
prior_state, new_state | string | The states before and after. |
cause | object | Why it changed, tagged by cause.kind (below). |
at | string | UTC timestamp. |
cause.kind | Extra fields | When |
|---|---|---|
command | command (for example "start") | An operator command. |
adapter-ready | none | The process is up after a start. |
launch-error | detail | The launch failed. |
stop-graceful | none | The process exited within the stop window. |
stop-forced | detail | The process was killed after the window. |
pause-best-effort, resume-best-effort | detail | A pause or resume on a best-effort declaration, or with no process held. |
detached-resumed | detail | Hekma woke an operator-paused detached agent as the command exited. |
crashed | detail | The process exited on its own, or was gone when a command looked for it. |
restarted | count, waited_ms | The restart policy started it again. |
budget-exceeded | scope, dimension, limit, observed; with a dollar budget also dollar_limit, dollar_observed, estimate_label | A 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"
}| Field | Type | Present | Meaning |
|---|---|---|---|
run_id | string | always | The run the breach happened in. |
scope | string | always | per-run or cumulative. |
dimension | string | always | tokens or dollars. |
limit, observed | integer | always | The ceiling and the value that crossed it, in tokens, or in micro-dollars for a dollar breach. |
dollar_limit, dollar_observed | integer (micro-dollars) | dollar breaches | The same values, as money. |
estimate_label | string | dollar breaches | estimated. |
action | string | always | The breach action taken: pause, stop or warn. |
metering_source | string | always | Where 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"
}
}| Field | Type | Meaning |
|---|---|---|
event.instance | string | The agent's name. |
event.run_id | string | The run the usage belongs to. |
event.input_tokens, event.output_tokens | integer | Tokens in this event; input includes cached. |
event.cached_tokens | integer or null | The cached part of the input. null only for usage recorded before Hekma tracked cached tokens. |
event.metering_source | string | self-reported or engine-observed. |
event.sequence | integer | The per-run number used to drop replays. Self-reporting agents supply it; Hekma assigns it for observed usage. |
event.occurred_at | string | UTC 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.
Read next#
- Logs, JSON and scripting: putting these documents to work in scripts.
- Exit codes and environment: the other half of the stable interface.
- Events, resync and diagnostics: receiving events from the engine.
- CLI reference: the commands that print these documents.