# Conversation model **Status:** Implemented · **Code:** `Scripts/session.js` `Session` consumes CLI messages and holds everything the views render: the ordered transcript, tool activity, changed files, pending approvals, and running cost. Views subscribe via `session.onChange`. ## Entries `session.entries` is an ordered array of: | Kind | Fields | | --- | --- | | `user` | `text`, `context` (the label of any attached selection) | | `assistant` | `messageId`, `blocks[]` of `{type: "text" \| "thinking", text}` | | `tool` | `id`, `name`, `input`, `summary`, `filePath`, `state`, `result` | | `notice` | `text`, `tone` (`info` \| `error`) | Tool `state` moves `running → done | error`. ## The assistant-message merge **One assistant message id arrives several times.** The CLI emits a separate `assistant` message per *completed content block* — one carrying `thinking`, then another carrying `text` — and with `--include-partial-messages` the same id also arrives as `stream_event` deltas. They are all one bubble. `_assistantEntry(messageId)` keeps a map from message id to entry so every delivery merges into the same entry. Two rules make it correct: 1. If the entry was streamed (`entry.streamed`), the authoritative `assistant` message's text/thinking blocks are **skipped** — they would duplicate content already assembled from deltas. Tool-use blocks are still registered. 2. `_handleAssistant` sets `liveAssistant` to the entry rather than clearing it. Clearing it was a real bug: the authoritative thinking message arrived mid-message, cleared the pointer, and every subsequent text delta was dropped — the reply rendered empty. The map is cleared on `result`, which bounds it to one turn. ## Changed files Only recorded when an editing tool's `tool_result` comes back **without** `is_error`. Recording optimistically on tool use was wrong: a denied write never touched the file, but still appeared in the Changed Files list. `tool_use_result.additions` / `.deletions` supply the counts when present. ## Ignored message types `rate_limit_event`, `system:status`, `system:thinking_tokens`, `system:commands_changed`, `system:mcp_status` carry nothing renderable. `system:permission_denied` becomes an error notice. ## Usage `result` carries `total_cost_usd` (cumulative for the session, not per turn — read the latest, never sum) and `usage`. Input tokens are summed across `input_tokens + cache_read_input_tokens + cache_creation_input_tokens`.