@@ -5,7 +5,10 @@ import { WebFetchTool } from "@superset/ui/ai-elements/web-fetch-tool";
55import { WebSearchTool } from "@superset/ui/ai-elements/web-search-tool" ;
66import { getToolName } from "ai" ;
77import { FileIcon , FolderIcon , MessageCircleQuestionIcon } from "lucide-react" ;
8+ import { useCallback } from "react" ;
9+ import { useTabsStore } from "renderer/stores/tabs/store" ;
810import { READ_ONLY_TOOLS } from "../../constants" ;
11+ import { normalizeWorkspaceFilePath } from "../../utils/file-paths" ;
912import type { ToolPart } from "../../utils/tool-helpers" ;
1013import {
1114 getArgs ,
@@ -14,24 +17,42 @@ import {
1417 toWsToolState ,
1518} from "../../utils/tool-helpers" ;
1619import { ReadOnlyToolCall } from "../ReadOnlyToolCall" ;
20+ import { EditToolExpandedDiff } from "./components/EditToolExpandedDiff" ;
1721import { GenericToolCall } from "./components/GenericToolCall" ;
1822
1923interface MastraToolCallBlockProps {
2024 part : ToolPart ;
25+ workspaceId ?: string ;
26+ workspaceCwd ?: string ;
2127 onAnswer ?: ( toolCallId : string , answers : Record < string , string > ) => void ;
2228}
2329
2430export function MastraToolCallBlock ( {
2531 part,
32+ workspaceId,
33+ workspaceCwd,
2634 onAnswer,
2735} : MastraToolCallBlockProps ) {
2836 const args = getArgs ( part ) ;
2937 const result = getResult ( part ) ;
3038 const state = toWsToolState ( part ) ;
3139 const toolName = normalizeToolName ( getToolName ( part ) ) ;
40+ const addFileViewerPane = useTabsStore ( ( store ) => store . addFileViewerPane ) ;
3241 const toolDisplayName = toolName
3342 . replace ( "mastra_workspace_" , "" )
3443 . replaceAll ( "_" , " " ) ;
44+ const openFileInPane = useCallback (
45+ ( filePath : string ) => {
46+ if ( ! workspaceId ) return ;
47+ const normalizedPath = normalizeWorkspaceFilePath ( {
48+ filePath,
49+ workspaceRoot : workspaceCwd ,
50+ } ) ;
51+ if ( ! normalizedPath ) return ;
52+ addFileViewerPane ( workspaceId , { filePath : normalizedPath } ) ;
53+ } ,
54+ [ addFileViewerPane , workspaceCwd , workspaceId ] ,
55+ ) ;
3556
3657 const outputObject =
3758 typeof result . output === "object" && result . output !== null
@@ -64,6 +85,17 @@ export function MastraToolCallBlock({
6485 return undefined ;
6586 } ;
6687
88+ const toRecord = ( value : unknown ) : Record < string , unknown > | undefined => {
89+ if ( typeof value === "object" && value !== null && ! Array . isArray ( value ) ) {
90+ return value as Record < string , unknown > ;
91+ }
92+ return undefined ;
93+ } ;
94+
95+ const getFilePath = ( ...values : unknown [ ] ) : string => {
96+ return firstText ( ...values ) ?? "" ;
97+ } ;
98+
6799 const toNumber = ( value : unknown ) : number | undefined => {
68100 if ( typeof value === "number" && Number . isFinite ( value ) ) return value ;
69101 if ( typeof value === "string" && value . trim ( ) . length > 0 ) {
@@ -151,36 +183,141 @@ export function MastraToolCallBlock({
151183
152184 // --- Write file → FileDiffTool (write mode) ---
153185 if ( toolName === "mastra_workspace_write_file" ) {
154- const filePath = String (
155- args . path ?? args . filePath ?? args . relative_workspace_path ?? "" ,
186+ const filePath = getFilePath (
187+ args . path ,
188+ args . filePath ,
189+ args . file_path ,
190+ args . relative_workspace_path ,
191+ args . relativePath ,
192+ args . file ,
193+ args . filename ,
194+ toRecord ( args . target ) ?. path ,
156195 ) ;
157196 const content = String ( args . content ?? args . data ?? "" ) ;
158197 return (
159198 < FileDiffTool
160199 filePath = { filePath }
161200 content = { content }
162201 isWriteMode
202+ onFilePathClick = { openFileInPane }
163203 state = { state }
164204 />
165205 ) ;
166206 }
167207
168208 // --- Edit file → FileDiffTool (diff mode) ---
169- if ( toolName === "mastra_workspace_edit_file" ) {
170- const filePath = String (
171- args . path ?? args . filePath ?? args . relative_workspace_path ?? "" ,
172- ) ;
173- const oldString = String (
174- args . oldString ?? args . old_string ?? args . old_str ?? "" ,
209+ if (
210+ toolName === "mastra_workspace_edit_file" ||
211+ toolName === "ast_smart_edit"
212+ ) {
213+ const editArgs = toRecord ( args . edit ) ;
214+ const filePath = getFilePath (
215+ args . path ,
216+ args . filePath ,
217+ args . file_path ,
218+ args . relative_workspace_path ,
219+ args . relativePath ,
220+ args . file ,
221+ args . filename ,
222+ args . file_name ,
223+ args . target_file ,
224+ args . target_path ,
225+ args . targetPath ,
226+ editArgs ?. path ,
227+ editArgs ?. filePath ,
228+ editArgs ?. file_path ,
229+ toRecord ( args . target ) ?. path ,
175230 ) ;
176- const newString = String (
177- args . newString ?? args . new_string ?? args . new_str ?? "" ,
231+ const oldString =
232+ firstText (
233+ args . oldString ,
234+ args . old_string ,
235+ args . old_str ,
236+ args . oldText ,
237+ args . old_text ,
238+ args . oldCode ,
239+ args . old_code ,
240+ args . before ,
241+ args . find ,
242+ args . search ,
243+ args . original ,
244+ args . previous ,
245+ args . from ,
246+ editArgs ?. oldString ,
247+ editArgs ?. old_string ,
248+ editArgs ?. oldText ,
249+ editArgs ?. before ,
250+ outputObject ?. oldString ,
251+ outputObject ?. old_string ,
252+ nestedResultObject ?. oldString ,
253+ nestedResultObject ?. old_string ,
254+ ) ?? "" ;
255+ const newString =
256+ firstText (
257+ args . newString ,
258+ args . new_string ,
259+ args . new_str ,
260+ args . newText ,
261+ args . new_text ,
262+ args . newCode ,
263+ args . new_code ,
264+ args . after ,
265+ args . replace ,
266+ args . replacement ,
267+ args . updated ,
268+ args . to ,
269+ editArgs ?. newString ,
270+ editArgs ?. new_string ,
271+ editArgs ?. newText ,
272+ editArgs ?. after ,
273+ outputObject ?. newString ,
274+ outputObject ?. new_string ,
275+ nestedResultObject ?. newString ,
276+ nestedResultObject ?. new_string ,
277+ ) ?? "" ;
278+
279+ const structuredPatchValue =
280+ ( Array . isArray ( result . structuredPatch )
281+ ? result . structuredPatch
282+ : Array . isArray ( outputObject ?. structuredPatch )
283+ ? outputObject ?. structuredPatch
284+ : Array . isArray ( nestedResultObject ?. structuredPatch )
285+ ? nestedResultObject ?. structuredPatch
286+ : undefined ) ??
287+ ( Array . isArray ( result . structured_patch )
288+ ? result . structured_patch
289+ : Array . isArray ( outputObject ?. structured_patch )
290+ ? outputObject ?. structured_patch
291+ : Array . isArray ( nestedResultObject ?. structured_patch )
292+ ? nestedResultObject ?. structured_patch
293+ : undefined ) ;
294+ const structuredPatch = structuredPatchValue ?. filter (
295+ ( hunk ) : hunk is { lines : string [ ] } => {
296+ return Boolean (
297+ typeof hunk === "object" &&
298+ hunk !== null &&
299+ Array . isArray ( ( hunk as { lines ?: unknown } ) . lines ) ,
300+ ) ;
301+ } ,
178302 ) ;
179303 return (
180304 < FileDiffTool
181305 filePath = { filePath }
182306 oldString = { oldString }
183307 newString = { newString }
308+ structuredPatch = { structuredPatch }
309+ onFilePathClick = { openFileInPane }
310+ renderExpandedContent = {
311+ oldString || newString
312+ ? ( ) => (
313+ < EditToolExpandedDiff
314+ filePath = { filePath }
315+ oldString = { oldString }
316+ newString = { newString }
317+ />
318+ )
319+ : undefined
320+ }
184321 state = { state }
185322 />
186323 ) ;
@@ -247,7 +384,7 @@ export function MastraToolCallBlock({
247384
248385 // --- Read-only exploration tools ---
249386 if ( READ_ONLY_TOOLS . has ( toolName ) ) {
250- return < ReadOnlyToolCall part = { part } /> ;
387+ return < ReadOnlyToolCall part = { part } onOpenFileInPane = { openFileInPane } /> ;
251388 }
252389
253390 // --- Destructive workspace tools ---
0 commit comments