feat(agent): echo initiating message_id on relay_message - #3434
Conversation
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
Pass the id of the user_message command that initiated a turn from the handler closure to relayAgentResponse and include it as an optional message_id field in the relay_message POST body. Each turn's relay carries its own initiating message's id; the field is omitted when no id is known (e.g. the boot prompt), so older backends are unaffected.
4c745d2 to
f11ab9f
Compare
|
| this.logger.debug("Failed to relay follow-up response", err), | ||
| // delivery path — the HTTP caller only handles reactions. Echo the | ||
| // initiating message's id so the backend can attribute the answer. | ||
| this.relayAgentResponse(this.session.payload, messageId).catch( |
There was a problem hiding this comment.
Captured ID Gets Later Response
When another command starts before this unawaited relay finishes flushing, that turn resets and repopulates the session-wide response buffer. The first relay can then read the later turn's text while retaining the first turn's messageId, causing the backend to attribute the wrong answer to a participant. Capture the completed response with the ID before another turn can mutate the buffer, or serialize relay completion with turn processing.
| this.logger.debug("Failed to relay follow-up response", err), | ||
| // delivery path — the HTTP caller only handles reactions. Echo the | ||
| // initiating message's id so the backend can attribute the answer. | ||
| this.relayAgentResponse(this.session.payload, messageId).catch( |
There was a problem hiding this comment.
Message Ownership Is Unchecked
A caller with a valid token for a multiplayer run can submit another participant's message ID because the request body value is forwarded without checking its owner. If the relay endpoint uses that ID directly for attribution, the answer is attached to the wrong sender; validate that the ID belongs to the authenticated user and current task/run before forwarding it.
Rule Used: When implementing new features, ensure that owners... (source)
Learned From
PostHog/posthog#31236
Problem
When a turn ends, the agent-server relays the answer to
relay_messagewith onlytext/text_parts— no link to the user message it answers, so the backend has to guess who to @-mention and mis-tags replies in multiplayer threads.Changes
user_message'smessageId(the id already used for duplicate-delivery suppression) as an optionalmessage_idon therelay_messagebody. Captured in the handler closure, so overlapping turns each relay their own id.Paired with PostHog/posthog#70762
The backend consumes
message_idto attribute each Slack reply to the right sender (that PR, part of the follow-up queue work #70806 → #70762). Neither side hard-depends on the other; verified end-to-end locally with both together.How did you test this?
/commandendpoint (overlapping turns, the no-id case, no leak from a failed turn into the next),relayAgentResponsepass-through, and the request body shape.pnpm --filter @posthog/agent test— 1295 tests pass; typecheck + biome clean.Automatic notifications