Skip to content

Events

Apeira is event-driven. Every emitted event includes the turnId of the turn it belongs to.

ts
import type { 
AgentEvent
} from 'apeira'
const
describe
= (
event
:
AgentEvent
) =>
`${
event
.
turnId
}: ${
event
.
type
}`

Lifecycle events

Apeira emits these lifecycle events:

EventPayloadDescription
turn.queuedA turn was queued waiting for a running turn to finish.
turn.startA turn started execution.
turn.input_queuedInput was queued into the active turn.
turn.input_drained{ count }Queued input was drained and submitted to the model.
turn.doneThe turn completed successfully.
turn.failed{ error }The turn failed with an error.
turn.aborted{ reason? }The turn was aborted.

xsAI forwarded events

Apeira forwards streaming events from the runner and attaches the same turnId. These include:

  • step.start — a model reasoning step started.
  • step.done — a step completed.
  • text.delta — a text content delta.
  • reasoning.delta — a reasoning content delta.
  • tool-call.start — a tool call was invoked.
  • tool-call.done — a tool call completed.

Use the type field to narrow the event you care about:

ts
import { 
createAgent
} from 'apeira'
import {
responses
} from 'apeira/responses'
const
agent
=
createAgent
({
instructions
: 'You are a helpful assistant.',
runner
:
responses
({
apiKey
:
process
.
env
.
OPENAI_API_KEY
,
baseURL
: 'https://api.openai.com/v1/',
model
: 'gpt-5.5',
}), })
agent
.
subscribe
('apeira', (
event
) => {
if (
event
.
type
=== 'turn.failed')
console
.
error
(
event
.
error
)
if (
event
.
type
=== 'text.delta')
process
.
stdout
.
write
(
event
.
delta
)
})

Per-turn streams

run() returns a ReadableStream that is automatically filtered to the submitted turn.

ts
import { 
createAgent
,
run
} from 'apeira'
import {
responses
} from 'apeira/responses'
const
agent
=
createAgent
({
instructions
: 'You are a helpful assistant.',
runner
:
responses
({
apiKey
:
process
.
env
.
OPENAI_API_KEY
,
baseURL
: 'https://api.openai.com/v1/',
model
: 'gpt-5.5',
}), }) const
stream
=
run
(
agent
, {
content
: 'Say hello.',
role
: 'user',
type
: 'message',
}) for await (const
event
of
stream
) {
if (
event
.
type
=== 'turn.done')
console
.
log
('done')
}

The stream closes after turn.done, turn.failed, or turn.aborted.

Global listeners

subscribe('apeira', ...) receives all core events from all turns.

ts
import { 
createAgent
} from 'apeira'
import {
responses
} from 'apeira/responses'
const
agent
=
createAgent
({
instructions
: 'You are a helpful assistant.',
runner
:
responses
({
apiKey
:
process
.
env
.
OPENAI_API_KEY
,
baseURL
: 'https://api.openai.com/v1/',
model
: 'gpt-5.5',
}), }) const
unsubscribe
=
agent
.
subscribe
('apeira',
event
=>
console
.
log
(
event
.
turnId
,
event
.
type
))
unsubscribe
()

The returned function removes the listener. Listener errors are silently ignored — one subscriber cannot break event delivery to others.