Repository navigation
[Blazor] Add shared agent and UI state - #68334
Merged
Merged
Conversation
javiercn
force-pushed
the
javiercn-components-ai-08-shared-state
branch
from
August 11, 2026 07:20
58a1ab0 to
8fae709
Compare
Member
Author
|
/azp run aspnetcore-ci |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Member
Author
|
/azp run aspnetcore-ci |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
javiercn
force-pushed
the
javiercn-components-ai-08-shared-state
branch
from
August 12, 2026 13:20
8fae709 to
c02241d
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a provider/protocol-neutral persistence contract to Components.AI so agents can commit streamed updates into a thread, then later restore conversation history, rendered blocks, and typed state from that thread. This PR also introduces the canonical “Shared State” dojo scenario (recipe editor) plus focused runtime and E2E coverage (including a deterministic recording) to validate stable thread identity and state forwarding across turns.
Changes:
- Introduces
IConversationThreadand plumbs it throughUIAgentOptions,UIAgent(commit + restore), andAgentContext(restore). - Adds the Shared State dojo scenario (UI + API tool mapping) and E2E replay asserting stable thread ID and exact serialized state in requests.
- Adds runtime tests + helpers validating commit boundaries, failed-stream non-commit behavior, conversation identity forwarding, and restoration.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Components/AI/src/Engine/IConversationThread.cs | New product contract for persisting committed streamed updates per conversation thread. |
| src/Components/AI/src/Pipeline/UIAgentOptions.cs | Adds opt-in thread configuration to the agent options surface. |
| src/Components/AI/src/Engine/UIAgent.cs | Persists streamed updates into a thread, forwards conversation IDs, and adds restore support. |
| src/Components/AI/src/Engine/AgentContext.cs | Adds restore API that rebuilds Turns from restored blocks. |
| src/Components/AI/src/PublicAPI.Unshipped.txt | Public API declarations for the new thread and restore methods. |
| src/Components/AI/test/TestHelpers/InMemoryConversationThread.cs | Test helper thread implementation for commit/restore tests. |
| src/Components/AI/test/Engine/UIAgentThreadTests.cs | Runtime tests covering thread commit, non-commit on failure, conversation ID forwarding, and restore behavior. |
| src/Components/AI/test/Engine/AgentContextThreadTests.cs | Runtime tests covering restoring turns without firing callbacks and restoring empty threads. |
| src/Components/AI/testassets/DojoClient/DojoScenarios.cs | Adds Shared State endpoint constant for the dojo client. |
| src/Components/AI/testassets/DojoClient/Program.cs | Registers a keyed IChatClient for the Shared State scenario. |
| src/Components/AI/testassets/DojoClient/Components/Pages/Home.razor | Adds Shared State scenario link to the dojo home page. |
| src/Components/AI/testassets/DojoClient/Components/_Imports.razor | Imports Shared State scenario namespace for Razor components. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/SharedStateScenario.razor | New dojo scenario wiring: agent, thread, state mapper, and editor/chat layout. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/SharedStateScenario.razor.css | Styling for the Shared State scenario layout. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/SharedStateConversationThread.cs | Dojo thread implementation that persists committed updates + captures conversation identity. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/RecipeSuggestions.razor | Suggestion buttons that send prompts via AgentContext. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/RecipeSuggestions.razor.css | Styling for recipe suggestion buttons. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/RecipeEditor.razor | Typed recipe editor UI that edits immutable recipe state and triggers agent prompts. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/RecipeEditor.razor.css | Styling for the recipe editor and “changed” section highlighting. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/IngredientRow.razor | Ingredient row component for editing ingredient fields. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/IngredientRow.razor.css | Styling for ingredient rows. |
| src/Components/AI/testassets/DojoClient/Components/Scenarios/SharedState/Recipe.cs | Shared typed state models (RecipeState, Recipe, Ingredient) for the dojo client. |
| src/Components/AI/testassets/AGUIDojoApi/Program.cs | Maps new /shared_state dojo API endpoint. |
| src/Components/AI/testassets/AGUIDojoApi/ChatClientAgentFactory.cs | Adds Shared State system prompt, generate_recipe tool, and state snapshot mapping. |
| src/Components/AI/testassets/AGUIDojoApi/SharedState/RecipeResponse.cs | Shared State tool response wrapper for API-side deserialization. |
| src/Components/AI/testassets/AGUIDojoApi/SharedState/Recipe.cs | API-side recipe model for tool arguments/results. |
| src/Components/AI/testassets/AGUIDojoApi/SharedState/Ingredient.cs | API-side ingredient model for tool arguments/results. |
| src/Components/AI/testassets/AGUIDojoApi/ScriptedChatClient.cs | Adds canned shared-state tool-call behavior for the no-credentials scripted model. |
| src/Components/AI/testassets/DojoClient.E2E.Tests/Tests/SharedStateScenarioTests.cs | Browser test validating state forwarding, stable thread ID, and preservation across snapshots. |
| src/Components/AI/testassets/DojoClient.E2E.Tests/ServiceOverrides/DojoModelOverrides.cs | Adds Shared State recorded model override wiring. |
| src/Components/AI/testassets/DojoClient.E2E.Tests/ServiceOverrides/RecordedScript.cs | Adds stable-thread assertion + expected state fields to the recorded script model. |
| src/Components/AI/testassets/DojoClient.E2E.Tests/ServiceOverrides/RecordedChatClient.cs | Asserts request includes expected serialized state and stable thread ID. |
| src/Components/AI/testassets/DojoClient.E2E.Tests/Baselines/SharedState.recording.json | Deterministic recording for Shared State scenario replay (state + thread requirements). |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
kotlarmilos
force-pushed
the
javiercn-components-ai-08-shared-state
branch
from
August 12, 2026 16:47
c02241d to
fea8616
Compare
javiercn
force-pushed
the
javiercn-components-ai-08-shared-state
branch
from
August 13, 2026 10:04
fea8616 to
c8cd78c
Compare
ilonatommy
force-pushed
the
javiercn-components-ai-08-shared-state
branch
from
August 14, 2026 12:37
c8cd78c to
d6f392c
Compare
kotlarmilos
reviewed
Aug 17, 2026
ilonatommy
force-pushed
the
javiercn-components-ai-08-shared-state
branch
from
August 20, 2026 06:55
d6f392c to
965f81b
Compare
Base automatically changed from
javiercn-components-ai-07-agentic-generative-ui
to
main
August 26, 2026 13:14
ilonatommy
force-pushed
the
javiercn-components-ai-08-shared-state
branch
from
August 26, 2026 13:46
32ce38d to
2ae845f
Compare
Rationale: let provider-neutral Components.AI applications persist completed streaming turns, restore rendered conversation state, and carry service conversation identity without coupling the runtime to AG-UI. Implementation: add the conversation thread contract, record and commit streamed updates in UIAgent, restore history and typed state through the existing mapping pipeline, and forward stateful conversation IDs on subsequent turns. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
Rationale: demonstrate canonical Shared State across the real DojoClient to AGUIDojoApi HTTP/SSE boundary so user edits and agent updates operate on one recipe instead of independent copies. Implementation: add the /shared_state endpoint and keyed AGUIChatClient, forward the stable thread ID and current recipe through RunAgentInput, map generate_recipe results to state snapshots, and render an editable recipe that preserves local fields across agent turns. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
Rationale: verify completed streaming turns restore typed state while partial failures remain uncommitted, and prove local recipe edits cross the real AG-UI transport without replacing DojoClient's AGUIChatClient. Implementation: add focused thread and restoration unit tests, recorded-model assertions for AG-UI state and stable thread identity, and a dual-host browser scenario that co-edits the recipe across two turns. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
Rationale: keep the Shared State browser scenario deterministic while preserving the production DojoClient to AGUIDojoApi HTTP/SSE boundary and replacing only the API model. Implementation: record two recipe turns with exact incoming state snapshots, stable thread identity, generate_recipe calls, tool continuations, and streamed assistant summaries. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: baddc46b-b86e-4be5-add2-1a7b3cac8195
ilonatommy
force-pushed
the
javiercn-components-ai-08-shared-state
branch
from
August 26, 2026 13:52
2ae845f to
654c24b
Compare
ilonatommy
reviewed
Aug 28, 2026
ilonatommy
reviewed
Aug 28, 2026
ilonatommy
reviewed
Aug 28, 2026
ilonatommy
approved these changes
Aug 28, 2026
lewing
approved these changes
Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Position 8 in the native Components.AI stack tracked by #68340, this layer depends on #68333 and adds persistent conversation threads plus the canonical Shared State recipe dojo. Relative to parent
javiercn-components-ai-07-agentic-generative-ui(913754f3988a8d4ebc6ddb29785a25bbe796c458), the stack-relative diff contains the product runtime contracts, the recipe scenario, focused tests, and its generated replay. The cross-cutting constraint is that product code remains provider/protocol-neutral while DojoClient keeps the realAGUIChatClient0.0.5 -> HTTP/SSE -> AGUIDojoApi/AGUI.Server 0.0.5 path.Design
The new product contract stores raw streamed chat updates rather than rendered UI blocks or AG-UI events. That keeps persistence owned by the application, lets restoration reuse the existing block/state mapping pipeline, and avoids coupling Components.AI to a transport protocol.
The lifecycle has three equivalence classes, each represented once in the implementation and tests: a successful stream appends every request message and response update before committing atomically; a failed or cancelled partial stream never reaches
CompleteTurn; restoration replays only committed updates into temporary history and a clean typed-state scope, then commits both together. A failed or cancelled restore leaves the previous history and state unchanged, while an empty thread clears both. For stateful providers, the thread-capturedConversationIdis forwarded on the next request; stateless providers continue to receive the full reconstructed history.Implementation
UIAgentstarts a pending turn before model invocation, forwards service conversation identity when present, and deliberately commits only after streaming, mapping, finalization, and history reconstruction all complete. The placement ofCompleteTurnis the failure-safety guarantee: an exception or cancellation inside the loop leaves no committed partial turn.Restoration treats both user and tool messages as request boundaries, groups assistant updates back into messages, and feeds every assistant update through
ApplyStateMapper. History is rebuilt separately while typed state suppresses intermediate notifications and resets to a newTStatebefore replay, which lets state deltas read the restored value rather than stale live state.AgentContext.RestoreAsyncrejects active turns and replaces turns, status, error, retry state, and the previous cancellation source only after agent restoration succeeds.The dojo is the protocol adapter. Local editor changes replace the same typed
RecipeStateread byRunAgentInput.State; AG-UI state snapshots deserialize back into that state. A stable thread ID is supplied on every request, while the product runtime sees onlyIConversationThread,ChatOptions, andChatResponseUpdate.Recipe fields form one editor equivalence class: title, details, preferences, ingredients, and instructions all update immutable recipe records through
RecipeChanged; section comparison only adds presentation highlighting for agent-originated changes. On the API side, onegenerate_recipetool returns the complete recipe and AGUI.Server maps that result to a state snapshot:The permanent browser replay replaces only AGUIDojoApi's model. Request assertions inspect the real API-side
RunAgentInputfor exact JSON state and one stable non-empty thread ID; DojoClient's keyedAGUIChatClient, both hosts, HTTP POST, SSE stream, tool invocation, and state mapping remain production code.Outcome
SharedStateScenarioTestspassedReview the product contract and
UIAgentcommit/restore boundaries first, then the DojoClientRunAgentInputserialization and API snapshot mapping; the recipe CSS and repeated editor-field markup can be skimmed. Acceptance criteria: manual recipe edits must be present in the next API request, a stable thread must span model calls and browser turns, agent snapshots must preserve unrelated/latest user edits, failed streams must not commit partial history, and restored state/history must behave like live state/history.