2.4 KiB
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:
- If the entry was streamed (
entry.streamed), the authoritativeassistantmessage's text/thinking blocks are skipped — they would duplicate content already assembled from deltas. Tool-use blocks are still registered. _handleAssistantsetsliveAssistantto 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.