summaryrefslogtreecommitdiff
path: root/packages/server/src
diff options
context:
space:
mode:
authorTaylor Mullen <[email protected]>2025-04-21 14:32:18 -0400
committerN. Taylor Mullen <[email protected]>2025-04-21 14:47:17 -0400
commit738c2692fb9bdeda7c801e7b4e773f81ebc1ded0 (patch)
tree5647ea34e0c18d790b54d7e5f2663a18688823cc /packages/server/src
parent618f8a43cf9402aba7d441341a46a229c92502dd (diff)
Fix confirmations.
- This fixes what it means to get confirmations in GC. Prior to this they had just been accidentally unwired as part of all of the refactorings to turns + to server/core. - The key piece of this is that we wrap the onConfirm in the gemini stream hook in order to resubmit function responses. This isn't 100% ideal but gets the job done for now. - Fixed history not updating properly with confirmations. Fixes https://b.corp.google.com/issues/412323656
Diffstat (limited to 'packages/server/src')
-rw-r--r--packages/server/src/core/turn.ts127
1 files changed, 61 insertions, 66 deletions
diff --git a/packages/server/src/core/turn.ts b/packages/server/src/core/turn.ts
index 0a1c594c..31656466 100644
--- a/packages/server/src/core/turn.ts
+++ b/packages/server/src/core/turn.ts
@@ -130,83 +130,78 @@ export class Turn {
yield event;
}
}
+ }
- // Execute pending tool calls
- const toolPromises = this.pendingToolCalls.map(
- async (pendingToolCall): Promise<ServerToolExecutionOutcome> => {
- const tool = this.availableTools.get(pendingToolCall.name);
- if (!tool) {
- return {
- ...pendingToolCall,
- error: new Error(
- `Tool "${pendingToolCall.name}" not found or not provided to Turn.`,
- ),
- confirmationDetails: undefined,
- };
- }
+ // Execute pending tool calls
+ const toolPromises = this.pendingToolCalls.map(
+ async (pendingToolCall): Promise<ServerToolExecutionOutcome> => {
+ const tool = this.availableTools.get(pendingToolCall.name);
+ if (!tool) {
+ return {
+ ...pendingToolCall,
+ error: new Error(
+ `Tool "${pendingToolCall.name}" not found or not provided to Turn.`,
+ ),
+ confirmationDetails: undefined,
+ };
+ }
- try {
- const confirmationDetails = await tool.shouldConfirmExecute(
- pendingToolCall.args,
- );
- if (confirmationDetails) {
- return { ...pendingToolCall, confirmationDetails };
- } else {
- const result = await tool.execute(pendingToolCall.args);
- return {
- ...pendingToolCall,
- result,
- confirmationDetails: undefined,
- };
- }
- } catch (execError: unknown) {
+ try {
+ const confirmationDetails = await tool.shouldConfirmExecute(
+ pendingToolCall.args,
+ );
+ if (confirmationDetails) {
+ return { ...pendingToolCall, confirmationDetails };
+ } else {
+ const result = await tool.execute(pendingToolCall.args);
return {
...pendingToolCall,
- error: new Error(
- `Tool execution failed: ${execError instanceof Error ? execError.message : String(execError)}`,
- ),
+ result,
confirmationDetails: undefined,
};
}
- },
- );
- const outcomes = await Promise.all(toolPromises);
+ } catch (execError: unknown) {
+ return {
+ ...pendingToolCall,
+ error: new Error(
+ `Tool execution failed: ${execError instanceof Error ? execError.message : String(execError)}`,
+ ),
+ confirmationDetails: undefined,
+ };
+ }
+ },
+ );
+ const outcomes = await Promise.all(toolPromises);
- // Process outcomes and prepare function responses
- this.pendingToolCalls = []; // Clear pending calls for this turn
+ // Process outcomes and prepare function responses
+ this.pendingToolCalls = []; // Clear pending calls for this turn
- for (let i = 0; i < outcomes.length; i++) {
- const outcome = outcomes[i];
- if (outcome.confirmationDetails) {
- this.confirmationDetails.push(outcome.confirmationDetails);
- const serverConfirmationetails: ServerToolCallConfirmationDetails = {
- request: {
- callId: outcome.callId,
- name: outcome.name,
- args: outcome.args,
- },
- details: outcome.confirmationDetails,
- };
- yield {
- type: GeminiEventType.ToolCallConfirmation,
- value: serverConfirmationetails,
- };
- } else {
- const responsePart = this.buildFunctionResponse(outcome);
- this.fnResponses.push(responsePart);
- const responseInfo: ToolCallResponseInfo = {
+ for (const outcome of outcomes) {
+ if (outcome.confirmationDetails) {
+ this.confirmationDetails.push(outcome.confirmationDetails);
+ const serverConfirmationetails: ServerToolCallConfirmationDetails = {
+ request: {
callId: outcome.callId,
- responsePart,
- resultDisplay: outcome.result?.returnDisplay,
- error: outcome.error,
- };
- yield { type: GeminiEventType.ToolCallResponse, value: responseInfo };
- }
+ name: outcome.name,
+ args: outcome.args,
+ },
+ details: outcome.confirmationDetails,
+ };
+ yield {
+ type: GeminiEventType.ToolCallConfirmation,
+ value: serverConfirmationetails,
+ };
+ } else {
+ const responsePart = this.buildFunctionResponse(outcome);
+ this.fnResponses.push(responsePart);
+ const responseInfo: ToolCallResponseInfo = {
+ callId: outcome.callId,
+ responsePart,
+ resultDisplay: outcome.result?.returnDisplay,
+ error: outcome.error,
+ };
+ yield { type: GeminiEventType.ToolCallResponse, value: responseInfo };
}
-
- // If there were function responses, the caller (GeminiService) will loop
- // and call run() again with these responses.
- // If no function responses, the turn ends here.
}
}