fix(chat): store bridge messages/tools as raw JSON to prevent double-escape corruption on agentic_resume
Neuron Soul CI / build (pull_request) Failing after 12m13s
Neuron Soul CI / build (pull_request) Failing after 12m13s
bridge_save was wrapping messages and tools_json with json_safe() before storing them as string fields. Since both are already well-formed JSON arrays containing double quotes, json_safe added a second escape layer. agentic_resume then called json_get() which stripped only one layer, leaving the messages array corrupted before it was passed back into agentic_loop. Fix: store messages as messages_raw and tools_json as tools_raw as inline raw JSON values (unquoted), and read them back with json_get_raw. Backward compatibility: fall back to the old string-escaped fields if the raw fields are absent, so sessions saved before this fix can still be resumed. Also fixes write_file returning a pre-escaped literal instead of calling json_safe consistently with every other tool result.
This commit is contained in:
@@ -387,7 +387,7 @@ fn dispatch_tool(tool_name: String, tool_input: String) -> String {
|
||||
let path: String = json_get(tool_input, "path")
|
||||
let content: String = json_get(tool_input, "content")
|
||||
fs_write(path, content)
|
||||
return "{\\\"ok\\\":true}"
|
||||
return json_safe("{\"ok\":true}")
|
||||
}
|
||||
if str_eq(tool_name, "web_get") {
|
||||
let url: String = json_get(tool_input, "url")
|
||||
@@ -766,10 +766,14 @@ fn agentic_loop(session_id: String, model: String, safe_sys: String, tools_json:
|
||||
// stored `messages` already includes the assistant turn that requested the tool, so
|
||||
// resume just appends the client's tool_result for `tool_use_id`.
|
||||
fn bridge_save(session_id: String, model: String, safe_sys: String, tools_json: String, messages: String, tools_log: String, tool_use_id: String) -> Bool {
|
||||
// messages and tools_json are already well-formed JSON arrays; embed them as raw
|
||||
// JSON values (not string-escaped) so the round-trip through state_get/json_get_raw
|
||||
// never corrupts nested quotes. Scalar strings (model, safe_sys, tools_log,
|
||||
// tool_use_id) stay as string fields via json_safe as before.
|
||||
let blob: String = "{\"model\":\"" + json_safe(model) + "\""
|
||||
+ ",\"safe_sys\":\"" + json_safe(safe_sys) + "\""
|
||||
+ ",\"tools_json\":\"" + json_safe(tools_json) + "\""
|
||||
+ ",\"messages\":\"" + json_safe(messages) + "\""
|
||||
+ ",\"messages_raw\":" + messages
|
||||
+ ",\"tools_raw\":" + tools_json
|
||||
+ ",\"tools_log\":\"" + json_safe(tools_log) + "\""
|
||||
+ ",\"tool_use_id\":\"" + json_safe(tool_use_id) + "\"}"
|
||||
state_set("mcp_bridge:" + session_id, blob)
|
||||
@@ -789,8 +793,12 @@ fn agentic_resume(session_id: String, tool_use_id: String, content: String) -> S
|
||||
|
||||
let model: String = json_get(blob, "model")
|
||||
let safe_sys: String = json_get(blob, "safe_sys")
|
||||
let tools_json: String = json_get(blob, "tools_json")
|
||||
let messages: String = json_get(blob, "messages")
|
||||
// messages_raw and tools_raw are embedded as raw JSON (not string-escaped);
|
||||
// fall back to legacy string-escaped fields for sessions saved before this fix.
|
||||
let messages: String = json_get_raw(blob, "messages_raw")
|
||||
let messages: String = if str_eq(messages, "") { json_get(blob, "messages") } else { messages }
|
||||
let tools_json: String = json_get_raw(blob, "tools_raw")
|
||||
let tools_json: String = if str_eq(tools_json, "") { json_get(blob, "tools_json") } else { tools_json }
|
||||
let tools_log: String = json_get(blob, "tools_log")
|
||||
let saved_use_id: String = json_get(blob, "tool_use_id")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user