app-server: expose loaded thread status via read/list and notifications (#11786)

Motivation
- Today, a newly connected client has no direct way to determine the
current runtime status of threads from read/list responses alone.
- This forces clients to infer state from transient events, which can
lead to stale or inconsistent UI when reconnecting or attaching late.

Changes
- Add `status` to `thread/read` responses.
- Add `statuses` to `thread/list` responses.
- Emit `thread/status/changed` notifications with `threadId` and the new
status.
- Track runtime status for all loaded threads and default unknown
threads to `idle`.
- Update protocol/docs/tests/schema fixtures for the revised API.

Testing
- Validated protocol API changes with automated protocol tests and
regenerated schema/type fixtures.
- Validated app-server behavior with unit and integration test suites,
including status transitions and notifications.
This commit is contained in:
Ruslan Nigmatullin
2026-02-18 15:20:03 -08:00
committed by GitHub
parent 216fe7f2ef
commit 1f54496c48
34 changed files with 2563 additions and 119 deletions

View File

@@ -774,6 +774,7 @@ server_notification_definitions! {
/// NEW NOTIFICATIONS
Error => "error" (v2::ErrorNotification),
ThreadStarted => "thread/started" (v2::ThreadStartedNotification),
ThreadStatusChanged => "thread/status/changed" (v2::ThreadStatusChangedNotification),
ThreadArchived => "thread/archived" (v2::ThreadArchivedNotification),
ThreadUnarchived => "thread/unarchived" (v2::ThreadUnarchivedNotification),
ThreadNameUpdated => "thread/name/updated" (v2::ThreadNameUpdatedNotification),
@@ -1334,6 +1335,28 @@ mod tests {
Ok(())
}
#[test]
fn serialize_thread_status_changed_notification() -> Result<()> {
let notification =
ServerNotification::ThreadStatusChanged(v2::ThreadStatusChangedNotification {
thread_id: "thr_123".to_string(),
status: v2::ThreadStatus::Idle,
});
assert_eq!(
json!({
"method": "thread/status/changed",
"params": {
"threadId": "thr_123",
"status": {
"type": "idle"
},
}
}),
serde_json::to_value(&notification)?,
);
Ok(())
}
#[test]
fn mock_experimental_method_is_marked_experimental() {
let request = ClientRequest::MockExperimentalMethod {