Skip to content

Add turn-complete signal for session_update notifications #554

Closed

Description

@simonrosenberg

Problem

After a prompt() call returns, there is no protocol-level signal indicating that all session_update notifications for that turn have been delivered. Clients that accumulate state from these notifications (e.g. streamed text chunks, usage updates) must resort to heuristic delays (asyncio.sleep) to wait for in-flight notifications to arrive before reading the accumulated result.

This is inherently racy — too short a delay misses late notifications, too long a delay adds unnecessary latency.

Current workaround

In OpenHands, we yield to the event loop and then sleep for a configurable duration (default 0.1s) after prompt() returns:

response = await conn.prompt(messages, session_id)
await asyncio.sleep(0)  # yield to let queued handlers run
await asyncio.sleep(0.1)  # wait for in-flight notifications

Proposed solution

Add a protocol-level mechanism that signals when all notifications for a given turn have been delivered. For example:

  • A dedicated turn_complete notification sent by the server after all session_update notifications for a prompt response
  • Or a field on the PromptResponse indicating whether all associated notifications have been flushed

This would let clients reliably synchronize without arbitrary delays.

Activity

  1. benbrandt commented on Feb 23, 2026

    @benbrandt
    Member

    Hi 👋🏻 yes I think I would like to move the entire prompt lifecycle to be notification based entirely, which I think will help with this.

  2. stablegenius49 commented on Mar 4, 2026

    @stablegenius49

    I opened a draft RFD PR for this: https://github.com/agentclientprotocol/agent-client-protocol/pull/644\n\nIt proposes a barrier (with capability gating) as an RFD-first step before unstable schema/runtime changes.

  3. stablegenius49 commented on Mar 4, 2026

    @stablegenius49

    Follow-up clarification: the proposal is a TURN_COMPLETE session update barrier (plus sessionCapabilities.turnComplete capability advertisement) as an RFD-first step before unstable schema/runtime changes.

  4. xenodium commented on Mar 6, 2026

    @xenodium

    Add a protocol-level mechanism that signals when all notifications for a given turn have been delivered. For example:
    A dedicated turn_complete notification sent by the server after all session_update notifications for a prompt response

    From the diagram in https://agentclientprotocol.com/protocol/prompt-turn and this text:

    The Agent MAY send session/update notifications with content or tool call updates after receiving the session/cancel notification, but it MUST ensure that it does so before responding to the session/prompt request.

    @simonrosenberg Doesn't this imply the session/prompt response is covering the use-cause suggested by the dedicated turn_complete (at the very least for session/prompt requests)?

    Having said that, I'm interested in additional markers/signals for a different reason. I'm looking to support broadcasted interactions (agent -> client). This would enable supporting flows like Claude's remote control in agent-shell.

    Hi 👋🏻 yes I think I would like to move the entire prompt lifecycle to be notification based entirely, which I think will help with this.

    @benbrandt is there a particular approach that's getting traction that would enable broadcasting session prompts to clients? I'd be happy to give it a try and see how well it works for a remote-control kind of flow.

  5. denofevil commented on Mar 6, 2026

    @denofevil

    @xenodium I believe you might be interested in #533 (already mentioned it here), it is basically covering agent to multiple clients scenario, your usecases might be important

  6. YOMXXX commented on May 18, 2026

    @YOMXXX

    +1 to a protocol-level signal — we hit exactly this race in slopus/happy and ended up shipping a client-side workaround in slopus/happy#1287: a two-signal state machine on the client that tracks responseReceived and updatesSettled independently, plus a POST_RESPONSE_GRACE_MS (500ms) fallback timer that closes the turn when the RPC response arrives but no further updates ever show up (the "empty turn" / response-first race).

    In practice we did see agents flush the prompt response before the trailing session/update (Codex through the bridge was the reproducible one for us), so the assumption "updates arrive last" really does fail in the wild and the heuristic-sleep approach has the failure modes the OP describes. Our fake-timer tests pin down four distinct orderings — spec ordering / response-first / empty turn / tool-complete-before-response — and a re-entrant turn case for completeness.

    Between the two options in this issue: a first-class turn_complete notification (option 1) would let us delete the entire state machine and the grace timer. A flag on PromptResponse (option 2) would also work but is slightly worse because it conflates "the server believes the turn is done" with the actual flush of in-flight session/update notifications — clients would still need a buffer/queue to handle the response-arrives-first case unless the transport guarantees strict ordering. So we'd lean toward option 1 (dedicated notification, sent as the last session/update of the turn).

    Happy to help draft the spec PR + a reference SDK implementation if useful.

  7. ivebenfreed commented on Aug 1, 2026

    @ivebenfreed

    One interaction worth folding into the design here: if a backgrounded tool call can settle after prompt() returns, its tool_call_update would arrive after the turn — which would make an unconditional turn_complete signal inaccurate rather than merely early.

    Either backgrounded tool calls need to be excluded from the completeness guarantee, or the signal means "all updates for the foreground turn" rather than "all updates for this session's work". Raised separately as #1847, since it may be a question about ToolCallStatus semantics rather than about this signal.

  8. benbrandt commented on Oct 12, 2026

    @benbrandt
    Member

    This is addressed by the idle state update in v2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions