Skip to content

Concepts · durable

Entries

How entry shapes control model visibility, transcript visibility, and persistence

Read this when
  • Deciding how to inject a reminder, skill body, or artifact into a turn
  • Working out why something reached the model but not the transcript
  • Implementing an application-defined entry type

This page gives you the entry shapes that control model visibility, transcript visibility, and persistence.

A durable agent takes session.Entry input, not ai.Message input. That is the one shape difference from the plain loop. Everything on this page follows from it.

Why entries

A conversation that survives restarts contains more than user text. It can include a reminder computed from live state. It can include an expanded slash command. It can include a shown artifact that the UI wants to show. Each item answers two questions differently: does the model see it, and does the store keep it?

An ai.Message cannot express those answers. It has content and a role, with no place for either answer. So Run takes entries. The same currency also feeds Append, Entries, Transcript, and the persistence receipts:

da.Run(ctx, durable.Text("What changed since Friday?"))

durable.Text, durable.Image, and durable.File build the ordinary user entry. They mirror the ai.UserMessage constructors one for one.

Entry shapes and input behavior

The Entry interface has three public shapes: MessageEntry, CompactionEntry, and application types that embed CustomEntry. Meta and ephemeral inputs are MessageEntry values whose message carries an ai.Injection. They are not separate entry types.

For run input, model visibility and persistence are independent. Every input has one of four behaviors:

KindModel sees itStore keeps it
Ordinary message entryyesyes
durable.Meta(…)yesyes, hidden from transcript views
durable.Ephemeral(…)yes, for one runno
Custom entrynoyes

Meta and ephemeral are the same idea at two lifetimes. Both are injected context. The model reads them. A transcript view hides them because showing a reader the reminder machinery adds noise. They differ only in whether the text must still be there tomorrow.

Use durable.Meta for context that must remain available after resume. A skill body is the clearest case. When the session reopens a week later, the model needs the instructions it used. Recomputing those instructions is not possible:

da.Run(ctx, durable.Meta(durable.Text(skillMarkdown)), durable.Text(input))

Use durable.Ephemeral for context that must not persist. A reminder built from the current file list or today’s date is true now and misleading later. If the agent persists it, stale text will stay in the session and replay on every resume:

reminder := durable.Ephemeral(durable.Text(liveState))
da.Run(ctx, reminder, durable.Text(input))

Custom entries are the mirror image: persisted, never sent to the model. Applications define them by embedding session.CustomEntry. They register the type once with session.RegisterCustom, so a store can decode it. Artifacts, UI state, and review records live here.

Entries reach the model in the order you pass them. A reminder written before the user’s message arrives before it.

Design: two views over one log

Callers do not need to remember these flags because the projections apply them. A path through the tree is read twice, for different audiences:

  • session.ModelView produces the []ai.Message for the provider. Message entries are in, meta included. Custom entries are skipped.
  • session.TranscriptView produces what a person sees. Injected context, meta and ephemeral alike, is hidden. Custom entries are kept.

One log serves two audiences. There is no second copy to keep in sync.

Design: the flag lives on the message, not the entry

Meta and Ephemeral used to be booleans on MessageEntry. They now live on the message itself, in ai.Message.Injected, with ai.Message.Origin naming the injector.

The reason is that an entry is not the only place this question gets asked. A live UI consumes agent events, which carry ai.Message values and never become entries until they persist. With the flag on the entry, the UI had no structured signal at all and had to guess from the text. With the flag on the message, one field answers the question at every layer: the stream event, the store, the model view, and the transcript view.

A message can also carry injected context inside it. An ai.Reminder block does that. A harness attaches one to a tool result: the result is for the reader, and the injected block is not. That is a block-level rule one level below these views, and the renderer owns it. The renderer walks the content and keeps the ai.Text blocks.

The flag moved with no migration path. A session written before the move carries a top-level "meta": true that nothing reads now, so its injected entries come back ordinary: the transcript shows them, and Compact counts a meta user entry as a turn. Start such a session again.

Design: ephemeral entries stay off the durable chain

An ephemeral entry stays in the agent’s in-memory log and gets an ID, so Entries returns it. It does not join the parent chain. It hangs off the current leaf without advancing it.

That is a correctness rule, not style. The tree comes entirely from parent pointers. The path walk stops when it meets a parent it cannot resolve. If a stored entry names an ephemeral parent, resume cannot resolve that parent. The path walk then stops there and silently omits every entry above it. A stored path user → reminder → user → assistant reopens with only the last two entries.

Chaining past ephemeral entries keeps every stored parent pointer resolvable from the store alone. Being off the chain also keeps ephemeral entries out of the active path for free. That is why the transcript, later runs, and Fork all skip them without knowing the flag exists.

Two consequences matter. First, Branch will not target an ephemeral entry. Branching onto one will re-root the durable chain on a parent the store lacks. Second, persistence receipts on run events carry only durable entries. A receipt means the data survived a crash, and an ephemeral entry has nothing to attest to. Read it back from Entries instead.

  • Sessions — identity, lineage, and the store contract
  • Transcript Tree — the leaf pointer, branching, forking, compaction
  • Durable Events — persistence receipts for stored entries
  • ai.Message — what a message entry wraps
  • Agent History — the plain loop’s []ai.Message, and hook-based injection