generated from nhcarrigan/template
feat: parse plugin_errors from stream-json init event (#270)
Adds plugin_errors field to the System init message type and emits a claude:output error event for each failed plugin, giving users visibility into plugin load failures within Hikari Desktop. Requires Claude Code v2.1.111+.
This commit is contained in:
@@ -98,6 +98,10 @@ pub enum ClaudeMessage {
|
|||||||
/// Output style hint from Claude Code (v2.1.81+). Informational only.
|
/// Output style hint from Claude Code (v2.1.81+). Informational only.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
output_style: Option<String>,
|
output_style: Option<String>,
|
||||||
|
/// Plugin errors from Claude Code (v2.1.111+). Populated when plugins are demoted
|
||||||
|
/// due to unsatisfied dependencies.
|
||||||
|
#[serde(default)]
|
||||||
|
plugin_errors: Option<serde_json::Value>,
|
||||||
},
|
},
|
||||||
#[serde(rename = "assistant")]
|
#[serde(rename = "assistant")]
|
||||||
Assistant {
|
Assistant {
|
||||||
@@ -977,6 +981,44 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_init_with_plugin_errors() {
|
||||||
|
let json = r#"{"type":"system","subtype":"init","session_id":"sess-1","plugin_errors":["Plugin 'foo' requires 'bar' which is not installed","Plugin 'baz' failed to load"]}"#;
|
||||||
|
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
|
||||||
|
if let ClaudeMessage::System { plugin_errors, .. } = msg {
|
||||||
|
let errors = plugin_errors.expect("plugin_errors should be present");
|
||||||
|
let arr = errors.as_array().expect("plugin_errors should be an array");
|
||||||
|
assert_eq!(arr.len(), 2);
|
||||||
|
assert_eq!(arr[0].as_str(), Some("Plugin 'foo' requires 'bar' which is not installed"));
|
||||||
|
} else {
|
||||||
|
panic!("Expected System variant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_init_without_plugin_errors() {
|
||||||
|
let json = r#"{"type":"system","subtype":"init","session_id":"sess-1"}"#;
|
||||||
|
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
|
||||||
|
if let ClaudeMessage::System { plugin_errors, .. } = msg {
|
||||||
|
assert!(plugin_errors.is_none());
|
||||||
|
} else {
|
||||||
|
panic!("Expected System variant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_init_with_empty_plugin_errors() {
|
||||||
|
let json = r#"{"type":"system","subtype":"init","session_id":"sess-1","plugin_errors":[]}"#;
|
||||||
|
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
|
||||||
|
if let ClaudeMessage::System { plugin_errors, .. } = msg {
|
||||||
|
let errors = plugin_errors.expect("plugin_errors should be present");
|
||||||
|
let arr = errors.as_array().expect("plugin_errors should be an array");
|
||||||
|
assert!(arr.is_empty());
|
||||||
|
} else {
|
||||||
|
panic!("Expected System variant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_result_message_with_fast_mode_state() {
|
fn test_result_message_with_fast_mode_state() {
|
||||||
let json = r#"{"type":"result","subtype":"success","fast_mode_state":"enabled"}"#;
|
let json = r#"{"type":"result","subtype":"success","fast_mode_state":"enabled"}"#;
|
||||||
|
|||||||
@@ -1850,6 +1850,7 @@ fn process_json_line(
|
|||||||
subtype,
|
subtype,
|
||||||
session_id,
|
session_id,
|
||||||
cwd,
|
cwd,
|
||||||
|
plugin_errors,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if subtype == "init" {
|
if subtype == "init" {
|
||||||
@@ -1874,6 +1875,31 @@ fn process_json_line(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Warn about any plugins that failed to load (v2.1.111+)
|
||||||
|
if let Some(errors) = plugin_errors {
|
||||||
|
if let Some(arr) = errors.as_array() {
|
||||||
|
for error in arr {
|
||||||
|
let msg = if let Some(s) = error.as_str() {
|
||||||
|
s.to_string()
|
||||||
|
} else {
|
||||||
|
error.to_string()
|
||||||
|
};
|
||||||
|
let _ = app.emit(
|
||||||
|
"claude:output",
|
||||||
|
OutputEvent {
|
||||||
|
line_type: "error".to_string(),
|
||||||
|
content: format!("Plugin error: {}", msg),
|
||||||
|
tool_name: None,
|
||||||
|
conversation_id: conversation_id.clone(),
|
||||||
|
cost: None,
|
||||||
|
parent_tool_use_id: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emit_state_change(app, CharacterState::Idle, None, conversation_id.clone());
|
emit_state_change(app, CharacterState::Idle, None, conversation_id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user