@@ -37,13 +37,25 @@ def _tool_result_pred_file_edit(tr: dict[str, Any]) -> bool:
3737
3838
3939def _tool_result_build_file_edit (tr : dict [str , Any ], base : dict [str , Any ]) -> dict [str , Any ]:
40+ # Summary fields only; full blob (e.g. structuredPatch) stays on message tool_result.
4041 result = dict (base )
4142 result ["result_type" ] = "file_edit"
4243 result ["file_path" ] = tr .get ("filePath" , "" )
4344 result ["replace_all" ] = tr .get ("replaceAll" , False )
4445 return result
4546
4647
48+ def _tool_result_pred_plan (tr : dict [str , Any ]) -> bool :
49+ return "plan" in tr and "filePath" in tr
50+
51+
52+ def _tool_result_build_plan (tr : dict [str , Any ], base : dict [str , Any ]) -> dict [str , Any ]:
53+ result = dict (base )
54+ result ["result_type" ] = "plan"
55+ result ["file_path" ] = tr .get ("filePath" , "" )
56+ return result
57+
58+
4759def _tool_result_pred_file_write (tr : dict [str , Any ]) -> bool :
4860 return "filePath" in tr and "content" in tr
4961
@@ -82,7 +94,7 @@ def _tool_result_build_grep(tr: dict[str, Any], base: dict[str, Any]) -> dict[st
8294 result ["num_lines" ] = tr .get ("numLines" , 0 )
8395 result ["duration_ms" ] = tr .get ("durationMs" )
8496 content = tr .get ("content" , "" )
85- if content and isinstance (content , str ):
97+ if isinstance (content , str ):
8698 result ["content" ] = content
8799 return result
88100
@@ -98,7 +110,7 @@ def _tool_result_build_file_read(tr: dict[str, Any], base: dict[str, Any]) -> di
98110 result ["file_path" ] = file_obj .get ("filePath" , "" )
99111 result ["num_lines" ] = file_obj .get ("numLines" )
100112 content = file_obj .get ("content" , "" )
101- if content and isinstance (content , str ):
113+ if isinstance (content , str ):
102114 result ["content" ] = content
103115 return result
104116
@@ -217,17 +229,6 @@ def _tool_result_build_user_input(tr: dict[str, Any], base: dict[str, Any]) -> d
217229 return result
218230
219231
220- def _tool_result_pred_plan (tr : dict [str , Any ]) -> bool :
221- return "plan" in tr and "filePath" in tr
222-
223-
224- def _tool_result_build_plan (tr : dict [str , Any ], base : dict [str , Any ]) -> dict [str , Any ]:
225- result = dict (base )
226- result ["result_type" ] = "plan"
227- result ["file_path" ] = tr .get ("filePath" , "" )
228- return result
229-
230-
231232# Dispatch registry: **first matching predicate wins** (same as legacy if/elif).
232233# Order is load-bearing — do not sort alphabetically or “more specific first”
233234# without replaying tests and real session fixtures.
@@ -241,6 +242,8 @@ def _tool_result_build_plan(tr: dict[str, Any], base: dict[str, Any]) -> dict[st
241242_TOOL_RESULT_DISPATCH = (
242243 (_tool_result_pred_bash , _tool_result_build_bash ),
243244 (_tool_result_pred_file_edit , _tool_result_build_file_edit ),
245+ # plan before file_write: plan blobs may also carry filePath + content
246+ (_tool_result_pred_plan , _tool_result_build_plan ),
244247 (_tool_result_pred_file_write , _tool_result_build_file_write ),
245248 (_tool_result_pred_glob , _tool_result_build_glob ),
246249 (_tool_result_pred_grep , _tool_result_build_grep ),
@@ -253,7 +256,6 @@ def _tool_result_build_plan(tr: dict[str, Any], base: dict[str, Any]) -> dict[st
253256 (_tool_result_pred_task_async , _tool_result_build_task_async ),
254257 (_tool_result_pred_todo_write , _tool_result_build_todo_write ),
255258 (_tool_result_pred_user_input , _tool_result_build_user_input ),
256- (_tool_result_pred_plan , _tool_result_build_plan ),
257259)
258260
259261
0 commit comments