Skip to content

StreamEvent

StreamEvent is the union type for all events yielded by assistant.chat(). It provides a unified interface across all engines.

Type Definition

typescript
type CompletionEvent =
  | { type: 'completion'; status: 'completed'; finalText: string;
      sessionId?: string; durationMs?: number; costUsd?: number; numTurns?: number }
  | { type: 'completion'; status: 'silent'; reason: 'pass' | 'skip';
      sessionId?: string; durationMs?: number; costUsd?: number; numTurns?: number }
  | { type: 'completion'; status: 'failed'; message: string; partialText?: string;
      sessionId?: string; durationMs?: number; costUsd?: number; numTurns?: number }
  | { type: 'completion'; status: 'aborted'; reason: 'user' | 'timeout'; partialText?: string;
      sessionId?: string; durationMs?: number; costUsd?: number; numTurns?: number };

type StreamEvent =
  | { type: 'text'; content: string }
  | { type: 'tool_call'; name: string; args: string }
  | { type: 'tool_result'; content: string }
  | { type: 'warning'; message: string }
  | { type: 'error'; message: string }
  | { type: 'done'; sessionId?: string; durationMs?: number;
      costUsd?: number; numTurns?: number; fullText?: string }
  | CompletionEvent;

Terminal contract

assistant.chat() now always ends with exactly one completion event. Downstream consumers should treat completion as the terminal outcome and treat done as a lower-level engine lifecycle event kept for compatibility.

Event Types

text

Streamed text content from the agent.

typescript
{ type: 'text', content: 'Here is the analysis...' }

TIP

Multiple text events form the complete response. Concatenate all content fields to build the full reply — each event is a partial chunk, not a complete message.

tool_call

The agent is invoking a tool (reading a file, running a command, etc.).

typescript
{ type: 'tool_call', name: 'readFile', args: '{"path": "data.csv"}' }
FieldDescription
nameTool name (e.g., readFile, bash, writeFile)
argsJSON string of tool arguments

tool_result

The result of a tool invocation.

typescript
{ type: 'tool_result', content: 'File contents here...' }

warning

Non-fatal warning from the engine.

typescript
{ type: 'warning', message: 'Running with --dangerously-skip-permissions' }

error

An error occurred during processing.

typescript
{ type: 'error', message: 'Engine process exited with code 1' }

done

Signals the end of a low-level engine turn.

typescript
{
  type: 'done',
  sessionId: 'abc-123',
  durationMs: 12345,
  costUsd: 0.042,
  numTurns: 3
}
FieldDescriptionAvailability
sessionIdEngine session ID for resumeAll engines
durationMsWall-clock durationAll engines
costUsdAPI cost in USDClaude Code, OpenCode
numTurnsNumber of agent turnsClaude Code
fullTextComplete agent response textCursor, Claude Code

done is not the final delivery contract. Some engines only attach metadata or fullText here. Prefer completion when deciding whether a reply completed, failed, stayed silent, or was aborted.

completion

The terminal outcome for a chat turn.

completed

typescript
{
  type: 'completion',
  status: 'completed',
  finalText: 'Here is the final answer...',
  durationMs: 12345,
  costUsd: 0.042
}

silent

typescript
{
  type: 'completion',
  status: 'silent',
  reason: 'pass'
}

Used when the assistant intentionally chooses not to reply, for example [PASS] / [SKIP] flows in gateway/group-chat routing.

failed

typescript
{
  type: 'completion',
  status: 'failed',
  message: 'Engine process exited with code 1',
  partialText: 'Partial answer...'
}

aborted

typescript
{
  type: 'completion',
  status: 'aborted',
  reason: 'timeout',
  partialText: 'Partial answer...'
}

Consuming Events

typescript
for await (const event of assistant.chat('Hello')) {
  if (event.type === 'text') process.stdout.write(event.content);
}

Full event handling

typescript
let fullText = '';

for await (const event of assistant.chat('Analyze the data')) {
  switch (event.type) {
    case 'text':
      fullText += event.content;
      break;
    case 'tool_call':
      console.log(`[tool] ${event.name}`);
      break;
    case 'tool_result':
      console.log(`[result] ${event.content.slice(0, 100)}...`);
      break;
    case 'error':
      console.error(`[error] ${event.message}`);
      break;
    case 'completion':
      if (event.status === 'completed') {
        console.log(`Completed in ${event.durationMs}ms`);
      } else if (event.status === 'failed') {
        console.error(`[failed] ${event.message}`);
      }
      break;
  }
}

Accumulate for IM reply

typescript
let reply = '';
for await (const event of assistant.chat(message)) {
  if (event.type === 'text') reply += event.content;
  if (event.type === 'completion' && event.status === 'completed' && !reply) {
    reply = event.finalText;
  }
}
await sendToIM(reply);

What's Next

Released under the MIT License.