@empiricalrun/test-gen 0.54.1 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.55.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 508565d: feat: add support for openai chat model
8
+ - 8da022c: feat: add environment fetching tool
9
+
10
+ ### Patch Changes
11
+
12
+ - 99e4e6e: refactor: decoupled agentloop and toolcall
13
+ - Updated dependencies [cc4cb5e]
14
+ - Updated dependencies [508565d]
15
+ - @empiricalrun/llm@0.15.0
16
+
3
17
  ## 0.54.1
4
18
 
5
19
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/agent-loop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,UAAU,EAIX,MAAM,wBAAwB,CAAC;AAiBhC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAyChE,wBAAsB,aAAa,CAAC,EAClC,SAAS,EACT,aAAa,EACb,QAAQ,EACR,KAAK,GACN,EAAE;IACD,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;IAC3B,aAAa,EAAE,mBAAmB,CAAC;IACnC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,iBAiDA"}
1
+ {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/agent-loop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,UAAU,EAIX,MAAM,wBAAwB,CAAC;AAOhC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAgBhE,wBAAsB,aAAa,CAAC,EAClC,SAAS,EACT,aAAa,EACb,QAAQ,EACR,KAAK,GACN,EAAE;IACD,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;IAC3B,aAAa,EAAE,mBAAmB,CAAC;IACnC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,iBAiDA"}
@@ -3,55 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.chatAgentLoop = void 0;
4
4
  const chat_1 = require("@empiricalrun/llm/chat");
5
5
  const picocolors_1 = require("picocolors");
6
- const web_1 = require("../../bin/utils/platform/web");
7
- const commit_and_create_pr_1 = require("../../tools/commit-and-create-pr");
8
- const diagnosis_fetcher_1 = require("../../tools/diagnosis-fetcher");
9
- const grep_1 = require("../../tools/grep");
6
+ const tool_call_service_1 = require("../../tool-call-service");
10
7
  const str_replace_editor_1 = require("../../tools/str_replace_editor");
11
- const test_gen_browser_1 = require("../../tools/test-gen-browser");
12
- const test_run_1 = require("../../tools/test-run");
13
- const test_run_fetcher_1 = require("../../tools/test-run-fetcher");
14
8
  const prompt_1 = require("./prompt");
15
9
  const state_1 = require("./state");
16
- function getTools(selectedModel) {
17
- let tools = [
18
- grep_1.grepTool,
19
- test_run_1.runTestTool,
20
- test_run_fetcher_1.fetchTestRunReportTool,
21
- diagnosis_fetcher_1.fetchDiagnosisReportTool,
22
- test_gen_browser_1.generateTestWithBrowserAgent,
23
- commit_and_create_pr_1.commitAndPushChangesTool,
24
- ];
25
- if (selectedModel.startsWith("gemini")) {
26
- // Claude will have its own built-in text editor tools
27
- str_replace_editor_1.textEditorTools.forEach((tool) => {
28
- const originalExecute = tool.execute;
29
- tool.execute = (input) => originalExecute(input, web_1.validateTypescript);
30
- });
31
- tools.push(...str_replace_editor_1.textEditorTools);
32
- }
33
- const toolExecutors = {
34
- ...Object.fromEntries(tools.map((tool) => [tool.schema.name, tool.execute])),
35
- };
36
- if (selectedModel.startsWith("claude")) {
37
- toolExecutors.str_replace_editor = (input) => (0, str_replace_editor_1.strReplaceEditorExecutor)(input, web_1.validateTypescript);
38
- }
39
- return { tools, toolExecutors };
40
- }
41
10
  function getModelName(model) {
42
11
  if (model.startsWith("claude"))
43
12
  return "Claude";
44
13
  if (model.startsWith("gemini"))
45
14
  return "Gemini";
15
+ if (model.startsWith("o4"))
16
+ return "o4";
46
17
  return "AI";
47
18
  }
48
19
  const log = (...args) => {
49
20
  console.log((0, picocolors_1.gray)(args.join(" ")));
50
21
  };
22
+ const isRemote = process.env.TOOL_EXECUTION_IS_REMOTE === "true" || false;
51
23
  async function chatAgentLoop({ chatModel, selectedModel, reporter, trace, }) {
52
24
  const systemPrompt = await (0, prompt_1.buildSystemPrompt)();
53
25
  trace?.update({ input: { systemPrompt } });
54
- const { tools, toolExecutors } = getTools(selectedModel);
26
+ const toolCallService = new tool_call_service_1.ToolCallService();
27
+ const { tools } = await toolCallService.getTools(selectedModel);
55
28
  while (!chatModel.askUserForInput) {
56
29
  const toolCalls = chatModel.getPendingToolCalls();
57
30
  if (toolCalls.length) {
@@ -59,22 +32,17 @@ async function chatAgentLoop({ chatModel, selectedModel, reporter, trace, }) {
59
32
  for (const call of toolCalls) {
60
33
  const args = JSON.stringify(call.input);
61
34
  log(`Executing tool ${call.name} with args: ${args}`);
62
- const toolExecutor = toolExecutors[call.name];
63
- let callResponse;
64
- if (!toolExecutor) {
65
- callResponse = {
66
- isError: true,
67
- result: `Invalid function/tool call: ${call.name} not found`,
68
- };
35
+ let callResponse = await toolCallService.execute({
36
+ tool: {
37
+ name: call.name,
38
+ input: call.input,
39
+ },
40
+ }, isRemote);
41
+ if (callResponse.isError) {
42
+ log(`Tool ${call.name} failed: ${callResponse.result}`);
69
43
  }
70
44
  else {
71
- callResponse = await toolExecutor(call.input);
72
- if (callResponse.isError) {
73
- log(`Tool ${call.name} failed: ${callResponse.result}`);
74
- }
75
- else {
76
- log(`Tool ${call.name} completed`);
77
- }
45
+ log(`Tool ${call.name} completed`);
78
46
  }
79
47
  toolResults.push(callResponse);
80
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,UAAU,EACX,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,GAAG,EAAE,EACf,aAAa,EAAE,mBAAmB,GACjC,UAAU,CAAC,GAAG,CAAC,CAQjB"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,UAAU,EAEX,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,GAAG,EAAE,EACf,aAAa,EAAE,mBAAmB,GACjC,UAAU,CAAC,GAAG,CAAC,CAWjB"}
@@ -9,6 +9,9 @@ function createChatModel(messages, selectedModel) {
9
9
  if (selectedModel.startsWith("gemini")) {
10
10
  return new chat_1.GeminiChatModel(messages);
11
11
  }
12
+ if (selectedModel.startsWith("o4")) {
13
+ return new chat_1.OpenAIChatModel(messages);
14
+ }
12
15
  throw new Error(`Unsupported model: ${selectedModel}`);
13
16
  }
14
17
  exports.createChatModel = createChatModel;
@@ -1,5 +1,5 @@
1
1
  import { ChatStateOnDisk } from "./state";
2
- export type SupportedChatModels = "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-preview-03-25";
2
+ export type SupportedChatModels = "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-preview-03-25" | "o4-mini-2025-04-16";
3
3
  type LatestMessage = {
4
4
  role: string;
5
5
  textMessage: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,MAAM,mBAAmB,GAC3B,4BAA4B,GAC5B,4BAA4B,GAC5B,8BAA8B,CAAC;AAEnC,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAC7B,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,EAC3B,0BAA0B,EAAE,aAAa,GAAG,SAAS,KAClD,OAAO,CAAC,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,MAAM,mBAAmB,GAC3B,4BAA4B,GAC5B,4BAA4B,GAC5B,8BAA8B,GAC9B,oBAAoB,CAAC;AAEzB,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAC7B,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,EAC3B,0BAA0B,EAAE,aAAa,GAAG,SAAS,KAClD,OAAO,CAAC,IAAI,CAAC,CAAC"}
package/dist/bin/index.js CHANGED
@@ -38,10 +38,11 @@ function setupProcessListeners(cleanup) {
38
38
  async function runChatAgent({ modelInput, chatSessionId, useDiskForChatState, initialPromptPath, }) {
39
39
  const MODEL_MAPPING = {
40
40
  "claude-3-7": "claude-3-7-sonnet-20250219",
41
- "3-7": "claude-3-7-sonnet-20250219",
42
41
  "claude-3-5": "claude-3-5-sonnet-20241022",
43
- "3-5": "claude-3-5-sonnet-20241022",
44
42
  "gemini-2.5-pro-preview-03-25": "gemini-2.5-pro-preview-03-25",
43
+ "gemini-2.5-pro": "gemini-2.5-pro-preview-03-25",
44
+ "o4-mini": "o4-mini-2025-04-16",
45
+ "o4-mini-2025-04-16": "o4-mini-2025-04-16",
45
46
  };
46
47
  if (modelInput && !MODEL_MAPPING[modelInput]) {
47
48
  throw new Error(`Invalid chat model: ${modelInput}`);
@@ -8,7 +8,7 @@ export interface CliOptions {
8
8
  useDiskForChatState?: boolean;
9
9
  initialPrompt?: string;
10
10
  chatSessionId?: string;
11
- chatModel?: "claude-3-7" | "3-7" | "claude-3-5" | "3-5" | "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-preview-03-25";
11
+ chatModel?: "claude-3-7" | "claude-3-5" | "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro" | "gemini-2.5-pro-preview-03-25" | "o4-mini" | "o4-mini-2025-04-16";
12
12
  }
13
13
  export declare function validateAndCompleteCliOptions(options: CliOptions): Promise<CliOptions>;
14
14
  export declare function printBanner(): void;
@@ -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;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EACN,YAAY,GACZ,KAAK,GACL,YAAY,GACZ,KAAK,GACL,4BAA4B,GAC5B,4BAA4B,GAC5B,8BAA8B,CAAC;CACpC;AAQD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CAyDrB;AAED,wBAAgB,WAAW,SAgC1B"}
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;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EACN,YAAY,GACZ,YAAY,GACZ,4BAA4B,GAC5B,4BAA4B,GAC5B,gBAAgB,GAChB,8BAA8B,GAC9B,SAAS,GACT,oBAAoB,CAAC;CAC1B;AAQD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CAyDrB;AAED,wBAAgB,WAAW,SAgC1B"}
@@ -0,0 +1,21 @@
1
+ import { Tool, ToolResult } from "@empiricalrun/llm/chat";
2
+ import { SupportedChatModels } from "../agent/chat/types";
3
+ export type { SupportedChatModels };
4
+ type ToolExecutors = {
5
+ [key: string]: (input: any) => Promise<ToolResult>;
6
+ };
7
+ export declare class ToolCallService {
8
+ tools: Tool[];
9
+ toolExecutors: ToolExecutors;
10
+ constructor();
11
+ getTools(selectedModel: SupportedChatModels): Promise<{
12
+ tools: Tool[];
13
+ }>;
14
+ execute(payload: {
15
+ tool: {
16
+ name: string;
17
+ input: any;
18
+ };
19
+ }, isRemote: boolean): Promise<ToolResult>;
20
+ }
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ToolCallService = void 0;
4
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
5
+ const commit_and_create_pr_1 = require("../tools/commit-and-create-pr");
6
+ const diagnosis_fetcher_1 = require("../tools/diagnosis-fetcher");
7
+ const environment_crud_1 = require("../tools/environment-crud");
8
+ const grep_1 = require("../tools/grep");
9
+ const str_replace_editor_1 = require("../tools/str_replace_editor");
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
+ async function sendToolRequestToRemoteQueue(toolName, input) {
14
+ const sqs = new client_sqs_1.SQSClient({
15
+ region: process.env.AWS_REGION,
16
+ credentials: {
17
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
18
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
19
+ },
20
+ });
21
+ const queueUrl = "https://sqs.us-east-1.amazonaws.com/381492172454/toolRequests.fifo";
22
+ await sqs.send(new client_sqs_1.SendMessageCommand({
23
+ QueueUrl: queueUrl,
24
+ MessageBody: JSON.stringify({ toolName, input }),
25
+ MessageGroupId: toolName, // tool request id
26
+ MessageDeduplicationId: toolName, // tool request id
27
+ }));
28
+ }
29
+ class ToolCallService {
30
+ tools = [];
31
+ toolExecutors = {};
32
+ constructor() {
33
+ this.tools = [
34
+ grep_1.grepTool,
35
+ test_run_1.runTestTool,
36
+ test_run_fetcher_1.fetchTestRunReportTool,
37
+ diagnosis_fetcher_1.fetchDiagnosisReportTool,
38
+ test_gen_browser_1.generateTestWithBrowserAgent,
39
+ commit_and_create_pr_1.commitAndPushChangesTool,
40
+ environment_crud_1.getEnvironmentTool,
41
+ ];
42
+ }
43
+ async getTools(selectedModel) {
44
+ if (!selectedModel.startsWith("claude")) {
45
+ this.tools.push(...str_replace_editor_1.textEditorTools);
46
+ }
47
+ this.tools.forEach((tool) => {
48
+ this.toolExecutors[tool.schema.name] = tool.execute;
49
+ });
50
+ if (selectedModel.startsWith("claude")) {
51
+ this.toolExecutors["str_replace_editor"] = str_replace_editor_1.strReplaceEditorExecutor;
52
+ }
53
+ return { tools: this.tools };
54
+ }
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
+ };
63
+ }
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
+ };
74
+ }
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
+ };
84
+ }
85
+ }
86
+ }
87
+ exports.ToolCallService = ToolCallService;
@@ -0,0 +1,4 @@
1
+ import type { Tool } from "@empiricalrun/llm/chat";
2
+ export declare const getEnvironmentTool: Tool;
3
+ export declare const environmentTools: Tool[];
4
+ //# sourceMappingURL=environment-crud.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-crud.d.ts","sourceRoot":"","sources":["../../src/tools/environment-crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAyDnD,eAAO,MAAM,kBAAkB,EAAE,IAwEhC,CAAC;AAGF,eAAO,MAAM,gBAAgB,EAAE,IAAI,EAAyB,CAAC"}
@@ -0,0 +1,98 @@
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.environmentTools = exports.getEnvironmentTool = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const zod_1 = require("zod");
9
+ const utils_1 = require("./utils");
10
+ const getProjectRepoName = () => {
11
+ const packageJson = fs_1.default.readFileSync("package.json", "utf8");
12
+ if (!packageJson) {
13
+ throw new Error("Could not find or read package.json file");
14
+ }
15
+ const packageJsonData = JSON.parse(packageJson);
16
+ if (!packageJsonData.name) {
17
+ throw new Error("package.json does not contain a name field");
18
+ }
19
+ return packageJsonData.name;
20
+ };
21
+ const GetEnvironmentSchema = zod_1.z.object({
22
+ environment_slug: zod_1.z
23
+ .string()
24
+ .describe("The unique identifier (slug) for the environment you want to fetch details for. This is typically a URL-friendly version of the environment name."),
25
+ });
26
+ // Get Environment Tool
27
+ exports.getEnvironmentTool = {
28
+ schema: {
29
+ name: "getEnvironment",
30
+ description: "Fetch details of an existing environment",
31
+ parameters: GetEnvironmentSchema,
32
+ },
33
+ execute: async (input) => {
34
+ // Get project repo name
35
+ let projectRepoName;
36
+ try {
37
+ projectRepoName = getProjectRepoName();
38
+ }
39
+ catch (error) {
40
+ return {
41
+ isError: true,
42
+ result: `Failed to get project repository name from package.json: ${error instanceof Error ? error.message : String(error)}`,
43
+ };
44
+ }
45
+ // Make API request
46
+ let response;
47
+ try {
48
+ const queryParams = new URLSearchParams({
49
+ project_repo_name: projectRepoName,
50
+ environment_slug: input.environment_slug,
51
+ });
52
+ response = await (0, utils_1.makeDashboardRequest)({
53
+ path: `/api/environments?${queryParams.toString()}`,
54
+ method: "GET",
55
+ });
56
+ }
57
+ catch (error) {
58
+ return {
59
+ isError: true,
60
+ result: `Failed to make API request to fetch environment: ${error instanceof Error ? error.message : String(error)}`,
61
+ };
62
+ }
63
+ // Parse and validate response
64
+ try {
65
+ const data = response;
66
+ if (data.error) {
67
+ return {
68
+ isError: true,
69
+ result: `API returned error while fetching environment: ${data.error.message}`,
70
+ data: data.error,
71
+ };
72
+ }
73
+ if (!data.data?.environment) {
74
+ return {
75
+ isError: true,
76
+ result: `Environment "${input.environment_slug}" not found in project "${projectRepoName}"`,
77
+ data: data.data,
78
+ };
79
+ }
80
+ return {
81
+ result: JSON.stringify({
82
+ message: `Successfully fetched environment "${input.environment_slug}"`,
83
+ data: data.data,
84
+ }),
85
+ isError: false,
86
+ data: data.data,
87
+ };
88
+ }
89
+ catch (error) {
90
+ return {
91
+ isError: true,
92
+ result: `Failed to parse or validate environment response data: ${error instanceof Error ? error.message : String(error)}`,
93
+ };
94
+ }
95
+ },
96
+ };
97
+ // Export all environment tools as a single array
98
+ exports.environmentTools = [exports.getEnvironmentTool];
@@ -17,7 +17,7 @@ export declare function cleanupBackupFiles(repoDir: string): number;
17
17
  * Our implementation of Claude's built-in text editor tool
18
18
  * https://docs.anthropic.com/en/docs/build-with-claude/tool-use/text-editor-tool
19
19
  */
20
- export declare function strReplaceEditorExecutor(input: StrReplaceInput, typeChecker?: (filePath: string) => string[]): Promise<ToolResult>;
20
+ export declare function strReplaceEditorExecutor(input: StrReplaceInput): Promise<ToolResult>;
21
21
  export declare const textEditorTools: Tool[];
22
22
  export {};
23
23
  //# sourceMappingURL=str_replace_editor.d.ts.map
@@ -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;AAyB1D,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,EACtB,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,EAAE,GAC3C,OAAO,CAAC,UAAU,CAAC,CA8IrB;AA6GD,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,CAwIrB;AA+FD,eAAO,MAAM,eAAe,EAAE,IAAI,EAKjC,CAAC"}
@@ -7,6 +7,7 @@ exports.textEditorTools = exports.strReplaceEditorExecutor = exports.cleanupBack
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const zod_1 = require("zod");
10
+ const web_1 = require("../bin/utils/platform/web");
10
11
  function createBackup(filePath) {
11
12
  const backupPath = `${filePath}.bak`;
12
13
  if (fs_1.default.existsSync(filePath)) {
@@ -79,7 +80,7 @@ function escapeRegExp(text) {
79
80
  * Our implementation of Claude's built-in text editor tool
80
81
  * https://docs.anthropic.com/en/docs/build-with-claude/tool-use/text-editor-tool
81
82
  */
82
- async function strReplaceEditorExecutor(input, typeChecker) {
83
+ async function strReplaceEditorExecutor(input) {
83
84
  const { path: filePath } = input;
84
85
  try {
85
86
  let content;
@@ -156,25 +157,19 @@ async function strReplaceEditorExecutor(input, typeChecker) {
156
157
  }
157
158
  newContent = content.replace(input.old_str, input.new_str);
158
159
  fs_1.default.writeFileSync(filePath, newContent);
159
- if (typeChecker) {
160
- const errors = typeChecker(filePath);
161
- if (errors.length > 0) {
162
- return {
163
- result: `Edits to file ${filePath} have been applied. However, type checks are failing with errors:\n${errors.join("\n")}`,
164
- isError: true,
165
- };
166
- }
167
- else {
168
- return {
169
- result: `Edits to file ${filePath} have been applied. Type checks have also passed.`,
170
- isError: false,
171
- };
172
- }
160
+ const errors = (0, web_1.validateTypescript)(filePath);
161
+ if (errors.length > 0) {
162
+ return {
163
+ result: `Edits to file ${filePath} have been applied. However, type checks are failing with errors:\n${errors.join("\n")}`,
164
+ isError: true,
165
+ };
166
+ }
167
+ else {
168
+ return {
169
+ result: `Edits to file ${filePath} have been applied. Type checks have also passed.`,
170
+ isError: false,
171
+ };
173
172
  }
174
- return {
175
- result: `Edits to file ${filePath} have been applied.`,
176
- isError: false,
177
- };
178
173
  }
179
174
  case "insert":
180
175
  if (input.insert_line === undefined || !input.new_str) {
@@ -245,12 +240,12 @@ const fileCreateTool = {
245
240
  file_text: zod_1.z.string().describe("The contents of the new file."),
246
241
  }),
247
242
  },
248
- execute: async (input, typeChecker) => {
243
+ execute: async (input) => {
249
244
  return strReplaceEditorExecutor({
250
245
  command: "create",
251
246
  path: input.path,
252
247
  file_text: input.file_text,
253
- }, typeChecker);
248
+ });
254
249
  },
255
250
  };
256
251
  const stringReplaceTool = {
@@ -264,13 +259,13 @@ in the file. If old_str is not unique, the tool will return an error.`,
264
259
  new_str: zod_1.z.string().describe("The string to replace old_str with."),
265
260
  }),
266
261
  },
267
- execute: async (input, typeChecker) => {
262
+ execute: async (input) => {
268
263
  return strReplaceEditorExecutor({
269
264
  command: "str_replace",
270
265
  path: input.path,
271
266
  old_str: input.old_str,
272
267
  new_str: input.new_str,
273
- }, typeChecker);
268
+ });
274
269
  },
275
270
  };
276
271
  const stringInsertTool = {
@@ -287,13 +282,13 @@ const stringInsertTool = {
287
282
  new_str: zod_1.z.string().describe("The string to insert."),
288
283
  }),
289
284
  },
290
- execute: async (input, typeChecker) => {
285
+ execute: async (input) => {
291
286
  return strReplaceEditorExecutor({
292
287
  command: "insert",
293
288
  path: input.path,
294
289
  insert_line: input.insert_line,
295
290
  new_str: input.new_str,
296
- }, typeChecker);
291
+ });
297
292
  },
298
293
  };
299
294
  exports.textEditorTools = [
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/index.ts"],"names":[],"mappings":"AAAA,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,EAC5C,IAAI,EACJ,MAAc,EACd,IAAI,GACL,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,GAAG,OAAO,CAAC,CAAC,CAAC,CAmBb;AAED,wBAAsB,eAAe,CAAC,EACpC,MAAM,EACN,GAAG,EACH,IAAI,GACL,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,oBAWA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/index.ts"],"names":[],"mappings":"AAAA,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,EAC5C,IAAI,EACJ,MAAc,EACd,IAAI,GACL,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,GAAG,OAAO,CAAC,CAAC,CAAC,CAwBb;AAED,wBAAsB,eAAe,CAAC,EACpC,MAAM,EACN,GAAG,EACH,IAAI,GACL,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,oBAWA"}
@@ -2,12 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.callGitHubProxy = exports.makeDashboardRequest = void 0;
4
4
  async function makeDashboardRequest({ path, method = "GET", body, }) {
5
+ const API_KEY = process.env.EMPIRICALRUN_API_KEY;
6
+ if (!API_KEY) {
7
+ throw new Error("EMPIRICALRUN_API_KEY is not found");
8
+ }
5
9
  const requestHeaders = {
6
10
  "Content-Type": "application/json",
7
- Authorization: `Bearer ${process.env.EMPIRICALRUN_API_KEY}`,
11
+ Authorization: `Bearer ${API_KEY}`,
8
12
  "User-Agent": "empiricalrun/test-gen",
9
13
  };
10
- const baseUrl = "https://dash.empirical.run";
14
+ const baseUrl = process.env.DASHBOARD_DOMAIN || "https://dash.empirical.run";
11
15
  const response = await fetch(`${baseUrl}${path}`, {
12
16
  method,
13
17
  headers: requestHeaders,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-gen",
3
- "version": "0.54.1",
3
+ "version": "0.55.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -25,6 +25,10 @@
25
25
  ".": {
26
26
  "types": "./dist/index.d.ts",
27
27
  "default": "./dist/index.js"
28
+ },
29
+ "./tool-call-service": {
30
+ "types": "./dist/tool-call-service/index.d.ts",
31
+ "default": "./dist/tool-call-service/index.js"
28
32
  }
29
33
  },
30
34
  "repository": {
@@ -33,6 +37,7 @@
33
37
  },
34
38
  "author": "Empirical Team <hey@empirical.run>",
35
39
  "dependencies": {
40
+ "@aws-sdk/client-sqs": "^3.787.0",
36
41
  "@babel/parser": "^7.26.3",
37
42
  "@types/sanitize-html": "^2.11.0",
38
43
  "async-retry": "^1.3.3",
@@ -56,7 +61,7 @@
56
61
  "tsx": "^4.16.2",
57
62
  "typescript": "^5.3.3",
58
63
  "zod": "^3.23.8",
59
- "@empiricalrun/llm": "^0.14.8",
64
+ "@empiricalrun/llm": "^0.15.0",
60
65
  "@empiricalrun/r2-uploader": "^0.3.8",
61
66
  "@empiricalrun/test-run": "^0.8.0"
62
67
  },