fix(coding-agent): kept short local root stable across session moves
- Derived the short Windows '/data/workspaces/can1357__oh-my-pi__2551/.omp-session/2026-06-14T07-09-37-753Z_019ec4f6-ee59-7000-8226-e1b7ed0680e9/local' root from the stable session id instead of the artifact path, so SessionManager.moveTo() keeps reading the pre-move local files. - Locked the move stability with a dedicated regression test alongside the long-path coverage. Refs #2551
This commit is contained in:
@@ -34,9 +34,11 @@ function safeSessionId(options: LocalProtocolOptions): string {
|
||||
return safe.length > 0 ? safe : "session";
|
||||
}
|
||||
|
||||
function shortLocalRoot(candidate: string, options: LocalProtocolOptions): string {
|
||||
const hash = Bun.hash(candidate).toString(36);
|
||||
return path.join(os.tmpdir(), "omp-local", `${safeSessionId(options)}-${hash}`);
|
||||
function shortLocalRoot(options: LocalProtocolOptions): string {
|
||||
// Derive the short root from the stable session id, never the artifact path,
|
||||
// so `SessionManager.moveTo()` and the resume-after-move flow keep finding
|
||||
// the same `local://` directory the session wrote pre-move.
|
||||
return path.join(os.tmpdir(), "omp-local", safeSessionId(options));
|
||||
}
|
||||
|
||||
function getContentType(filePath: string): InternalResource["contentType"] {
|
||||
@@ -126,7 +128,7 @@ export function resolveLocalRoot(options: LocalProtocolOptions, platform: NodeJS
|
||||
if (artifactsDir) {
|
||||
const candidate = path.resolve(artifactsDir, "local");
|
||||
if (platform === "win32" && candidate.length >= WINDOWS_LOCAL_ROOT_MAX_CHARS) {
|
||||
return shortLocalRoot(candidate, options);
|
||||
return shortLocalRoot(options);
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
@@ -90,8 +90,9 @@ describe("LocalProtocolHandler", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("uses a short temp root for long Windows artifact paths", async () => {
|
||||
it("uses a stable short temp root for long Windows artifact paths", async () => {
|
||||
const longArtifactsDir = path.join(os.tmpdir(), "a".repeat(220), "artifacts");
|
||||
const expectedRoot = path.join(os.tmpdir(), "omp-local", "session_long");
|
||||
const options = {
|
||||
getArtifactsDir: () => longArtifactsDir,
|
||||
getSessionId: () => "session:long",
|
||||
@@ -99,10 +100,17 @@ describe("LocalProtocolHandler", () => {
|
||||
const root = resolveLocalRoot(options, "win32");
|
||||
const resolved = resolveLocalUrlToPath("local://memo.txt", options, "win32");
|
||||
|
||||
expect(root).toContain(path.join("omp-local", "session_long-"));
|
||||
expect(root).not.toContain(longArtifactsDir);
|
||||
expect(root.length).toBeLessThan(path.join(longArtifactsDir, "local").length);
|
||||
expect(resolved).toBe(path.join(root, "memo.txt"));
|
||||
expect(root).toBe(expectedRoot);
|
||||
expect(resolved).toBe(path.join(expectedRoot, "memo.txt"));
|
||||
|
||||
// The short root must survive moves of the artifact directory so
|
||||
// `local://PLAN.md` and handoff files written pre-move stay reachable
|
||||
// after `SessionManager.moveTo()` updates `getArtifactsDir()`.
|
||||
const movedOptions = {
|
||||
getArtifactsDir: () => path.join(os.tmpdir(), "b".repeat(220), "artifacts"),
|
||||
getSessionId: () => "session:long",
|
||||
};
|
||||
expect(resolveLocalRoot(movedOptions, "win32")).toBe(expectedRoot);
|
||||
});
|
||||
|
||||
it("blocks symlink escapes outside local root", async () => {
|
||||
|
||||
Reference in New Issue
Block a user