Skip to content

Commit e485b3f

Browse files
committed
mason: add tool_call preview controls
1 parent 62e8004 commit e485b3f

15 files changed

Lines changed: 344 additions & 8 deletions

File tree

crates/aft/src/commands/tool_call.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ fn handle_with_dispatch(req: &RawRequest, ctx: &AppContext, dispatch: &DispatchF
5151
.get("arguments")
5252
.cloned()
5353
.unwrap_or_else(|| json!({}));
54+
let preview = req
55+
.params
56+
.get("preview")
57+
.and_then(Value::as_bool)
58+
.unwrap_or(false);
5459
let config = ctx.config();
5560
let project_root = config
5661
.project_root
@@ -61,6 +66,7 @@ fn handle_with_dispatch(req: &RawRequest, ctx: &AppContext, dispatch: &DispatchF
6166
session_id: Some(req.session().to_string()),
6267
request_id: req.id.clone(),
6368
diagnostics_on_edit: config.diagnostics_on_edit,
69+
preview,
6470
};
6571

6672
match run_tool_call(name, &arguments, &tool_ctx, ctx, dispatch) {

crates/aft/src/commands/write.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ pub fn handle_write(req: &RawRequest, ctx: &AppContext) -> Response {
7272
String::new()
7373
};
7474

75+
if edit::wants_preview(&req.params) {
76+
let mut result = serde_json::json!({
77+
"file": file,
78+
"created": !existed,
79+
"formatted": false,
80+
});
81+
if existed && original == content {
82+
result["no_op"] = serde_json::json!(true);
83+
}
84+
edit::attach_preview_diff(&mut result, &req.params, file, &original, content);
85+
return Response::success(&req.id, result);
86+
}
87+
7588
// Auto-backup existing files before overwriting. For create-only writes,
7689
// record a tombstone so operation undo removes the created file.
7790
let backup_id = if existed {

crates/aft/src/run_tool_call.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ pub enum ToolCallOutcome {
2424
Unary(ToolCallResult),
2525
}
2626

27-
/// Single owner of per-call context (Oracle #4). diagnostics_on_edit has ONE source here.
27+
/// Server-owned settings for a single `tool_call` request.
28+
/// These fields cannot be supplied through the agent's arguments object.
2829
#[derive(Debug, Clone)]
2930
pub struct ToolCallContext {
3031
pub project_root: PathBuf,
3132
pub session_id: Option<String>,
3233
pub request_id: String,
3334
pub diagnostics_on_edit: bool,
35+
pub preview: bool,
3436
}
3537

3638
pub fn run_tool_call(
@@ -40,17 +42,19 @@ pub fn run_tool_call(
4042
app_ctx: &AppContext,
4143
dispatch: &DispatchFn<'_>,
4244
) -> ToolCallOutcome {
45+
let sanitized_args = strip_agent_preview_arg(args);
4346
let format_context = crate::subc_format::FormatContext::from_tool_call(
4447
bare_name,
45-
args,
48+
&sanitized_args,
4649
ctx.project_root.as_path(),
4750
);
4851
let translate_context = crate::subc_translate::TranslateContext {
4952
diagnostics_on_edit: ctx.diagnostics_on_edit,
53+
preview: ctx.preview,
5054
};
5155
let (command, translated_args) = match crate::subc_translate::subc_translate_with_context(
5256
bare_name,
53-
args,
57+
&sanitized_args,
5458
ctx.project_root.as_path(),
5559
translate_context,
5660
) {
@@ -64,7 +68,7 @@ pub fn run_tool_call(
6468
));
6569
}
6670
Err(_) => {
67-
let map = args.as_object().cloned().unwrap_or_default();
71+
let map = sanitized_args.as_object().cloned().unwrap_or_default();
6872
(bare_name.to_string(), map)
6973
}
7074
};
@@ -98,6 +102,19 @@ pub fn run_tool_call(
98102
))
99103
}
100104

105+
fn strip_agent_preview_arg(args: &Value) -> Value {
106+
let Some(map) = args.as_object() else {
107+
return args.clone();
108+
};
109+
if !map.contains_key("preview") {
110+
return args.clone();
111+
}
112+
113+
let mut sanitized = map.clone();
114+
sanitized.remove("preview");
115+
Value::Object(sanitized)
116+
}
117+
101118
fn tool_call_result_from_response(
102119
bare_name: &str,
103120
format_context: &crate::subc_format::FormatContext,

crates/aft/src/subc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,7 @@ async fn handle_tool_call(
19371937
session_id: Some(identity.session.clone()),
19381938
request_id: request_id.clone(),
19391939
diagnostics_on_edit,
1940+
preview: false,
19401941
};
19411942
let arguments_for_run = call.arguments.clone();
19421943
let bare_name_for_run = bare_name.clone();

crates/aft/src/subc_translate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub struct Translated {
1313
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1414
pub struct TranslateContext {
1515
pub diagnostics_on_edit: bool,
16+
pub preview: bool,
1617
}
1718

1819
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -193,6 +194,7 @@ fn insert_common_mutation_flags(out: &mut Map<String, Value>, ctx: TranslateCont
193194
Value::Bool(ctx.diagnostics_on_edit),
194195
);
195196
out.insert("include_diff_content".to_string(), Value::Bool(true));
197+
out.insert("preview".to_string(), Value::Bool(ctx.preview));
196198
}
197199

198200
fn translate_read(args: &Value, project_root: &Path) -> Result<Translated, TranslateError> {

crates/aft/tests/fixtures/subc_parity/translate/edit_append/expected.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"diagnostics": true,
66
"file": "<PROJECT_ROOT>/src/main.ts",
77
"include_diff_content": true,
8-
"op": "append"
8+
"op": "append",
9+
"preview": false
910
},
1011
"command": "edit_match"
1112
}

crates/aft/tests/fixtures/subc_parity/translate/edit_batch/expected.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
}
1111
],
1212
"file": "<PROJECT_ROOT>/src/main.ts",
13-
"include_diff_content": true
13+
"include_diff_content": true,
14+
"preview": false
1415
},
1516
"command": "batch"
1617
}

crates/aft/tests/fixtures/subc_parity/translate/edit_replace_occurrence_zero/expected.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"include_diff_content": true,
66
"match": "value",
77
"occurrence": 0,
8+
"preview": false,
89
"replacement": "answer"
910
},
1011
"command": "edit_match"

crates/aft/tests/fixtures/subc_parity/translate/edit_symbol/expected.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"file": "<PROJECT_ROOT>/src/main.ts",
66
"include_diff_content": true,
77
"operation": "replace",
8+
"preview": false,
89
"symbol": "value"
910
},
1011
"command": "edit_symbol"

crates/aft/tests/fixtures/subc_parity/translate/write_default_diagnostics/expected.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"create_dirs": true,
55
"diagnostics": false,
66
"file": "<PROJECT_ROOT>/src/new.ts",
7-
"include_diff_content": true
7+
"include_diff_content": true,
8+
"preview": false
89
},
910
"command": "write"
1011
}

0 commit comments

Comments
 (0)