Parse Codex app-server patch payloads
This commit is contained in:
1 parent
10ce1a216b
commit
b00e89795e
1 file changed
+63
-28
@@ -305,34 +305,58 @@ fn shell_word(word: &Value) -> String {
|
||||
}
|
||||
|
||||
fn file_change_diff(item: &Value) -> String {
|
||||
let Some(changes) = item.get("changes").and_then(Value::as_object) else {
|
||||
return String::new();
|
||||
};
|
||||
changes
|
||||
.iter()
|
||||
.map(|(path, change)| {
|
||||
let kind = change.get("type").and_then(Value::as_str);
|
||||
let from = if kind == Some("add") {
|
||||
"/dev/null"
|
||||
} else {
|
||||
path
|
||||
};
|
||||
let to = if kind == Some("delete") {
|
||||
"/dev/null"
|
||||
} else {
|
||||
change
|
||||
.get("move_path")
|
||||
match item.get("changes") {
|
||||
// Current app-server protocol: [{path, kind: {type, move_path?}, diff}].
|
||||
Some(Value::Array(changes)) => changes
|
||||
.iter()
|
||||
.filter_map(|change| {
|
||||
let path = change.get("path")?.as_str()?;
|
||||
let kind = change.pointer("/kind/type").and_then(Value::as_str);
|
||||
let moved = change.pointer("/kind/move_path").and_then(Value::as_str);
|
||||
let body = change
|
||||
.get("diff")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or(path)
|
||||
};
|
||||
let body = change
|
||||
.get("unified_diff")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default();
|
||||
format!("--- {from}\n+++ {to}\n{body}")
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
.unwrap_or_default();
|
||||
Some(render_file_diff(path, kind, moved, body))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
// Older exec protocol and durable Codex rollouts: {path: {type, unified_diff, ...}}.
|
||||
Some(Value::Object(changes)) => changes
|
||||
.iter()
|
||||
.map(|(path, change)| {
|
||||
render_file_diff(
|
||||
path,
|
||||
change.get("type").and_then(Value::as_str),
|
||||
change.get("move_path").and_then(Value::as_str),
|
||||
change
|
||||
.get("unified_diff")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_file_diff(path: &str, kind: Option<&str>, moved: Option<&str>, body: &str) -> String {
|
||||
// Some protocol revisions carry complete unified diffs and some carry only their hunks.
|
||||
if body.starts_with("--- ") || body.starts_with("diff --git ") {
|
||||
return body.to_string();
|
||||
}
|
||||
let from = if kind == Some("add") {
|
||||
"/dev/null"
|
||||
} else {
|
||||
path
|
||||
};
|
||||
let to = if kind == Some("delete") {
|
||||
"/dev/null"
|
||||
} else {
|
||||
moved.unwrap_or(path)
|
||||
};
|
||||
format!("--- {from}\n+++ {to}\n{body}")
|
||||
}
|
||||
|
||||
fn tool_output(item: &Value) -> String {
|
||||
@@ -537,7 +561,7 @@ mod tests {
|
||||
assert!(started.is_empty());
|
||||
assert_eq!(
|
||||
translator.translate(&line(
|
||||
r#"{"method":"item/completed","params":{"item":{"id":"patch-1","type":"fileChange","changes":{"src/main.rs":{"type":"update","unified_diff":"@@ -1 +1 @@\n-old\n+new\n","move_path":null}},"status":"completed","stdout":"Success"}}}"#
|
||||
r#"{"method":"item/completed","params":{"item":{"id":"patch-1","type":"fileChange","changes":[{"path":"src/main.rs","kind":{"type":"update","move_path":null},"diff":"@@ -1 +1 @@\n-old\n+new\n"}],"status":"completed","stdout":"Success"}}}"#
|
||||
)),
|
||||
vec![
|
||||
patch_start(
|
||||
@@ -552,6 +576,17 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_legacy_file_change_shape_remains_readable() {
|
||||
let item = line(
|
||||
r#"{"changes":{"old.rs":{"type":"update","unified_diff":"@@ -1 +1 @@\n-a\n+b\n","move_path":"new.rs"}}}"#,
|
||||
);
|
||||
assert_eq!(
|
||||
file_change_diff(&item),
|
||||
"--- old.rs\n+++ new.rs\n@@ -1 +1 @@\n-a\n+b\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn translates_native_app_server_streaming_without_repeating_the_final_item() {
|
||||
let mut translator = Translator::default();
|
||||
|
||||
Reference in new issue
Block a user