feat(audit): emit tool.spillover events when output is spilled (#500 polish)
The existing `tool.result` audit event records that a tool
finished but says nothing about spillover — operators tailing
`~/.deepseek/audit.log` couldn't see when 200 KiB of stdout
landed under `~/.deepseek/tool_outputs/`.
Adds a discrete `tool.spillover` event keyed off
`apply_spillover`'s return value, fired in both the sequential
and parallel tool paths so the log entry exists regardless of
how the tool was scheduled. Each event carries:
{"event": "tool.spillover", "tool_id": "...",
"tool_name": "exec_shell", "path": "/.../call-abc.txt"}
This is a pure observability addition. The model still receives
the same truncated head + footer; the UI still renders the
inline `full output: <path>` annotation; the spillover writer
contract is unchanged. No new tests — `apply_spillover` already
has unit-level coverage and the engine paths are exercised by
integration runs.
This commit is contained in:
@@ -188,6 +188,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
ones. New `skills_directories()` and
|
||||
`discover_in_workspace()` helpers in
|
||||
`crates/tui/src/skills/mod.rs`.
|
||||
- **`tool.spillover` audit event** (#500 polish) — emit a
|
||||
discrete audit-log entry whenever `apply_spillover` writes a
|
||||
spillover file, so operators tailing
|
||||
`~/.deepseek/audit.log` can correlate large-output episodes
|
||||
with disk-usage growth in `~/.deepseek/tool_outputs/`. Fires
|
||||
in both the sequential and parallel tool paths.
|
||||
- **RLM tool family** (#512) — `rlm` tool cards map to
|
||||
`ToolFamily::Rlm` and render `rlm`, not `swarm`. Stale "swarm"
|
||||
wording cleaned out of docs / comments / tests.
|
||||
|
||||
@@ -1092,9 +1092,19 @@ impl Engine {
|
||||
.await;
|
||||
|
||||
// #500: spill outsized output before fanout (mirror
|
||||
// of the sequential path below).
|
||||
if let Ok(tool_result) = result.as_mut() {
|
||||
crate::tools::truncate::apply_spillover(tool_result, &plan.id);
|
||||
// of the sequential path below). Emit a
|
||||
// `tool.spillover` audit event so operators can
|
||||
// correlate large-output episodes with disk usage.
|
||||
if let Ok(tool_result) = result.as_mut()
|
||||
&& let Some(path) =
|
||||
crate::tools::truncate::apply_spillover(tool_result, &plan.id)
|
||||
{
|
||||
emit_tool_audit(json!({
|
||||
"event": "tool.spillover",
|
||||
"tool_id": plan.id.clone(),
|
||||
"tool_name": plan.name.clone(),
|
||||
"path": path.display().to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
let _ = tx_event
|
||||
@@ -1374,8 +1384,19 @@ impl Engine {
|
||||
// result fans out to the model context and the UI cell.
|
||||
// Both consumers see the same truncated content + the
|
||||
// `spillover_path` metadata pointing at the full file.
|
||||
if let Ok(tool_result) = result.as_mut() {
|
||||
crate::tools::truncate::apply_spillover(tool_result, &tool_id);
|
||||
// Emit a discrete `tool.spillover` audit event so
|
||||
// operators can correlate large-output episodes with
|
||||
// disk-usage growth in `~/.deepseek/tool_outputs/`.
|
||||
if let Ok(tool_result) = result.as_mut()
|
||||
&& let Some(path) =
|
||||
crate::tools::truncate::apply_spillover(tool_result, &tool_id)
|
||||
{
|
||||
emit_tool_audit(json!({
|
||||
"event": "tool.spillover",
|
||||
"tool_id": tool_id.clone(),
|
||||
"tool_name": tool_name.clone(),
|
||||
"path": path.display().to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
let _ = self
|
||||
|
||||
Reference in New Issue
Block a user