Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 4 additions & 18 deletions crates/openplotva-app/src/python_sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ impl PythonSandboxTool {
} else {
match delivery_paths(&send_files, &response) {
Err(count) => outcome.unselected = count,
Ok(paths) if request.withhold_delivery => outcome.withheld = paths,
Ok(paths) if request.withhold_delivery => outcome.report.sent = paths,
Ok(paths) if paths.is_empty() => {}
Ok(paths) => {
outcome.report = self
Expand Down Expand Up @@ -318,9 +318,6 @@ struct DeliveryOutcome {
report: DeliveryReport,
/// Named files left unsent because the script failed.
skipped: Vec<String>,
/// Files a run with withheld delivery would have sent. The model reads
/// them as received, so a test dialog continues as a real chat would.
withheld: Vec<String>,
/// Changed files left unsent because there were too many to send unasked.
unselected: usize,
}
Expand Down Expand Up @@ -437,7 +434,6 @@ fn run_result(response: &RunResponse, inputs: Vec<String>, outcome: DeliveryOutc
let DeliveryOutcome {
report,
skipped,
withheld,
unselected,
} = outcome;
let mut data = Map::new();
Expand Down Expand Up @@ -495,22 +491,14 @@ fn run_result(response: &RunResponse, inputs: Vec<String>, outcome: DeliveryOutc
if !skipped.is_empty() {
data.insert("send_skipped".to_owned(), json!(skipped));
}
if !withheld.is_empty() {
data.insert("send_withheld".to_owned(), json!(withheld));
}
let data = Some(Value::Object(data));

let (code, reason) = match (response.termination, response.exit_code) {
(Termination::Exited, Some(0)) if report.errors.is_empty() => {
let received = if report.sent.is_empty() {
&withheld
} else {
&report.sent
};
let message = if !received.is_empty() {
let message = if !report.sent.is_empty() {
format!(
"The script finished; the chat received {}.",
received.join(", ")
report.sent.join(", ")
)
} else if unselected > 0 {
format!(
Expand Down Expand Up @@ -1304,9 +1292,7 @@ mod tests {
result.message,
"The script finished; the chat received chart.png."
);
let data = result.data.expect("data");
assert_eq!(data["send_withheld"], json!(["chart.png"]));
assert!(data.get("sent").is_none());
assert_eq!(result.data.expect("data")["sent"], json!(["chart.png"]));
}

#[tokio::test]
Expand Down
3 changes: 2 additions & 1 deletion crates/openplotva-dialog/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ pub struct PythonRunRequest {
/// Caption for the delivered files.
#[serde(default, skip_serializing_if = "String::is_empty")]
pub caption: String,
/// Run the script but report the files instead of sending them.
/// Run the script and report its files as sent without sending them,
/// so a test dialog continues the way a real chat would.
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub withhold_delivery: bool,
}
Expand Down
4 changes: 2 additions & 2 deletions docs/superpowers/specs/2026-10-20-python-sandbox-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,5 @@ scripts themselves never see that network.
- The stored tool result renders the call's `code` next to its output, which
is how a follow-up turn finds the script to edit.
- The tool is offered only when `SANDBOX_URL` is set. SAFE runtime virtual
dialogs run the script and report the files they would send as
`send_withheld`.
dialogs run the script and report its files as sent without sending them,
next to `tool_mode: "SAFE"`.
Loading