1a100fe96c
Splits `core/engine.rs` (4670 → 4314 lines) into a small folder module: - `engine/approval.rs` (~125 lines) — `ApprovalDecision`, `UserInputDecision`, `ApprovalResult`, plus the two handshake methods `Engine::await_tool_approval` and `Engine::await_user_input`. - `engine/dispatch.rs` (~300 lines) — tool-input parsing (`final_tool_input`, `parse_tool_input`, fenced/JSON segment helpers), `multi_tool_use.parallel` payload parser, dispatch policy predicates (`should_parallelize_tool_batch`, `should_force_update_plan_first`, `should_stop_after_plan_tool`, the read-only MCP tool helpers), and the `ToolExecutionPlan`/`ToolExecOutcome`/`ParallelToolResult*`/ `ToolExecGuard` types the batch driver passes around. The public engine surface (`EngineConfig`, `EngineHandle`, `spawn_engine`, `MockEngineHandle`, `mock_engine_handle`, `compact_tool_result_for_context`, `TOOL_CALL_*_MARKERS`, `FAKE_WRAPPER_NOTICE`) stays in `engine.rs` — every external user imports unchanged. Not split this round: the 1268-line `handle_deepseek_turn` method. Carving its inline parallel/sequential dispatch and approval handshake arms requires extracting two new methods from a borrow-heavy turn loop; flagged in the v0.6.0 audit doc as future work. Workspace tests: 1011/1011 still green. No clippy regressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>