Skip to content

[dotnet-port-fixes] Bound tool approval auto-approval loop #812

Description

@github-actions

Summary

Bound the tool-approval middleware's internal auto-approval re-entry loop to 40 passes and forward one final unsplit inner turn once the cap is hit. This aligns Go with the upstream .NET safety fix for runaway auto-approved tool requests without changing the public Go API.

Upstream commit: 74a144085a5fd05921b473001528f3ae0725b76a
(microsoft/agent-framework@74a1440)

Ported .NET PRs

Breaking Changes

No.

Tests and Examples

  • go test ./agent/harness/toolapproval
  • Added TestToolApproval_AutoApprovedRequestsStopAtIterationCap
  • Examples updated: None

Notes

  • Go keeps the safety cap internal at 40 iterations so this port stays within the no-public-API-change constraint for [dotnet-port-fixes].
  • No examples or comparison-doc updates were needed because this is an internal behavior correction in an already supported area.

Generated by .NET to Go Fixes and Test Porting Agent · gpt54 · 151.1 AIC · ⌖ 12.7 AIC · ⊞ 24.2K ·


Note

This was originally intended as a pull request, but GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch dotnet-port-fixes/toolapproval-cap-a876efa58dda8090.

Click here to create the pull request

To fix the permissions issue, go to SettingsActionsGeneral and enable Allow GitHub Actions to create and approve pull requests. See also: gh-aw FAQ

Show patch preview (116 of 116 lines)
From 800e6285f0fed7dd5ee4129e95a3252b7fc5a8dc Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 7 Aug 2026 03:32:33 +0000
Subject: [PATCH] toolapproval: bound auto-approval loop

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 agent/harness/toolapproval/toolapproval.go    | 24 +++++++++-
 .../harness/toolapproval/toolapproval_test.go | 44 +++++++++++++++++++
 2 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/agent/harness/toolapproval/toolapproval.go b/agent/harness/toolapproval/toolapproval.go
index d07482b69..178fa7560 100644
--- a/agent/harness/toolapproval/toolapproval.go
+++ b/agent/harness/toolapproval/toolapproval.go
@@ -26,7 +26,10 @@ import (
 	"github.com/microsoft/agent-framework-go/tool"
 )
 
-const stateKey = "toolApprovalState"
+const (
+	stateKey                    = "toolApprovalState"
+	defaultMaxAutoApprovalTurns = 40
+)
 
 // Rule is a standing approval rule. If Arguments is nil, all invocations of
 // the named tool are auto-approved. Otherwise only invocations with an exact
@@ -121,7 +124,7 @@ func run(cfg Config, next agent.RunFunc, ctx context.Context, messages []*messag
 		}
 
 		// Step 3: Main loop — call inner agent, classify approval requests.
-		for {
+		for iteration := 0; ; iteration++ {
 			// Inject collected approval responses as user messages.
 			callMessages := messages
 			if len(st.CollectedApprovalResponses) > 0 {
@@ -130,6 +133,23 @@ func run(cfg Config, next agent.RunFunc, ctx context.Context, messages []*messag
 				st.CollectedApprovalResponses = nil
 			}
 
+			if iteration >= defaultMaxAutoApprovalTurns {
+				// Cap reached: forward one final inner turn as-is so any approval request
+				// is surfaced to the caller instead of continuing the auto-approval chain.
+				for update, err := range next(ctx, callMessages, opts...) {
+					if err != nil {
+						yield(nil, err)
+						return
+					}
+					if !yield(update, nil) 
... (truncated)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions