Skip to content
AIPDocs
AIP 1.055 pagesArchitecture
Release status
DocumentationAIP 1.0ArchitectureArchitecture and Security

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

  1. Gateway receives an Action envelope.
  2. Runtime checks idempotency.
  3. Runtime resolves the target capability from discovery.
  4. Runtime authorizes the caller through aip-auth.
  5. Runtime dispatches to a registered ActionHandler.
  6. Runtime stores idempotent results when a key is present.
  7. 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.