@equinor/fusion-framework-cli-plugin-copilot 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/dist/esm/commands/app/command.js +176 -0
- package/dist/esm/commands/app/command.js.map +1 -0
- package/dist/esm/commands/app/eval.js +92 -0
- package/dist/esm/commands/app/eval.js.map +1 -0
- package/dist/esm/commands/app/format.js +93 -0
- package/dist/esm/commands/app/format.js.map +1 -0
- package/dist/esm/commands/app/login.js +71 -0
- package/dist/esm/commands/app/login.js.map +1 -0
- package/dist/esm/commands/app/prompts/execute-step.prompt.js +44 -0
- package/dist/esm/commands/app/prompts/execute-step.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/judge.prompt.js +116 -0
- package/dist/esm/commands/app/prompts/judge.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/plan.prompt.js +47 -0
- package/dist/esm/commands/app/prompts/plan.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/step.prompt.js +42 -0
- package/dist/esm/commands/app/prompts/step.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/system.dev-server.prompt.js +21 -0
- package/dist/esm/commands/app/prompts/system.dev-server.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/system.prompt.js +77 -0
- package/dist/esm/commands/app/prompts/system.prompt.js.map +1 -0
- package/dist/esm/commands/app/server.js +83 -0
- package/dist/esm/commands/app/server.js.map +1 -0
- package/dist/esm/commands/app/session-logger.js +171 -0
- package/dist/esm/commands/app/session-logger.js.map +1 -0
- package/dist/esm/commands/app/tools/click.js +27 -0
- package/dist/esm/commands/app/tools/click.js.map +1 -0
- package/dist/esm/commands/app/tools/errors.js +24 -0
- package/dist/esm/commands/app/tools/errors.js.map +1 -0
- package/dist/esm/commands/app/tools/eval-js.js +36 -0
- package/dist/esm/commands/app/tools/eval-js.js.map +1 -0
- package/dist/esm/commands/app/tools/fill.js +28 -0
- package/dist/esm/commands/app/tools/fill.js.map +1 -0
- package/dist/esm/commands/app/tools/find.js +44 -0
- package/dist/esm/commands/app/tools/find.js.map +1 -0
- package/dist/esm/commands/app/tools/get-styles.js +36 -0
- package/dist/esm/commands/app/tools/get-styles.js.map +1 -0
- package/dist/esm/commands/app/tools/get-url.js +24 -0
- package/dist/esm/commands/app/tools/get-url.js.map +1 -0
- package/dist/esm/commands/app/tools/go-back.js +15 -0
- package/dist/esm/commands/app/tools/go-back.js.map +1 -0
- package/dist/esm/commands/app/tools/hover.js +24 -0
- package/dist/esm/commands/app/tools/hover.js.map +1 -0
- package/dist/esm/commands/app/tools/index.js +32 -0
- package/dist/esm/commands/app/tools/index.js.map +1 -0
- package/dist/esm/commands/app/tools/navigate.js +31 -0
- package/dist/esm/commands/app/tools/navigate.js.map +1 -0
- package/dist/esm/commands/app/tools/press-key.js +24 -0
- package/dist/esm/commands/app/tools/press-key.js.map +1 -0
- package/dist/esm/commands/app/tools/read-file.js +98 -0
- package/dist/esm/commands/app/tools/read-file.js.map +1 -0
- package/dist/esm/commands/app/tools/registry.js +34 -0
- package/dist/esm/commands/app/tools/registry.js.map +1 -0
- package/dist/esm/commands/app/tools/reload.js +15 -0
- package/dist/esm/commands/app/tools/reload.js.map +1 -0
- package/dist/esm/commands/app/tools/screenshot.js +133 -0
- package/dist/esm/commands/app/tools/screenshot.js.map +1 -0
- package/dist/esm/commands/app/tools/scroll.js +41 -0
- package/dist/esm/commands/app/tools/scroll.js.map +1 -0
- package/dist/esm/commands/app/tools/select.js +25 -0
- package/dist/esm/commands/app/tools/select.js.map +1 -0
- package/dist/esm/commands/app/tools/snapshot.js +24 -0
- package/dist/esm/commands/app/tools/snapshot.js.map +1 -0
- package/dist/esm/commands/app/tools/type-text.js +29 -0
- package/dist/esm/commands/app/tools/type-text.js.map +1 -0
- package/dist/esm/commands/app/tools/types.js +2 -0
- package/dist/esm/commands/app/tools/types.js.map +1 -0
- package/dist/esm/commands/app/tools/wait.js +39 -0
- package/dist/esm/commands/app/tools/wait.js.map +1 -0
- package/dist/esm/commands/app/tools/write-file.js +98 -0
- package/dist/esm/commands/app/tools/write-file.js.map +1 -0
- package/dist/esm/commands/app/types.js +2 -0
- package/dist/esm/commands/app/types.js.map +1 -0
- package/dist/esm/eval-resolve.js +83 -0
- package/dist/esm/eval-resolve.js.map +1 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/utils/agent-browser.js +67 -0
- package/dist/esm/utils/agent-browser.js.map +1 -0
- package/dist/esm/utils/browser-poll.js +26 -0
- package/dist/esm/utils/browser-poll.js.map +1 -0
- package/dist/esm/utils/daemon.js +83 -0
- package/dist/esm/utils/daemon.js.map +1 -0
- package/dist/esm/utils/index.js +6 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/dist/esm/utils/process.js +30 -0
- package/dist/esm/utils/process.js.map +1 -0
- package/dist/esm/utils/server.js +65 -0
- package/dist/esm/utils/server.js.map +1 -0
- package/dist/esm/version.js +3 -0
- package/dist/esm/version.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/commands/app/command.d.ts +7 -0
- package/dist/types/commands/app/eval.d.ts +9 -0
- package/dist/types/commands/app/format.d.ts +14 -0
- package/dist/types/commands/app/login.d.ts +14 -0
- package/dist/types/commands/app/prompts/execute-step.prompt.d.ts +13 -0
- package/dist/types/commands/app/prompts/judge.prompt.d.ts +15 -0
- package/dist/types/commands/app/prompts/plan.prompt.d.ts +13 -0
- package/dist/types/commands/app/prompts/step.prompt.d.ts +12 -0
- package/dist/types/commands/app/prompts/system.dev-server.prompt.d.ts +12 -0
- package/dist/types/commands/app/prompts/system.prompt.d.ts +12 -0
- package/dist/types/commands/app/server.d.ts +26 -0
- package/dist/types/commands/app/session-logger.d.ts +14 -0
- package/dist/types/commands/app/tools/click.d.ts +9 -0
- package/dist/types/commands/app/tools/errors.d.ts +9 -0
- package/dist/types/commands/app/tools/eval-js.d.ts +13 -0
- package/dist/types/commands/app/tools/fill.d.ts +9 -0
- package/dist/types/commands/app/tools/find.d.ts +9 -0
- package/dist/types/commands/app/tools/get-styles.d.ts +13 -0
- package/dist/types/commands/app/tools/get-url.d.ts +9 -0
- package/dist/types/commands/app/tools/go-back.d.ts +9 -0
- package/dist/types/commands/app/tools/hover.d.ts +9 -0
- package/dist/types/commands/app/tools/index.d.ts +9 -0
- package/dist/types/commands/app/tools/navigate.d.ts +9 -0
- package/dist/types/commands/app/tools/press-key.d.ts +9 -0
- package/dist/types/commands/app/tools/read-file.d.ts +13 -0
- package/dist/types/commands/app/tools/registry.d.ts +28 -0
- package/dist/types/commands/app/tools/reload.d.ts +9 -0
- package/dist/types/commands/app/tools/screenshot.d.ts +9 -0
- package/dist/types/commands/app/tools/scroll.d.ts +9 -0
- package/dist/types/commands/app/tools/select.d.ts +9 -0
- package/dist/types/commands/app/tools/snapshot.d.ts +9 -0
- package/dist/types/commands/app/tools/type-text.d.ts +9 -0
- package/dist/types/commands/app/tools/types.d.ts +12 -0
- package/dist/types/commands/app/tools/wait.d.ts +9 -0
- package/dist/types/commands/app/tools/write-file.d.ts +19 -0
- package/dist/types/commands/app/types.d.ts +68 -0
- package/dist/types/eval-resolve.d.ts +31 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/utils/agent-browser.d.ts +26 -0
- package/dist/types/utils/browser-poll.d.ts +9 -0
- package/dist/types/utils/daemon.d.ts +16 -0
- package/dist/types/utils/index.d.ts +5 -0
- package/dist/types/utils/process.d.ts +21 -0
- package/dist/types/utils/server.d.ts +26 -0
- package/dist/types/version.d.ts +1 -0
- package/package.json +57 -0
- package/src/commands/app/command.ts +204 -0
- package/src/commands/app/eval.ts +114 -0
- package/src/commands/app/format.ts +110 -0
- package/src/commands/app/login.ts +92 -0
- package/src/commands/app/prompts/execute-step.prompt.ts +48 -0
- package/src/commands/app/prompts/judge.prompt.ts +121 -0
- package/src/commands/app/prompts/plan.prompt.ts +48 -0
- package/src/commands/app/prompts/step.prompt.ts +45 -0
- package/src/commands/app/prompts/system.dev-server.prompt.ts +22 -0
- package/src/commands/app/prompts/system.prompt.ts +78 -0
- package/src/commands/app/server.ts +105 -0
- package/src/commands/app/session-logger.ts +187 -0
- package/src/commands/app/tools/click.ts +28 -0
- package/src/commands/app/tools/errors.ts +26 -0
- package/src/commands/app/tools/eval-js.ts +40 -0
- package/src/commands/app/tools/fill.ts +29 -0
- package/src/commands/app/tools/find.ts +49 -0
- package/src/commands/app/tools/get-styles.ts +40 -0
- package/src/commands/app/tools/get-url.ts +26 -0
- package/src/commands/app/tools/go-back.ts +16 -0
- package/src/commands/app/tools/hover.ts +25 -0
- package/src/commands/app/tools/index.ts +57 -0
- package/src/commands/app/tools/navigate.ts +32 -0
- package/src/commands/app/tools/press-key.ts +25 -0
- package/src/commands/app/tools/read-file.ts +113 -0
- package/src/commands/app/tools/registry.ts +38 -0
- package/src/commands/app/tools/reload.ts +16 -0
- package/src/commands/app/tools/screenshot.ts +168 -0
- package/src/commands/app/tools/scroll.ts +45 -0
- package/src/commands/app/tools/select.ts +26 -0
- package/src/commands/app/tools/snapshot.ts +27 -0
- package/src/commands/app/tools/type-text.ts +30 -0
- package/src/commands/app/tools/types.ts +15 -0
- package/src/commands/app/tools/wait.ts +43 -0
- package/src/commands/app/tools/write-file.ts +104 -0
- package/src/commands/app/types.ts +76 -0
- package/src/eval-resolve.ts +113 -0
- package/src/index.ts +20 -0
- package/src/utils/agent-browser.ts +66 -0
- package/src/utils/browser-poll.ts +27 -0
- package/src/utils/daemon.ts +84 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/process.ts +33 -0
- package/src/utils/server.ts +66 -0
- package/src/version.ts +2 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates the computed CSS styles inspection tool.
|
|
8
|
+
*
|
|
9
|
+
* Use this tool instead of (or alongside) screenshots to verify color and layout criteria.
|
|
10
|
+
* It returns the actual browser-computed style values for a given selector, which cannot
|
|
11
|
+
* be misread or hallucinated the way image data can.
|
|
12
|
+
*
|
|
13
|
+
* @param context - Shared browser tool execution context
|
|
14
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
15
|
+
* @returns Copilot tool definition for retrieving computed CSS style properties
|
|
16
|
+
*/
|
|
17
|
+
export function createGetStylesTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
18
|
+
return defineTool('browser_get_styles', {
|
|
19
|
+
description:
|
|
20
|
+
'Get computed CSS styles of an element. Use this for verifying visual properties such as background-color, color, or font-size WITHOUT relying on screenshot interpretation. Returns the computed style object as plain text.',
|
|
21
|
+
parameters: {
|
|
22
|
+
type: 'object' as const,
|
|
23
|
+
properties: {
|
|
24
|
+
selector: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description:
|
|
27
|
+
'Element ref (e.g. @e1), CSS selector, or tag name to inspect (e.g. "body", "main", "h1")',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ['selector'],
|
|
31
|
+
},
|
|
32
|
+
handler: async (args) => {
|
|
33
|
+
const { selector } = args as { selector: string };
|
|
34
|
+
const output = context.invoke(['get', 'styles', selector]);
|
|
35
|
+
const safeSelector = selector.replace(/[^a-z0-9]/gi, '_').slice(0, 40);
|
|
36
|
+
writeFileSync(join(context.outDir, `styles-${safeSelector}.txt`), output, 'utf-8');
|
|
37
|
+
return output;
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates the current URL inspection tool.
|
|
8
|
+
*
|
|
9
|
+
* @param context - Shared browser tool execution context
|
|
10
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
11
|
+
* @returns Copilot tool definition for retrieving the current page URL
|
|
12
|
+
*/
|
|
13
|
+
export function createGetUrlTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
14
|
+
return defineTool('browser_get_url', {
|
|
15
|
+
description: 'Get the current page URL.',
|
|
16
|
+
parameters: {
|
|
17
|
+
type: 'object' as const,
|
|
18
|
+
properties: {},
|
|
19
|
+
},
|
|
20
|
+
handler: async () => {
|
|
21
|
+
const output = context.invoke(['get', 'url']);
|
|
22
|
+
writeFileSync(join(context.outDir, 'url.txt'), output, 'utf-8');
|
|
23
|
+
return output;
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the back navigation tool.
|
|
5
|
+
*
|
|
6
|
+
* @param context - Shared browser tool execution context
|
|
7
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
8
|
+
* @returns Copilot tool definition for going back in browser history
|
|
9
|
+
*/
|
|
10
|
+
export function createGoBackTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
11
|
+
return defineTool('browser_go_back', {
|
|
12
|
+
description: 'Navigate back in browser history.',
|
|
13
|
+
parameters: { type: 'object' as const, properties: {} },
|
|
14
|
+
handler: async () => context.invoke(['back']),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the hover interaction tool.
|
|
5
|
+
*
|
|
6
|
+
* @param context - Shared browser tool execution context
|
|
7
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
8
|
+
* @returns Copilot tool definition for hovering a page element
|
|
9
|
+
*/
|
|
10
|
+
export function createHoverTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
11
|
+
return defineTool('browser_hover', {
|
|
12
|
+
description: 'Hover over an element.',
|
|
13
|
+
parameters: {
|
|
14
|
+
type: 'object' as const,
|
|
15
|
+
properties: {
|
|
16
|
+
target: { type: 'string', description: 'Element ref or CSS selector' },
|
|
17
|
+
},
|
|
18
|
+
required: ['target'],
|
|
19
|
+
},
|
|
20
|
+
handler: async (args) => {
|
|
21
|
+
const { target } = args as { target: string };
|
|
22
|
+
return context.invoke(['hover', target]);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createClickTool } from './click.js';
|
|
2
|
+
import { createErrorsTool } from './errors.js';
|
|
3
|
+
import { createFillTool } from './fill.js';
|
|
4
|
+
import { createFindTool } from './find.js';
|
|
5
|
+
import { createGetUrlTool } from './get-url.js';
|
|
6
|
+
import { createGoBackTool } from './go-back.js';
|
|
7
|
+
import { createHoverTool } from './hover.js';
|
|
8
|
+
import { createNavigateTool } from './navigate.js';
|
|
9
|
+
import { createPressKeyTool } from './press-key.js';
|
|
10
|
+
import { createReloadTool } from './reload.js';
|
|
11
|
+
import { createEvalJsTool } from './eval-js.js';
|
|
12
|
+
import { createGetStylesTool } from './get-styles.js';
|
|
13
|
+
import { AgentBrowserToolRegistry } from './registry.js';
|
|
14
|
+
import { createScreenshotTool } from './screenshot.js';
|
|
15
|
+
import { createScrollTool } from './scroll.js';
|
|
16
|
+
import { createSelectTool } from './select.js';
|
|
17
|
+
import { createSnapshotTool } from './snapshot.js';
|
|
18
|
+
import { createTypeTextTool } from './type-text.js';
|
|
19
|
+
import type { AgentBrowserToolContext, AgentBrowserToolList, DefineTool } from './types.js';
|
|
20
|
+
import { createWaitTool } from './wait.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates the full set of Copilot browser tools for an eval session.
|
|
24
|
+
*
|
|
25
|
+
* @param context - Shared execution context for all browser tool wrappers
|
|
26
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
27
|
+
* @returns Registered browser tools in the order they should be exposed to the model
|
|
28
|
+
*/
|
|
29
|
+
export function createAgentBrowserTools(
|
|
30
|
+
context: AgentBrowserToolContext,
|
|
31
|
+
defineTool: DefineTool,
|
|
32
|
+
): AgentBrowserToolList {
|
|
33
|
+
const agentBrowser = new AgentBrowserToolRegistry();
|
|
34
|
+
|
|
35
|
+
agentBrowser.addTools(
|
|
36
|
+
createNavigateTool(context, defineTool),
|
|
37
|
+
createSnapshotTool(context, defineTool),
|
|
38
|
+
createScreenshotTool(context, defineTool),
|
|
39
|
+
createGetStylesTool(context, defineTool),
|
|
40
|
+
createEvalJsTool(context, defineTool),
|
|
41
|
+
createClickTool(context, defineTool),
|
|
42
|
+
createFillTool(context, defineTool),
|
|
43
|
+
createTypeTextTool(context, defineTool),
|
|
44
|
+
createPressKeyTool(context, defineTool),
|
|
45
|
+
createHoverTool(context, defineTool),
|
|
46
|
+
createSelectTool(context, defineTool),
|
|
47
|
+
createScrollTool(context, defineTool),
|
|
48
|
+
createWaitTool(context, defineTool),
|
|
49
|
+
createFindTool(context, defineTool),
|
|
50
|
+
createErrorsTool(context, defineTool),
|
|
51
|
+
createGetUrlTool(context, defineTool),
|
|
52
|
+
createGoBackTool(context, defineTool),
|
|
53
|
+
createReloadTool(context, defineTool),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return agentBrowser.getTools();
|
|
57
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { clearMsalInteraction } from '../../../utils/index.js';
|
|
2
|
+
|
|
3
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates the browser navigation tool.
|
|
7
|
+
*
|
|
8
|
+
* @param context - Shared browser tool execution context
|
|
9
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
10
|
+
* @returns Copilot tool definition for opening a URL
|
|
11
|
+
*/
|
|
12
|
+
export function createNavigateTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
13
|
+
return defineTool('browser_navigate', {
|
|
14
|
+
description: 'Navigate the browser to a URL. Use this to open the application or follow links.',
|
|
15
|
+
parameters: {
|
|
16
|
+
type: 'object' as const,
|
|
17
|
+
properties: {
|
|
18
|
+
url: { type: 'string', description: 'The URL to navigate to' },
|
|
19
|
+
},
|
|
20
|
+
required: ['url'],
|
|
21
|
+
},
|
|
22
|
+
handler: async (args) => {
|
|
23
|
+
const { url } = args as { url: string };
|
|
24
|
+
try {
|
|
25
|
+
const result = context.invoke(['open', url], 120_000);
|
|
26
|
+
return result || 'Navigation complete';
|
|
27
|
+
} finally {
|
|
28
|
+
clearMsalInteraction();
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the keyboard press tool.
|
|
5
|
+
*
|
|
6
|
+
* @param context - Shared browser tool execution context
|
|
7
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
8
|
+
* @returns Copilot tool definition for pressing a single key
|
|
9
|
+
*/
|
|
10
|
+
export function createPressKeyTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
11
|
+
return defineTool('browser_press_key', {
|
|
12
|
+
description: 'Press a keyboard key (Enter, Tab, Escape, ArrowDown, etc.).',
|
|
13
|
+
parameters: {
|
|
14
|
+
type: 'object' as const,
|
|
15
|
+
properties: {
|
|
16
|
+
key: { type: 'string', description: 'Key name (e.g. Enter, Tab, Escape)' },
|
|
17
|
+
},
|
|
18
|
+
required: ['key'],
|
|
19
|
+
},
|
|
20
|
+
handler: async (args) => {
|
|
21
|
+
const { key } = args as { key: string };
|
|
22
|
+
return context.invoke(['press', key]);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { basename, extname, isAbsolute, join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import type { ToolResultObject } from '@github/copilot-sdk';
|
|
5
|
+
|
|
6
|
+
import type { DefineTool } from './types.js';
|
|
7
|
+
|
|
8
|
+
/** Image extensions that should be returned as vision-capable binary results. */
|
|
9
|
+
const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp']);
|
|
10
|
+
|
|
11
|
+
/** MIME types for supported image formats. */
|
|
12
|
+
const IMAGE_MIME: Record<string, string> = {
|
|
13
|
+
'.png': 'image/png',
|
|
14
|
+
'.jpg': 'image/jpeg',
|
|
15
|
+
'.jpeg': 'image/jpeg',
|
|
16
|
+
'.gif': 'image/gif',
|
|
17
|
+
'.webp': 'image/webp',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolves a path within a base directory, rejecting escapes.
|
|
22
|
+
*
|
|
23
|
+
* @param baseDir - The allowed root directory
|
|
24
|
+
* @param input - The user-provided path (relative or absolute within baseDir)
|
|
25
|
+
* @returns The resolved absolute path
|
|
26
|
+
* @throws {Error} When the path escapes the base directory
|
|
27
|
+
*/
|
|
28
|
+
function resolveSafe(baseDir: string, input: string): string {
|
|
29
|
+
const relative = isAbsolute(input)
|
|
30
|
+
? input.startsWith(baseDir + '/') || input.startsWith(baseDir + '\\')
|
|
31
|
+
? input.slice(baseDir.length + 1)
|
|
32
|
+
: (() => {
|
|
33
|
+
throw new Error(`Absolute path outside output directory: ${input}`);
|
|
34
|
+
})()
|
|
35
|
+
: input;
|
|
36
|
+
const resolved = join(baseDir, relative);
|
|
37
|
+
if (!resolved.startsWith(baseDir)) {
|
|
38
|
+
throw new Error(`Path escapes the output directory: ${input}`);
|
|
39
|
+
}
|
|
40
|
+
return resolved;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates the read-file tool so the judge agent can inspect evidence artifacts.
|
|
45
|
+
*
|
|
46
|
+
* For image files the tool returns a `ToolBinaryResult` with base64-encoded
|
|
47
|
+
* data that multimodal models can view directly. Text files are returned
|
|
48
|
+
* as plain text. Directories return a listing of their contents.
|
|
49
|
+
*
|
|
50
|
+
* @param baseDir - The run output directory that scopes all reads
|
|
51
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
52
|
+
* @returns Copilot tool definition for reading evidence files
|
|
53
|
+
*/
|
|
54
|
+
export function createReadFileTool(baseDir: string, defineTool: DefineTool) {
|
|
55
|
+
return defineTool('read_file', {
|
|
56
|
+
description:
|
|
57
|
+
'Read a file or list a directory from the output directory. ' +
|
|
58
|
+
'Image files (png, jpg, jpeg, gif, webp) are returned as inline images the model can see. ' +
|
|
59
|
+
'Text files are returned as plain text. Directories return a file listing.',
|
|
60
|
+
parameters: {
|
|
61
|
+
type: 'object' as const,
|
|
62
|
+
properties: {
|
|
63
|
+
path: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description:
|
|
66
|
+
'File or directory path relative to the output directory (e.g. "evidence/step-hello.png" or "executions.jsonl")',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
required: ['path'],
|
|
70
|
+
},
|
|
71
|
+
handler: async (args): Promise<string | ToolResultObject> => {
|
|
72
|
+
const { path } = args as { path: string };
|
|
73
|
+
const resolved = resolveSafe(baseDir, path);
|
|
74
|
+
|
|
75
|
+
if (!existsSync(resolved)) {
|
|
76
|
+
return `File not found: ${path}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const stat = statSync(resolved);
|
|
80
|
+
|
|
81
|
+
// ── Directory listing ──
|
|
82
|
+
if (stat.isDirectory()) {
|
|
83
|
+
const entries = readdirSync(resolved).sort();
|
|
84
|
+
return entries.length > 0
|
|
85
|
+
? `Directory ${path}/:\n${entries.join('\n')}`
|
|
86
|
+
: `Directory ${path}/ is empty`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const ext = extname(resolved).toLowerCase();
|
|
90
|
+
|
|
91
|
+
// ── Image: return as binary vision result ──
|
|
92
|
+
if (IMAGE_EXTENSIONS.has(ext)) {
|
|
93
|
+
const mime = IMAGE_MIME[ext] ?? 'application/octet-stream';
|
|
94
|
+
return {
|
|
95
|
+
textResultForLlm: `Image file: ${path} (${mime}, ${stat.size} bytes). Inspect the attached image.`,
|
|
96
|
+
binaryResultsForLlm: [
|
|
97
|
+
{
|
|
98
|
+
data: readFileSync(resolved, 'base64'),
|
|
99
|
+
mimeType: mime,
|
|
100
|
+
type: 'image',
|
|
101
|
+
description: `Evidence artifact: ${basename(resolved)}`,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
resultType: 'success',
|
|
105
|
+
toolTelemetry: {},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── Text file ──
|
|
110
|
+
return readFileSync(resolved, 'utf-8');
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AgentBrowserToolList } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ordered collection of agent-browser tool registrations for a Copilot session.
|
|
5
|
+
*
|
|
6
|
+
* Tools are exposed to the model in insertion order. Use {@link addTools} to
|
|
7
|
+
* register multiple tools at once during session setup.
|
|
8
|
+
*/
|
|
9
|
+
export class AgentBrowserToolRegistry {
|
|
10
|
+
private readonly tools: AgentBrowserToolList = [];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Adds a tool to the registry.
|
|
14
|
+
*
|
|
15
|
+
* @param tool - Tool definition created with the Copilot SDK `defineTool` helper
|
|
16
|
+
*/
|
|
17
|
+
addTool(tool: AgentBrowserToolList[number]): void {
|
|
18
|
+
this.tools.push(tool);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Adds multiple tools to the registry in order.
|
|
23
|
+
*
|
|
24
|
+
* @param tools - Tool definitions created with the Copilot SDK `defineTool` helper
|
|
25
|
+
*/
|
|
26
|
+
addTools(...tools: AgentBrowserToolList): void {
|
|
27
|
+
this.tools.push(...tools);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns a shallow copy of the registered tools in insertion order.
|
|
32
|
+
*
|
|
33
|
+
* @returns The registered Copilot SDK tool list
|
|
34
|
+
*/
|
|
35
|
+
getTools(): AgentBrowserToolList {
|
|
36
|
+
return [...this.tools];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the page reload tool.
|
|
5
|
+
*
|
|
6
|
+
* @param context - Shared browser tool execution context
|
|
7
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
8
|
+
* @returns Copilot tool definition for reloading the current page
|
|
9
|
+
*/
|
|
10
|
+
export function createReloadTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
11
|
+
return defineTool('browser_reload', {
|
|
12
|
+
description: 'Reload the current page.',
|
|
13
|
+
parameters: { type: 'object' as const, properties: {} },
|
|
14
|
+
handler: async () => context.invoke(['reload']),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { basename, extname, join, relative, resolve, sep } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import type { ToolResultObject } from '@github/copilot-sdk';
|
|
5
|
+
|
|
6
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the requested screenshot format to a supported CLI value.
|
|
10
|
+
*
|
|
11
|
+
* @param format - Optional model-provided image format
|
|
12
|
+
* @returns Validated format string, defaulting to `jpeg`
|
|
13
|
+
*/
|
|
14
|
+
function resolveScreenshotFormat(format: string | undefined): 'jpeg' | 'png' {
|
|
15
|
+
return format === 'png' ? 'png' : 'jpeg';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Ensures the screenshot artifact filename extension matches the selected image format.
|
|
20
|
+
*
|
|
21
|
+
* @param requestedPath - Optional model-provided filename
|
|
22
|
+
* @param format - Selected image format
|
|
23
|
+
* @returns Safe filename with the correct extension, or `undefined` when no path was provided
|
|
24
|
+
*/
|
|
25
|
+
function resolveScreenshotArtifactName(
|
|
26
|
+
requestedPath: string | undefined,
|
|
27
|
+
format: 'jpeg' | 'png',
|
|
28
|
+
): string | undefined {
|
|
29
|
+
if (!requestedPath) return undefined;
|
|
30
|
+
const fileName = basename(requestedPath);
|
|
31
|
+
const currentExtension = extname(fileName).toLowerCase();
|
|
32
|
+
const expectedExtension = format === 'jpeg' ? '.jpg' : '.png';
|
|
33
|
+
|
|
34
|
+
if (currentExtension === expectedExtension) {
|
|
35
|
+
return fileName;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (currentExtension.length > 0) {
|
|
39
|
+
return `${fileName.slice(0, -currentExtension.length)}${expectedExtension}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return `${fileName}${expectedExtension}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolves an optional screenshot directory to a safe location within the eval evidence directory.
|
|
47
|
+
*
|
|
48
|
+
* @param evidenceDir - Base evidence directory for the current eval run
|
|
49
|
+
* @param requestedDirectory - Optional model-provided screenshot directory
|
|
50
|
+
* @returns Absolute screenshot directory constrained to the evidence directory tree
|
|
51
|
+
*/
|
|
52
|
+
function resolveScreenshotDirectory(
|
|
53
|
+
evidenceDir: string,
|
|
54
|
+
requestedDirectory: string | undefined,
|
|
55
|
+
): string {
|
|
56
|
+
if (!requestedDirectory) {
|
|
57
|
+
return evidenceDir;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const resolvedDirectory = resolve(evidenceDir, requestedDirectory);
|
|
61
|
+
const relativeDirectory = relative(evidenceDir, resolvedDirectory);
|
|
62
|
+
const isOutsideEvidenceDirectory =
|
|
63
|
+
relativeDirectory === '..' || relativeDirectory.startsWith(`..${sep}`);
|
|
64
|
+
|
|
65
|
+
return isOutsideEvidenceDirectory ? evidenceDir : resolvedDirectory;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Builds a binary screenshot tool result that multimodal models can inspect directly.
|
|
70
|
+
*
|
|
71
|
+
* @param screenshotPath - Saved screenshot artifact path
|
|
72
|
+
* @param format - Image format used for the screenshot
|
|
73
|
+
* @returns Tool result object containing both text and inline image data
|
|
74
|
+
*/
|
|
75
|
+
function buildScreenshotResult(screenshotPath: string, format: 'jpeg' | 'png'): ToolResultObject {
|
|
76
|
+
return {
|
|
77
|
+
textResultForLlm: `Saved screenshot to ${screenshotPath}. Inspect the attached image for visual evidence.`,
|
|
78
|
+
binaryResultsForLlm: [
|
|
79
|
+
{
|
|
80
|
+
data: readFileSync(screenshotPath, 'base64'),
|
|
81
|
+
mimeType: format === 'jpeg' ? 'image/jpeg' : 'image/png',
|
|
82
|
+
type: 'image',
|
|
83
|
+
description: `Screenshot artifact: ${basename(screenshotPath)}`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
resultType: 'success',
|
|
87
|
+
toolTelemetry: {},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Creates the screenshot capture tool.
|
|
93
|
+
*
|
|
94
|
+
* @param context - Shared browser tool execution context
|
|
95
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
96
|
+
* @returns Copilot tool definition for saving an image screenshot and returning it to the model
|
|
97
|
+
*/
|
|
98
|
+
export function createScreenshotTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
99
|
+
return defineTool('browser_screenshot', {
|
|
100
|
+
description:
|
|
101
|
+
'Capture a screenshot of the current page and return the image to the model. Use this for visual assertions such as colors, spacing, and layout.',
|
|
102
|
+
parameters: {
|
|
103
|
+
type: 'object' as const,
|
|
104
|
+
properties: {
|
|
105
|
+
path: {
|
|
106
|
+
type: 'string',
|
|
107
|
+
description:
|
|
108
|
+
'Optional filename for the screenshot artifact (for example: "landing-page.jpg")',
|
|
109
|
+
},
|
|
110
|
+
annotate: {
|
|
111
|
+
type: 'boolean',
|
|
112
|
+
description: 'Overlay numbered labels on interactive elements in the screenshot.',
|
|
113
|
+
},
|
|
114
|
+
format: {
|
|
115
|
+
type: 'string',
|
|
116
|
+
description: 'Image format: "jpeg" (default) or "png"',
|
|
117
|
+
},
|
|
118
|
+
quality: {
|
|
119
|
+
type: 'number',
|
|
120
|
+
description: 'JPEG quality from 0-100. Recommended/default is 60 when format is "jpeg".',
|
|
121
|
+
},
|
|
122
|
+
screenshotDir: {
|
|
123
|
+
type: 'string',
|
|
124
|
+
description:
|
|
125
|
+
'Default screenshot output directory, scoped inside the eval evidence folder.',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
handler: async (args) => {
|
|
130
|
+
const { path, annotate, format, quality, screenshotDir } = args as {
|
|
131
|
+
path?: string;
|
|
132
|
+
annotate?: boolean;
|
|
133
|
+
format?: string;
|
|
134
|
+
quality?: number;
|
|
135
|
+
screenshotDir?: string;
|
|
136
|
+
};
|
|
137
|
+
const screenshotFormat = resolveScreenshotFormat(format);
|
|
138
|
+
const screenshotDirectory = resolveScreenshotDirectory(context.outDir, screenshotDir);
|
|
139
|
+
mkdirSync(screenshotDirectory, { recursive: true });
|
|
140
|
+
const artifactName = resolveScreenshotArtifactName(path, screenshotFormat);
|
|
141
|
+
const screenshotPath = join(
|
|
142
|
+
screenshotDirectory,
|
|
143
|
+
artifactName ?? `screenshot-${Date.now()}.${screenshotFormat === 'jpeg' ? 'jpg' : 'png'}`,
|
|
144
|
+
);
|
|
145
|
+
const command = [
|
|
146
|
+
'screenshot',
|
|
147
|
+
'--screenshot-format',
|
|
148
|
+
screenshotFormat,
|
|
149
|
+
'--screenshot-dir',
|
|
150
|
+
screenshotDirectory,
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
if (annotate) {
|
|
154
|
+
command.push('--annotate');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (screenshotFormat === 'jpeg') {
|
|
158
|
+
const screenshotQuality = Math.min(100, Math.max(0, quality ?? 60));
|
|
159
|
+
command.push('--screenshot-quality', String(screenshotQuality));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
command.push(screenshotPath);
|
|
163
|
+
context.invoke(command, 60_000);
|
|
164
|
+
|
|
165
|
+
return buildScreenshotResult(screenshotPath, screenshotFormat);
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the scroll tool.
|
|
5
|
+
*
|
|
6
|
+
* @param context - Shared browser tool execution context
|
|
7
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
8
|
+
* @returns Copilot tool definition for page scrolling or scrolling an element into view
|
|
9
|
+
*/
|
|
10
|
+
export function createScrollTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
11
|
+
return defineTool('browser_scroll', {
|
|
12
|
+
description: 'Scroll the page or scroll an element into view.',
|
|
13
|
+
parameters: {
|
|
14
|
+
type: 'object' as const,
|
|
15
|
+
properties: {
|
|
16
|
+
direction: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Scroll direction: "up" or "down"',
|
|
19
|
+
},
|
|
20
|
+
amount: {
|
|
21
|
+
type: 'number',
|
|
22
|
+
description: 'Pixels to scroll (default: one viewport height)',
|
|
23
|
+
},
|
|
24
|
+
target: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Element ref to scroll into view (overrides direction/amount)',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
handler: async (args) => {
|
|
31
|
+
const { direction, amount, target } = args as {
|
|
32
|
+
direction?: string;
|
|
33
|
+
amount?: number;
|
|
34
|
+
target?: string;
|
|
35
|
+
};
|
|
36
|
+
if (target) {
|
|
37
|
+
return context.invoke(['scrollintoview', target]);
|
|
38
|
+
}
|
|
39
|
+
const scrollDirection = direction ?? 'down';
|
|
40
|
+
const command = ['scroll', scrollDirection];
|
|
41
|
+
if (amount) command.push(String(amount));
|
|
42
|
+
return context.invoke(command);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the select interaction tool.
|
|
5
|
+
*
|
|
6
|
+
* @param context - Shared browser tool execution context
|
|
7
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
8
|
+
* @returns Copilot tool definition for choosing an option in a select element
|
|
9
|
+
*/
|
|
10
|
+
export function createSelectTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
11
|
+
return defineTool('browser_select', {
|
|
12
|
+
description: 'Select an option from a dropdown/select element.',
|
|
13
|
+
parameters: {
|
|
14
|
+
type: 'object' as const,
|
|
15
|
+
properties: {
|
|
16
|
+
target: { type: 'string', description: 'Element ref or CSS selector for the select' },
|
|
17
|
+
value: { type: 'string', description: 'Value or label of the option to select' },
|
|
18
|
+
},
|
|
19
|
+
required: ['target', 'value'],
|
|
20
|
+
},
|
|
21
|
+
handler: async (args) => {
|
|
22
|
+
const { target, value } = args as { target: string; value: string };
|
|
23
|
+
return context.invoke(['select', target, value]);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import type { AgentBrowserToolContext, DefineTool } from './types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates the accessibility snapshot tool.
|
|
8
|
+
*
|
|
9
|
+
* @param context - Shared browser tool execution context
|
|
10
|
+
* @param defineTool - Copilot SDK helper used to declare tools
|
|
11
|
+
* @returns Copilot tool definition for capturing the current page snapshot
|
|
12
|
+
*/
|
|
13
|
+
export function createSnapshotTool(context: AgentBrowserToolContext, defineTool: DefineTool) {
|
|
14
|
+
return defineTool('browser_snapshot', {
|
|
15
|
+
description:
|
|
16
|
+
'Capture an accessibility snapshot of the current page. Returns a text representation of all visible elements with ref identifiers (e.g. @e1, @e2) that can be used in other commands.',
|
|
17
|
+
parameters: {
|
|
18
|
+
type: 'object' as const,
|
|
19
|
+
properties: {},
|
|
20
|
+
},
|
|
21
|
+
handler: async () => {
|
|
22
|
+
const output = context.invoke(['snapshot', '-ci']);
|
|
23
|
+
writeFileSync(join(context.outDir, 'snapshot.txt'), output, 'utf-8');
|
|
24
|
+
return output;
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|