Choose the invocation model
Directagent.call(input, ctx) and agent.streamEvents(input, ctx) leave subscription and invocation lifetime to the application. Calls on the same Thread reuse context, while each new invocation gets a new Run. Native turnId may be absent; journaling does not create persistent tasks.
Background applications obtain Sessions through runtime and submit Turns on the primary or another Thread. Controls target the Turn; Run/Step describe actual execution. The following assumes a Runtime configured using the Durable guide:
PAUSED, call turn.resume(requestId, revision) using that state’s revision. Answer interactions with turn.respond and cancel tasks with turn.cancel. Queries, wait timeouts and SSE disconnection do not cancel durable tasks. Cancelling a native Agent subscription ends that invocation.
Read one Thread’s messages and execution facts
ThreadKey(userId, agentId, sessionId, threadId) identifies the context. Preserve this identity and its storage namespace. Threads in one Session have independent working state and journals. Applications coordinate concurrent direct calls on the same Thread; Durable provides persistent FIFO and ownership.
threadTranscript(ctx) and inspectThread(ctx). Native queries and storage SPIs are blocking; WebFlux applications must schedule them on suitable blocking workers rather than network event loops.
ThreadHistory.Transcript contains complete messages and asOfSeq, not a public UI snapshot with task controls, partial text and tool progress. Durable applications restore the UI through Session/Thread/Turn snapshot(), history() and event APIs instead of inferring Turn state from native logs. See the runnable recoverable chat example.
Event envelope and reads
AgentEvent carries live invocation notifications; JournalEvent carries committed native facts. Live emission is not a persistence acknowledgment. Native seq increases within one Thread and is not interchangeable with Durable’s Session-wide public cursor.
A normal Step includes a model decision and zero or more tool calls. A recovery Step may only apply an existing tool result. It belongs to the new Run and references its source Step. Native
run/end does not declare a persistent Turn successful; Durable decides that after checking input and external-operation settlement.
ModelUtils observes inside retry through the call-scoped ModelRequestObserver, preserving each
transport attempt. Custom models outside this boundary report logical calls without inferred request
counts. Usage can commit before request completion; failure/loss cannot turn missing usage into zero.
Recording failures do not trigger provider retries, and committed-result replay creates no request.
Storage and backend configuration
Harness defaults toWorkspaceThreadJournalStore, scoped by its Workspace Filesystem. POSIX local storage uses atomic file commits under .agentscope-runtime/; Windows uses journal.sqlite3 in that directory. Remote storage uses a reserved BaseStore partition. Do not edit internal files or keys manually.
Override storage with .threadJournalStore(store). Custom backends implement ThreadJournalStore; atomic storage implementations can supply AtomicJournalStorage and reuse AtomicThreadJournal. Preserve CAS, writer fencing, ordered/idempotent batches and export cursors. Filesystems expose this through journalStorage(context); ordinary file reads/writes cannot implement atomic journal commits.
ThreadJournalStore.list(ctx) discovers Threads only within the caller’s storage scope; SESSION isolation does not enumerate other Sessions. In-memory stores are process-local. Shared persistence alone provides neither background claiming nor autonomous recovery; use a Durable Backend for those capabilities.
The authoritative head.json carries the native journal format version, currently 2. Missing/unsupported versions and corruption fail explicitly without conversion, clearing or silently creating a fresh journal over old data. Recovery follows the committed chain rather than guessing committed segments through directory listing.
For state snapshots without execution history, use .journalEnabled(false).stateStore(snapshotStore). Snapshot mode does not provide complete historical events. With journaling enabled, checkpoints are the native recovery authority. For Durable-managed execution, factories return Agent builders and the adapter installs the invocation Journal so it and the Backend commit state and facts together.
Explicit recovery and tool reconciliation
Direct-call recovery uses the saved native phase, input and tool results. A new invocation after completion reuses context but starts fresh execution counters; continuation retains accumulated progress and provenance. This does not restore arbitrary Java stacks or automatically execute after restart.ThreadInteractions.pending(journal) reads native pending requests; continuationRequests validates selected requests and returns their original Run/Step provenance. See the ordinary Agent HITL APIs. Durable interactions use their Turn’s respond; applications must not bypass Backend by writing managed journals directly.
For uncertain tool outcomes, query the original operation and obtain real results before invoking agent.reconcileToolOutcomes(ctx, outcomes, reason) or ThreadRecovery.reconcile. Reconciliation commits results and checkpoint together while preserving execution provenance. Re-executing a side effect is not a way to discover an unknown result.
Export records to another system
ThreadTranscriptExport exports message views. JournalExporter and JournalExportSink export native events. The persistent cursor advances only after sink acknowledgment; failures can be drained again, so destinations deduplicate by event identity.
Install a sink under JournalExportSink.CONTEXT_KEY in the invocation RuntimeContext to trigger export during execution. Applications still arrange redelivery after failure. Public SSE and business consumers use Durable events rather than exposing journals containing private state.
Append application events
ObtainInvocationRecorder.from(ctx) inside the current tool or middleware invocation, call record(type, payload) and await its Mono. Diagnostic facts can use append(type, payload, false) followed by flush(). Do not write through a recorder after its invocation ends.
Register validation and state application through JournalEventCodecRegistry before executing or restoring recovery-relevant extension events. An unknown required type blocks recovery rather than being silently discarded. Application facts obey the same commit and writer checks.
Host-managed iteration budgets
AgentExecutionPosition records accumulated logical reasoning progress separately from the current Run’s Step index. Durable preserves the budget across Runs of one Turn; applying an existing tool result consumes no additional model reasoning iteration. A fresh direct call after completion starts a new budget.
Host-managed finalization
Core’sAgentExecutionHost cooperates with its host at reasoning, model, acting and finalization boundaries. The interface stays in Core and the Durable implementation in its adapter; Harness does not depend on Durable. When a final answer exists but finalization is unfinished, save the answer and continuation position before confirming pause so recovery does not regenerate it.
Initialize, export state or fork
ThreadCheckpoints supplies current-format initialize, exportSnapshot, exportState and fork. The source must have no active writer; first reconcile uncertain tools and settle unfinished executions and interactions. exportSnapshot returns state and asOfSeq. initialize(target, state, source, sourceSequence) writes a provenance-bearing state/initialized into an empty target without inventing Run history.
Fork preserves valid working state while replacing destination user/session/thread identity. It cannot undo external effects or copy executable old tasks. Durable-managed Threads use public checkpoint/restore/fork APIs to retain task and ownership checks.
Journal persistence scheduling
Commits use the boundedagentscope-session-commit executor rather than model-request threads. Each recorder serializes commits; concurrent flush() calls await their respective accepted prefixes, and cancelling one waiter does not cancel a shared commit. Commit or ownership failure stops the execution boundary instead of permitting new effects.
JVM properties agentscope.journal.commitThreadCap, maintenanceThreadCap, leaseThreadCap and taskQueueCapacity configure the corresponding lanes. Larger queues do not increase storage throughput; size workers against backend capacity. ModelCallPolicy supplies model admission hooks, without scheduling empty work when no hook is needed.