Add conversations and semantic memory
Persist append-only transcripts, keep context bounded, summarize safely, and retrieve curated memory across sessions.
Three kinds of state
Runifold deliberately separates:
| State | Purpose | Can the model see it? |
|---|---|---|
| Transcript | immutable user, assistant, and tool messages | bounded window only |
| Summary | lossy derived view of an older transcript prefix | yes, as labeled context |
| Semantic memory | explicitly curated facts shared by a namespace | only when retrieved |
The execution journal is a fourth record, but it is not conversation state. It describes what the runtime did, not what the model said.
Run a conversation
Give each conversation a stable ID and each tenant or isolation domain a validated namespace. The context policy controls the recent message window.
use runifold::{
ConversationContextPolicy, ConversationId, ConversationWindow,
InMemoryConversationStore, MemoryNamespace,
};
let store = InMemoryConversationStore::new();
let conversation_id = ConversationId::new();
let namespace = MemoryNamespace::parse("tenant-42")?;
let policy = ConversationContextPolicy::new(ConversationWindow::new(16)?);
let result = agent
.run_conversation(
"What did we decide?",
&run,
&store,
conversation_id,
namespace,
policy,
)
.await?;The turn commits atomically with optimistic concurrency. Preserve
result.conversation_version when the caller needs to diagnose concurrent
updates.
Bound the context
The transcript remains append-only even when it outgrows the model window.
ConversationWindow selects the recent suffix. Older entries move into a
bounded summary batch; they are never silently deleted or rewritten.
If run_conversation returns SummaryRequired, either run an explicit
summarization workflow or use run_conversation_with_summary with a trusted
ConversationSummarizer. Summary generation consumes the same run budget and
observes the same cancellation and deadline.
Treat summaries as derived data. A newer monotonic summary may replace an older one, but the source transcript remains the audit record.
Semantic memory
Semantic memory is not every sentence the user has ever written. Curate facts
that are useful across conversations, attach source sequence ranges, and store
them inside the same MemoryNamespace.
Enable bounded lookup with policy.with_semantic_memory(limit)?. Retrieved
memories enter the request as explicitly untrusted context, never as system
instructions. Apply retention, correction, and deletion policy at the memory
store boundary.
Concurrency and storage
The in-memory store is suitable for tests and a single process. Use the PostgreSQL conversation store for durable, multi-process access and semantic vector memory.
Two writers can execute from the same version, but only one append commits. The losing caller receives a conflict together with its completed Agent outcome. Do not automatically rerun the model: the first execution already consumed budget and may have called tools.
Production checklist:
- derive namespaces from authenticated application identity;
- set finite window, summary batch, and memory limits;
- keep system instructions out of persisted transcript entries;
- redact sensitive content before observability export;
- test concurrent append, store outage, and summary backlog recovery.