use super::*;
pub(super) fn write_card(
state: &MissionControlState,
id: TranscriptCardId,
link: Option<TranscriptActivityLink>,
body: &str,
) -> TranscriptCard {
use crate::tools::contract::{arg_key, metadata_key};
let node = link
.as_ref()
.and_then(|link| state.nodes.get(&link.activity_id));
let detail = node.and_then(|node| node.tool_detail.as_ref());
let mut status = node
.map(|node| transcript_status_from_activity(node.status))
.or_else(|| detail.map(|detail| transcript_status_from_activity(detail.status)))
.unwrap_or_else(|| transcript_tool_status(state, id.entry_index));
if status == TranscriptCardStatus::Failed && tool_was_canceled(detail) {
status = TranscriptCardStatus::Canceled;
}
let metadata = detail.map(|detail| &detail.metadata);
let params = detail.map(|detail| &detail.params);
let path = if status == TranscriptCardStatus::Success {
metadata
.and_then(|value| value.get(metadata_key::PATH))
.and_then(serde_json::Value::as_str)
.or_else(|| {
params
.and_then(|value| value.get(arg_key::PATH))
.and_then(serde_json::Value::as_str)
})
} else {
params
.and_then(|value| value.get(arg_key::PATH))
.and_then(serde_json::Value::as_str)
}
.map(sanitized_single_line)
.map(|path| truncate_preview_line(&path, PREVIEW_LINE_MAX_DISPLAY_WIDTH))
.unwrap_or_default();
let bytes = (status == TranscriptCardStatus::Success)
.then(|| {
metadata
.and_then(|value| value.get(metadata_key::BYTES))
.and_then(serde_json::Value::as_u64)
})
.flatten();
let title = match status {
TranscriptCardStatus::Success => bytes.map_or_else(
|| "Write".to_string(),
|bytes| format!("Write • {bytes} bytes"),
),
TranscriptCardStatus::Canceled => "Write • canceled".to_string(),
_ => "Write".to_string(),
};
let path_lines = usize::from(!path.is_empty());
let body = match status {
TranscriptCardStatus::Canceled => [path.as_str(), "Canceled before completion"]
.into_iter()
.filter(|row| !row.is_empty())
.collect::<Vec<_>>()
.join("\n"),
TranscriptCardStatus::Failed => {
let error = bounded_error_line(detail.map_or(body, |detail| detail.output.as_ref()));
let mut rows = vec![path, error];
if metadata
.and_then(|value| value.get("humanize_protection_denied"))
.and_then(serde_json::Value::as_bool)
== Some(true)
{
rows.extend(attempted_change_preview(
params.and_then(|value| value.get(arg_key::CONTENT)),
));
}
rows.into_iter()
.filter(|row| !row.is_empty())
.collect::<Vec<_>>()
.join("\n")
}
_ => path,
};
let body_lines = body
.lines()
.enumerate()
.map(|(index, line)| {
DisplayLine::from_span(
line,
if index < path_lines {
DisplayRole::InlineCode
} else {
DisplayRole::Plain
},
)
})
.collect();
TranscriptCard {
id,
role: TranscriptCardRole::Write,
status,
title,
metadata: humanize_protection_metadata(detail),
body,
body_lines,
children: Vec::new(),
activity_rows: Vec::new(),
subagent_card: None,
}
}