Skip to content

More clear AgentMessageResult handling to fix stuck AgentParticipants (#1011)#1057

Open
rasmi wants to merge 1 commit intoPAIR-code:mainfrom
rasmi:agent-message-result
Open

More clear AgentMessageResult handling to fix stuck AgentParticipants (#1011)#1057
rasmi wants to merge 1 commit intoPAIR-code:mainfrom
rasmi:agent-message-result

Conversation

@rasmi
Copy link
Copy Markdown
Collaborator

@rasmi rasmi commented Mar 12, 2026

Fixes #1011

Agent participants get stuck indefinitely in private chat stages when all mediators stop responding. The trigger (onPrivateChatMessageCreated) is event-driven -- when no mediator writes a new message, no trigger fires, and the agent has no opportunity to advance.

A secondary issue: in private chats, shouldRespond: false unconditionally forced readyToEndChat: true on agent participants, conflating a per-turn skip with a conversation-ending signal.

Changes

functions/src/chat/chat.agent.ts

  • Add AgentMessageResult enum (SENT, DECLINED, ERROR) replacing boolean returns from createAgentChatMessageFromPrompt
  • Decouple shouldRespond from readyToEndChat — respect the model's actual readyToEndChat value instead of forcing it
  • Remove dead-code empty if (!shouldRespond) block from Enable chatting with models that produce images #846

functions/src/triggers/chat.triggers.ts

  • Collect mediator results and check if all mediators are done
  • Advance agent participant when conversation is dead (all mediators declined)
  • Guards: message type (prevent premature advancement on mediator self-decline), currentStageId (prevent double-advancement), participant.agentConfig (no effect on human participants)
  • Skip "No mediators found" error for agent participants

What this does NOT change

  • Human participant behavior
  • Group chat stages (return type change applies to shared code but group chat trigger discards the return value -- no change to existing group chat behavior)
  • Mediator behavior (return type change only)
  • Frontend spinner handling

@cjqian cjqian requested a review from mkbehr March 20, 2026 15:13
Copy link
Copy Markdown
Collaborator

@mkbehr mkbehr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

/** Result of an agent's attempt to send a chat message. */
export enum AgentMessageResult {
SENT = 'sent',
DECLINED = 'declined',
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing DECLINED means that the mediator decided not to respond? It took me a while to figure that out, so let's make that more clear. The comment as-is implies that the agent attempted to send a message, but for DECLINED it didn't.


const allMediatorsDone =
mediators.length === 0 ||
results.every((r) => r === AgentMessageResult.DECLINED);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we're determining whether a mediator is done based entirely on whether it declined to respond on its last message, right? Does that mean we're not checking a mediator's readyToEndChat anywhere? Should we still be asking them for that field after this change?

).not.toHaveBeenCalled();
});

it('does NOT advance when all mediators return error', async () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the correct behavior? If an agent participant is chatting with a mediator and the mediator gets an error responding, won't we still get stuck? Or if that actually does trigger the agent participant to respond, then can we verify that in this test?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent participants can get stuck in private chat

2 participants