The handbook
TenantsUsers and rolesApplicationsScopesAgentsContext (graphium)CultivatorsChatsTracingAudit log
Concepts

Concepts: Chats

A chat is the durable record of one human conversing with one agent. The dh_chats row holds the metadata; its id doubles as the LangGraph thread_id, so the conversational state is recoverable across reconnects.

Lifecycle

  1. The operator opens an agent and clicks New chat.
  2. The first user message creates a dh_chats row with user_id = the bound user, agent_name = the agent's name, and status = active.
  3. The console establishes an SSE stream against POST /v1/agents/{agent}/stream with the chat_id passed through. Tokens stream into the message list; tool-call events render as inline collapsible cards.
  4. On disconnect or completion, the LangGraph checkpointer persists the turn. Subsequent fetches of GET /v1/chats/{chat_id}/messages reconstruct the conversation byte-for-byte.

Visibility

dh_chats has a dedicated chat_visibility RLS policy combining tenant, user, and scope:

  • The chat's owner always sees their own chats.
  • A caller holding conversations:read_others sees every chat in the tenant.
  • Everyone else sees only their own.

The console surfaces this on the /chats page. Without conversations:read_others, the user-id filter is hidden. With it, an operator picks another user from the dropdown to inspect their chats.

Note: A caller without conversations:read_others who navigates directly to /chats/<someone-elses-id> receives a 404 from GET /v1/chats/{chat_id}. The page renders the "Chat not found" empty state. This is intentional — 404 does not leak existence.

History replay

GET /v1/chats/{chat_id}/messages?limit=50&cursor=... paginates the messages in chronological order. Each message has a stable shape:

json
{
  "id": "msg_abc",
  "role": "user",
  "content": "...",
  "tool_calls": [{ "name": "find_people", "args": {}, "result": {} }],
  "created_at": "2026-05-18T14:23:11Z"
}

The shape matches the AG-UI messages emitted by the live SSE endpoint — assistant turns carry tool_calls, and each tool result is a following role: "tool" message. The same React component renders both — there is no visual gap between a freshly-streamed turn and one replayed from history.

Archive and retention

Set status = archived to flag a chat as no longer needed. The chat is hidden from the default list, but the LangGraph checkpoints remain available until the tenant's langgraph_checkpoint_retention_days window elapses, at which point the worker drops them.

PreviousConceptsCultivatorsNext ConceptsTracing