pub struct RunMeta {Show 43 fields
pub run_id: String,
pub agent_name: String,
pub agent_path: String,
pub task: String,
pub model: Option<String>,
pub pid: u32,
pub status: RunStatus,
pub current_stage: String,
pub stage_index: usize,
pub num_stages: usize,
pub iteration: usize,
pub prompt_tokens: usize,
pub completion_tokens: usize,
pub cached_tokens: usize,
pub cache_write_tokens: usize,
pub tool_calls: usize,
pub cost_usd: Option<f64>,
pub unpriced_calls: usize,
pub cost_is_exact: bool,
pub cost_priced_usd: f64,
pub workdir: String,
pub started_at: i64,
pub updated_at: i64,
pub last_progress_at: Option<i64>,
pub active: Option<ActiveClock>,
pub error: Option<String>,
pub title: Option<String>,
pub title_error: Option<String>,
pub metadata: HashMap<String, String>,
pub callback_url: Option<String>,
pub callback_secret: Option<String>,
pub parent_run_id: Option<String>,
pub children: Vec<String>,
pub depth: usize,
pub max_child_depth: usize,
pub flags: RunFlags,
pub yolo: bool,
pub yolo_profile: Option<String>,
pub read_paths: Option<ReadPathGrantCounts>,
pub final_output: Option<FinalOutputDescriptor>,
pub waiting_on: Option<WaitReason>,
pub output_request: Option<OutputSpec>,
pub model_override: Option<String>,
}Expand description
Metadata for a single background agent run.
Fields§
§run_id: StringIdentifies the run everywhere, and names its directory under
~/.leviath/runs/. Assigned at spawn and never reused.
agent_name: StringThe blueprint’s [agent] name, not the file it was loaded from. Two runs
of the same agent from different paths share this.
agent_path: StringAbsolute path to the agent manifest directory
task: StringThe task text the run was started with, verbatim.
model: Option<String>The provider/model actually resolved for the entry stage, or None
before resolution. Later stages may use a different one; this is not
rewritten to follow them.
pid: u32Always 0. There is no worker process per run: the daemon hosts every run as an entity in one shared world, so no run has a pid of its own.
Kept because it is written into every meta.json there has ever been,
and served from GET /api/agents. Do not key liveness on it. pid == 0
is true of a run that is working, a run that has finished, and a run
nothing is driving, so a sweeper that reverts on it reverts everything.
Ask the daemon (lev ps) whether it is still hosting the run, and read
status and last_progress_at off disk for what became of it.
status: RunStatusWhere the run stands. The durable counterpart to the ECS world’s live
AgentStatus, and the one that survives a daemon restart.
current_stage: StringName of the stage the run is in, matching a key under [stages].
stage_index: usizeZero-based position of current_stage in the blueprint’s stage list.
Not a progress measure: stages can loop and revisit.
num_stages: usizeHow many stages the blueprint declares, so a reader can render
stage_index as “3 of 7” without loading the manifest.
iteration: usizeInference turns taken in the current stage, reset on entering a new one.
Compared against the stage’s max_iterations.
prompt_tokens: usizeCumulative input tokens billed across every inference this run has made, including retries.
completion_tokens: usizeCumulative output tokens billed across every inference this run has made.
cached_tokens: usizeCumulative tokens read from provider cache.
cache_write_tokens: usizeCumulative tokens written to provider cache.
tool_calls: usizeTotal number of tool calls made across all iterations.
cost_usd: Option<f64>What this run has spent, when every call could be priced.
None means unknown, never free: some call was served by a model with
no reported cost and no known rates, so any total would be understating
by an unknown amount. See unpriced_calls for
how many, and cost_is_exact for whether the
figure is the provider’s own or reconstructed from rate cards.
unpriced_calls: usizeCalls that could not be priced at all. Non-zero forces
cost_usd to None.
cost_is_exact: boolWhether every priced call carried the provider’s own cost figure rather
than one computed from published rates. false means the total is this
process’s best reconstruction of the invoice, not the invoice.
cost_priced_usd: f64The priced subtotal, kept even when cost_usd is None so a resumed run
does not restart its accounting from zero.
workdir: StringAbsolute path to the working directory for tool execution
started_at: i64Unix timestamp (seconds)
updated_at: i64Unix timestamp (seconds)
last_progress_at: Option<i64>Unix seconds when this run last actually moved: a new iteration, a new
stage, or a change of status. None before the first snapshot lands, and
on runs written by a daemon older than this field.
Distinct from updated_at, which also advances on the 30-second
persistence heartbeat and so stays fresh on a run that is wedged. A fresh
updated_at is evidence the daemon is alive, and no evidence at all about
the run. Anything that ages a run must read this instead. Note that a
daemon restart resets it: a reloaded run really is re-driven from its
saved context, so it really has just moved.
active: Option<ActiveClock>How long this run has actually been working, as against how long it has
existed. See ActiveClock, and read it through
RunMeta::active_runtime_secs rather than directly.
None means no clock was kept - a run written by a daemon older than
this field. Deliberately an Option rather than a zeroed clock: a run
that finished inside a second has a genuine total of zero, and the two
have different right answers.
error: Option<String>What went wrong, set alongside RunStatus::Error. None on every
other status.
title: Option<String>Short human-readable title generated from the task prompt (None until generated).
title_error: Option<String>Why Self::title is still None, once title generation has given up
- the provider it could not reach, or what came back instead of a title.
None is the ordinary state: titling has not finished yet, or was never
asked for. Some means it ran and could not produce a name, which is
otherwise indistinguishable from either.
metadata: HashMap<String, String>Custom key-value pairs from the spawn request (API metadata).
callback_url: Option<String>Webhook URL to POST on agent completion/error.
callback_secret: Option<String>Optional shared secret used to HMAC-SHA256 sign the webhook body
(X-Leviath-Signature header) so the receiver can verify authenticity.
Persisted, because the daemon must still be able to sign a webhook for a
run it reloaded after a restart. Never serve it - strip it with
RunMeta::redacted before any of this struct leaves the process.
parent_run_id: Option<String>Links sub-agent runs to their parent run.
children: Vec<String>Run-ids of this agent’s direct sub-agents (sub-agent-tool spawns and fan-out workers). Persisted so the daemon can rebuild the exact parent→children tree on restart rather than reload children as orphans.
depth: usizeThis agent’s depth in the sub-agent tree (0 for a top-level run). Persisted so a reloaded child enforces its remaining spawn-depth budget.
max_child_depth: usizeThe sub-agent depth cap this agent imposes on its own children
(0 when it has none). Restores SubAgentChildren::max_child_depth.
flags: RunFlagsWhy this run may have produced nothing useful - see RunFlags.
yolo: boolWhether the run was launched unattended (--yolo), so a daemon restart
resumes it the way it was started.
Persisted rather than dropped on reload. Forgetting a launch override only ever prompts more, never less, which is why dropping it reads as safe; what it actually does is convert an unattended run into one parked on a prompt nobody is watching for, discarding consent the operator gave at launch. Runs written before this field existed default to attended, so nothing is escalated retroactively.
yolo_profile: Option<String>The named yolo profile (--yolo=<name>) the run was launched under,
persisted with yolo for the same reason: a restart that dropped the
name would resume a carefully scoped run under bare --yolo, which is
the escalating direction. Absent for the bare flag and for runs written
before profiles existed.
read_paths: Option<ReadPathGrantCounts>How much of the blueprint’s [read_paths] the config granted, as
resolved at spawn. None for a blueprint that declared none, and for
runs written before this field existed.
final_output: Option<FinalOutputDescriptor>What the agent handed back, if it submitted anything: everything about the answer except the bytes.
This is the run’s answer, as distinct from error (why it failed) and
from the stage logs (what it did along the way). The content itself is
in a sidecar file beside this one, because this file is parsed for every
run on every listing and must stay small no matter how long an answer is.
waiting_on: Option<WaitReason>Why this run is parked, when it is. None on every other status, and
on a run written before this field existed. Same vocabulary the live
listing reports, so lev ps and a client reading this file describe a
run the same way.
Additive on purpose: default means a meta.json from an older build
still loads, and skip_serializing_if means a run that is not parked
writes exactly the file it wrote before, so an older build reading a
newer run sees nothing new either.
output_request: Option<OutputSpec>The output shape this run was launched asking for, when the caller overrode the blueprint’s.
Persisted for the same reason yolo is: a daemon restart rebuilds the
run’s spawn arguments from this file, and dropping the request would
silently revert the run to the blueprint’s shape partway through. The
caller asked once and should not have to ask again.
model_override: Option<String>The --model the run was launched with, exactly as given
(provider/model or a bare model), when the caller gave one.
Distinct from model, which is what the entry stage resolved to and
is recorded whether or not anything was overridden. A daemon restart
rebuilds the run’s spawn arguments from this file, and must hand back
this field rather than model: handing back model pins every stage
of a run launched with no --model to its first stage’s provider and
model, and loses its failover list. This field is what was actually
asked for, so a reload asks for the same thing - and for a run that
asked for nothing, resolves each stage afresh, as the launch did.
Runs written before this field existed reload with no override. That
loses a --model given to such a run, which is the smaller harm: the
stage falls back to its blueprint’s list rather than being pinned to a
pair the user may never have named.
Implementations§
Source§impl RunMeta
impl RunMeta
Sourcepub fn redacted(&self) -> Self
pub fn redacted(&self) -> Self
This run’s metadata with the webhook signing secret removed, for anything that leaves the process.
GET /api/agents, /api/agents/{id} and /api/agents/{id}/children
all serialize RunMeta whole, so without this any holder of the API
token reads every run’s callback_secret - the key that authenticates
Leviath’s webhooks to their receivers. Mirrors the RedactedConfig
pattern the /api/config handler uses.
Returns an owned copy rather than mutating in place so a caller cannot accidentally redact the record the daemon still needs for signing.
Sourcepub fn new(
run_id: String,
agent_name: String,
agent_path: String,
task: String,
model: Option<String>,
workdir: String,
num_stages: usize,
) -> Self
pub fn new( run_id: String, agent_name: String, agent_path: String, task: String, model: Option<String>, workdir: String, num_stages: usize, ) -> Self
A newly accepted run: RunStatus::Starting, both timestamps now, every
counter at zero and every optional field unset.
Only the seven values a caller genuinely knows at spawn are parameters. Everything else is filled in by the daemon as the run proceeds, so taking them here would invite a caller to invent a stage or a token count.
Sourcepub fn age_secs(&self, now: i64) -> u64
pub fn age_secs(&self, now: i64) -> u64
How long ago this run was launched, at now.
One of the three spans a reader can ask a run about, and they answer different questions - keep them apart:
- age (
Self::age_secs): how long since it was launched. Says nothing about whether it has done anything. - working (
Self::active_runtime_secs): how long it actually spent working. This is the one to call a run’s duration. - last moved (from
Self::last_progress_at): how long since it made progress. A health signal, not a duration: it is how a wedged run is told from a slow one. No accessor, because the surfaces that show it read it off a live listing row rather than off aRunMeta.
Every surface reads these rather than doing the arithmetic itself, so
lev ps, the dashboard and the HTTP API cannot disagree about what a run
has been doing.
Sourcepub fn active_runtime_secs(&self, now: i64) -> u64
pub fn active_runtime_secs(&self, now: i64) -> u64
How long this run has actually been working, at now.
This is the number to show as a run’s duration. Wall-clock age answers a
different question, and answers it misleadingly: a run left paused, or
sitting on a question nobody has answered, kept climbing while nothing
was happening on its behalf. See the sibling spans on Self::age_secs.
A run written before the clock existed carries no spans at all, so it falls back to the wall-clock span - a finished run that claims to have taken no time is the worse answer of the two.
Sourcepub fn touch(&mut self)
pub fn touch(&mut self)
Stamp updated_at with the current time.
Deliberately does not touch last_progress_at: the 30-second
persistence heartbeat calls this, and a run that is wedged must not look
like one that just moved. See RunMeta::last_progress_at.