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:
Lukas May
2026-01-31 18:02:54 +01:00
parent d012680dbe
commit a9e46a2843
2 changed files with 74 additions and 7 deletions

View File

@@ -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();
});
});
});

View File

@@ -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`);