diff --git a/internal/lua/adapters/trae.lua b/internal/lua/adapters/trae.lua index d05b45a..45949cc 100644 --- a/internal/lua/adapters/trae.lua +++ b/internal/lua/adapters/trae.lua @@ -46,9 +46,6 @@ end -- Returns (tool_calls_array_or_nil, content_with_blocks_removed). local function parse_text_tool_calls(content) if type(content) ~= "string" or content == "" then return nil, content end - if not (content:find("]*>%s*(.-)%s*") collect("]*>%s*(.-)%s*") + -- Legacy / mimicked form: models that saw past assistant turns folded as + -- [Called tool: name({...})] + -- tend to emit the same shape instead of a real block. Recover it so + -- the agent loop keeps working; otherwise the client receives plain text + -- where a structured tool call should be. Capture the name and the JSON + -- object literal independently (the JSON is the only {...} run here). + if #tcs == 0 and content:find("%[Called tool") then + for name, argsraw in content:gmatch("%[Called tool%s*:%s*([%w_%-%.]+)%s*%((%b{})%s*%)%s*%]") do + local aok, decoded = pcall(json.decode, argsraw) + idx = idx + 1 + table.insert(tcs, { + id = "call_legacy_" .. idx, + type = "function", + name = name, + arguments = aok and decoded or {} + }) + end + end + if #tcs == 0 then return nil, content end -- drop the blocks from user-visible content; keep any surrounding prose local stripped = content:gsub("]*>%s*.-%s*", "") stripped = stripped:gsub("]*>%s*.-%s*", "") + stripped = stripped:gsub("%[Called tool%s*:%s*[%w_%-%.]+%s*%(%b{}%s*%)%s*%]", "") stripped = stripped:gsub("^%s+", ""):gsub("%s+$", "") return tcs, stripped end