> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# EventLog

> A persistent, replayable progress-event stream — emit with monotonic seq, read from a cursor, count-based retention.

`EventLog` is a persistent, replayable progress-event stream over ordered KV storage.
`emit(type, payload)` assigns a strictly increasing `seq` and persists the row, so
consumers can replay events in order after a restart, or resume from a cursor
(`read(afterSeq)`).

Retention is count-based: the log keeps at most `retain` rows (default 1000), and each
emit past that bound prunes the oldest rows — the storage footprint stays bounded
regardless of emit rate, and pruning never reuses a `seq`.

## Obtaining an EventLog

An agent constructs its own log over the actor's durable storage — typically once, as
a field:

```ts theme={null}
import { Agent, EventLog } from "@telnyx/edge-runtime";

class ResearchAgent extends Agent<MyEnv> {
  private readonly activity = new EventLog(this.ctx.storage, { retain: 500 });
}
```

The first argument is the actor's `ctx.storage`, so the log shares the actor's
durability and single-writer guarantees; `retain` bounds how many rows are kept
(default 1000). One actor can hold several logs for unrelated streams — each
`EventLog` keys its rows independently.

## Worked example: streaming progress from a long-running task

The pattern has three parts: the task **emits** durable events, the socket server
**replays and pushes** them, and the client **subscribes** from a cursor.

**1. Emit from the task, push to live watchers.** `emit` persists the row and returns
its `seq`; reading strictly after `seq - 1` yields the stored row to hand to
[AgentSocketServer](/docs/agent-sdk/api-reference/agent-socket-server)'s
`broadcastEvent`:

```ts theme={null}
class ResearchAgent extends Agent<MyEnv> {
  private readonly activity = new EventLog(this.ctx.storage, { retain: 500 });
  private readonly desk = new AgentSocketServer(this, {
    getState: () => this.getState(),
    // cursor replay: a client attaching with { events: N } gets everything after N
    getEvents: (afterSeq) => this.activity.read(afterSeq),
  });

  override webSocket(ws: WebSocket, req: Request) {
    return this.desk.attach(ws, req);
  }

  async runReport(): Promise<void> {
    const steps = ["fetch", "analyze", "summarize"];
    for (let i = 0; i < steps.length; i++) {
      await this.doStep(steps[i]);
      const seq = await this.activity.emit("progress", {
        step: steps[i],
        done: i + 1,
        total: steps.length,
      });
      const [event] = await this.activity.read(seq - 1);
      this.desk.broadcastEvent(event); // live push to subscribed watchers
    }
  }
}
```

**2. Subscribe on the client.** [AgentClient](/docs/agent-sdk/api-reference/agent-client)'s
`onEvents` delivers each event in `seq` order; `from` replays missed events after a
disconnect, so a page that reconnects mid-task picks up exactly where it left off:

```ts theme={null}
const agent = new AgentClient(url, { token, subscribe: ["events"], resume: true });

agent.onEvents((e) => renderProgress(e.type, e.payload), { from: lastSeenSeq });
```

Because the log is durable and `seq` never regresses, this survives agent restarts
mid-task: the replay comes from storage, not from anything held in memory.

## emit()

> **emit**(`type`, `payload`): `Promise`\<`number`>

Append one event; returns its assigned seq. Atomic (counter + row +
pruning). If the log is at its retention bound, the oldest row(s) are
pruned in the same transaction.

**Parameters**

| Parameter | Type      |
| --------- | --------- |
| `type`    | `string`  |
| `payload` | `unknown` |

**Returns**

`Promise`\<`number`>

## read()

> **read**(`afterSeq?`, `limit?`): `Promise`\<[`StoredEvent`](/docs/agent-sdk/api-reference/event-log#storedevent)\[]>

Read retained events in seq order. `afterSeq` is an exclusive cursor:
only events with `seq > afterSeq` are returned (default 0 = from the
start). `limit` caps the number of rows; omitted = all retained rows
(paginates internally past the per-list cap).

**Parameters**

| Parameter  | Type     | Default value |
| ---------- | -------- | ------------- |
| `afterSeq` | `number` | `0`           |
| `limit?`   | `number` | `undefined`   |

**Returns**

`Promise`\<[`StoredEvent`](/docs/agent-sdk/api-reference/event-log#storedevent)\[]>

## count()

> **count**(): `Promise`\<`number`>

Total events ever emitted (the current seq high-water mark).

**Returns**

`Promise`\<`number`>

## StoredEvent

A progress event as persisted: assigned a monotonic seq + wall-clock stamp.

**Properties**

**at**

> **at**: `Date`

Wall-clock stamp at emit time; informational only (ordering is by `seq`).

***

**payload**

> **payload**: `unknown`

Arbitrary payload. MUST be codec-safe (JSON-native + Date/Map/Set/
ArrayBuffer/TypedArray/Buffer/BigInt/RegExp) — it is stored via
`ctx.storage.put`, which throws `CodecError` on functions, class instances,
or circular refs.

***

**seq**

> **seq**: `number`

Monotonic sequence number, assigned on emit — never reused, only moves forward.

***

**type**

> **type**: `string`

Caller-chosen event kind (e.g. "progress").

## EventLogOptions

Options for the event log.

**Properties**

**retain?**

> `optional` **retain?**: `number`

Maximum number of event rows kept (count-based retention). Once the log
holds this many rows, each new emit prunes the oldest. Default 1000.
