Skip to content

How to

Persist and Resume Conversations

Give an agent a store and a session ID so conversations survive the process

Read this when
  • Making an agent resumable
  • Adding branch, retry, or compaction to a conversation product

Pick a store, then keep the same session ID for the same conversation.

Choose a store

memory := session.NewMemoryStore() // tests and examples
disk, err := sessionfs.New(".pi-sessions") // survives a restart

session.Store is an interface. Implement it over your own database when a file tree is not where your sessions belong.

Create a durable agent

chat, err := pi.DurableAgent(ctx, "openai-completions/gpt-5-mini",
durable.WithStore(store),
durable.WithSessionID("support-42"),
pi.WithSystemPrompt("Help support agents answer customer questions."),
)
defer chat.Close()
messages, err := chat.Run(ctx,
durable.Text("The customer asked about order order_1234."),
).Wait()

A durable agent persists the input before the run starts. It persists each produced message before it forwards the event for that message.

Resume after a restart

// Monday, in one process.
chat, err := pi.DurableAgent(ctx, spec,
durable.WithStore(store),
durable.WithSessionID("user-42"),
)
chat.Run(ctx, durable.Text("My preferred name is Ravi.")).Wait()
chat.Close()
// Thursday, in another process. The same ID gives the same conversation.
resumed, err := pi.DurableAgent(ctx, spec,
durable.WithStore(store),
durable.WithSessionID("user-42"),
)

The store and the session ID define the memory boundary. Nothing else carries over, so the ID is the only thing your application must keep.

Branch and retry

One checkpoint, two drafts
e1 ─── e2 ─── e3 (refund reply) abandoned, still in the store
└──── e4 (replacement reply) ← leaf after Branch(ctx, checkpoint)
checkpoint := chat.LeafID()
chat.Run(ctx, durable.Text("Draft a refund reply.")).Wait()
err = chat.Branch(ctx, checkpoint)
chat.Run(ctx, durable.Text("Draft a replacement shipment reply.")).Wait()
entries, err := chat.Entries(ctx) // both paths are still here
roots := session.Tree(entries)

A branch moves the leaf pointer to an earlier entry. The next run grows a sibling path, and the abandoned path stays in the store.

Compact when the context outgrows the window

err = chat.Compact(ctx,
durable.KeepTurns(1),
durable.CompactPrompt("Summarize the older turns for support handoff."),
)

Compaction appends a summary entry and removes nothing. The transcript tree still keeps the full history, so a branch to an earlier entry returns the uncompacted path.

Next: sessions.