For Gemini's implicit cache (and similar provider caches) to actually
hit step over step, the rendered transcript for steps 1..N-1 must be
byte-identical at step N and at step N+1. The agent already appends
HistoryItems immutably in practice, but nothing in the type prevented
a future caller from mutating one in place — which would silently kill
the cache from that byte onward.
Mark HistoryItem frozen so any mutation now fails loud at runtime
rather than slowly burning input tokens. Add a regression test set
that asserts the cache property directly:
- render(items[:N]) must be a strict byte-prefix of render(items[:N+1])
- to_string() is deterministic for identical inputs
- the prefix property holds across mixed entry shapes (normal steps,
errors, system messages, follow-up tasks)
- conditional field inclusion doesn't collapse to ambiguous output
Known limitation, not addressed here: max_history_items compaction in
MessageManager.agent_history_description rewrites earlier bytes once
the cap is exceeded (the omitted-count message changes). That's a
larger redesign and deserves its own PR.
- Updated page info text format to enhance readability and structure.
- Changed date format in agent state description to display only the current date.
- Simplified action results formatting in message manager for better clarity.
- Adjusted history item string representation to streamline output and remove unnecessary prefixes.
- changed the system messages to have <sys> tag instead of <s> to avoid confusion with HTML
- Got rid of MessageMetadata, SupportedMessageTypes and implemented cleaner MessageManagerState
- Implemented a HistoryItem class to cleanly reconstruct agent history description
- Update `_add_message_with_tokens` and `add_message` methods to support `None` as default position
- Modify last step warning message in agent service
- Add logging for last step completion
- Improve type hints for message position parameters