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);
|
||||
expect(waitingAgent?.status).toBe('waiting_for_input');
|
||||
|
||||
// Resume the agent
|
||||
await manager.resume(agent.id, 'Continue with this input');
|
||||
// Resume the agent with answers map
|
||||
await manager.resume(agent.id, { q1: 'Continue with this input' });
|
||||
|
||||
// Check agent:resumed event emitted
|
||||
const resumedEvent = eventBus.emittedEvents.find((e) => e.type === 'agent:resumed');
|
||||
@@ -292,13 +292,13 @@ describe('MockAgentManager', () => {
|
||||
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'
|
||||
);
|
||||
});
|
||||
|
||||
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'
|
||||
);
|
||||
});
|
||||
@@ -668,8 +668,8 @@ describe('MockAgentManager', () => {
|
||||
expect(pendingBefore).not.toBeNull();
|
||||
expect(pendingBefore?.questions[0].question).toBe('Need your input');
|
||||
|
||||
// Resume the agent
|
||||
await manager.resume(agent.id, 'Option A');
|
||||
// Resume the agent with answers map
|
||||
await manager.resume(agent.id, { q1: 'Option A' });
|
||||
|
||||
// Pending questions should be cleared
|
||||
const pendingAfter = await manager.getPendingQuestions(agent.id);
|
||||
@@ -688,5 +688,69 @@ describe('MockAgentManager', () => {
|
||||
const pending = await manager.getPendingQuestions(agent.id);
|
||||
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.
|
||||
* 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);
|
||||
if (!record) {
|
||||
throw new Error(`Agent '${agentId}' not found`);
|
||||
|
||||
Reference in New Issue
Block a user