Merge branch 'fix/perf-q06' into fix/perf-omo-in-tree

This commit is contained in:
Sisyphus
2026-04-18 14:43:37 +09:00
3 changed files with 78 additions and 4 deletions
+10 -2
View File
@@ -76,7 +76,15 @@ export function isOverwriteEnabled(value: boolean | string | undefined): boolean
export function createWriteExistingFileGuardHook(ctx: PluginInput): Hooks {
const readPermissionsBySession = new Map<string, Set<string>>()
const sessionLastAccess = new Map<string, number>()
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,
})
},
@@ -0,0 +1,65 @@
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
const realFs = await import("node:fs")
const existsSyncMock = mock(realFs.existsSync)
const realpathNativeMock = mock(realFs.realpathSync.native)
mock.module("fs", () => ({
...realFs,
existsSync: existsSyncMock,
realpathSync: {
...realFs.realpathSync,
native: realpathNativeMock,
},
}))
const { createWriteExistingFileGuardHook } = await import("./index")
describe("createWriteExistingFileGuardHook", () => {
let tempDir = ""
beforeEach(() => {
// given
tempDir = mkdtempSync(join(tmpdir(), "write-existing-file-guard-lazy-"))
mkdirSync(tempDir, { recursive: true })
existsSyncMock.mockClear()
realpathNativeMock.mockClear()
})
afterEach(() => {
rmSync(tempDir, { recursive: true, force: true })
})
test("#given hook factory #when created #then defers fs canonical path calls until first tool invocation", async () => {
// given
const existingFile = join(tempDir, "existing.txt")
writeFileSync(existingFile, "content")
// when
const hook = createWriteExistingFileGuardHook({ directory: tempDir } as never)
// then
expect(existsSyncMock).toHaveBeenCalledTimes(0)
expect(realpathNativeMock).toHaveBeenCalledTimes(0)
// when
await expect(
hook["tool.execute.before"]?.(
{
tool: "write",
sessionID: "ses_lazy",
callID: "call_lazy",
} as never,
{ args: { filePath: existingFile, content: "updated" } } as never,
),
).rejects.toThrow("File already exists. Use edit tool instead.")
// then
expect(existsSyncMock).toHaveBeenCalledTimes(3)
expect(realpathNativeMock).toHaveBeenCalledTimes(2)
})
})
@@ -90,10 +90,10 @@ export async function handleWriteExistingFileGuardToolExecuteBefore(params: {
output: { args?: unknown }
readPermissionsBySession: Map<string, Set<string>>
sessionLastAccess: Map<string, number>
canonicalSessionRoot: string
getCanonicalSessionRoot: () => string
maxTrackedSessions: number
}): Promise<void> {
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