fix agent create: keep draft view until agent ready

This commit is contained in:
Mohamed Boudra
2026-01-24 19:29:56 +07:00
parent 6ecf88b502
commit 3e2d403f1b
10 changed files with 133 additions and 51 deletions

View File

@@ -1,31 +0,0 @@
import { useLocalSearchParams } from "expo-router";
import { AgentReadyScreen } from "@/screens/agent/agent-ready-screen";
import { LegacyAgentIdScreen } from "@/screens/agent/legacy-agent-id-screen";
type AgentRouteParams = {
route?: string[] | string;
};
function normalizeSegments(raw: AgentRouteParams["route"]): string[] {
if (!raw) return [];
if (Array.isArray(raw)) return raw.filter((s) => typeof s === "string" && s.length > 0);
if (typeof raw === "string") return raw.length > 0 ? [raw] : [];
return [];
}
export default function AgentCatchAllRoute() {
const params = useLocalSearchParams<AgentRouteParams>();
const segments = normalizeSegments(params.route);
if (segments.length === 1) {
return <LegacyAgentIdScreen agentId={segments[0]} />;
}
if (segments.length >= 2) {
const [serverId, agentId] = segments;
return <AgentReadyScreen serverId={serverId} agentId={agentId} />;
}
return <LegacyAgentIdScreen agentId="" />;
}

View File

@@ -0,0 +1,109 @@
import { useEffect, useMemo, useState } from "react";
import { View } from "react-native";
import { useLocalSearchParams } from "expo-router";
import { StyleSheet } from "react-native-unistyles";
import { DraftAgentScreen } from "@/screens/agent/draft-agent-screen";
import { AgentReadyScreen } from "@/screens/agent/agent-ready-screen";
import { LegacyAgentIdScreen } from "@/screens/agent/legacy-agent-id-screen";
import { useSessionStore } from "@/stores/session-store";
type AgentRouteParams = {
route?: string[] | string;
};
function normalizeSegments(raw: AgentRouteParams["route"]): string[] {
if (!raw) return [];
if (Array.isArray(raw)) return raw.filter((s) => typeof s === "string" && s.length > 0);
if (typeof raw === "string") return raw.length > 0 ? [raw] : [];
return [];
}
type RouteKind = "draft" | "legacy" | "ready";
export default function AgentRoute() {
const params = useLocalSearchParams<AgentRouteParams>();
const segments = useMemo(() => normalizeSegments(params.route), [params.route]);
const routeKind: RouteKind =
segments.length === 0 ? "draft" : segments.length === 1 ? "legacy" : "ready";
const legacyAgentId = segments.length === 1 ? segments[0] : "";
const serverId = segments.length >= 2 ? segments[0] : "";
const agentId = segments.length >= 2 ? segments[1] : "";
const agent = useSessionStore((state) =>
serverId && agentId ? state.sessions[serverId]?.agents?.get(agentId) : undefined
);
const isInitializingFromMap = useSessionStore((state) =>
serverId && agentId
? state.sessions[serverId]?.initializingAgents?.get(agentId) ?? false
: false
);
const isInitializing = agentId ? isInitializingFromMap !== false : false;
const isAgentReady = Boolean(agent && !isInitializing);
const [createFlowActive, setCreateFlowActive] = useState(false);
const [shouldMountDraft, setShouldMountDraft] = useState(routeKind === "draft");
useEffect(() => {
if (routeKind === "draft") {
setShouldMountDraft(true);
}
}, [routeKind]);
const ensureAgentIsInitialized = useSessionStore((state) =>
serverId ? state.sessions[serverId]?.methods?.ensureAgentIsInitialized : undefined
);
const isConnected = useSessionStore((state) =>
serverId ? state.sessions[serverId]?.connection.isConnected ?? false : false
);
useEffect(() => {
if (routeKind !== "ready") return;
if (!agentId || !ensureAgentIsInitialized) return;
if (!isConnected) return;
ensureAgentIsInitialized(agentId).catch((error) => {
console.warn("[AgentRoute] Agent initialization failed", {
agentId,
serverId,
error,
});
});
}, [agentId, ensureAgentIsInitialized, isConnected, routeKind, serverId]);
useEffect(() => {
if (routeKind !== "ready") {
return;
}
if (createFlowActive && isAgentReady) {
setCreateFlowActive(false);
}
}, [createFlowActive, isAgentReady, routeKind]);
if (routeKind === "legacy") {
return <LegacyAgentIdScreen agentId={legacyAgentId} />;
}
const shouldShowDraft =
routeKind === "draft" || (routeKind === "ready" && createFlowActive && !isAgentReady);
const shouldShowReady = routeKind === "ready" && (!createFlowActive || isAgentReady);
return (
<View style={styles.container}>
{shouldShowReady ? <AgentReadyScreen serverId={serverId} agentId={agentId} /> : null}
{shouldMountDraft && shouldShowDraft ? (
<DraftAgentScreen
isVisible={shouldShowDraft}
onCreateFlowActiveChange={setCreateFlowActive}
/>
) : null}
</View>
);
}
const styles = StyleSheet.create((theme) => ({
container: {
flex: 1,
backgroundColor: theme.colors.surface0,
},
}));

View File

@@ -1,6 +0,0 @@
import { DraftAgentScreen } from "@/screens/agent/draft-agent-screen";
export default function AgentDraftRoute() {
return <DraftAgentScreen />;
}

View File

@@ -85,10 +85,8 @@ export function AgentList({
onAgentSelect?.();
navigate({
pathname: "/agent/[...route]",
params: {
route: [serverId, agentId],
},
pathname: "/agent/[[...route]]",
params: { route: [serverId, agentId] },
});
},
[isActionSheetVisible, pathname, onAgentSelect]

View File

@@ -607,7 +607,7 @@ export function GitDiffPane({ serverId, agentId }: GitDiffPaneProps) {
predicate: (query) =>
Array.isArray(query.queryKey) && query.queryKey[0] === "paseoWorktreeList",
});
router.replace("/agent");
router.replace({ pathname: "/agent/[[...route]]" });
},
onError: (err) => {
const message = err instanceof Error ? err.message : "Failed to archive worktree";

View File

@@ -142,7 +142,7 @@ export function HomeFooter() {
<Pressable
onPress={() => {
console.log("[HomeFooter] New Agent button pressed");
router.push("/agent");
router.push({ pathname: "/agent/[[...route]]" });
}}
style={({ pressed }) => [
styles.footerButton,

View File

@@ -82,7 +82,7 @@ export function SlidingSidebar({ selectedAgentId }: SlidingSidebarProps) {
}, [closeToAgent]);
const handleCreateAgentClean = useCallback(() => {
router.push("/agent");
router.push({ pathname: "/agent/[[...route]]" });
}, []);
// Mobile: close sidebar and navigate

View File

@@ -118,7 +118,7 @@ export function AgentReadyScreen({
targetMs: 300,
});
}
router.replace("/agent");
router.replace({ pathname: "/agent/[[...route]]" });
}, [resolvedAgentId, resolvedServerId, router]);
const focusServerId = resolvedServerId;

View File

@@ -103,7 +103,15 @@ type DraftAgentParams = {
workingDir?: string;
};
export function DraftAgentScreen() {
type DraftAgentScreenProps = {
isVisible?: boolean;
onCreateFlowActiveChange?: (active: boolean) => void;
};
export function DraftAgentScreen({
isVisible = true,
onCreateFlowActiveChange,
}: DraftAgentScreenProps = {}) {
const { theme } = useUnistyles();
const router = useRouter();
const insets = useSafeAreaInsets();
@@ -171,7 +179,7 @@ export function DraftAgentScreen() {
} = useAgentFormState({
initialServerId: resolvedServerId ?? null,
initialValues,
isVisible: true,
isVisible,
isCreateFlow: true,
});
const hostEntry = selectedServerId
@@ -695,6 +703,7 @@ export function DraftAgentScreen() {
}
Keyboard.dismiss();
dispatch({ type: "SUBMIT", attempt });
onCreateFlowActiveChange?.(true);
try {
const result = await createAgent({
@@ -707,7 +716,7 @@ export function DraftAgentScreen() {
const agentId = (result as { id?: string })?.id;
if (agentId && selectedServerId) {
router.replace({
pathname: "/agent/[...route]",
pathname: "/agent/[[...route]]",
params: { route: [selectedServerId, agentId] },
});
return;
@@ -717,10 +726,12 @@ export function DraftAgentScreen() {
type: "CREATE_FAILED",
message: "Failed to create agent",
});
onCreateFlowActiveChange?.(false);
throw new Error("Failed to create agent");
} catch (error) {
const message = error instanceof Error ? error.message : "Failed to create agent";
dispatch({ type: "CREATE_FAILED", message });
onCreateFlowActiveChange?.(false);
throw error; // Re-throw so AgentInputArea knows it failed
}
},
@@ -745,6 +756,7 @@ export function DraftAgentScreen() {
workingDir,
isAttachWorktree,
isSubmitting,
onCreateFlowActiveChange,
]
);

View File

@@ -51,19 +51,19 @@ export function LegacyAgentIdScreen({ agentId }: { agentId: string }) {
}
const match = matches[0];
router.replace({
pathname: "/agent/[...route]",
pathname: "/agent/[[...route]]",
params: { route: [match.serverId, match.agent.id] },
});
}, [isRedirecting, matches, router]);
const handleGoDraft = useCallback(() => {
router.replace("/agent");
router.replace({ pathname: "/agent/[[...route]]" });
}, [router]);
const handleSelectMatch = useCallback(
(match: AgentMatch) => {
router.replace({
pathname: "/agent/[...route]",
pathname: "/agent/[[...route]]",
params: { route: [match.serverId, match.agent.id] },
});
},