-
-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Bug Description
The FocusedAgent panel progress bar never advances from 0%. The [0%] display and empty progress bar remain static throughout the entire session regardless of tool activity.
Root Cause
AgentId mismatch between the primary agent registration and TOOL_INVOKED events.
How agents are registered
In response-event-extractor.ts, extractFromParseMode() creates the primary agent:
// agentId = "primary:solution-architect"
events.push({
event: TUI_EVENTS.AGENT_ACTIVATED,
payload: {
agentId: `primary:${delegateName}`, // ← e.g. "primary:solution-architect"
name: delegateName,
role: 'primary',
isPrimary: true,
},
});This agent is stored in state.agents Map with key "primary:solution-architect".
How TOOL_INVOKED fires
In tui-interceptor.ts, every tool call emits TOOL_INVOKED with agentId from parseAgentFromToolName():
// parse-agent.ts returns agentId like "search_rules", "context", "plan-mode"
this.eventBus.emit(TUI_EVENTS.TOOL_INVOKED, {
toolName,
agentId: agentInfo?.agentId ?? null, // ← e.g. "search_rules", "context", null
timestamp: Date.now(),
});How progress should increment
In use-dashboard-state.ts, the TOOL_INVOKED handler:
case 'TOOL_INVOKED': {
const invokedAgentId = action.payload.agentId;
if (invokedAgentId) {
const agent = state.agents.get(invokedAgentId); // ← LOOKUP FAILS
// "search_rules" !== "primary:solution-architect"
if (agent && agent.status === 'running') {
agents.set(invokedAgentId, {
...agent,
progress: Math.min(95, agent.progress + 5), // ← NEVER REACHED
});
}
}
}The lookup state.agents.get("search_rules") always returns undefined because the primary agent is stored under key "primary:solution-architect". Progress never increments.
Proposed Fix
Add a fallback to focusedAgentId when direct agentId lookup fails:
use-dashboard-state.ts — TOOL_INVOKED handler
case 'TOOL_INVOKED': {
const entry: EventLogEntry = { /* ... unchanged ... */ };
const base = state.eventLog.length >= EVENT_LOG_MAX
? state.eventLog.slice(1) : state.eventLog;
// Progress increment logic with fallback
const invokedAgentId = action.payload.agentId;
let agents = state.agents;
// 1st: Try exact agentId match
let targetAgent = invokedAgentId
? state.agents.get(invokedAgentId) ?? null
: null;
// 2nd: Fallback to focused agent when no exact match
if (!targetAgent && state.focusedAgentId) {
targetAgent = state.agents.get(state.focusedAgentId) ?? null;
}
if (targetAgent && targetAgent.status === 'running') {
agents = cloneAgents(state.agents);
agents.set(targetAgent.id, {
...targetAgent,
progress: Math.min(95, targetAgent.progress + 5),
});
}
const toolCall: ToolCallRecord = { /* ... unchanged ... */ };
const toolCallsBase = state.toolCalls.length >= TOOL_CALLS_MAX
? state.toolCalls.slice(1) : state.toolCalls;
return {
...state,
agents,
eventLog: [...base, entry],
toolCalls: [...toolCallsBase, toolCall],
};
}Why this works
focusedAgentIdis always set to the primary agent afterAGENT_ACTIVATED(viaselectFocusedAgent())- The primary agent is always
'running'during the session - Each tool call increments progress by 5 (capped at 95)
- When
AGENT_DEACTIVATEDfires withreason !== 'error', progress jumps to 100
Alternative considered: Map tool-level IDs to primary agent
This was rejected because it would require maintaining a mapping between short-lived tool-agent IDs (from parseAgentFromToolName) and the primary agent ID. The fallback approach is simpler and covers the common case.
Files to Modify
| File | Change |
|---|---|
apps/mcp-server/src/tui/hooks/use-dashboard-state.ts |
Add focusedAgentId fallback in TOOL_INVOKED |
apps/mcp-server/src/tui/hooks/use-dashboard-state.spec.ts |
Add test for fallback progress increment |
Test Plan
yarn workspace codingbuddy test -- --testPathPattern="use-dashboard-state"- Fallback progress: Dispatch
AGENT_ACTIVATED(primary, id="primary:arch"), thenTOOL_INVOKED(agentId="search_rules") → primary agent progress should be 5 (not 0) - Exact match still works: Dispatch
AGENT_ACTIVATED(id="search_rules"), thenTOOL_INVOKED(agentId="search_rules") → exact match agent progress = 5 - Cap at 95: Dispatch 20×
TOOL_INVOKED→ progress capped at 95 (not 100) - No fallback when no focused agent:
focusedAgentId=null+ unmatched TOOL_INVOKED → no crash, agents unchanged - Only running agents: Primary agent with
status='done'→ progress not incremented
Acceptance Criteria
- Progress bar advances with each tool call during active session
- Progress increments by 5 per tool call, capped at 95
- Reaches 100 only on AGENT_DEACTIVATED (completed)
- Exact agentId match takes priority over fallback
- No regression on existing TOOL_INVOKED behavior
- All tests pass