Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/services/tools/file_edit_insert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 2 additions & 10 deletions src/services/tools/file_edit_insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<FileEditInsertToolResult> => {
try {
Expand All @@ -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) {
Expand Down Expand Up @@ -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 });
Expand Down
9 changes: 8 additions & 1 deletion src/telemetry/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>).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",
};
Expand Down
2 changes: 0 additions & 2 deletions src/types/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
11 changes: 1 addition & 10 deletions src/utils/tools/toolDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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"],
Expand Down
14 changes: 13 additions & 1 deletion tests/ipcMain/runtimeExecuteBash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand Down