fix(coding-agent): use async operations in tools#4756
Conversation
| import { resolve } from "node:path"; | ||
|
|
||
| const fileMutationQueues = new Map<string, Promise<void>>(); | ||
| let registrationQueue = Promise.resolve(); |
There was a problem hiding this comment.
the registrationQueue was the simplest thing i could come up with that retains the "execute everything in order" behavior from before.
|
i see no better way to do the registration ordering if we have to make There are two separate race questions here.
If const key = await getMutationQueueKey(filePath);
const currentQueue = fileMutationQueues.get(key);
fileMutationQueues.set(key, chainedQueue);Two same-file operations can both resolve keys concurrently, and whichever
The bigger problem is in the tools using the queue. They have this shape: return withFileMutationQueue(path, () =>
new Promise((resolve, reject) => {
const onAbort = () => {
aborted = true;
reject(new Error("Operation aborted"));
};
(async () => {
await ops.writeFile(...);
if (aborted) return;
resolve(...);
})();
}),
);That means:
So the queue serializes the lifetime of the wrapper promise, not necessarily the lifetime of the actual filesystem operation. Abort can release the queue early. The queued mutation body should not use a detached async IIFE plus immediate abort rejection. It should be an return withFileMutationQueue(absolutePath, async () => {
if (signal?.aborted) throw new Error("Operation aborted");
let aborted = false;
const onAbort = () => {
aborted = true;
};
signal?.addEventListener("abort", onAbort, { once: true });
try {
await ops.access(absolutePath);
if (aborted) throw new Error("Operation aborted");
const buffer = await ops.readFile(absolutePath);
if (aborted) throw new Error("Operation aborted");
await ops.writeFile(absolutePath, content);
if (aborted) throw new Error("Operation aborted");
return result;
} finally {
signal?.removeEventListener("abort", onAbort);
}
});So I don't think the registration ordering is the main issue. The queued tool bodies need to be fixed too, otherwise the queue can still be bypassed by abort timing. |
|
Wrapped this locally with the follow-up cleanup:
This comment is AI-generated by |
On Windows the Microsoft Defender can make things hang and when it hangs, the TUI also locks up. This moves some of the more likely sync fs operations that can happen while during streaming to async operations.
Additionally this moves the image handling (resizing) into a worker.