@empiricalrun/test-gen 0.60.0 → 0.62.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 (92) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/agent/browsing/run.d.ts +2 -0
  3. package/dist/agent/browsing/run.d.ts.map +1 -1
  4. package/dist/agent/browsing/run.js +11 -7
  5. package/dist/agent/browsing/utils.d.ts.map +1 -1
  6. package/dist/agent/browsing/utils.js +1 -1
  7. package/dist/agent/chat/agent-loop.js +2 -3
  8. package/dist/agent/chat/exports.d.ts +2 -2
  9. package/dist/agent/chat/exports.d.ts.map +1 -1
  10. package/dist/agent/chat/exports.js +1 -1
  11. package/dist/agent/chat/index.d.ts.map +1 -1
  12. package/dist/agent/chat/index.js +16 -2
  13. package/dist/agent/chat/models.d.ts +1 -3
  14. package/dist/agent/chat/models.d.ts.map +1 -1
  15. package/dist/agent/chat/models.js +4 -25
  16. package/dist/agent/chat/prompt.d.ts.map +1 -1
  17. package/dist/agent/chat/prompt.js +58 -0
  18. package/dist/agent/cua/computer.d.ts +6 -6
  19. package/dist/agent/cua/computer.d.ts.map +1 -1
  20. package/dist/agent/cua/computer.js +38 -83
  21. package/dist/agent/cua/index.d.ts +2 -1
  22. package/dist/agent/cua/index.d.ts.map +1 -1
  23. package/dist/agent/cua/index.js +26 -33
  24. package/dist/agent/cua/pw-codegen/element-from-point.d.ts +8 -0
  25. package/dist/agent/cua/pw-codegen/element-from-point.d.ts.map +1 -0
  26. package/dist/agent/cua/pw-codegen/element-from-point.js +118 -0
  27. package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts +15 -0
  28. package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -0
  29. package/dist/agent/cua/pw-codegen/pw-pause/index.js +84 -0
  30. package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts +16 -0
  31. package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts.map +1 -0
  32. package/dist/agent/cua/pw-codegen/pw-pause/utils.js +98 -0
  33. package/dist/agent/cua/pw-codegen/types.d.ts +46 -0
  34. package/dist/agent/cua/pw-codegen/types.d.ts.map +1 -0
  35. package/dist/agent/cua/pw-codegen/types.js +2 -0
  36. package/dist/agent/master/browser-tests/cua.spec.js +13 -1
  37. package/dist/artifacts/index.d.ts +43 -0
  38. package/dist/artifacts/index.d.ts.map +1 -0
  39. package/dist/artifacts/index.js +209 -0
  40. package/dist/bin/index.js +7 -11
  41. package/dist/bin/utils/index.d.ts +5 -3
  42. package/dist/bin/utils/index.d.ts.map +1 -1
  43. package/dist/bin/utils/index.js +13 -0
  44. package/dist/bin/utils/platform/web/index.d.ts +4 -1
  45. package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
  46. package/dist/bin/utils/platform/web/index.js +24 -8
  47. package/dist/bin/utils/scenarios/index.d.ts +3 -3
  48. package/dist/file/client.d.ts +2 -0
  49. package/dist/file/client.d.ts.map +1 -1
  50. package/dist/file/client.js +16 -0
  51. package/dist/file/server.d.ts +3 -1
  52. package/dist/file/server.d.ts.map +1 -1
  53. package/dist/file/server.js +27 -3
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4 -1
  56. package/dist/test-build/index.d.ts +6 -2
  57. package/dist/test-build/index.d.ts.map +1 -1
  58. package/dist/test-build/index.js +9 -7
  59. package/dist/tool-call-service/index.d.ts +14 -7
  60. package/dist/tool-call-service/index.d.ts.map +1 -1
  61. package/dist/tool-call-service/index.js +19 -8
  62. package/dist/tools/commit-and-create-pr.d.ts.map +1 -1
  63. package/dist/tools/commit-and-create-pr.js +5 -1
  64. package/dist/tools/download-build.d.ts.map +1 -1
  65. package/dist/tools/download-build.js +3 -3
  66. package/dist/tools/grep/index.d.ts.map +1 -1
  67. package/dist/tools/grep/index.js +4 -2
  68. package/dist/tools/str_replace_editor.d.ts.map +1 -1
  69. package/dist/tools/str_replace_editor.js +25 -8
  70. package/dist/tools/test-gen-browser.d.ts.map +1 -1
  71. package/dist/tools/test-gen-browser.js +21 -4
  72. package/dist/tools/test-run.d.ts.map +1 -1
  73. package/dist/tools/test-run.js +11 -8
  74. package/dist/tools/utils/index.d.ts +13 -0
  75. package/dist/tools/utils/index.d.ts.map +1 -1
  76. package/dist/tools/utils/index.js +47 -0
  77. package/dist/utils/exec.d.ts +2 -0
  78. package/dist/utils/exec.d.ts.map +1 -1
  79. package/dist/utils/exec.js +4 -1
  80. package/dist/utils/git.d.ts.map +1 -1
  81. package/dist/utils/git.js +1 -1
  82. package/dist/utils/slug.d.ts +16 -0
  83. package/dist/utils/slug.d.ts.map +1 -1
  84. package/dist/utils/slug.js +27 -1
  85. package/dist/utils/stripAnsi.d.ts +2 -0
  86. package/dist/utils/stripAnsi.d.ts.map +1 -0
  87. package/dist/utils/stripAnsi.js +9 -0
  88. package/package.json +6 -4
  89. package/tsconfig.tsbuildinfo +1 -1
  90. package/dist/utils/pw-test.d.ts +0 -2
  91. package/dist/utils/pw-test.d.ts.map +0 -1
  92. package/dist/utils/pw-test.js +0 -13
package/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.62.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f137da5: feat: collect artifact from browser agent, update Artifact UI.
8
+ - e69b9d4: feat: implement artifact collection and enhance tool result handling in chat agent
9
+
10
+ ### Patch Changes
11
+
12
+ - 4923078: fix: add production=false to npm ls command
13
+ - fe1ea6e: fix: debug logs for npm ls cmd, missing try-catch
14
+ - 333b99f: fix: test run tool should report correct status
15
+ - 6b6742c: refactor: create codegen abstraction for computer-use agent
16
+ - a44e96c: feat: inject working directory and API key instead of deriving them from process context or env
17
+ - 62fa1f2: feat: add support for claude 4 sonnet and opus
18
+ - a0167b3: fix: add more validation in file creation tool
19
+ - 696925d: feat: patch playwright for page.pause codegen approach
20
+ - fc27187: chore: skip some flaky tests to ensure green reviews
21
+ - 28f95d0: fix: ripgrep handles left brackets correctly
22
+ - 1d39277: feat: implement ArtifactDisplay component to showcase artifacts in session details
23
+ - fe3cdcb: Revert "feat: enhance tool execution with working directory and environment v…"
24
+ - a44e96c: refactor: remove getTools method and initiate tool executors in constructor
25
+ - c79ee3d: fix: add more debug logs for pm2/npm ls issue
26
+ - Updated dependencies [333b99f]
27
+ - Updated dependencies [3739bc0]
28
+ - Updated dependencies [a44e96c]
29
+ - Updated dependencies [62fa1f2]
30
+ - Updated dependencies [e69b9d4]
31
+ - Updated dependencies [fe3cdcb]
32
+ - @empiricalrun/test-run@0.9.3
33
+ - @empiricalrun/llm@0.17.0
34
+
35
+ ## 0.61.0
36
+
37
+ ### Minor Changes
38
+
39
+ - fca6729: feat: implement TypeScript compiler execution and error handling in strReplaceEditor
40
+
41
+ ### Patch Changes
42
+
43
+ - f33bdbf: fix: git changed line should cover untracked also
44
+ - 1b4e3d0: fix: dont wait for fonts when agent needs screenshot
45
+ - bbf735a: chore: remove unused test.only cleaner
46
+ - b7a4821: fix: update pr tool prompt to encourage proactiveness
47
+ - 779dd6e: feat: add email automation recipe to system prompt
48
+ - 05f5184: fix: file create should create parent dir if required
49
+ - Updated dependencies [d71508f]
50
+ - Updated dependencies [79857b3]
51
+ - Updated dependencies [fca6729]
52
+ - @empiricalrun/test-run@0.9.2
53
+ - @empiricalrun/llm@0.16.1
54
+
3
55
  ## 0.60.0
4
56
 
5
57
  ### Minor Changes
@@ -1,3 +1,4 @@
1
+ import { ArtifactInput } from "@empiricalrun/shared-types";
1
2
  type GenerateTestsType = {
2
3
  testCaseName: string;
3
4
  testCaseSuites: string[];
@@ -18,6 +19,7 @@ export declare function generateTestsUsingMasterAgent({ testCaseName, testCaseSu
18
19
  isError: boolean;
19
20
  error: string;
20
21
  actionsSummary?: string;
22
+ artifacts?: ArtifactInput[];
21
23
  }>;
22
24
  export {};
23
25
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAcA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,wBAAsB,8BAA8B,CAAC,EACnD,gBAAgB,EAChB,OAAO,EACP,YAAY,GACb,EAAE;IACD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQlB;AAED,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,OAAO,EACP,OAAO,EACP,yBAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC,CAiFD"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAc3D,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,wBAAsB,8BAA8B,CAAC,EACnD,gBAAgB,EAChB,OAAO,EACP,YAAY,GACb,EAAE;IACD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQlB;AAED,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,OAAO,EACP,OAAO,EACP,yBAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;CAC7B,CAAC,CAmFD"}
@@ -23,8 +23,7 @@ async function generateTestsUsingMasterAgent({ testCaseName, testCaseSuites, tes
23
23
  }
24
24
  const pm = new exec_1.ProcessManager();
25
25
  const availablePort = await (0, detect_port_1.default)(3030);
26
- // start a file service to handle file updates from agent
27
- // - also update the file path with updates when agent is done spitting out code
26
+ // Start a file service for IPC with the agent (which runs in a different process)
28
27
  const fileServer = new server_1.FileServiceServer({
29
28
  port: availablePort,
30
29
  repoDir,
@@ -46,12 +45,14 @@ async function generateTestsUsingMasterAgent({ testCaseName, testCaseSuites, tes
46
45
  ],
47
46
  projects: [projectName],
48
47
  passthroughArgs: "--retries 0 --timeout 0",
49
- //@ts-ignore
48
+ repoDir,
49
+ // @ts-ignore
50
50
  platform: "web",
51
51
  });
52
52
  let error = undefined;
53
53
  try {
54
- console.log(`[generateTestsUsingMasterAgent] Running command: ${cmd.command} ${cmd.args.join(" ")}`);
54
+ const cmdLog = `${cmd.command} ${cmd.args.join(" ")}`;
55
+ console.log(`[generateTestsUsingMasterAgent] Running command: ${cmdLog}`);
55
56
  await pm.execute(cmd.command, cmd.args, {
56
57
  env: {
57
58
  IPC_FILE_SERVICE_PORT: availablePort.toString(),
@@ -60,6 +61,10 @@ async function generateTestsUsingMasterAgent({ testCaseName, testCaseSuites, tes
60
61
  PAGE_VAR_NAME: pageVar || "page",
61
62
  DISPLAY: ":99",
62
63
  LANGFUSE_TRACE_ID: traceId || "",
64
+ // To ensure page.screenshot() doesn't fail when fonts are not loaded
65
+ PW_TEST_SCREENSHOT_NO_FONTS_READY: "1",
66
+ // Enable this for the page.pause approach in codegen
67
+ PW_CODEGEN_NO_INSPECTOR: "1",
63
68
  },
64
69
  });
65
70
  }
@@ -68,7 +73,7 @@ async function generateTestsUsingMasterAgent({ testCaseName, testCaseSuites, tes
68
73
  console.error(error);
69
74
  }
70
75
  if (error) {
71
- // clean up the file if there is any error
76
+ // Clean up the file if there is any error
72
77
  try {
73
78
  const fileContent = fs_1.default.readFileSync(filePathToUpdate, "utf-8");
74
79
  const updatedContent = (0, web_1.replaceCreateTestWithNewCode)(filePathToUpdate, fileContent, "");
@@ -79,12 +84,11 @@ async function generateTestsUsingMasterAgent({ testCaseName, testCaseSuites, tes
79
84
  console.error("Failed to remove extra scripts from files post test gen error", e);
80
85
  }
81
86
  }
82
- // remove the test only from the file
83
- await (0, web_1.removeTestOnly)(testFilePath);
84
87
  await fileServer.stop();
85
88
  return {
86
89
  isError: !!error,
87
90
  error: error || "",
88
91
  actionsSummary: fileServer.getActionsSummary(),
92
+ artifacts: fileServer.getArtifactInputsFromServer(),
89
93
  };
90
94
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAI3D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAsBvD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;AAiFD,wBAAsB,yBAAyB,CAAC,YAAY,EAAE,MAAM,iBAsBnE;AAED,wBAAsB,cAAc,CAAC,EACnC,YAAY,EACZ,cAAc,EACd,QAAQ,GACT,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB,iBAoBA;AAED,wBAAsB,yBAAyB,CAAC,EAC9C,QAAQ,EACR,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyDlB;AAyBD,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,iBA2HxD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,CAAC,CA2B/B;AAWD,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,oBAAoB,GACrC,OAAO,CAAC,MAAM,EAAE,CAAC,CAQnB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,oBAAoB,EACtC,gBAAgB,GAAE,MAAM,EAAU,GACjC,OAAO,CAAC,MAAM,CAAC,CA+CjB"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAI3D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAsBvD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;AAiFD,wBAAsB,yBAAyB,CAAC,YAAY,EAAE,MAAM,iBAsBnE;AAED,wBAAsB,cAAc,CAAC,EACnC,YAAY,EACZ,cAAc,EACd,QAAQ,GACT,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB,iBAoBA;AAED,wBAAsB,yBAAyB,CAAC,EAC9C,QAAQ,EACR,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyDlB;AAyBD,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,iBA8HxD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,CAAC,CA2B/B;AAWD,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,oBAAoB,GACrC,OAAO,CAAC,MAAM,EAAE,CAAC,CAQnB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,oBAAoB,EACtC,gBAAgB,GAAE,MAAM,EAAU,GACjC,OAAO,CAAC,MAAM,CAAC,CA+CjB"}
@@ -239,7 +239,7 @@ async function injectPwLocatorGenerator(page) {
239
239
  });
240
240
  }
241
241
  catch (e) {
242
- console.warn("Error during script injection on page load:", e);
242
+ console.warn(`Error during script injection on page load: ${e.message || String(e)}`);
243
243
  }
244
244
  });
245
245
  try {
@@ -8,7 +8,6 @@ const utils_1 = require("./utils");
8
8
  async function chatAgentLoop({ chatModel, selectedModel, reporter, trace, toolCallService, fileInfo, isToolExecutionRemote, }) {
9
9
  const systemPrompt = await (0, prompt_1.buildSystemPrompt)(fileInfo);
10
10
  trace?.update({ input: { systemPrompt } });
11
- const { tools } = await toolCallService.getTools();
12
11
  while (!chatModel.askUserForInput) {
13
12
  try {
14
13
  const toolCalls = chatModel.getPendingToolCalls();
@@ -19,7 +18,7 @@ async function chatAgentLoop({ chatModel, selectedModel, reporter, trace, toolCa
19
18
  break;
20
19
  }
21
20
  else {
22
- const toolResults = await toolCallService.execute(toolCalls, trace);
21
+ const toolResults = await toolCallService.execute(toolCalls);
23
22
  chatModel.pushToolResultsMessage(toolCalls, toolResults);
24
23
  }
25
24
  }
@@ -27,7 +26,7 @@ async function chatAgentLoop({ chatModel, selectedModel, reporter, trace, toolCa
27
26
  let response = null;
28
27
  response = await chatModel.getLLMResponse({
29
28
  systemPrompt,
30
- tools: tools.map((tool) => (0, chat_1.zodToOpenAITool)(tool.schema)),
29
+ tools: toolCallService.tools.map((tool) => (0, chat_1.zodToOpenAITool)(tool.schema)),
31
30
  selectedModel,
32
31
  trace,
33
32
  });
@@ -1,7 +1,7 @@
1
- import { createChatModel, IChatModel } from "@empiricalrun/llm/chat";
1
+ import { createChatModel, IChatModel, SUPPORTED_CHAT_MODELS, type SupportedChatModels } from "@empiricalrun/llm/chat";
2
2
  import { FileInfo } from "../../types";
3
3
  import { chatAgentLoop } from "./agent-loop";
4
- import { defaultModel, SUPPORTED_CHAT_MODELS, SupportedChatModels } from "./models";
4
+ import { defaultModel } from "./models";
5
5
  import { CHAT_STATE_VERSIONS_MIGRATIONS_MAP, chatStateFromModel, createChatState, createChatStateForMessages, fetchToolCallAvailability, getLatestDownloadBuildUrl, LATEST_CHAT_STATE_VERSION, migrateChatState } from "./state";
6
6
  import { ReporterFunction } from "./types";
7
7
  export { CHAT_STATE_VERSIONS_MIGRATIONS_MAP, chatAgentLoop, chatStateFromModel, createChatModel, createChatState, createChatStateForMessages, defaultModel, fetchToolCallAvailability, getLatestDownloadBuildUrl, LATEST_CHAT_STATE_VERSION, migrateChatState, SUPPORTED_CHAT_MODELS, };
@@ -1 +1 @@
1
- {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,kCAAkC,EAClC,kBAAkB,EAClB,eAAe,EACf,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,EACL,kCAAkC,EAClC,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,0BAA0B,EAC1B,YAAY,EACZ,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EAChB,qBAAqB,GACtB,CAAC;AAEF,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,UAAU,EACV,qBAAqB,EACrB,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,kCAAkC,EAClC,kBAAkB,EAClB,eAAe,EACf,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EACjB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,EACL,kCAAkC,EAClC,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,0BAA0B,EAC1B,YAAY,EACZ,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EAChB,qBAAqB,GACtB,CAAC;AAEF,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC"}
@@ -3,11 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SUPPORTED_CHAT_MODELS = exports.migrateChatState = exports.LATEST_CHAT_STATE_VERSION = exports.getLatestDownloadBuildUrl = exports.fetchToolCallAvailability = exports.defaultModel = exports.createChatStateForMessages = exports.createChatState = exports.createChatModel = exports.chatStateFromModel = exports.chatAgentLoop = exports.CHAT_STATE_VERSIONS_MIGRATIONS_MAP = void 0;
4
4
  const chat_1 = require("@empiricalrun/llm/chat");
5
5
  Object.defineProperty(exports, "createChatModel", { enumerable: true, get: function () { return chat_1.createChatModel; } });
6
+ Object.defineProperty(exports, "SUPPORTED_CHAT_MODELS", { enumerable: true, get: function () { return chat_1.SUPPORTED_CHAT_MODELS; } });
6
7
  const agent_loop_1 = require("./agent-loop");
7
8
  Object.defineProperty(exports, "chatAgentLoop", { enumerable: true, get: function () { return agent_loop_1.chatAgentLoop; } });
8
9
  const models_1 = require("./models");
9
10
  Object.defineProperty(exports, "defaultModel", { enumerable: true, get: function () { return models_1.defaultModel; } });
10
- Object.defineProperty(exports, "SUPPORTED_CHAT_MODELS", { enumerable: true, get: function () { return models_1.SUPPORTED_CHAT_MODELS; } });
11
11
  const state_1 = require("./state");
12
12
  Object.defineProperty(exports, "CHAT_STATE_VERSIONS_MIGRATIONS_MAP", { enumerable: true, get: function () { return state_1.CHAT_STATE_VERSIONS_MIGRATIONS_MAP; } });
13
13
  Object.defineProperty(exports, "chatStateFromModel", { enumerable: true, get: function () { return state_1.chatStateFromModel; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAyChC,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,SAAS,GACV,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,iBAgHA;AAuBD,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,aAAa,GACd,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,iBAsDA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAyChC,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,SAAS,GACV,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,iBAmHA;AAuBD,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,aAAa,GACd,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,iBAyDA"}
@@ -100,7 +100,14 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
100
100
  }
101
101
  else {
102
102
  // TODO: Should we pass a loader function? That would allow us to show a spinner
103
- const toolCallService = new tool_call_service_1.ToolCallService(null, selectedModel, branchName);
103
+ const toolCallService = new tool_call_service_1.ToolCallService({
104
+ chatSessionId: null,
105
+ selectedModel,
106
+ branchName,
107
+ repoPath: process.cwd(),
108
+ apiKey: process.env.EMPIRICALRUN_API_KEY,
109
+ trace,
110
+ });
104
111
  const fileInfo = await (0, file_tree_1.getFileInfoFromFS)(process.cwd());
105
112
  await (0, agent_loop_1.chatAgentLoop)({
106
113
  chatModel,
@@ -152,7 +159,14 @@ async function runChatAgentForDashboard({ chatSessionId, selectedModel, }) {
152
159
  chatSessionId,
153
160
  },
154
161
  });
155
- const toolCallService = new tool_call_service_1.ToolCallService(chatSessionId, selectedModel, branchName);
162
+ const toolCallService = new tool_call_service_1.ToolCallService({
163
+ chatSessionId,
164
+ selectedModel,
165
+ branchName,
166
+ repoPath: process.cwd(),
167
+ apiKey: process.env.EMPIRICALRUN_API_KEY,
168
+ trace,
169
+ });
156
170
  await (0, git_1.checkoutBranch)(branchName);
157
171
  let chatModel = (0, chat_1.createChatModel)(chatState.messages, selectedModel);
158
172
  let reporterFunc = async (chatState, latest) => {
@@ -1,6 +1,4 @@
1
- import type { ModelInfo } from "@empiricalrun/shared-types";
2
- export declare const SUPPORTED_CHAT_MODELS: readonly ModelInfo[];
3
- export type SupportedChatModels = (typeof SUPPORTED_CHAT_MODELS)[number]["id"];
1
+ import { type SupportedChatModels } from "@empiricalrun/llm/chat";
4
2
  export declare const defaultModel: SupportedChatModels;
5
3
  export declare const modelLabels: Record<SupportedChatModels, string>;
6
4
  //# sourceMappingURL=models.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,eAAO,MAAM,qBAAqB,EAAE,SAAS,SAAS,EAqB5C,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAW/E,eAAO,MAAM,YAAY,EAAE,mBAA6C,CAAC;AAEzE,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAOzD,CAAC"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAWhC,eAAO,MAAM,YAAY,EAAE,mBAA6C,CAAC;AAEzE,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAOzD,CAAC"}
@@ -1,37 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.modelLabels = exports.defaultModel = exports.SUPPORTED_CHAT_MODELS = void 0;
4
- exports.SUPPORTED_CHAT_MODELS = [
5
- {
6
- id: "gemini-2.5-pro-preview-03-25",
7
- label: "Gemini 2.5 Pro",
8
- provider: "google",
9
- },
10
- {
11
- id: "o4-mini-2025-04-16",
12
- label: "OpenAI O4 Mini",
13
- provider: "openai",
14
- },
15
- {
16
- id: "claude-3-7-sonnet-20250219",
17
- label: "Claude 3.7 Sonnet",
18
- provider: "claude",
19
- },
20
- {
21
- id: "claude-3-5-sonnet-20241022",
22
- label: "Claude 3.5 Sonnet",
23
- provider: "claude",
24
- },
25
- ];
3
+ exports.modelLabels = exports.defaultModel = void 0;
4
+ const chat_1 = require("@empiricalrun/llm/chat");
26
5
  const DEFAULT_CHAT_MODEL_ID = "gemini-2.5-pro-preview-03-25";
27
6
  function getDefaultChatModelId() {
28
- if (!exports.SUPPORTED_CHAT_MODELS.some((m) => m.id === DEFAULT_CHAT_MODEL_ID)) {
7
+ if (!chat_1.SUPPORTED_CHAT_MODELS.some((m) => m.id === DEFAULT_CHAT_MODEL_ID)) {
29
8
  throw new Error("Default chat model is not in SUPPORTED_CHAT_MODELS");
30
9
  }
31
10
  return DEFAULT_CHAT_MODEL_ID;
32
11
  }
33
12
  exports.defaultModel = getDefaultChatModelId();
34
- exports.modelLabels = exports.SUPPORTED_CHAT_MODELS.reduce((acc, model) => ({
13
+ exports.modelLabels = chat_1.SUPPORTED_CHAT_MODELS.reduce((acc, model) => ({
35
14
  ...acc,
36
15
  [model.id]: model.label,
37
16
  }), {});
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,mBAiGzD"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAuDvC,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,mBAwGzD"}
@@ -2,6 +2,57 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildSystemPrompt = buildSystemPrompt;
4
4
  const repo_1 = require("./repo");
5
+ const emailRecipe = `
6
+ # Email automation
7
+
8
+ ## Example usage
9
+
10
+ ### Dynamic email
11
+
12
+ This dynamically generates a random email address that can
13
+ be used for the test (e.g. invite a new user).
14
+
15
+ \`\`\`ts
16
+ import { EmailClient } from "@empiricalrun/playwright-utils";
17
+ import { expect } from "@playwright/test";
18
+
19
+ const client = new EmailClient();
20
+ const address = client.getAddress();
21
+
22
+ // Input the \`address\` in the application
23
+ // that sends the email.
24
+
25
+ // Get email received on the \`address\`
26
+ const email = await client.waitForEmail();
27
+ expect(
28
+ email.links.find((l) => l.text === "Join your team")
29
+ ).toBeTruthy();
30
+ \`\`\`
31
+
32
+ ### Static email
33
+
34
+ This uses a known (static) email that can be used to login
35
+ into an application.
36
+
37
+ This needs an email id (e.g. \`test-login-user\`). The email id
38
+ is appended with the domain (managed internally) to get the full
39
+ email address.
40
+
41
+ \`\`\`ts
42
+ import { EmailClient } from "@empiricalrun/playwright-utils";
43
+
44
+ const emailId = \`test-login-user\`;
45
+
46
+ const client = new EmailClient({ emailId });
47
+ const address = client.getAddress(); // Returns full address with domain
48
+
49
+ // Get email received on the \`address\`
50
+ const email = await client.waitForEmail();
51
+
52
+ // Get login OTP
53
+ const loginCode = email.codes[0];
54
+ \`\`\`
55
+ `;
5
56
  async function buildSystemPrompt(fileInfo) {
6
57
  const repoContext = await (0, repo_1.getRepoInfoPrompt)(fileInfo);
7
58
  return `
@@ -96,6 +147,13 @@ if (await saveButton.isVisible()) {
96
147
  }
97
148
  \`\`\`
98
149
 
150
+ # Recipes
151
+ You can refer to the following recipes to learn how to write tests for different scenarios.
152
+
153
+ <email-automation>
154
+ ${emailRecipe}
155
+ </email-automation>
156
+
99
157
  # Repo context
100
158
  ${repoContext}
101
159
  `;
@@ -1,12 +1,12 @@
1
1
  import { ResponseComputerToolCall } from "openai/resources/responses/responses.mjs";
2
2
  import type { Page } from "playwright";
3
- type ComputerAction = ResponseComputerToolCall.Click | ResponseComputerToolCall.DoubleClick | ResponseComputerToolCall.Drag | ResponseComputerToolCall.Keypress | ResponseComputerToolCall.Move | ResponseComputerToolCall.Screenshot | ResponseComputerToolCall.Scroll | ResponseComputerToolCall.Type | ResponseComputerToolCall.Wait;
3
+ import { BasePlaywrightCodegen } from "./pw-codegen/types";
4
+ type OpenAIComputerAction = ResponseComputerToolCall.Click | ResponseComputerToolCall.DoubleClick | ResponseComputerToolCall.Drag | ResponseComputerToolCall.Keypress | ResponseComputerToolCall.Move | ResponseComputerToolCall.Screenshot | ResponseComputerToolCall.Scroll | ResponseComputerToolCall.Type | ResponseComputerToolCall.Wait;
4
5
  export declare function getScreenshot(page: Page): Promise<string>;
5
- export declare function handlePageGoto(page: Page, url: string): Promise<{
6
- actionSummary: string;
7
- actionCode: string;
8
- }>;
9
- export declare function handleModelAction(page: Page, action: ComputerAction): Promise<{
6
+ export declare function executeModelAction(page: Page, action: OpenAIComputerAction | {
7
+ type: "goto";
8
+ url: string;
9
+ }, codegen: BasePlaywrightCodegen): Promise<{
10
10
  actionSummary: string;
11
11
  actionCode: string;
12
12
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"computer.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/computer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIvC,KAAK,cAAc,GACf,wBAAwB,CAAC,KAAK,GAC9B,wBAAwB,CAAC,WAAW,GACpC,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,QAAQ,GACjC,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,UAAU,GACnC,wBAAwB,CAAC,MAAM,GAC/B,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,IAAI,CAAC;AAElC,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,mBAG7C;AAgCD,wBAAsB,cAAc,CAClC,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IACT,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CAMD;AA4DD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;IACT,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CAiID"}
1
+ {"version":3,"file":"computer.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/computer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,KAAK,oBAAoB,GACrB,wBAAwB,CAAC,KAAK,GAC9B,wBAAwB,CAAC,WAAW,GACpC,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,QAAQ,GACjC,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,UAAU,GACnC,wBAAwB,CAAC,MAAM,GAC/B,wBAAwB,CAAC,IAAI,GAC7B,wBAAwB,CAAC,IAAI,CAAC;AAElC,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,mBAG7C;AAgCD,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,oBAAoB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAC5D,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC;IACT,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CAkJD"}
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getScreenshot = getScreenshot;
4
- exports.handlePageGoto = handlePageGoto;
5
- exports.handleModelAction = handleModelAction;
6
- const utils_1 = require("../browsing/utils");
4
+ exports.executeModelAction = executeModelAction;
7
5
  async function getScreenshot(page) {
8
6
  const screenshotBytes = await page.screenshot();
9
7
  return Buffer.from(screenshotBytes).toString("base64");
@@ -37,70 +35,24 @@ const CUA_KEY_TO_PLAYWRIGHT_KEY = {
37
35
  tab: "Tab",
38
36
  win: "Meta",
39
37
  };
40
- async function handlePageGoto(page, url) {
41
- await page.goto(url);
42
- return {
43
- actionSummary: `Navigated page to ${url}`,
44
- actionCode: `await page.goto("${url}");\n`,
45
- };
46
- }
47
- async function getLocatorForClick(page, { x, y }) {
48
- const locator = await page.evaluate(([x, y]) => {
49
- const element = document.elementFromPoint(x, y);
50
- const bbox = element?.getBoundingClientRect();
51
- return {
52
- locator: window.playwright.generateLocator(element),
53
- isIframe: element?.tagName === "IFRAME",
54
- x0: bbox?.x,
55
- y0: bbox?.y,
56
- src: element?.getAttribute("src"),
57
- id: element?.getAttribute("id"),
58
- };
59
- }, [x, y]);
60
- if (!locator.isIframe) {
61
- return locator.locator;
62
- }
63
- await (0, utils_1.injectPwLocatorGenerator)(page);
64
- const relativeX = x - locator.x0;
65
- const relativeY = y - locator.y0;
66
- // TODO: Reuse locator.locator for the frameLocator.
67
- let frameEl = locator.src
68
- ? page.locator(`[src="${locator.src}"]`)
69
- : page.locator(`#${locator.id}`);
70
- const frameLocator = locator.src
71
- ? `locator('[src="${locator.src}"]')`
72
- : `locator('#${locator.id}')`;
73
- const elementLocatorInsideFrame = await frameEl
74
- .contentFrame()
75
- .locator(":root")
76
- .evaluate((rootElement, coords) => {
77
- const xPos = coords[0];
78
- const yPos = coords[1];
79
- if (xPos === undefined || yPos === undefined) {
80
- throw new Error("Coordinates are undefined in evaluate call");
81
- }
82
- const element = document.elementFromPoint(xPos, yPos);
83
- return window.playwright.generateLocator(element);
84
- }, [relativeX, relativeY]);
85
- return `${frameLocator}.contentFrame().${elementLocatorInsideFrame}`;
86
- }
87
- async function getLocatorForFill(page) {
88
- const locator = await page.evaluate(() => {
89
- const element = document.activeElement;
90
- return window.playwright.generateLocator(element);
91
- });
92
- return locator;
93
- }
94
- async function handleModelAction(page, action) {
38
+ async function executeModelAction(page, action, codegen) {
95
39
  const actionType = action.type;
96
40
  let actionCode = "";
97
41
  let actionSummary = "";
98
42
  try {
99
43
  switch (actionType) {
44
+ // For every action, this method 1. executes the action,
45
+ // 2. adds to action summary, and 3. adds to action code
46
+ case "goto": {
47
+ const { url } = action;
48
+ await page.goto(url);
49
+ actionSummary = `Navigated page to ${url}`;
50
+ actionCode = await codegen.codeForAction({ type: "goto", url });
51
+ break;
52
+ }
100
53
  case "click": {
101
54
  const { x, y, button = "left" } = action;
102
55
  console.log(`Action: click at (${x}, ${y}) with button '${button}'`);
103
- actionSummary = `Click at (${x}, ${y}) with button '${button}'`;
104
56
  let pwButton = undefined;
105
57
  if (button === "left" || button === "right") {
106
58
  pwButton = button;
@@ -109,19 +61,20 @@ async function handleModelAction(page, action) {
109
61
  pwButton = "middle";
110
62
  }
111
63
  if (pwButton) {
112
- const locator = await getLocatorForClick(page, { x, y });
113
- actionCode = `await page.${locator}.click();\n`;
114
64
  await page.mouse.click(x, y, { button: pwButton });
65
+ actionCode = await codegen.codeForAction({ type: "click", x, y });
66
+ actionSummary = `Click at (${x}, ${y}) with button '${button}'`;
115
67
  }
116
68
  if (button === "back" || button === "forward") {
117
- // Do page navigations, since there is no way to click on the back/forward buttons
118
69
  if (button === "back") {
119
- actionCode = `await page.goBack();\n`;
120
70
  await page.goBack();
71
+ actionCode = await codegen.codeForAction({ type: "back" });
72
+ actionSummary = `Navigated page back`;
121
73
  }
122
74
  else if (button === "forward") {
123
- actionCode = `await page.goForward();\n`;
124
75
  await page.goForward();
76
+ actionCode = await codegen.codeForAction({ type: "forward" });
77
+ actionSummary = `Navigated page forward`;
125
78
  }
126
79
  }
127
80
  break;
@@ -129,23 +82,22 @@ async function handleModelAction(page, action) {
129
82
  case "double_click": {
130
83
  const { x, y } = action;
131
84
  console.log(`Action: doubleclick at (${x}, ${y})`);
132
- actionSummary = `Double click at (${x}, ${y})`;
133
- const locator = await getLocatorForClick(page, { x, y });
134
- actionCode = `await page.${locator}.dblclick();\n`;
135
85
  await page.mouse.dblclick(x, y, { button: "left" });
86
+ actionSummary = `Double click at (${x}, ${y})`;
87
+ actionCode = await codegen.codeForAction({ type: "doubleclick", x, y });
136
88
  break;
137
89
  }
138
90
  case "move": {
139
91
  const { x, y } = action;
140
92
  console.log(`Action: mouse move to (${x}, ${y})`);
141
- actionSummary = `Mouse move to (${x}, ${y})`;
142
93
  await page.mouse.move(x, y);
94
+ actionSummary = `Mouse move to (${x}, ${y})`;
95
+ actionCode = await codegen.codeForAction({ type: "move", x, y });
143
96
  break;
144
97
  }
145
98
  case "drag": {
146
99
  const { path } = action;
147
100
  console.log(`Action: drag along path ${path}`);
148
- // TODO: actionCode is not implemented
149
101
  actionSummary = `Drag along path ${path}`;
150
102
  if (!path || path.length === 0) {
151
103
  break;
@@ -156,14 +108,22 @@ async function handleModelAction(page, action) {
156
108
  await page.mouse.move(path[i].x, path[i].y);
157
109
  }
158
110
  await page.mouse.up();
111
+ actionCode = await codegen.codeForAction({ type: "drag", path });
159
112
  break;
160
113
  }
161
114
  case "scroll": {
162
115
  const { x, y, scroll_x, scroll_y } = action;
163
116
  console.log(`Action: scroll at (${x}, ${y}) with offsets (scroll_x=${scroll_x}, scroll_y=${scroll_y})`);
164
- actionSummary = `Scroll at (${x}, ${y}) with offsets (scroll_x=${scroll_x}, scroll_y=${scroll_y})`;
165
117
  await page.mouse.move(x, y);
166
118
  await page.evaluate(`window.scrollBy(${scroll_x}, ${scroll_y})`);
119
+ actionSummary = `Scroll at (${x}, ${y}) with offsets (scroll_x=${scroll_x}, scroll_y=${scroll_y})`;
120
+ actionCode = await codegen.codeForAction({
121
+ type: "scroll",
122
+ x,
123
+ y,
124
+ scroll_x,
125
+ scroll_y,
126
+ });
167
127
  break;
168
128
  }
169
129
  case "keypress": {
@@ -173,29 +133,24 @@ async function handleModelAction(page, action) {
173
133
  });
174
134
  const mappedKey = mappedKeys.join("+"); // ["CTRL", "A"] becomes ControlOrMeta+A
175
135
  console.log(`Action: keypress for keys ${keys} -> '${mappedKey}'`);
136
+ await page.keyboard.press(mappedKey);
176
137
  actionSummary = `Keypress for keys ${keys} (mapped to '${mappedKey}' for Playwright)`;
177
- try {
178
- await page.keyboard.press(mappedKey);
179
- actionCode = `await page.keyboard.press('${mappedKey}');\n`;
180
- }
181
- catch (e) {
182
- console.error("Error pressing key", mappedKey, ":", e);
183
- }
138
+ actionCode = await codegen.codeForAction({ type: "keypress", keys });
184
139
  break;
185
140
  }
186
141
  case "type": {
187
142
  const { text } = action;
188
143
  console.log(`Action: type text '${text}'`);
189
- actionSummary = `Type text '${text}'`;
190
144
  await page.keyboard.type(text);
191
- const locator = await getLocatorForFill(page);
192
- actionCode = `await page.${locator}.fill("${text}");\n`;
145
+ actionSummary = `Type text '${text}'`;
146
+ actionCode = await codegen.codeForAction({ type: "type", text });
193
147
  break;
194
148
  }
195
149
  case "wait": {
196
150
  console.log(`Action: wait`);
197
- actionSummary = `Wait for 2 seconds`;
198
151
  await page.waitForTimeout(2000);
152
+ actionSummary = `Wait for 2 seconds`;
153
+ actionCode = await codegen.codeForAction({ type: "wait" });
199
154
  break;
200
155
  }
201
156
  case "screenshot": {
@@ -208,8 +163,8 @@ async function handleModelAction(page, action) {
208
163
  console.log("Unrecognized action:", action);
209
164
  }
210
165
  }
211
- catch (e) {
212
- console.error("Error handling action", action, ":", e);
166
+ catch (err) {
167
+ console.error(`Error handling action ${action.type}: ${err.message || err.toString()}`);
213
168
  }
214
169
  return { actionSummary, actionCode };
215
170
  }
@@ -1,6 +1,6 @@
1
1
  import { TraceClient } from "@empiricalrun/llm";
2
+ import { ArtifactInput } from "@empiricalrun/shared-types";
2
3
  import { Page } from "playwright";
3
- export declare function startPlaywrightCodegen(page: Page): Promise<void>;
4
4
  export declare function createTestUsingComputerUseAgent({ page, task, trace, }: {
5
5
  page: Page;
6
6
  task: string;
@@ -9,5 +9,6 @@ export declare function createTestUsingComputerUseAgent({ page, task, trace, }:
9
9
  code: string;
10
10
  importPaths: string[];
11
11
  actionsSummary: string;
12
+ artifacts: ArtifactInput[];
12
13
  }>;
13
14
  //# sourceMappingURL=index.d.ts.map