@empiricalrun/test-gen 0.47.2 → 0.47.4

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 (78) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/actions/index.d.ts +1 -1
  3. package/dist/actions/index.js +1 -1
  4. package/dist/agent/browsing/index.d.ts.map +1 -1
  5. package/dist/agent/browsing/index.js +2 -3
  6. package/dist/agent/browsing/run.d.ts +4 -1
  7. package/dist/agent/browsing/run.d.ts.map +1 -1
  8. package/dist/agent/browsing/run.js +8 -3
  9. package/dist/agent/browsing/utils.d.ts +6 -13
  10. package/dist/agent/browsing/utils.d.ts.map +1 -1
  11. package/dist/agent/browsing/utils.js +9 -38
  12. package/dist/agent/chat.d.ts +7 -0
  13. package/dist/agent/chat.d.ts.map +1 -0
  14. package/dist/agent/chat.js +89 -0
  15. package/dist/agent/codegen/create-test-block.d.ts +2 -3
  16. package/dist/agent/codegen/create-test-block.d.ts.map +1 -1
  17. package/dist/agent/codegen/create-test-block.js +4 -9
  18. package/dist/agent/codegen/fix-ts-errors.d.ts +2 -3
  19. package/dist/agent/codegen/fix-ts-errors.d.ts.map +1 -1
  20. package/dist/agent/codegen/fix-ts-errors.js +4 -5
  21. package/dist/agent/codegen/generate-code-apply-changes.d.ts.map +1 -1
  22. package/dist/agent/codegen/generate-code-apply-changes.js +5 -6
  23. package/dist/agent/codegen/run.d.ts +6 -4
  24. package/dist/agent/codegen/run.d.ts.map +1 -1
  25. package/dist/agent/codegen/run.js +8 -6
  26. package/dist/agent/codegen/update-flow.d.ts +7 -5
  27. package/dist/agent/codegen/update-flow.d.ts.map +1 -1
  28. package/dist/agent/codegen/update-flow.js +9 -49
  29. package/dist/agent/codegen/utils.d.ts +2 -16
  30. package/dist/agent/codegen/utils.d.ts.map +1 -1
  31. package/dist/agent/codegen/utils.js +3 -41
  32. package/dist/agent/diagnosis-agent/index.d.ts +2 -9
  33. package/dist/agent/diagnosis-agent/index.d.ts.map +1 -1
  34. package/dist/agent/diagnosis-agent/index.js +1 -8
  35. package/dist/agent/enrich-prompt/index.d.ts.map +1 -1
  36. package/dist/agent/enrich-prompt/index.js +0 -1
  37. package/dist/agent/infer-agent/index.d.ts.map +1 -1
  38. package/dist/agent/infer-agent/index.js +0 -9
  39. package/dist/agent/master/browser-tests/index.spec.js +15 -1
  40. package/dist/agent/master/element-annotation.d.ts.map +1 -1
  41. package/dist/agent/master/element-annotation.js +1 -2
  42. package/dist/agent/master/execute-browser-action.d.ts.map +1 -1
  43. package/dist/agent/master/execute-browser-action.js +1 -2
  44. package/dist/agent/master/execute-skill-action.d.ts.map +1 -1
  45. package/dist/agent/master/execute-skill-action.js +1 -2
  46. package/dist/agent/master/next-action.d.ts.map +1 -1
  47. package/dist/agent/master/next-action.js +2 -3
  48. package/dist/agent/master/planner.d.ts.map +1 -1
  49. package/dist/agent/master/planner.js +1 -2
  50. package/dist/agent/master/run.d.ts.map +1 -1
  51. package/dist/agent/master/run.js +1 -2
  52. package/dist/agent/master/scroller.d.ts.map +1 -1
  53. package/dist/agent/master/scroller.js +2 -3
  54. package/dist/agent/planner/run-time-planner.d.ts.map +1 -1
  55. package/dist/agent/planner/run-time-planner.js +1 -2
  56. package/dist/bin/index.js +49 -34
  57. package/dist/bin/utils/index.d.ts +1 -0
  58. package/dist/bin/utils/index.d.ts.map +1 -1
  59. package/dist/bin/utils/index.js +9 -3
  60. package/dist/file/server.d.ts +2 -0
  61. package/dist/file/server.d.ts.map +1 -1
  62. package/dist/file/server.js +18 -1
  63. package/dist/tools/browser-agent.d.ts +16 -0
  64. package/dist/tools/browser-agent.d.ts.map +1 -0
  65. package/dist/tools/browser-agent.js +76 -0
  66. package/dist/tools/codegen-agent.d.ts +9 -0
  67. package/dist/tools/codegen-agent.d.ts.map +1 -0
  68. package/dist/tools/codegen-agent.js +44 -0
  69. package/dist/tools/test-run.d.ts +10 -0
  70. package/dist/tools/test-run.d.ts.map +1 -0
  71. package/dist/tools/test-run.js +35 -0
  72. package/dist/utils/git.d.ts +2 -0
  73. package/dist/utils/git.d.ts.map +1 -0
  74. package/dist/utils/git.js +11 -0
  75. package/package.json +3 -2
  76. package/dist/agent/utils.d.ts +0 -2
  77. package/dist/agent/utils.d.ts.map +0 -1
  78. package/dist/agent/utils.js +0 -12
@@ -4,7 +4,6 @@ exports.runtimePlannerWithScreenshot = void 0;
4
4
  const llm_1 = require("@empiricalrun/llm");
5
5
  const vision_1 = require("@empiricalrun/llm/vision");
6
6
  const constants_1 = require("../../constants");
7
- const utils_1 = require("../utils");
8
7
  async function runtimePlannerWithScreenshot({ trace, task, conversation, pages, page, currentPage, }) {
9
8
  const buffer = await page.screenshot({
10
9
  //This is done to improve element annotation accuracy, anyways it doesn't annotate elements which are out of viewport
@@ -120,7 +119,7 @@ async function runtimePlannerWithScreenshot({ trace, task, conversation, pages,
120
119
  });
121
120
  const toolCallResp = (response?.tool_calls || [])[0];
122
121
  if (toolCallResp) {
123
- const toolCall = (0, utils_1.parseJson)(toolCallResp.function.arguments);
122
+ const toolCall = JSON.parse(toolCallResp.function.arguments);
124
123
  const output = {
125
124
  pageName: toolCall.pageName,
126
125
  isDone: toolCall.isDone,
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/master/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,QAAQ,EACR,oBAAoB,EACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAelC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAWxC,OAAO,EAAE,4BAA4B,EAAE,MAAM,QAAQ,CAAC;AAuBtD,wBAAsB,0BAA0B,CAAC,EAC/C,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;;;GAwRA"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/master/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,QAAQ,EACR,oBAAoB,EACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAelC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAUxC,OAAO,EAAE,4BAA4B,EAAE,MAAM,QAAQ,CAAC;AAuBtD,wBAAsB,0BAA0B,CAAC,EAC/C,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;;;GAwRA"}
@@ -15,7 +15,6 @@ const utils_2 = require("../browsing/utils");
15
15
  const skills_retriever_1 = require("../codegen/skills-retriever");
16
16
  const run_1 = require("../planner/run");
17
17
  const run_time_planner_1 = require("../planner/run-time-planner");
18
- const utils_3 = require("../utils");
19
18
  const action_tool_calls_1 = require("./action-tool-calls");
20
19
  const execute_browser_action_1 = require("./execute-browser-action");
21
20
  const execute_skill_action_1 = require("./execute-skill-action");
@@ -164,7 +163,7 @@ async function createTestUsingMasterAgent({ task, page, testCase, specPath, opti
164
163
  await testgenUpdatesReporter.sendMessage("Agent is not able to figure out next action since element is not visible on screen.");
165
164
  break;
166
165
  }
167
- const args = (0, utils_3.parseJson)(nextAction.toolCallArgs);
166
+ const args = JSON.parse(nextAction.toolCallArgs);
168
167
  const masterAgentActionSpan = masterAgentSpan?.span({
169
168
  name: "master-agent-execute-action",
170
169
  });
@@ -1 +1 @@
1
- {"version":3,"file":"scroller.d.ts","sourceRoot":"","sources":["../../../src/agent/master/scroller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAchD,MAAM,MAAM,cAAc,GAAG;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AA2ZF,wBAAsB,QAAQ,CAAC,EAC7B,kBAAkB,EAClB,IAAI,EACJ,KAAK,EACL,cAAc,EACd,MAAM,GACP,EAAE;IACD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA6D5B"}
1
+ {"version":3,"file":"scroller.d.ts","sourceRoot":"","sources":["../../../src/agent/master/scroller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAahD,MAAM,MAAM,cAAc,GAAG;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AA2ZF,wBAAsB,QAAQ,CAAC,EAC7B,kBAAkB,EAClB,IAAI,EACJ,KAAK,EACL,cAAc,EACd,MAAM,GACP,EAAE;IACD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA6D5B"}
@@ -5,7 +5,6 @@ const llm_1 = require("@empiricalrun/llm");
5
5
  const vision_1 = require("@empiricalrun/llm/vision");
6
6
  const constants_1 = require("../../constants");
7
7
  const reporter_1 = require("../../reporter");
8
- const utils_1 = require("../utils");
9
8
  const action_tool_calls_1 = require("./action-tool-calls");
10
9
  const element_annotation_1 = require("./element-annotation");
11
10
  let usedAnnotations = [];
@@ -165,7 +164,7 @@ Follow the instructions before responding:
165
164
  const toolCall = completion?.tool_calls?.[0];
166
165
  scrollSpan?.end({ output: toolCall });
167
166
  if (toolCall) {
168
- const args = (0, utils_1.parseJson)(toolCall.function.arguments);
167
+ const args = JSON.parse(toolCall.function.arguments);
169
168
  isVisible = args.is_visible || false;
170
169
  }
171
170
  else {
@@ -303,7 +302,7 @@ ${annotationKeysString}`,
303
302
  const toolCall = completion?.tool_calls?.[0];
304
303
  annotationsSpan?.end({ output: toolCall });
305
304
  if (toolCall) {
306
- const args = (0, utils_1.parseJson)(toolCall.function.arguments);
305
+ const args = JSON.parse(toolCall.function.arguments);
307
306
  const isAnnotationPresentInKeys = annotationKeys.some((annotation) => annotation.elementID === args.element_annotation);
308
307
  if (args.element_annotation !== "NA" && isAnnotationPresentInKeys) {
309
308
  usedAnnotations.push(args.element_annotation);
@@ -1 +1 @@
1
- {"version":3,"file":"run-time-planner.d.ts","sourceRoot":"","sources":["../../../src/agent/planner/run-time-planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,wBAAsB,cAAc,CAAC,EACnC,KAAK,EACL,IAAI,EACJ,iBAAiB,EACjB,KAAK,EACL,WAAW,GACZ,EAAE;IACD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5B,WAAW,EAAE,WAAW,CAAC;CAC1B;;;;GA+FA"}
1
+ {"version":3,"file":"run-time-planner.d.ts","sourceRoot":"","sources":["../../../src/agent/planner/run-time-planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,wBAAsB,cAAc,CAAC,EACnC,KAAK,EACL,IAAI,EACJ,iBAAiB,EACjB,KAAK,EACL,WAAW,GACZ,EAAE;IACD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5B,WAAW,EAAE,WAAW,CAAC;CAC1B;;;;GA+FA"}
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runtimePlanner = void 0;
4
4
  const llm_1 = require("@empiricalrun/llm");
5
5
  const promptTemplate_0 = "{{#section \"system\"}}\nYou are given a list of successfully executed actions that are done towards completing a task (which\nis also provided to you). Your goal is to analyse the list and determine if the task is completed.\n\nIf the task is not fully completed, identify which specific actions are missing\nand suggest next steps to complete the task. Assume that the conversation provided\nis entirely truthful and no additional actions were performed beyond those listed.\n\nThese actions were executed by AI agents using Playwright on a browser. These agents\nalready have access to browser tabs to execute actions. If there is a pending action,\none of the agents will execute it in the browser. However, they need your help to\nchoose which browser tab (= page) to use for the next action.\n\nTo fulfil your goal, follow these steps:\n- Divide the task into individual actions.\n- Compare each task action against the actions listed in the successfully executed actions list.\n- Identify which actions have been executed and which have not.\n- If all actions are executed, respond with the task as done.\n- If any actions are missing, respond with the task as not done, listing all actions\n and specifying which are complete and which are missing.\n- If provided with list of pages, based on the next pending action and previously executed\n action, identify the page on which next action needs to be taken\n{{/section}}\n\n{{#section \"user\"}}\nTask:\n{{task}}\n\n----\n\nSuccessfully executed actions:\n{{successfulActions}}\n\n----\n\nList of pages with their current URLs:\n{{pagesSummary}}\n\n\n{{/section}}\n";
6
- const utils_1 = require("../utils");
7
6
  async function runtimePlanner({ trace, task, successfulActions, pages, currentPage, }) {
8
7
  const runTimePlannerSpan = trace?.span({
9
8
  name: "runtime-planner",
@@ -76,7 +75,7 @@ async function runtimePlanner({ trace, task, successfulActions, pages, currentPa
76
75
  });
77
76
  const toolCallResp = (response?.tool_calls || [])[0];
78
77
  if (toolCallResp) {
79
- const toolCall = (0, utils_1.parseJson)(toolCallResp.function.arguments);
78
+ const toolCall = JSON.parse(toolCallResp.function.arguments);
80
79
  const output = {
81
80
  pageName: toolCall.pageName,
82
81
  isDone: toolCall.isDone,
package/dist/bin/index.js CHANGED
@@ -9,6 +9,7 @@ const commander_1 = require("commander");
9
9
  const dotenv_1 = __importDefault(require("dotenv"));
10
10
  const run_1 = require("../agent/browsing/run");
11
11
  const utils_1 = require("../agent/browsing/utils");
12
+ const chat_1 = require("../agent/chat");
12
13
  const repo_edit_1 = require("../agent/codegen/repo-edit");
13
14
  const run_2 = require("../agent/codegen/run");
14
15
  const diagnosis_agent_1 = require("../agent/diagnosis-agent");
@@ -31,14 +32,12 @@ process.on("beforeExit", async () => await flushEvents());
31
32
  process.on("exit", async () => await flushEvents());
32
33
  process.on("SIGINT", async () => await flushEvents());
33
34
  process.on("SIGTERM", async () => await flushEvents());
34
- async function resolveAgentUsingTask({ testCase, trace, }) {
35
- const { response } = await (0, infer_agent_1.inferAgentBasedTask)({
36
- task: testCase.steps.join("\n"),
37
- trace,
35
+ async function runChatAgent(prompt) {
36
+ return await (0, chat_1.chatAgent)({
37
+ prompt,
38
38
  });
39
- return response;
40
39
  }
41
- async function runAgent(testGenConfig, testGenToken) {
40
+ async function runAgentsWorkflow(testGenConfig, testGenToken) {
42
41
  const logger = new logger_1.CustomLogger();
43
42
  const { specPath, testCase } = testGenConfig;
44
43
  if (process.env.LOG_URL) {
@@ -89,29 +88,10 @@ async function runAgent(testGenConfig, testGenToken) {
89
88
  });
90
89
  return;
91
90
  }
92
- // TODO: this needs to be moved to an orchestrator which decides what needs to be done first before executing the sub tasks
93
- if (testGenConfig.testErrorDiagnosis &&
94
- testGenConfig.testErrorDiagnosis.failingLine &&
95
- // TODO: fix this hardcoding of user prompt - ideally its an auto fix intent
96
- testCase.steps[0]?.toLowerCase().trim() == "can you please fix the test") {
97
- const { task: updatedTask } = await (0, diagnosis_agent_1.createTaskUsingFailureDiagnosis)({
98
- options: testGenConfig.options,
99
- trace,
100
- diagnosis: testGenConfig.testErrorDiagnosis,
101
- });
102
- if (updatedTask) {
103
- testCase.steps = [updatedTask];
104
- }
105
- }
106
- if (!agent || agent === "auto") {
107
- agent = await resolveAgentUsingTask({
108
- testCase,
109
- trace,
110
- });
111
- }
112
- logger.success(`Generating test using ${agent} agent. ${process.env.LOG_URL ? `[view log](${process.env.LOG_URL})` : ""}`);
113
91
  if (testGenConfig.testErrorDiagnosis &&
114
92
  testGenConfig.testErrorDiagnosis.failingLine) {
93
+ // If failure context is available, we can enrich the user prompt to contain
94
+ // the failure context
115
95
  const requestedChangeResp = await (0, enrich_prompt_1.enrichPromptWithFailingLine)({
116
96
  trace,
117
97
  testBlock: testGenConfig.testErrorDiagnosis.failingLine,
@@ -119,7 +99,28 @@ async function runAgent(testGenConfig, testGenToken) {
119
99
  suggestionForFix: testCase.steps.join("\n"),
120
100
  });
121
101
  testCase.steps = [requestedChangeResp.output];
102
+ // For "auto-fix" we use the user prompt (which is hard-coded in the dashboard
103
+ // entrypoints), and invoke more interesting enrichment
104
+ if (
105
+ // TODO: fix this hardcoding of user prompt - ideally its an auto fix intent
106
+ testCase.steps[0]?.toLowerCase().trim() == "can you please fix the test") {
107
+ const { task: updatedTask } = await (0, diagnosis_agent_1.createTaskUsingFailureDiagnosis)({
108
+ trace,
109
+ diagnosis: testGenConfig.testErrorDiagnosis,
110
+ });
111
+ if (updatedTask) {
112
+ testCase.steps = [updatedTask];
113
+ }
114
+ }
115
+ }
116
+ if (!agent || agent === "auto") {
117
+ const { response } = await (0, infer_agent_1.inferAgentBasedTask)({
118
+ task: testCase.steps.join("\n"),
119
+ trace,
120
+ });
121
+ agent = response;
122
122
  }
123
+ logger.success(`Generating test using ${agent} agent. ${process.env.LOG_URL ? `[view log](${process.env.LOG_URL})` : ""}`);
123
124
  if (agent === "plan") {
124
125
  const task = testCase.steps.join("\n");
125
126
  const plan = await (0, run_3.planTask)({
@@ -132,11 +133,18 @@ async function runAgent(testGenConfig, testGenToken) {
132
133
  await new reporter_1.TestGenUpdatesReporter().sendMessage(plan);
133
134
  }
134
135
  else if (agent === "code") {
135
- await (0, run_2.generateTest)(testCase, specPath, testGenConfig.options, trace);
136
+ await (0, run_2.generateTestWithCodegen)({
137
+ testCase,
138
+ file: specPath,
139
+ trace,
140
+ });
136
141
  }
137
142
  else {
138
- // this assumes we have only one scenario in test config
139
- const filePathToUpdate = await (0, utils_1.prepareFileForMasterAgent)(testGenConfig, trace);
143
+ const filePathToUpdate = await (0, utils_1.prepareFileForMasterAgent)({
144
+ testCase,
145
+ specPath,
146
+ trace,
147
+ });
140
148
  void (0, session_1.updateSessionStatus)(testGenConfig.options?.metadata.testSessionId, {
141
149
  status: "agent_live_session_started",
142
150
  });
@@ -155,10 +163,11 @@ async function runAgent(testGenConfig, testGenToken) {
155
163
  const program = new commander_1.Command();
156
164
  program
157
165
  .option("--token <token>", "Test generation token")
166
+ .option("--prompt <prompt>", "Prompt for the chat agent")
158
167
  .option("--name <test-name>", "Name of the test case")
159
- .option("--prompt <prompt>", "Prompt for the test case")
160
168
  .option("--file <test-file>", "File path of the test case (inside tests dir)")
161
169
  .option("--suites <suites>", "Comma separated list of describe blocks")
170
+ .option("--use-chat", "Use chat agent (and not the workflow)")
162
171
  .parse(process.argv);
163
172
  const options = program.opts();
164
173
  const completedOptions = await (0, utils_2.validateAndCompleteCliOptions)(options);
@@ -181,10 +190,16 @@ async function runAgent(testGenConfig, testGenToken) {
181
190
  });
182
191
  let testGenFailed = false;
183
192
  let agentUsed;
193
+ // Download the build if repo has a download script
194
+ await (0, test_build_1.downloadBuild)(testGenConfig.build || {});
184
195
  try {
185
- // download the build if it exists
186
- await (0, test_build_1.downloadBuild)(testGenConfig.build || {});
187
- agentUsed = await runAgent(testGenConfig, testGenToken);
196
+ if (completedOptions.useChat) {
197
+ await runChatAgent(completedOptions.prompt);
198
+ return;
199
+ }
200
+ else {
201
+ agentUsed = await runAgentsWorkflow(testGenConfig, testGenToken);
202
+ }
188
203
  }
189
204
  catch (e) {
190
205
  testGenFailed = true;
@@ -4,6 +4,7 @@ export interface CliOptions {
4
4
  file?: string;
5
5
  prompt?: string;
6
6
  suites?: string;
7
+ useChat?: boolean;
7
8
  }
8
9
  export declare function validateAndCompleteCliOptions(options: CliOptions): Promise<CliOptions>;
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CAmDrB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bin/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAQD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CAyDrB"}
@@ -6,12 +6,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.validateAndCompleteCliOptions = void 0;
7
7
  const inquirer_1 = __importDefault(require("inquirer"));
8
8
  async function validateAndCompleteCliOptions(options) {
9
+ // For existing flow between dashboard <> test-gen (via ci-worker)
9
10
  const hasToken = !!options.token;
10
11
  if (hasToken) {
11
12
  return options;
12
13
  }
14
+ let requiredFields = ["name", "file", "prompt"];
15
+ // For new chat flow in local CLI usage, only prompt is required
16
+ if (options.useChat) {
17
+ requiredFields = ["prompt"];
18
+ }
13
19
  const questions = [];
14
- if (!options.name) {
20
+ if (!options.name && requiredFields.includes("name")) {
15
21
  questions.push({
16
22
  type: "input",
17
23
  name: "name",
@@ -19,7 +25,7 @@ async function validateAndCompleteCliOptions(options) {
19
25
  validate: (input) => input.trim().length > 0 || "Test name is required",
20
26
  });
21
27
  }
22
- if (!options.file) {
28
+ if (!options.file && requiredFields.includes("file")) {
23
29
  questions.push({
24
30
  type: "input",
25
31
  name: "file",
@@ -27,7 +33,7 @@ async function validateAndCompleteCliOptions(options) {
27
33
  validate: (input) => input.trim().length > 0 || "Test file path is required",
28
34
  });
29
35
  }
30
- if (!options.prompt) {
36
+ if (!options.prompt && requiredFields.includes("prompt")) {
31
37
  questions.push({
32
38
  type: "editor",
33
39
  name: "prompt",
@@ -2,12 +2,14 @@ export declare class FileService {
2
2
  private port;
3
3
  private filePath;
4
4
  private repoDir;
5
+ private server;
5
6
  constructor({ port, repoDir }: {
6
7
  port: number;
7
8
  repoDir: string;
8
9
  });
9
10
  setFilePath(filePath: string): void;
10
11
  startFileService(): Promise<number>;
12
+ stop(): Promise<void>;
11
13
  }
12
14
  export declare function startFileService(): Promise<void>;
13
15
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/file/server.ts"],"names":[],"mappings":"AAWA,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,OAAO,CAAc;gBAEjB,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAKhE,WAAW,CAAC,QAAQ,EAAE,MAAM;IAItB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;CAyC1C;AAED,wBAAsB,gBAAgB,kBAAK"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/file/server.ts"],"names":[],"mappings":"AAWA,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,MAAM,CAA4C;gBAE9C,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAKhE,WAAW,CAAC,QAAQ,EAAE,MAAM;IAItB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IA0CnC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAgB5B;AAED,wBAAsB,gBAAgB,kBAAK"}
@@ -13,6 +13,7 @@ class FileService {
13
13
  port = 0;
14
14
  filePath = "";
15
15
  repoDir = "";
16
+ server;
16
17
  constructor({ port, repoDir }) {
17
18
  this.port = port;
18
19
  this.repoDir = repoDir;
@@ -43,7 +44,23 @@ class FileService {
43
44
  return res.send({ success: false });
44
45
  });
45
46
  return new Promise((resolve) => {
46
- app.listen(this.port, () => resolve(this.port));
47
+ this.server = app.listen(this.port, () => resolve(this.port));
48
+ });
49
+ }
50
+ async stop() {
51
+ return new Promise((resolve, reject) => {
52
+ if (!this.server) {
53
+ resolve();
54
+ return;
55
+ }
56
+ this.server.close((err) => {
57
+ if (err) {
58
+ reject(err);
59
+ return;
60
+ }
61
+ this.server = undefined;
62
+ resolve();
63
+ });
47
64
  });
48
65
  }
49
66
  }
@@ -0,0 +1,16 @@
1
+ import { OpenAI } from "openai";
2
+ export declare const schema: OpenAI.Chat.Completions.ChatCompletionTool;
3
+ export declare const generateTestWithBrowserAgentTool: ({ testName, fileName, changeToMake, }: {
4
+ testName: string;
5
+ fileName: string;
6
+ changeToMake: string;
7
+ }) => Promise<{
8
+ result: string;
9
+ gitPatch: string;
10
+ error?: undefined;
11
+ } | {
12
+ result: string;
13
+ error: string;
14
+ gitPatch?: undefined;
15
+ }>;
16
+ //# sourceMappingURL=browser-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-agent.d.ts","sourceRoot":"","sources":["../../src/tools/browser-agent.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAQhC,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,kBA0B5C,CAAC;AAEF,eAAO,MAAM,gCAAgC;cAKjC,MAAM;cACN,MAAM;kBACF,MAAM;;;;;;;;;EAsCrB,CAAC"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateTestWithBrowserAgentTool = exports.schema = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const run_1 = require("../agent/browsing/run");
9
+ const utils_1 = require("../agent/browsing/utils");
10
+ const scenarios_1 = require("../bin/utils/scenarios");
11
+ const git_1 = require("../utils/git");
12
+ exports.schema = {
13
+ type: "function",
14
+ function: {
15
+ name: "generateTestWithBrowserAgent",
16
+ description: "Create or modify a test case with browser agent. This is useful when the modifications involve changing a selector or executing browser interactions (like click, fill, etc)",
17
+ parameters: {
18
+ type: "object",
19
+ properties: {
20
+ testName: {
21
+ type: "string",
22
+ description: "The name of the test to create or modify",
23
+ },
24
+ fileName: {
25
+ type: "string",
26
+ description: "The name of the file where the test is located. File name must end with .spec.ts",
27
+ },
28
+ changeToMake: {
29
+ type: "string",
30
+ description: "The change to make to the test",
31
+ },
32
+ },
33
+ required: ["testName", "fileName", "changeToMake"],
34
+ },
35
+ },
36
+ };
37
+ const generateTestWithBrowserAgentTool = async ({ testName, fileName, changeToMake, }) => {
38
+ const testCase = {
39
+ id: 0,
40
+ name: testName,
41
+ filePath: fileName,
42
+ suites: [], // TODO: Support suites
43
+ steps: [changeToMake],
44
+ };
45
+ const filePathFromCwd = path_1.default.join("tests", fileName);
46
+ const filePathToUpdate = await (0, utils_1.prepareFileForMasterAgent)({
47
+ testCase,
48
+ specPath: filePathFromCwd,
49
+ });
50
+ const { isError, error } = await (0, run_1.generateTestsUsingMasterAgent)({
51
+ testFilePath: filePathFromCwd,
52
+ filePathToUpdate,
53
+ // TODO: Remove this hardcoded project name
54
+ pwProjectsFilter: ["chromium"],
55
+ testGenToken: (0, scenarios_1.buildTokenFromOptions)({
56
+ name: testName,
57
+ file: fileName,
58
+ prompt: changeToMake,
59
+ }),
60
+ repoDir: process.cwd(),
61
+ });
62
+ if (!isError) {
63
+ const gitPatch = (0, git_1.getGitDiff)(filePathFromCwd);
64
+ return {
65
+ result: "Test was generated successfully",
66
+ gitPatch,
67
+ };
68
+ }
69
+ else {
70
+ return {
71
+ result: "Test was not generated successfully",
72
+ error,
73
+ };
74
+ }
75
+ };
76
+ exports.generateTestWithBrowserAgentTool = generateTestWithBrowserAgentTool;
@@ -0,0 +1,9 @@
1
+ import { TestCase } from "@empiricalrun/shared-types";
2
+ import { OpenAI } from "openai";
3
+ export declare const schema: OpenAI.Chat.Completions.ChatCompletionTool;
4
+ export declare const generateTestWithCodegenTool: ({ testName, fileName, changeToMake, }: {
5
+ testName: string;
6
+ fileName: string;
7
+ changeToMake: string;
8
+ }) => Promise<void | TestCase[]>;
9
+ //# sourceMappingURL=codegen-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen-agent.d.ts","sourceRoot":"","sources":["../../src/tools/codegen-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,kBA0B5C,CAAC;AAEF,eAAO,MAAM,2BAA2B;cAK5B,MAAM;cACN,MAAM;kBACF,MAAM;gCAcrB,CAAC"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateTestWithCodegenTool = exports.schema = void 0;
4
+ const run_1 = require("../agent/codegen/run");
5
+ exports.schema = {
6
+ type: "function",
7
+ function: {
8
+ name: "generateTestWithCodegen",
9
+ description: "Create or modify a test case with code generation. This is useful when modifications can be done with TypeScript only, and don't require any browser interactions or element selectors.",
10
+ parameters: {
11
+ type: "object",
12
+ properties: {
13
+ testName: {
14
+ type: "string",
15
+ description: "The name of the test to create or modify",
16
+ },
17
+ fileName: {
18
+ type: "string",
19
+ description: "The name of the file where the test is located. File name must end with .spec.ts",
20
+ },
21
+ changeToMake: {
22
+ type: "string",
23
+ description: "The change to make to the test",
24
+ },
25
+ },
26
+ required: ["testName", "fileName", "changeToMake"],
27
+ },
28
+ },
29
+ };
30
+ const generateTestWithCodegenTool = async ({ testName, fileName, changeToMake, }) => {
31
+ const testCase = {
32
+ id: 0,
33
+ name: testName,
34
+ filePath: fileName,
35
+ suites: [], // TODO: Support suites
36
+ steps: [changeToMake],
37
+ };
38
+ const result = await (0, run_1.generateTestWithCodegen)({
39
+ testCase,
40
+ file: fileName,
41
+ });
42
+ return result;
43
+ };
44
+ exports.generateTestWithCodegenTool = generateTestWithCodegenTool;
@@ -0,0 +1,10 @@
1
+ import { OpenAI } from "openai";
2
+ export declare const schema: OpenAI.Chat.Completions.ChatCompletionTool;
3
+ export declare const runTestTool: ({ testName, fileName, }: {
4
+ testName: string;
5
+ fileName: string;
6
+ }) => Promise<{
7
+ hasTestPassed: boolean;
8
+ summaryJson: any;
9
+ }>;
10
+ //# sourceMappingURL=test-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-run.d.ts","sourceRoot":"","sources":["../../src/tools/test-run.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,kBAqB5C,CAAC;AAEF,eAAO,MAAM,WAAW;cAIZ,MAAM;cACN,MAAM;;;;EASjB,CAAC"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runTestTool = exports.schema = void 0;
4
+ const test_run_1 = require("@empiricalrun/test-run");
5
+ exports.schema = {
6
+ type: "function",
7
+ function: {
8
+ name: "runTest",
9
+ description: "Run a test",
10
+ parameters: {
11
+ type: "object",
12
+ properties: {
13
+ testName: {
14
+ type: "string",
15
+ description: "The name of the test to run",
16
+ },
17
+ fileName: {
18
+ type: "string",
19
+ description: "The name of the file where the test is located. File name must end with .spec.ts",
20
+ },
21
+ },
22
+ required: ["testName", "fileName"],
23
+ },
24
+ },
25
+ };
26
+ const runTestTool = async ({ testName, fileName, }) => {
27
+ // TODO: Remove this hardcoded project name
28
+ const result = await (0, test_run_1.runSingleTest)({
29
+ testName,
30
+ fileName,
31
+ projects: ["chromium"],
32
+ });
33
+ return result;
34
+ };
35
+ exports.runTestTool = runTestTool;
@@ -0,0 +1,2 @@
1
+ export declare function getGitDiff(filepath: string): string;
2
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKnD"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getGitDiff = void 0;
4
+ const child_process_1 = require("child_process");
5
+ function getGitDiff(filepath) {
6
+ const diff = (0, child_process_1.execSync)(`git diff ${filepath}`, {
7
+ encoding: "utf-8",
8
+ });
9
+ return diff;
10
+ }
11
+ exports.getGitDiff = getGitDiff;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-gen",
3
- "version": "0.47.2",
3
+ "version": "0.47.4",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -75,7 +75,8 @@
75
75
  "typescript": "^5.3.3",
76
76
  "@empiricalrun/llm": "^0.9.36",
77
77
  "@empiricalrun/r2-uploader": "^0.3.8",
78
- "@empiricalrun/reporter": "^0.23.1"
78
+ "@empiricalrun/reporter": "^0.23.1",
79
+ "@empiricalrun/test-run": "^0.7.1"
79
80
  },
80
81
  "devDependencies": {
81
82
  "@playwright/test": "1.47.1",
@@ -1,2 +0,0 @@
1
- export declare function parseJson(args: string): any;
2
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/agent/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,OAMrC"}
@@ -1,12 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseJson = void 0;
4
- function parseJson(args) {
5
- try {
6
- return JSON.parse(args);
7
- }
8
- catch (e) {
9
- console.error(`Failed to parse JSON with args ${args}`, e);
10
- }
11
- }
12
- exports.parseJson = parseJson;