feat: record agent approvals
This commit is contained in:
@@ -62,6 +62,7 @@ pub(crate) struct ChatMessage {
|
||||
pub(super) reasoning_open: bool,
|
||||
pub(super) content: String,
|
||||
pub(super) model_content: Option<String>,
|
||||
pub(super) tool_approval_reasons: Vec<Option<String>>,
|
||||
pub(super) markdown: markdown::Content,
|
||||
pub(super) transcript: text_editor::Content,
|
||||
pub(super) a2ui_lines_processed: usize,
|
||||
@@ -229,6 +230,11 @@ impl From<StoredMessage> for ChatMessage {
|
||||
reasoning_open: false,
|
||||
content: message.content,
|
||||
model_content: message.model_content,
|
||||
tool_approval_reasons: message
|
||||
.tool_approval_reasons
|
||||
.as_deref()
|
||||
.and_then(|reasons| serde_json::from_str(reasons).ok())
|
||||
.unwrap_or_default(),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
@@ -789,6 +795,26 @@ impl App {
|
||||
self.pending_tool_approval = Some((prompt, decision));
|
||||
self.activity = Some(format!("Tool {} · Awaiting approval", index + 1));
|
||||
}
|
||||
crate::agent::ToolEvent::ApprovalReason { index, reason } => {
|
||||
if let Some(card) = self.tool_cards.get_mut(index) {
|
||||
card.approval_reason = Some(reason);
|
||||
}
|
||||
let reasons = self
|
||||
.tool_cards
|
||||
.iter()
|
||||
.map(|card| card.approval_reason.clone())
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(message) = self.conversation.last_mut() {
|
||||
message.tool_approval_reasons = reasons.clone();
|
||||
if let Some(database) = &mut self.database
|
||||
&& let Err(error) = database
|
||||
.update_message_tool_approval_reasons(message.id, &reasons)
|
||||
{
|
||||
self.error =
|
||||
Some(format!("Could not save tool approval reasons: {error}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1961,6 +1987,7 @@ mod tests {
|
||||
reasoning_open: false,
|
||||
content: content.to_owned(),
|
||||
model_content: None,
|
||||
tool_approval_reasons: Vec::new(),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
@@ -2156,6 +2183,7 @@ mod tests {
|
||||
content: "### Core / Setup\n\n| File | Lines |\n|---|---:|\n| `src/app.rs` | **1,750** |\n| `src/engine.rs` | 2,400 |\n\n### Summary\n\nDone."
|
||||
.to_owned(),
|
||||
model_content: None,
|
||||
tool_approval_reasons: Vec::new(),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
@@ -2223,6 +2251,7 @@ mod tests {
|
||||
reasoning_open: false,
|
||||
content: format!("message {id}"),
|
||||
model_content: None,
|
||||
tool_approval_reasons: Vec::new(),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
@@ -2262,6 +2291,7 @@ mod tests {
|
||||
reasoning_open: false,
|
||||
content: format!("message {id}"),
|
||||
model_content: None,
|
||||
tool_approval_reasons: Vec::new(),
|
||||
markdown: iced::widget::markdown::Content::new(),
|
||||
transcript: iced::widget::text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
|
||||
@@ -40,6 +40,7 @@ const ICON_MORE: &[u8] = include_bytes!("../../assets/icons/more.svg");
|
||||
const ICON_PAPERCLIP: &[u8] = include_bytes!("../../assets/icons/paperclip.svg");
|
||||
const ICON_SEND: &[u8] = include_bytes!("../../assets/icons/send.svg");
|
||||
const ICON_MODEL: &[u8] = include_bytes!("../../assets/icons/model.svg");
|
||||
const ICON_ROBOT: &[u8] = include_bytes!("../../assets/icons/robot.svg");
|
||||
const ICON_SPARK: &[u8] = include_bytes!("../../assets/icons/spark.svg");
|
||||
const ICON_PIN: &[u8] = include_bytes!("../../assets/icons/pin.svg");
|
||||
const ICON_SIDEBAR: &[u8] = include_bytes!("../../assets/icons/sidebar.svg");
|
||||
|
||||
@@ -81,6 +81,7 @@ impl App {
|
||||
self.config.model,
|
||||
&self.conversation[index - 1].content,
|
||||
None,
|
||||
&self.conversation[index - 1].tool_approval_reasons,
|
||||
)
|
||||
.is_empty()
|
||||
{
|
||||
@@ -191,6 +192,7 @@ impl App {
|
||||
self.config.model,
|
||||
&message.content,
|
||||
stored_result,
|
||||
&message.tool_approval_reasons,
|
||||
)
|
||||
};
|
||||
if !cards.is_empty() {
|
||||
@@ -540,9 +542,25 @@ fn tool_cards(cards: Vec<crate::agent::ToolCard>) -> Element<'static, Message> {
|
||||
.gap(6),
|
||||
);
|
||||
}
|
||||
let mut name = row![text(card.call.name).size(13)]
|
||||
.spacing(5)
|
||||
.align_y(Alignment::Center);
|
||||
if let Some(reason) = &card.approval_reason {
|
||||
name = name.push(
|
||||
tooltip(
|
||||
icon(ICON_ROBOT, 13),
|
||||
container(text(reason.clone()).size(12))
|
||||
.padding(8)
|
||||
.max_width(320)
|
||||
.style(preference_group_style),
|
||||
tooltip::Position::Top,
|
||||
)
|
||||
.gap(5),
|
||||
);
|
||||
}
|
||||
let mut content = column![
|
||||
row![
|
||||
text(card.call.name).size(13),
|
||||
name,
|
||||
Space::new().width(Length::Fill),
|
||||
text(card.state.label()).size(11).color(muted_text()),
|
||||
actions,
|
||||
|
||||
@@ -825,6 +825,7 @@ mod tests {
|
||||
reasoning_open: false,
|
||||
content: content.to_owned(),
|
||||
model_content: None,
|
||||
tool_approval_reasons: Vec::new(),
|
||||
markdown: markdown::Content::new(),
|
||||
transcript: text_editor::Content::new(),
|
||||
a2ui_lines_processed: 0,
|
||||
|
||||
Reference in New Issue
Block a user