Skip to content

Align Go agent and workflow contracts with .NET - #950

Merged
Quim Muntal (qmuntal) merged 6 commits into
mainfrom
dotnet-parity-pass
Sep 1, 2026
Merged

Align Go agent and workflow contracts with .NET#950
Quim Muntal (qmuntal) merged 6 commits into
mainfrom
dotnet-parity-pass

Conversation

@qmuntal

Copy link
Copy Markdown
Member

Summary

  • align agent/provider contracts for tool selection, approval handling, response aggregation, telemetry identity, and provider-owned auto-call configuration
  • add session-backed MessageInjector support inside provider auto-call loops and update the message-injection example
  • align A2A client and hosting lifecycle behavior, task/context state, artifacts, metadata, continuation, and terminal statuses
  • align workflow routing, events, run status, external responses, subworkflow execution, checkpoint restore, and concurrent execution behavior
  • add NewAggregatingExecutor, checkpoint.Manager.LatestCheckpoint, and ordered checkpoint-index deduplication
  • expand parity tests and update the .NET/Go comparison matrix and workflow examples

Reference

  • overlapping contracts audited against microsoft/agent-framework@5996105a1bf2726918101adc6e9c9857b7f68b98
  • workflow source/test delta checked through microsoft/agent-framework@6a0773ba2180e8036d138dbb9794ae64ec2d978b; no later workflow code or test changes were present
  • intentionally keeps usage in message.UsageContent, provider-specific request metadata, and A2A continuation state in task metadata
  • does not port the inconsistent .NET StreamingAggregators.Union behavior, whose documentation promises a distinct union while its implementation appends duplicates

Validation

  • go test ./... -count=1 -p=2 -timeout=180s
  • go vet ./...
  • git diff HEAD^ --check

Align provider options, A2A lifecycle, message injection, telemetry, and tool auto-calling with the audited .NET contracts.

Bring workflow execution, request routing, checkpointing, and aggregation behavior into parity, and update tests, examples, and the comparison matrix.
Copilot AI lite review requested due to automatic review settings August 31, 2026 12:55
@qmuntal
Quim Muntal (qmuntal) requested a review from a team as a code owner August 31, 2026 12:55
@github-actions github-actions Bot added area:agent Changes files in the agent area area:examples Changes files in the examples area area:internal Changes files in the internal area area:provider Changes files in the provider area area:provider/a2a Changes files in the provider / a2a area area:provider/agui Changes files in the provider / agui area area:provider/anthropic Changes files in the provider / anthropic area area:provider/copilot Changes files in the provider / copilot area area:provider/foundry Changes files in the provider / foundry area area:provider/gemini Changes files in the provider / gemini area area:provider/openai Changes files in the provider / openai area area:provider/otel Changes files in the provider / otel area area:tool Changes files in the tool area area:workflow Changes files in the workflow area size:xlarge More than 300 changed lines or 10 files labels Aug 31, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

Review tier: Lite
Findings: 2 Medium severity

New issues introduced by this change (2)
Severity Finding
Medium severity tool/​tool.go — RequireTool validates blank names using TrimSpace, but it does not normalize the stored tool name.…
Medium severity provider/​a2aprovider/​executor.go — yieldCompletedResponse ignores the boolean return from yield when emitting the final Completed…
What changed in this PR

Aligns Go agent/provider/workflow runtime contracts with the .NET implementation, covering tool selection/autocall configuration, message routing/declared-vs-runtime typing, lifecycle/telemetry identity, A2A hosting semantics, and workflow execution/checkpoint behavior.

Changes:

  • Updates workflow routing/execution to better preserve declared vs runtime types (including portable values) and adjusts run/stream lifecycle behavior.
  • Introduces session-backed message injection (agent.MessageInjector) and updates providers/tests/examples to use the new model.
  • Adds new workflow utilities/APIs (NewAggregatingExecutor, checkpoint.Manager.LatestCheckpoint) and expands parity/regression tests.
File Description
workflow/​route.go Adjust portable value unwrapping during routing.
workflow/​route_test.go Adds regression test for catch-all routing with unknown portable type.
workflow/​internal/​execution/​step.go Preserve declared vs concrete types through checkpoint envelope round-trip.
workflow/​internal/​execution/​step_test.go Adds tests for declared-type vs concrete-type checkpoint behavior.
workflow/​internal/​execution/​run.go Adds untyped enqueue API with declared type; improves close cancellation handling.
workflow/​internal/​execution/​execution.go Updates SuperStepRunner interface to new enqueue/validation signatures.
workflow/​internal/​execution/​eventstream.go Ensures workflow run span ends on early stop; comment/behavior alignment.
workflow/​internal/​execution/​eventstream_test.go Adds tests for early stop span ending; tests for EnqueueMessageUntyped behavior.
workflow/​internal/​execution/​edgerunner.go Routes/drops based on runtime type inference for portable messages.
workflow/​inproc/​subworkflow.go Uses active runner input types; improves response qualification erroring.
workflow/​inproc/​subworkflow_test.go Adds tests for active runner input types and unqualified response rejection; updates send APIs.
workflow/​inproc/​state_test.go Updates to OpenStreaming/TrySendMessage APIs.
workflow/​inproc/​runner.go Changes input validation to return (bool, error); untyped enqueue; concurrent subworkflow stepping.
workflow/​inproc/​runner_test.go Adds enqueue path test around ExternalResponse declared as any.
workflow/​inproc/​run.go NewEvents becomes snapshot-based; introduces TrySendMessage; updates Resume path.
workflow/​inproc/​routing_test.go Adds polymorphic external input routing coverage; fan-in runtime routing coverage.
workflow/​inproc/​external_request_test.go Updates ExternalResponse handler to interface route; OpenStreaming usage.
workflow/​inproc/​environment.go Adds OpenStreaming; hardens nil-message behavior; closes handles on error.
workflow/​inproc/​context.go Switches join IDs to UUID strings (with hyphens).
workflow/​inproc/​concurrent_test.go Updates tests to OpenStreaming.
workflow/​inproc/​checkpoint_test.go Updates tests to TrySendMessage.
workflow/​inproc/​binding_test.go Output tag assertion changes; improves halted-run test cleanup/timing.
workflow/​info.go Checkpoint IDs now use UUID strings (with hyphens).
workflow/​executor.go Eager protocol init in Initialize; adds NewAggregatingExecutor helper.
workflow/​executor_test.go Adds tests for NewAggregatingExecutor and updates streaming entrypoints.
workflow/​errors.go Removes ErrInvalidInputType sentinel error.
workflow/​checkpoint/​manager.go Adds LatestCheckpoint API to Manager implementations.
workflow/​checkpoint/​manager_test.go Adds tests for LatestCheckpoint behavior.
workflow/​checkpoint/​jsonstore.go Deduplicates persisted index entries during load.
workflow/​checkpoint/​jsonstore_test.go Adds test ensuring persisted index deduplication.
workflow/​agentworkflow/​workflow.go Updates hosted workflow agent behavior (IDs, injection handling, turn handling).
workflow/​agentworkflow/​workflow_test.go Expands coverage for empty-first-turn behavior and close error propagation; removes DisableFuncAutoCall usage.
workflow/​agentworkflow/​session.go Loads/starts state with initial messages; session IDs use newMessageID; returns “initial messages sent” flag.
workflow/​agentworkflow/​inprocess_execution_test.go Updates tag assertions and streaming entrypoints.
workflow/​agentworkflow/​hosting.go newMessageID now uses UUID string (with hyphens).
workflow/​agentworkflow/​hosting_test.go Updates streaming send API; removes DisableFuncAutoCall usage in configs.
workflow/​agentworkflow/​groupchat_test.go Updates streaming entrypoints and send API; removes DisableFuncAutoCall usage.
workflow/​agentworkflow/​concurrent_test.go Fixes deterministic assertion ordering; updates OpenStreaming usage.
workflow/​agentworkflow/​builders_test.go Updates OpenStreaming/TrySendMessage usage; removes DisableFuncAutoCall usage.
tool/​tool.go Replaces RequireTools/Required with RequireTool/RequiredTool semantics.
tool/​tool_test.go Updates tests for new RequireTool/RequiredTool behavior, including blank-name panic.
tool/​shelltool/​shelltool_test.go Removes t.Parallel to avoid test interaction.
tool/​agenttool/​agenttool_test.go Changes empty args behavior to reject blank query; validates provider not invoked.
provider/​otelprovider/​otel.go Records gen_ai.response.id attribute from updates when available.
provider/​otelprovider/​otel_test.go Adds early-stop-safe yield usage; validates response ID attribute.
provider/​openaiprovider/​responses.go Provider-owned autocall config; preserves caller slice backings; ProviderName override; RequiredTool support.
provider/​openaiprovider/​responses_test.go Adds immutability/tool_choice tests; updates required-tool test expectations; config updates.
provider/​openaiprovider/​chat.go Provider-owned autocall config; preserves caller slice backings; sets RawRepresentation; ProviderName override; RequiredTool support.
provider/​openaiprovider/​chat_test.go Adds immutability/tool_choice tests; adds concurrent tool invocation + injection tests; validates RawRepresentation.
provider/​geminiprovider/​agent.go Provider-owned autocall config; tool mode applies consistently; tool call IDs use UUID strings.
provider/​geminiprovider/​agent_test.go Updates config expectations; rejects empty message; tool mode required uses RequireTool; adds native-tool ToolModeNone test.
provider/​foundryprovider/​servedmodel.go Ensures served-model box updated consistently (no early return).
provider/​foundryprovider/​servedmodel_test.go Adds test ensuring served-model metadata doesn’t leak across calls.
provider/​foundryprovider/​clientheaders.go Normalizes x-client-* names to lowercase; detects duplicates case-insensitively; avoids maps.Copy.
provider/​foundryprovider/​clientheaders_test.go Updates agent configs; adds case-insensitive upsert test.
provider/​foundryprovider/​agent.go ProviderName set; OpenAIOptions precedence hardened; ToolAutoCall plumbed through.
provider/​foundryprovider/​agent_test.go Validates ProviderName; adds test ensuring Foundry-owned routing overrides hostile OpenAI options.
provider/​copilotprovider/​copilot.go Prevents mutating caller RunOptions backing by cloning before appending instructions.
provider/​anthropicprovider/​agent.go Provider-owned autocall config; RequiredTool support (single tool vs any).
provider/​anthropicprovider/​agent_test.go Updates configs; adds RequiredTool tool_choice assertion; test harness config updates.
provider/​aguiprovider/​agui.go Provider-owned autocall config; clones RunOptions before appending instructions.
provider/​a2aprovider/​session.go Stores a single current task ID instead of a growing list; renames session keys.
provider/​a2aprovider/​hosting_test.go Adds context-id/service-id separation tests; adds background continuation polling tests; updates expected status sequences.
provider/​a2aprovider/​hosting_convert.go Avoids emitting empty metadata maps by cloning only when non-empty.
provider/​a2aprovider/​hosting_convert_test.go Adds executor lifecycle tests (cancellation/failure); updates empty-metadata expectation.
provider/​a2aprovider/​executor.go Adds background polling loop; improves cancellation semantics; forwards metadata; normalizes status sequencing and failure messaging.
internal/​telemetry/​telemetry.go Makes user-agent detection case-insensitive and segment-aware.
internal/​telemetry/​telemetry_test.go Adds mixed-case user-agent recognition test.
examples/​03-workflows/​concurrent/​concurrent_custom_aggregator/​main.go Uses TrySendMessage for turn tokens.
examples/​03-workflows/​agents/​group_chat_tool_approval/​main.go Uses TrySendMessage for turn tokens.
examples/​03-workflows/​01-start-here/​08_sequential_chain_only_responses/​main.go Uses TrySendMessage; removes DisableFuncAutoCall usage in example agent config.
examples/​03-workflows/​01-start-here/​04_multi_model_service/​main.go Uses TrySendMessage for turn tokens.
examples/​03-workflows/​01-start-here/​03_agent_workflow_patterns/​main.go Uses TrySendMessage for turn tokens.
examples/​03-workflows/​01-start-here/​02_agents_in_workflows/​main.go Uses TrySendMessage for turn tokens.
examples/​02-agents/​agui/​step03_frontend_tools/​server/​main.go Shows ToolAutoCall configuration instead of disabling autocall globally.
examples/​02-agents/​agents/​message-injection/​main.go Updates message-injection example to use session-backed agent.MessageInjector.
agent/​response.go Adds response-level RawRepresentation aggregation; centralizes append behavior.
agent/​response_test.go Extends RawRepresentation tests to include response-level aggregation.
agent/​messageinjection.go Adds new session-backed MessageInjector implementation.
agent/​harness/​toolautocall/​inject.go Removes old context-based injector implementation.
agent/​harness/​toolautocall/​doc.go Updates docs to reflect provider-default autocall + Config customization.
agent/​harness/​toolautocall/​autocall_test.go Updates tests for new RequiredTool semantics, iteration config pointers, and message injection integration.
agent/​harness/​toolautocall/​autocall_log_test.go Updates iteration config to pointer form.
agent/​harness/​toolautocall/​autocall_inject_test.go Removes old injection tests (replaced by new injector integration tests).
agent/​agent.go Removes DisableFuncAutoCall; adds MessageInjector to config; rejects blank RunText and nil RunMessage; adjusts early-stop handling.
agent/​agent_test.go Adds tests for injection middleware placement and new input validation; updates early-stop context-provider semantics.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tool/tool.go
Comment thread provider/a2aprovider/executor.go Outdated
@github-actions

This comment has been minimized.

@github-actions github-actions Bot added parity-approved Go API consistency review found no parity issues public-api-change Pull Request changes public APIs risk:high Large blast radius, difficult rollback, or sensitive behavior and removed pending-auto-risk Automatic risk classification is in progress labels Aug 31, 2026
Co-authored-by: qmuntal <3747816+qmuntal@users.noreply.github.com>
Copilot AI and others added 2 commits August 31, 2026 13:24
Co-authored-by: qmuntal <3747816+qmuntal@users.noreply.github.com>
Co-authored-by: qmuntal <3747816+qmuntal@users.noreply.github.com>

Copilot AI commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Copilot fix codereview comments.

Addressed both code review comments in 3988d64: RequireTool now stores the trimmed tool name, and yieldCompletedResponse now respects the final yield return value. Validation passed with targeted tests/lint and CodeQL reported 0 alerts.

@github-actions github-actions Bot added pending-auto-risk Automatic risk classification is in progress risk:high Large blast radius, difficult rollback, or sensitive behavior and removed risk:high Large blast radius, difficult rollback, or sensitive behavior labels Aug 31, 2026
@github-actions

This comment has been minimized.

@github-actions github-actions Bot removed the pending-auto-risk Automatic risk classification is in progress label Aug 31, 2026
@qmuntal
Quim Muntal (qmuntal) added this pull request to the merge queue Sep 1, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 1, 2026
@qmuntal

Copy link
Copy Markdown
Member Author

Copilot fix merge conflicts

Copilot AI and others added 2 commits September 1, 2026 08:31
# Conflicts:
#	provider/foundryprovider/agent.go

Co-authored-by: qmuntal <3747816+qmuntal@users.noreply.github.com>
Co-authored-by: qmuntal <3747816+qmuntal@users.noreply.github.com>

Copilot AI commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Copilot fix merge conflicts

Resolved the merge conflicts by merging origin/main in 0d4c521 and preserving the PR’s Foundry provider identity/tool auto-call behavior alongside main’s updated endpoint handling. Also addressed the follow-up AG-UI reasoning event issue in 32598ff. Targeted tests/lint passed and CodeQL reported 0 alerts.

@github-actions github-actions Bot added pending-auto-risk Automatic risk classification is in progress risk:high Large blast radius, difficult rollback, or sensitive behavior and removed risk:high Large blast radius, difficult rollback, or sensitive behavior pending-auto-risk Automatic risk classification is in progress labels Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Go API Consistency Review — PR #950

Scope: public API, user-visible behavior, examples

Changed Go contract:

  • tool.RequireTools(name ...string) ToolModetool.RequireTool(name string) ToolMode (breaking rename, single-name API)
  • ToolMode.Required() []stringToolMode.RequiredTool() (string, bool)
  • agent.Config.DisableFuncAutoCall bool removed
  • agent.Config.MessageInjector *MessageInjector added (new exported field)
  • agent.MessageInjector type added with EnqueueMessages exported method (agent/messageinjection.go)
  • workflow.NewAggregatingExecutor[TInput, TAggregate] constructor added (workflow/executor.go)
  • checkpoint.Manager.LatestCheckpoint(ctx, sessionID) method added to the public Manager interface
  • Workflow routing, event-stream, subworkflow, and concurrent-execution behavior changes (user-visible)

Upstream evidence reviewed:

  • dotnet/src/Microsoft.Agents.AI/ChatClient/MessageInjectingChatClient.cs — stores injected messages per-session in AgentSession.StateBag, loops internally when no actionable FunctionCallContent is present. Go MessageInjector preserves identical semantics (drains from Session, loops until no function call present, re-injects queued messages). Aligned.
  • dotnet/src/Microsoft.Agents.AI.Workflows/AggregatingExecutor.csAggregatingExecutor<TInput, TAggregate>(id, Func<TAggregate?, TInput, TAggregate?> aggregator, ...), aggregate state stored under key "Aggregate". Go NewAggregatingExecutor uses pointer semantics in place of nullable TAggregate?, stores under the same key. Optional .NET parameters options and declareCrossRunShareable omitted; not part of the core behavioral contract. Aligned.
  • dotnet/src/Microsoft.Agents.AI.Workflows/CheckpointManager.cs (GetLatestCheckpointAsync) and dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/CheckpointManagerLatestTests.cs — returns the last entry of the commit-ordered index. Go LatestCheckpoint does the same (index[len(index)-1]). Naming follows Go conventions. Aligned.
  • dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/Models/ToolChoice.cs (internal) — single function name for required mode. No multi-name variant exists in any upstream surface. Go previously over-extended with RequireTools(name ...string); narrowing to RequireTool(name string) corrects this. Aligned.
  • DisableFuncAutoCall — no equivalent .NET flag found; removal simplifies the auto-call pipeline with no upstream parity impact.

Result: aligned — all new and changed public Go APIs map to upstream .NET equivalents with compatible semantics. The RequireTool rename corrects a prior Go-only over-extension. MessageInjector, NewAggregatingExecutor, and LatestCheckpoint faithfully port their .NET counterparts. No parity issues identified.

Generated by Go API Consistency Review Agent for #950 · sonnet46 · 58.3 AIC · ⌖ 5.21 AIC · ⊞ 6.4K ·

@qmuntal
Quim Muntal (qmuntal) added this pull request to the merge queue Sep 1, 2026
Merged via the queue into main with commit 7cbb954 Sep 1, 2026
43 of 45 checks passed
@qmuntal
Quim Muntal (qmuntal) deleted the dotnet-parity-pass branch September 1, 2026 08:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:agent Changes files in the agent area area:examples Changes files in the examples area area:internal Changes files in the internal area area:provider/a2a Changes files in the provider / a2a area area:provider/agui Changes files in the provider / agui area area:provider/anthropic Changes files in the provider / anthropic area area:provider/copilot Changes files in the provider / copilot area area:provider/foundry Changes files in the provider / foundry area area:provider/gemini Changes files in the provider / gemini area area:provider/openai Changes files in the provider / openai area area:provider/otel Changes files in the provider / otel area area:provider Changes files in the provider area area:tool Changes files in the tool area area:workflow Changes files in the workflow area parity-approved Go API consistency review found no parity issues public-api-change Pull Request changes public APIs risk:high Large blast radius, difficult rollback, or sensitive behavior size:xlarge More than 300 changed lines or 10 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants