Validate this issue, fix it, and create a PR on TS repo, and tag me. (make sure to read agents.md)
# TypeScript SDK: Wire-Safe Streaming Events
## Problem
Every event from `agent.stream()` carries `agent: LocalAgent` — the full Agent instance with conversation history, app state, model config, and internal registries. `JSON.stringify(event)` doesn't crash, but it's bloated:
| Conversation size | Per-event JSON | Should be |
|---|---|---|
| 100 messages | ~54 KB | ~100-200 bytes |
| 1,000 messages | ~246 KB | ~100-200 bytes |
## Fix
Add `toJSON()` to every event class. `JSON.stringify` calls it automatically. Zero API changes — `event.agent` still works for direct access.
## What `stream()` Yields (in order)
```
BeforeInvocationEvent
MessageAddedEvent ← user message appended
BeforeModelCallEvent
ModelStreamUpdateEvent (×N) ← token deltas (bulk of events)
ContentBlockEvent (×N) ← completed blocks (text, tool_use, reasoning)
ModelMessageEvent ← full message + stopReason
AfterModelCallEvent
BeforeToolsEvent
BeforeToolCallEvent
ToolStreamUpdateEvent (×N) ← tool progress events
AfterToolCallEvent
ToolResultEvent
AfterToolsEvent
MessageAddedEvent (×2) ← assistant + tool result messages
── loop repeats if LLM requests more tools ──
ModelStreamUpdateEvent (×N) ← final answer tokens
ContentBlockEvent
ModelMessageEvent
AfterModelCallEvent
MessageAddedEvent
AfterInvocationEvent
AgentResultEvent ← final result
```
## Event-by-Event: What to Keep, What to Exclude
**Excluded from ALL events:** `agent: LocalAgent`
| Event | Keep | Exclude (besides `agent`) | `toJSON()` |
|---|---|---|---|
| `ModelStreamUpdateEvent` | `event: ModelStreamEvent` | | `{ type, event }` |
| `ContentBlockEvent` | `contentBlock` | | `{ type, contentBlock }` |
| `ModelMessageEvent` | `message`, `stopReason` | | `{ type, message, stopReason }` |
| `ToolResultEvent` | `result: ToolResultBlock` | | `{ type, result }` |
| `ToolStreamUpdateEvent` | `event: ToolStreamEvent` | | `{ type, event }` |
| `AgentResultEvent` | `result: AgentResult` | | `{ type, result }` |
| `MessageAddedEvent` | `message` | | `{ type, message }` |
| `BeforeToolCallEvent` | `toolUse: {name,id,input}` | `tool: Tool` (class w/ methods), `cancel` (mutable state) | `{ type, toolUse }` |
| `AfterToolCallEvent` | `toolUse`, `result` | `tool: Tool`, `error` → `.message`, `retry` (mutable state) | `{ type, toolUse, result, error? }` |
| `AfterModelCallEvent` | `stopData?` | `error` → `.message` | `{ type, stopData?, error? }` |
| `BeforeToolsEvent` | `message` | `cancel` (mutable state) | `{ type, message }` |
| `AfterToolsEvent` | `message` | | `{ type, message }` |
| `BeforeInvocationEvent` | *(nothing)* | | `{ type }` |
| `AfterInvocationEvent` | *(nothing)* | | `{ type }` |
| `BeforeModelCallEvent` | *(nothing)* | | `{ type }` |
| `InitializedEvent` | *(nothing)* | | `{ type }` |
All the "keep" fields are already serializable: `Message`, `ContentBlock`, `ToolResultBlock`, `AgentResult` have `toJSON()`. `ModelStreamEvent` and `ToolStreamEvent` are plain data. `StopReason` is a string. `toolUse` is `{ name: string, toolUseId: string, input: JSONValue }`.
## Before / After
```typescript
for await (const event of agent.stream('Hello')) {
res.write(`data: ${JSON.stringify(event)}\n\n`)
}
```
Same code. Before: ~54KB per text delta (entire agent serialized). After: ~120 bytes (just the delta).
Validate this issue, fix it, and create a PR on TS repo, and tag me. (make sure to read agents.md)