Quellcode durchsuchen

Merge pull request #2760 from deepseek-harness/fix/continuation-cleanup-temp-handles

test(subagent): close persistence handle before deleting temp root
Chinesezjc vor 1 Monat
Ursprung
Commit
b7135e6206
1 geänderte Dateien mit 25 neuen und 7 gelöschten Zeilen
  1. 25 7
      packages/subagent/subagent/tests/continuation.spec.ts

+ 25 - 7
packages/subagent/subagent/tests/continuation.spec.ts

@@ -51,9 +51,17 @@ class GatedAdapter extends LlmAdapter {
   }
 }
 
-const roots: string[] = []
-afterEach(() => {
-  for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
+// Each persistence-backed temp root cleans up by closing its handle before
+// removing the directory: Windows rmSync over a dir holding a still-open handle
+// fails with EPERM.
+const cleanups: Array<() => Promise<void>> = []
+afterEach(async () => {
+  const errors: unknown[] = []
+  for (const cleanup of cleanups.splice(0)) {
+    try { await cleanup() } catch (error) { errors.push(error) }
+  }
+  if (errors.length === 1) throw errors[0]
+  if (errors.length > 1) throw new AggregateError(errors, 'temp-root cleanup failed')
 })
 
 /** Boot the full continuable stack: loop, persistence, providers, and subagents. */
@@ -64,9 +72,13 @@ async function setupWith(adapter: LlmAdapter, options: { persistence?: boolean }
   let root: string | undefined
   if (options.persistence !== false) {
     root = mkdtempSync(join(tmpdir(), 'dsh-subagent-continuation-'))
-    roots.push(root)
+    const persistedRoot = root
     const persistenceFiber = await ctx.plugin(JsonlSessionPersistence, { root })
     disposePersistence = () => persistenceFiber.dispose()
+    cleanups.push(async () => {
+      await persistenceFiber.dispose()
+      rmSync(persistedRoot, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
+    })
   }
   await ctx.plugin(AgentLoop, { agents: [] })
   await ctx.plugin(SubagentRuntime)
@@ -404,7 +416,10 @@ describe('SubagentRuntime.startContinuable', () => {
 
     const fresh = new Context()
     await mountAgentLoopTestDependencies(fresh)
-    await fresh.plugin(JsonlSessionPersistence, { root: root! })
+    const freshPersistence = await fresh.plugin(JsonlSessionPersistence, { root: root! })
+    // This context opened a second handle on the same root; register it so
+    // afterEach closes it before removing the root (even on a failure path).
+    cleanups.push(async () => { await freshPersistence.dispose() })
     await fresh.plugin(AgentLoop, { agents: [] })
     await fresh.plugin(SubagentRuntime)
     await fresh.plugin(SubagentSpawn, { providerName: 'spawn' })
@@ -2432,8 +2447,11 @@ describe('continuable errors', () => {
     const ctx = new Context()
     await mountAgentLoopTestDependencies(ctx)
     const root = mkdtempSync(join(tmpdir(), 'dsh-subagent-continuation-'))
-    roots.push(root)
-    await ctx.plugin(JsonlSessionPersistence, { root })
+    const persistenceFiber = await ctx.plugin(JsonlSessionPersistence, { root })
+    cleanups.push(async () => {
+      await persistenceFiber.dispose()
+      rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
+    })
     await ctx.plugin(AgentLoop, { agents: [] })
     const serviceFiber = await ctx.plugin(SubagentRuntime)
     await ctx.plugin(SubagentSpawn, { providerName: 'spawn' })