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

# Stream events and retrieve results

> Consume typed run events, reconnect with cursors, recover from replay gaps, and retrieve large artifacts.

A run publishes an ordered stream ending in one `run.terminal` event. Use one `RunHandle` for both the stream and `result()`; calling `runtime.stream()` and `runtime.run()` separately starts two runs.

## Event envelope

Every `HarnessEvent` includes `schemaVersion: 1`, `eventId`, `sequence`, `timestamp`, `sessionId`, `runId`, `attemptId`, `rootRunId`, optional `parentRunId`, and a discriminated `payload`.

| Payload type | Fields | Meaning |
| - | - | - |
| `run.started` | `driverId` | Runtime admitted the run lifecycle. |
| `text.delta` | `text` | Output text fragment. |
| `model.complete` | `providerId`, `modelId`, `usage` | Controlled model operation completed. |
| `tool.complete` | `toolName`, `toolCallId`, `denied` | Tool outcome observed, including denial. |
| `control` | `operation`, optional `reason` | Cancellation or accepted input control. |
| `completion` | `action`, `reason` | Completion policy decision. |
| `run.terminal` | `result` | Final status, output, usage, and cursor after cleanup. |

Use the discriminator before reading payload-specific fields. Event sequences start at `1` within a run. `result.finalCursor` identifies that run's terminal sequence.

## Resume observation

`handle.events({ after })` replays events with sequence greater than `after`, then follows live events. Persist the last processed sequence on the client. This resumes observation of an existing handle; it does not resume execution after a process restart.

```typescript theme={null}
import { HarnessEventGapError, type RunHandle } from "@agentium/harness";

export async function observe(handle: RunHandle, after = 0) {
  try {
    for await (const event of handle.events({ after })) {
      console.log(event.sequence, event.payload.type);
      after = event.sequence;
    }
  } catch (error) {
    if (!(error instanceof HarnessEventGapError)) throw error;
    // The client missed retained events. Replace its partial view with a
    // settled result, rather than presenting an incomplete stream as complete.
    console.log("Replay gap; earliest usable cursor:", error.earliestCursor);
  }
  return handle.result();
}
```

The default store retains 256 events. A cursor must be a nonnegative integer no greater than the current cursor. An expired cursor throws `HarnessEventGapError` with `code: "event_gap"`; `earliestCursor` is the value to pass as `after` to read the oldest retained event. Replaying from it still cannot recover discarded events.

Slow consumers can also fall behind while iterating. Decide whether your UI should replace its partial state with the terminal result, show a gap, or use an application-owned persistence layer. Raising `eventCapacity` changes the memory bound, not durability. The exported `InMemoryHarnessEventStore` exposes `append()`, `events()`, `cursor`, and `durable: false`; `HarnessRuntimeConfig` currently has no custom event-store injection option.

## Large output and artifacts

Generic event payloads are capped at 65,536 serialized bytes. A custom driver should put large values in `services.putArtifact(value)` and emit an artifact reference in its output.

When a terminal result exceeds that bound, the runtime stores its full value as an artifact, truncates the inline text to 4,096 characters, omits inline `structured`, and returns an artifact reference. Both `handle.result()` and the terminal event carry that bounded result.

```typescript theme={null}
import type { HarnessIdentity, HarnessResult, HarnessRuntime } from "@agentium/harness";

export function resultArtifacts(
  runtime: HarnessRuntime,
  identity: HarnessIdentity,
  result: HarnessResult,
) {
  return (result.artifacts ?? []).map(({ id }) => ({
    id,
    value: runtime.getArtifact(identity, result.sessionId, id),
  }));
}
```

An artifact value must be structured-cloneable. Reads return clones, and a mismatched identity/session returns `undefined`. Runtime artifact storage is in memory; it is not a durable download service.

## Telemetry versus the run stream

`telemetry: EventBus` provides runtime and direct model/controller observations for application instrumentation. Keep Agent instrumentation on its own bus when necessary to avoid counting the same model call twice. The typed run stream is the lifecycle contract for consumers; core telemetry events have their own schema.

See [event and result declarations](/harness/api/events), [runtime status handling](/harness/runtime), and [observability](/observability/overview).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.