GatewayRunner._handle_message() loses messages from the transcript on every conversation turn after the first.
Root cause
The raw transcript history includes session_meta entries (tool definitions, model info, etc.) that are written on the first turn. When _run_agent builds agent_history, it filters these out (lines 1978-1981) before passing the conversation to the agent. The agent returns messages built from this filtered list plus new messages from the current turn.
The bug is on line 949-950:
history_len = len(history) # includes session_meta
new_messages = agent_messages[history_len:] if len(agent_messages) > history_len else agent_messages
len(history) counts session_meta entries, but agent_messages was built from agent_history which has no session_meta. So the slice offset is too high by the number of session_meta entries, and the extraction skips that many valid new messages.
Example
Turn 2 of a conversation:
history = [session_meta, user_msg1, assistant_msg1] - length 3
agent_history = [user_msg1, assistant_msg1] - length 2 (session_meta filtered)
- Agent returns [user_msg1, assistant_msg1, user_msg2, assistant_msg2] - length 4
agent_messages[3:] = [assistant_msg2] - user_msg2 is lost
agent_messages[2:] = [user_msg2, assistant_msg2] - correct
Second bug (else branch)
The else branch else agent_messages treats ALL messages as new when the count comparison fails. With multiple session_meta entries, len(history) can equal len(agent_messages), causing the entire conversation to be duplicated into the transcript. The else branch should return [] to fall through to the simple user/assistant fallback.
Impact
From turn 2 onwards, every turn loses 1 message from the persistent transcript. Over a long conversation this accumulates - the transcript becomes increasingly incomplete, missing user messages and tool interactions.
GatewayRunner._handle_message()loses messages from the transcript on every conversation turn after the first.Root cause
The raw transcript
historyincludessession_metaentries (tool definitions, model info, etc.) that are written on the first turn. When_run_agentbuildsagent_history, it filters these out (lines 1978-1981) before passing the conversation to the agent. The agent returns messages built from this filtered list plus new messages from the current turn.The bug is on line 949-950:
len(history)counts session_meta entries, butagent_messageswas built fromagent_historywhich has no session_meta. So the slice offset is too high by the number of session_meta entries, and the extraction skips that many valid new messages.Example
Turn 2 of a conversation:
history= [session_meta, user_msg1, assistant_msg1] - length 3agent_history= [user_msg1, assistant_msg1] - length 2 (session_meta filtered)agent_messages[3:]= [assistant_msg2] - user_msg2 is lostagent_messages[2:]= [user_msg2, assistant_msg2] - correctSecond bug (else branch)
The else branch
else agent_messagestreats ALL messages as new when the count comparison fails. With multiple session_meta entries,len(history)can equallen(agent_messages), causing the entire conversation to be duplicated into the transcript. The else branch should return[]to fall through to the simple user/assistant fallback.Impact
From turn 2 onwards, every turn loses 1 message from the persistent transcript. Over a long conversation this accumulates - the transcript becomes increasingly incomplete, missing user messages and tool interactions.