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

# Core concepts: agents, sessions, traces, and events

> Understand the key building blocks of Mortem — agents, sessions, events, autopsies, and shared traces — before you instrument your first bot.

Mortem is built around a small set of concepts that fit together cleanly. Understanding them before you integrate saves you time when you're reading traces in the dashboard or deciding how to structure your instrumentation. This page defines each concept precisely and explains how they relate to one another.

## Agent

An **agent** is the unit of identity in Mortem. It represents a single trading bot — one codebase, one purpose, one API key. You create agents in the dashboard, and every trace belongs to exactly one agent.

Each agent has:

* A stable **agent ID** that you set in `MORTEM_AGENT_ID`
* An **API key** used to authenticate SDK batches against the ingest service
* An **environment** label (`devnet` or `mainnet`) that indicates which Solana network the agent operates on
* An optional **agent wallet** address, used to automatically update the Helius webhook watchlist so on-chain transactions are enriched

When you rotate an API key from the dashboard, the old key is immediately invalidated. Update your environment variable before the next run.

## Session

A **session** is one agent run from start to finish. You create a session by calling `mortem.startSession(options)`, which returns a `Session` object and immediately enqueues the trace start event.

```typescript theme={null}
const session = await mortem.startSession({
  inputSummary: "Evaluate a Solana trade setup and optionally submit the swap",
  tags: ["swap", "devnet"],
})
```

The `inputSummary` field is a human-readable description of what this run is attempting to do. It appears in the trace list in the dashboard and makes it much easier to identify a specific run after the fact. Set it to something meaningful for your strategy.

Every session has a `traceId` (also accessible as `session.id`) that uniquely identifies the run. You can use this to construct a direct link to the trace in the dashboard.

### Session lifecycle

A session moves through the following states:

1. **running** — the session is active and accepting events
2. **completed** — `session.complete()` was called, or `session.run()` returned successfully
3. **errored** — `session.fail(error)` was called, or `session.run()` caught an uncaught exception

Once a session reaches `completed` or `errored`, the SDK flushes any remaining buffered events and the trace is marked ready for analysis.

<Note>
  The analysis worker only processes traces in `completed` or `errored` status. A trace that is still `running` does not receive an autopsy. Always call `session.complete()`, `session.fail()`, or use `session.run()` to ensure the session is properly closed.
</Note>

### The `session.run` pattern

`session.run(callback)` is the recommended way to wrap your agent logic. It keeps Mortem's async trace context active throughout the callback so that all LLM calls, tool calls, and Solana transactions made inside it are automatically associated with the current trace.

```typescript theme={null}
const result = await session.run(async () => {
  // All events created here are attached to this trace
  return { ok: true }
})
```

Without `session.run`, you can still create events manually using `session.beginEvent`, but automatic context propagation won't work for nested async code in instrumentation wrappers.

## Trace

A **trace** is the stored record of a session. The terms are related but distinct: a session is the live SDK object you interact with during a run; a trace is the persisted evidence chain that the dashboard shows after the run.

Every trace contains:

* The session start and end timestamps
* The `inputSummary` and `outputSummary` (if set)
* Aggregate metrics: total tokens, estimated LLM cost in USD, total lamports, Solana transaction count, and the set of tool names called
* The ordered list of events captured during the run
* The session status (`completed` or `errored`)
* The error message, if any
* Any tags you assigned at session creation

Traces are immutable once the session ends. You cannot append events to a completed trace.

## Event

An **event** is a single recorded step within a trace — one LLM call, one tool invocation, one Solana transaction. Events are ordered by sequence number within a trace so you can reconstruct the exact chronology of a run.

### Event types

Mortem defines six built-in event types:

| Type           | What it captures                                                                           |
| -------------- | ------------------------------------------------------------------------------------------ |
| `llm_call`     | A call to a language model: prompt, completion, model name, token usage, and cost estimate |
| `tool_call`    | A tool invocation: tool name, input arguments, and output                                  |
| `solana_tx`    | A Solana transaction: signature, lamport flow, and on-chain state at the time of the call  |
| `x402_payment` | An HTTP x402 payment channel interaction                                                   |
| `mcp_call`     | A model context protocol round-trip                                                        |
| `custom`       | Any step you want to record explicitly using `session.beginEvent("custom", payload)`       |

The instrumentation wrappers (`wrapOpenAI`, `wrapAnthropic`, `wrapLanguageModel`, `wrapTools`, `wrapConnection`, and so on) create `llm_call`, `tool_call`, and `solana_tx` events automatically. You don't need to create those manually unless you're working with a provider that doesn't have a wrapper.

### Recording custom events

Use `session.beginEvent` to record any step that isn't covered by a built-in wrapper. Call `beginEvent` to start the event, perform your work, then call `.complete()` on the returned `EventBuilder`.

```typescript theme={null}
const planning = session.beginEvent("custom", {
  step: "planning",
})

// ... do your planning work ...

planning.complete({
  payload: {
    step: "planning",
    result: "ready",
  },
})
```

Events can be nested. If you call `beginEvent` inside a `session.run` callback, Mortem automatically sets the parent event ID based on the active async context, so the event tree in the dashboard reflects the actual call structure.

### Aggregate metrics

The `Session` object accumulates metrics locally as events are recorded and sends updated snapshots with each batch:

* `llm_call` events contribute to `totalTokens` and `totalCostUsd`
* `tool_call` events add to the `toolsCalled` set
* `solana_tx` events increment `solanaTxCount` and accumulate `totalLamports`

These aggregates appear on the trace list in the dashboard so you can compare runs at a glance without opening each trace individually.

## Tags

Tags are free-form string labels you attach to a session at creation time. They appear on the trace in the dashboard and can help you filter and organize runs.

```typescript theme={null}
const session = await mortem.startSession({
  inputSummary: "Momentum signal evaluation",
  tags: ["momentum", "devnet", "jup-swap"],
})
```

Good tags to consider: the strategy name, the token pair being evaluated, the network, the model being used, or any feature flag active during the run.

## Autopsy and analysis

An **autopsy** is the AI-generated analysis of a completed trace. After a trace reaches `completed` or `errored` status, the analysis worker fetches the full event sequence, calls the configured LLM, and writes a `TraceAnalysis` record.

The autopsy answers the questions operators care about most after a bad run:

* Which events in the sequence most likely caused the outcome?
* What market context, LLM output, or tool result was the deciding factor?
* What should you change in your strategy code before the next run?

Analysis is **asynchronous and post-trade**. It is not real-time intervention — it is a structured post-mortem that you review after the run completes. The dashboard shows a loading state while analysis is queued and updates automatically when the autopsy is ready.

<Tip>
  If analysis never appears for a trace, confirm that the analysis worker is running and that the trace status is `completed` or `errored`, not `running`. See the [troubleshooting guide](/reference/troubleshooting) for more detail.
</Tip>

## Shared trace

A **shared trace** is a trace that has been made publicly accessible via a share token. Anyone with the link can view the trace events and autopsy without logging in to Mortem.

You share a trace from the trace detail page in the dashboard. Mortem generates a unique token and a public URL at `/share/[token]`. The public page shows the event sequence and analysis but does not expose your agent configuration or API keys.

You can unshare a trace at any time from the same settings panel, which immediately invalidates the token.

## Best-effort design

The Mortem SDK is designed so that instrumentation failures never interrupt your agent. Every buffer flush error is caught internally and reported through the optional `logger` you pass to `new Mortem(config)`, but never re-thrown. If the ingest service is unreachable, your agent continues executing normally — you just won't have trace data for that run.

This means you can add Mortem to production bots without worrying that a Mortem outage will cause trade execution failures.

```typescript theme={null}
const mortem = new Mortem({
  apiKey: process.env.MORTEM_API_KEY ?? "",
  agentId: process.env.MORTEM_AGENT_ID,
  environment: "devnet",
  logger: {
    warn(message, context) {
      console.warn("[mortem]", message, context)
    },
  },
})
```

If `apiKey` is an empty string, the SDK disables itself entirely and logs a warning. No events are buffered or sent.

## Putting it all together

The full lifecycle of a Mortem-instrumented agent run looks like this:

1. Your agent calls `mortem.startSession(options)` — a `Session` is created, the trace is initialized as `running`, and the first batch is enqueued.
2. Your agent performs work inside `session.run(callback)` — each LLM call, tool call, and Solana transaction is captured as an event and enqueued in batches every 250ms.
3. `session.run` completes — the session is marked `completed` (or `errored`), the final batch is flushed synchronously, and the trace is closed.
4. `mortem.close()` is called — any remaining buffered data is flushed before the process exits.
5. The ingest service writes the trace and events to storage, pushes live updates to the dashboard, and enqueues the trace ID for analysis.
6. The analysis worker picks up the trace, calls the configured LLM, and writes the autopsy.
7. The dashboard shows the completed trace, the full event chronology, and the autopsy.

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/quickstart">
    Send your first trace end-to-end in under five minutes.
  </Card>

  <Card title="SDK sessions" icon="layer-group" href="/sdk/sessions">
    All session methods, event builder options, and context propagation details.
  </Card>

  <Card title="SDK events" icon="bolt" href="/sdk/events">
    Full reference for all six event types and custom event recording.
  </Card>

  <Card title="Dashboard traces" icon="list" href="/dashboard/traces">
    Navigate trace lists, replay chronologies, and read autopsies in the dashboard.
  </Card>
</CardGroup>
