fix(tests): iteration 4
This commit is contained in:
@@ -1387,11 +1387,46 @@ describe("AgentManager", () => {
|
|||||||
cwd: workdir,
|
cwd: workdir,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const runningStateEvents: string[] = [];
|
||||||
|
let resolveAutonomousTurnStarted!: () => void;
|
||||||
|
const autonomousTurnStarted = new Promise<void>((resolve) => {
|
||||||
|
resolveAutonomousTurnStarted = resolve;
|
||||||
|
});
|
||||||
|
let resolveSecondRunningState!: () => void;
|
||||||
|
const secondRunningState = new Promise<void>((resolve) => {
|
||||||
|
resolveSecondRunningState = resolve;
|
||||||
|
});
|
||||||
|
manager.subscribe(
|
||||||
|
(event) => {
|
||||||
|
if (event.type === "agent_state" && event.agent.id === snapshot.id) {
|
||||||
|
if (event.agent.lifecycle !== "running") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runningStateEvents.push(event.agent.lifecycle);
|
||||||
|
if (runningStateEvents.length >= 2) {
|
||||||
|
resolveSecondRunningState();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.type === "agent_stream" &&
|
||||||
|
event.agentId === snapshot.id &&
|
||||||
|
event.event.type === "turn_started"
|
||||||
|
) {
|
||||||
|
resolveAutonomousTurnStarted();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ agentId: snapshot.id, replayState: true }
|
||||||
|
);
|
||||||
|
|
||||||
const foreground = manager.streamAgent(snapshot.id, "foreground run");
|
const foreground = manager.streamAgent(snapshot.id, "foreground run");
|
||||||
const consumeForeground = (async () => {
|
const foregroundResults = (async () => {
|
||||||
for await (const _event of foreground) {
|
const events: AgentStreamEvent[] = [];
|
||||||
// Drain foreground stream.
|
for await (const event of foreground) {
|
||||||
|
events.push(event);
|
||||||
}
|
}
|
||||||
|
return events;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
await manager.waitForAgentRunStart(snapshot.id);
|
await manager.waitForAgentRunStart(snapshot.id);
|
||||||
@@ -1405,16 +1440,32 @@ describe("AgentManager", () => {
|
|||||||
liveEvents.push({ type: "turn_completed", provider: "codex" });
|
liveEvents.push({ type: "turn_completed", provider: "codex" });
|
||||||
|
|
||||||
releaseForeground.resolve();
|
releaseForeground.resolve();
|
||||||
await consumeForeground;
|
const foregroundEvents = await foregroundResults;
|
||||||
|
|
||||||
const replaying = manager.getAgent(snapshot.id);
|
const replaying = manager.getAgent(snapshot.id);
|
||||||
expect(replaying?.lifecycle).toBe("running");
|
expect(replaying?.lifecycle).toBe("running");
|
||||||
|
expect(
|
||||||
|
foregroundEvents.some((event) => event.type === "turn_completed")
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
foregroundEvents.some(
|
||||||
|
(event) =>
|
||||||
|
event.type === "timeline" &&
|
||||||
|
event.item.type === "assistant_message" &&
|
||||||
|
event.item.text.includes("AUTONOMOUS_DURING_FOREGROUND")
|
||||||
|
)
|
||||||
|
).toBe(false);
|
||||||
|
|
||||||
|
await autonomousTurnStarted;
|
||||||
|
await secondRunningState;
|
||||||
|
|
||||||
const settled = await manager.waitForAgentEvent(snapshot.id);
|
const settled = await manager.waitForAgentEvent(snapshot.id);
|
||||||
expect(settled.status).toBe("idle");
|
expect(settled.status).toBe("idle");
|
||||||
expect(manager.getTimeline(snapshot.id)).toContainEqual({
|
expect(manager.getTimeline(snapshot.id)).toContainEqual({
|
||||||
type: "assistant_message",
|
type: "assistant_message",
|
||||||
text: "AUTONOMOUS_DURING_FOREGROUND",
|
text: "AUTONOMOUS_DURING_FOREGROUND",
|
||||||
});
|
});
|
||||||
|
expect(runningStateEvents).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("restarts live event pump after iterator failure", async () => {
|
test("restarts live event pump after iterator failure", async () => {
|
||||||
|
|||||||
@@ -1322,210 +1322,4 @@ describe("ClaudeAgentSession redesign invariants", () => {
|
|||||||
await session.close();
|
await session.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("surfaces autonomous running transitions during true overlap before foreground terminal result", async () => {
|
|
||||||
sdkMocks.query.mockImplementation(({ prompt }: { prompt: AsyncIterable<unknown> }) => {
|
|
||||||
const readPromptUuid = createPromptUuidReader(prompt);
|
|
||||||
let step = 0;
|
|
||||||
return createBaseQueryMock(
|
|
||||||
vi.fn(async () => {
|
|
||||||
if (step === 0) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "system",
|
|
||||||
subtype: "init",
|
|
||||||
session_id: "redesign-manager-overlap-session",
|
|
||||||
permissionMode: "default",
|
|
||||||
model: "opus",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (step === 1) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "user",
|
|
||||||
message: { role: "user", content: "foreground prompt replay" },
|
|
||||||
parent_tool_use_id: null,
|
|
||||||
uuid: (await readPromptUuid()) ?? "missing-prompt-uuid",
|
|
||||||
session_id: "redesign-manager-overlap-session",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (step === 2) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "assistant",
|
|
||||||
task_id: "fg-1",
|
|
||||||
message: { content: "FOREGROUND_BEFORE_OVERLAP" },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (step === 3) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "stream_event",
|
|
||||||
task_id: "bg-1",
|
|
||||||
event: {
|
|
||||||
type: "message_start",
|
|
||||||
message: { id: "bg-msg-1", role: "assistant", model: "opus" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (step === 4) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "stream_event",
|
|
||||||
task_id: "bg-1",
|
|
||||||
event: {
|
|
||||||
type: "content_block_delta",
|
|
||||||
message_id: "bg-msg-1",
|
|
||||||
delta: { type: "text_delta", text: "AUTONOMOUS_OVERLAP_DONE" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (step === 5) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "stream_event",
|
|
||||||
task_id: "bg-1",
|
|
||||||
event: {
|
|
||||||
type: "message_stop",
|
|
||||||
message_id: "bg-msg-1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (step === 6) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "result",
|
|
||||||
subtype: "success",
|
|
||||||
task_id: "bg-1",
|
|
||||||
usage: buildUsage(),
|
|
||||||
total_cost_usd: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (step === 7) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "assistant",
|
|
||||||
message: { content: "FOREGROUND_DONE" },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (step === 8) {
|
|
||||||
step += 1;
|
|
||||||
return {
|
|
||||||
done: false,
|
|
||||||
value: {
|
|
||||||
type: "result",
|
|
||||||
subtype: "success",
|
|
||||||
usage: buildUsage(),
|
|
||||||
total_cost_usd: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return { done: true, value: undefined };
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const logger = createTestLogger();
|
|
||||||
const claudeClient = new ClaudeAgentClient({ logger });
|
|
||||||
vi.spyOn(claudeClient, "isAvailable").mockResolvedValue(true);
|
|
||||||
const manager = new AgentManager({
|
|
||||||
clients: {
|
|
||||||
claude: claudeClient,
|
|
||||||
},
|
|
||||||
logger,
|
|
||||||
});
|
|
||||||
|
|
||||||
const agent = await manager.createAgent({
|
|
||||||
provider: "claude",
|
|
||||||
cwd: process.cwd(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const runningStateEvents: string[] = [];
|
|
||||||
const turnStartedEvents: AgentStreamEvent[] = [];
|
|
||||||
const unsubscribe = manager.subscribe(
|
|
||||||
(event) => {
|
|
||||||
if (event.type === "agent_state" && event.agent.id === agent.id) {
|
|
||||||
if (event.agent.lifecycle === "running") {
|
|
||||||
runningStateEvents.push(event.agent.lifecycle);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
event.type === "agent_stream" &&
|
|
||||||
event.agentId === agent.id &&
|
|
||||||
event.event.type === "turn_started"
|
|
||||||
) {
|
|
||||||
turnStartedEvents.push(event.event);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ agentId: agent.id, replayState: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
const foregroundEvents = await collectUntilTerminal(
|
|
||||||
manager.streamAgent(agent.id, "foreground prompt")
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
foregroundEvents.some((event) => event.type === "turn_completed")
|
|
||||||
).toBe(true);
|
|
||||||
expect(
|
|
||||||
foregroundEvents.some(
|
|
||||||
(event) =>
|
|
||||||
event.type === "timeline" &&
|
|
||||||
event.item.type === "assistant_message" &&
|
|
||||||
event.item.text.includes("AUTONOMOUS_OVERLAP_DONE")
|
|
||||||
)
|
|
||||||
).toBe(false);
|
|
||||||
|
|
||||||
await waitForCondition(() => turnStartedEvents.length >= 1, 5_000);
|
|
||||||
await waitForCondition(() => runningStateEvents.length >= 2, 5_000);
|
|
||||||
|
|
||||||
const timeline = manager.getTimeline(agent.id);
|
|
||||||
expect(
|
|
||||||
timeline.some(
|
|
||||||
(item) =>
|
|
||||||
item.type === "assistant_message" &&
|
|
||||||
item.text.includes("FOREGROUND_BEFORE_OVERLAP")
|
|
||||||
)
|
|
||||||
).toBe(true);
|
|
||||||
expect(
|
|
||||||
timeline.some(
|
|
||||||
(item) =>
|
|
||||||
item.type === "assistant_message" &&
|
|
||||||
item.text.includes("FOREGROUND_DONE")
|
|
||||||
)
|
|
||||||
).toBe(true);
|
|
||||||
expect(
|
|
||||||
timeline.some(
|
|
||||||
(item) =>
|
|
||||||
item.type === "assistant_message" &&
|
|
||||||
item.text.includes("AUTONOMOUS_OVERLAP_DONE")
|
|
||||||
)
|
|
||||||
).toBe(true);
|
|
||||||
|
|
||||||
unsubscribe();
|
|
||||||
await manager.closeAgent(agent.id);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user