Skip to content

Commit 36c179c

Browse files
committed
fix
1 parent b3399fb commit 36c179c

File tree

2 files changed

+26
-21
lines changed

2 files changed

+26
-21
lines changed

packages/agents-core/src/runImplementation.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,24 @@ export async function executeToolsAndSideEffects<TContext>(
559559
);
560560
}
561561

562-
// check if the agent produced any messages
562+
// If the model issued any tool calls or handoffs in this turn,
563+
// we must NOT treat any assistant message in the same turn as the final output.
564+
// We should run the loop again so the model can see the tool results and respond.
565+
const hadToolCallsOrActions =
566+
(processedResponse.functions?.length ?? 0) > 0 ||
567+
(processedResponse.computerActions?.length ?? 0) > 0 ||
568+
(processedResponse.mcpApprovalRequests?.length ?? 0) > 0 ||
569+
(processedResponse.handoffs?.length ?? 0) > 0;
570+
if (hadToolCallsOrActions) {
571+
return new SingleStepResult(
572+
originalInput,
573+
newResponse,
574+
preStepItems,
575+
newItems,
576+
{ type: 'next_step_run_again' },
577+
);
578+
}
579+
// No tool calls/actions in this turn; safe to consider a plain assistant message as final.
563580
const messageItems = newItems.filter(
564581
(item) => item instanceof RunMessageOutputItem,
565582
);

packages/agents-core/test/runImplementation.test.ts

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ describe('executeToolsAndSideEffects', () => {
883883
state = new RunState(new RunContext(), 'test input', TEST_AGENT, 1);
884884
});
885885

886-
it('returns final output for text agent once tools finish in the same turn', async () => {
886+
it('does not finalize when tools are used in the same turn (text output); runs again', async () => {
887887
const textAgent = new Agent({ name: 'TextAgent', outputType: 'text' });
888888
const processedResponse = processModelResponse(
889889
TEST_MODEL_RESPONSE_WITH_FUNCTION,
@@ -906,13 +906,10 @@ describe('executeToolsAndSideEffects', () => {
906906
),
907907
);
908908

909-
expect(result.nextStep.type).toBe('next_step_final_output');
910-
if (result.nextStep.type === 'next_step_final_output') {
911-
expect(result.nextStep.output).toBe('Hello World');
912-
}
909+
expect(result.nextStep.type).toBe('next_step_run_again');
913910
});
914911

915-
it('returns final output for structured agent once tools finish in the same turn', async () => {
912+
it('does not finalize when tools are used in the same turn (structured output); runs again', async () => {
916913
const structuredAgent = new Agent({
917914
name: 'StructuredAgent',
918915
outputType: z.object({
@@ -956,10 +953,7 @@ describe('executeToolsAndSideEffects', () => {
956953
),
957954
);
958955

959-
expect(result.nextStep.type).toBe('next_step_final_output');
960-
if (result.nextStep.type === 'next_step_final_output') {
961-
expect(result.nextStep.output).toBe('{"foo":"bar"}');
962-
}
956+
expect(result.nextStep.type).toBe('next_step_run_again');
963957
});
964958

965959
it('returns final output when text agent has no tools pending', async () => {
@@ -1033,7 +1027,7 @@ describe('executeToolsAndSideEffects', () => {
10331027
}
10341028
});
10351029

1036-
it('returns final output after computer actions complete in the same turn', async () => {
1030+
it('does not finalize after computer actions in the same turn; runs again', async () => {
10371031
const computerAgent = new Agent({
10381032
name: 'ComputerAgent',
10391033
outputType: 'text',
@@ -1093,13 +1087,10 @@ describe('executeToolsAndSideEffects', () => {
10931087
),
10941088
);
10951089

1096-
expect(result.nextStep.type).toBe('next_step_final_output');
1097-
if (result.nextStep.type === 'next_step_final_output') {
1098-
expect(result.nextStep.output).toBe('Hello World');
1099-
}
1090+
expect(result.nextStep.type).toBe('next_step_run_again');
11001091
});
11011092

1102-
it('returns final output when hosted MCP approval resolves immediately', async () => {
1093+
it('does not finalize when hosted MCP approval happens in the same turn; runs again', async () => {
11031094
const approvalAgent = new Agent({ name: 'MCPAgent', outputType: 'text' });
11041095
const mcpTool = hostedMcpTool({
11051096
serverLabel: 'demo_server',
@@ -1155,10 +1146,7 @@ describe('executeToolsAndSideEffects', () => {
11551146
),
11561147
);
11571148

1158-
expect(result.nextStep.type).toBe('next_step_final_output');
1159-
if (result.nextStep.type === 'next_step_final_output') {
1160-
expect(result.nextStep.output).toBe('Hello World');
1161-
}
1149+
expect(result.nextStep.type).toBe('next_step_run_again');
11621150
});
11631151

11641152
it('returns interruption when hosted MCP approval requires user input', async () => {

0 commit comments

Comments
 (0)