Skip to main content

Command Palette

Search for a command to run...

BDK

Sessions, events, and streaming

A session keeps one conversation, its workspace, and an append-only record of every message and tool call. Continue it with a continuation token, inspect it with a session ID, and follow progress on the NDJSON stream. Chat sessions wait for follow-ups; task sessions run once and stop.

Session contents

Each session combines:

  • A channel and authenticated caller
  • A conversation the caller can continue
  • A workspace for local turns
  • An NDJSON event stream
  • State that lets the conversation resume after a restart

Sessions belong to the principal that created them. Follow-up, stream, and list routes return 403 when another caller tries to access one.

Session identifiers

Sessions have two identifiers because conversation routing and inspection are different jobs.

IdentifierUse it for
continuationTokenContinue a conversation through its channel
sessionIdStream events, inspect state, or run a session-bound tool call

Channels decide what a continuation token looks like. Slack uses its thread identity. A PR channel can use a key such as pr:owner/repo#1. The built-in HTTP API returns an opaque token and rotates it after each accepted follow-up. Reusing a stale HTTP token returns 409.

Use the continuation token to keep talking. Use the session ID to observe or manage the stored session.

Session modes

ModeCreated byWhat happens after a turn
chatHTTP sessions, channel send, Slack, or MCP askWaits in session.waiting and accepts follow-ups
taskMarkdown schedules and fire-and-forget dispatchEnds in session.completed or session.failed

Task sessions don't accept follow-ups. Trying one returns 409.

Follow-ups

A follow-up to an idle chat session starts another turn. Admission when the session is already busy depends on the channel:

PathBusy-session policy
HTTP playground / POST /v1/session/:id / MCP askPreempt (default): interrupt the in-flight turn, wait for it to settle, then run the new message
Slack mentions / DMs / alert-watchCoalesce: leave the active turn running and queue the follow-up. The running turn may receive it at a tool boundary; any asks that remain become one follow-up turn when it finishes

Pass admission: "coalesce" on send() to opt into the Slack policy from other callers. Omit it (or pass "preempt") to keep interrupt semantics.

POST /v1/session/:id/stop interrupts a turn without sending a new message. The stream records turn.failed with status: "cancelled"; the session stays a chat session and can take another follow-up. A whole-message Slack stop / @agent stop does the same for that thread and clears pending coalesced follow-ups.

See Tools for direct-call behavior while a session is busy.

Stream events

Each NDJSON line uses this envelope: { type, index, sessionId, turnId?, at, data }. The index increases within one session. The at field is an ISO-8601 timestamp.

PhaseEventsWhat they tell you
Sessionsession.started, session.waiting, session.completed, session.failedSession creation, readiness, and task completion
Inputmessage.receivedA user message was accepted
Turnturn.queued, turn.started, turn.completed, turn.failedQueue position under a maxRunningTurns cap, then turn status, final result, and token usage
Stepsstep.started, step.completedModel step boundaries and duration
Reasoningreasoning.appended, reasoning.completedStreamed reasoning blocks
Replymessage.appended, message.completedText deltas and finalized assistant messages
Toolsactions.requested, actions.completed, action.result, action.tool_retryTool names, validated arguments, outputs, and errors. action.tool_retry means a read is running again after a transient failure
Subagentssubagent.called, subagent.completedDelegated work
Artifactsartifact.taggedA durable artifact was tagged for this session, by host code or tag_artifact

Pair actions.requested with actions.completed for each model-step batch. action.result is the live per-call envelope. Read turn.completed.data.usage for input, output, and cache token counts.

Stream or replay events

One endpoint handles both live streaming and replay:

curl -N 'http://127.0.0.1:3000/<slug>/v1/session/ses_…/stream?startIndex=0'

Pass startIndex to continue after the last event you received, including after a server restart. Omit it or pass 0 to replay the full session before following new events. GET /v1/session/:id/events returns a one-time dump without staying connected.

Local session workspace

BDK creates a workspace before the first local turn:

Source pathLands as
skills/*.cursor/skills/<name>/SKILL.md
agent tools (execution: "agent")scripts in the session workspace, with a catalog in AGENTS.md
sandbox/workspace/**copied in as seed files
per-send workspaceFileswritten before the turn

Instructions reach the model as its system prompt; they aren't written into the workspace. See Instructions.

Local turns use this workspace as their working directory. Parent directories can contribute AGENTS.md and .cursor settings. Set local.cwd when you need a clean parent directory. A channel can also provide a different working directory for one session, such as a PR worktree.

See Agent config: local cwd for the inheritance rules.

Session storage

Local session state lives under --state-root. A slugged mount stores it in a subdirectory named for the slug.

Deleting a session directory removes the session from the server: it disappears from listings and can no longer be streamed or continued.

Inspect a saved event stream

Use trajectory with a saved trace:

bdk trajectory --events <state-root>/traces/<sessionId>.ndjson

The command prints tool calls, the reply, and token usage in the same JSON shape as run. Open the session's Trace view in the playground for a visual timeline.