fix(coding-agent): stamp agent messages with the sender's compose time - #1189
Open
Hotragn wants to merge 1 commit into
Open
fix(coding-agent): stamp agent messages with the sender's compose time#1189Hotragn wants to merge 1 commit into
Hotragn wants to merge 1 commit into
Conversation
Agent mail always uses steering delivery, so a message sent to a busy target is queued and waits for that target's turn to reach a boundary. That wait is bounded only by the target's own progress: one measured orchestrator session had child handback messages arrive 39-96 minutes after they were written. createAgentSessionMessagePrompt rendered sender, relationship, route, and message id, and no time of any kind. A time was already known to the system - the sender's receipt carries queuedAt/deliveredAt and the transcript message carries a timestamp - but neither reached the prompt text, so the recipient could not tell a message composed seconds ago from one that had waited an hour, and could not judge whether a phrase like "the latest result" still meant what the sender meant. Stamp composedAt on the payload where it is minted and render it as a Composed: line. This is the RFC 5322 3.6.1 Date: origination header applied to agent mail: the transport records when the message was written and the reader decides how old is too old. It also matches the <heartbeat> block that carries delivery context on scheduled prompts. parseAgentSessionMessagePromptId walks the header by fixed offsets and stops at the message id line, so the new line is appended strictly after it and both the stamped and unstamped forms still parse. A payload without composedAt renders byte-identical text to before. fixes PrimeIntellect-ai#823
Hotragn
force-pushed
the
fix/823-agent-message-compose-time
branch
from
August 11, 2026 02:23
1a7e512 to
e26d5ff
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.
Fixes #823 (part B in full; part A's stated minimum is discussed below and deliberately left as a maintainer decision).
The gap
Agent mail always uses steering delivery, so a message sent to a busy target is queued and waits until that target's turn reaches a boundary. The wait is bounded only by the target's own progress — the issue reports measured waits of 39–96 minutes for child handback messages sitting behind an orchestrator's long sleep cells.
createAgentSessionMessagePromptrendered sender, relationship, route, and message id, and no time of any kind:A time was already known to the system — the sender's receipt carries
queuedAt/deliveredAt, andcreateAgentSessionMessagestamps a transcript-leveltimestamp— but neither value is ever rendered intocontent, so it never reaches the receiving model. The recipient could not distinguish a message composed seconds ago from one that had waited an hour, and so could not judge whether "the latest result" still referred to what the sender meant, or whether a later message had superseded it.Approach, and the prior art it follows
This is the RFC 5322 §3.6.1
Date:origination header applied to agent mail. That field is mandatory in Internet messages precisely because store-and-forward delivery decouples send time from read time: the transport records when the message was written, and the reader decides how old is too old. Nothing here is invented — the queued-agent-mail path has exactly the store-and-forward shape the header exists for.It also matches the in-repo precedent for the other injected-prompt surface: scheduled heartbeat prompts carry their delivery context in the prompt body rather than in details the model never sees.
composedAtis stamped once, where the payload is minted insendAgentSessionMessage, and rendered as aComposed:line. It is also surfaced onAgentSessionMessageDetailsso transcript consumers and UI have it without re-parsing text.Placement is load-bearing
parseAgentSessionMessagePromptIdwalks the header by fixed offsets — optional[from …], thenAgent-to-agent message received.,Source:, optionalFrom:,To:,Message id:— and stops at the id line. The new line is appended strictly after the line the parser stops on, so:From:/no-relationship shape;composedAtrenders text byte-identical to before, so nothing changes for callers that do not stamp.Both properties are asserted, including an exact full-string equality check on the unstamped form.
What I did not do, and why
Part A of the issue — the queue draining one message per assistant turn — is not in this PR. The issue's own "Expected" allows either coalescing or "at minimum … told that more are waiting", and I could not implement either half to the standard the rest of this change meets:
steeringModesetting that deliberately covers both human keyboard steering (where one-at-a-time is the right ergonomic default) and machine-generated agent mail. Splitting them means adding a per-source override tosettings.json. That is a product decision about the settings surface, not a bug fix, and it belongs to whoever owns that surface.unfinishedActionCountcounts all unfinished actions, not agent mail, so using it would print a number that is wrong in the common case. Doing it properly needs a narrow queued-agent-message accessor onAgentSession, and I could not exercise the daemon queue end-to-end on this machine (Unix-socket tests do not run on Windows), so I would be shipping an unverified count. A misleading number is worse than none.Happy to follow up on either with direction on the settings shape.
Tests
packages/coding-agent/test/suite/regressions/823-agent-message-compose-time.test.ts(5 cases): the stamped prompt renders the compose time and still ends with the message body; the fixed-offset parser resolves the id for both stamped and unstamped prompts and theComposed:line is positioned afterMessage id:; the id parses for the bare no-sender/no-relationship shape; an unstamped payload renders byte-identical text to before; and the compose time reaches transcript details while an unstamped message has nocomposedAtkey.Verified:
npm run checkclean. Every test file in the repo that referencescreateAgentSessionMessagePrompt,Agent-to-agent message received, orMessage id:was run — 436 passed. The 5 failures are all indaemon-mode.test.ts(unix-socket bind and symlink cases) and reproduce identically on a cleanmainworktree on this Windows machine.Note
Stamp agent-to-agent messages with the sender's compose time
composedAtfield (ISO 8601) toAgentSessionMessagePayloadandAgentSessionMessageDetailsin agent-messages.ts.composedAtis present,createAgentSessionMessagePromptappends aComposed:header line after theMessage id:line so recipients can judge queue wait time.composedAt: new Date().toISOString()on every outgoing message in daemon-mode.ts.Macroscope summarized e26d5ff.