feat(subagent): sibling delegations are concurrency-safe
One assistant message carrying several subagent calls now overlaps them under the rolling pool (maxParallelToolCalls) instead of serializing each foreground delegation behind an exclusive barrier; results still commit in model order. tool-subagent declares isConcurrencySafe: () => true for every call form: children work in their own sessions, a run never mutates the parent session, and sibling workspace coordination already belongs to the model for background, continuable, and workflow children. The former serial-execution pin flips to a parallel pin plus a gated genuine-overlap test, and the authored subagent-parallel snapshot pins the assembled transcript (tool/call, tool/call, tool/result, tool/result) with interchangeable twin children, which replay race-free under the existing first-call binding and createdAt harvest ordering. The provider seam now requires concurrent starts and continuable preparations for distinct children to isolate operation-local state, cancellation, settlement, and cleanup; a continuable gate test proves a cancelled preparation leaves no Agent or durable Session while its sibling persists independently. Closes deepseek-harness/deepseek-harness#1489
This commit is contained in:
@@ -33,6 +33,8 @@ export interface Config {
|
||||
inheritsParentContext?: boolean
|
||||
/** Structured value returned when the request asks for one. */
|
||||
structured?: unknown
|
||||
/** Observes each start; the child's result additionally waits for the returned promise. */
|
||||
onStart?: (request: SubagentStartRequest) => Promise<void> | void
|
||||
}
|
||||
|
||||
/** Scripted provider whose result aborts if its signal or disposer wins first. */
|
||||
@@ -68,9 +70,10 @@ class ScriptedSubagentProvider implements SubagentProvider {
|
||||
...wantsStructured ? { structured: this.config.structured ?? { reply } } : {},
|
||||
stopReason: state.cancelled ? 'aborted' : stopReason,
|
||||
})
|
||||
const result = new Promise<SubagentResult>((resolve) => {
|
||||
const gate = Promise.resolve(this.config.onStart?.(request))
|
||||
const result = gate.then(() => new Promise<SubagentResult>((resolve) => {
|
||||
setTimeout(() => { resolve(resultFor()) }, 0)
|
||||
}).finally(() => {
|
||||
})).finally(() => {
|
||||
request.signal.removeEventListener('abort', onAbort)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user