Repository navigation
Bound agent session Flow state with chunked messages and retention - #1221
Merged
Merged
Conversation
zzheng-aiplay
force-pushed
the
zzheng/agents-chunked-messages
branch
from
November 3, 2026 05:35
fa70b7e to
95cd76f
Compare
Implement item 4 of the owner review of the M2 decisions (#1178 (comment)) in the Python, TypeScript, and Go agents cores, with identical persisted names and values: - AgentMessages is now the window: the newest messages, one instance per message, so storing a message never rewrites a larger value. Past message_window_size (64), the oldest messages move into immutable instances of the new AgentMessageChunks AttributeMap, keyed by their first sequence, in the same commit. A chunk holds at most message_chunk_size (32) messages and message_chunk_max_bytes (1 MiB) of encoded JSON, so it stays under values.maxValueBytes. A larger message skips the window and goes straight into its own chunk when it is stored, so a message too large for the Server fails its turn at once instead of wedging a later rollover. One commit moves at most 4 chunks and 8 MiB. The new AgentMessageIndex Attribute lists each kept chunk's first and last sequence, the window start, and the oldest kept message, and changes only when chunks are written or deleted. - RunModel no longer loads the whole map. Movements into RunModel and ApplyTurnControl list only the chunks after the summary, and CompactContext only the chunks it summarizes. Without compaction the model input is every message, so those Steps load every chunk. - With compaction, retention deletes the oldest chunk while its last sequence is at or before ContextSummary.last_sequence and at least retained_message_limit (1,000) messages are stored after it, so the newest 1,000 messages always stay. Deleted messages leave list_agent_messages and get_agent_snapshot; AgentMessagesPage gains deleted_before_sequence. A runner without compaction on a session with deleted messages logs a warning. Go has no compaction, so it never deletes. - Turn message counts come from the new TurnReplySequence Attribute, tagged with the turn ID, so a turn compacted or retired mid-turn still counts every reply and no Step rewrites the index each turn. - A session that 0.1.0 stored is read as one large window and migrates into chunks through the same bounded rollover, a few chunks a Step. Tests run the released 0.1.0 Workers. Upgrades stop every 0.1.0 Worker before any 0.2.0 Worker starts. The conformance suite gains the AgentMessageChunk, AgentMessageIndex, TurnReplySequence, and AgentMessagesPage shapes, the AgentMessageChunks key format, the new RPC loads, and the default sizes and per-commit caps. The A7-5 guard and the nightly 2,000-turn test measure the stored messages and the whole live state, which stay flat as the session grows. The contract doc, the plan's A7 "As built" note, the core READMEs, the deployment guide in both locales, and the CHANGELOG describe the layout and the upgrade rule.
zzheng-aiplay
force-pushed
the
zzheng/agents-chunked-messages
branch
from
November 3, 2026 06:30
95cd76f to
6891866
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Store agent session messages as a Sequentially Chunked AttributeMap with retention, in the Python, TypeScript, and Go agents cores, so a session's Flow state stays bounded however long it runs.
AgentMessagesnow holds only the newest messages, one instance per message. Pastmessage_window_size(64), the oldest move into immutable instances of the newAgentMessageChunksAttributeMap, keyed by their first sequence, in the same commit.message_chunk_size(32) messages andmessage_chunk_max_bytes(1 MiB, at most 8 MiB) of encoded JSON, so it stays well undervalues.maxValueBytes. One commit moves at most 4 chunks and 8 MiB.AgentMessageIndexAttribute lists every kept chunk's first and last sequence, the window start, and the oldest kept message. It is written only when a chunk is written or deleted.TurnReplySequenceAttribute,{turn_id, sequence}, records where the turn's replies start, soturn_completed,turn_steered, andturn_cancellednever read a turn's older messages. A value that names another turn falls back to a scan.RunModelno longer loads the whole map. Movements intoRunModelandApplyTurnControl(from every path, including Make cancel_turn abort model calls and abandon tool tasks #1223'sFinishTurnCancellation) list only the chunks afterContextSummary.last_sequence, andCompactContextonly the chunks it summarizes. Without compaction the model input is every message, so those Steps load every chunk.ContextSummary.last_sequenceand at leastretained_message_limit(1,000;None/nullkeeps all) messages are stored after it, so the newest 1,000 messages and everything after the summary always stay. Deleted messages leavelist_agent_messagesandget_agent_snapshot; the additiveAgentMessagesPage.deleted_before_sequencemarks the deleted range. A runner without compaction on a session with deleted messages logs a warning.turn_completedcounts; taggingTurnReplySequencewith the turn ID removed that.) The CHANGELOG, the guide in both locales, and every core README state it.WithMessageWindowSize,WithMessageChunkSize,WithMessageChunkMaxBytes). It has no compaction, so it never deletes.The conformance suite gains the new persistence names, the
AgentMessageChunk,AgentMessageIndex, andAgentMessagesPageshapes, the chunk key format, the new RPC loads, and the default sizes and per-commit caps. The contract doc has a Message storage section, and the plan's A7 has an "As built" note.Why
Item 4 of the owner review of the M2 decisions: #1178 (comment).
RunModelloaded the wholeAgentMessagesmap, so both the per-call load and Flow state grew with the session, and compaction only shortened the model input. This is a persisted layout, so it has to be settled before 0.2.0 publishes.Design decisions and deviations from the review
currentvalue. The pattern rewrites onecurrentinstance on every append. Superseded values count towardlimits.maxFlowStateBytesuntil the history window prunes them, so a rewritten window would add about half a window of superseded bytes per stored message. Keeping one instance per message makes each store one blind write and the window still bounded. Every message-writing Step loads the whole window, which is at mostmessage_window_sizemessages.RunModelorCompactContext, need the deleted chunks loaded there, and turn a projection failure into a model-call failure. The guide documents the guarantee (the newestretained_message_limitmessages and every message after the summary always stay), how to copy messages out withlist_agent_messagesafter eachturn_completed, anddeleted_before_sequencelets a reader detect a gap.get_agent_snapshot/list_agent_messageshave many callers. Retention bounds that load. The chunk keys are computable fromAgentMessageIndex, so invocation-time loads can be added later without a layout change.RecordTurnFailureis a failure route without movement options, so for a runner that builds unfinished-turn messages it loads every kept chunk.limits.maxFlowStateBytes. The guard therefore sums the encoded size of every live Attribute and map instance value directly.Testing
Integration tests against the private
dexcli devengine (cli-v1.30.0), in each core:RunModelwhose commit rolls over; afterwards every message is stored once, in order, the chunks match the index exactly, everyturn_completedcounts 1, and the last model call saw every user message.message_chunk_max_bytes, chunks close beforemessage_chunk_size, every chunk is under the cap, an oversized message goes straight into its own chunk (history shows noAgentMessageswrite for it), and reads, paging, and the model input cross them.values.maxValueBytes8192, a message sized a few bytes under it (which used to fit the window and then wedge its rollover) fails the turn that first stores it, and the next turn completes.dexcli flow history, every Step that writesAgentMessageIndexalso writes or deletes anAgentMessageChunksinstance, most turns write no index, andTurnReplySequenceis written once per turn.ContextSummary.last_sequenceare deleted, at least the limit always stays, the next chunk is not deletable, and pages carrydeleted_before_sequence. Go asserts it never deletes.SetAttributes(no released Go Worker exists).Ran locally after rebasing onto #1222 and #1223: every core
ci.sh(Python 408 passed, TypeScript 243 passed, Go), all nine adapterci.sh,agents/script/smoke-test-built-packages.sh all,generate.py --check,make copyright-check, andcd docs && npm run check. Agents CI runs the same;Agents Long Tests Nightlyruns the 2,000-turn tests.