@@ -59,27 +59,139 @@ extension Agent {
5959 }
6060
6161 func executeAndAppendResults(
62- _ calls: [ ToolCall ] , context: C , messages: inout [ ChatMessage ]
62+ _ calls: [ ToolCall ] , context: C , messages: inout [ ChatMessage ] ,
63+ approvalHandler: ToolApprovalHandler ? = nil , allowlist: inout Set < String >
6364 ) async throws {
6465 guard !calls. isEmpty else { return }
65- let results = try await executeToolsInParallel ( calls, context: context. withParentHistory ( messages) )
66- for (call, result) in results {
67- let content = ContextCompactor . truncateToolResult ( result. content, configuration: configuration)
68- messages. append ( . tool( id: call. id, name: call. name, content: content) )
66+ let executionContext = context. withParentHistory ( messages)
67+
68+ guard let handler = approvalHandler, configuration. approvalPolicy != . none else {
69+ let results = try await executeToolsInParallel (
70+ calls,
71+ context: executionContext,
72+ approvalHandler: approvalHandler
73+ )
74+ for (call, result) in results {
75+ let content = ContextCompactor . truncateToolResult ( result. content, configuration: configuration)
76+ messages. append ( . tool( id: call. id, name: call. name, content: content) )
77+ }
78+ return
79+ }
80+
81+ var autoExecute : [ IndexedToolCall ] = [ ]
82+ var needsApproval : [ IndexedToolCall ] = [ ]
83+ for (offset, call) in calls. enumerated ( ) {
84+ let indexed = IndexedToolCall ( index: offset, call: call)
85+ if requiresApproval ( call, allowlist: allowlist) {
86+ needsApproval. append ( indexed)
87+ } else {
88+ autoExecute. append ( indexed)
89+ }
90+ }
91+
92+ var allResults : [ IndexedToolResult ] = [ ]
93+
94+ if !autoExecute. isEmpty {
95+ let results = try await executeToolsInParallel (
96+ autoExecute. map ( \. call) , context: executionContext, approvalHandler: handler
97+ )
98+ for (position, ( call, result) ) in results. enumerated ( ) {
99+ allResults. append ( IndexedToolResult ( index: autoExecute [ position] . index, call: call, result: result) )
100+ }
101+ }
102+
103+ let ( approved, denied) = try await resolveApprovals (
104+ needsApproval, handler: handler, allowlist: & allowlist, continuation: nil
105+ )
106+ try Task . checkCancellation ( )
107+
108+ allResults. append ( contentsOf: denied)
109+
110+ if !approved. isEmpty {
111+ let results = try await executeToolsInParallel (
112+ approved. map ( \. call) , context: executionContext, approvalHandler: handler
113+ )
114+ for (position, ( call, result) ) in results. enumerated ( ) {
115+ allResults. append ( IndexedToolResult ( index: approved [ position] . index, call: call, result: result) )
116+ }
117+ }
118+
119+ allResults. sort { $0. index < $1. index }
120+ for entry in allResults {
121+ let content = ContextCompactor . truncateToolResult ( entry. result. content, configuration: configuration)
122+ messages. append ( . tool( id: entry. call. id, name: entry. call. name, content: content) )
69123 }
70124 }
71125
72126 func executeStreamingAndAppendResults(
73127 _ calls: [ ToolCall ] , context: C , messages: inout [ ChatMessage ] ,
74- continuation: AsyncThrowingStream < StreamEvent , Error > . Continuation
128+ continuation: AsyncThrowingStream < StreamEvent , Error > . Continuation ,
129+ approvalHandler: ToolApprovalHandler ? = nil , allowlist: inout Set < String >
75130 ) async throws {
76131 guard !calls. isEmpty else { return }
77- let results = try await executeToolsStreaming (
78- calls, context: context. withParentHistory ( messages) , continuation: continuation
132+ let executionContext = context. withParentHistory ( messages)
133+
134+ guard let handler = approvalHandler, configuration. approvalPolicy != . none else {
135+ let results = try await executeToolsStreaming (
136+ calls,
137+ context: executionContext,
138+ continuation: continuation,
139+ approvalHandler: approvalHandler
140+ )
141+ for (call, result) in results {
142+ let content = ContextCompactor . truncateToolResult ( result. content, configuration: configuration)
143+ messages. append ( . tool( id: call. id, name: call. name, content: content) )
144+ }
145+ return
146+ }
147+
148+ var autoExecute : [ IndexedToolCall ] = [ ]
149+ var needsApproval : [ IndexedToolCall ] = [ ]
150+ for (offset, call) in calls. enumerated ( ) {
151+ let indexed = IndexedToolCall ( index: offset, call: call)
152+ if requiresApproval ( call, allowlist: allowlist) {
153+ needsApproval. append ( indexed)
154+ } else {
155+ autoExecute. append ( indexed)
156+ }
157+ }
158+
159+ var allResults : [ IndexedToolResult ] = [ ]
160+
161+ if !autoExecute. isEmpty {
162+ let results = try await executeToolsStreaming (
163+ autoExecute. map ( \. call) , context: executionContext,
164+ continuation: continuation, approvalHandler: handler
165+ )
166+ for (position, ( call, result) ) in results. enumerated ( ) {
167+ allResults. append ( IndexedToolResult ( index: autoExecute [ position] . index, call: call, result: result) )
168+ }
169+ }
170+
171+ let ( approved, denied) = try await resolveApprovals (
172+ needsApproval, handler: handler, allowlist: & allowlist, continuation: continuation
79173 )
80- for (call, result) in results {
81- let content = ContextCompactor . truncateToolResult ( result. content, configuration: configuration)
82- messages. append ( . tool( id: call. id, name: call. name, content: content) )
174+ try Task . checkCancellation ( )
175+
176+ for entry in denied {
177+ continuation. yield ( . toolCallCompleted( id: entry. call. id, name: entry. call. name, result: entry. result) )
178+ allResults. append ( entry)
179+ }
180+
181+ if !approved. isEmpty {
182+ let results = try await executeToolsStreaming (
183+ approved. map ( \. call) , context: executionContext,
184+ continuation: continuation, approvalHandler: handler
185+ )
186+ for (position, ( call, result) ) in results. enumerated ( ) {
187+ allResults. append ( IndexedToolResult ( index: approved [ position] . index, call: call, result: result) )
188+ }
189+ }
190+
191+ allResults. sort { $0. index < $1. index }
192+ for entry in allResults {
193+ let content = ContextCompactor . truncateToolResult ( entry. result. content, configuration: configuration)
194+ messages. append ( . tool( id: entry. call. id, name: entry. call. name, content: content) )
83195 }
84196 }
85197}
0 commit comments