From b1e19d8b697865d20db02facd990052e9d6347d0 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Thu, 16 Jul 2026 18:07:23 +0800 Subject: [PATCH] fix(core): drain cross-realm execution promises --- docs/cordis-catalog/services.md | 2 +- packages/core/agent-execution/src/index.ts | 3 ++- .../tests/agent-execution.spec.ts | 26 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/cordis-catalog/services.md b/docs/cordis-catalog/services.md index dc4976238f..ac002b3e4b 100644 --- a/docs/cordis-catalog/services.md +++ b/docs/cordis-catalog/services.md @@ -21,7 +21,7 @@ run(execution: AgentExecution | undefined, operation: () => T): T 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` diff --git a/packages/core/agent-execution/src/index.ts b/packages/core/agent-execution/src/index.ts index 3aef4c1856..cb2683dbaa 100644 --- a/packages/core/agent-execution/src/index.ts +++ b/packages/core/agent-execution/src/index.ts @@ -6,6 +6,7 @@ import type { Context } from 'cordis' import { AsyncLocalStorage } from 'node:async_hooks' +import { isPromise } from 'node:util/types' import type { AgentExecution } from './types.ts' export type { AgentExecution } from './types.ts' @@ -75,7 +76,7 @@ class DefaultAgentExecutionService implements AgentExecutionService { this.releaseRun() throw error } - if (result instanceof Promise) { + if (isPromise(result)) { void result.then( () => { this.releaseRun() }, () => { this.releaseRun() }, diff --git a/packages/core/agent-execution/tests/agent-execution.spec.ts b/packages/core/agent-execution/tests/agent-execution.spec.ts index ccf17f6722..e03010c569 100644 --- a/packages/core/agent-execution/tests/agent-execution.spec.ts +++ b/packages/core/agent-execution/tests/agent-execution.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest' import { Context } from 'cordis' +import { runInNewContext } from 'node:vm' import { AgentId, type Agent } from '@deepseek-ai/dsh-agent' import AgentExecutionProvider 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.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() + const operation = runInNewContext( + '(async () => { await release; inspect() })', + { + release: release.promise, + inspect: () => { expect(service.require()).toBe(active) }, + }, + ) as () => Promise + 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) + }) })