From 4793ca154ab1f6c9d0d5aaf4fbc18467baff074c Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 3 Jun 2026 19:59:49 +0800 Subject: [PATCH] fix(v0.1): recover session PVC when prior create left session without storage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前的失败用例会让 session 留在 storageKind=none 状态但 pvcName 缺失, 现在 POST /api/v1/sessions 在 storageKind=none || !storagePvcName 时 重新调 createSessionPvc 补建,action=session-storage-recovered。 selftest 覆盖:显式 reset storageKind=none 后第二次 POST 走 recovery。 --- src/mgr/server.ts | 4 ++++ src/selftest/cases/10-mgr-session-pvc.ts | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/mgr/server.ts b/src/mgr/server.ts index b99c104..51e2f48 100644 --- a/src/mgr/server.ts +++ b/src/mgr/server.ts @@ -179,6 +179,10 @@ async function route({ method, url, body, store, sourceCommit, runnerJobDefaults const existing = await store.getSession(sessionId); if (existing) { if (existing.storageKind === "evicted") throw new AgentRunError("session-store-evicted", `session ${sessionId} storage has been evicted; create a new sessionId`, { httpStatus: 409 }); + if (existing.storageKind === "none" || !existing.storagePvcName) { + const recovered = await createSessionPvc({ store, sessionId, options: { ...sessionPvcOptionsForRequest(sessionPvcDefaults, runnerJobDefaults), defaultCodexRolloutSubdir: codexRolloutSubdir } }); + return { action: "session-storage-recovered", session: existing, pvc: recovered, valuesPrinted: false } as unknown as JsonValue; + } return { action: "session-exists", session: existing, pvcName: existing.storagePvcName ?? null, pvcPhase: existing.storagePvcPhase ?? null, codexRolloutSubdir: existing.codexRolloutSubdir ?? "sessions", valuesPrinted: false } as unknown as JsonValue; } const now = new Date().toISOString(); diff --git a/src/selftest/cases/10-mgr-session-pvc.ts b/src/selftest/cases/10-mgr-session-pvc.ts index e755712..60e1caf 100644 --- a/src/selftest/cases/10-mgr-session-pvc.ts +++ b/src/selftest/cases/10-mgr-session-pvc.ts @@ -79,8 +79,15 @@ const selfTest: SelfTestCase = async () => { const create = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_001", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string; pvc: { pvcName: string; pvcPhase: string } }; assert.equal(create.action, "session-created"); assert.ok(create.pvc.pvcName.startsWith("agentrun-v01-session-")); - const existing = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_001", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string }; - assert.equal(existing.action, "session-exists"); + const existing = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_001", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string }; + assert.equal(existing.action, "session-exists"); + const recovered = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_002", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string }; + assert.equal(recovered.action, "session-created"); + const recoveredFromNone = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_003", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string }; + assert.equal(recoveredFromNone.action, "session-created"); + await restStore.refreshSessionStorage({ sessionId: "sess_rest_create_003", storageKind: "none", codexRolloutSubdir: "sessions" }); + const recoveredAgain = await client.post("/api/v1/sessions", { sessionId: "sess_rest_create_003", tenantId: "hwlab", projectId: "pikasTech/HWLAB", backendProfile: "codex" }) as { action: string }; + assert.equal(recoveredAgain.action, "session-storage-recovered"); } finally { if (server.server.listening) await new Promise((resolve) => server.server.close(() => resolve())); }