Skip to content

Commit 79374e0

Browse files
committed
fix(AttemptCompletionTool): child tasks return to parent when parent status is active (#510)
* chore: add subtask delegation diagnostics * fix(AttempCompletionTool): allow completion of active parents * test(subtasks): harness updates
1 parent ff55788 commit 79374e0

6 files changed

Lines changed: 479 additions & 12 deletions

File tree

apps/vscode-e2e/src/fixtures/subtasks.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,24 @@ import { toolResultContains } from "./tool-result"
55

66
const SUBTASK_PARENT_MARKER = "SUBTASK_PARENT_CANCELLATION_SMOKE"
77
const SUBTASK_CHILD_MARKER = "SUBTASK_CHILD_CALCULATOR_SMOKE"
8+
const SUBTASK_FAST_PARENT_MARKER = "SUBTASK_PARENT_IMMEDIATE_COMPLETION"
9+
const SUBTASK_FAST_CHILD_MARKER = "SUBTASK_CHILD_IMMEDIATE_COMPLETION"
10+
const SUBTASK_XPROFILE_PARENT_MARKER = "SUBTASK_PARENT_CROSS_PROFILE"
11+
const SUBTASK_XPROFILE_SAME_CHILD_MARKER = "SUBTASK_CHILD_SAME_PROFILE"
12+
const SUBTASK_XPROFILE_DIFFERENT_CHILD_MARKER = "SUBTASK_CHILD_DIFFERENT_PROFILE"
813

914
const SUBTASK_CHILD_PROMPT = `${SUBTASK_CHILD_MARKER}: Ask the user exactly this follow-up question: What is the square root of 81? After the user answers, complete with only the answer.`
1015
export const SUBTASK_PARENT_PROMPT = `${SUBTASK_PARENT_MARKER}: Use the new_task tool exactly once. Create an ask-mode subtask with this exact message: "${SUBTASK_CHILD_PROMPT}" Do not answer directly.`
1116
export const SUBTASK_CHILD_FOLLOWUP_ANSWER = "9"
17+
const SUBTASK_FAST_CHILD_PROMPT = `${SUBTASK_FAST_CHILD_MARKER}: Complete immediately with the exact result "Fast child completed".`
18+
export const SUBTASK_FAST_PARENT_PROMPT = `${SUBTASK_FAST_PARENT_MARKER}: Use the new_task tool exactly once. Create an ask-mode subtask with this exact message: "${SUBTASK_FAST_CHILD_PROMPT}" Do not answer directly.`
19+
20+
const SUBTASK_XPROFILE_SAME_CHILD_PROMPT = `${SUBTASK_XPROFILE_SAME_CHILD_MARKER}: Complete immediately with the exact result "Same-profile child completed".`
21+
const SUBTASK_XPROFILE_DIFFERENT_CHILD_PROMPT = `${SUBTASK_XPROFILE_DIFFERENT_CHILD_MARKER}: Complete immediately with the exact result "Different-profile child completed".`
22+
export const SUBTASK_XPROFILE_PARENT_PROMPT = `${SUBTASK_XPROFILE_PARENT_MARKER}: First use new_task to create a code-mode subtask with this exact message: "${SUBTASK_XPROFILE_SAME_CHILD_PROMPT}" After it returns, create an ask-mode subtask with the next instructions you receive.`
23+
export const SUBTASK_XPROFILE_SAME_CHILD_RESULT = "Same-profile child completed"
24+
export const SUBTASK_XPROFILE_DIFFERENT_CHILD_RESULT = "Different-profile child completed"
25+
export const SUBTASK_XPROFILE_PARENT_RESULT = "Sequential cross-profile parent resumed"
1226

1327
const requestContains = (req: ChatCompletionRequest, expected: string[]) => {
1428
const rawRequest = JSON.stringify(req)
@@ -40,6 +54,55 @@ const completionAfterAnswer = (followupId: string, completionId: string) => ({
4054
})
4155

4256
export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
57+
mock.addFixture({
58+
match: {
59+
userMessage: new RegExp(SUBTASK_FAST_PARENT_MARKER),
60+
sequenceIndex: 0,
61+
},
62+
response: {
63+
toolCalls: [
64+
{
65+
name: "new_task",
66+
arguments: JSON.stringify({
67+
mode: "ask",
68+
message: SUBTASK_FAST_CHILD_PROMPT,
69+
}),
70+
id: "call_subtasks_fast_parent_new_task_001",
71+
},
72+
],
73+
},
74+
})
75+
76+
mock.addFixture({
77+
match: {
78+
userMessage: new RegExp(SUBTASK_FAST_CHILD_MARKER),
79+
},
80+
response: {
81+
toolCalls: [
82+
{
83+
name: "attempt_completion",
84+
arguments: JSON.stringify({ result: "Fast child completed" }),
85+
id: "call_subtasks_fast_child_completion_002",
86+
},
87+
],
88+
},
89+
})
90+
91+
mock.addFixture({
92+
match: {
93+
toolCallId: "call_subtasks_fast_parent_new_task_001",
94+
},
95+
response: {
96+
toolCalls: [
97+
{
98+
name: "attempt_completion",
99+
arguments: JSON.stringify({ result: "Fast parent resumed" }),
100+
id: "call_subtasks_fast_parent_completion_003",
101+
},
102+
],
103+
},
104+
})
105+
43106
mock.addFixture({
44107
match: {
45108
userMessage: new RegExp(SUBTASK_PARENT_MARKER),
@@ -92,4 +155,95 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
92155
],
93156
},
94157
})
158+
159+
// Issue #457 sequence: a same-profile child returns first, then the resumed
160+
// parent delegates to a child whose mode uses a different API profile.
161+
mock.addFixture({
162+
match: {
163+
userMessage: new RegExp(SUBTASK_XPROFILE_PARENT_MARKER),
164+
sequenceIndex: 0,
165+
},
166+
response: {
167+
toolCalls: [
168+
{
169+
name: "new_task",
170+
arguments: JSON.stringify({
171+
mode: "code",
172+
message: SUBTASK_XPROFILE_SAME_CHILD_PROMPT,
173+
}),
174+
id: "call_subtasks_xprofile_parent_same_child_001",
175+
},
176+
],
177+
},
178+
})
179+
180+
mock.addFixture({
181+
match: {
182+
userMessage: new RegExp(SUBTASK_XPROFILE_SAME_CHILD_MARKER),
183+
},
184+
response: {
185+
toolCalls: [
186+
{
187+
name: "attempt_completion",
188+
arguments: JSON.stringify({ result: SUBTASK_XPROFILE_SAME_CHILD_RESULT }),
189+
id: "call_subtasks_xprofile_same_child_completion_002",
190+
},
191+
],
192+
},
193+
})
194+
195+
mock.addFixture({
196+
match: {
197+
predicate: (req: ChatCompletionRequest) =>
198+
requestContains(req, [SUBTASK_XPROFILE_PARENT_MARKER, SUBTASK_XPROFILE_SAME_CHILD_RESULT]) &&
199+
!requestContains(req, [SUBTASK_XPROFILE_DIFFERENT_CHILD_RESULT]),
200+
},
201+
response: {
202+
toolCalls: [
203+
{
204+
name: "new_task",
205+
arguments: JSON.stringify({
206+
mode: "ask",
207+
message: SUBTASK_XPROFILE_DIFFERENT_CHILD_PROMPT,
208+
}),
209+
id: "call_subtasks_xprofile_parent_different_child_003",
210+
},
211+
],
212+
},
213+
})
214+
215+
mock.addFixture({
216+
match: {
217+
userMessage: new RegExp(SUBTASK_XPROFILE_DIFFERENT_CHILD_MARKER),
218+
},
219+
response: {
220+
toolCalls: [
221+
{
222+
name: "attempt_completion",
223+
arguments: JSON.stringify({ result: SUBTASK_XPROFILE_DIFFERENT_CHILD_RESULT }),
224+
id: "call_subtasks_xprofile_different_child_completion_004",
225+
},
226+
],
227+
},
228+
})
229+
230+
mock.addFixture({
231+
match: {
232+
predicate: (req: ChatCompletionRequest) =>
233+
requestContains(req, [
234+
SUBTASK_XPROFILE_PARENT_MARKER,
235+
SUBTASK_XPROFILE_SAME_CHILD_RESULT,
236+
SUBTASK_XPROFILE_DIFFERENT_CHILD_RESULT,
237+
]),
238+
},
239+
response: {
240+
toolCalls: [
241+
{
242+
name: "attempt_completion",
243+
arguments: JSON.stringify({ result: SUBTASK_XPROFILE_PARENT_RESULT }),
244+
id: "call_subtasks_xprofile_parent_completion_005",
245+
},
246+
],
247+
},
248+
})
95249
}

0 commit comments

Comments
 (0)