Skip to main content

deepstrike_core/scheduler/
rollback.rs

1//! Pure helpers for turn-transaction rollback messaging.
2//!
3//! Extracted from `LoopStateMachine` — these functions carry no state machine
4//! context, only formatting a [`RollbackReason`] into the text surfaced to the
5//! model (concise) or telemetry (verbose) and extracting tool-result output.
6
7use crate::runtime::session::RollbackReason;
8use crate::types::message::{Content, ToolResult};
9
10/// Flatten a tool result's output into plain text.
11/// `Content::Parts` are serialised to JSON so the text can be carried faithfully.
12pub(crate) fn tool_result_output_text(result: &ToolResult) -> String {
13    match &result.output {
14        Content::Text(s) => s.clone(),
15        Content::Parts(parts) => serde_json::to_string(parts).unwrap_or_default(),
16    }
17}
18
19/// Internal, telemetry-oriented description of a rollback reason.
20pub(crate) fn rollback_reason_message(reason: &RollbackReason) -> String {
21    match reason {
22        RollbackReason::FatalToolError { tool_name, error } => {
23            format!("fatal tool error in {tool_name}: {error}")
24        }
25        RollbackReason::GovernanceDenied { tool_name, reason } => {
26            format!("governance denied {tool_name}: {reason}")
27        }
28        RollbackReason::ProviderFailure { error } => {
29            format!("provider failure: {error}")
30        }
31        RollbackReason::Timeout => "timeout".to_string(),
32        RollbackReason::UserInterrupt => "user interrupt".to_string(),
33        RollbackReason::MalformedReplay { reason } => {
34            format!("malformed replay: {reason}")
35        }
36    }
37}
38
39/// Build the note injected into the conversation after a rollback.
40/// `verbose` uses the internal `[SYSTEM] Transaction rollback: …` format;
41/// otherwise a concise, model-facing instruction is produced.
42pub(crate) fn build_rollback_note(reason: &RollbackReason, verbose: bool) -> String {
43    if verbose {
44        return format!(
45            "[SYSTEM] Transaction rollback: {}",
46            rollback_reason_message(reason)
47        );
48    }
49    match reason {
50        RollbackReason::FatalToolError { tool_name, error } => {
51            format!("The previous step failed (`{tool_name}`: {error}). Please try a different approach.")
52        }
53        RollbackReason::GovernanceDenied { tool_name, reason } => {
54            format!("Action `{tool_name}` was not allowed ({reason}). Please choose a different approach.")
55        }
56        RollbackReason::ProviderFailure { .. } => {
57            "The previous attempt failed. Please try again.".to_string()
58        }
59        RollbackReason::Timeout => {
60            "The previous step timed out. Please try a faster approach.".to_string()
61        }
62        RollbackReason::UserInterrupt => "Interrupted. Please continue.".to_string(),
63        RollbackReason::MalformedReplay { .. } => {
64            "Context inconsistency detected. Please continue.".to_string()
65        }
66    }
67}