Skip to main content

codetether_agent/session/helper/
confirmation.rs

1//! Pending-confirmation helpers for tool results.
2//!
3//! Edit tools such as `edit`, `multiedit`, and `write` emit *preview* results
4//! that must be explicitly confirmed before being applied to disk. This module
5//! provides the small set of helpers that decide whether a tool result
6//! requires confirmation, how to render the user-facing status message, and
7//! how to auto-apply the confirmation when the session has
8//! [`SessionMetadata::auto_apply_edits`](super::super::SessionMetadata::auto_apply_edits)
9//! enabled.
10
11use anyhow::Result;
12use std::collections::HashMap;
13
14use super::edit::build_pending_confirmation_apply_request;
15use crate::tool::Tool;
16use crate::tool::confirm_edit::ConfirmEditTool;
17use crate::tool::confirm_multiedit::ConfirmMultiEditTool;
18
19/// Reminder appended to tool output when `auto_apply_edits` is **off** and the
20/// tool produced only a preview.
21pub(crate) fn pending_confirmation_tool_result_content(tool_name: &str, content: &str) -> String {
22    format!(
23        "{content}\n\nStatus: Pending confirmation only. `{tool_name}` has NOT been applied yet. \
24         Auto-apply is off. Enable it in TUI Settings or with `/autoapply on` if you want pending \
25         edit previews to be confirmed automatically."
26    )
27}
28
29/// Banner prefix shown when `auto_apply_edits` is **on** and a pending change
30/// has been automatically confirmed.
31pub(crate) fn auto_apply_pending_confirmation_result_content(
32    output: &str,
33    success: bool,
34) -> String {
35    let status = if success {
36        "TUI edit auto-apply is enabled. The pending change was automatically confirmed and applied."
37    } else {
38        "TUI edit auto-apply is enabled, but confirming the pending change failed."
39    };
40    format!("{status}\n\n{output}")
41}
42
43/// Inspects tool metadata for the `requires_confirmation` boolean flag.
44pub(crate) fn tool_result_requires_confirmation(
45    tool_metadata: Option<&HashMap<String, serde_json::Value>>,
46) -> bool {
47    tool_metadata
48        .and_then(|metadata| metadata.get("requires_confirmation"))
49        .and_then(serde_json::Value::as_bool)
50        .unwrap_or(false)
51}
52
53/// Automatically apply a pending-confirmation edit when the session has
54/// `auto_apply_edits` enabled.
55///
56/// Returns:
57/// * `Ok(Some((content, success, metadata)))` when the pending edit was
58///   resolved (either successfully applied or explicitly failed).
59/// * `Ok(None)` when the tool call cannot be auto-confirmed (the caller should
60///   render the pending-confirmation banner instead).
61/// * `Err(_)` when the underlying confirm tool returned an error.
62pub(crate) async fn auto_apply_pending_confirmation(
63    tool_name: &str,
64    tool_input: &serde_json::Value,
65    tool_metadata: Option<&HashMap<String, serde_json::Value>>,
66) -> Result<Option<(String, bool, Option<HashMap<String, serde_json::Value>>)>> {
67    let Some((confirm_tool_name, confirm_input)) =
68        build_pending_confirmation_apply_request(tool_name, tool_input, tool_metadata)
69    else {
70        return Ok(None);
71    };
72
73    let result = match confirm_tool_name.as_str() {
74        "confirm_edit" => ConfirmEditTool::new().execute(confirm_input).await?,
75        "confirm_multiedit" => ConfirmMultiEditTool::new().execute(confirm_input).await?,
76        _ => return Ok(None),
77    };
78
79    let metadata = if result.metadata.is_empty() {
80        tool_metadata.cloned()
81    } else {
82        Some(result.metadata)
83    };
84
85    Ok(Some((
86        auto_apply_pending_confirmation_result_content(&result.output, result.success),
87        result.success,
88        metadata,
89    )))
90}