fix(agent-loop): tighten parallel tool-call safety
This commit is contained in:
@@ -26,7 +26,7 @@ With `run_in_background: true`, the tool registers the parent-owned task before
|
||||
|
||||
## Concurrency
|
||||
|
||||
Foreground calls opt into concurrent scheduling because each owns an independent child run and returns only its final answer. Background starts remain exclusive because they register parent-owned task state. Providers must accept concurrent `start()` calls for independent runs; they may queue internally, enforce capacity, or return a typed failure. See the [parallel tool-call RFC](../../../docs/rfc/implemented/feature/2026-07-10-parallel-tool-call-execution.md).
|
||||
Foreground and background calls are exclusive. Children may share the parent's workspace or external resources, and the unary scheduler classifier cannot prove that sibling delegations have disjoint effects. See the [parallel tool-call RFC](../../../docs/rfc/implemented/feature/2026-07-10-parallel-tool-call-execution.md).
|
||||
|
||||
## Model Experience
|
||||
|
||||
|
||||
@@ -174,8 +174,7 @@ export function providerWording(inheritsConversation: boolean): { description: s
|
||||
+ 'completed turns so far (it does not see the current in-flight turn), returning only its final '
|
||||
+ 'result. Use this when the subtask builds on this conversation\'s context — a follow-up analysis, '
|
||||
+ 'a review, a continuation — without consuming this conversation\'s context for the work itself. '
|
||||
+ 'You receive only its final answer, not its intermediate steps. You may issue several subagent '
|
||||
+ 'calls in one message to run independent tasks concurrently when their work scopes do not overlap.',
|
||||
+ 'You receive only its final answer, not its intermediate steps.',
|
||||
promptDescription:
|
||||
'The task for the subagent. It already sees this conversation\'s completed turns, so build on them '
|
||||
+ 'freely and state only what is new.',
|
||||
@@ -187,8 +186,7 @@ export function providerWording(inheritsConversation: boolean): { description: s
|
||||
+ 'and return its final result. Use this to offload focused, independent work — research, a scoped '
|
||||
+ 'implementation, an analysis — so it does not consume this conversation\'s context. The subagent '
|
||||
+ 'runs to completion and you receive only its final answer, not its intermediate steps. Give it a '
|
||||
+ 'complete, standalone prompt: it does not see this conversation. You may issue several subagent '
|
||||
+ 'calls in one message to run independent tasks concurrently when their work scopes do not overlap.',
|
||||
+ 'complete, standalone prompt: it does not see this conversation.',
|
||||
promptDescription:
|
||||
'The complete, self-contained task for the subagent. It does not share this '
|
||||
+ 'conversation\'s context, so include everything it needs.',
|
||||
@@ -254,9 +252,6 @@ export function apply(ctx: Context, config: Config): void {
|
||||
},
|
||||
} : {},
|
||||
},
|
||||
// A foreground call owns only its child run; background mode first
|
||||
// registers parent-owned task state and therefore remains exclusive.
|
||||
isConcurrencySafe: args => args.run_in_background !== true,
|
||||
async execute(args, exec): Promise<ContentBlock[]> {
|
||||
const parent = exec.agent
|
||||
if (!parent) {
|
||||
|
||||
@@ -96,13 +96,13 @@ describe('dsh-tool-subagent', () => {
|
||||
expect(foreground.isError).toBe(false)
|
||||
})
|
||||
|
||||
it('classifies foreground calls as parallel and background starts as exclusive', async () => {
|
||||
it('keeps foreground and background calls exclusive', async () => {
|
||||
const ctx = await setup({ provider: 'mock' })
|
||||
expect(ctx.tools.executionMode({
|
||||
callId: CallId('subagent-safe'),
|
||||
callId: CallId('subagent-foreground'),
|
||||
name: 'subagent',
|
||||
arguments: { description: 'do work', prompt: 'Reply OK' },
|
||||
})).toEqual({ kind: 'parallel' })
|
||||
})).toEqual({ kind: 'exclusive' })
|
||||
expect(ctx.tools.executionMode({
|
||||
callId: CallId('subagent-background'),
|
||||
name: 'subagent',
|
||||
|
||||
Reference in New Issue
Block a user