@empiricalrun/test-gen 0.51.5 → 0.51.6

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 (40) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/agent/browsing/run.d.ts.map +1 -1
  3. package/dist/agent/chat/index.d.ts +5 -0
  4. package/dist/agent/chat/index.d.ts.map +1 -0
  5. package/dist/agent/chat/index.js +123 -0
  6. package/dist/agent/chat/prompt.d.ts +2 -0
  7. package/dist/agent/chat/prompt.d.ts.map +1 -0
  8. package/dist/agent/chat/prompt.js +74 -0
  9. package/dist/agent/chat/repo.d.ts +2 -0
  10. package/dist/agent/chat/repo.d.ts.map +1 -0
  11. package/dist/agent/chat/repo.js +64 -0
  12. package/dist/agent/cua/index.d.ts.map +1 -1
  13. package/dist/agent/cua/index.js +10 -35
  14. package/dist/agent/cua/model.d.ts +8 -0
  15. package/dist/agent/cua/model.d.ts.map +1 -0
  16. package/dist/agent/cua/model.js +35 -0
  17. package/dist/bin/index.js +1 -1
  18. package/dist/bin/logger/index.js +3 -3
  19. package/dist/bin/utils/platform/web/index.d.ts +5 -0
  20. package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
  21. package/dist/bin/utils/platform/web/index.js +13 -1
  22. package/dist/reporter/index.d.ts +1 -1
  23. package/dist/reporter/index.d.ts.map +1 -1
  24. package/dist/reporter/index.js +8 -8
  25. package/dist/reporter/lib.d.ts +31 -0
  26. package/dist/reporter/lib.d.ts.map +1 -0
  27. package/dist/reporter/lib.js +72 -0
  28. package/dist/tools/test-gen-browser.d.ts.map +1 -1
  29. package/dist/tools/test-gen-browser.js +7 -0
  30. package/dist/uploader/index.d.ts.map +1 -1
  31. package/dist/uploader/index.js +3 -3
  32. package/dist/uploader/utils.d.ts +8 -0
  33. package/dist/uploader/utils.d.ts.map +1 -0
  34. package/dist/uploader/utils.js +35 -0
  35. package/dist/utils/repo-tree.d.ts.map +1 -1
  36. package/dist/utils/repo-tree.js +2 -0
  37. package/package.json +4 -3
  38. package/dist/agent/chat.d.ts +0 -5
  39. package/dist/agent/chat.d.ts.map +0 -1
  40. package/dist/agent/chat.js +0 -188
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.51.6
4
+
5
+ ### Patch Changes
6
+
7
+ - dc17737: fix: repo context dir not found
8
+ - b058de5: feat: add app knowledge to chat agent system prompt
9
+ - dbe2ace: fix: remove Anthropic type from chat agent
10
+ - ce7fece: fix: improvements to cua usage in test-gen-browser tool call
11
+ - b8b4eff: fix: crash when last-chat is not found
12
+ - af84555: chore: remove reporter dependency from test-gen package
13
+ - 2766be8: feat: introduce chat model interface to support multiple llms
14
+ - Updated dependencies [dbe2ace]
15
+ - Updated dependencies [b8b4eff]
16
+ - Updated dependencies [2766be8]
17
+ - @empiricalrun/llm@0.11.5
18
+
3
19
  ## 0.51.5
4
20
 
5
21
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAiBA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,OAAO,GACR,EAAE,iBAAiB;;;GAgFnB"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/run.ts"],"names":[],"mappings":"AAiBA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAsB,6BAA6B,CAAC,EAClD,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,OAAO,GACR,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAgFD"}
@@ -0,0 +1,5 @@
1
+ export declare function chatAgent({ selectedModel, useDiskForChatState, }: {
2
+ selectedModel?: "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022";
3
+ useDiskForChatState?: boolean;
4
+ }): Promise<string>;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAsCA,wBAAsB,SAAS,CAAC,EAC9B,aAA4C,EAC5C,mBAA2B,GAC5B,EAAE;IACD,aAAa,CAAC,EAAE,4BAA4B,GAAG,4BAA4B,CAAC;IAC5E,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,mBAqGA"}
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chatAgent = void 0;
4
+ const chat_1 = require("@empiricalrun/llm/chat");
5
+ const picocolors_1 = require("picocolors");
6
+ const web_1 = require("../../bin/utils/platform/web");
7
+ const human_in_the_loop_1 = require("../../human-in-the-loop");
8
+ const diagnosis_fetcher_1 = require("../../tools/diagnosis-fetcher");
9
+ const grep_1 = require("../../tools/grep");
10
+ const test_gen_browser_1 = require("../../tools/test-gen-browser");
11
+ const test_run_1 = require("../../tools/test-run");
12
+ const test_run_fetcher_1 = require("../../tools/test-run-fetcher");
13
+ const zod_schema_1 = require("../../tools/zod-schema");
14
+ const prompt_1 = require("./prompt");
15
+ const tools = [
16
+ test_run_1.runTestTool,
17
+ test_gen_browser_1.generateTestWithBrowserAgent,
18
+ diagnosis_fetcher_1.diagnosisTool,
19
+ grep_1.grepTool,
20
+ test_run_fetcher_1.testRunTool,
21
+ ];
22
+ const toolExecutors = {
23
+ ...Object.fromEntries(tools.map((tool) => [tool.schema.name, tool.execute])),
24
+ str_replace_editor: (input) => (0, chat_1.strReplaceEditorTool)(input, web_1.validateTypescript),
25
+ };
26
+ function createChatModel(useDiskForChatState, selectedModel) {
27
+ if (selectedModel.startsWith("claude")) {
28
+ return new chat_1.ClaudeChatModel(useDiskForChatState);
29
+ }
30
+ throw new Error(`Unsupported model: ${selectedModel}`);
31
+ }
32
+ async function chatAgent({ selectedModel = "claude-3-7-sonnet-20250219", useDiskForChatState = false, }) {
33
+ let chatModel = createChatModel(useDiskForChatState, selectedModel);
34
+ let userPrompt = undefined;
35
+ const handleSigInt = () => {
36
+ console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatModel.getUsageSummary())}`);
37
+ process.exit(0);
38
+ };
39
+ process.once("SIGINT", handleSigInt);
40
+ process.once("SIGTERM", handleSigInt);
41
+ const ora = (await import("ora")).default;
42
+ if (chatModel.askUserForInput) {
43
+ // Show last message to the user for context when we loaded from disk
44
+ const latest = chatModel.getHumanReadableLatestMessage();
45
+ if (latest) {
46
+ console.log(`${latest.role}: ${latest.textMessage}`);
47
+ }
48
+ }
49
+ const systemPrompt = await (0, prompt_1.buildSystemPrompt)();
50
+ while (!userPrompt?.toLowerCase().includes("stop")) {
51
+ if (chatModel.askUserForInput) {
52
+ try {
53
+ userPrompt = await human_in_the_loop_1.humanLoop.getFeedback({
54
+ message: "User:",
55
+ });
56
+ }
57
+ catch (e) {
58
+ // https://github.com/SBoudrias/Inquirer.js/issues/1502#issuecomment-2275991680
59
+ if (e instanceof Error && e.name === "ExitPromptError") {
60
+ console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatModel.getUsageSummary())}`);
61
+ process.exit(0);
62
+ }
63
+ throw e;
64
+ }
65
+ chatModel.pushMessage({
66
+ role: "user",
67
+ content: [
68
+ {
69
+ type: "text",
70
+ text: userPrompt,
71
+ },
72
+ ],
73
+ });
74
+ continue;
75
+ }
76
+ const toolUse = chatModel.getPendingToolCall();
77
+ if (toolUse) {
78
+ const spinner = ora(`Executing tool ${toolUse.name} with args: ${JSON.stringify(toolUse.input)}`).start();
79
+ const toolExecutor = toolExecutors[toolUse.name];
80
+ if (!toolExecutor) {
81
+ throw new Error(`Tool ${toolUse.name} not found`);
82
+ }
83
+ const toolResult = await toolExecutor(toolUse.input);
84
+ if (toolResult.isError) {
85
+ spinner.fail(`Tool ${toolUse.name} failed with error: ${toolResult.result}`);
86
+ }
87
+ else {
88
+ spinner.succeed(`Tool ${toolUse.name} completed`);
89
+ }
90
+ chatModel.pushMessage({
91
+ role: "user",
92
+ content: [
93
+ {
94
+ type: "tool_result",
95
+ tool_use_id: toolUse.id,
96
+ content: toolResult.result,
97
+ is_error: toolResult.isError,
98
+ },
99
+ ],
100
+ });
101
+ continue;
102
+ }
103
+ const spinner = ora("Claude is working...").start();
104
+ const response = await chatModel.getLLMResponse({
105
+ systemPrompt,
106
+ tools: tools.map((tool) => (0, zod_schema_1.zodToOpenAITool)(tool.schema)),
107
+ selectedModel,
108
+ });
109
+ spinner.stop();
110
+ if (!response) {
111
+ throw new Error("No response from LLM");
112
+ }
113
+ chatModel.pushMessage(response);
114
+ const latest = chatModel.getHumanReadableLatestMessage();
115
+ if (latest) {
116
+ console.log(`${latest.role}: ${latest.textMessage}`);
117
+ }
118
+ }
119
+ const usageSummary = chatModel.getUsageSummary();
120
+ console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatModel.getUsageSummary())}`);
121
+ return usageSummary;
122
+ }
123
+ exports.chatAgent = chatAgent;
@@ -0,0 +1,2 @@
1
+ export declare function buildSystemPrompt(): Promise<string>;
2
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/prompt.ts"],"names":[],"mappings":"AAEA,wBAAsB,iBAAiB,oBAoEtC"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSystemPrompt = void 0;
4
+ const repo_1 = require("./repo");
5
+ async function buildSystemPrompt() {
6
+ const repoContext = await (0, repo_1.getRepoContext)();
7
+ return `
8
+ You are a helpful assistant that can answer questions and help with tasks related to writing
9
+ and maintaining Playwright tests.
10
+
11
+ # Supported capabilities
12
+
13
+ - Adding new Playwright tests or helper methods
14
+ - Going through test reports and identifying app issues versus test issues
15
+ - Modifying existing tests
16
+ - Modifying repo configuration (e.g. in playwright.config.ts and other)
17
+
18
+ # Going through test reports
19
+
20
+ - App issues: app issues caught by test failures, like UI issues, API endpoint issues, etc. These are issues that
21
+ will be reported to an app developer to investigate and fix.
22
+ - Test issues: Playwright tests can become outdated when app code changes. These are issues that need to be
23
+ fixed with modifications to the test code, and it is your job to do that.
24
+
25
+ # Tools
26
+
27
+ You are given a set of tools to help you fulfill the user's request. Read their descriptions to
28
+ understand what each tool does.
29
+
30
+ For example, if the user asks you to run a test, you could use the runTest tool.
31
+ Once the test is run, you will receive the results in the form of a JSON object.
32
+ Summarize the results in a few sentences.
33
+
34
+ If the user provides a diagnosis URL, you can use the fetchDiagnosisDetails tool
35
+ to get more information about the test case and its results.
36
+
37
+ If the user provides a test run URL, you can use the fetchTestRunDetails tool
38
+ to get detailed information about a specific test run.
39
+
40
+ Or if the user asks you to modify a test, you could use the generateTestWithBrowserAgent tool. If you suspect
41
+ that a UI selector needs to be updated, using the browser agent is a good idea.
42
+
43
+ Before using generateTestWithBrowserAgent, you need to prepare the test code for the browser agent.
44
+ You can do this by using the str_replace_editor tool to add a TODO comment to the test code. This
45
+ comment should explain to the browser agent what to do.
46
+
47
+ For example, if the expected modification is to click on a login button, you could add the following comment.
48
+
49
+ // TODO(agent): Click on the login button
50
+
51
+ The position of the comment is important: the browser agent will look for this comment and replace it with
52
+ the actual code to click on the login button. If you are fixing a failing test, your comment should be
53
+ around the failing line of code, so that it can be replaced/modified.
54
+
55
+ # Rules for fixing Playwright tests
56
+
57
+ You must follow these rules while adding new tests or modifying existing tests. There can be exceptions to these rules, but
58
+ ONLY when explicitly asked for by the user.
59
+
60
+ 1. Do not add any conditional logic or try catch blocks in a test. A good test deterministically tests a user scenario
61
+ 2. Trust Playwright's ability to auto-wait while taking actions on elements. For example, do not add checks on locator.isVisible() before clicking on it: Playwright already does this
62
+ 3. Do not add waitForTimeout or waitForLoadState in a test. Playwright will automatically wait for the page to load.
63
+ 4. You can't delete some steps from the test to make it pass. The test needs to accomplish its objective (which is to validate a particular user scenario)
64
+
65
+ # Proactiveness
66
+
67
+ You are allowed to be proactive, but ONLY for read-only actions, like searching for content, reading files, fetching data from tools, and
68
+ running Playwright tests. For any read-write actions (e.g. modifying any file), you should share your plan and get the user's approval before proceeding.
69
+
70
+ # Repo context
71
+ ${repoContext}
72
+ `;
73
+ }
74
+ exports.buildSystemPrompt = buildSystemPrompt;
@@ -0,0 +1,2 @@
1
+ export declare function getRepoContext(): Promise<string>;
2
+ //# sourceMappingURL=repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/repo.ts"],"names":[],"mappings":"AAmCA,wBAAsB,cAAc,oBAwBnC"}
@@ -0,0 +1,64 @@
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.getRepoContext = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const repo_tree_1 = require("../../utils/repo-tree");
10
+ async function getAllMarkdownFiles() {
11
+ const dir = path_1.default.join(process.cwd(), ".empiricalrun");
12
+ if (!fs_extra_1.default.existsSync(dir)) {
13
+ return [];
14
+ }
15
+ const files = await fs_extra_1.default.readdir(dir);
16
+ return files
17
+ .filter((file) => file.endsWith(".md"))
18
+ .map((file) => {
19
+ return {
20
+ name: file,
21
+ content: fs_extra_1.default.readFileSync(path_1.default.join(dir, file), "utf8"),
22
+ };
23
+ });
24
+ }
25
+ async function knowledgeContext() {
26
+ const mdFiles = await getAllMarkdownFiles();
27
+ const knowledge = mdFiles.map((file) => {
28
+ return `
29
+ <knowledge_file>
30
+ <file_name>${file.name}</file_name>
31
+ <file_content>
32
+ ${file.content}
33
+ </file_content>
34
+ </knowledge_file>
35
+ `;
36
+ });
37
+ return knowledge.join("\n");
38
+ }
39
+ async function getRepoContext() {
40
+ let REPO_CONTEXT_PROMPT = `
41
+ You are running as a CLI tool inside the directory of the repo that has Playwright tests.
42
+
43
+ Here is the repo directory structure:
44
+
45
+ ${(0, repo_tree_1.generateAsciiTree)(process.cwd())}
46
+
47
+ While specifying paths to files, use relative paths from the current working directory. For example:
48
+ - Correct path: "tests/lesson.spec.ts"
49
+ - Incorrect path: "/repo/tests/lesson.spec.ts" or "${path_1.default.basename(process.cwd())}/tests/lesson.spec.ts"
50
+ `;
51
+ const knowledge = await knowledgeContext();
52
+ if (knowledge.length > 0) {
53
+ REPO_CONTEXT_PROMPT += `
54
+ ## Repo-specific knowledge
55
+
56
+ You can use the following knowledge with your tasks.
57
+ <knowledge>
58
+ ${knowledge}
59
+ </knowledge>
60
+ `;
61
+ }
62
+ return REPO_CONTEXT_PROMPT;
63
+ }
64
+ exports.getRepoContext = getRepoContext;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAWlC,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,IAAI,iBAoBtD;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CAAC,EACpD,IAAI,EACJ,IAAI,GACL,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC,CAmGD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAMlC,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,IAAI,iBAoBtD;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CAAC,EACpD,IAAI,EACJ,IAAI,GACL,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC,CAkFD"}
@@ -1,17 +1,9 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.createTestUsingComputerUseAgent = exports.startPlaywrightCodegen = void 0;
7
- const openai_1 = __importDefault(require("openai"));
8
4
  const utils_1 = require("../browsing/utils");
9
5
  const computer_1 = require("./computer");
10
- const INSTRUCTIONS = `You will be asked to execute some actions in a browser context.
11
- Don't ask the user for confirmations - just execute the actions.
12
-
13
- For example, if the user message says "Click on Submit button", then
14
- you click on the submit button -- even if it looks like a scary action.`;
6
+ const model_1 = require("./model");
15
7
  async function startPlaywrightCodegen(page) {
16
8
  // TODO: Use this method to offload code generation to Playwright
17
9
  // Unclear how to retrieve source code that is generated
@@ -44,18 +36,7 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
44
36
  const viewport = page.viewportSize();
45
37
  let screenWidth = viewport?.width || 1280;
46
38
  let screenHeight = viewport?.height || 720;
47
- const openai = new openai_1.default();
48
- let response = await openai.responses.create({
49
- model: "computer-use-preview",
50
- tools: [
51
- {
52
- type: "computer-preview",
53
- display_width: screenWidth,
54
- display_height: screenHeight,
55
- environment: "browser",
56
- },
57
- ],
58
- instructions: INSTRUCTIONS,
39
+ let response = await (0, model_1.callComputerUseModel)({
59
40
  input: [
60
41
  {
61
42
  role: "user",
@@ -72,7 +53,8 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
72
53
  ],
73
54
  },
74
55
  ],
75
- truncation: "auto",
56
+ screenWidth,
57
+ screenHeight,
76
58
  });
77
59
  // eslint-disable-next-line no-constant-condition
78
60
  while (true) {
@@ -92,6 +74,7 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
92
74
  const computerCall = computerCalls[0];
93
75
  const lastCallId = computerCall.call_id;
94
76
  const action = computerCall.action;
77
+ const pendingSafetyChecks = computerCall.pending_safety_checks;
95
78
  // Execute the action (function defined in step 3)
96
79
  const actionCode = await (0, computer_1.handleModelAction)(page, action);
97
80
  generatedCode += actionCode;
@@ -99,18 +82,8 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
99
82
  // Take a screenshot after the action (function defined in step 4)
100
83
  const screenshotBytes = await (0, computer_1.getScreenshot)(page);
101
84
  // Send the screenshot back as a computer_call_output
102
- response = await openai.responses.create({
103
- model: "computer-use-preview",
104
- previous_response_id: response.id,
105
- tools: [
106
- {
107
- type: "computer-preview",
108
- display_width: screenWidth,
109
- display_height: screenHeight,
110
- environment: "browser",
111
- },
112
- ],
113
- instructions: INSTRUCTIONS,
85
+ response = await (0, model_1.callComputerUseModel)({
86
+ previousResponseId: response.id,
114
87
  input: [
115
88
  {
116
89
  call_id: lastCallId,
@@ -119,9 +92,11 @@ async function createTestUsingComputerUseAgent({ page, task, }) {
119
92
  type: "computer_screenshot",
120
93
  image_url: `data:image/png;base64,${screenshotBytes}`,
121
94
  },
95
+ acknowledged_safety_checks: pendingSafetyChecks,
122
96
  },
123
97
  ],
124
- truncation: "auto",
98
+ screenWidth,
99
+ screenHeight,
125
100
  });
126
101
  }
127
102
  return {
@@ -0,0 +1,8 @@
1
+ import { Response, ResponseInput } from "openai/resources/responses/responses.mjs";
2
+ export declare function callComputerUseModel({ input, previousResponseId, screenWidth, screenHeight, }: {
3
+ input: ResponseInput;
4
+ previousResponseId?: string;
5
+ screenWidth: number;
6
+ screenHeight: number;
7
+ }): Promise<Response>;
8
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/agent/cua/model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,aAAa,EACd,MAAM,0CAA0C,CAAC;AAQlD,wBAAsB,oBAAoB,CAAC,EACzC,KAAK,EACL,kBAAkB,EAClB,WAAW,EACX,YAAY,GACb,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqBpB"}
@@ -0,0 +1,35 @@
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.callComputerUseModel = void 0;
7
+ const openai_1 = __importDefault(require("openai"));
8
+ const INSTRUCTIONS = `You will be asked to execute some actions in a browser context.
9
+ Don't ask the user for confirmations - just execute the actions.
10
+
11
+ For example, if the user message says "Click on Submit button", then
12
+ you click on the submit button -- even if it looks like a scary action.`;
13
+ async function callComputerUseModel({ input, previousResponseId, screenWidth, screenHeight, }) {
14
+ const openai = new openai_1.default();
15
+ return await openai.responses.create({
16
+ model: "computer-use-preview",
17
+ previous_response_id: previousResponseId,
18
+ tools: [
19
+ {
20
+ type: "computer-preview",
21
+ display_width: screenWidth,
22
+ display_height: screenHeight,
23
+ environment: "browser",
24
+ },
25
+ ],
26
+ reasoning: {
27
+ effort: "medium",
28
+ generate_summary: "concise",
29
+ },
30
+ instructions: INSTRUCTIONS,
31
+ input,
32
+ truncation: "auto",
33
+ });
34
+ }
35
+ exports.callComputerUseModel = callComputerUseModel;
package/dist/bin/index.js CHANGED
@@ -46,7 +46,7 @@ async function runChatAgent(modelInput, useDiskForChatState) {
46
46
  throw new Error(`Invalid chat model: ${modelInput}`);
47
47
  }
48
48
  return await (0, chat_1.chatAgent)({
49
- chatModel: modelInput ? MODEL_MAPPING[modelInput] : undefined,
49
+ selectedModel: modelInput ? MODEL_MAPPING[modelInput] : undefined,
50
50
  useDiskForChatState,
51
51
  });
52
52
  }
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.waitForLogsToFlush = exports.CustomLogger = void 0;
4
- const reporter_1 = require("@empiricalrun/reporter");
5
4
  const picocolors_1 = require("picocolors");
6
- const reporter_2 = require("../../reporter");
5
+ const reporter_1 = require("../../reporter");
6
+ const lib_1 = require("../../reporter/lib");
7
7
  let queuedReporterMessages = [];
8
8
  class CustomLogger {
9
9
  useReporter = false;
@@ -13,7 +13,7 @@ class CustomLogger {
13
13
  logToReporter(message) {
14
14
  if (this.useReporter) {
15
15
  (() => {
16
- const promise = (0, reporter_2.getReporter)()?.report(new reporter_1.ProcessLogMessageBuilder({ type: "message", message: message }));
16
+ const promise = (0, reporter_1.getReporter)()?.report(new lib_1.MessageBuilder({ type: "message", message: message }));
17
17
  if (promise) {
18
18
  queuedReporterMessages.push(promise);
19
19
  }
@@ -18,6 +18,11 @@ export declare function getTypescriptTestBlock({ scenarioName, suites, content,
18
18
  testNode: Node | undefined;
19
19
  testAlias: string;
20
20
  };
21
+ export declare function hasTestBlock({ testName, testSuites, filePath, }: {
22
+ testName: string;
23
+ testSuites: string[];
24
+ filePath: string;
25
+ }): boolean;
21
26
  export declare function hasTopLevelDescribeConfigureWithSerialMode(filePath: string): Promise<boolean>;
22
27
  /**
23
28
  * Function to find the first 'describe' block configured with 'serial: true'
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAK3D,OAAO,EAGL,IAAI,EAEJ,UAAU,EAEX,MAAM,UAAU,CAAC;AAIlB,eAAO,MAAM,gCAAgC,eAC/B,UAAU,KACrB,MAgBF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,YAAY,EACZ,MAAM,EACN,OAAO,GACR,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG;IACF,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,IAAI,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB,CA2CA;AAwBD,wBAAsB,0CAA0C,CAC9D,QAAQ,EAAE,MAAM,oBA+BjB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,IAAI,GAAG,SAAS,CA4BlB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA8C7D;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,mCAWjB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,iBAShD;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,iBAgBrE;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,UAE5E;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,iBAMpD;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,UAcpD;AAED,wBAAsB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,+BAoBvE;AA+CD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,UAoCtB;AAED,eAAO,MAAM,6BAA6B;qBAKvB,MAAM;iBACV,MAAM;YACX,MAAM,EAAE;YA2DjB,CAAC;AAEF,eAAO,MAAM,iCAAiC,YACnC,MAAM,aACJ,MAAM,EAAE,gBACL,MAAM,sBAyBrB,CAAC;AAEF,wBAAsB,qBAAqB,CAAC,EAC1C,YAAY,EACZ,QAAQ,EACR,MAAM,GACP,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,iBAgDA;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EAAE,iBAsBzB;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;CACpB,WAYA;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,UAOA;AAED,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA4B5E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/bin/utils/platform/web/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAK3D,OAAO,EAGL,IAAI,EAEJ,UAAU,EAEX,MAAM,UAAU,CAAC;AAIlB,eAAO,MAAM,gCAAgC,eAC/B,UAAU,KACrB,MAgBF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,YAAY,EACZ,MAAM,EACN,OAAO,GACR,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG;IACF,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,IAAI,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB,CA2CA;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,UAAU,EACV,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,WAUA;AAwBD,wBAAsB,0CAA0C,CAC9D,QAAQ,EAAE,MAAM,oBA+BjB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,IAAI,GAAG,SAAS,CA4BlB;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA8C7D;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,mCAWjB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,iBAShD;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,iBAgBrE;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,UAE5E;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,iBAMpD;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,UAcpD;AAED,wBAAsB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,+BAoBvE;AA+CD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,UAoCtB;AAED,eAAO,MAAM,6BAA6B;qBAKvB,MAAM;iBACV,MAAM;YACX,MAAM,EAAE;YA2DjB,CAAC;AAEF,eAAO,MAAM,iCAAiC,YACnC,MAAM,aACJ,MAAM,EAAE,gBACL,MAAM,sBAyBrB,CAAC;AAEF,wBAAsB,qBAAqB,CAAC,EAC1C,YAAY,EACZ,QAAQ,EACR,MAAM,GACP,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,iBAgDA;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EAAE,iBAsBzB;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;CACpB,WAYA;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,UAOA;AAED,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA4B5E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnD"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isSyntaxValid = exports.getVariableDeclarationsFromCode = exports.buildTestNamePrompt = exports.isTestPresent = exports.appendScopeToCreateTest = exports.addUserContextFixture = exports.importAllExportsStmtFromFilePaths = exports.injectCodeSnippetBySuiteChain = exports.replaceCreateTestWithNewCode = exports.getPageVariableNameFromCreateTest = exports.getFixtureImportPath = exports.removeTestOnly = exports.addNewImport = exports.formatCode = exports.lintErrors = exports.stripAndPrependImports = exports.validateTypescript = exports.appendToTestBlock = exports.findFirstSerialDescribeBlock = exports.hasTopLevelDescribeConfigureWithSerialMode = exports.getTypescriptTestBlock = exports.getTestModuleAliasFromSourceFile = void 0;
6
+ exports.isSyntaxValid = exports.getVariableDeclarationsFromCode = exports.buildTestNamePrompt = exports.isTestPresent = exports.appendScopeToCreateTest = exports.addUserContextFixture = exports.importAllExportsStmtFromFilePaths = exports.injectCodeSnippetBySuiteChain = exports.replaceCreateTestWithNewCode = exports.getPageVariableNameFromCreateTest = exports.getFixtureImportPath = exports.removeTestOnly = exports.addNewImport = exports.formatCode = exports.lintErrors = exports.stripAndPrependImports = exports.validateTypescript = exports.appendToTestBlock = exports.findFirstSerialDescribeBlock = exports.hasTopLevelDescribeConfigureWithSerialMode = exports.hasTestBlock = exports.getTypescriptTestBlock = exports.getTestModuleAliasFromSourceFile = void 0;
7
7
  const parser_1 = require("@babel/parser");
8
8
  const eslint_1 = require("eslint");
9
9
  const fs_extra_1 = __importDefault(require("fs-extra"));
@@ -72,6 +72,18 @@ function getTypescriptTestBlock({ scenarioName, suites, content, }) {
72
72
  };
73
73
  }
74
74
  exports.getTypescriptTestBlock = getTypescriptTestBlock;
75
+ function hasTestBlock({ testName, testSuites, filePath, }) {
76
+ if (!fs_extra_1.default.existsSync(filePath)) {
77
+ return false;
78
+ }
79
+ const { testBlock } = getTypescriptTestBlock({
80
+ scenarioName: testName,
81
+ content: fs_extra_1.default.readFileSync(filePath, "utf-8"),
82
+ suites: testSuites,
83
+ });
84
+ return Boolean(testBlock);
85
+ }
86
+ exports.hasTestBlock = hasTestBlock;
75
87
  // get the names of parent describe blocks
76
88
  function getParentDescribeNames(node) {
77
89
  const names = [];
@@ -1,4 +1,4 @@
1
- import { Reporter } from "@empiricalrun/reporter";
1
+ import { Reporter } from "./lib";
2
2
  type ReporterConfigType = {
3
3
  testSessionId: number;
4
4
  generationId: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporter/index.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAa5E,KAAK,kBAAkB,GAAG;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAKF,wBAAgB,WAAW,IAAI,QAAQ,GAAG,SAAS,CAUlD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAGlE;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,OAAO,CAAS;;IAKlB,eAAe,CAAC,EACpB,eAAe,EACf,QAAQ,GACT,EAAE;QACD,eAAe,EAAE,MAAM,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB;IAgDK,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAWxD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reporter/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAkB,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjD,KAAK,kBAAkB,GAAG;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAKF,wBAAgB,WAAW,IAAI,QAAQ,GAAG,SAAS,CAUlD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAGlE;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,OAAO,CAAS;;IAKlB,eAAe,CAAC,EACpB,eAAe,EACf,QAAQ,GACT,EAAE;QACD,eAAe,EAAE,MAAM,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB;IAgDK,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAWxD"}
@@ -5,11 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.TestGenUpdatesReporter = exports.setReporterConfig = exports.getReporter = void 0;
7
7
  const r2_uploader_1 = require("@empiricalrun/r2-uploader");
8
- const reporter_1 = require("@empiricalrun/reporter");
9
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
10
9
  const path_1 = __importDefault(require("path"));
11
10
  const logger_1 = require("../bin/logger");
12
11
  const uploader_1 = require("../uploader");
12
+ const lib_1 = require("./lib");
13
13
  let reporterInstance = undefined;
14
14
  let reporterConfig = undefined;
15
15
  function getReporter() {
@@ -19,7 +19,7 @@ function getReporter() {
19
19
  }
20
20
  // initialise once config is set
21
21
  if (!reporterInstance && reporterConfig) {
22
- reporterInstance = new reporter_1.Reporter(reporterConfig);
22
+ reporterInstance = new lib_1.Reporter(reporterConfig);
23
23
  }
24
24
  return reporterInstance;
25
25
  }
@@ -55,7 +55,7 @@ class TestGenUpdatesReporter {
55
55
  await Promise.allSettled([
56
56
  ...(videoUrls.length
57
57
  ? [
58
- reporter?.report(new reporter_1.ProcessLogMessageBuilder({
58
+ reporter?.report(new lib_1.MessageBuilder({
59
59
  type: "video",
60
60
  message: JSON.stringify({
61
61
  type: "video",
@@ -66,7 +66,7 @@ class TestGenUpdatesReporter {
66
66
  : []),
67
67
  ...(traceFiles.length
68
68
  ? [
69
- reporter?.report(new reporter_1.ProcessLogMessageBuilder({
69
+ reporter?.report(new lib_1.MessageBuilder({
70
70
  type: "pw-trace",
71
71
  message: JSON.stringify({
72
72
  type: "trace",
@@ -107,7 +107,7 @@ class TestGenUpdatesReporter {
107
107
  const filePath = Object.keys(files)[0];
108
108
  const relativeFilePath = filePath.replace(path_1.default.join(this.repoDir, "gen-assets"), "");
109
109
  const url = `${uploader_1.UPLOAD_DOMAIN}/${uploadDir}${relativeFilePath}`;
110
- await getReporter()?.report(new reporter_1.ProcessLogMessageBuilder({
110
+ await getReporter()?.report(new lib_1.MessageBuilder({
111
111
  type: "current-snapshot",
112
112
  message: JSON.stringify({ type: "current-view", url }),
113
113
  }));
@@ -122,7 +122,7 @@ class TestGenUpdatesReporter {
122
122
  async sendMessage(message) {
123
123
  const reporter = getReporter();
124
124
  if (reporter) {
125
- await reporter.report(new reporter_1.ProcessLogMessageBuilder({
125
+ await reporter.report(new lib_1.MessageBuilder({
126
126
  type: "message",
127
127
  message,
128
128
  }));
@@ -131,7 +131,7 @@ class TestGenUpdatesReporter {
131
131
  async sendLogUrl(message) {
132
132
  const reporter = getReporter();
133
133
  if (reporter) {
134
- await reporter.report(new reporter_1.ProcessLogMessageBuilder({
134
+ await reporter.report(new lib_1.MessageBuilder({
135
135
  type: "log-url",
136
136
  message,
137
137
  }));
@@ -140,7 +140,7 @@ class TestGenUpdatesReporter {
140
140
  async sendAgentTraceUrl(message) {
141
141
  const reporter = getReporter();
142
142
  if (reporter) {
143
- await reporter.report(new reporter_1.ProcessLogMessageBuilder({
143
+ await reporter.report(new lib_1.MessageBuilder({
144
144
  type: "agent-trace",
145
145
  message,
146
146
  }));
@@ -0,0 +1,31 @@
1
+ type DashboardMessagePayload = {
2
+ message: string;
3
+ messageType: "message" | "agent-trace" | "current-snapshot" | "video" | "pw-trace" | "log-url" | "commit-id";
4
+ pull_request?: string;
5
+ testCaseName?: string;
6
+ testCaseId?: number;
7
+ projectRepoName?: string;
8
+ testSessionId?: number;
9
+ generationId?: number;
10
+ };
11
+ export declare class MessageBuilder {
12
+ private props;
13
+ constructor(props: {
14
+ message: string;
15
+ type: DashboardMessagePayload["messageType"];
16
+ });
17
+ buildMessage(dashboardOptions?: {
18
+ override?: Partial<DashboardMessagePayload>;
19
+ }): DashboardMessagePayload;
20
+ }
21
+ export declare class Reporter {
22
+ private config;
23
+ constructor(config: {
24
+ testSessionId: number;
25
+ generationId: number;
26
+ });
27
+ report(messageBuilder: MessageBuilder): Promise<void>;
28
+ private sendMessageToDashboard;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/reporter/lib.ts"],"names":[],"mappings":"AAMA,KAAK,uBAAuB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EACP,SAAS,GACT,aAAa,GACb,kBAAkB,GAClB,OAAO,GACP,UAAU,GACV,SAAS,GACT,WAAW,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,qBAAa,cAAc;IAEvB,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,uBAAuB,CAAC,aAAa,CAAC,CAAC;KAC9C;IAGH,YAAY,CAAC,gBAAgB,CAAC,EAAE;QAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;KAC7C,GAAG,uBAAuB;CAO5B;AAED,qBAAa,QAAQ;IAEjB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;KACtB;IAGG,MAAM,CAAC,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;YAc7C,sBAAsB;CAmCrC"}
@@ -0,0 +1,72 @@
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.Reporter = exports.MessageBuilder = void 0;
7
+ const async_retry_1 = __importDefault(require("async-retry"));
8
+ const DASHBOARD_DOMAIN = process.env.DASHBOARD_DOMAIN ||
9
+ (process.env.CI === "true" ? "https://dash.empirical.run" : "");
10
+ class MessageBuilder {
11
+ props;
12
+ constructor(props) {
13
+ this.props = props;
14
+ }
15
+ buildMessage(dashboardOptions) {
16
+ return {
17
+ ...dashboardOptions?.override,
18
+ message: this.props.message,
19
+ messageType: this.props.type,
20
+ };
21
+ }
22
+ }
23
+ exports.MessageBuilder = MessageBuilder;
24
+ class Reporter {
25
+ config;
26
+ constructor(config) {
27
+ this.config = config;
28
+ }
29
+ async report(messageBuilder) {
30
+ const message = messageBuilder.buildMessage({
31
+ override: {
32
+ testSessionId: this.config.testSessionId,
33
+ generationId: this.config.generationId,
34
+ },
35
+ });
36
+ if (!message) {
37
+ console.info("No message found. Skipping sending message to dashboard");
38
+ return;
39
+ }
40
+ await this.sendMessageToDashboard(message);
41
+ }
42
+ async sendMessageToDashboard(message) {
43
+ if (!DASHBOARD_DOMAIN) {
44
+ console.warn("No dashboard domain found. Skipping send message to dashboard");
45
+ return;
46
+ }
47
+ try {
48
+ await (0, async_retry_1.default)(async () => {
49
+ const body = JSON.stringify(message);
50
+ await fetch(`${DASHBOARD_DOMAIN}/api/github/updates`, {
51
+ method: "POST",
52
+ headers: {
53
+ "Content-Type": "application/json",
54
+ // TODO: fix this with authentication of github updates api
55
+ Authorization: "weQPMWKT",
56
+ },
57
+ body,
58
+ });
59
+ }, {
60
+ retries: 3,
61
+ minTimeout: 1000,
62
+ maxTimeout: 60000,
63
+ factor: 3,
64
+ });
65
+ }
66
+ catch (e) {
67
+ console.error(`Error sending message to Dashboard: ${e.code}`);
68
+ console.error(e.message);
69
+ }
70
+ }
71
+ }
72
+ exports.Reporter = Reporter;
@@ -1 +1 @@
1
- {"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAmDpC,eAAO,MAAM,4BAA4B,EAAE,IAiE1C,CAAC"}
1
+ {"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAmDpC,eAAO,MAAM,4BAA4B,EAAE,IAuE1C,CAAC"}
@@ -4,6 +4,7 @@ exports.generateTestWithBrowserAgent = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const run_1 = require("../agent/browsing/run");
6
6
  const utils_1 = require("../agent/browsing/utils");
7
+ const web_1 = require("../bin/utils/platform/web");
7
8
  const scenarios_1 = require("../bin/utils/scenarios");
8
9
  const git_1 = require("../utils/git");
9
10
  const BrowserAgentSchema = zod_1.z.object({
@@ -65,6 +66,12 @@ exports.generateTestWithBrowserAgent = {
65
66
  result: `Invalid project name: ${project}. Valid project names are: ${validProjectNames.join(", ")}`,
66
67
  };
67
68
  }
69
+ if (!(0, web_1.hasTestBlock)({ testName, testSuites, filePath: fileName })) {
70
+ return {
71
+ isError: true,
72
+ result: `Test block not found for test name: "${testName}" in file: "${fileName}" with describe blocks: "${testSuites.join(", ")}"`,
73
+ };
74
+ }
68
75
  try {
69
76
  await (0, utils_1.replaceTodoWithCreateTest)({
70
77
  testCaseName: testName,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploader/index.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAC3C,eAAO,MAAM,aAAa,kCAAkC,CAAC;AAG7D,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,UAKlB;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAEtE;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,EAClD,eAAe,EACf,QAAQ,EACR,OAAO,GACR,EAAE;IACD,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC,CAgED;AAED,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,UAM1D;AAED,wBAAgB,2BAA2B,uBAQ1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploader/index.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAC3C,eAAO,MAAM,aAAa,kCAAkC,CAAC;AAG7D,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,UAKlB;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAEtE;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,EAClD,eAAe,EACf,QAAQ,EACR,OAAO,GACR,EAAE;IACD,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC,CAgED;AAED,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,UAM1D;AAED,wBAAgB,2BAA2B,uBAQ1C"}
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.checkIfResultsUploadAllowed = exports.getUploadPathForRun = exports.uploadTestResultsUsingPrjRepo = exports.getRelativeUploadPath = exports.getFullUploadPath = exports.UPLOAD_DOMAIN = exports.UPLOAD_BUCKET = void 0;
7
7
  const r2_uploader_1 = require("@empiricalrun/r2-uploader");
8
- const reporter_1 = require("@empiricalrun/reporter");
9
8
  const path_1 = __importDefault(require("path"));
9
+ const utils_1 = require("./utils");
10
10
  // json summary of test results
11
11
  // originally we used to upload test results directory for this
12
12
  // but now we have our test results (per testcase artifacts) inside playwright-report/data
@@ -55,8 +55,8 @@ async function uploadTestResultsUsingPrjRepo({ projectRepoName, testName, repoDi
55
55
  const fileNames = Object.keys(files);
56
56
  console.log("Uploaded files", fileNames.map((f) => getFullUploadPath(repoDir, f, uploadDir)));
57
57
  const defaultLocation = path_1.default.join(repoDir, "playwright-report", "summary.json");
58
- const results = (0, reporter_1.parseJsonReport)(defaultLocation);
59
- const flatTestsList = (0, reporter_1.getFlattenedTestList)(results.suites);
58
+ const results = (0, utils_1.parseJsonReport)(defaultLocation);
59
+ const flatTestsList = (0, utils_1.getFlattenedTestList)(results.suites);
60
60
  const testAttachmentPaths = [];
61
61
  for (const test of flatTestsList) {
62
62
  if (test.title === testName) {
@@ -0,0 +1,8 @@
1
+ import type { JSONReportSpec, JSONReportSuite } from "@playwright/test/reporter";
2
+ export declare function parseJsonReport<T>(srcFile: string): T;
3
+ export type flattenedSpecT = JSONReportSpec & {
4
+ nesting: string[];
5
+ suitesString: string;
6
+ };
7
+ export declare const getFlattenedTestList: (suites: JSONReportSuite[]) => flattenedSpecT[];
8
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/uploader/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAChB,MAAM,2BAA2B,CAAC;AAGnC,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAErD;AAMD,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG;IAC5C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AACF,eAAO,MAAM,oBAAoB,WACvB,eAAe,EAAE,KACxB,cAAc,EA4BhB,CAAC"}
@@ -0,0 +1,35 @@
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.getFlattenedTestList = exports.parseJsonReport = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ function parseJsonReport(srcFile) {
9
+ return JSON.parse(fs_1.default.readFileSync(srcFile, { encoding: "utf-8" }));
10
+ }
11
+ exports.parseJsonReport = parseJsonReport;
12
+ const getFlattenedTestList = (suites) => {
13
+ let flattenedSpecs = [];
14
+ const traverseSuites = (suite, nesting = []) => {
15
+ nesting = [...nesting, suite.title];
16
+ flattenedSpecs.push(...suite.specs.map((spec) => {
17
+ const finalNesting = [...nesting, spec.title];
18
+ const suites = nesting.slice(1);
19
+ const suitesString = suites.length ? suites.join("~~") : "~~";
20
+ return {
21
+ ...spec,
22
+ nesting: finalNesting,
23
+ suitesString,
24
+ };
25
+ }));
26
+ // Traverse nested suites recursively
27
+ if (suite.suites && suite.suites.length > 0) {
28
+ suite.suites.forEach((suite) => traverseSuites(suite, nesting));
29
+ }
30
+ };
31
+ // Start the traversal from the top-level suites
32
+ suites.forEach((suite) => traverseSuites(suite));
33
+ return flattenedSpecs;
34
+ };
35
+ exports.getFlattenedTestList = getFlattenedTestList;
@@ -1 +1 @@
1
- {"version":3,"file":"repo-tree.d.ts","sourceRoot":"","sources":["../../src/utils/repo-tree.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,qBAQ3B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,UAsE9D"}
1
+ {"version":3,"file":"repo-tree.d.ts","sourceRoot":"","sources":["../../src/utils/repo-tree.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,qBAU3B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,UAsE9D"}
@@ -13,7 +13,9 @@ exports.DEFAULT_EXCLUDE = [
13
13
  /\.git/,
14
14
  ".DS_Store",
15
15
  "playwright-report",
16
+ "test-results",
16
17
  ".empiricalrun",
18
+ "auth",
17
19
  ];
18
20
  function generateAsciiTree(dirPath, options = {}) {
19
21
  const defaultOptions = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-gen",
3
- "version": "0.51.5",
3
+ "version": "0.51.6",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -48,6 +48,7 @@
48
48
  "@actions/core": "^1.10.1",
49
49
  "@babel/parser": "^7.26.3",
50
50
  "@types/sanitize-html": "^2.11.0",
51
+ "async-retry": "^1.3.3",
51
52
  "commander": "^12.1.0",
52
53
  "detect-port": "^1.6.1",
53
54
  "dotenv": "^16.4.5",
@@ -74,13 +75,13 @@
74
75
  "tsx": "^4.16.2",
75
76
  "typescript": "^5.3.3",
76
77
  "zod": "^3.23.8",
77
- "@empiricalrun/llm": "^0.11.4",
78
+ "@empiricalrun/llm": "^0.11.5",
78
79
  "@empiricalrun/r2-uploader": "^0.3.8",
79
- "@empiricalrun/reporter": "^0.23.2",
80
80
  "@empiricalrun/test-run": "^0.7.6"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@playwright/test": "1.47.1",
84
+ "@types/async-retry": "^1.4.8",
84
85
  "@types/detect-port": "^1.3.5",
85
86
  "@types/express": "^4.17.21",
86
87
  "@types/fs-extra": "^11.0.4",
@@ -1,5 +0,0 @@
1
- export declare function chatAgent({ chatModel, useDiskForChatState, }: {
2
- chatModel?: "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022";
3
- useDiskForChatState?: boolean;
4
- }): Promise<string>;
5
- //# sourceMappingURL=chat.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/agent/chat.ts"],"names":[],"mappings":"AA+FA,wBAAsB,SAAS,CAAC,EAC9B,SAAwC,EACxC,mBAAmB,GACpB,EAAE;IACD,SAAS,CAAC,EAAE,4BAA4B,GAAG,4BAA4B,CAAC;IACxE,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,mBAkHA"}
@@ -1,188 +0,0 @@
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.chatAgent = void 0;
7
- const chat_1 = require("@empiricalrun/llm/chat");
8
- const path_1 = __importDefault(require("path"));
9
- const picocolors_1 = require("picocolors");
10
- const web_1 = require("../bin/utils/platform/web");
11
- const human_in_the_loop_1 = require("../human-in-the-loop");
12
- const diagnosis_fetcher_1 = require("../tools/diagnosis-fetcher");
13
- const grep_1 = require("../tools/grep");
14
- const test_gen_browser_1 = require("../tools/test-gen-browser");
15
- const test_run_1 = require("../tools/test-run");
16
- const test_run_fetcher_1 = require("../tools/test-run-fetcher");
17
- const zod_schema_1 = require("../tools/zod-schema");
18
- const repo_tree_1 = require("../utils/repo-tree");
19
- const systemPrompt = `
20
- You are a helpful assistant that can answer questions and help with tasks.
21
- You are given a set of tools to use to fulfill the user's request. Read their descriptions to
22
- understand what each tool does.
23
-
24
- # Tools
25
-
26
- For example, if the user asks you to run a test, you could use the runTest tool.
27
- Once the test is run, you will receive the results in the form of a JSON object.
28
- Summarize the results in a few sentences.
29
-
30
- If the user provides a diagnosis URL, you can use the fetchDiagnosisDetails tool
31
- to get more information about the test case and its results.
32
-
33
- If the user provides a test run URL, you can use the fetchTestRunDetails tool
34
- to get detailed information about a specific test run.
35
-
36
- Or if the user asks you to modify a test, you could use the generateTestWithBrowserAgent tool. If you suspect
37
- that a UI selector needs to be updated, using the browser agent is a good idea.
38
-
39
- Before using generateTestWithBrowserAgent, you need to prepare the test code for the browser agent.
40
- You can do this by using the str_replace_editor tool to add a TODO comment to the test code. This
41
- comment should explain to the browser agent what to do.
42
-
43
- For example, if the expected modification is to click on a login button, you could add the following comment.
44
-
45
- // TODO(agent): Click on the login button
46
-
47
- The position of the comment is important: the browser agent will look for this comment and replace it with
48
- the actual code to click on the login button. If you are fixing a failing test, your comment should be
49
- around the failing line of code, so that it can be replaced/modified.
50
-
51
- # Repo context
52
-
53
- You are running as a CLI tool inside the directory of the repo where this test file is located. Here is
54
- the repo directory structure:
55
-
56
- ${(0, repo_tree_1.generateAsciiTree)(process.cwd())}
57
-
58
- While specifying paths to files, use relative paths from the current working directory. For example:
59
- - Correct path: "tests/lesson.spec.ts"
60
- - Incorrect path: "/repo/tests/lesson.spec.ts" or "${path_1.default.basename(process.cwd())}/tests/lesson.spec.ts"
61
-
62
- # Rules for fixing Playwright tests
63
-
64
- You must follow these rules while adding new tests or modifying existing tests. There can be exceptions to these rules, but
65
- ONLY when explicitly asked for by the user.
66
-
67
- 1. Do not add any conditional logic or try catch blocks in a test. A good test deterministically tests a user scenario
68
- 2. Trust Playwright's ability to auto-wait while taking actions on elements. For example, do not add checks on locator.isVisible() before clicking on it: Playwright already does this
69
- 3. Do not add waitForTimeout or waitForLoadState in a test. Playwright will automatically wait for the page to load.
70
- 4. You can't delete some steps from the test to make it pass. The test needs to accomplish its objective (which is to validate a particular user scenario)
71
-
72
- # Proactiveness
73
-
74
- You are allowed to be proactive, but only when the user asks you to do something. You should strive to
75
- strike a balance between:
76
- 1. Doing the right thing when asked, including taking actions and follow-up actions
77
- 2. Not surprising the user with actions you take without asking. It is okay to ask the user for confirmation before taking actions.
78
- `;
79
- const tools = [
80
- test_run_1.runTestTool,
81
- test_gen_browser_1.generateTestWithBrowserAgent,
82
- diagnosis_fetcher_1.diagnosisTool,
83
- grep_1.grepTool,
84
- test_run_fetcher_1.testRunTool,
85
- ];
86
- const toolExecutors = {
87
- ...Object.fromEntries(tools.map((tool) => [tool.schema.name, tool.execute])),
88
- str_replace_editor: (input) => (0, chat_1.strReplaceEditorTool)(input, web_1.validateTypescript),
89
- };
90
- async function chatAgent({ chatModel = "claude-3-7-sonnet-20250219", useDiskForChatState, }) {
91
- const ora = (await import("ora")).default;
92
- let userPrompt = undefined;
93
- let chatState = useDiskForChatState ? chat_1.ChatState.load() : new chat_1.ChatState(false);
94
- const handleSigInt = () => {
95
- console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatState.getUsageSummary())}`);
96
- process.exit(0);
97
- };
98
- process.once("SIGINT", handleSigInt);
99
- process.once("SIGTERM", handleSigInt);
100
- if (chatState.askUserForInput) {
101
- // Show last message to the user for context when we loaded from disk
102
- const messages = chatState.messages;
103
- const lastMessage = messages[messages.length - 1];
104
- if (lastMessage && Array.isArray(lastMessage.content)) {
105
- const textContent = lastMessage.content.find((b) => b.type === "text");
106
- if (textContent) {
107
- const role = lastMessage.role.charAt(0).toUpperCase() + lastMessage.role.slice(1);
108
- console.log(`${role}: ${textContent.text}`);
109
- }
110
- }
111
- }
112
- while (!userPrompt?.toLowerCase().includes("stop")) {
113
- chatState.saveToDisk();
114
- if (chatState.askUserForInput) {
115
- try {
116
- userPrompt = await human_in_the_loop_1.humanLoop.getFeedback({
117
- message: "User:",
118
- });
119
- }
120
- catch (e) {
121
- // https://github.com/SBoudrias/Inquirer.js/issues/1502#issuecomment-2275991680
122
- if (e instanceof Error && e.name === "ExitPromptError") {
123
- console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatState.getUsageSummary())}`);
124
- process.exit(0);
125
- }
126
- throw e;
127
- }
128
- chatState.pushMessage({
129
- role: "user",
130
- content: [
131
- {
132
- type: "text",
133
- text: userPrompt,
134
- },
135
- ],
136
- });
137
- continue;
138
- }
139
- const toolUse = chatState.getPendingToolCall();
140
- if (toolUse) {
141
- const spinner = ora(`Executing tool ${toolUse.name} with args: ${JSON.stringify(toolUse.input)}`).start();
142
- const toolExecutor = toolExecutors[toolUse.name];
143
- if (!toolExecutor) {
144
- throw new Error(`Tool ${toolUse.name} not found`);
145
- }
146
- const toolResult = await toolExecutor(toolUse.input);
147
- if (toolResult.isError) {
148
- spinner.fail(`Tool ${toolUse.name} failed with error: ${toolResult.result}`);
149
- }
150
- else {
151
- spinner.succeed(`Tool ${toolUse.name} completed`);
152
- }
153
- chatState.pushMessage({
154
- role: "user",
155
- content: [
156
- {
157
- type: "tool_result",
158
- tool_use_id: toolUse.id,
159
- content: toolResult.result,
160
- is_error: toolResult.isError,
161
- },
162
- ],
163
- });
164
- continue;
165
- }
166
- const spinner = ora("Claude is working...").start();
167
- const response = await (0, chat_1.createClaudeMessage)({
168
- systemPrompt,
169
- messages: chatState.getMessagesForCreateCompletion(),
170
- tools: tools.map((tool) => (0, chat_1.convertOpenAISchemaToAnthropic)((0, zod_schema_1.zodToOpenAITool)(tool.schema))),
171
- model: chatModel,
172
- withStrReplaceEditor: true,
173
- });
174
- spinner.stop();
175
- if (!response) {
176
- throw new Error("No response from LLM");
177
- }
178
- chatState.pushMessage(response);
179
- const textBlock = response.content.find((b) => b.type === "text");
180
- if (textBlock) {
181
- console.log("Assistant:", textBlock.text);
182
- }
183
- }
184
- const usageSummary = chatState.getUsageSummary();
185
- console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatState.getUsageSummary())}`);
186
- return usageSummary;
187
- }
188
- exports.chatAgent = chatAgent;