Fix SDK tools to respect cwd option

Core tools now properly use the cwd passed to createAgentSession().
Added tool factory functions for SDK users who specify custom cwd with explicit tools.

Fixes #279
This commit is contained in:
Mario Zechner 2025-12-22 16:17:55 +01:00
parent 42bc368e70
commit face745f3d
16 changed files with 1243 additions and 1044 deletions

View file

@ -1,82 +1,93 @@
import type { AgentTool } from "@mariozechner/pi-ai";
import { Type } from "@sinclair/typebox";
import { mkdir, writeFile } from "fs/promises";
import { dirname, resolve as resolvePath } from "path";
import { expandPath } from "./path-utils.js";
import { dirname } from "path";
import { resolveToCwd } from "./path-utils.js";
const writeSchema = Type.Object({
path: Type.String({ description: "Path to the file to write (relative or absolute)" }),
content: Type.String({ description: "Content to write to the file" }),
});
export const writeTool: AgentTool<typeof writeSchema> = {
name: "write",
label: "write",
description:
"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.",
parameters: writeSchema,
execute: async (_toolCallId: string, { path, content }: { path: string; content: string }, signal?: AbortSignal) => {
const absolutePath = resolvePath(expandPath(path));
const dir = dirname(absolutePath);
export function createWriteTool(cwd: string): AgentTool<typeof writeSchema> {
return {
name: "write",
label: "write",
description:
"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.",
parameters: writeSchema,
execute: async (
_toolCallId: string,
{ path, content }: { path: string; content: string },
signal?: AbortSignal,
) => {
const absolutePath = resolveToCwd(path, cwd);
const dir = dirname(absolutePath);
return new Promise<{ content: Array<{ type: "text"; text: string }>; details: undefined }>((resolve, reject) => {
// Check if already aborted
if (signal?.aborted) {
reject(new Error("Operation aborted"));
return;
}
let aborted = false;
// Set up abort handler
const onAbort = () => {
aborted = true;
reject(new Error("Operation aborted"));
};
if (signal) {
signal.addEventListener("abort", onAbort, { once: true });
}
// Perform the write operation
(async () => {
try {
// Create parent directories if needed
await mkdir(dir, { recursive: true });
// Check if aborted before writing
if (aborted) {
return new Promise<{ content: Array<{ type: "text"; text: string }>; details: undefined }>(
(resolve, reject) => {
// Check if already aborted
if (signal?.aborted) {
reject(new Error("Operation aborted"));
return;
}
// Write the file
await writeFile(absolutePath, content, "utf-8");
let aborted = false;
// Check if aborted after writing
if (aborted) {
return;
}
// Set up abort handler
const onAbort = () => {
aborted = true;
reject(new Error("Operation aborted"));
};
// Clean up abort handler
if (signal) {
signal.removeEventListener("abort", onAbort);
signal.addEventListener("abort", onAbort, { once: true });
}
resolve({
content: [{ type: "text", text: `Successfully wrote ${content.length} bytes to ${path}` }],
details: undefined,
});
} catch (error: any) {
// Clean up abort handler
if (signal) {
signal.removeEventListener("abort", onAbort);
}
// Perform the write operation
(async () => {
try {
// Create parent directories if needed
await mkdir(dir, { recursive: true });
if (!aborted) {
reject(error);
}
}
})();
});
},
};
// Check if aborted before writing
if (aborted) {
return;
}
// Write the file
await writeFile(absolutePath, content, "utf-8");
// Check if aborted after writing
if (aborted) {
return;
}
// Clean up abort handler
if (signal) {
signal.removeEventListener("abort", onAbort);
}
resolve({
content: [{ type: "text", text: `Successfully wrote ${content.length} bytes to ${path}` }],
details: undefined,
});
} catch (error: any) {
// Clean up abort handler
if (signal) {
signal.removeEventListener("abort", onAbort);
}
if (!aborted) {
reject(error);
}
}
})();
},
);
},
};
}
/** Default write tool using process.cwd() - for backwards compatibility */
export const writeTool = createWriteTool(process.cwd());