fabric/run
What a run is and what it can become. These are plain data: a suspended
run is exactly its stored Snapshot, with no process behind it.
Types
Identifies an action within one run. A provider call id alone is not unique: providers reuse ids on later turns.
pub type ActionId {
ActionId(turn: Int, call_id: String)
}
Constructors
-
ActionId(turn: Int, call_id: String)
One action of a run. Read it by label: Fabric may add fields.
approvals lists the answered approval requests of the action, oldest
first. child names the sub-agent run a delegation started, from the
moment it is started, and stays after the action settles. replays
counts how often a replayable tool’s body was started again after a
crash, a timeout or a lost runner (tool.with_replay).
pub type ActionRecord {
ActionRecord(
id: ActionId,
call: model.ToolCall,
state: ActionState,
approvals: List(Approval),
child: option.Option(RunId),
replays: Int,
)
}
Constructors
-
ActionRecord( id: ActionId, call: model.ToolCall, state: ActionState, approvals: List(Approval), child: option.Option(RunId), replays: Int, )
Where one action stands. This union may grow: keep a catch-all, or
branch on action_state_kind.
pub type ActionState {
Queued
Running
AwaitingApproval(
requirement: Requirement,
revision: Int,
expires: option.Option(timestamp.Timestamp),
)
Succeeded(content: String)
ToolFailed(content: String)
Denied(reason: String)
Rejected(reason: String)
InvalidArguments(detail: String)
UnknownTool
Uncertain(evidence: String)
Reconciled(content: String)
ChildSettled(outcome: Outcome(String))
NotStarted
Delegated
LimitReached(DelegationLimit)
Faulted(detail: String)
}
Constructors
-
QueuedAllowed and waiting for a concurrency slot.
-
RunningThe start was committed before the tool body ran.
-
AwaitingApproval( requirement: Requirement, revision: Int, expires: option.Option(timestamp.Timestamp), )Waiting for an answer to its approval request.
expiresis the request’s deadline (agent.with_approval_expiry): after it the request expires, and the action is rejected.Nonenever expires. -
Succeeded(content: String) -
ToolFailed(content: String)A typed failure;
contentis what the model sees. -
Denied(reason: String) -
Rejected(reason: String)A reviewer rejected the action; the model sees
reason. -
InvalidArguments(detail: String) -
UnknownTool -
Uncertain(evidence: String)The effect may or may not have happened; the next model turn waits for reconciliation.
-
Reconciled(content: String) -
ChildSettled(outcome: Outcome(String))A finished parent’s uncertain delegation was settled from the child’s saved outcome. This is evidence only, never a model-visible tool result. Its answer is the child’s stored text.
-
NotStartedWithdrawn before it started (cancellation, a budget, or a host failure).
-
DelegatedA sub-agent run (
ActionRecord.child) is working on it; its outcome becomes this action’s result. -
LimitReached(DelegationLimit)A delegation refused before the policy because it would exceed a sub-agent limit; the model sees why.
-
Faulted(detail: String)The host could not complete the action: its output could not be encoded, or its tool changed after admission. The run stopped.
A stable classification of ActionState, by what the caller does next.
It never gains variants.
pub type ActionStateKind {
Active
NeedsApproval
NeedsReconciliation
Ended
}
Constructors
-
ActiveThe action settles without the caller (
Queued,Running,Delegated). A delegation’s sub-agent run may itself wait for input. -
NeedsApprovalThe action waits for an answer to its approval request (
AwaitingApproval):fabric.approveorfabric.rejectit. -
NeedsReconciliationThe action’s effect is of unknown status (
Uncertain):fabric.reconcileit. -
EndedThe action has its final state: a result the model sees or saw, a refusal, a settlement, a withdrawal or a host fault.
The answer to an approval request.
pub type Answer {
Approve
Reject(reason: String)
Expired
}
Constructors
-
Approve -
Reject(reason: String)The action does not run; the model sees
reason. -
ExpiredNo one answered before the request’s deadline: the action does not run, and the model sees that its approval expired.
An answered approval request, kept on its action. Read it by label: Fabric may add fields.
reviewer is the identity the approvers’ verify returned for the
proof the answer was given with (fabric/approvers), and verifier
those approvers’ name. Both are None for an Expired answer. An
answer stored before reviewers were required has neither, and one
stored before proofs were required has a reviewer the application
named, with no verifier.
pub type Approval {
Approval(
requirement: Requirement,
revision: Int,
answer: Answer,
reviewer: option.Option(reviewer.Reviewer),
verifier: option.Option(String),
)
}
Constructors
-
Approval( requirement: Requirement, revision: Int, answer: Answer, reviewer: option.Option(reviewer.Reviewer), verifier: option.Option(String), )
Identifies one approval request. revision is unique within the run and
survives restarts, so a reference stays answerable until it is answered,
superseded by a new requirement, or voided by the run ending. References are
plain data: an application may serialize one (into a link or a form) and
rebuild it later; fabric.approve and fabric.reject check it against the
stored record.
pub type ApprovalRef {
ApprovalRef(
run: RunId,
id: ActionId,
requirement: Requirement,
revision: Int,
)
}
Constructors
-
ApprovalRef( run: RunId, id: ActionId, requirement: Requirement, revision: Int, )
The run-wide budget that ended a run.
pub type Budget {
TurnLimit(limit: Int)
TokenLimit(limit: Int, used: Int)
FamilyLimit(budget.Denial)
}
Constructors
-
TurnLimit(limit: Int) -
TokenLimit(limit: Int, used: Int) -
FamilyLimit(budget.Denial)Shared by the root and all managed descendants.
The agent definition a run was started with. A stored run continues only under the same name and version.
pub type DefinitionId {
DefinitionId(name: String, version: Int)
}
Constructors
-
DefinitionId(name: String, version: Int)
The sub-agent limit that refused one delegation; the run continues.
pub type DelegationLimit {
ChildLimit(limit: Int)
DepthLimit(limit: Int)
}
Constructors
-
ChildLimit(limit: Int)A run starts at most
limitsub-agent runs. -
DepthLimit(limit: Int)Sub-agents nest at most
limitlevels below the root run.
Failures of the host rather than of the model or a tool’s business logic.
This union may grow: keep a catch-all, or branch on host_failure_kind.
pub type HostFailure {
PolicyFailed(id: ActionId, reason: String)
OutputEncodingFailed(id: ActionId, detail: String)
ToolChanged(id: ActionId, detail: String)
ModelFailed(model.ModelError)
ModelProtocolViolation(reason: String)
}
Constructors
-
PolicyFailed(id: ActionId, reason: String) -
OutputEncodingFailed(id: ActionId, detail: String) -
ToolChanged(id: ActionId, detail: String)The call’s arguments were admitted, but when its tool started they no longer decode, or no tool of that name is registered: the tool changed between admission and start. The handler did not run. It is not shown to the model, whose arguments were valid.
-
ModelFailed(model.ModelError) -
ModelProtocolViolation(reason: String)
A stable classification of HostFailure, by the part of the host to
look at. It never gains variants.
pub type HostFailureKind {
PolicyFault
ToolFault
ModelFault
}
Constructors
-
PolicyFaultThe policy failed on an action (
PolicyFailed). -
ToolFaultA tool’s result could not be recorded, or the tool changed between admission and start (
OutputEncodingFailed,ToolChanged). -
ModelFaultThe model call failed after its retries, or the model broke the protocol (
ModelFailed,ModelProtocolViolation). AModelFailedcarries its ownmodel.error_kind.
Why a stored run cannot continue under a given agent.
pub type Incompatibility {
OtherAgent(stored: DefinitionId)
ToolNotRegistered(id: ActionId, tool: String)
ArgumentsNotAccepted(
id: ActionId,
tool: String,
detail: String,
)
}
Constructors
-
OtherAgent(stored: DefinitionId)The run was started by another agent definition.
-
ToolNotRegistered(id: ActionId, tool: String)An action that may still run names a tool the agent does not have.
-
ArgumentsNotAccepted(id: ActionId, tool: String, detail: String)An action that has not started yet names a tool whose input codec no longer accepts its arguments.
How a run ended. This union may grow: keep a catch-all.
pub type Outcome(answer) {
Completed(answer: answer)
AnswerInvalid(raw: String, reason: String)
Refused(reason: String)
OutputLimited(partial_text: String)
BudgetExhausted(Budget)
BudgetUnverifiable(turn: Int)
Cancelled
Failed(HostFailure)
}
Constructors
-
Completed(answer: answer)The model’s final answer, read with the agent’s answer codec (
agent.with_answer), or its text for a plain agent. Completion does not mean that every action succeeded: denied or failed tools can be observations the model answered. Readfabric.snapshot(handle)and inspect the returned snapshot’sactionsand the application’s effect record. -
AnswerInvalid(raw: String, reason: String)The model’s final answer is not one the agent’s codec reads:
rawis the text it sent last,reasonthe codec’s complaint. A run ends this way when the model answers so on its last answer attempt (agent.with_answer_attempts) or with no turn or token left for a correction; a stored run reads this way when the agent that reads it has another codec than the one it completed under (a run stored beforewith_answer, say). Its effects have happened. -
Refused(reason: String) -
OutputLimited(partial_text: String) -
BudgetExhausted(Budget)A limit was reached. Outstanding tool calls are kept as
NotStarted. -
BudgetUnverifiable(turn: Int)A token budget is set but the provider did not report usage for the reply of this turn, so the budget cannot be enforced.
-
Cancelled -
Failed(HostFailure)
An approval request waiting for an answer. Read it by label: Fabric may
add fields. expires is when it expires unanswered (None: never); an
expired request rejects its action (see agent.with_approval_expiry).
pub type PendingApproval {
PendingApproval(
reference: ApprovalRef,
tool: String,
arguments_json: String,
expires: option.Option(timestamp.Timestamp),
)
}
Constructors
-
PendingApproval( reference: ApprovalRef, tool: String, arguments_json: String, expires: option.Option(timestamp.Timestamp), )
Which approval an action needs. version lets an application change the
requirement for an action and have stale approvals refused.
pub type Requirement {
Requirement(name: String, version: Int)
}
Constructors
-
Requirement(name: String, version: Int)
The id of a run. The caller chooses it when it starts a run
(fabric.start, graph.start): new_id() for a fresh one, or
parse_id of an application key (a job id, an order id) so that a
retried start finds the run it already started. A string from outside (a
link, a form, a job payload) becomes one only through parse_id.
pub type RunId =
@internal RunId
A run as stored. Read it by label: Fabric may add fields.
pub type Snapshot(answer) {
Snapshot(
run: RunId,
agent: DefinitionId,
incarnation: Int,
parent: option.Option(Parent),
status: Status(answer),
turns_used: Int,
max_turns: Int,
usage: TokenUsage,
transcript: List(model.Message),
actions: List(ActionRecord),
)
}
Constructors
-
Snapshot( run: RunId, agent: DefinitionId, incarnation: Int, parent: option.Option(Parent), status: Status(answer), turns_used: Int, max_turns: Int, usage: TokenUsage, transcript: List(model.Message), actions: List(ActionRecord), )Arguments
- incarnation
-
Increases by one each time recovery takes over work that no live runner owned.
- parent
-
The action or graph activation that owns this run;
Nonefor a root.
A run’s status. answer is the agent’s answer type
(agent.with_answer): String for a plain agent, and for commands that
read the store without an agent (fabric.cancel_stored).
pub type Status(answer) {
Working
Unattended
Suspended(
approvals: List(PendingApproval),
uncertain: List(UncertainAction),
)
Finished(Outcome(answer))
}
Constructors
-
WorkingA model call or a tool is in flight, driven by a runner.
-
UnattendedWork is in flight but no runner known to this store drives it: its runner was lost, or the run is driven through another
Store.fabric.recovertakes it over. On an unleased store the previous owner must be known to be gone; a leased store checks ownership. A configured sweeper recovers it after the lease expires. -
Suspended( approvals: List(PendingApproval), uncertain: List(UncertainAction), )Nothing is in flight and the run cannot continue without outside input: approvals to answer or uncertain effects to reconcile.
-
Finished(Outcome(answer))
A bound on a wait. Infinity is never a default: a caller that wants
a wait unbounded says so.
pub type Timeout {
After(duration.Duration)
Infinity
}
Constructors
-
After(duration.Duration) -
Infinity
Token totals over every reply. unreported_replies counts replies that
arrived without usage; they are not counted as zero.
pub type TokenUsage {
TokenUsage(
input_tokens: Int,
output_tokens: Int,
unreported_replies: Int,
)
}
Constructors
-
TokenUsage( input_tokens: Int, output_tokens: Int, unreported_replies: Int, )
An effect of unknown status. reference names its run (this run, or one
of its sub-agent runs) and action; fabric.reconcile takes it.
pub type UncertainAction {
UncertainAction(
reference: ActionRef,
tool: String,
evidence: String,
)
}
Constructors
-
UncertainAction( reference: ActionRef, tool: String, evidence: String, )
Values
pub fn action_state_kind(state: ActionState) -> ActionStateKind
pub fn describe_action_state(state: ActionState) -> String
One line for logs. It names a tool’s result (Succeeded, ToolFailed,
Reconciled) and a sub-agent’s answer only by their presence: they may
carry application data.
pub fn describe_host_failure(failure: HostFailure) -> String
One line for logs; describe_outcome uses it for Failed.
pub fn describe_outcome(outcome: Outcome(answer)) -> String
One line for logs and for a caller that reports why a run did not complete, such as an MCP tool that served the run.
pub fn host_failure_kind(failure: HostFailure) -> HostFailureKind
pub fn id_from_parts(
prefix: String,
parts: List(String),
) -> RunId
A run id derived from an application key, for a start that must be
idempotent: the same parts always give the same id, so a start retried
with them finds its run (AlreadyStarted). Unlike parse_id it is total.
prefix names the kind of work, is written in source code, and must be 1
to 32 ASCII letters and digits; any other prefix is a bug and panics.
The parts are joined to it with -: id_from_parts("job", ["42", "1"])
is job-42-1. When that text is not a valid id (a part has other
characters, or it is longer than 128 characters), the id is the prefix,
_ and the SHA-256 of the parts in hexadecimal instead, which stays
unique and stable. Give each part a fixed shape (digits, a uuid), since
["4-2"] and ["4", "2"] join to the same text.
When a retry must start a fresh run rather than find the old one, include the attempt among the parts.
pub fn id_to_string(id: RunId) -> String
pub fn new_id() -> RunId
A fresh run id: run- and 32 random lowercase hexadecimal characters
(128 random bits), so fresh ids do not collide.