Sessions
A session is a single agent's conversation history — the unit that holds messages, tool calls, and status.
A session is one agent's conversation thread. It owns the messages, the tool calls and results, and the live runtime status. Every chat with an agent — through the canvas, an API call, or a peer message — happens inside a session.
Anatomy of a session
| Field | Purpose |
|---|---|
agentId | The agent that runs in this session. |
projectId | Optional project the session belongs to. |
workspaceId | Workspace scope when workspaces are enabled. |
label | Human-readable name shown in the UI. |
status | active, awaiting_peer, idle, completed, failed. |
metadata | JSON for runtime hints and UI state. |
deletedAt | Soft-delete tombstone. |
Messages
Messages live in their own table and reference the session. Each message has:
- A
role(user,assistant,tool,system) - Either a
fromUserIdor afromAgentId— never both contentplus structuredparts,toolCalls, andtoolResults- Optional
parentMessageIdfor threading anddeliveredToSessionIdfor cross-session peer messages tokenUsagefor billing
A DB check constraint enforces that exactly one sender (or neither, for system messages) is set.
Session vs project
| Session | Project | |
|---|---|---|
| Scope | One agent, one conversation | A whole unit of work |
| Lifetime | Until completed/deleted | Until archived |
| Owns | Messages, tool calls | Sessions, agents, files, memories, datasets |
| Identity | chat_sessions.id | projects.id |
A project contains many sessions. A session belongs to at most one project — you can also have ad-hoc sessions outside any project.
Peer messaging across sessions
When agent A calls message_agent to talk to agent B, the runtime delivers the message into B's session. The deliveredToSessionId field on the message records the cross-session hop so the canvas can render the link. Status flips to awaiting_peer when an agent is blocked on a reply — but most peer flows are fire-and-forget. See /docs/concepts/peer-messaging.
Lifetime
Sessions stay active while the agent is running, drop to idle between turns, and end as completed or failed. deletedAt is a soft delete — rows stay in the database so memories and references remain resolvable.