Skip to content

feat(a2a): add deterministic multi-turn conversations - #591

Merged
jpr5 merged 19 commits into
CopilotKit:mainfrom
AnshulBhati01:upstream/aimock-conversations
Oct 29, 2026
Merged

jpr5 merged 19 commits into
CopilotKit:mainfrom
AnshulBhati01:upstream/aimock-conversations

Conversation

@AnshulBhati01

Copy link
Copy Markdown
Contributor

A2A mocks currently answer each message independently. This adds onConversation() for deterministic multi-turn tasks: intermediate turns request more input, continuation uses the existing task/context IDs, and the final turn completes or fails the task. JSON-RPC and SSE share the same state, including mounted agents and reset behavior.

Optional bounded message-ID retention replays the exact accepted response without advancing the conversation; conflicting reuse is rejected. Configuration-file registration and usage documentation are included.

Validation: 339 tests pass across the conversation, A2A, JSON-RPC, configuration, mounting and suite tests. Type checks, build, ESLint, formatting and package export checks pass. Updated API reports are included; please apply public-api-addition for compatibility review.

@pkg-pr-new

pkg-pr-new Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@copilotkit/aimock@591

commit: a176979

jpr5 added 17 commits October 29, 2026 07:59
A send with message.taskId was routed to the conversation store even
when no conversation created that task. Mocks without conversations
returned -32001/-32004 where they previously served the matching
legacy fixture, which broke A2A clients that send taskId on follow-ups.

Track the task IDs a conversation creates and route by task ID only for
those. Every other send keeps the legacy path, including the dispatcher
SendStreamingMessage handler, which again selects legacy fixtures with
findMatch unless a conversation is the streaming match.
The streaming request check began requiring jsonrpc "2.0" and an id.
Requests without them stopped streaming: a missing jsonrpc field got
-32600 and a notification got an empty response. Restore the previous
check so these requests use the SSE transport again.
The conversation store built its error envelope inline, which the A8
common-envelope inventory in response-adoption.test.ts reports as an
unclassified runtime error object. Export errorResponse from jsonrpc.ts
and use it. The wire output is unchanged.
Clone the caller's turns first and validate the clone, so an accessor or proxy
cannot return valid values to the validator and different ones to the copy.
A part needs exactly one content field with a value: a string text, url or raw,
or non-null data. Fixture parts with null or undefined data or two content
fields fail registration, and matching sends with such parts get -32602.
A null or empty taskId, contextId or messageId counts as absent, as an empty
context already did. A first send with taskId "" or null starts the
conversation, and an absent messageId is generated and never retained.
Stored user messages and agent replies carry the task's taskId and contextId,
not the raw values the client sent.
A turn that leaves the task in TASK_STATE_INPUT_REQUIRED sets status.message to
the agent reply, so A2A clients that read the prompt from the status see it.
…ssages

Run a conversation through batched SendStreamingMessage, test the message-parts
guard, a legacy task ID sent with conversation text, JSON error bodies on the
streaming route and non-string artifact fields, and assert the specific
registration error messages instead of any thrown error.
@jpr5 jpr5 added the public-api-addition Intended public API report changes reviewed for compatibility; freshness remains required label Oct 29, 2026
@jpr5

jpr5 commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Thanks again for this. Multi-turn conversations are a genuinely useful addition, and we'd like to get it in. We pushed some fixes on top of your branch so it's safe for people who don't use conversations: requests with a taskId and streaming requests without jsonrpc/id now behave exactly as before unless a conversation is registered. We also tightened input validation, put the agent's clarifying question in status.message so A2A SDK clients see it, kept history IDs consistent, and filled in some test gaps. Shout if anything looks off to you.

@jpr5
jpr5 merged commit bc17796 into CopilotKit:main Oct 29, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

public-api-addition Intended public API report changes reviewed for compatibility; freshness remains required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants