> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.pensar.dev/apex/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pensar.dev/apex/_mcp/server.

# AgentEventBus

> Subscribe to streaming text, tool calls, sub-agent lifecycle, and workflow phase events from Apex agents

**`Signature`**

```ts title="Signature"
class AgentEventBus {
  on<K extends keyof AgentEventMap>(event: K, handler: (payload: AgentEventMap[K]) => void): this;
  once<K extends keyof AgentEventMap>(event: K, handler: (payload: AgentEventMap[K]) => void): this;
  off<K extends keyof AgentEventMap>(event: K, handler: (payload: AgentEventMap[K]) => void): this;
  emit<K extends keyof AgentEventMap>(event: K, payload: AgentEventMap[K]): boolean;
  removeAllListeners(event?: keyof AgentEventMap): this;
}
```

A typed event emitter for agent output. Pass one bus to any runner as `eventBus`. Events from sub-agents (for example, each worker in a pentest swarm) are forwarded to the same bus, tagged with `subagentId` and `sessionId`, so one subscriber sees the whole run.

Handlers run synchronously while the agent streams. Keep them fast, and hand slow work (database writes, network calls) off to a queue.

**`Import`**

```ts title="Import"
import { AgentEventBus } from "@pensar/apex/core/eventBus";
```

## Events

### Model output

**`text-delta`** `{ text: string; subagentId?: string; sessionId?: string; messageId?: string; partId?: string }`

A chunk of the model's response text.

---

**`reasoning-delta`** `{ text: string; subagentId?: string; sessionId?: string; messageId?: string }`

A chunk of extended-thinking output.

---

**`reasoning-start`** `{ subagentId?: string; sessionId?: string; messageId?: string }`

A reasoning block opened.

---

**`reasoning-end`** `{ subagentId?: string; sessionId?: string; messageId?: string }`

A reasoning block closed.

---

**`step-finish`** `{ messages: unknown[]; subagentId?: string; sessionId?: string; messageId?: string }`

An agent step finished. `messages` holds the conversation so far.

---

### Tools

**`tool-call-start`** `{ toolCallId: string; toolName: string; subagentId?: string; sessionId?: string }`

The model started generating a tool call.

---

**`tool-call-delta`** `{ toolCallId: string; argsTextDelta: string; subagentId?: string; sessionId?: string }`

A chunk of the tool call's JSON arguments.

---

**`tool-call-complete`** `{ toolCallId: string; toolName: string; args: unknown; subagentId?: string; sessionId?: string }`

The tool call is fully formed and about to execute.

---

**`tool-result`** `{ toolCallId: string; toolName: string; result: unknown; subagentId?: string; sessionId?: string }`

The tool finished. Match it to its call with `toolCallId`.

---

**`command-output`** `{ data: string; subagentId?: string }`

Live output from a running shell command.

---

### Sub-agents and workflow

**`subagent-spawn`** `{ subagentId: string; sessionId?: string; name?: string; input: unknown; parentSubagentId?: string; parentSessionId?: string }`

A sub-agent started. `parentSubagentId` is unset for top-level agents.

---

**`subagent-complete`** `{ subagentId: string; sessionId?: string; status: 'completed' | 'failed'; parentSubagentId?: string; parentSessionId?: string }`

A sub-agent finished.

---

**`workflow-phase-start`** `{ phase: 'discovery' | 'pentesting' | 'reporting'; label: string; metadata?: Record<string, unknown> }`

A [`runPentestAgent`](/apex/sdk/reference/run-pentest-agent) phase started.

---

**`workflow-phase-complete`** `{ phase: 'discovery' | 'pentesting' | 'reporting'; summary: Record<string, unknown> }`

A phase finished. For `reporting`, `summary` includes `reportPath`, `findingsCount`, and `findingsBySeverity`.

---

**`app-analysis-progress`** `{ totalApps: number; completedApps: number; appName?: string }`

Whitebox discovery progress across the apps in a repository.

---

### Errors and tracing

**`error`** `{ error: unknown; subagentId?: string }`

An agent or sub-agent hit an error. See [Errors and cancellation](/apex/sdk/errors-and-cancellation#agent-errors).

---

**`trace-record`** `{ record: TraceRecord; subagentId?: string }`

A structured step-level trace record (reasoning, tool calls, token usage), suitable for exporting to a tracing backend.

---

## Example

**`Per-agent progress for a pentest swarm`**

```ts title="Per-agent progress for a pentest swarm"
import { AgentEventBus } from "@pensar/apex/core/eventBus";

const eventBus = new AgentEventBus();
const names = new Map<string, string>();

eventBus
  .on("workflow-phase-start", ({ label }) => console.log(`\n== ${label}`))
  .on("subagent-spawn", ({ subagentId, name }) => {
    names.set(subagentId, name ?? subagentId);
    console.log(`+ ${names.get(subagentId)}`);
  })
  .on("tool-call-complete", ({ toolName, subagentId }) => {
    const who = subagentId ? names.get(subagentId) : "root";
    console.log(`  [${who}] → ${toolName}`);
  })
  .on("subagent-complete", ({ subagentId, status }) =>
    console.log(`${status === "completed" ? "✓" : "✗"} ${names.get(subagentId)}`),
  )
  .on("error", ({ error }) => console.error(error));

// await runPentestAgent({ ..., eventBus });
```