fix(acp): prevent memory blowup from streaming terminal tool output
Large raw_output snapshots from tool_call_update notifications caused O(N²) traffic through the event pipeline, multi-GB transient allocations and WKWebView crashes. The fix turns cumulative snapshots into bounded incremental deltas and removes redundant payload copies. - Add ToolCallOutputCache keyed by tool_call_id with a 8KB tail fingerprint and total length. Detects cumulative extensions by matching the cached tail at the expected offset in the incoming snapshot, so it works even when the full output grows into the MB range. Emits suffix deltas with raw_output_append=true; falls back to a truncated replacement when content diverges. - Cap any single emitted raw_output chunk at 64KB (MAX_SINGLE_EMIT_BYTES) with a UTF-8 char-boundary-safe tail and ANSI-sequence-safe trimming. Apply the same cap to emit_terminal_output_update. - Bound the cache at 256 entries with FIFO eviction, and clear entries when the tool call reaches completed / failed / cancelled / error. - Seed the cache via a dedicated seed() method on SessionUpdate::ToolCall so the initial event never emits an accidental append. - Share emit payloads as Arc<serde_json::Value> across broadcast receivers and skip the Tauri-side clone: serialize once and hand the same Arc to both the webview emit and the WebSocket broadcaster. - Add 14 unit tests covering boundary cases: identity, prefix extension past the cached tail, divergence, oversized deltas, multibyte UTF-8 truncation, final-status cleanup, FIFO eviction, seed semantics, and ANSI-safe trimming.
This commit is contained in:
@@ -108,7 +108,7 @@ pub fn spawn_event_subscriber(
|
||||
last_push.retain(|_, t| t.elapsed() < Duration::from_secs(DEBOUNCE_SECS * 2));
|
||||
|
||||
if let Some((event_type, msg)) =
|
||||
parse_event(&event.channel, &event.payload, config.lang)
|
||||
parse_event(&event.channel, event.payload.as_ref(), config.lang)
|
||||
{
|
||||
// Global event filter check
|
||||
if let Some(filter) = &config.global_filter {
|
||||
|
||||
@@ -48,7 +48,7 @@ pub fn spawn_session_event_subscriber(
|
||||
|
||||
if event.channel == "acp://event" {
|
||||
handle_acp_event_payload(
|
||||
&event.payload,
|
||||
event.payload.as_ref(),
|
||||
&bridge,
|
||||
&manager,
|
||||
&conn_mgr,
|
||||
|
||||
Reference in New Issue
Block a user