Skip to content

.NET: Implement tool events > function call events for AG-UI support#4824

Open
kzu wants to merge 1 commit intomicrosoft:mainfrom
kzu:toolevents
Open

.NET: Implement tool events > function call events for AG-UI support#4824
kzu wants to merge 1 commit intomicrosoft:mainfrom
kzu:toolevents

Conversation

@kzu
Copy link
Contributor

@kzu kzu commented Mar 21, 2026

Added two new case branches and three new converter methods:

  • ToolExecutionStartEvent → FunctionCallContent (the AGUI layer then emits TOOL_CALL_START/ARGS/END to the client)
  • ToolExecutionCompleteEvent → FunctionResultContent → TOOL_CALL_RESULT
  • Arguments are converted from JsonElement using AOT-safe JsonElement.EnumerateObject() — no reflection/dynamic code

Motivation and Context

When using Copilot SDK adapter to expose an AG-UI client, tool call events aren't surfaced at all as events the client can show.

Fixes #4823

Added two new case branches and three new converter methods:

 - ToolExecutionStartEvent → FunctionCallContent (the AGUI layer then emits TOOL_CALL_START/ARGS/END to the client)
 - ToolExecutionCompleteEvent → FunctionResultContent → TOOL_CALL_RESULT
 - Arguments are converted from JsonElement using AOT-safe JsonElement.EnumerateObject() — no reflection/dynamic code

Fixes microsoft#4823
Copilot AI review requested due to automatic review settings March 21, 2026 02:24
@github-actions github-actions bot changed the title Implement tool events > function call events for AG-UI support .NET: Implement tool events > function call events for AG-UI support Mar 21, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds forwarding of GitHub Copilot SDK tool execution events into the Agent Framework’s function-call/function-result content model so the AG-UI adapter can surface tool-call lifecycle events to clients (fixing #4823).

Changes:

  • Handle ToolExecutionStartEvent and ToolExecutionCompleteEvent in the Copilot session streaming event switch.
  • Convert tool-start to FunctionCallContent (including JSON argument conversion) and tool-complete to FunctionResultContent.
  • Add unit tests covering tool start/complete conversions and JSON argument handling.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs Adds streaming handling + conversion helpers to emit FunctionCallContent/FunctionResultContent from tool events.
dotnet/tests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests/GitHubCopilotAgentTests.cs Adds tests validating tool event conversion behavior and argument mapping.

JsonValueKind.Null => null,
JsonValueKind.Number => property.Value.TryGetInt64(out long l)
? (object?)l
: property.Value.GetDouble(),
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

ConvertJsonElementToArguments currently converts non-primitive argument values (e.g., JsonValueKind.Object/Array) using GetRawText(), which turns them into JSON strings. When these arguments are later serialized (e.g., into AG-UI ToolCallArgs events), nested objects/arrays will be double-encoded instead of remaining structured JSON. Prefer preserving structured values by returning JsonElement (e.g., property.Value.Clone()) for Object/Array (and consider how to handle Undefined), so downstream serialization emits proper JSON objects/arrays.

Suggested change
: property.Value.GetDouble(),
: property.Value.GetDouble(),
JsonValueKind.Object => property.Value.Clone(),
JsonValueKind.Array => property.Value.Clone(),
JsonValueKind.Undefined => null,

Copilot uses AI. Check for mistakes.
Comment on lines +370 to +371
// Non-primitive values fall back to raw JSON text
Assert.IsType<string>(content.Arguments["objVal"]);
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

The test currently asserts objVal is a string (raw JSON text). If arguments are intended to round-trip as structured JSON through the AG-UI layer, nested object/array values should be preserved as structured types (e.g., JsonElement) rather than raw JSON strings. Consider updating this assertion to validate the intended structured representation so regressions don’t reintroduce double-encoding of nested arguments.

Suggested change
// Non-primitive values fall back to raw JSON text
Assert.IsType<string>(content.Arguments["objVal"]);
// Non-primitive values are preserved as structured JSON
var objValElement = Assert.IsType<JsonElement>(content.Arguments["objVal"]);
Assert.Equal("value", objValElement.GetProperty("nested").GetString());

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.NET: [Bug]: Tool events from GitHub Copilot SDK not forwarded as FunctionCallContent/FunctionResultContent

3 participants