feat(10-02): update MockAgentManager for batched answers
- Change resume() signature from (agentId, prompt) to (agentId, answers) - Accept Record<string, string> mapping question IDs to user answers - Clear pendingQuestions on resume - Update mock-manager tests for new signature
This commit is contained in:
@@ -262,8 +262,8 @@ describe('MockAgentManager', () => {
|
|||||||
const waitingAgent = await manager.get(agent.id);
|
const waitingAgent = await manager.get(agent.id);
|
||||||
expect(waitingAgent?.status).toBe('waiting_for_input');
|
expect(waitingAgent?.status).toBe('waiting_for_input');
|
||||||
|
|
||||||
// Resume the agent
|
// Resume the agent with answers map
|
||||||
await manager.resume(agent.id, 'Continue with this input');
|
await manager.resume(agent.id, { q1: 'Continue with this input' });
|
||||||
|
|
||||||
// Check agent:resumed event emitted
|
// Check agent:resumed event emitted
|
||||||
const resumedEvent = eventBus.emittedEvents.find((e) => e.type === 'agent:resumed');
|
const resumedEvent = eventBus.emittedEvents.find((e) => e.type === 'agent:resumed');
|
||||||
@@ -292,13 +292,13 @@ describe('MockAgentManager', () => {
|
|||||||
prompt: 'Work',
|
prompt: 'Work',
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(manager.resume(agent.id, 'input')).rejects.toThrow(
|
await expect(manager.resume(agent.id, { q1: 'input' })).rejects.toThrow(
|
||||||
'is not waiting for input'
|
'is not waiting for input'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if agent not found', async () => {
|
it('should throw if agent not found', async () => {
|
||||||
await expect(manager.resume('non-existent-id', 'input')).rejects.toThrow(
|
await expect(manager.resume('non-existent-id', { q1: 'input' })).rejects.toThrow(
|
||||||
'not found'
|
'not found'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -668,8 +668,8 @@ describe('MockAgentManager', () => {
|
|||||||
expect(pendingBefore).not.toBeNull();
|
expect(pendingBefore).not.toBeNull();
|
||||||
expect(pendingBefore?.questions[0].question).toBe('Need your input');
|
expect(pendingBefore?.questions[0].question).toBe('Need your input');
|
||||||
|
|
||||||
// Resume the agent
|
// Resume the agent with answers map
|
||||||
await manager.resume(agent.id, 'Option A');
|
await manager.resume(agent.id, { q1: 'Option A' });
|
||||||
|
|
||||||
// Pending questions should be cleared
|
// Pending questions should be cleared
|
||||||
const pendingAfter = await manager.getPendingQuestions(agent.id);
|
const pendingAfter = await manager.getPendingQuestions(agent.id);
|
||||||
@@ -688,5 +688,69 @@ describe('MockAgentManager', () => {
|
|||||||
const pending = await manager.getPendingQuestions(agent.id);
|
const pending = await manager.getPendingQuestions(agent.id);
|
||||||
expect(pending).toBeNull();
|
expect(pending).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles multiple questions in single scenario', async () => {
|
||||||
|
manager.setScenario('multi-q-agent', {
|
||||||
|
status: 'questions',
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
id: 'q1',
|
||||||
|
question: 'Which database should we use?',
|
||||||
|
options: [
|
||||||
|
{ label: 'PostgreSQL', description: 'Full-featured relational DB' },
|
||||||
|
{ label: 'SQLite', description: 'Lightweight embedded DB' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'q2',
|
||||||
|
question: 'Which ORM do you prefer?',
|
||||||
|
options: [
|
||||||
|
{ label: 'Drizzle', description: 'TypeScript-first ORM' },
|
||||||
|
{ label: 'Prisma', description: 'Popular Node.js ORM' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'q3',
|
||||||
|
question: 'Any additional notes?',
|
||||||
|
// No options - free-form text question
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const agent = await manager.spawn({ name: 'multi-q-agent', taskId: 'task-1', prompt: 'test' });
|
||||||
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
|
// Check status
|
||||||
|
const updated = await manager.get(agent.id);
|
||||||
|
expect(updated?.status).toBe('waiting_for_input');
|
||||||
|
|
||||||
|
// Check event has all questions
|
||||||
|
const waitingEvent = eventBus.emittedEvents.find((e) => e.type === 'agent:waiting');
|
||||||
|
expect(waitingEvent).toBeDefined();
|
||||||
|
expect((waitingEvent as any).payload.questions).toHaveLength(3);
|
||||||
|
expect((waitingEvent as any).payload.questions[0].id).toBe('q1');
|
||||||
|
expect((waitingEvent as any).payload.questions[1].id).toBe('q2');
|
||||||
|
expect((waitingEvent as any).payload.questions[2].id).toBe('q3');
|
||||||
|
|
||||||
|
// Check pending questions retrieval
|
||||||
|
const pending = await manager.getPendingQuestions(agent.id);
|
||||||
|
expect(pending?.questions).toHaveLength(3);
|
||||||
|
expect(pending?.questions[0].question).toBe('Which database should we use?');
|
||||||
|
expect(pending?.questions[1].question).toBe('Which ORM do you prefer?');
|
||||||
|
expect(pending?.questions[2].question).toBe('Any additional notes?');
|
||||||
|
expect(pending?.questions[2].options).toBeUndefined();
|
||||||
|
|
||||||
|
// Resume with answers to all questions
|
||||||
|
await manager.resume(agent.id, { q1: 'PostgreSQL', q2: 'Drizzle', q3: 'Use WAL mode' });
|
||||||
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
|
// Agent should complete
|
||||||
|
const completed = await manager.get(agent.id);
|
||||||
|
expect(completed?.status).toBe('idle');
|
||||||
|
|
||||||
|
// Pending questions should be cleared
|
||||||
|
const clearedPending = await manager.getPendingQuestions(agent.id);
|
||||||
|
expect(clearedPending).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -330,8 +330,11 @@ export class MockAgentManager implements AgentManager {
|
|||||||
*
|
*
|
||||||
* Re-runs the scenario for the resumed agent. Emits agent:resumed event.
|
* Re-runs the scenario for the resumed agent. Emits agent:resumed event.
|
||||||
* Agent must be in 'waiting_for_input' status.
|
* Agent must be in 'waiting_for_input' status.
|
||||||
|
*
|
||||||
|
* @param agentId - Agent to resume
|
||||||
|
* @param answers - Map of question ID to user's answer
|
||||||
*/
|
*/
|
||||||
async resume(agentId: string, prompt: string): Promise<void> {
|
async resume(agentId: string, answers: Record<string, string>): Promise<void> {
|
||||||
const record = this.agents.get(agentId);
|
const record = this.agents.get(agentId);
|
||||||
if (!record) {
|
if (!record) {
|
||||||
throw new Error(`Agent '${agentId}' not found`);
|
throw new Error(`Agent '${agentId}' not found`);
|
||||||
|
|||||||
Reference in New Issue
Block a user