Runtime
aip-runtime owns reusable in-process services for sessions, capabilities,
action lifecycle, event streams, idempotency, escalation, audit, settlement
state, and lease-aware queued work execution. It has no dependency on product
connectors.
Services
| Service | Purpose |
|---|---|
SessionManager |
creates sessions and validates lifecycle transitions |
DiscoveryService |
stores manifests and capability lookup metadata |
IdempotencyStore |
returns cached results for replay-safe action keys |
EventLog |
append-only event stream with cursor reads |
LifecycleStore |
stores stream chunks, results, cancellations, escalations, conversations, receipt chains, audit events, and settlement batches |
DelegationStore |
stores parent-child delegation graph edges and terminal delegation results |
TransactionStore |
stores transaction and saga records, captured identity context, plans, terminal outcomes, and recoverable records |
ActionQueue |
stores queued/running async actions, worker leases, and terminal results for restart recovery or worker execution |
Action Path
- Gateway receives an
Actionenvelope. - Runtime checks idempotency.
- Runtime resolves the target capability from discovery.
- Runtime authorizes the caller through
aip-auth. - Runtime dispatches to a registered
ActionHandler. - Runtime stores idempotent results when a key is present.
- Gateway returns an
ActionResult.
For ActionMode::Async, runtime validates the same capability, policy, and
handler preconditions, returns an Ack with queued status, and executes the
handler on the background scheduler. Terminal results are recorded in
LifecycleStore, settled in ActionQueue, and dispatched to the action
callback when one is present. Retryable failures are not recorded as terminal
lifecycle results; the runtime stores next_attempt_at and continues the same
action through a targeted leased retry path until it either succeeds, exhausts
the retry budget, or enters the dead-letter queue.
RuntimeStores::durable_local persists the async queue in queued_actions.json.
On startup, embedders can call recover_queued_actions to replay queued or
running actions through the same authorization, idempotency, handler, lifecycle,
event, and callback path. aipd performs this recovery during daemon startup
after registering built-in and connector handlers.
Runtime::recover_runtime_state(RuntimeRecoveryConfig) is the unified restart
entrypoint for protocol runtimes. It returns a RuntimeRecoveryReport covering
pending approvals, recoverable transaction or saga records, queued action
replay results, and running delegation recovery results. Lower-level recovery
methods remain available for embedders that need to recover one category at a
time, but production supervisors should prefer the report when they need a
single auditable recovery artifact.
For horizontally scaled workers, embedders can call
Runtime::run_queued_action_once(worker_id, lease_ttl_ms). The runtime claims
one queued or expired action through ActionQueueBackend::lease_next, executes
the registered handler, records lifecycle state, settles or dead-letters the
queue record, emits aip.action.worker_result, and dispatches the callback.
Retryable failures are rescheduled before terminal lifecycle settlement, and
future retry windows are not leaseable until next_attempt_at.
Runtime::run_queue_worker_until_idle(QueueWorkerConfig) is the standard finite
worker drain for daemons and test harnesses. It repeatedly calls the same
lease-aware path, sleeps between idle polls, honors retry windows, and returns a
QueueWorkerReport with claimed attempts, terminal results, retry scheduling,
failed attempts, and worker errors. The method is intentionally finite so
process supervisors own shutdown, backpressure, and long-running loop policy.
When a supervisor already knows the action to resume, it can use
Runtime::run_queued_action_by_id_once; the claim is still atomic through
ActionQueueBackend::lease_action.
Backends must make lease_next, renew_lease, and release_lease atomic for
their storage technology. The built-in file backend is single-host durable.
aip-storage-postgres is the reference multi-process implementation and uses
database transactions, row-level coordination, compare-and-set transitions,
and durable queue state. Additional database or queue backends must preserve
the same lease, fencing, retry, idempotency, and recovery contracts rather than
only persisting serialized records.
Transaction Path
Transactional actions use Action.transaction and, when routed as first-class
messages, TransactionRequest / TransactionResult. DryRun with
SchemaOnly or PolicyAndSchema fidelity returns a runtime preview without
handler execution. DownstreamValidation and FullSimulation dry-runs are sent
to the registered handler with the dry-run transaction context and are recorded
as dry-run transaction evidence without lifecycle commit. Action-level dry-runs
that omit a transaction id are normalized with a generated TransactionId before
preview, handler dispatch, and durable evidence persistence, so every dry-run
record remains addressable and correlation-safe.
Plan produces a typed TransactionPlan rather than opaque metadata. The plan
captures the plan id, transaction id, action and capability binding, canonical
plan-input hash, side effects, approval policy, compensation metadata, identity
snapshot, and commit requirements. Commit must reference a known planned
record and is rejected before handler execution if the capability, transaction
id, input hash, tenant, or external-account identity no longer matches.
Compensate checks that the target action completed, consults durable
transaction records when they exist, enforces the compensation window, and then
routes execution to the declared compensation capability.
Delegation Path
DelegationRequest creates a durable graph edge from a parent action to a child
action. Runtime verifies that the requester matches the envelope actor, appends a
delegation chain entry to the child action, and stores the edge before any
execution starts.
Runtime then asks its configured DelegationRouter whether the request should be
sent to a remote peer. A router can settle the delegation by returning a
DelegationResult; otherwise the runtime falls back to local handler execution
as the delegate principal.
Synchronous child actions return a terminal DelegationResult. Asynchronous
child actions return a non-terminal running result, remain open in
DelegationStore, and are settled by the background scheduler when the child
handler completes.
The final result includes a hash-linked delegation_made receipt chain. Runtime
also emits aip.delegation.requested and aip.delegation.completed events so
gateways and operators can inspect multi-agent workflow progress independently
of the final result payload.
When stream chunks are recorded for a delegated child action, runtime annotates
the lifecycle event with delegation_id and parent_action_id. This lets parent
workflows and observers correlate streaming child progress without parsing
product-specific connector payloads.
On startup, embedders can call recover_running_delegations. Recovery replays
persisted running graph edges through the router first and then through local
execution. Deployments should pair recovery with child action idempotency keys
when remote peers may receive the same delegated request more than once.
Lifecycle Path
Non-action messages are also first-class. Heartbeats, cancellations, stream chunks, human escalation, channel messages, audit events, receipts, and settlements are stored or converted into event stream entries. This keeps the protocol useful for long-running multi-agent workflows instead of limiting the runtime to request/response tool calls.