Merge remote-tracking branch 'origin/main' into merge/main-into-dev

# Conflicts:
#	packages/app/src/app/h/[serverId]/index.tsx
#	packages/app/src/components/agent-stream-view.tsx
#	packages/app/src/utils/host-routes.ts
This commit is contained in:
Mohamed Boudra
2026-04-08 11:57:24 +07:00
21 changed files with 63 additions and 63 deletions
+2 -2
View File
@@ -577,7 +577,7 @@ function OfferLinkListener({
if (cancelled) return; if (cancelled) return;
const serverId = (profile as any)?.serverId; const serverId = (profile as any)?.serverId;
if (typeof serverId !== "string" || !serverId) return; if (typeof serverId !== "string" || !serverId) return;
router.replace(buildHostRootRoute(serverId) as any); router.replace(buildHostRootRoute(serverId));
}) })
.catch((error) => { .catch((error) => {
if (cancelled) return; if (cancelled) return;
@@ -695,7 +695,7 @@ function AppWithSidebar({ children }: { children: ReactNode }) {
if (hosts.some((host) => host.serverId === activeServerId)) { if (hosts.some((host) => host.serverId === activeServerId)) {
return; return;
} }
router.replace(mapPathnameToServer(pathname, hosts[0]!.serverId) as any); router.replace(mapPathnameToServer(pathname, hosts[0]!.serverId));
}, [activeServerId, hosts, pathname, router]); }, [activeServerId, hosts, pathname, router]);
// Parse selectedAgentKey directly from pathname // Parse selectedAgentKey directly from pathname
@@ -73,7 +73,7 @@ export default function HostAgentReadyRoute() {
} }
if (!client || !isConnected) { if (!client || !isConnected) {
redirectedRef.current = true; redirectedRef.current = true;
router.replace(buildHostRootRoute(serverId) as any); router.replace(buildHostRootRoute(serverId));
} }
}, [agentCwd, agentId, client, hasHydratedWorkspaces, isConnected, router, serverId]); }, [agentCwd, agentId, client, hasHydratedWorkspaces, isConnected, router, serverId]);
@@ -111,14 +111,14 @@ export default function HostAgentReadyRoute() {
); );
return; return;
} }
router.replace(buildHostRootRoute(serverId) as any); router.replace(buildHostRootRoute(serverId));
}) })
.catch(() => { .catch(() => {
if (cancelled || redirectedRef.current) { if (cancelled || redirectedRef.current) {
return; return;
} }
redirectedRef.current = true; redirectedRef.current = true;
router.replace(buildHostRootRoute(serverId) as any); router.replace(buildHostRootRoute(serverId));
}); });
return () => { return () => {
+1 -1
View File
@@ -93,7 +93,7 @@ export default function HostIndexRoute() {
return; return;
} }
router.replace(buildHostOpenProjectRoute(serverId) as any); router.replace(buildHostOpenProjectRoute(serverId));
}, HOST_ROOT_REDIRECT_DELAY_MS); }, HOST_ROOT_REDIRECT_DELAY_MS);
return () => clearTimeout(timer); return () => clearTimeout(timer);
+1 -1
View File
@@ -61,7 +61,7 @@ export default function Index() {
const targetRoute = anyOnlineServerId const targetRoute = anyOnlineServerId
? buildHostRootRoute(anyOnlineServerId) ? buildHostRootRoute(anyOnlineServerId)
: WELCOME_ROUTE; : WELCOME_ROUTE;
router.replace(targetRoute as any); router.replace(targetRoute);
}, [anyOnlineServerId, pathname, router, storeReady]); }, [anyOnlineServerId, pathname, router, storeReady]);
return <StartupSplashScreen bootstrapState={isDesktop ? bootstrapState : undefined} />; return <StartupSplashScreen bootstrapState={isDesktop ? bootstrapState : undefined} />;
+3 -3
View File
@@ -174,7 +174,7 @@ export default function PairScanScreen() {
const returnToSource = useCallback( const returnToSource = useCallback(
(serverId: string) => { (serverId: string) => {
if (source === "onboarding") { if (source === "onboarding") {
router.replace(buildHostRootRoute(serverId) as any); router.replace(buildHostRootRoute(serverId));
return; return;
} }
if (source === "editHost" && targetServerId) { if (source === "editHost" && targetServerId) {
@@ -190,7 +190,7 @@ export default function PairScanScreen() {
router.back(); router.back();
} catch { } catch {
const settingsServerId = sourceServerId ?? serverId; const settingsServerId = sourceServerId ?? serverId;
router.replace(buildHostSettingsRoute(settingsServerId) as any); router.replace(buildHostSettingsRoute(settingsServerId));
} }
}, },
[router, source, sourceServerId, targetServerId], [router, source, sourceServerId, targetServerId],
@@ -209,7 +209,7 @@ export default function PairScanScreen() {
router.back(); router.back();
} catch { } catch {
if (sourceServerId) { if (sourceServerId) {
router.replace(buildHostSettingsRoute(sourceServerId) as any); router.replace(buildHostSettingsRoute(sourceServerId));
return; return;
} }
router.replace("/" as any); router.replace("/" as any);
+1 -1
View File
@@ -16,7 +16,7 @@ export default function LegacySettingsRoute() {
if (!targetServerId) { if (!targetServerId) {
return; return;
} }
router.replace(buildHostSettingsRoute(targetServerId) as any); router.replace(buildHostSettingsRoute(targetServerId));
}, [router, targetServerId]); }, [router, targetServerId]);
if (!targetServerId) { if (!targetServerId) {
+1 -1
View File
@@ -256,7 +256,7 @@ export function AgentList({
target: { kind: "agent", agentId }, target: { kind: "agent", agentId },
pin: Boolean(agent.archivedAt), pin: Boolean(agent.archivedAt),
}); });
router.navigate(route as any); router.navigate(route);
}, },
[isActionSheetVisible, onAgentSelect], [isActionSheetVisible, onAgentSelect],
); );
@@ -187,7 +187,7 @@ const AgentStreamViewComponent = forwardRef<AgentStreamViewHandle, AgentStreamVi
workspaceId, workspaceId,
target: { kind: "file", path: normalized.file }, target: { kind: "file", path: normalized.file },
}); });
router.navigate(route as any); router.navigate(route);
} }
return; return;
} }
@@ -983,7 +983,7 @@ export function GitDiffPane({ serverId, workspaceId, cwd, hideHeaderRow }: GitDi
const targetWorkingDir = resolveNewAgentWorkingDir(cwd, status ?? null); const targetWorkingDir = resolveNewAgentWorkingDir(cwd, status ?? null);
void runArchiveWorktree({ serverId, cwd, worktreePath }) void runArchiveWorktree({ serverId, cwd, worktreePath })
.then(() => { .then(() => {
router.replace(buildNewAgentRoute(serverId, targetWorkingDir) as any); router.replace(buildNewAgentRoute(serverId, targetWorkingDir));
}) })
.catch((err) => { .catch((err) => {
const message = err instanceof Error ? err.message : "Failed to archive worktree"; const message = err instanceof Error ? err.message : "Failed to archive worktree";
+4 -4
View File
@@ -229,21 +229,21 @@ export const LeftSidebar = memo(function LeftSidebar({
return; return;
} }
closeToAgent(); closeToAgent();
router.push(buildHostSettingsRoute(activeServerId) as any); router.push(buildHostSettingsRoute(activeServerId));
}, [activeServerId, closeToAgent]); }, [activeServerId, closeToAgent]);
const handleSettingsDesktop = useCallback(() => { const handleSettingsDesktop = useCallback(() => {
if (!activeServerId) { if (!activeServerId) {
return; return;
} }
router.push(buildHostSettingsRoute(activeServerId) as any); router.push(buildHostSettingsRoute(activeServerId));
}, [activeServerId]); }, [activeServerId]);
const handleViewMoreNavigate = useCallback(() => { const handleViewMoreNavigate = useCallback(() => {
if (!activeServerId) { if (!activeServerId) {
return; return;
} }
router.push(buildHostSessionsRoute(activeServerId) as any); router.push(buildHostSessionsRoute(activeServerId));
}, [activeServerId]); }, [activeServerId]);
const handleHostSelect = useCallback( const handleHostSelect = useCallback(
@@ -253,7 +253,7 @@ export const LeftSidebar = memo(function LeftSidebar({
} }
const nextPath = mapPathnameToServer(pathname, nextServerId); const nextPath = mapPathnameToServer(pathname, nextServerId);
setIsHostPickerOpen(false); setIsHostPickerOpen(false);
router.push(nextPath as any); router.push(nextPath);
}, },
[pathname], [pathname],
); );
@@ -1118,7 +1118,7 @@ function WorkspaceRowWithMenu({
serverId: workspace.serverId, serverId: workspace.serverId,
archivedWorkspaceId: workspace.workspaceId, archivedWorkspaceId: workspace.workspaceId,
workspaces: sessionWorkspaces.values(), workspaces: sessionWorkspaces.values(),
}) as any, }),
); );
}, [activeWorkspaceSelection, sessionWorkspaces, workspace.serverId, workspace.workspaceId]); }, [activeWorkspaceSelection, sessionWorkspaces, workspace.serverId, workspace.workspaceId]);
@@ -275,12 +275,12 @@ export function WelcomeScreen({ onHostAdded }: WelcomeScreenProps) {
if (pendingNameHost) { if (pendingNameHost) {
return; return;
} }
router.replace(buildHostRootRoute(anyOnlineServerId) as any); router.replace(buildHostRootRoute(anyOnlineServerId));
}, [anyOnlineServerId, pendingNameHost, router]); }, [anyOnlineServerId, pendingNameHost, router]);
const finishOnboarding = useCallback( const finishOnboarding = useCallback(
(serverId: string) => { (serverId: string) => {
router.replace(buildHostRootRoute(serverId) as any); router.replace(buildHostRootRoute(serverId));
}, },
[router], [router],
); );
+2 -2
View File
@@ -171,7 +171,7 @@ export function useCommandCenter() {
const settingsRoute = useMemo<Href>(() => { const settingsRoute = useMemo<Href>(() => {
const serverIdFromPath = activeServerId; const serverIdFromPath = activeServerId;
return serverIdFromPath ? (buildHostSettingsRoute(serverIdFromPath) as Href) : "/"; return serverIdFromPath ? buildHostSettingsRoute(serverIdFromPath) : "/";
}, [activeServerId]); }, [activeServerId]);
const actionItems = useMemo(() => { const actionItems = useMemo(() => {
@@ -234,7 +234,7 @@ export function useCommandCenter() {
workspaceId, workspaceId,
target: { kind: "agent", agentId: agent.id }, target: { kind: "agent", agentId: agent.id },
}); });
router.navigate(route as any); router.navigate(route);
}, },
[setOpen], [setOpen],
); );
+1 -1
View File
@@ -190,7 +190,7 @@ export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): Use
const targetWorkingDir = resolveNewAgentWorkingDir(cwd, status ?? null); const targetWorkingDir = resolveNewAgentWorkingDir(cwd, status ?? null);
void runArchiveWorktree({ serverId, cwd, worktreePath }) void runArchiveWorktree({ serverId, cwd, worktreePath })
.then(() => { .then(() => {
router.replace(buildNewAgentRoute(serverId, targetWorkingDir) as any); router.replace(buildNewAgentRoute(serverId, targetWorkingDir));
}) })
.catch((err) => { .catch((err) => {
const message = err instanceof Error ? err.message : "Failed to archive worktree"; const message = err instanceof Error ? err.message : "Failed to archive worktree";
@@ -11,7 +11,7 @@ import { buildHostWorkspaceRoute } from "@/utils/host-routes";
*/ */
export function navigateToWorkspace(serverId: string, workspaceId: string) { export function navigateToWorkspace(serverId: string, workspaceId: string) {
const href = buildHostWorkspaceRoute(serverId, workspaceId); const href = buildHostWorkspaceRoute(serverId, workspaceId);
router.navigate(href as any); router.navigate(href);
} }
export function useWorkspaceNavigation() { export function useWorkspaceNavigation() {
+1 -1
View File
@@ -54,7 +54,7 @@ function SessionsScreenContent({ serverId }: { serverId: string }) {
<Button <Button
variant="ghost" variant="ghost"
leftIcon={ChevronLeft} leftIcon={ChevronLeft}
onPress={() => router.navigate(buildHostOpenProjectRoute(serverId) as any)} onPress={() => router.navigate(buildHostOpenProjectRoute(serverId))}
> >
Back Back
</Button> </Button>
+31 -31
View File
@@ -288,125 +288,125 @@ export function parseHostWorkspaceRouteFromPathname(
return { serverId, workspaceId }; return { serverId, workspaceId };
} }
export function buildHostWorkspaceRoute(serverId: string, workspaceId: string): string { export function buildHostWorkspaceRoute(serverId: string, workspaceId: string) {
const normalizedServerId = trimNonEmpty(serverId); const normalizedServerId = trimNonEmpty(serverId);
const normalizedWorkspaceId = trimNonEmpty(workspaceId); const normalizedWorkspaceId = trimNonEmpty(workspaceId);
if (!normalizedServerId || !normalizedWorkspaceId) { if (!normalizedServerId || !normalizedWorkspaceId) {
return "/"; return "/" as const;
} }
const encodedWorkspaceId = encodeWorkspaceIdForPathSegment(normalizedWorkspaceId); const encodedWorkspaceId = encodeWorkspaceIdForPathSegment(normalizedWorkspaceId);
if (!encodedWorkspaceId) { if (!encodedWorkspaceId) {
return "/"; return "/" as const;
} }
return `/h/${encodeSegment(normalizedServerId)}/workspace/${encodeSegment(encodedWorkspaceId)}`; return `/h/${encodeSegment(normalizedServerId)}/workspace/${encodeSegment(encodedWorkspaceId)}` as const;
} }
export function buildHostWorkspaceOpenRoute( export function buildHostWorkspaceOpenRoute(
serverId: string, serverId: string,
workspaceId: string, workspaceId: string,
openIntent: string, openIntent: string,
): string { ) {
const base = buildHostWorkspaceRoute(serverId, workspaceId); const base = buildHostWorkspaceRoute(serverId, workspaceId);
const normalizedOpenIntent = trimNonEmpty(openIntent); const normalizedOpenIntent = trimNonEmpty(openIntent);
if (base === "/" || !normalizedOpenIntent) { if (base === "/" || !normalizedOpenIntent) {
return base; return base;
} }
return `${base}?open=${encodeURIComponent(normalizedOpenIntent)}`; return `${base}?open=${encodeURIComponent(normalizedOpenIntent)}` as const;
} }
export function buildHostAgentDetailRoute( export function buildHostAgentDetailRoute(
serverId: string, serverId: string,
agentId: string, agentId: string,
workspaceId?: string, workspaceId?: string,
): string { ) {
const normalizedWorkspaceId = trimNonEmpty(workspaceId); const normalizedWorkspaceId = trimNonEmpty(workspaceId);
if (normalizedWorkspaceId) { if (normalizedWorkspaceId) {
const normalizedAgentId = trimNonEmpty(agentId); const normalizedAgentId = trimNonEmpty(agentId);
if (!normalizedAgentId) { if (!normalizedAgentId) {
return "/"; return "/" as const;
} }
return buildHostWorkspaceOpenRoute(serverId, normalizedWorkspaceId, `agent:${normalizedAgentId}`); return buildHostWorkspaceOpenRoute(serverId, normalizedWorkspaceId, `agent:${normalizedAgentId}`);
} }
const normalizedServerId = trimNonEmpty(serverId); const normalizedServerId = trimNonEmpty(serverId);
const normalizedAgentId = trimNonEmpty(agentId); const normalizedAgentId = trimNonEmpty(agentId);
if (!normalizedServerId || !normalizedAgentId) { if (!normalizedServerId || !normalizedAgentId) {
return "/"; return "/" as const;
} }
return `${buildHostRootRoute(normalizedServerId)}/agent/${encodeSegment(normalizedAgentId)}`; return `${buildHostRootRoute(normalizedServerId)}/agent/${encodeSegment(normalizedAgentId)}` as const;
} }
export function buildHostRootRoute(serverId: string): string { export function buildHostRootRoute(serverId: string) {
const normalized = trimNonEmpty(serverId); const normalized = trimNonEmpty(serverId);
if (!normalized) { if (!normalized) {
return "/"; return "/" as const;
} }
return `/h/${encodeSegment(normalized)}`; return `/h/${encodeSegment(normalized)}` as const;
} }
export function buildHostSessionsRoute(serverId: string): string { export function buildHostSessionsRoute(serverId: string) {
const base = buildHostRootRoute(serverId); const base = buildHostRootRoute(serverId);
if (base === "/") { if (base === "/") {
return "/"; return "/" as const;
} }
return `${base}/sessions`; return `${base}/sessions` as const;
} }
export function buildHostOpenProjectRoute(serverId: string): string { export function buildHostOpenProjectRoute(serverId: string) {
const base = buildHostRootRoute(serverId); const base = buildHostRootRoute(serverId);
if (base === "/") { if (base === "/") {
return "/"; return "/" as const;
} }
return `${base}/open-project`; return `${base}/open-project` as const;
} }
export function buildHostNewWorkspaceRoute( export function buildHostNewWorkspaceRoute(
serverId: string, serverId: string,
sourceDirectory: string, sourceDirectory: string,
options?: { displayName?: string }, options?: { displayName?: string },
): string { ){
const base = buildHostRootRoute(serverId); const base = buildHostRootRoute(serverId);
if (base === "/") { if (base === "/") {
return "/"; return "/" as const;
} }
const params = new URLSearchParams(); const params = new URLSearchParams();
params.set("dir", sourceDirectory); params.set("dir", sourceDirectory);
if (options?.displayName) { if (options?.displayName) {
params.set("name", options.displayName); params.set("name", options.displayName);
} }
return `${base}/new?${params.toString()}`; return `${base}/new?${params.toString()}` as const;
} }
export function buildHostSettingsRoute(serverId: string): string { export function buildHostSettingsRoute(serverId: string) {
const base = buildHostRootRoute(serverId); const base = buildHostRootRoute(serverId);
if (base === "/") { if (base === "/") {
return "/"; return "/" as const;
} }
return `${base}/settings`; return `${base}/settings` as const;
} }
export function mapPathnameToServer(pathname: string, nextServerId: string): string { export function mapPathnameToServer(pathname: string, nextServerId: string) {
const normalized = trimNonEmpty(nextServerId); const normalized = trimNonEmpty(nextServerId);
if (!normalized) { if (!normalized) {
return "/"; return "/" as const;
} }
const suffix = pathname.replace(/^\/h\/[^/]+\/?/, ""); const suffix = pathname.replace(/^\/h\/[^/]+\/?/, "");
const base = buildHostRootRoute(normalized); const base = buildHostRootRoute(normalized);
if (suffix.startsWith("settings")) { if (suffix.startsWith("settings")) {
return `${base}/settings`; return `${base}/settings` as const;
} }
if (suffix.startsWith("sessions")) { if (suffix.startsWith("sessions")) {
return `${base}/sessions`; return `${base}/sessions` as const;
} }
if (suffix.startsWith("open-project")) { if (suffix.startsWith("open-project")) {
return `${base}/open-project`; return `${base}/open-project` as const;
} }
const workspaceRoute = parseHostWorkspaceRouteFromPathname(pathname); const workspaceRoute = parseHostWorkspaceRouteFromPathname(pathname);
if (workspaceRoute) { if (workspaceRoute) {
return buildHostWorkspaceRoute(normalized, workspaceRoute.workspaceId); return buildHostWorkspaceRoute(normalized, workspaceRoute.workspaceId);
} }
if (suffix.startsWith("agent/")) { if (suffix.startsWith("agent/")) {
return `${base}/${suffix}`; return `${base}/${suffix}` as const;
} }
return base; return base;
} }
+1 -1
View File
@@ -69,7 +69,7 @@ export function resolveNewAgentWorkingDir(
return inferMainRepoRootFromPaseoWorktreePath(cwd) ?? cwd; return inferMainRepoRootFromPaseoWorktreePath(cwd) ?? cwd;
} }
export function buildNewAgentRoute(serverId: string, workingDir?: string | null): string { export function buildNewAgentRoute(serverId: string, workingDir?: string | null) {
const trimmedWorkingDir = workingDir?.trim(); const trimmedWorkingDir = workingDir?.trim();
return buildHostWorkspaceRoute(serverId, trimmedWorkingDir || "."); return buildHostWorkspaceRoute(serverId, trimmedWorkingDir || ".");
} }
@@ -27,17 +27,17 @@ export function resolveNotificationTarget(data: NotificationData): {
}; };
} }
export function buildNotificationRoute(data: NotificationData): string { export function buildNotificationRoute(data: NotificationData) {
const { serverId, agentId, workspaceId } = resolveNotificationTarget(data); const { serverId, agentId, workspaceId } = resolveNotificationTarget(data);
if (serverId && agentId) { if (serverId && agentId) {
if (workspaceId) { if (workspaceId) {
const base = buildHostWorkspaceRoute(serverId, workspaceId); const base = buildHostWorkspaceRoute(serverId, workspaceId);
return `${base}?open=${encodeURIComponent(`agent:${agentId}`)}`; return `${base}?open=${encodeURIComponent(`agent:${agentId}`)}` as const;
} }
return buildHostAgentDetailRoute(serverId, agentId); return buildHostAgentDetailRoute(serverId, agentId);
} }
if (serverId) { if (serverId) {
return buildHostRootRoute(serverId); return buildHostRootRoute(serverId);
} }
return "/"; return "/" as const;
} }
@@ -41,7 +41,7 @@ export function buildWorkspaceArchiveRedirectRoute(input: {
serverId: string; serverId: string;
archivedWorkspaceId: string; archivedWorkspaceId: string;
workspaces: Iterable<WorkspaceDescriptor>; workspaces: Iterable<WorkspaceDescriptor>;
}): string { }) {
const redirectWorkspaceId = resolveWorkspaceArchiveRedirectWorkspaceId({ const redirectWorkspaceId = resolveWorkspaceArchiveRedirectWorkspaceId({
archivedWorkspaceId: input.archivedWorkspaceId, archivedWorkspaceId: input.archivedWorkspaceId,
workspaces: input.workspaces, workspaces: input.workspaces,
@@ -25,7 +25,7 @@ function getPreparedTarget(target: WorkspaceTabTarget): WorkspaceTabTarget {
return { kind: "draft", draftId: generateDraftId() }; return { kind: "draft", draftId: generateDraftId() };
} }
export function prepareWorkspaceTab(input: PrepareWorkspaceTabInput): string { export function prepareWorkspaceTab(input: PrepareWorkspaceTabInput) {
const target = getPreparedTarget(input.target); const target = getPreparedTarget(input.target);
const key = const key =
buildWorkspaceTabPersistenceKey({ buildWorkspaceTabPersistenceKey({