Skip to content

Commit fa98a89

Browse files
committed
add more tests
1 parent 852e463 commit fa98a89

File tree

1 file changed

+199
-2
lines changed

1 file changed

+199
-2
lines changed

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

Lines changed: 199 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ import {
2727
executeHandoffCalls,
2828
executeToolsAndSideEffects,
2929
} from '../src/runImplementation';
30-
import { FunctionTool, FunctionToolResult, tool } from '../src/tool';
30+
import {
31+
FunctionTool,
32+
FunctionToolResult,
33+
tool,
34+
computerTool,
35+
hostedMcpTool,
36+
} from '../src/tool';
3137
import { handoff } from '../src/handoff';
3238
import { ModelBehaviorError, UserError } from '../src/errors';
3339
import { Computer } from '../src/computer';
@@ -43,7 +49,6 @@ import {
4349
FakeModelProvider,
4450
fakeModelMessage,
4551
} from './stubs';
46-
import { computerTool } from '../src/tool';
4752
import * as protocol from '../src/types/protocol';
4853
import { Runner } from '../src/run';
4954
import { RunContext } from '../src/runContext';
@@ -1027,4 +1032,196 @@ describe('executeToolsAndSideEffects', () => {
10271032
expect(result.nextStep.output).toBe('');
10281033
}
10291034
});
1035+
1036+
it('returns final output after computer actions complete in the same turn', async () => {
1037+
const computerAgent = new Agent({
1038+
name: 'ComputerAgent',
1039+
outputType: 'text',
1040+
});
1041+
const fakeComputer = {
1042+
environment: 'mac',
1043+
dimensions: [1, 1] as [number, number],
1044+
screenshot: vi.fn().mockResolvedValue('img'),
1045+
click: vi.fn(),
1046+
doubleClick: vi.fn(),
1047+
drag: vi.fn(),
1048+
keypress: vi.fn(),
1049+
move: vi.fn(),
1050+
scroll: vi.fn(),
1051+
type: vi.fn(),
1052+
wait: vi.fn(),
1053+
};
1054+
const computer = computerTool({
1055+
computer: fakeComputer as unknown as Computer,
1056+
});
1057+
const computerCall: protocol.ComputerUseCallItem = {
1058+
type: 'computer_call',
1059+
id: 'comp1',
1060+
callId: 'comp1',
1061+
status: 'completed',
1062+
action: { type: 'screenshot' },
1063+
} as protocol.ComputerUseCallItem;
1064+
1065+
const computerResponse: ModelResponse = {
1066+
output: [computerCall, { ...TEST_MODEL_MESSAGE }],
1067+
usage: new Usage(),
1068+
} as any;
1069+
1070+
const processedResponse = processModelResponse(
1071+
computerResponse,
1072+
computerAgent,
1073+
[computer],
1074+
[],
1075+
);
1076+
1077+
const computerState = new RunState(
1078+
new RunContext(),
1079+
'test input',
1080+
computerAgent,
1081+
1,
1082+
);
1083+
1084+
const result = await withTrace('test', () =>
1085+
executeToolsAndSideEffects(
1086+
computerAgent,
1087+
'test input',
1088+
[],
1089+
computerResponse,
1090+
processedResponse,
1091+
runner,
1092+
computerState,
1093+
),
1094+
);
1095+
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+
}
1100+
});
1101+
1102+
it('returns final output when hosted MCP approval resolves immediately', async () => {
1103+
const approvalAgent = new Agent({ name: 'MCPAgent', outputType: 'text' });
1104+
const mcpTool = hostedMcpTool({
1105+
serverLabel: 'demo_server',
1106+
serverUrl: 'https://example.com',
1107+
requireApproval: {
1108+
always: { toolNames: ['demo_tool'] },
1109+
},
1110+
onApproval: async () => ({ approve: true, reason: 'approved in test' }),
1111+
});
1112+
1113+
const approvalCall: protocol.HostedToolCallItem = {
1114+
type: 'hosted_tool_call',
1115+
id: 'approval1',
1116+
name: 'mcp_approval_request',
1117+
status: 'completed',
1118+
providerData: {
1119+
type: 'mcp_approval_request',
1120+
server_label: 'demo_server',
1121+
name: 'demo_tool',
1122+
id: 'approval1',
1123+
arguments: '{}',
1124+
},
1125+
} as protocol.HostedToolCallItem;
1126+
1127+
const approvalResponse: ModelResponse = {
1128+
output: [approvalCall, { ...TEST_MODEL_MESSAGE }],
1129+
usage: new Usage(),
1130+
} as any;
1131+
1132+
const processedResponse = processModelResponse(
1133+
approvalResponse,
1134+
approvalAgent,
1135+
[mcpTool],
1136+
[],
1137+
);
1138+
1139+
const approvalState = new RunState(
1140+
new RunContext(),
1141+
'test input',
1142+
approvalAgent,
1143+
1,
1144+
);
1145+
1146+
const result = await withTrace('test', () =>
1147+
executeToolsAndSideEffects(
1148+
approvalAgent,
1149+
'test input',
1150+
[],
1151+
approvalResponse,
1152+
processedResponse,
1153+
runner,
1154+
approvalState,
1155+
),
1156+
);
1157+
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+
}
1162+
});
1163+
1164+
it('returns interruption when hosted MCP approval requires user input', async () => {
1165+
const approvalAgent = new Agent({ name: 'MCPAgent', outputType: 'text' });
1166+
const mcpTool = hostedMcpTool({
1167+
serverLabel: 'demo_server',
1168+
serverUrl: 'https://example.com',
1169+
requireApproval: {
1170+
always: { toolNames: ['demo_tool'] },
1171+
},
1172+
});
1173+
1174+
const approvalCall: protocol.HostedToolCallItem = {
1175+
type: 'hosted_tool_call',
1176+
id: 'approval1',
1177+
name: 'mcp_approval_request',
1178+
status: 'completed',
1179+
providerData: {
1180+
type: 'mcp_approval_request',
1181+
server_label: 'demo_server',
1182+
name: 'demo_tool',
1183+
id: 'approval1',
1184+
arguments: '{}',
1185+
},
1186+
} as protocol.HostedToolCallItem;
1187+
1188+
const approvalResponse: ModelResponse = {
1189+
output: [approvalCall, { ...TEST_MODEL_MESSAGE }],
1190+
usage: new Usage(),
1191+
} as any;
1192+
1193+
const processedResponse = processModelResponse(
1194+
approvalResponse,
1195+
approvalAgent,
1196+
[mcpTool],
1197+
[],
1198+
);
1199+
1200+
const approvalState = new RunState(
1201+
new RunContext(),
1202+
'test input',
1203+
approvalAgent,
1204+
1,
1205+
);
1206+
1207+
const result = await withTrace('test', () =>
1208+
executeToolsAndSideEffects(
1209+
approvalAgent,
1210+
'test input',
1211+
[],
1212+
approvalResponse,
1213+
processedResponse,
1214+
runner,
1215+
approvalState,
1216+
),
1217+
);
1218+
1219+
expect(result.nextStep.type).toBe('next_step_interruption');
1220+
if (result.nextStep.type === 'next_step_interruption') {
1221+
expect(result.nextStep.data.interruptions).toHaveLength(1);
1222+
expect(result.nextStep.data.interruptions[0].rawItem).toMatchObject({
1223+
providerData: { id: 'approval1', type: 'mcp_approval_request' },
1224+
});
1225+
}
1226+
});
10301227
});

0 commit comments

Comments
 (0)