feat: Auto-resume idle agents for inter-agent conversations

When an agent asks a question via `cw ask` targeting an idle agent,
the conversation router now auto-resumes the idle agent's session so
it can answer. Previously, questions to idle agents sat unanswered
forever because target resolution only matched running agents.

Changes:
- Add `resumeForConversation()` to AgentManager interface and implement
  on MultiProviderAgentManager (mirrors resumeForCommit pattern)
- Relax createConversation target resolution: prefer running, fall back
  to idle (was running-only)
- Trigger auto-resume after conversation creation for idle targets
- Add concurrency lock (conversationResumeLocks Set) to prevent
  double-resume race conditions
This commit is contained in:
Lukas May
2026-03-03 13:29:39 +01:00
parent 938700d45d
commit 9edc93a268
7 changed files with 116 additions and 7 deletions

View File

@@ -471,6 +471,42 @@ export class MockAgentManager implements AgentManager {
record.info.updatedAt = now;
}
/**
* Resume an idle agent to answer an inter-agent conversation.
* Mock implementation: marks agent as running and schedules immediate completion.
*/
async resumeForConversation(
agentId: string,
conversationId: string,
question: string,
fromAgentId: string,
): Promise<boolean> {
const record = this.agents.get(agentId);
if (!record || record.info.status !== 'idle' || !record.info.sessionId) {
return false;
}
record.info.status = 'running';
record.info.updatedAt = new Date();
if (this.eventBus) {
const event: AgentResumedEvent = {
type: 'agent:resumed',
timestamp: new Date(),
payload: {
agentId,
name: record.info.name,
taskId: record.info.taskId,
sessionId: record.info.sessionId,
},
};
this.eventBus.emit(event);
}
this.scheduleCompletion(agentId, { status: 'done', delay: 0, result: 'Answered conversation' });
return true;
}
/**
* Clear all agents and pending timers.
* Useful for test cleanup.

View File

@@ -237,4 +237,24 @@ export interface AgentManager {
* @param agentId - Agent to dismiss
*/
dismiss(agentId: string): Promise<void>;
/**
* Resume an idle agent to answer an inter-agent conversation.
*
* When Agent A asks Agent B a question via `cw ask` and Agent B is idle,
* this resumes B's session with a prompt to answer via `cw answer` and
* drain any remaining pending conversations via `cw listen`.
*
* @param agentId - The idle agent to resume
* @param conversationId - The conversation that triggered the resume
* @param question - The question being asked
* @param fromAgentId - The agent asking the question
* @returns true if resume was initiated, false if not possible
*/
resumeForConversation(
agentId: string,
conversationId: string,
question: string,
fromAgentId: string,
): Promise<boolean>;
}