Sessions and memory
A session is a multi-turn conversation with an agent. The full history is passed to the model on every turn, so the agent remembers what you said two messages ago. Memory is what survives a conversation: an optional pass distills transcripts into durable facts such as “prefers morning meetings” and re-injects them into future sessions. You keep what matters without storing or replaying every word.
1. A message is posted
The caller sends content to the session.
- Outside PAL
- Control plane
- Data stores
Sessions
Section titled “Sessions”Session turns run in the control plane’s session handler, not the run executor, so no run row is created. Each turn calls the provider-failover chain: streaming when no tools are attached, otherwise a non-streaming tool loop of up to 5 rounds. History lives in the messages JSONB column of sessions.
POST /v1/sessions { "agentName": "my-agent" } → { "id": "sess_abc123", "status": "active" }POST /v1/sessions/sess_abc123/messages { "content": "What can you help me with?" }GET /v1/sessions/sess_abc123/events # SSE: the reply streams herePOST /v1/sessions/sess_abc123/stop # marks it stopped, terminates its microVM; a turn in flight still completesDELETE /v1/sessions/sess_abc123With the TypeScript SDK:
import { PalClient } from "@pal-runtime/sdk";
const client = new PalClient({ apiKey: "...", baseUrl: "http://localhost:8080" });const session = await client.sessions.create({ agent: "my-agent" });
for await (const event of client.sessions.streamMessage(session.id, { content: "Summarise the quarter.",})) { if (event.type === "delta") process.stdout.write(event.delta);}
await client.sessions.delete(session.id);The event contract is in Streaming.
Session-scoped microVMs
Section titled “Session-scoped microVMs”When an agent declares isolation: "microvm", sessions can reuse one long-lived VM across all turns (ADR 0022). The VM is spawned on the first turn, so later messages skip the boot. It is terminated when the session is deleted or stopped, or reaped after PAL_SESSION_VM_IDLE_TTL (default 30 minutes) of inactivity.
Memory distillates
Section titled “Memory distillates”Raw timeline events (memory_ingest.go, identity.go) and recent session transcripts accumulate. A distillation pass runs every 6 hours by default (PAL_MEMORY_DISTILL_INTERVAL, a Go duration, minimum 1 minute). It asks the LLM, through the provider-failover chain, to extract durable facts, preferences and relationship notes, one compact row per (topic, person) in memory_distillates.
An item is persisted only if the LLM returns confidence >= 0.60 and a non-empty topic and content. Any error (database, LLM, parse) results in no writes. Session content is never logged above debug level.
{ "id": "mem_abc123", "person_id": "per_abc123", "topic": "preferred communication style", "content": "Prefers short, direct replies. No bullet lists.", "confidence": 0.87, "source_kind": "session", "source_ref": "sess_abc123"}person_id and source_ref are omitted when empty.
| Method | Path | Description |
|---|---|---|
GET |
/v1/memory/distillates |
Active (non-superseded) distillates for the tenant. Returns { enabled: false } when the flag is off |
POST |
/v1/memory/distill |
Run a pass now. Returns { found: N }, the number of items persisted |
The memory_distillates table is row-level-security enforced, and the pass reads and writes through tenant-scoped connections. Full shapes: REST API. For how PAL uses one memory across iMessage and WhatsApp, see Your documents and memory.