Update files

This commit is contained in:
Mohamed Boudra
2026-01-28 15:25:12 +07:00
parent 96b211a2cf
commit f7935f9cc1
23 changed files with 1336 additions and 856 deletions
@@ -381,6 +381,7 @@ const styles = StyleSheet.create((theme) => ({
},
sectionHeader: {
paddingVertical: theme.spacing[2],
paddingHorizontal: theme.spacing[3],
marginTop: theme.spacing[2],
},
sectionTitle: {
@@ -62,8 +62,7 @@ export function AgentStatusBar({ agentId, serverId }: AgentStatusBarProps) {
</DropdownMenuTrigger>
<DropdownMenuContent
side="top"
align="center"
fullWidth
align="start"
testID="agent-mode-menu"
>
<DropdownMenuLabel>Mode</DropdownMenuLabel>
@@ -17,7 +17,7 @@ import { router, usePathname } from "expo-router";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { type GestureType } from "react-native-gesture-handler";
import { useQueries, useQueryClient, type UseQueryOptions } from "@tanstack/react-query";
import { ChevronDown, ChevronRight, Plus } from "lucide-react-native";
import { Archive, ChevronDown, ChevronRight, Plus } from "lucide-react-native";
import {
DraggableList,
type DraggableRenderItemInfo,
@@ -394,8 +394,21 @@ export function GroupedAgentList({
[setProjectOrder]
);
const handleArchiveAgent = useCallback(
(e: { stopPropagation: () => void }, agent: AggregatedAgent) => {
e.stopPropagation();
const session = useSessionStore.getState().sessions[agent.serverId];
const archiveMethod = session?.methods?.archiveAgent;
if (archiveMethod) {
archiveMethod(agent.id);
}
},
[]
);
const AgentListRow = useCallback(
({ agent }: { agent: AggregatedAgent }) => {
const [isHovered, setIsHovered] = useState(false);
const timeAgo = formatTimeAgo(agent.lastActivityAt);
const agentKey = `${agent.serverId}:${agent.id}`;
const isSelected = selectedAgentId === agentKey;
@@ -415,49 +428,74 @@ export function GroupedAgentList({
? (checkout.currentBranch ?? checkout.baseRef ?? "git")
: null;
const canArchive = !isRunning && !agent.requiresAttention;
return (
<Pressable
style={({ pressed, hovered }) => [
style={({ pressed }) => [
styles.agentItem,
isSelected && styles.agentItemSelected,
hovered && styles.agentItemHovered,
isHovered && styles.agentItemHovered,
pressed && styles.agentItemPressed,
]}
onPress={() => handleAgentPress(agent.serverId, agent.id)}
onLongPress={() => handleAgentLongPress(agent)}
onHoverIn={() => setIsHovered(true)}
onHoverOut={() => setIsHovered(false)}
testID={`agent-row-${agent.serverId}-${agent.id}`}
>
{({ hovered }) => (
<View style={styles.agentContent}>
<View style={styles.row}>
{statusColor && (
<View
style={[styles.statusDot, { backgroundColor: statusColor }]}
/>
)}
<Text
style={[
styles.agentTitle,
(isSelected || hovered) && styles.agentTitleHighlighted,
]}
numberOfLines={1}
>
{agent.title || "New agent"}
</Text>
</View>
<Text style={styles.secondaryRow} numberOfLines={1}>
{activeBranchLabel ? `${activeBranchLabel} · ${timeAgo}` : timeAgo}
<View style={styles.agentContent}>
<View style={styles.row}>
{statusColor && (
<View
style={[styles.statusDot, { backgroundColor: statusColor }]}
/>
)}
<Text
style={[
styles.agentTitle,
(isSelected || isHovered) && styles.agentTitleHighlighted,
]}
numberOfLines={1}
>
{agent.title || "New agent"}
</Text>
{isHovered && canArchive && (
<Pressable
style={styles.archiveButton}
onPress={(e) => handleArchiveAgent(e, agent)}
onHoverIn={() => setIsHovered(true)}
onHoverOut={() => setIsHovered(true)}
testID={`agent-archive-${agent.serverId}-${agent.id}`}
>
{({ hovered: archiveHovered }) => (
<Archive
size={14}
color={
archiveHovered
? theme.colors.foreground
: theme.colors.foregroundMuted
}
/>
)}
</Pressable>
)}
</View>
)}
<Text style={styles.secondaryRow} numberOfLines={1}>
{activeBranchLabel ? `${activeBranchLabel} · ${timeAgo}` : timeAgo}
</Text>
</View>
</Pressable>
);
},
[
handleAgentLongPress,
handleAgentPress,
handleArchiveAgent,
selectedAgentId,
theme.colors.foreground,
theme.colors.foregroundMuted,
theme.colors.palette.blue,
theme.colors.success,
]
@@ -661,6 +699,11 @@ const styles = StyleSheet.create((theme) => ({
color: theme.colors.foreground,
opacity: 0.8,
},
archiveButton: {
alignItems: "center",
justifyContent: "center",
marginLeft: theme.spacing[1],
},
agentTitleHighlighted: {
color: theme.colors.foreground,
opacity: 1,
@@ -220,6 +220,9 @@ function normalizeAgentSnapshot(
const attentionTimestamp = snapshot.attentionTimestamp
? new Date(snapshot.attentionTimestamp)
: null;
const archivedAt = snapshot.archivedAt
? new Date(snapshot.archivedAt)
: null;
return {
serverId,
id: snapshot.id,
@@ -244,6 +247,7 @@ function normalizeAgentSnapshot(
attentionReason: snapshot.attentionReason ?? null,
attentionTimestamp,
parentAgentId: snapshot.parentAgentId,
archivedAt,
};
}
@@ -297,6 +301,7 @@ export interface SessionContextValue {
images?: Array<{ uri: string; mimeType?: string }>
) => Promise<void>;
deleteAgent: (agentId: string) => void;
archiveAgent: (agentId: string) => void;
createAgent: (options: {
config: any;
initialPrompt: string;
@@ -1436,6 +1441,27 @@ export function SessionProvider({
});
});
const unsubAgentArchived = client.on("agent_archived", (message) => {
if (message.type !== "agent_archived") {
return;
}
const { agentId, archivedAt } = message.payload;
console.log("[Session] Agent archived:", agentId);
setAgents(serverId, (prev) => {
const existing = prev.get(agentId);
if (!existing) {
return prev;
}
const next = new Map(prev);
next.set(agentId, {
...existing,
archivedAt: new Date(archivedAt),
});
return next;
});
});
return () => {
unsubSessionState();
unsubAgentState();
@@ -1450,6 +1476,7 @@ export function SessionProvider({
unsubTranscription();
unsubProviderModels();
unsubAgentDeleted();
unsubAgentArchived();
};
}, [
client,
@@ -1837,6 +1864,19 @@ export function SessionProvider({
[client]
);
const archiveAgent = useCallback(
(agentId: string) => {
if (!client) {
console.warn("[Session] archiveAgent skipped: daemon unavailable");
return;
}
void client.archiveAgent(agentId).catch((error) => {
console.error("[Session] Failed to archive agent:", error);
});
},
[client]
);
const restartServer = useCallback(
(reason?: string) => {
if (!client) {
@@ -2158,6 +2198,7 @@ export function SessionProvider({
refreshSession,
cancelAgentRun,
deleteAgent,
archiveAgent,
sendAgentMessage,
createAgent,
setAgentMode,
@@ -2181,6 +2222,7 @@ export function SessionProvider({
refreshSession,
cancelAgentRun,
deleteAgent,
archiveAgent,
sendAgentMessage,
createAgent,
setAgentMode,
@@ -2208,6 +2250,7 @@ export function SessionProvider({
cancelAgentRun,
sendAgentMessage,
deleteAgent,
archiveAgent,
createAgent,
setAgentMode,
respondToPermission,
@@ -2228,6 +2271,7 @@ export function SessionProvider({
cancelAgentRun,
sendAgentMessage,
deleteAgent,
archiveAgent,
createAgent,
setAgentMode,
respondToPermission,
@@ -80,6 +80,7 @@ export function useAggregatedAgents(): AggregatedAgentsResult {
attentionReason: agent.attentionReason,
attentionTimestamp: agent.attentionTimestamp,
parentAgentId: agent.parentAgentId,
archivedAt: agent.archivedAt,
};
allAgents.push(nextAgent);
}
+2
View File
@@ -99,6 +99,7 @@ export interface Agent {
attentionReason?: "finished" | "error" | "permission" | null;
attentionTimestamp?: Date | null;
parentAgentId?: string | null;
archivedAt?: Date | null;
}
export type ExplorerEntryKind = "file" | "directory";
@@ -183,6 +184,7 @@ export interface SessionState {
images?: Array<{ uri: string; mimeType?: string }>
) => Promise<void>;
deleteAgent: (agentId: string) => void;
archiveAgent: (agentId: string) => void;
createAgent: (options: {
config: any;
initialPrompt: string;
@@ -13,4 +13,5 @@ export interface AgentDirectoryEntry {
attentionReason?: "finished" | "error" | "permission" | null;
attentionTimestamp?: Date | null;
parentAgentId?: string | null;
archivedAt?: Date | null;
}
+6
View File
@@ -237,6 +237,12 @@ export function groupAgents(
const now = Date.now();
for (const agent of agents) {
// Archived agents are always inactive (hidden from sidebar)
if (agent.archivedAt) {
inactiveAgents.push(agent);
continue;
}
const isRunningOrAttention =
agent.status === "running" || agent.requiresAttention;
const isRecentlyActive =