Skip to content

Session lifecycle

A conversation lives on disk under its session id, so a later process can continue it.

const command = Command.open(Claude, { workingDir: '/proj' });
await command.setMessage('Start the refactor').send();
const sessionId = command.sessionId; // keep this
command.close();
// Later, in another process entirely.
const resumed = Command.resume(sessionId, Claude, { workingDir: '/proj' });
await resumed.setMessage('Carry on where we left off').send();

Whether a conversation is new or continued is stated explicitly, never inferred from the id being present. Every command carries an id, so inferring would make every one look like a resume — and asking the CLI to resume an id it has never seen fails outright with No conversation found.

These are different actions and should not be confused.

command.interrupt(); // stop the turn, keep the conversation
command.close(); // stop the CLI and everything it spawned

Measured against claude 2.1.170, not assumed. The CLI answers on the control channel and ends the turn with a ResultEvent whose subtype is error_during_execution — which here means interrupted, not broken. The process then stays up but does not serve further messages: a turn sent afterwards gets no reply.

So the sequence a host wants is interrupt, then stop, then resume:

command.interrupt();
command.close();
const next = Command.resume(command.sessionId, Claude, { workingDir: '/proj' });
await next.setMessage('Try a different approach').send();

The conversation survives on disk; only this process is spent. Interrupting still beats calling close() alone, because the CLI writes its result and closes the transcript entry rather than being killed mid-write.

A Command exposes the session it is driving, for inspection rather than for driving.

command.isLive(); // whether a CLI process is running right now
command.sessionId; // the conversation's id
command.session; // Session | null — pid, mode, stderr
command.session?.diagnostics; // what the CLI wrote to stderr

diagnostics is the one to reach for when a CLI fails to start: the protocol stream carries a result saying the run errored, but the reason — a bad flag, a missing login, a version mismatch — is on stderr and nowhere else.