feat: add sub-agents section to agent info menu
Added a "Sub-Agents" section to the agent info menu dropdown that: - Queries session store for agents where parentAgentId matches current agent - Displays child agents as clickable menu items with chevron icons - Shows "No sub-agents" when the agent has no children - Navigates to child agent screen when tapped 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
11afd22858
commit
cdc17006da
@@ -14,7 +14,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|||||||
import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
|
import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
|
||||||
import ReanimatedAnimated, { useAnimatedStyle, useSharedValue } from "react-native-reanimated";
|
import ReanimatedAnimated, { useAnimatedStyle, useSharedValue } from "react-native-reanimated";
|
||||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
import { MoreVertical, GitBranch, Folder, RotateCcw, PlusCircle, Download } from "lucide-react-native";
|
import { MoreVertical, GitBranch, Folder, RotateCcw, PlusCircle, Download, Users, ChevronRight } from "lucide-react-native";
|
||||||
import { BackHeader } from "@/components/headers/back-header";
|
import { BackHeader } from "@/components/headers/back-header";
|
||||||
import { AgentStreamView } from "@/components/agent-stream-view";
|
import { AgentStreamView } from "@/components/agent-stream-view";
|
||||||
import { AgentInputArea } from "@/components/agent-input-area";
|
import { AgentInputArea } from "@/components/agent-input-area";
|
||||||
@@ -153,6 +153,20 @@ function AgentScreenContent({ serverId, agentId, onBack }: AgentScreenContentPro
|
|||||||
resolvedAgentId ? state.sessions[serverId]?.agents?.get(resolvedAgentId) : undefined
|
resolvedAgentId ? state.sessions[serverId]?.agents?.get(resolvedAgentId) : undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Select child agents (agents where parentAgentId === current agentId)
|
||||||
|
const childAgents = useSessionStore((state) => {
|
||||||
|
if (!resolvedAgentId) return [];
|
||||||
|
const agents = state.sessions[serverId]?.agents;
|
||||||
|
if (!agents) return [];
|
||||||
|
const children: Array<{ id: string; title: string | null }> = [];
|
||||||
|
for (const [id, a] of agents) {
|
||||||
|
if (a.parentAgentId === resolvedAgentId) {
|
||||||
|
children.push({ id, title: a.title });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
});
|
||||||
|
|
||||||
// Select only the specific stream state - use stable empty array to avoid infinite loop
|
// Select only the specific stream state - use stable empty array to avoid infinite loop
|
||||||
const streamItemsRaw = useSessionStore((state) =>
|
const streamItemsRaw = useSessionStore((state) =>
|
||||||
resolvedAgentId ? state.sessions[serverId]?.agentStreamState?.get(resolvedAgentId) : undefined
|
resolvedAgentId ? state.sessions[serverId]?.agentStreamState?.get(resolvedAgentId) : undefined
|
||||||
@@ -439,6 +453,20 @@ function AgentScreenContent({ serverId, agentId, onBack }: AgentScreenContentPro
|
|||||||
setShowImportAgentModal(false);
|
setShowImportAgentModal(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleNavigateToChildAgent = useCallback(
|
||||||
|
(childAgentId: string) => {
|
||||||
|
handleCloseMenu();
|
||||||
|
router.push({
|
||||||
|
pathname: "/agent/[serverId]/[agentId]",
|
||||||
|
params: {
|
||||||
|
serverId: serverId,
|
||||||
|
agentId: childAgentId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[handleCloseMenu, router, serverId]
|
||||||
|
);
|
||||||
|
|
||||||
const createAgentModal = (
|
const createAgentModal = (
|
||||||
<CreateAgentModal
|
<CreateAgentModal
|
||||||
isVisible={showCreateAgentModal}
|
isVisible={showCreateAgentModal}
|
||||||
@@ -585,6 +613,36 @@ function AgentScreenContent({ serverId, agentId, onBack }: AgentScreenContentPro
|
|||||||
|
|
||||||
<View style={styles.menuDivider} />
|
<View style={styles.menuDivider} />
|
||||||
|
|
||||||
|
{/* Sub-Agents Section */}
|
||||||
|
<View style={styles.menuSubAgentsSection}>
|
||||||
|
<View style={styles.menuSubAgentsHeader}>
|
||||||
|
<Users size={16} color={theme.colors.mutedForeground} />
|
||||||
|
<Text style={styles.menuSubAgentsLabel}>Sub-Agents</Text>
|
||||||
|
</View>
|
||||||
|
{childAgents.length === 0 ? (
|
||||||
|
<Text style={styles.menuSubAgentsEmpty}>No sub-agents</Text>
|
||||||
|
) : (
|
||||||
|
childAgents.map((child) => (
|
||||||
|
<Pressable
|
||||||
|
key={child.id}
|
||||||
|
onPress={() => handleNavigateToChildAgent(child.id)}
|
||||||
|
style={styles.menuSubAgentItem}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={styles.menuSubAgentText}
|
||||||
|
numberOfLines={1}
|
||||||
|
ellipsizeMode="tail"
|
||||||
|
>
|
||||||
|
{child.title || "Untitled Agent"}
|
||||||
|
</Text>
|
||||||
|
<ChevronRight size={16} color={theme.colors.mutedForeground} />
|
||||||
|
</Pressable>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.menuDivider} />
|
||||||
|
|
||||||
<Pressable onPress={handleViewChanges} style={styles.menuItem}>
|
<Pressable onPress={handleViewChanges} style={styles.menuItem}>
|
||||||
<GitBranch size={20} color={theme.colors.foreground} />
|
<GitBranch size={20} color={theme.colors.foreground} />
|
||||||
<Text style={styles.menuItemText}>View Changes</Text>
|
<Text style={styles.menuItemText}>View Changes</Text>
|
||||||
@@ -830,4 +888,40 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
color: theme.colors.foreground,
|
color: theme.colors.foreground,
|
||||||
fontWeight: theme.fontWeight.normal,
|
fontWeight: theme.fontWeight.normal,
|
||||||
},
|
},
|
||||||
|
menuSubAgentsSection: {
|
||||||
|
gap: theme.spacing[1],
|
||||||
|
},
|
||||||
|
menuSubAgentsHeader: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: theme.spacing[2],
|
||||||
|
paddingHorizontal: theme.spacing[1],
|
||||||
|
paddingBottom: theme.spacing[1],
|
||||||
|
},
|
||||||
|
menuSubAgentsLabel: {
|
||||||
|
fontSize: theme.fontSize.xs,
|
||||||
|
color: theme.colors.mutedForeground,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
},
|
||||||
|
menuSubAgentsEmpty: {
|
||||||
|
fontSize: theme.fontSize.sm,
|
||||||
|
color: theme.colors.mutedForeground,
|
||||||
|
paddingHorizontal: theme.spacing[3],
|
||||||
|
paddingVertical: theme.spacing[2],
|
||||||
|
},
|
||||||
|
menuSubAgentItem: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingVertical: theme.spacing[2],
|
||||||
|
paddingHorizontal: theme.spacing[3],
|
||||||
|
borderRadius: theme.borderRadius.md,
|
||||||
|
},
|
||||||
|
menuSubAgentText: {
|
||||||
|
fontSize: theme.fontSize.sm,
|
||||||
|
color: theme.colors.foreground,
|
||||||
|
flex: 1,
|
||||||
|
marginRight: theme.spacing[2],
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ Files requiring modification:
|
|||||||
- Run typecheck after changes.
|
- Run typecheck after changes.
|
||||||
- **Done (2025-12-21 21:45)**: Added filter in `use-aggregated-agents.ts:66-68` to skip agents with `parentAgentId` when building the aggregated agents list. Also included `parentAgentId` in the aggregated agent object. Typecheck passes.
|
- **Done (2025-12-21 21:45)**: Added filter in `use-aggregated-agents.ts:66-68` to skip agents with `parentAgentId` when building the aggregated agents list. Also included `parentAgentId` in the aggregated agent object. Typecheck passes.
|
||||||
|
|
||||||
- [ ] **Implement**: Add sub-agents section to agent info menu.
|
- [x] **Implement**: Add sub-agents section to agent info menu.
|
||||||
|
|
||||||
- In `[agentId].tsx:536`, add a "Sub-Agents" section to the menu
|
- In `[agentId].tsx:536`, add a "Sub-Agents" section to the menu
|
||||||
- Query the session store for agents where `parentAgentId === currentAgentId`
|
- Query the session store for agents where `parentAgentId === currentAgentId`
|
||||||
@@ -332,6 +332,7 @@ Files requiring modification:
|
|||||||
- Tapping a child agent navigates to that agent's screen
|
- Tapping a child agent navigates to that agent's screen
|
||||||
- Show "No sub-agents" if none exist
|
- Show "No sub-agents" if none exist
|
||||||
- Run typecheck after changes.
|
- Run typecheck after changes.
|
||||||
|
- **Done (2025-12-21 22:00)**: Added Sub-Agents section to agent info menu in `[agentId].tsx`. Added `childAgents` selector to query agents with matching `parentAgentId`. Added `handleNavigateToChildAgent` callback for navigation. UI shows "No sub-agents" when empty, or clickable list of child agents with chevron icons. Typecheck passes.
|
||||||
|
|
||||||
- [ ] **Test**: Verify parent/child hierarchy end-to-end.
|
- [ ] **Test**: Verify parent/child hierarchy end-to-end.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user