* fix: persist turns after approval and add agent-level tests
Port relevant changes from PR #112 that were not carried over to #237:
- Add persist_turn calls in process_approval for the response, error,
and auth-required paths. Previously, turns completed after tool
approval were never persisted to DB — if the process crashed after
approval the entire turn (user message + assistant response) was lost.
- Add agent-level unit tests: StaticLlmProvider mock, make_test_agent
helper, tests for auto-approval logic, destructive shell command
detection, and PendingApproval backward-compatible deserialization
(without deferred_tool_calls field).
- Remove unused _thread_state binding in process_approval.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: address 14 audit findings in src/agent/
Audit of the agent module found 2 High, 7 Medium, 3 Low, and 2 Nit
severity issues. This commit fixes all of them:
High:
- Remove 4 `.expect()` calls in session.rs (entry API, match, direct
indexing, if-let) to eliminate panic paths in production
- Add typed RoutineError enum replacing Result<_, String> across
routine.rs, routine_engine.rs, and callers in history/store.rs and
db/libsql/mod.rs
Medium:
- Sanitize routine names in path construction to prevent directory
traversal (routine_engine.rs)
- Log warnings for 5 silently-swallowed errors in scheduler.rs,
compaction.rs, and worker.rs
- Extract shared handle_auth_intercept helper to deduplicate auth
interception in thread_ops.rs
- Add session count warning threshold in session_manager.rs
- Make FullJob stub degradation visible via warn-level log and
prepended warning in output
Low:
- Restrict dead code visibility with #[cfg(test)] on 19 unused items
in submission.rs, task.rs, and undo.rs
- Narrow pub to pub(crate) on self_repair.rs builder methods
- Remove TaskStatus from mod.rs re-exports (test-only type)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: address PR review comments
- Reorder persist_turn before persist_response_chain so the
conversation row exists before the metadata UPDATE runs
- Add persist_response_chain call to handle_auth_intercept so
auth-required paths preserve the response chain
- Harden sanitize_routine_name to use allowlist (alphanumeric,
dash, underscore) instead of denylist replacements
- Fix stale active_thread ID in get_or_create_thread: fall back
to create_thread() when the stored ID is missing from the map
- Persist turn on approval rejection so user messages survive
crashes after a tool is rejected
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
* 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]>