@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.
Files changed (186) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -0
  4. package/dist/esm/commands/app/command.js +176 -0
  5. package/dist/esm/commands/app/command.js.map +1 -0
  6. package/dist/esm/commands/app/eval.js +92 -0
  7. package/dist/esm/commands/app/eval.js.map +1 -0
  8. package/dist/esm/commands/app/format.js +93 -0
  9. package/dist/esm/commands/app/format.js.map +1 -0
  10. package/dist/esm/commands/app/login.js +71 -0
  11. package/dist/esm/commands/app/login.js.map +1 -0
  12. package/dist/esm/commands/app/prompts/execute-step.prompt.js +44 -0
  13. package/dist/esm/commands/app/prompts/execute-step.prompt.js.map +1 -0
  14. package/dist/esm/commands/app/prompts/judge.prompt.js +116 -0
  15. package/dist/esm/commands/app/prompts/judge.prompt.js.map +1 -0
  16. package/dist/esm/commands/app/prompts/plan.prompt.js +47 -0
  17. package/dist/esm/commands/app/prompts/plan.prompt.js.map +1 -0
  18. package/dist/esm/commands/app/prompts/step.prompt.js +42 -0
  19. package/dist/esm/commands/app/prompts/step.prompt.js.map +1 -0
  20. package/dist/esm/commands/app/prompts/system.dev-server.prompt.js +21 -0
  21. package/dist/esm/commands/app/prompts/system.dev-server.prompt.js.map +1 -0
  22. package/dist/esm/commands/app/prompts/system.prompt.js +77 -0
  23. package/dist/esm/commands/app/prompts/system.prompt.js.map +1 -0
  24. package/dist/esm/commands/app/server.js +83 -0
  25. package/dist/esm/commands/app/server.js.map +1 -0
  26. package/dist/esm/commands/app/session-logger.js +171 -0
  27. package/dist/esm/commands/app/session-logger.js.map +1 -0
  28. package/dist/esm/commands/app/tools/click.js +27 -0
  29. package/dist/esm/commands/app/tools/click.js.map +1 -0
  30. package/dist/esm/commands/app/tools/errors.js +24 -0
  31. package/dist/esm/commands/app/tools/errors.js.map +1 -0
  32. package/dist/esm/commands/app/tools/eval-js.js +36 -0
  33. package/dist/esm/commands/app/tools/eval-js.js.map +1 -0
  34. package/dist/esm/commands/app/tools/fill.js +28 -0
  35. package/dist/esm/commands/app/tools/fill.js.map +1 -0
  36. package/dist/esm/commands/app/tools/find.js +44 -0
  37. package/dist/esm/commands/app/tools/find.js.map +1 -0
  38. package/dist/esm/commands/app/tools/get-styles.js +36 -0
  39. package/dist/esm/commands/app/tools/get-styles.js.map +1 -0
  40. package/dist/esm/commands/app/tools/get-url.js +24 -0
  41. package/dist/esm/commands/app/tools/get-url.js.map +1 -0
  42. package/dist/esm/commands/app/tools/go-back.js +15 -0
  43. package/dist/esm/commands/app/tools/go-back.js.map +1 -0
  44. package/dist/esm/commands/app/tools/hover.js +24 -0
  45. package/dist/esm/commands/app/tools/hover.js.map +1 -0
  46. package/dist/esm/commands/app/tools/index.js +32 -0
  47. package/dist/esm/commands/app/tools/index.js.map +1 -0
  48. package/dist/esm/commands/app/tools/navigate.js +31 -0
  49. package/dist/esm/commands/app/tools/navigate.js.map +1 -0
  50. package/dist/esm/commands/app/tools/press-key.js +24 -0
  51. package/dist/esm/commands/app/tools/press-key.js.map +1 -0
  52. package/dist/esm/commands/app/tools/read-file.js +98 -0
  53. package/dist/esm/commands/app/tools/read-file.js.map +1 -0
  54. package/dist/esm/commands/app/tools/registry.js +34 -0
  55. package/dist/esm/commands/app/tools/registry.js.map +1 -0
  56. package/dist/esm/commands/app/tools/reload.js +15 -0
  57. package/dist/esm/commands/app/tools/reload.js.map +1 -0
  58. package/dist/esm/commands/app/tools/screenshot.js +133 -0
  59. package/dist/esm/commands/app/tools/screenshot.js.map +1 -0
  60. package/dist/esm/commands/app/tools/scroll.js +41 -0
  61. package/dist/esm/commands/app/tools/scroll.js.map +1 -0
  62. package/dist/esm/commands/app/tools/select.js +25 -0
  63. package/dist/esm/commands/app/tools/select.js.map +1 -0
  64. package/dist/esm/commands/app/tools/snapshot.js +24 -0
  65. package/dist/esm/commands/app/tools/snapshot.js.map +1 -0
  66. package/dist/esm/commands/app/tools/type-text.js +29 -0
  67. package/dist/esm/commands/app/tools/type-text.js.map +1 -0
  68. package/dist/esm/commands/app/tools/types.js +2 -0
  69. package/dist/esm/commands/app/tools/types.js.map +1 -0
  70. package/dist/esm/commands/app/tools/wait.js +39 -0
  71. package/dist/esm/commands/app/tools/wait.js.map +1 -0
  72. package/dist/esm/commands/app/tools/write-file.js +98 -0
  73. package/dist/esm/commands/app/tools/write-file.js.map +1 -0
  74. package/dist/esm/commands/app/types.js +2 -0
  75. package/dist/esm/commands/app/types.js.map +1 -0
  76. package/dist/esm/eval-resolve.js +83 -0
  77. package/dist/esm/eval-resolve.js.map +1 -0
  78. package/dist/esm/index.js +18 -0
  79. package/dist/esm/index.js.map +1 -0
  80. package/dist/esm/utils/agent-browser.js +67 -0
  81. package/dist/esm/utils/agent-browser.js.map +1 -0
  82. package/dist/esm/utils/browser-poll.js +26 -0
  83. package/dist/esm/utils/browser-poll.js.map +1 -0
  84. package/dist/esm/utils/daemon.js +83 -0
  85. package/dist/esm/utils/daemon.js.map +1 -0
  86. package/dist/esm/utils/index.js +6 -0
  87. package/dist/esm/utils/index.js.map +1 -0
  88. package/dist/esm/utils/process.js +30 -0
  89. package/dist/esm/utils/process.js.map +1 -0
  90. package/dist/esm/utils/server.js +65 -0
  91. package/dist/esm/utils/server.js.map +1 -0
  92. package/dist/esm/version.js +3 -0
  93. package/dist/esm/version.js.map +1 -0
  94. package/dist/tsconfig.tsbuildinfo +1 -0
  95. package/dist/types/commands/app/command.d.ts +7 -0
  96. package/dist/types/commands/app/eval.d.ts +9 -0
  97. package/dist/types/commands/app/format.d.ts +14 -0
  98. package/dist/types/commands/app/login.d.ts +14 -0
  99. package/dist/types/commands/app/prompts/execute-step.prompt.d.ts +13 -0
  100. package/dist/types/commands/app/prompts/judge.prompt.d.ts +15 -0
  101. package/dist/types/commands/app/prompts/plan.prompt.d.ts +13 -0
  102. package/dist/types/commands/app/prompts/step.prompt.d.ts +12 -0
  103. package/dist/types/commands/app/prompts/system.dev-server.prompt.d.ts +12 -0
  104. package/dist/types/commands/app/prompts/system.prompt.d.ts +12 -0
  105. package/dist/types/commands/app/server.d.ts +26 -0
  106. package/dist/types/commands/app/session-logger.d.ts +14 -0
  107. package/dist/types/commands/app/tools/click.d.ts +9 -0
  108. package/dist/types/commands/app/tools/errors.d.ts +9 -0
  109. package/dist/types/commands/app/tools/eval-js.d.ts +13 -0
  110. package/dist/types/commands/app/tools/fill.d.ts +9 -0
  111. package/dist/types/commands/app/tools/find.d.ts +9 -0
  112. package/dist/types/commands/app/tools/get-styles.d.ts +13 -0
  113. package/dist/types/commands/app/tools/get-url.d.ts +9 -0
  114. package/dist/types/commands/app/tools/go-back.d.ts +9 -0
  115. package/dist/types/commands/app/tools/hover.d.ts +9 -0
  116. package/dist/types/commands/app/tools/index.d.ts +9 -0
  117. package/dist/types/commands/app/tools/navigate.d.ts +9 -0
  118. package/dist/types/commands/app/tools/press-key.d.ts +9 -0
  119. package/dist/types/commands/app/tools/read-file.d.ts +13 -0
  120. package/dist/types/commands/app/tools/registry.d.ts +28 -0
  121. package/dist/types/commands/app/tools/reload.d.ts +9 -0
  122. package/dist/types/commands/app/tools/screenshot.d.ts +9 -0
  123. package/dist/types/commands/app/tools/scroll.d.ts +9 -0
  124. package/dist/types/commands/app/tools/select.d.ts +9 -0
  125. package/dist/types/commands/app/tools/snapshot.d.ts +9 -0
  126. package/dist/types/commands/app/tools/type-text.d.ts +9 -0
  127. package/dist/types/commands/app/tools/types.d.ts +12 -0
  128. package/dist/types/commands/app/tools/wait.d.ts +9 -0
  129. package/dist/types/commands/app/tools/write-file.d.ts +19 -0
  130. package/dist/types/commands/app/types.d.ts +68 -0
  131. package/dist/types/eval-resolve.d.ts +31 -0
  132. package/dist/types/index.d.ts +15 -0
  133. package/dist/types/utils/agent-browser.d.ts +26 -0
  134. package/dist/types/utils/browser-poll.d.ts +9 -0
  135. package/dist/types/utils/daemon.d.ts +16 -0
  136. package/dist/types/utils/index.d.ts +5 -0
  137. package/dist/types/utils/process.d.ts +21 -0
  138. package/dist/types/utils/server.d.ts +26 -0
  139. package/dist/types/version.d.ts +1 -0
  140. package/package.json +57 -0
  141. package/src/commands/app/command.ts +204 -0
  142. package/src/commands/app/eval.ts +114 -0
  143. package/src/commands/app/format.ts +110 -0
  144. package/src/commands/app/login.ts +92 -0
  145. package/src/commands/app/prompts/execute-step.prompt.ts +48 -0
  146. package/src/commands/app/prompts/judge.prompt.ts +121 -0
  147. package/src/commands/app/prompts/plan.prompt.ts +48 -0
  148. package/src/commands/app/prompts/step.prompt.ts +45 -0
  149. package/src/commands/app/prompts/system.dev-server.prompt.ts +22 -0
  150. package/src/commands/app/prompts/system.prompt.ts +78 -0
  151. package/src/commands/app/server.ts +105 -0
  152. package/src/commands/app/session-logger.ts +187 -0
  153. package/src/commands/app/tools/click.ts +28 -0
  154. package/src/commands/app/tools/errors.ts +26 -0
  155. package/src/commands/app/tools/eval-js.ts +40 -0
  156. package/src/commands/app/tools/fill.ts +29 -0
  157. package/src/commands/app/tools/find.ts +49 -0
  158. package/src/commands/app/tools/get-styles.ts +40 -0
  159. package/src/commands/app/tools/get-url.ts +26 -0
  160. package/src/commands/app/tools/go-back.ts +16 -0
  161. package/src/commands/app/tools/hover.ts +25 -0
  162. package/src/commands/app/tools/index.ts +57 -0
  163. package/src/commands/app/tools/navigate.ts +32 -0
  164. package/src/commands/app/tools/press-key.ts +25 -0
  165. package/src/commands/app/tools/read-file.ts +113 -0
  166. package/src/commands/app/tools/registry.ts +38 -0
  167. package/src/commands/app/tools/reload.ts +16 -0
  168. package/src/commands/app/tools/screenshot.ts +168 -0
  169. package/src/commands/app/tools/scroll.ts +45 -0
  170. package/src/commands/app/tools/select.ts +26 -0
  171. package/src/commands/app/tools/snapshot.ts +27 -0
  172. package/src/commands/app/tools/type-text.ts +30 -0
  173. package/src/commands/app/tools/types.ts +15 -0
  174. package/src/commands/app/tools/wait.ts +43 -0
  175. package/src/commands/app/tools/write-file.ts +104 -0
  176. package/src/commands/app/types.ts +76 -0
  177. package/src/eval-resolve.ts +113 -0
  178. package/src/index.ts +20 -0
  179. package/src/utils/agent-browser.ts +66 -0
  180. package/src/utils/browser-poll.ts +27 -0
  181. package/src/utils/daemon.ts +84 -0
  182. package/src/utils/index.ts +5 -0
  183. package/src/utils/process.ts +33 -0
  184. package/src/utils/server.ts +66 -0
  185. package/src/version.ts +2 -0
  186. 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
+ }