fix(agent): tolerate unknown Claude stream message types

Newer Claude Code CLIs emit stream message types the bundled SDK
was not compiled against — `rate_limit_event` being the one that
surfaced — and serde aborted the whole chat stream on the first
one ("unknown variant rate_limit_event").

The `Message` enum gains a `#[serde(other)] Unknown` catch-all so
any unmodelled `type` deserializes cleanly instead of failing the
parse; `hooks.rs` and `chat_claude.rs` match it as a silent no-op
(an unknown event carries no hook payload and no chat turn).
This commit is contained in:
Kayshen-X 2026-05-22 11:39:57 +08:00
parent f1b4321a31
commit 12747384c6
3 changed files with 18 additions and 3 deletions

View file

@ -213,9 +213,14 @@ async fn handle_message(msg: Message, tx: &mpsc::Sender<ChatDelta>) -> Option<(b
};
Some((true, reason))
}
Message::System { .. } | Message::User { .. } | Message::StreamEvent { .. } => {
// Init / context / partial-stream events — the chat
// widget doesn't surface them today; silent.
Message::System { .. }
| Message::User { .. }
| Message::StreamEvent { .. }
| Message::Unknown => {
// Init / context / partial-stream events plus any
// message type the SDK doesn't model (e.g.
// `rate_limit_event`) — the chat widget doesn't surface
// them today; silent.
None
}
}

View file

@ -576,6 +576,8 @@ impl HookManager {
}
Message::StreamEvent { .. } => {}
// Unmodelled message types carry no hook payload.
Message::Unknown => {}
}
Ok(outputs)

View file

@ -220,6 +220,14 @@ pub enum Message {
#[serde(skip_serializing_if = "Option::is_none")]
parent_tool_use_id: Option<String>,
},
/// Any message `type` this SDK build does not model. Newer
/// Claude Code CLIs emit events the SDK was not compiled
/// against — e.g. `rate_limit_event` — and serde would
/// otherwise abort the whole stream on the first one. Routing
/// unknown tags here keeps the stream alive; downstream
/// consumers simply ignore an `Unknown`.
#[serde(other)]
Unknown,
}
#[cfg(test)]