mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Add multi-agent E2E test (DaemonClient Phase 4)
Adds test that verifies parent agent can create child agent via agent-control MCP. Test creates Codex parent, prompts it to call create_agent tool, and verifies both agents are visible. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -366,6 +366,119 @@ describe("daemon E2E", () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe("multi-agent orchestration", () => {
|
||||
test(
|
||||
"parent agent creates child agent via agent-control MCP",
|
||||
async () => {
|
||||
const cwd = tmpCwd();
|
||||
const childCwd = tmpCwd();
|
||||
|
||||
// Create parent Codex agent
|
||||
const parent = await ctx.client.createAgent({
|
||||
provider: "codex",
|
||||
cwd,
|
||||
title: "Parent Agent",
|
||||
});
|
||||
|
||||
expect(parent.id).toBeTruthy();
|
||||
expect(parent.status).toBe("idle");
|
||||
|
||||
// Clear message queue before sending prompt
|
||||
ctx.client.clearMessageQueue();
|
||||
|
||||
// Prompt the parent to create a child agent using agent-control MCP
|
||||
const prompt = [
|
||||
`Use the create_agent tool from the agent-control MCP server to create a new codex agent.`,
|
||||
`Set the cwd to: ${childCwd}`,
|
||||
`Set the title to: Child Agent`,
|
||||
`Set agentType to: codex`,
|
||||
`Do NOT set an initialPrompt - just create the agent.`,
|
||||
`After creating the agent, reply with "CREATED" followed by the child's agentId.`,
|
||||
].join(" ");
|
||||
|
||||
await ctx.client.sendMessage(parent.id, prompt);
|
||||
|
||||
// Wait for parent to complete
|
||||
const afterCreate = await ctx.client.waitForAgentIdle(
|
||||
parent.id,
|
||||
120000
|
||||
);
|
||||
expect(afterCreate.status).toBe("idle");
|
||||
|
||||
// Verify timeline contains a tool call to create_agent
|
||||
const queue = ctx.client.getMessageQueue();
|
||||
const timelineItems: AgentTimelineItem[] = [];
|
||||
for (const m of queue) {
|
||||
if (
|
||||
m.type === "agent_stream" &&
|
||||
m.payload.agentId === parent.id &&
|
||||
m.payload.event.type === "timeline"
|
||||
) {
|
||||
timelineItems.push(m.payload.event.item);
|
||||
}
|
||||
}
|
||||
|
||||
// Should have a tool call to create_agent from agent-control
|
||||
const hasCreateAgentCall = timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.tool === "create_agent" &&
|
||||
item.server === "agent-control"
|
||||
);
|
||||
expect(hasCreateAgentCall).toBe(true);
|
||||
|
||||
// Now verify we can see both agents via session_state
|
||||
// Send a list_persisted_agents_request to trigger session_state refresh
|
||||
// Or we can check the queue for agent_state messages
|
||||
const agentStateMessages = queue.filter(
|
||||
(m) => m.type === "agent_state"
|
||||
);
|
||||
|
||||
// Extract unique agent IDs from state messages
|
||||
const agentIds = new Set<string>();
|
||||
for (const m of agentStateMessages) {
|
||||
if (m.type === "agent_state") {
|
||||
agentIds.add(m.payload.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Should have at least 2 agents (parent + child)
|
||||
expect(agentIds.size).toBeGreaterThanOrEqual(2);
|
||||
expect(agentIds.has(parent.id)).toBe(true);
|
||||
|
||||
// Get the child agent ID from the tool call output
|
||||
const createAgentCall = timelineItems.find(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.tool === "create_agent" &&
|
||||
item.server === "agent-control"
|
||||
);
|
||||
|
||||
let childAgentId: string | null = null;
|
||||
if (
|
||||
createAgentCall &&
|
||||
createAgentCall.type === "tool_call" &&
|
||||
createAgentCall.output
|
||||
) {
|
||||
// The output contains the agentId
|
||||
const output = createAgentCall.output as { agentId?: string };
|
||||
if (output.agentId) {
|
||||
childAgentId = output.agentId;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify we found the child agent ID
|
||||
expect(childAgentId).toBeTruthy();
|
||||
expect(agentIds.has(childAgentId!)).toBe(true);
|
||||
|
||||
// Cleanup
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
rmSync(childCwd, { recursive: true, force: true });
|
||||
},
|
||||
300000 // 5 minute timeout for multi-agent E2E
|
||||
);
|
||||
});
|
||||
|
||||
// Claude permission tests are skipped due to SDK behavior:
|
||||
// - The sandbox config IS passed correctly to Claude SDK
|
||||
// - Claude executes tool calls without requesting permission
|
||||
|
||||
3
plan.md
3
plan.md
@@ -1032,7 +1032,7 @@ Build a new Codex MCP provider side‑by‑side with the existing Codex SDK prov
|
||||
- Persistence round-trip works via DaemonClient
|
||||
- **Done (2025-12-25 17:23)**: WHAT: Modified `daemon-client.ts:217-265` to fix `resumeAgent()` to properly wait for the new agent's idle state using `skipQueueBefore` option (not cached old agent messages). Added E2E test `daemon.e2e.test.ts:255-366` "persists and resumes Codex agent with conversation history" that creates agent, sends message, deletes, and resumes from persistence handle. RESULT: Persistence round-trip works - agent is deleted, resumed via persistence handle with conversationId, and responds to follow-up messages. EVIDENCE: `npm run test -- daemon.e2e.test.ts -t "persists and resumes"` passed (9.2s). Note: `listPersistedAgents()` and `resumeAgent()` methods already existed; the fix was to make `resumeAgent()` properly skip stale queue messages when waiting for the new agent.
|
||||
|
||||
- [ ] **Implement**: Multi-agent E2E test (Phase 4).
|
||||
- [x] **Implement**: Multi-agent E2E test (Phase 4).
|
||||
|
||||
**Add E2E test**:
|
||||
- `multi-agent: agent A launches agent B` - parent agent uses agent-control MCP to create child
|
||||
@@ -1040,6 +1040,7 @@ Build a new Codex MCP provider side‑by‑side with the existing Codex SDK prov
|
||||
**Acceptance criteria**:
|
||||
- Multi-agent orchestration works via DaemonClient
|
||||
- Both parent and child agents visible in listAgents()
|
||||
- **Done (2025-12-25 17:32)**: WHAT: Added `daemon.e2e.test.ts:369-483` describe block "multi-agent orchestration" with test "parent agent creates child agent via agent-control MCP". Test creates a parent Codex agent, prompts it to call `create_agent` tool via agent-control MCP, then verifies: (1) tool call to `create_agent` with server `agent-control` exists in timeline, (2) both parent and child agent IDs are visible via `agent_state` messages, (3) child agent ID from tool output matches tracked agents. RESULT: Multi-agent orchestration verified - parent agent successfully creates child agent using agent-control MCP, both agents tracked by daemon. EVIDENCE: `npm run test --workspace=@paseo/server -- daemon.e2e.test.ts` (5 passed, 2 skipped in 40.8s), `npm run typecheck --workspace=@paseo/server` (exit 0).
|
||||
|
||||
- [ ] **Review**: Audit daemon E2E test coverage.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user