9841a6c651
Fixes 13 issues found in PR #20774 review: 1. Wiring: engine selection moved from run_agent.py to agent/agent_init.py (where init_agent lives on current main). Transform hook moved from run_agent.py to agent/conversation_loop.py (where run_conversation lives). 2. Prompt caching: replace copy.deepcopy with copy-on-write (shallow list copy + clone only messages that are mutated). Use last_prompt_tokens from update_from_response instead of re-estimating tokens every call. System extension injection is idempotent (one-time cache break). 3. Signature mismatch: _message_signature renamed to _content_signature and now excludes tool_calls/tool_call_id from the hash. This prevents mismatches when _canonicalize_api_tool_calls re-serializes argument JSON with sort_keys=True on the API copy. 4. update_model: accepts api_mode parameter (required by agent_init.py). 5. Reconciled with select_context: transform_api_messages is a separate hook that runs AFTER select_context and sanitization, before prompt-cache marker placement. Both hooks coexist with clear ordering. 6. Dedup/purge: kept as DCP-specific strategies (different semantics from ContextCompressor._prune_old_tool_results — DCP deduplicates by tool+args signature, not by content hash). 7. Removed copy.deepcopy: replaced with shallow list copy + copy-on-write via _clone_if_needed. Only messages that are actually mutated get cloned. 8. Removed redundant _ensure_refs call: _match_api_messages_to_refs no longer calls _ensure_refs (the caller already called it). 9. _message_key still uses index (needed for positional ref assignment), but _content_signature is cached per id(msg) to avoid re-hashing. 10. _inject_nudge: only injects into user messages, never falls back to non-user messages (prevents role semantics violations). 11. Memory: _evict_inactive_blocks bounds blocks_by_id to _MAX_INACTIVE_BLOCKS (50) deactivated blocks. 12. Merged _range_tool_schema and _message_tool_schema into a single _compress_tool_schema. Merged _handle_range_compress and _handle_message_compress into _handle_compress. 13. Dropped DCP_CONTEXT_ENGINE_PR_SPEC.md (temporary file, not for tree). Config defaults kept minimal in hermes_cli/config_defaults.py (only the keys the engine actually reads, not the full DCP-compatible surface). Closes #20717