Guide

Journals, replay, and tracing

Persist authored decisions for resume and observe execution through structured events.

A journal and a tracer answer different questions. The journal stores authored decisions that the runtime may recall on a later run. The tracer records events for operators, tests, and user interfaces.

JournalTrace
PurposeResume without authoring completed decisions againExplain what happened and when
ConsumerRuntimeOperators, tests, and UIs
Databranch, payload, track, and protocol identitysession, step, model, tool, delivery, and failure events
Effect on a later runSupplies replay inputNone

Do not reconstruct replay input from trace logs. A trace may be sampled or lost without changing protocol semantics; losing a journal decision can stop a resume.

Journal implementations

MemoryJournal fits a single process or test. JsonlJournal(path) writes an append-only file. SqliteJournal(connection, session_id) uses a caller-owned SQLite connection and lifecycle.

Session binds the closed Protocol.id when execution begins. A journal recorded under another shape raises ProtocolMismatch; silently interpreting old decisions under a new message or branch layout would change their meaning.

import agentsparty as ap

Client, Worker = ap.roles('Client', 'Worker')


class Task(ap.Message):
    """Work to perform."""

    text: str


class Result(ap.Message):
    """Completed work."""

    text: str


protocol = ap.Protocol(
    ap.send(Client, Worker, Task),
    ap.send(Worker, Client, Result),
)
journal = ap.MemoryJournal()
session = ap.Session(
    protocol,
    {
        Client: ap.Script([Task('draft')]),
        Worker: ap.Agent(ap.replies([Result('done')]), ''),
    },
    journal=journal,
)
transcript = session.run()

Resume

At each protocol position, the runtime asks the journal whether a decision is already recorded. A recorded decision is decoded and recalled by the sender; the model, machine handler, policy, or human is not asked again. Once recorded steps end, execution continues live and appends new decisions.

Replay covers authored protocol decisions, not arbitrary heap state or external effects. Suppose a machine wrote a file before returning a journaled Saved message. A resumed run can recall Saved without calling the machine, but the journal cannot prove that the earlier write committed exactly once. External effects still need idempotency keys or reconciliation owned by the application.

Journal files and decision layouts are not a stable migration format in 0.2.x. Pin the package version for a durable run. When a protocol changes, start a new journal unless the application owns and tests an explicit conversion against both versions.

Tracing

ap.watching(print) is the smallest live observer. MemoryTracer retains events for assertions; QueueTracer exposes an asynchronous queue. Run.events() streams events from a live run.

import agentsparty as ap

tracer = ap.MemoryTracer()

The signal catalogue follows runtime boundaries:

GroupSignals
SessionSessionStarted, SessionFinished, SessionCancelled, Unfolded, Forked
StepStepStarted, Selected, Recalled, Delivered
ModelModelCalled, ModelAnswered, ModelStreamed, ModelCorrected
Tool and failureToolCalled, ToolAnswered, Failed

A tracer observes only. It must not mutate messages, choose a branch, or raise an error into the protocol. Send slow telemetry through a queue so the tracer does not add latency to each step.

During an incident

Use the journal to answer “which decisions can this session safely recall?” Use the trace to answer “what did this run attempt, in what order, and where did it stop?” Keep both under the same session identifier when the host needs an audit trail, and apply the application's payload-redaction policy before exporting either artifact.

Stable

On this page