mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix: coordinate overlapping configured reloads
This commit is contained in:
@@ -53,6 +53,7 @@ function renderThrown(value: unknown): string {
|
||||
/** Factory-level ownership of every preparing or live transaction. */
|
||||
class FactoryOwnership {
|
||||
private accepting = true
|
||||
private readonly inactive = Promise.withResolvers<void>()
|
||||
private transactions = new Set<AgentCreationTransaction>()
|
||||
private startupTasks = new Set<Promise<void>>()
|
||||
|
||||
@@ -74,8 +75,14 @@ class FactoryOwnership {
|
||||
void task.then(forget, forget)
|
||||
}
|
||||
|
||||
/** Resolve `task`, or stop waiting when factory teardown begins. */
|
||||
async waitWhileActive(task: Promise<void>): Promise<void> {
|
||||
await Promise.race([task, this.inactive.promise])
|
||||
}
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
this.accepting = false
|
||||
this.inactive.resolve()
|
||||
const reason = new Error('agent loop is not active')
|
||||
await Promise.all([
|
||||
...[...this.transactions].map(transaction => transaction.disposeForFactory(reason)),
|
||||
@@ -455,6 +462,8 @@ export class AgentLoop extends Service implements AgentFactory {
|
||||
agentOptions: AgentOptions,
|
||||
meta: Pick<SessionHeader, 'cwd'>,
|
||||
): Promise<void> {
|
||||
await this.waitForDrainingConfiguredIdentity(ownerCtx, sessionId)
|
||||
if (!this.ownership.isActive()) return
|
||||
const exists = (await persistence.list()).some(header => header.id === sessionId)
|
||||
if (!this.ownership.isActive()) return
|
||||
if (exists) {
|
||||
@@ -464,6 +473,32 @@ export class AgentLoop extends Service implements AgentFactory {
|
||||
this.create(sessionId, agentOptions, meta)
|
||||
}
|
||||
|
||||
/** Wait for an already-disposed same-id lifecycle to finish registry teardown. */
|
||||
private async waitForDrainingConfiguredIdentity(ownerCtx: Context, sessionId: SessionId): Promise<void> {
|
||||
const current = ownerCtx.agents.get(sessionId)
|
||||
if (current?.status !== 'disposed') return
|
||||
|
||||
const released = Promise.withResolvers<void>()
|
||||
const checkReleased = (): void => {
|
||||
if (ownerCtx.agents.get(sessionId) === undefined && ownerCtx.sessions.get(sessionId) === undefined) {
|
||||
released.resolve()
|
||||
}
|
||||
}
|
||||
const disposeAgentListener = ownerCtx.on('agent/disposed', (agent) => {
|
||||
if (agent.id === sessionId) checkReleased()
|
||||
})
|
||||
const disposeSessionListener = ownerCtx.on('session/disposed', (session) => {
|
||||
if (session.id === sessionId) checkReleased()
|
||||
})
|
||||
try {
|
||||
checkReleased()
|
||||
await this.ownership.waitWhileActive(released.promise)
|
||||
} finally {
|
||||
disposeAgentListener()
|
||||
disposeSessionListener()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an agent and session under one caller-supplied identity, owned by
|
||||
* the accessing fiber. Constructor-driven config calls mint a fresh combined
|
||||
|
||||
Reference in New Issue
Block a user