feat: improve agent activity UI and fix status update propagation

**Agent Activity UI Improvements:**
- Create typed discriminated unions for SessionNotification updates
- Add AgentActivity component with specialized rendering for each update type
- Group consecutive text chunks into single messages (like main chat)
- Add collapsible tool call rendering with input/output details
- Add plan rendering with task status indicators
- Add fallback rendering for unknown activity types (badge + drawer)

**Agent Status Update Fix:**
- Fix agent killing not updating UI status
- Session now detects status change notifications and emits agent_status messages
- Agent manager delays agent deletion to allow status updates to propagate
- Frontend agent_status handler now receives proper updates

**Technical Details:**
- Created /packages/app/src/types/agent-activity.ts with typed session updates
- Created /packages/app/src/components/agent-activity.tsx with activity renderers
- Updated AgentStreamView to use grouped activities with useMemo
- Modified session.ts subscribeToAgent to emit agent_status on status changes
- Modified agent-manager.ts killAgent to delay deletion for status propagation

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
Mohamed Boudra
2025-10-22 18:10:29 +02:00
co-authored by Claude Happy
parent e1e14b1473
commit 674977d6f8
5 changed files with 776 additions and 181 deletions
@@ -349,6 +349,9 @@ export class AgentManager {
}
agent.status = "killed";
// Notify subscribers BEFORE removing from manager
// This ensures subscribers can still query agent info
this.notifySubscribers(agentId);
// Kill the process
@@ -361,8 +364,11 @@ export class AgentManager {
}
}, 5000);
// Remove from manager
this.agents.delete(agentId);
// Remove from manager after a small delay to allow status updates to propagate
setTimeout(() => {
this.agents.delete(agentId);
console.log(`[Agent ${agentId}] Removed from manager`);
}, 100);
}
/**
+35
View File
@@ -145,6 +145,41 @@ export class Session {
notification: update.notification,
},
});
// Check if this is a status change notification
// The agent manager sends custom notifications with status field
const notification = update.notification as any;
if (notification && notification.sessionUpdate && notification.sessionUpdate.status) {
const status = notification.sessionUpdate.status;
// Get current agent info
try {
const info = this.agentManager!.listAgents().find(a => a.id === agentId);
if (info) {
// Emit agent_status message
this.emit({
type: "agent_status",
payload: {
agentId,
status,
info: {
id: info.id,
status: info.status,
createdAt: info.createdAt.toISOString(),
type: info.type,
sessionId: info.sessionId,
error: info.error,
currentModeId: info.currentModeId,
availableModes: info.availableModes,
},
},
});
console.log(`[Session ${this.clientId}] Agent ${agentId} status changed to: ${status}`);
}
} catch (error) {
console.error(`[Session ${this.clientId}] Failed to get agent info for status update:`, error);
}
}
}
);