fix(core): drain cross-realm execution promises
This commit is contained in:
@@ -21,7 +21,7 @@ run<T>(execution: AgentExecution | undefined, operation: () => T): T
|
|||||||
|
|
||||||
Types: [AgentExecution](../core-data-structures/core.md)
|
Types: [AgentExecution](../core-data-structures/core.md)
|
||||||
|
|
||||||
Source: [`packages/core/agent-execution/src/index.ts:17`](../../packages/core/agent-execution/src/index.ts)
|
Source: [`packages/core/agent-execution/src/index.ts:18`](../../packages/core/agent-execution/src/index.ts)
|
||||||
|
|
||||||
## `ctx.agentLoop` — `AgentLoop`
|
## `ctx.agentLoop` — `AgentLoop`
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
import type { Context } from 'cordis'
|
import type { Context } from 'cordis'
|
||||||
import { AsyncLocalStorage } from 'node:async_hooks'
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
||||||
|
import { isPromise } from 'node:util/types'
|
||||||
import type { AgentExecution } from './types.ts'
|
import type { AgentExecution } from './types.ts'
|
||||||
|
|
||||||
export type { AgentExecution } from './types.ts'
|
export type { AgentExecution } from './types.ts'
|
||||||
@@ -75,7 +76,7 @@ class DefaultAgentExecutionService implements AgentExecutionService {
|
|||||||
this.releaseRun()
|
this.releaseRun()
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
if (result instanceof Promise) {
|
if (isPromise(result)) {
|
||||||
void result.then(
|
void result.then(
|
||||||
() => { this.releaseRun() },
|
() => { this.releaseRun() },
|
||||||
() => { this.releaseRun() },
|
() => { this.releaseRun() },
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { Context } from 'cordis'
|
import { Context } from 'cordis'
|
||||||
|
import { runInNewContext } from 'node:vm'
|
||||||
import { AgentId, type Agent } from '@deepseek-ai/dsh-agent'
|
import { AgentId, type Agent } from '@deepseek-ai/dsh-agent'
|
||||||
import AgentExecutionProvider from '@deepseek-ai/dsh-agent-execution'
|
import AgentExecutionProvider from '@deepseek-ai/dsh-agent-execution'
|
||||||
import type { AgentExecution, AgentExecutionService } from '@deepseek-ai/dsh-agent-execution'
|
import type { AgentExecution, AgentExecutionService } from '@deepseek-ai/dsh-agent-execution'
|
||||||
@@ -133,4 +134,29 @@ describe('AgentExecutionProvider', () => {
|
|||||||
expect(() => service.current()).toThrow('agent execution service is disposed')
|
expect(() => service.current()).toThrow('agent execution service is disposed')
|
||||||
expect(() => service.require()).toThrow('agent execution service is disposed')
|
expect(() => service.require()).toThrow('agent execution service is disposed')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('drains cross-realm Promise boundaries before disposal', async () => {
|
||||||
|
const { service, dispose } = await harness()
|
||||||
|
const active = execution('cross-realm')
|
||||||
|
const release = Promise.withResolvers<boolean>()
|
||||||
|
const operation = runInNewContext(
|
||||||
|
'(async () => { await release; inspect() })',
|
||||||
|
{
|
||||||
|
release: release.promise,
|
||||||
|
inspect: () => { expect(service.require()).toBe(active) },
|
||||||
|
},
|
||||||
|
) as () => Promise<void>
|
||||||
|
const pending = service.run(active, operation)
|
||||||
|
expect(pending).not.toBeInstanceOf(Promise)
|
||||||
|
|
||||||
|
let disposed = false
|
||||||
|
const disposal = dispose().then(() => { disposed = true })
|
||||||
|
await Promise.resolve()
|
||||||
|
expect(disposed).toBe(false)
|
||||||
|
|
||||||
|
release.resolve(true)
|
||||||
|
await pending
|
||||||
|
await disposal
|
||||||
|
expect(disposed).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user