@empiricalrun/test-gen 0.55.0 → 0.56.1

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 (39) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/agent/browsing/run.js +4 -4
  3. package/dist/agent/browsing/utils.d.ts.map +1 -1
  4. package/dist/agent/browsing/utils.js +39 -21
  5. package/dist/agent/chat/agent-loop.d.ts +3 -1
  6. package/dist/agent/chat/agent-loop.d.ts.map +1 -1
  7. package/dist/agent/chat/agent-loop.js +7 -23
  8. package/dist/agent/chat/exports.d.ts +8 -0
  9. package/dist/agent/chat/exports.d.ts.map +1 -0
  10. package/dist/agent/chat/exports.js +7 -0
  11. package/dist/agent/chat/index.d.ts.map +1 -1
  12. package/dist/agent/chat/index.js +5 -0
  13. package/dist/agent/chat/repo.js +4 -4
  14. package/dist/agent/codegen/fix-ts-errors.js +3 -3
  15. package/dist/agent/codegen/generate-code-apply-changes.js +4 -4
  16. package/dist/agent/codegen/repo-edit.js +2 -2
  17. package/dist/agent/codegen/run.js +4 -4
  18. package/dist/agent/codegen/update-flow.js +4 -4
  19. package/dist/agent/codegen/utils.js +11 -11
  20. package/dist/bin/index.js +4 -2
  21. package/dist/bin/utils/context.js +12 -12
  22. package/dist/bin/utils/fs/index.js +4 -4
  23. package/dist/bin/utils/platform/web/index.js +19 -19
  24. package/dist/reporter/index.js +5 -5
  25. package/dist/test-build/index.d.ts +2 -9
  26. package/dist/test-build/index.d.ts.map +1 -1
  27. package/dist/test-build/index.js +18 -17
  28. package/dist/tool-call-service/index.d.ts +6 -9
  29. package/dist/tool-call-service/index.d.ts.map +1 -1
  30. package/dist/tool-call-service/index.js +50 -36
  31. package/dist/tools/download-build.d.ts +3 -0
  32. package/dist/tools/download-build.d.ts.map +1 -0
  33. package/dist/tools/download-build.js +39 -0
  34. package/dist/tools/grep.d.ts.map +1 -1
  35. package/dist/tools/grep.js +1 -1
  36. package/dist/tools/str_replace_editor.d.ts.map +1 -1
  37. package/dist/tools/str_replace_editor.js +20 -9
  38. package/dist/tools/utils/index.d.ts.map +1 -1
  39. package/package.json +7 -5
package/dist/bin/index.js CHANGED
@@ -233,8 +233,10 @@ async function main() {
233
233
  generationId: testGenConfig.options?.metadata.generationId,
234
234
  projectRepoName: testGenConfig.options?.metadata.projectRepoName,
235
235
  });
236
- // Download the build if repo has a download script
237
- await (0, test_build_1.downloadBuild)(testGenConfig.build || {});
236
+ if (testGenConfig.build?.url) {
237
+ // Download the build if repo has a download script
238
+ await (0, test_build_1.downloadBuild)(testGenConfig.build.url);
239
+ }
238
240
  if (completedOptions.useChat) {
239
241
  await runChatAgent({
240
242
  chatSessionId: completedOptions.chatSessionId,
@@ -4,14 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.fetchAppKnowledge = exports.generateTxtForRepository = exports.createRepoEditFilter = exports.contextForGeneration = exports.createGitIgnoreFileFilter = void 0;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
7
+ const fs_1 = __importDefault(require("fs"));
8
8
  const ignore_1 = __importDefault(require("ignore"));
9
- const fs_1 = require("./fs");
9
+ const fs_2 = require("./fs");
10
10
  async function createGitIgnoreFileFilter() {
11
11
  const ignoreFn = (0, ignore_1.default)();
12
- if (fs_extra_1.default.existsSync(".gitignore")) {
12
+ if (fs_1.default.existsSync(".gitignore")) {
13
13
  // Not checking for nested gitignore
14
- let gitignore = (await fs_extra_1.default.readFile(".gitignore")).toString();
14
+ let gitignore = fs_1.default.readFileSync(".gitignore").toString();
15
15
  gitignore = `
16
16
  ${gitignore}
17
17
  .git/
@@ -27,10 +27,10 @@ exports.createGitIgnoreFileFilter = createGitIgnoreFileFilter;
27
27
  async function contextForGeneration(file) {
28
28
  const filter = await createGitIgnoreFileFilter();
29
29
  return {
30
- codePrompt: await (0, fs_1.generatePromptFromDirectory)("./tests", filter),
31
- pomPrompt: await (0, fs_1.generatePromptFromDirectory)("./pages", filter),
32
- nonSpecFilePrompt: await (0, fs_1.generatePromptFromNonSpecFiles)("./tests", filter),
33
- testFileContent: file ? await fs_extra_1.default.readFile(file, "utf-8") : "",
30
+ codePrompt: await (0, fs_2.generatePromptFromDirectory)("./tests", filter),
31
+ pomPrompt: await (0, fs_2.generatePromptFromDirectory)("./pages", filter),
32
+ nonSpecFilePrompt: await (0, fs_2.generatePromptFromNonSpecFiles)("./tests", filter),
33
+ testFileContent: file ? fs_1.default.readFileSync(file, "utf-8") : "",
34
34
  };
35
35
  }
36
36
  exports.contextForGeneration = contextForGeneration;
@@ -39,14 +39,14 @@ async function createRepoEditFilter() {
39
39
  const gitIgnoreFilter = await createGitIgnoreFileFilter();
40
40
  return (filePath) => {
41
41
  return (gitIgnoreFilter(filePath) &&
42
- (fs_extra_1.default.statSync(filePath).isDirectory() || allowedExtensions.test(filePath)));
42
+ (fs_1.default.statSync(filePath).isDirectory() || allowedExtensions.test(filePath)));
43
43
  };
44
44
  }
45
45
  exports.createRepoEditFilter = createRepoEditFilter;
46
46
  async function generateTxtForRepository() {
47
47
  const filter = await createRepoEditFilter();
48
48
  return {
49
- prompt: await (0, fs_1.generatePromptFromDirectory)(".", filter),
49
+ prompt: await (0, fs_2.generatePromptFromDirectory)(".", filter),
50
50
  };
51
51
  }
52
52
  exports.generateTxtForRepository = generateTxtForRepository;
@@ -54,7 +54,7 @@ async function fetchAppKnowledge() {
54
54
  let fileExists = true;
55
55
  const appKnowledgePath = "./app_knowledge.md";
56
56
  try {
57
- await fs_extra_1.default.access(appKnowledgePath);
57
+ fs_1.default.accessSync(appKnowledgePath);
58
58
  }
59
59
  catch (e) {
60
60
  fileExists = false;
@@ -62,6 +62,6 @@ async function fetchAppKnowledge() {
62
62
  if (!fileExists) {
63
63
  return "";
64
64
  }
65
- return await fs_extra_1.default.readFile(appKnowledgePath, "utf-8");
65
+ return fs_1.default.readFileSync(appKnowledgePath, "utf-8");
66
66
  }
67
67
  exports.fetchAppKnowledge = fetchAppKnowledge;
@@ -4,19 +4,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.generatePromptFromNonSpecFiles = exports.generatePromptFromDirectory = exports.convertFileContentsToString = exports.readFilesInDirectory = void 0;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
7
+ const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  async function readFilesInDirectory(dir = "", filterFunc) {
10
10
  let files = [];
11
- const items = await fs_extra_1.default.readdir(dir);
11
+ const items = fs_1.default.readdirSync(dir);
12
12
  const filteredItems = items.map((i) => path_1.default.join(dir, i)).filter(filterFunc);
13
13
  for (const item of filteredItems) {
14
- const stat = await fs_extra_1.default.stat(item);
14
+ const stat = fs_1.default.statSync(item);
15
15
  if (stat.isDirectory()) {
16
16
  files = files.concat(await readFilesInDirectory(item, filterFunc));
17
17
  }
18
18
  else if (stat.isFile()) {
19
- const content = await fs_extra_1.default.readFile(item, "utf-8");
19
+ const content = fs_1.default.readFileSync(item, "utf-8");
20
20
  files.push({ filePath: item, content });
21
21
  }
22
22
  }
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
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
- const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const prettier_1 = __importDefault(require("prettier"));
12
12
  const ts_morph_1 = require("ts-morph");
@@ -73,12 +73,12 @@ function getTypescriptTestBlock({ scenarioName, suites, content, }) {
73
73
  }
74
74
  exports.getTypescriptTestBlock = getTypescriptTestBlock;
75
75
  function hasTestBlock({ testName, testSuites, filePath, }) {
76
- if (!fs_extra_1.default.existsSync(filePath)) {
76
+ if (!fs_1.default.existsSync(filePath)) {
77
77
  return false;
78
78
  }
79
79
  const { testBlock } = getTypescriptTestBlock({
80
80
  scenarioName: testName,
81
- content: fs_extra_1.default.readFileSync(filePath, "utf-8"),
81
+ content: fs_1.default.readFileSync(filePath, "utf-8"),
82
82
  suites: testSuites,
83
83
  });
84
84
  return Boolean(testBlock);
@@ -107,7 +107,7 @@ function getParentDescribeNames(node) {
107
107
  }
108
108
  async function hasTopLevelDescribeConfigureWithSerialMode(filePath) {
109
109
  const project = new ts_morph_1.Project();
110
- const content = await fs_extra_1.default.readFile(filePath, "utf-8");
110
+ const content = fs_1.default.readFileSync(filePath, "utf-8");
111
111
  const sourceFile = project.createSourceFile("test.ts", content);
112
112
  const statements = sourceFile.getStatements();
113
113
  for (const statement of statements) {
@@ -188,7 +188,7 @@ exports.appendToTestBlock = appendToTestBlock;
188
188
  function validateTypescript(filePath) {
189
189
  // Create a compiler host to read files
190
190
  const compilerHost = typescript_1.default.createCompilerHost({});
191
- compilerHost.readFile = (file) => fs_extra_1.default.readFileSync(file, "utf8");
191
+ compilerHost.readFile = (file) => fs_1.default.readFileSync(file, "utf8");
192
192
  // Create a program with a single source file
193
193
  const program = typescript_1.default.createProgram([filePath], {
194
194
  esModuleInterop: true,
@@ -245,12 +245,12 @@ async function lintErrors(filePath) {
245
245
  });
246
246
  const [result] = await eslint.lintFiles(filePath);
247
247
  if (result?.output) {
248
- await fs_extra_1.default.writeFile(filePath, result.output);
248
+ fs_1.default.writeFileSync(filePath, result.output);
249
249
  }
250
250
  }
251
251
  exports.lintErrors = lintErrors;
252
252
  async function formatCode(filePath, trace) {
253
- const fileContent = fs_extra_1.default.readFileSync(filePath, "utf8");
253
+ const fileContent = fs_1.default.readFileSync(filePath, "utf8");
254
254
  if (!fileContent) {
255
255
  trace?.span({
256
256
  name: "prettier-format-output",
@@ -264,7 +264,7 @@ async function formatCode(filePath, trace) {
264
264
  filepath: filePath,
265
265
  });
266
266
  trace?.span({ name: "prettier-format-output", output: formattedContent });
267
- await fs_extra_1.default.writeFile(filePath, formattedContent);
267
+ fs_1.default.writeFileSync(filePath, formattedContent);
268
268
  }
269
269
  exports.formatCode = formatCode;
270
270
  function addNewImport(contents, modules, pkg) {
@@ -272,11 +272,11 @@ function addNewImport(contents, modules, pkg) {
272
272
  }
273
273
  exports.addNewImport = addNewImport;
274
274
  async function removeTestOnly(filePath) {
275
- const contents = await fs_extra_1.default.readFile(filePath, "utf8");
275
+ const contents = fs_1.default.readFileSync(filePath, "utf8");
276
276
  const updatedContent = contents
277
277
  .replace("test.only(", "test(")
278
278
  .replace("test.describe.only(", "test.describe(");
279
- return fs_extra_1.default.writeFile(filePath, updatedContent);
279
+ fs_1.default.writeFileSync(filePath, updatedContent);
280
280
  }
281
281
  exports.removeTestOnly = removeTestOnly;
282
282
  function getFixtureImportPath(filePath) {
@@ -296,7 +296,7 @@ function getFixtureImportPath(filePath) {
296
296
  }
297
297
  exports.getFixtureImportPath = getFixtureImportPath;
298
298
  async function getPageVariableNameFromCreateTest(filePath) {
299
- const contents = await fs_extra_1.default.readFile(filePath, "utf-8");
299
+ const contents = fs_1.default.readFileSync(filePath, "utf-8");
300
300
  const project = new ts_morph_1.Project();
301
301
  const sourceFile = project.createSourceFile("test.ts", contents);
302
302
  const createTestNode = sourceFile.getFirstDescendant((node) => !!(node.isKind(ts_morph_1.SyntaxKind.CallExpression) &&
@@ -418,7 +418,7 @@ const importAllExportsStmtFromFilePaths = async (repoDir, filePaths, testFilePat
418
418
  if (!importPath.startsWith(".")) {
419
419
  importPath = "./" + importPath;
420
420
  }
421
- const file = await fs_extra_1.default.readFile(fullPath, "utf-8");
421
+ const file = fs_1.default.readFileSync(fullPath, "utf-8");
422
422
  const project = new ts_morph_1.Project();
423
423
  const sourceFile = project.createSourceFile("index.ts", file);
424
424
  const exportedFunctions = sourceFile
@@ -440,7 +440,7 @@ async function addUserContextFixture({ scenarioName, filePath, suites, }) {
440
440
  // TODO: remove this check when we support user context across repos
441
441
  let fixtureContent = "";
442
442
  try {
443
- fixtureContent = await fs_extra_1.default.readFile("./tests/fixtures.ts", "utf-8");
443
+ fixtureContent = fs_1.default.readFileSync("./tests/fixtures.ts", "utf-8");
444
444
  }
445
445
  catch (e) {
446
446
  // do nothing
@@ -448,7 +448,7 @@ async function addUserContextFixture({ scenarioName, filePath, suites, }) {
448
448
  if (!fixtureContent || !fixtureContent.includes("userContext")) {
449
449
  return;
450
450
  }
451
- const fileContent = await fs_extra_1.default.readFile(filePath, "utf-8");
451
+ const fileContent = fs_1.default.readFileSync(filePath, "utf-8");
452
452
  const { testNode } = getTypescriptTestBlock({
453
453
  scenarioName,
454
454
  content: fileContent,
@@ -469,14 +469,14 @@ async function addUserContextFixture({ scenarioName, filePath, suites, }) {
469
469
  .replace(",}", "}") // Remove any trailing commas
470
470
  .replace("}", ", userContext }");
471
471
  destructuringParam.replaceWithText(updatedImport);
472
- await fs_extra_1.default.writeFile(filePath, fileContent.replace(currentBlock, testNode.getText()), "utf-8");
472
+ fs_1.default.writeFileSync(filePath, fileContent.replace(currentBlock, testNode.getText()), "utf-8");
473
473
  }
474
474
  }
475
475
  }
476
476
  }
477
477
  exports.addUserContextFixture = addUserContextFixture;
478
478
  async function appendScopeToCreateTest(filePath, scopeVariables) {
479
- const contents = await fs_extra_1.default.readFile(filePath, "utf-8");
479
+ const contents = fs_1.default.readFileSync(filePath, "utf-8");
480
480
  const project = new ts_morph_1.Project();
481
481
  const sourceFile = project.createSourceFile("test.ts", contents);
482
482
  const createTestNode = sourceFile
@@ -491,17 +491,17 @@ async function appendScopeToCreateTest(filePath, scopeVariables) {
491
491
  const args = createTestNode.getArguments();
492
492
  const scopeArgStr = `{ ${scopeVariables.join(", ")} }`;
493
493
  createTestNode.insertArgument(args.length, scopeArgStr);
494
- await fs_extra_1.default.writeFile(filePath, sourceFile.getFullText());
494
+ fs_1.default.writeFileSync(filePath, sourceFile.getFullText());
495
495
  }
496
496
  exports.appendScopeToCreateTest = appendScopeToCreateTest;
497
497
  function isTestPresent({ specPath, testCase, }) {
498
498
  let isTestPresent = true;
499
- isTestPresent = fs_extra_1.default.existsSync(specPath);
499
+ isTestPresent = fs_1.default.existsSync(specPath);
500
500
  if (isTestPresent) {
501
501
  const { testBlock } = getTypescriptTestBlock({
502
502
  suites: testCase.suites,
503
503
  scenarioName: testCase.name,
504
- content: fs_extra_1.default.readFileSync(specPath, "utf-8"),
504
+ content: fs_1.default.readFileSync(specPath, "utf-8"),
505
505
  });
506
506
  isTestPresent = Boolean(testBlock);
507
507
  }
@@ -5,7 +5,7 @@ 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 fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const logger_1 = require("../bin/logger");
11
11
  const uploader_1 = require("../uploader");
@@ -93,10 +93,10 @@ class TestGenUpdatesReporter {
93
93
  }
94
94
  try {
95
95
  // upload current screenshot to r2 and report it to reporter
96
- if (!fs_extra_1.default.existsSync(path_1.default.join(this.repoDir, "gen-assets"))) {
97
- await fs_extra_1.default.mkdir(path_1.default.join(this.repoDir, "gen-assets"));
96
+ if (!fs_1.default.existsSync(path_1.default.join(this.repoDir, "gen-assets"))) {
97
+ fs_1.default.mkdirSync(path_1.default.join(this.repoDir, "gen-assets"));
98
98
  }
99
- await fs_extra_1.default.writeFile(path_1.default.join(this.repoDir, "gen-assets", `current-view-${Date.now()}.png`), buffer);
99
+ fs_1.default.writeFileSync(path_1.default.join(this.repoDir, "gen-assets", `current-view-${Date.now()}.png`), buffer);
100
100
  const uploadDir = (0, uploader_1.getUploadPathForRun)(reporterConfig?.projectRepoName);
101
101
  const files = await (0, r2_uploader_1.uploadDirectory)({
102
102
  sourceDir: path_1.default.join(this.repoDir, "gen-assets"),
@@ -110,7 +110,7 @@ class TestGenUpdatesReporter {
110
110
  type: "current-snapshot",
111
111
  message: JSON.stringify({ type: "current-view", url }),
112
112
  }));
113
- await fs_extra_1.default.rmdir(path_1.default.join(this.repoDir, "gen-assets"), {
113
+ fs_1.default.rmdirSync(path_1.default.join(this.repoDir, "gen-assets"), {
114
114
  recursive: true,
115
115
  });
116
116
  }
@@ -1,10 +1,3 @@
1
- import { Build } from "@empiricalrun/shared-types";
2
- /**
3
- * method to download the build from the URL provided in the build object
4
- * this is only used in cases like chrome extension as of yet.
5
- * @export
6
- * @param {Build} build
7
- * @return {*} {Promise<void>}
8
- */
9
- export declare function downloadBuild(build: Build): Promise<void>;
1
+ export declare function hasDownloadScript(): Promise<boolean>;
2
+ export declare function downloadBuild(buildUrl: string): Promise<void>;
10
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-build/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAUnD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAY/D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-build/index.ts"],"names":[],"mappings":"AAeA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAG1D;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUnE"}
@@ -3,26 +3,27 @@ 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.downloadBuild = void 0;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
6
+ exports.downloadBuild = exports.hasDownloadScript = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
8
  const logger_1 = require("../bin/logger");
9
9
  const exec_1 = require("../utils/exec");
10
- /**
11
- * method to download the build from the URL provided in the build object
12
- * this is only used in cases like chrome extension as of yet.
13
- * @export
14
- * @param {Build} build
15
- * @return {*} {Promise<void>}
16
- */
17
- async function downloadBuild(build) {
18
- const logger = new logger_1.CustomLogger({ useReporter: false });
10
+ async function getPackageJSON() {
19
11
  const packageJSONPath = "package.json";
20
- const packageJsonStr = await fs_extra_1.default.readFile(packageJSONPath, "utf-8");
21
- const packageJSONData = JSON.parse(packageJsonStr);
22
- const buildDownloadScript = packageJSONData.scripts["download"];
23
- if (buildDownloadScript && build?.url) {
24
- logger.log(`Downloading build from ${build.url}`);
25
- await (0, exec_1.cmd)(`npm run download ${build.url}`.split(" "), {
12
+ const packageJsonStr = fs_1.default.readFileSync(packageJSONPath, "utf-8");
13
+ return JSON.parse(packageJsonStr);
14
+ }
15
+ async function hasDownloadScript() {
16
+ const packageJSON = await getPackageJSON();
17
+ return !!packageJSON.scripts["download"];
18
+ }
19
+ exports.hasDownloadScript = hasDownloadScript;
20
+ async function downloadBuild(buildUrl) {
21
+ const logger = new logger_1.CustomLogger({ useReporter: false });
22
+ const packageJSON = await getPackageJSON();
23
+ const buildDownloadScript = packageJSON.scripts["download"];
24
+ if (buildDownloadScript && buildUrl) {
25
+ logger.log(`Downloading build from ${buildUrl}`);
26
+ await (0, exec_1.cmd)(`npm run download ${buildUrl}`.split(" "), {
26
27
  env: { ...Object(process.env) },
27
28
  });
28
29
  }
@@ -1,4 +1,4 @@
1
- import { Tool, ToolResult } from "@empiricalrun/llm/chat";
1
+ import { PendingToolCall, Tool, ToolResult } from "@empiricalrun/llm/chat";
2
2
  import { SupportedChatModels } from "../agent/chat/types";
3
3
  export type { SupportedChatModels };
4
4
  type ToolExecutors = {
@@ -7,15 +7,12 @@ type ToolExecutors = {
7
7
  export declare class ToolCallService {
8
8
  tools: Tool[];
9
9
  toolExecutors: ToolExecutors;
10
- constructor();
11
- getTools(selectedModel: SupportedChatModels): Promise<{
10
+ chatSessionId: number | null;
11
+ selectedModel: SupportedChatModels;
12
+ constructor(chatSessionId: number | null, selectedModel: SupportedChatModels);
13
+ getTools(): Promise<{
12
14
  tools: Tool[];
13
15
  }>;
14
- execute(payload: {
15
- tool: {
16
- name: string;
17
- input: any;
18
- };
19
- }, isRemote: boolean): Promise<ToolResult>;
16
+ execute(toolCalls: PendingToolCall[], isRemote: boolean): Promise<ToolResult[]>;
20
17
  }
21
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool-call-service/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAa1D,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,KAAK,aAAa,GAAG;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACpD,CAAC;AAqBF,qBAAa,eAAe;IAC1B,KAAK,EAAE,IAAI,EAAE,CAAM;IACnB,aAAa,EAAE,aAAa,CAAM;;IAa5B,QAAQ,CAAC,aAAa,EAAE,mBAAmB;;;IAa3C,OAAO,CACX,OAAO,EAAE;QACP,IAAI,EAAE;YACJ,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,GAAG,CAAC;SACZ,CAAC;KACH,EACD,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,UAAU,CAAC;CA8BvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool-call-service/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAa1D,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,KAAK,aAAa,GAAG;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACpD,CAAC;AAyBF,qBAAa,eAAe;IAC1B,KAAK,EAAE,IAAI,EAAE,CAAM;IACnB,aAAa,EAAE,aAAa,CAAM;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,mBAAmB,CAAC;gBAGjC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,aAAa,EAAE,mBAAmB;IAgB9B,QAAQ;;;IAaR,OAAO,CACX,SAAS,EAAE,eAAe,EAAE,EAC5B,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC;CAqCzB"}
@@ -4,13 +4,14 @@ exports.ToolCallService = void 0;
4
4
  const client_sqs_1 = require("@aws-sdk/client-sqs");
5
5
  const commit_and_create_pr_1 = require("../tools/commit-and-create-pr");
6
6
  const diagnosis_fetcher_1 = require("../tools/diagnosis-fetcher");
7
+ const download_build_1 = require("../tools/download-build");
7
8
  const environment_crud_1 = require("../tools/environment-crud");
8
9
  const grep_1 = require("../tools/grep");
9
10
  const str_replace_editor_1 = require("../tools/str_replace_editor");
10
11
  const test_gen_browser_1 = require("../tools/test-gen-browser");
11
12
  const test_run_1 = require("../tools/test-run");
12
13
  const test_run_fetcher_1 = require("../tools/test-run-fetcher");
13
- async function sendToolRequestToRemoteQueue(toolName, input) {
14
+ async function sendToolRequestToRemoteQueue(payload) {
14
15
  const sqs = new client_sqs_1.SQSClient({
15
16
  region: process.env.AWS_REGION,
16
17
  credentials: {
@@ -18,18 +19,22 @@ async function sendToolRequestToRemoteQueue(toolName, input) {
18
19
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
19
20
  },
20
21
  });
21
- const queueUrl = "https://sqs.us-east-1.amazonaws.com/381492172454/toolRequests.fifo";
22
+ const queueUrl = process.env.TOOL_REQUEST_QUEUE_URL;
22
23
  await sqs.send(new client_sqs_1.SendMessageCommand({
23
24
  QueueUrl: queueUrl,
24
- MessageBody: JSON.stringify({ toolName, input }),
25
- MessageGroupId: toolName, // tool request id
26
- MessageDeduplicationId: toolName, // tool request id
25
+ MessageBody: JSON.stringify(payload),
26
+ MessageGroupId: payload.requestId,
27
+ MessageDeduplicationId: payload.requestId, // unique id for the tool request
27
28
  }));
28
29
  }
29
30
  class ToolCallService {
30
31
  tools = [];
31
32
  toolExecutors = {};
32
- constructor() {
33
+ chatSessionId;
34
+ selectedModel;
35
+ constructor(chatSessionId, selectedModel) {
36
+ this.chatSessionId = chatSessionId;
37
+ this.selectedModel = selectedModel;
33
38
  this.tools = [
34
39
  grep_1.grepTool,
35
40
  test_run_1.runTestTool,
@@ -38,49 +43,58 @@ class ToolCallService {
38
43
  test_gen_browser_1.generateTestWithBrowserAgent,
39
44
  commit_and_create_pr_1.commitAndPushChangesTool,
40
45
  environment_crud_1.getEnvironmentTool,
46
+ download_build_1.downloadBuildTool,
41
47
  ];
42
48
  }
43
- async getTools(selectedModel) {
44
- if (!selectedModel.startsWith("claude")) {
49
+ async getTools() {
50
+ if (!this.selectedModel.startsWith("claude")) {
45
51
  this.tools.push(...str_replace_editor_1.textEditorTools);
46
52
  }
47
53
  this.tools.forEach((tool) => {
48
54
  this.toolExecutors[tool.schema.name] = tool.execute;
49
55
  });
50
- if (selectedModel.startsWith("claude")) {
56
+ if (this.selectedModel.startsWith("claude")) {
51
57
  this.toolExecutors["str_replace_editor"] = str_replace_editor_1.strReplaceEditorExecutor;
52
58
  }
53
59
  return { tools: this.tools };
54
60
  }
55
- async execute(payload, isRemote) {
56
- const { tool } = payload;
57
- const toolExecutor = this.toolExecutors[tool.name];
58
- if (!toolExecutor) {
59
- return {
60
- isError: true,
61
- result: `Invalid function/tool call: invalid_tool_call not found`,
62
- };
61
+ async execute(toolCalls, isRemote) {
62
+ if (isRemote && this.chatSessionId) {
63
+ console.log("Executing tool remotely", toolCalls);
64
+ await sendToolRequestToRemoteQueue({
65
+ toolCalls,
66
+ requestId: crypto.randomUUID(),
67
+ chatSessionId: this.chatSessionId,
68
+ selectedModel: this.selectedModel,
69
+ });
70
+ return toolCalls.map(() => ({
71
+ isError: false,
72
+ result: `Tool request sent to remote queue to execute.`,
73
+ }));
63
74
  }
64
- try {
65
- if (isRemote) {
66
- console.log("Executing tool remotely", tool.name, tool.input);
67
- // push to sqs
68
- await sendToolRequestToRemoteQueue(tool.name, tool.input);
69
- // TODO: Need to stop the agent loop here
70
- return {
71
- isError: false,
72
- result: `Tool request sent to remote queue to execute ${tool.name}.`,
73
- };
75
+ else {
76
+ const toolResults = [];
77
+ for (const toolCall of toolCalls) {
78
+ const toolExecutor = this.toolExecutors[toolCall.name];
79
+ if (!toolExecutor) {
80
+ toolResults.push({
81
+ isError: true,
82
+ result: `Invalid function/tool call: invalid_tool_call not found`,
83
+ });
84
+ continue;
85
+ }
86
+ try {
87
+ const result = await toolExecutor(toolCall.input);
88
+ toolResults.push(result);
89
+ }
90
+ catch (error) {
91
+ toolResults.push({
92
+ isError: true,
93
+ result: error instanceof Error ? error.message : String(error),
94
+ });
95
+ }
74
96
  }
75
- else {
76
- return await toolExecutor(tool.input);
77
- }
78
- }
79
- catch (error) {
80
- return {
81
- isError: true,
82
- result: error instanceof Error ? error.message : String(error),
83
- };
97
+ return toolResults;
84
98
  }
85
99
  }
86
100
  }
@@ -0,0 +1,3 @@
1
+ import type { Tool } from "@empiricalrun/llm/chat";
2
+ export declare const downloadBuildTool: Tool;
3
+ //# sourceMappingURL=download-build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-build.d.ts","sourceRoot":"","sources":["../../src/tools/download-build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAKnD,eAAO,MAAM,iBAAiB,EAAE,IAkC/B,CAAC"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.downloadBuildTool = void 0;
4
+ const zod_1 = require("zod");
5
+ const test_build_1 = require("../test-build");
6
+ exports.downloadBuildTool = {
7
+ schema: {
8
+ name: "downloadBuild",
9
+ description: `Download a build from a build URL. If you do not have
10
+ have a build URL, you can try getting the environment details with the getEnvironment tool.
11
+ Environment details will include the build URL.`,
12
+ parameters: zod_1.z.object({
13
+ buildUrl: zod_1.z.string().describe("The URL of the build to download"),
14
+ }),
15
+ },
16
+ execute: async (input) => {
17
+ if (!(await (0, test_build_1.hasDownloadScript)())) {
18
+ return {
19
+ isError: true,
20
+ result: `This repo does not have a download script in package.json.
21
+ You probably don't need to worry about this, since it means this repo does not have a build to download.`,
22
+ };
23
+ }
24
+ const { buildUrl } = input;
25
+ try {
26
+ await (0, test_build_1.downloadBuild)(buildUrl);
27
+ return {
28
+ isError: false,
29
+ result: "Build downloaded successfully",
30
+ };
31
+ }
32
+ catch (error) {
33
+ return {
34
+ isError: true,
35
+ result: `Failed to download build: ${error}`,
36
+ };
37
+ }
38
+ },
39
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/tools/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,wBAAwB,CAAC;AAqB/D,eAAO,MAAM,QAAQ,EAAE,IA+CtB,CAAC"}
1
+ {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/tools/grep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,wBAAwB,CAAC;AAmB/D,eAAO,MAAM,QAAQ,EAAE,IAgDtB,CAAC"}
@@ -5,7 +5,6 @@ const child_process_1 = require("child_process");
5
5
  const util_1 = require("util");
6
6
  const zod_1 = require("zod");
7
7
  const repo_tree_1 = require("../utils/repo-tree");
8
- const execAsync = (0, util_1.promisify)(child_process_1.exec);
9
8
  const GrepInputSchema = zod_1.z.object({
10
9
  pattern: zod_1.z.string().describe("The pattern to search for"),
11
10
  directory: zod_1.z
@@ -40,6 +39,7 @@ exports.grepTool = {
40
39
  // Using -n to show line numbers and removed -l to show actual matches
41
40
  cmd = `find ${dir} ${excludeFind} -name "${input.filePattern}" -exec grep -rin "${input.pattern}" {} \\;`;
42
41
  }
42
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
43
43
  const { stdout, stderr } = await execAsync(cmd);
44
44
  if (stdout) {
45
45
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"str_replace_editor.d.ts","sourceRoot":"","sources":["../../src/tools/str_replace_editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA2B1D,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAwC1D;AAMD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,UAAU,CAAC,CAwIrB;AA+FD,eAAO,MAAM,eAAe,EAAE,IAAI,EAKjC,CAAC"}
1
+ {"version":3,"file":"str_replace_editor.d.ts","sourceRoot":"","sources":["../../src/tools/str_replace_editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA2B1D,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAwC1D;AAMD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,UAAU,CAAC,CAmJrB;AA+FD,eAAO,MAAM,eAAe,EAAE,IAAI,EAKjC,CAAC"}