feat: add structured stderr output for error diagnostics (#29)

Add StderrOutput schema with head/tail/truncated/total_lines fields to
provide better error diagnostics when agent processes fail.
This commit is contained in:
Nathan Flurry 2026-01-29 07:18:56 -08:00 committed by GitHub
parent 82ac0b3880
commit c7d6482fd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 247 additions and 3 deletions

View file

@ -109,6 +109,9 @@ pub fn event_to_universal(event: &schema::StreamJsonMessage) -> Result<Vec<Event
UniversalEventData::SessionEnded(SessionEndedData {
reason: SessionEndReason::Completed,
terminated_by: TerminatedBy::Agent,
message: None,
exit_code: None,
stderr: None,
}),
)
.with_raw(serde_json::to_value(event).ok()),

View file

@ -521,6 +521,9 @@ pub fn session_ended_event(thread_id: &str, reason: SessionEndReason) -> EventCo
UniversalEventData::SessionEnded(SessionEndedData {
reason,
terminated_by: TerminatedBy::Agent,
message: None,
exit_code: None,
stderr: None,
}),
)
.with_native_session(Some(thread_id.to_string()))

View file

@ -79,9 +79,33 @@ pub struct SessionStartedData {
pub struct SessionEndedData {
pub reason: SessionEndReason,
pub terminated_by: TerminatedBy,
/// Error message when reason is Error
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
/// Process exit code when reason is Error
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exit_code: Option<i32>,
/// Agent stderr output when reason is Error
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stderr: Option<StderrOutput>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
pub struct StderrOutput {
/// First N lines of stderr (if truncated) or full stderr (if not truncated)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub head: Option<String>,
/// Last N lines of stderr (only present if truncated)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tail: Option<String>,
/// Whether the output was truncated
pub truncated: bool,
/// Total number of lines in stderr
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_lines: Option<usize>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum SessionEndReason {
Completed,