diff --git a/src/services/tools/file_edit_insert.test.ts b/src/services/tools/file_edit_insert.test.ts index 94113cae5..b3fad60a8 100644 --- a/src/services/tools/file_edit_insert.test.ts +++ b/src/services/tools/file_edit_insert.test.ts @@ -96,13 +96,12 @@ describe("file_edit_insert tool", () => { } }); - it("creates new file when create flag is provided", async () => { + it("creates a new file without requiring create flag or guards", async () => { const newFile = path.join(testDir, "new.txt"); const tool = createTestTool(testDir); const args: FileEditInsertToolArgs = { file_path: path.relative(testDir, newFile), content: "Hello world!\n", - create: true, }; const result = (await tool.execute!(args, mockToolCallOptions)) as FileEditInsertToolResult; diff --git a/src/services/tools/file_edit_insert.ts b/src/services/tools/file_edit_insert.ts index 524a634ce..6f0c49d1e 100644 --- a/src/services/tools/file_edit_insert.ts +++ b/src/services/tools/file_edit_insert.ts @@ -48,7 +48,7 @@ export const createFileEditInsertTool: ToolFactory = (config: ToolConfiguration) description: TOOL_DEFINITIONS.file_edit_insert.description, inputSchema: TOOL_DEFINITIONS.file_edit_insert.schema, execute: async ( - { file_path, content, before, after, create }: FileEditInsertToolArgs, + { file_path, content, before, after }: FileEditInsertToolArgs, { abortSignal } ): Promise => { try { @@ -71,14 +71,6 @@ export const createFileEditInsertTool: ToolFactory = (config: ToolConfiguration) const exists = await fileExists(config.runtime, resolvedPath, abortSignal); if (!exists) { - if (!create) { - return { - success: false, - error: `File not found: ${file_path}. Set create: true to create it.`, - note: `${EDIT_FAILED_NOTE_PREFIX} File does not exist. Set create: true to create it, or check the file path.`, - }; - } - try { await writeFileString(config.runtime, resolvedPath, content, abortSignal); } catch (err) { @@ -139,7 +131,7 @@ function insertContent( } if (before === undefined && after === undefined) { - return guardFailure("Provide either a before or after guard to anchor the insertion point."); + return guardFailure("Provide either a before or after guard when editing existing files."); } return insertWithGuards(originalContent, contentToInsert, { before, after }); diff --git a/src/telemetry/utils.ts b/src/telemetry/utils.ts index be35a1d00..b6b30008f 100644 --- a/src/telemetry/utils.ts +++ b/src/telemetry/utils.ts @@ -9,8 +9,15 @@ import { VERSION } from "../version"; * Get base telemetry properties included with all events */ export function getBaseTelemetryProperties(): BaseTelemetryProperties { + const gitDescribe = + typeof VERSION === "object" && + VERSION !== null && + typeof (VERSION as Record).git_describe === "string" + ? (VERSION as { git_describe: string }).git_describe + : "unknown"; + return { - version: String(VERSION.git_describe), + version: gitDescribe, platform: window.api?.platform || "unknown", electronVersion: window.api?.versions?.electron || "unknown", }; diff --git a/src/types/tools.ts b/src/types/tools.ts index 7a060b233..633d151f7 100644 --- a/src/types/tools.ts +++ b/src/types/tools.ts @@ -74,8 +74,6 @@ export interface FileEditErrorResult { export interface FileEditInsertToolArgs { file_path: string; content: string; - /** When true, create the file if it doesn't exist */ - create?: boolean; /** Optional substring that must appear immediately before the insertion point */ before?: string; /** Optional substring that must appear immediately after the insertion point */ diff --git a/src/utils/tools/toolDefinitions.ts b/src/utils/tools/toolDefinitions.ts index e63f7f221..b8c838e8a 100644 --- a/src/utils/tools/toolDefinitions.ts +++ b/src/utils/tools/toolDefinitions.ts @@ -116,13 +116,12 @@ export const TOOL_DEFINITIONS = { description: "Insert content into a file using substring guards. " + "Provide exactly one of before or after to anchor the operation when editing an existing file. " + - "Set create: true to write a brand new file without guards. " + + "When the file does not exist, it is created automatically without guards. " + `Optional before/after substrings must uniquely match surrounding content. ${TOOL_EDIT_WARNING}`, schema: z .object({ file_path: FILE_EDIT_FILE_PATH, content: z.string().describe("The content to insert"), - create: z.boolean().optional().describe("If true, create the file if it does not exist"), before: z .string() .min(1) @@ -134,14 +133,6 @@ export const TOOL_DEFINITIONS = { .optional() .describe("Optional substring that must appear immediately after the insertion point"), }) - .refine( - (data) => data.create === true || data.before !== undefined || data.after !== undefined, - { - message: - "Provide before or after when editing existing files, or set create: true to write a new file.", - path: ["before"], - } - ) .refine((data) => !(data.before !== undefined && data.after !== undefined), { message: "Provide only one of before or after (not both).", path: ["before"], diff --git a/tests/ipcMain/runtimeExecuteBash.test.ts b/tests/ipcMain/runtimeExecuteBash.test.ts index dcb99484e..1decaa388 100644 --- a/tests/ipcMain/runtimeExecuteBash.test.ts +++ b/tests/ipcMain/runtimeExecuteBash.test.ts @@ -33,6 +33,7 @@ import { type SSHServerConfig, } from "../runtime/ssh-fixture"; import type { RuntimeConfig } from "../../src/types/runtime"; +import type { WorkspaceChatMessage } from "../../src/types/ipc"; import type { ToolPolicy } from "../../src/utils/tools/toolPolicy"; // Tool policy: Only allow bash tool @@ -41,6 +42,16 @@ const BASH_ONLY: ToolPolicy = [ { regex_match: "file_.*", action: "disable" }, ]; +function collectToolOutputs(events: WorkspaceChatMessage[], toolName: string): string { + return events + .filter((event: any) => event.type === "tool-call-end" && event.toolName === toolName) + .map((event: any) => { + const output = event.result?.output; + return typeof output === "string" ? output : ""; + }) + .join("\n"); +} + // Skip all tests if TEST_INTEGRATION is not set const describeIntegration = shouldRunIntegrationTests() ? describe : describe.skip; @@ -264,7 +275,8 @@ describeIntegration("Runtime Bash Execution", () => { // Verify command completed successfully (not timeout) expect(responseText).toContain("test"); - expect(responseText).toContain("data"); + const bashOutput = collectToolOutputs(events, "bash"); + expect(bashOutput).toContain('"test": "data"'); // Verify command completed quickly (not hanging until timeout) // With tokenizer preloading, both local and SSH complete in ~8s total