Think sends cf_agent_chat_messages / cf_agent_use_chat_response to connections where shouldSendProtocolMessages() returns false · Issue #2637 · cloudflare/agents · GitHub
Skip to content

Think sends cf_agent_chat_messages / cf_agent_use_chat_response to connections where shouldSendProtocolMessages() returns false #2637

Open

Description

@TheDominator54

Versions: @cloudflare/think 0.20.2 and agents 0.28.1. Still reproduces on 0.21.0 and 0.29.0, and the inspected main at fc7f59f has the same code.

What happens

Agent.shouldSendProtocolMessages() is documented as: "When this returns false for a connection, that connection will not receive any protocol text frames — neither on connect nor via broadcasts." The base Agent honours it: no identity, state or MCP frames are sent. Think doesn't:

  1. Think's onConnect wrapper (_setupProtocolHandlers) sends _buildIdleConnectMessages() (the whole transcript) or _notifyStreamResuming() to every new connection. That wrapper is installed in onStart, so it runs before the base Agent's wrapper sets the per-connection protocol flag. _preStream.park() can also send cf_agent_stream_pending there.
  2. _broadcastChat() and _broadcast() call this.broadcast() with no protocol filter. The base Agent's _broadcastProtocol() excludes connections where isConnectionProtocolEnabled() is false.

Reproduction

export class FramesAgent extends Think<Env> {
  getModel() { return scripted(() => text("one two three four five")); }
  shouldSendProtocolMessages() { return false; }   // every connection opts out
  onMessage() {}                                    // the socket speaks the app's own protocol
  async turn() { return this.runTurn({ input: "hi" }); }
}

Open a WebSocket to /agents/frames-agent/x, then call turn() over RPC. The socket receives:

on connect:  cf_agent_chat_messages
during turn: cf_agent_use_chat_response ×12, cf_agent_chat_messages ×2

Expected: none of those frames. No cf_agent_identity, cf_agent_state or cf_agent_mcp_servers arrive, which is correct.

Workaround: override broadcast() to exclude the app's sockets, and replace the private _buildIdleConnectMessages and _notifyStreamResuming on the instance.

Proposed fix

  • In Think's onConnect wrapper, skip the transcript, resume and park frames when !this.shouldSendProtocolMessages(connection, ctx). Asking the hook directly is needed because the flag isn't set yet at that point.
  • In _broadcastChat() and _broadcast(), exclude connections where !this.isConnectionProtocolEnabled(c), as _broadcastProtocol() does. Alternatively, route both through _broadcastProtocol().

Patching those three places in the 0.21.0 build gives zero frames on the opted-out socket. In-browser clients using useAgentChat are unaffected, because they keep the default true.

Activity

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