Fix subagent yield abort cleanup
(cherry picked from commit e22673537f7cc95920cd4ec75d2a4b36d81d7626)
This commit is contained in:
@@ -43,7 +43,7 @@ function createMockSession(
|
||||
promptIndex: number;
|
||||
emit: (event: AgentSessionEvent) => void;
|
||||
state: { messages: AssistantMessage[] };
|
||||
}) => void,
|
||||
}) => void | Promise<void>,
|
||||
): AgentSession {
|
||||
const listeners: Array<(event: AgentSessionEvent) => void> = [];
|
||||
const state = { messages: [] as AssistantMessage[] };
|
||||
@@ -72,7 +72,7 @@ function createMockSession(
|
||||
},
|
||||
prompt: async (text: string, options?: PromptOptions) => {
|
||||
promptIndex += 1;
|
||||
onPrompt({ text, options, promptIndex, emit, state });
|
||||
await onPrompt({ text, options, promptIndex, emit, state });
|
||||
},
|
||||
waitForIdle: async () => {},
|
||||
getLastAssistantMessage: () => state.messages[state.messages.length - 1],
|
||||
@@ -341,6 +341,66 @@ describe("runSubprocess yield reminders", () => {
|
||||
expect(result.output).toContain('"ok": true');
|
||||
});
|
||||
|
||||
it("waits for yield-triggered abort cleanup before resolving the subagent", async () => {
|
||||
const promptCleanup = Promise.withResolvers<void>();
|
||||
const abortCleanup = Promise.withResolvers<void>();
|
||||
const validYieldEmitted = Promise.withResolvers<void>();
|
||||
let abortCalls = 0;
|
||||
const session = createMockSession(async ({ promptIndex, emit, state }) => {
|
||||
if (promptIndex === 1) {
|
||||
const assistant = createAssistantStopMessage("malformed yield attempt");
|
||||
state.messages.push(assistant);
|
||||
emit({ type: "message_end", message: assistant });
|
||||
emit({
|
||||
type: "tool_execution_end",
|
||||
toolCallId: "tool-malformed",
|
||||
toolName: "yield",
|
||||
result: {
|
||||
content: [{ type: "text", text: "result must be an object containing either data or error" }],
|
||||
details: { status: "error", error: "result must be an object containing either data or error" },
|
||||
},
|
||||
isError: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
emit({
|
||||
type: "tool_execution_end",
|
||||
toolCallId: "tool-success-after-malformed",
|
||||
toolName: "yield",
|
||||
result: {
|
||||
content: [{ type: "text", text: "Result submitted." }],
|
||||
details: { status: "success", data: { ok: true } },
|
||||
},
|
||||
isError: false,
|
||||
});
|
||||
validYieldEmitted.resolve();
|
||||
await promptCleanup.promise;
|
||||
});
|
||||
(session as unknown as { abort: () => Promise<void> }).abort = async () => {
|
||||
abortCalls += 1;
|
||||
promptCleanup.resolve();
|
||||
await abortCleanup.promise;
|
||||
};
|
||||
|
||||
mockCreateAgentSession(session);
|
||||
|
||||
let settled = false;
|
||||
const resultPromise = runSubprocess({ ...baseOptions, id: "subagent-yield-abort-cleanup" }).finally(() => {
|
||||
settled = true;
|
||||
});
|
||||
|
||||
await validYieldEmitted.promise;
|
||||
await Bun.sleep(20);
|
||||
expect(abortCalls).toBe(1);
|
||||
expect(settled).toBe(false);
|
||||
|
||||
abortCleanup.resolve();
|
||||
const result = await resultPromise;
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.output).toContain('"ok": true');
|
||||
});
|
||||
|
||||
it("keeps a real run failure from being masked by a successful yield", () => {
|
||||
const result = finalizeSubprocessOutput({
|
||||
rawOutput: "partial output",
|
||||
|
||||
Reference in New Issue
Block a user