From a4c45e2770781978d594586421b94efdd107ccfb Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sat, 18 Apr 2026 14:11:03 +0900 Subject: [PATCH] fix(write-existing-file-guard): defer realpath/existsSync to first tool call Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/hooks/write-existing-file-guard/hook.ts | 12 ++++++++++-- .../lazy-canonical-path-init.test.ts | 4 ++-- .../tool-execute-before-handler.ts | 5 +++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/hooks/write-existing-file-guard/hook.ts b/src/hooks/write-existing-file-guard/hook.ts index bdaf5cad8..ab7bd9aef 100644 --- a/src/hooks/write-existing-file-guard/hook.ts +++ b/src/hooks/write-existing-file-guard/hook.ts @@ -76,7 +76,15 @@ export function isOverwriteEnabled(value: boolean | string | undefined): boolean export function createWriteExistingFileGuardHook(ctx: PluginInput): Hooks { const readPermissionsBySession = new Map>() const sessionLastAccess = new Map() - const canonicalSessionRoot = toCanonicalPath(resolveInputPath(ctx, ctx.directory)) + let canonicalSessionRoot: string | undefined + + function getCanonicalSessionRoot(): string { + if (!canonicalSessionRoot) { + canonicalSessionRoot = toCanonicalPath(resolveInputPath(ctx, ctx.directory)) + } + + return canonicalSessionRoot + } return { "tool.execute.before": async (input, output) => { @@ -86,7 +94,7 @@ export function createWriteExistingFileGuardHook(ctx: PluginInput): Hooks { output, readPermissionsBySession, sessionLastAccess, - canonicalSessionRoot, + getCanonicalSessionRoot, maxTrackedSessions: MAX_TRACKED_SESSIONS, }) }, diff --git a/src/hooks/write-existing-file-guard/lazy-canonical-path-init.test.ts b/src/hooks/write-existing-file-guard/lazy-canonical-path-init.test.ts index 1d3ada213..0f1d4bb88 100644 --- a/src/hooks/write-existing-file-guard/lazy-canonical-path-init.test.ts +++ b/src/hooks/write-existing-file-guard/lazy-canonical-path-init.test.ts @@ -59,7 +59,7 @@ describe("createWriteExistingFileGuardHook", () => { ).rejects.toThrow("File already exists. Use edit tool instead.") // then - expect(existsSyncMock).toHaveBeenCalledTimes(2) - expect(realpathNativeMock).toHaveBeenCalledTimes(1) + expect(existsSyncMock).toHaveBeenCalledTimes(3) + expect(realpathNativeMock).toHaveBeenCalledTimes(2) }) }) diff --git a/src/hooks/write-existing-file-guard/tool-execute-before-handler.ts b/src/hooks/write-existing-file-guard/tool-execute-before-handler.ts index 25eebbda3..848238a8a 100644 --- a/src/hooks/write-existing-file-guard/tool-execute-before-handler.ts +++ b/src/hooks/write-existing-file-guard/tool-execute-before-handler.ts @@ -90,10 +90,10 @@ export async function handleWriteExistingFileGuardToolExecuteBefore(params: { output: { args?: unknown } readPermissionsBySession: Map> sessionLastAccess: Map - canonicalSessionRoot: string + getCanonicalSessionRoot: () => string maxTrackedSessions: number }): Promise { - const { ctx, input, output, readPermissionsBySession, sessionLastAccess, canonicalSessionRoot, maxTrackedSessions } = params + const { ctx, input, output, readPermissionsBySession, sessionLastAccess, getCanonicalSessionRoot, maxTrackedSessions } = params const toolName = input.tool?.toLowerCase() if (toolName !== "write" && toolName !== "read") { return @@ -107,6 +107,7 @@ export async function handleWriteExistingFileGuardToolExecuteBefore(params: { } const resolvedPath = resolveInputPath(ctx, filePath) + const canonicalSessionRoot = getCanonicalSessionRoot() const canonicalPath = toCanonicalPath(resolvedPath) if (!isPathInsideDirectory(canonicalPath, canonicalSessionRoot)) { return