Notify subscribers when thread read state changes (#52384)

## What changed

Add the experimental `thread/readState/changed` notification with `threadId` and the committed `readState` receipt. Send it to existing thread subscribers when terminal activity updates read state or a revert clears a removed unread position. Emit terminal read-state updates before `turn/completed` so completion remains the last notification for that result.

Expose the notification in generated JSON schemas, TypeScript types, and Python SDK models. Document experimental capability gating, subscription lifetime, notification opt-out, and refreshing receipts when concurrent responses disagree.

## Testing

Extend the read-state integration test to assert that the notification receipt matches the receipt returned by `thread/read` after terminal activity.

GitOrigin-RevId: 28935352a881b7a5e2e7f629173c3b2061bd7941
This commit is contained in:
Brent Traut
2026-10-09 02:59:21 +00:00
committed by copyberry
parent 5b3c1522f8
commit 82883da25e
22 changed files with 382 additions and 11 deletions
@@ -10249,6 +10249,14 @@ class ThreadReadState(BaseModel):
revision: str
class ThreadReadStateChangedNotification(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
read_state: Annotated[ThreadReadState, Field(alias="readState")]
thread_id: Annotated[str, Field(alias="threadId")]
class TranscriptSegmentThreadRealtimeItem(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -11317,6 +11325,24 @@ class ErrorServerNotification(BaseModel):
params: ErrorNotification
class ThreadReadStateChangedServerNotification(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
)
emitted_at_ms: Annotated[
int | None,
Field(
alias="emittedAtMs",
description="Unix timestamp (in milliseconds) when app-server emitted this notification.",
),
] = None
method: Annotated[
Literal["thread/readState/changed"],
Field(title="Thread/readState/changedNotificationMethod"),
]
params: ThreadReadStateChangedNotification
class ThreadGoalUpdatedServerNotification(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
@@ -13116,6 +13142,7 @@ class ServerNotification(
ErrorServerNotification
| ThreadStartedServerNotification
| ThreadStatusChangedServerNotification
| ThreadReadStateChangedServerNotification
| ThreadArchivedServerNotification
| ThreadDeletedServerNotification
| ThreadUnarchivedServerNotification
@@ -13206,6 +13233,7 @@ class ServerNotification(
ErrorServerNotification
| ThreadStartedServerNotification
| ThreadStatusChangedServerNotification
| ThreadReadStateChangedServerNotification
| ThreadArchivedServerNotification
| ThreadDeletedServerNotification
| ThreadUnarchivedServerNotification