host: preserve journals, full traps and embedded agent output

Write new journal entries under logs/core and merge read-only legacy
history without moving SQLite/WAL files. Atomically spool failed batches,
retaining unspoolable entries for event-driven retries. Journal full trap
diagnostics before UTF-8-safe status display. Port session overflow archives
and followable line/byte excerpts to the still-shipped embedded agent.
Document retention/recovery and cover the contracts with regressions.
Include one rustfmt-only normalization in the existing SDK WASI test.

Card: SAU-143
This commit is contained in:
SAU-143 implementer
2026-10-26 13:40:15 +00:00
parent 168795b408
commit 037377fd27
13 changed files with 818 additions and 83 deletions
+28 -9
View File
@@ -94,19 +94,38 @@ impl Agent {
return Ok(final_text);
}
for (id, name, arguments) in calls {
let output = match serde_json::from_str(&arguments) {
Ok(args) => tools::run(&name, &args)
let args = serde_json::from_str::<serde_json::Value>(&arguments);
let output = match &args {
Ok(args) => tools::run(&name, args)
.await
.unwrap_or_else(|error| json!({"error":error})),
Err(error) => json!({"error":format!("arguments are not valid JSON: {error}")}),
};
append(
&mut entries,
Entry::ToolResult {
id,
output: tools::limit_output(output.to_string()),
},
)?;
let output = if name == "read_file"
&& args
.as_ref()
.ok()
.and_then(|a| a["path"].as_str())
.is_some_and(|p| p.starts_with("/logs/"))
{
output["content"]
.as_str()
.map(str::to_owned)
.unwrap_or_else(|| output.to_string())
} else {
output.to_string()
};
let tail = args
.as_ref()
.ok()
.and_then(|a| a["method"].as_str())
.is_some_and(|m| {
[".exec", ".log", ".build", ".test"]
.iter()
.any(|suffix| m.ends_with(suffix))
});
let output = tools::overflow::guard(output, "default", &id, tail);
append(&mut entries, Entry::ToolResult { id, output })?;
}
}
}