Skip to content

Add ConversationResetMessage for the CLI's conversation_reset frame - #1196

Merged
qing-ant merged 1 commit into
mainfrom
qing/conversation-reset-message
Aug 11, 2026
Merged

Add ConversationResetMessage for the CLI's conversation_reset frame#1196
qing-ant merged 1 commit into
mainfrom
qing/conversation-reset-message

Conversation

@qing-ant

Copy link
Copy Markdown
Contributor

Summary

TypeScript-parity gap. In streaming-input mode one connection carries many user turns, and a /clear (or any other flow that discards the transcript mid-session) resets the conversation and zeroes the running totals reported on subsequent result messages. The CLI announces this with a top-level frame:

{"type": "conversation_reset", "new_conversation_id": "<uuid>", "uuid": "<uuid>", "session_id": "<outgoing session id>"}

The TypeScript SDK surfaces it as SDKConversationResetMessage. The Python parser only recognized its known message types and dropped this one through the forward-compat fallthrough in message_parser.py, so Python apps never saw resets — including ones they didn't initiate — and had no signal to snapshot totals before they zero.

  • types.py: new ConversationResetMessage dataclass (new_conversation_id, uuid, session_id), added to the Message union next to the other top-level non-system frames (RateLimitEvent, StreamEvent).
  • message_parser.py: parse type == "conversation_reset"; missing required field → MessageParseError, same as siblings.
  • __init__.py: export.

Compatibility note: like RateLimitEvent (#648), this widens the public Message union. Code that exhaustively matches on Message with assert_never will get a new type-check error, and code that raises on unrecognized message classes will now see a frame that was previously dropped silently.

Test plan

  • tests/test_message_parser.py: parse happy path + missing-field error.
  • e2e-tests/test_conversation_reset.py (runs in CI against the real CLI): open a ClaudeSDKClient, run one turn, send /clear, assert a ConversationResetMessage arrives before the /clear turn's result, stamped with the outgoing session_id, and that the following result carries a new session_id. Passed locally against CLI 2.1.227.
  • ruff check, ruff format --check, mypy src/, full pytest tests/ green locally.

In streaming-input mode one connection carries many user turns, and a
`/clear` (or any other flow that discards the transcript mid-session)
resets the conversation and zeroes the running totals on subsequent
result messages. The CLI announces this with a top-level
`{"type":"conversation_reset","new_conversation_id","uuid","session_id"}`
frame, which the TypeScript SDK surfaces as SDKConversationResetMessage.
The Python parser only recognized its known message types and dropped
this one through the forward-compat fallthrough, so Python apps never
saw resets at all.

Model it as a `ConversationResetMessage` dataclass alongside the other
top-level non-system frames (RateLimitEvent, StreamEvent), add it to the
`Message` union, parse and export it, and cover it with unit tests plus
an e2e test that sends `/clear` mid-session and asserts the frame
arrives stamped with the outgoing session id.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — small, self-contained addition that mirrors the existing RateLimitEvent pattern for a new top-level CLI frame.

What was reviewed:

  • The new conversation_reset parser case in message_parser.py — matches sibling frames (required-field KeyError → MessageParseError), and unknown types still fall through to the forward-compat skip.
  • Checked client.py internals for exhaustive Message-type handling — only ResultMessage is special-cased, so the new frame flows through receive_messages/receive_response without changes.
  • Public-surface wiring (types.py union, __init__.py export) and test coverage (unit happy path + missing-field, plus an e2e test against the real CLI).
Extended reasoning...

Overview

This PR adds a ConversationResetMessage dataclass surfacing the CLI's conversation_reset frame, which was previously dropped by the parser's forward-compat fallthrough. It touches four source locations — a new dataclass in types.py added to the Message union, a new match case in message_parser.py, an export in __init__.py — plus unit tests and a new e2e test. The change is a TypeScript-parity gap fix and is structurally identical to the prior RateLimitEvent addition (#648).

Security risks

None. The change parses three string fields from an already-trusted subprocess stream into a plain dataclass; there is no auth, permission, subprocess-spawning, or injection surface involved.

Level of scrutiny

Low-to-moderate. This is a mechanical, pattern-following addition to the message parser — the riskiest aspect is the public-API widening of the Message union, which the PR description explicitly calls out and which has direct precedent in RateLimitEvent. Code that exhaustively matches on Message may need a type-level update, but runtime behavior for existing consumers is unchanged (the frame was previously dropped silently). I verified that the SDK's own internals (client.py, query.py) only special-case ResultMessage, so the new type flows through the streaming iterators without further changes.

Other factors

Test coverage is solid: unit tests for the happy path and missing-field error mirror the sibling frames' tests, and a new e2e test exercises the real CLI flow (/clear mid-session → reset frame stamped with the outgoing session_id → fresh session_id on the next result). The bug hunting system found no bugs, and the PR timeline has no outstanding reviewer comments. The one behavioral caveat — a malformed conversation_reset frame now raises MessageParseError instead of being dropped — is the same contract every sibling frame follows and is validated by the e2e test against the bundled CLI version.

@qing-ant
qing-ant enabled auto-merge (squash) August 11, 2026 20:53

@chrislloyd chrislloyd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stamp via #claude-code-shadow-stamps

@qing-ant
qing-ant merged commit 54dd3b4 into main Aug 11, 2026
11 checks passed
@qing-ant
qing-ant deleted the qing/conversation-reset-message branch August 11, 2026 20:57
Flohs pushed a commit to Flohs/claude-agent-sdk-go that referenced this pull request Aug 12, 2026
Flohs added a commit to Flohs/claude-agent-sdk-go that referenced this pull request Aug 12, 2026
#589)

Port of Python SDK commit 54dd3b4 (anthropics/claude-agent-sdk-python#1196).

Fixes #582

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants