* fix: undo() peeks without popping, breaking repeated undo and leaking redo stack
undo() used self.undo_stack.back() (peek) instead of pop_back(), so
repeated undo always returned the same checkpoint while pushing to
the redo stack unboundedly.
Additionally, redo() did not save the current state to the undo stack,
breaking the undo/redo cycle.
Changes:
- undo(): change back() to pop_back(), return owned Checkpoint
- redo(): accept current_turn/current_messages params, save current
state to undo stack before popping from redo stack
- Update process_undo/process_redo callers in agent_loop.rs
- Add tests for repeated undo, undo/redo cycling, stack size invariant
* fix: standardize lock ordering and extract push_undo helper
Address review feedback:
- Standardize lock order (Session before UndoManager) in process_undo
and process_redo to match process_user_input and prevent deadlocks
- Extract push_undo() helper to deduplicate push-and-trim logic shared
by checkpoint() and redo()
* docs: add move-semantics notes and stack invariant to UndoManager
Address review feedback requesting documentation about the ownership
semantics of undo/redo parameters and the stack size invariant.
---------
Co-authored-by: Yi LIU <[email protected]>
Co-authored-by: firat.sertgoz <[email protected]>
* fix: check Content-Length before downloading HTTP response body
The HTTP tool previously downloaded the entire response body into memory
before checking the size limit, allowing a malicious server to cause OOM.
Now the Content-Length header is checked first to reject obviously
oversized responses, and the body is streamed with a hard size cap so
reading stops as soon as the limit is exceeded.
* fix: check chunk size before allocation and fix Content-Length parsing
Address review feedback:
- Check body.len() + chunk.len() before extend_from_slice to prevent
OOM from a single oversized chunk
- Use let-chain for Content-Length parsing instead of unwrap_or to
gracefully handle invalid headers
* docs: document MAX_RESPONSE_SIZE rationale and add tracing on rejection
Address review feedback: explain why 5 MB was chosen for the response
size limit and log a warning when Content-Length causes early rejection.
---------
Co-authored-by: Yi LIU <[email protected]>
The worker (both agent/worker.rs and worker/runtime.rs) was passing the
literal string "tool_call_id" to ChatMessage::tool_result instead of
the actual tool call ID from the LLM response. This breaks
OpenAI-compatible providers that match tool results to their
corresponding calls by ID.
- Add tool_call_id field to ToolSelection struct
- Propagate ToolCall.id through select_tools() into ToolSelection
- Replace all hardcoded "tool_call_id" usages with selection.tool_call_id
- Generate unique IDs for plan-based synthetic selections
- Add test verifying tool_call_id is preserved
Co-authored-by: Yi LIU <[email protected]>
Co-authored-by: Illia Polosukhin <[email protected]>