diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx
index 92e3561b3..294943631 100644
--- a/packages/app/src/app/_layout.tsx
+++ b/packages/app/src/app/_layout.tsx
@@ -45,6 +45,7 @@ import {
} from "@/utils/os-notifications";
import { buildNotificationRoute } from "@/utils/notification-routing";
import {
+ buildHostRootRoute,
parseHostAgentRouteFromPathname,
parseHostWorkspaceTabRouteFromPathname,
} from "@/utils/host-routes";
@@ -350,7 +351,7 @@ function OfferLinkListener({
if (cancelled) return;
const serverId = (profile as any)?.serverId;
if (typeof serverId !== "string" || !serverId) return;
- router.replace(`/h/${encodeURIComponent(serverId)}` as any);
+ router.replace(buildHostRootRoute(serverId) as any);
})
.catch((error) => {
if (cancelled) return;
@@ -471,14 +472,14 @@ export default function RootLayout() {
>
-
-
+
+
diff --git a/packages/app/src/app/h/[serverId]/agent/[agentId].tsx b/packages/app/src/app/h/[serverId]/agent/[agentId].tsx
index 277529c9f..7413bae15 100644
--- a/packages/app/src/app/h/[serverId]/agent/[agentId].tsx
+++ b/packages/app/src/app/h/[serverId]/agent/[agentId].tsx
@@ -3,6 +3,7 @@ import { useLocalSearchParams, useRouter } from "expo-router";
import { useSessionStore } from "@/stores/session-store";
import { useHostRuntimeSession } from "@/runtime/host-runtime";
import {
+ buildHostRootRoute,
buildHostWorkspaceAgentTabRoute,
} from "@/utils/host-routes";
@@ -54,7 +55,7 @@ export default function HostAgentReadyRoute() {
}
if (!client || !isConnected) {
redirectedRef.current = true;
- router.replace(`/h/${encodeURIComponent(serverId)}` as any);
+ router.replace(buildHostRootRoute(serverId) as any);
}
}, [agentCwd, agentId, client, isConnected, router, serverId]);
@@ -79,14 +80,14 @@ export default function HostAgentReadyRoute() {
router.replace(buildHostWorkspaceAgentTabRoute(serverId, cwd, agentId) as any);
return;
}
- router.replace(`/h/${encodeURIComponent(serverId)}` as any);
+ router.replace(buildHostRootRoute(serverId) as any);
})
.catch(() => {
if (cancelled || redirectedRef.current) {
return;
}
redirectedRef.current = true;
- router.replace(`/h/${encodeURIComponent(serverId)}` as any);
+ router.replace(buildHostRootRoute(serverId) as any);
});
return () => {
diff --git a/packages/app/src/app/h/[serverId]/new-agent.tsx b/packages/app/src/app/h/[serverId]/new-agent.tsx
new file mode 100644
index 000000000..64bcc2833
--- /dev/null
+++ b/packages/app/src/app/h/[serverId]/new-agent.tsx
@@ -0,0 +1,9 @@
+import { useLocalSearchParams } from "expo-router";
+import { DraftAgentScreen } from "@/screens/agent/draft-agent-screen";
+
+export default function HostNewAgentRoute() {
+ const params = useLocalSearchParams<{ serverId?: string }>();
+ const serverId = typeof params.serverId === "string" ? params.serverId : "";
+
+ return ;
+}
diff --git a/packages/app/src/app/h/[serverId]/workspace/[workspaceId]/_layout.tsx b/packages/app/src/app/h/[serverId]/workspace/[workspaceId]/_layout.tsx
index 45d3c56ee..3d0dff7a3 100644
--- a/packages/app/src/app/h/[serverId]/workspace/[workspaceId]/_layout.tsx
+++ b/packages/app/src/app/h/[serverId]/workspace/[workspaceId]/_layout.tsx
@@ -1,20 +1,32 @@
-import { useLocalSearchParams } from "expo-router";
+import { useLocalSearchParams, usePathname } from "expo-router";
import { WorkspaceScreen } from "@/screens/workspace/workspace-screen";
+import {
+ parseHostWorkspaceRouteFromPathname,
+ parseHostWorkspaceTabRouteFromPathname,
+} from "@/utils/host-routes";
+
+function readNonEmptyParam(value: unknown): string | null {
+ if (typeof value !== "string") {
+ return null;
+ }
+ const trimmed = value.trim();
+ return trimmed.length > 0 ? trimmed : null;
+}
export default function HostWorkspaceLayout() {
- const params = useLocalSearchParams<{
- serverId?: string;
- workspaceId?: string;
- tabId?: string;
- }>();
-
- const tabId = typeof params.tabId === "string" ? params.tabId : "";
+ const pathname = usePathname();
+ const params = useLocalSearchParams<{ tabId?: string }>();
+ const tabRoute = parseHostWorkspaceTabRouteFromPathname(pathname);
+ const activeRoute = tabRoute ?? parseHostWorkspaceRouteFromPathname(pathname);
+ const serverId = activeRoute?.serverId ?? "";
+ const workspaceId = activeRoute?.workspaceId ?? "";
+ const routeTabId = tabRoute?.tabId ?? readNonEmptyParam(params.tabId);
return (
);
}
diff --git a/packages/app/src/app/index.tsx b/packages/app/src/app/index.tsx
index 71049b6cc..7b86312aa 100644
--- a/packages/app/src/app/index.tsx
+++ b/packages/app/src/app/index.tsx
@@ -5,6 +5,7 @@ import { useUnistyles } from "react-native-unistyles";
import { DraftAgentScreen } from "@/screens/agent/draft-agent-screen";
import { useDaemonRegistry } from "@/contexts/daemon-registry-context";
import { useFormPreferences } from "@/hooks/use-form-preferences";
+import { buildHostRootRoute } from "@/utils/host-routes";
export default function Index() {
const router = useRouter();
@@ -44,7 +45,7 @@ export default function Index() {
if (!targetServerId) {
return;
}
- router.replace(`/h/${encodeURIComponent(targetServerId)}` as any);
+ router.replace(buildHostRootRoute(targetServerId) as any);
}, [preferencesLoading, registryLoading, router, targetServerId]);
if (registryLoading || preferencesLoading) {
diff --git a/packages/app/src/app/pair-scan.tsx b/packages/app/src/app/pair-scan.tsx
index ca15c11fd..60cd9989e 100644
--- a/packages/app/src/app/pair-scan.tsx
+++ b/packages/app/src/app/pair-scan.tsx
@@ -12,6 +12,7 @@ import { decodeOfferFragmentPayload, normalizeHostPort } from "@/utils/daemon-en
import { probeConnection } from "@/utils/test-daemon-connection";
import { ConnectionOfferSchema } from "@server/shared/connection-offer";
import {
+ buildHostRootRoute,
buildHostSettingsRoute,
} from "@/utils/host-routes";
@@ -169,7 +170,7 @@ export default function PairScanScreen() {
const returnToSource = useCallback(
(serverId: string) => {
if (source === "onboarding") {
- router.replace(`/h/${encodeURIComponent(serverId)}` as any);
+ router.replace(buildHostRootRoute(serverId) as any);
return;
}
if (source === "editHost" && targetServerId) {
diff --git a/packages/app/src/components/left-sidebar.tsx b/packages/app/src/components/left-sidebar.tsx
index cf4eb428c..0b3f171b9 100644
--- a/packages/app/src/components/left-sidebar.tsx
+++ b/packages/app/src/components/left-sidebar.tsx
@@ -21,21 +21,11 @@ import { useTauriDragHandlers, useTrafficLightPadding } from '@/utils/tauri-wind
import { Combobox } from '@/components/ui/combobox'
import { useDaemonRegistry } from '@/contexts/daemon-registry-context'
import { getHostRuntimeStore } from '@/runtime/host-runtime'
-import { useSessionStore } from '@/stores/session-store'
import { formatConnectionStatus } from '@/utils/daemons'
import { HEADER_INNER_HEIGHT, HEADER_INNER_HEIGHT_MOBILE } from '@/constants/layout'
-import {
- checkoutStatusQueryKey,
- type CheckoutStatusPayload,
-} from '@/hooks/use-checkout-status-query'
-import { queryClient } from '@/query/query-client'
-import {
- buildNewAgentRoute,
- resolveNewAgentWorkingDir,
- resolveSelectedAgentForNewAgent,
-} from '@/utils/new-agent-routing'
import {
buildHostAgentsRoute,
+ buildHostNewAgentRoute,
buildHostSettingsRoute,
mapPathnameToServer,
parseServerIdFromPathname,
@@ -47,7 +37,7 @@ interface LeftSidebarProps {
selectedAgentId?: string
}
-export function LeftSidebar({ selectedAgentId }: LeftSidebarProps) {
+export function LeftSidebar({ selectedAgentId: _selectedAgentId }: LeftSidebarProps) {
const { theme } = useUnistyles()
const insets = useSafeAreaInsets()
const isMobile = UnistylesRuntime.breakpoint === 'xs' || UnistylesRuntime.breakpoint === 'sm'
@@ -79,13 +69,24 @@ export function LeftSidebar({ selectedAgentId }: LeftSidebarProps) {
.join('|')
)
const activeServerIdFromPath = useMemo(() => parseServerIdFromPathname(pathname), [pathname])
- const activeServerId = activeServerIdFromPath ?? daemons[0]?.serverId ?? null
+ const activeDaemon = useMemo(() => {
+ if (daemons.length === 0) {
+ return null
+ }
+ if (activeServerIdFromPath) {
+ const routeMatch = daemons.find((entry) => entry.serverId === activeServerIdFromPath)
+ if (routeMatch) {
+ return routeMatch
+ }
+ }
+ return daemons[0] ?? null
+ }, [activeServerIdFromPath, daemons])
+ const activeServerId = activeDaemon?.serverId ?? null
const activeHostLabel = useMemo(() => {
- if (!activeServerId) return 'No host'
- const daemon = daemons.find((entry) => entry.serverId === activeServerId)
- const trimmed = daemon?.label?.trim()
- return trimmed && trimmed.length > 0 ? trimmed : activeServerId
- }, [activeServerId, daemons])
+ if (!activeDaemon) return 'No host'
+ const trimmed = activeDaemon.label?.trim()
+ return trimmed && trimmed.length > 0 ? trimmed : activeDaemon.serverId
+ }, [activeDaemon])
const activeHostStatus = activeServerId
? (runtime.getSnapshot(activeServerId)?.connectionStatus ?? 'connecting')
: 'idle'
@@ -150,33 +151,11 @@ export function LeftSidebar({ selectedAgentId }: LeftSidebarProps) {
}, [closeToAgent])
const handleCreateAgentClean = useCallback(() => {
- let targetServerId = activeServerId
- let targetWorkingDir: string | null = null
-
- const selectedAgent = resolveSelectedAgentForNewAgent({
- pathname,
- selectedAgentId,
- })
- if (selectedAgent) {
- targetServerId = selectedAgent.serverId
- const agent = useSessionStore
- .getState()
- .sessions[selectedAgent.serverId]?.agents?.get(selectedAgent.agentId)
- const cwd = agent?.cwd?.trim()
- if (cwd) {
- const checkout =
- queryClient.getQueryData(
- checkoutStatusQueryKey(selectedAgent.serverId, cwd)
- ) ?? null
- targetWorkingDir = resolveNewAgentWorkingDir(cwd, checkout)
- }
- }
-
- if (!targetServerId) {
+ if (!activeServerId) {
return
}
- router.push(buildNewAgentRoute(targetServerId, targetWorkingDir) as any)
- }, [activeServerId, pathname, selectedAgentId])
+ router.push(buildHostNewAgentRoute(activeServerId) as any)
+ }, [activeServerId])
// Mobile: close sidebar and navigate
const handleCreateAgentCleanMobile = useCallback(() => {
diff --git a/packages/app/src/components/welcome-screen.tsx b/packages/app/src/components/welcome-screen.tsx
index 607523e49..49cdd87fb 100644
--- a/packages/app/src/components/welcome-screen.tsx
+++ b/packages/app/src/components/welcome-screen.tsx
@@ -11,6 +11,7 @@ import { PairLinkModal } from "./pair-link-modal";
import { NameHostModal } from "./name-host-modal";
import { resolveAppVersion } from "@/utils/app-version";
import { formatVersionWithPrefix } from "@/desktop/updates/desktop-updates";
+import { buildHostRootRoute } from "@/utils/host-routes";
const styles = StyleSheet.create((theme) => ({
container: {
@@ -105,7 +106,7 @@ export function WelcomeScreen({ onHostAdded }: WelcomeScreenProps) {
const finishOnboarding = useCallback(
(serverId: string) => {
- router.replace(`/h/${encodeURIComponent(serverId)}` as any);
+ router.replace(buildHostRootRoute(serverId) as any);
},
[router]
);
diff --git a/packages/app/src/hooks/use-command-center.ts b/packages/app/src/hooks/use-command-center.ts
index 8c80a80f6..054d55cd6 100644
--- a/packages/app/src/hooks/use-command-center.ts
+++ b/packages/app/src/hooks/use-command-center.ts
@@ -3,21 +3,12 @@ import type { TextInput } from "react-native";
import { router, usePathname, type Href } from "expo-router";
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
import { useAggregatedAgents, type AggregatedAgent } from "@/hooks/use-aggregated-agents";
-import { useSessionStore } from "@/stores/session-store";
-import {
- checkoutStatusQueryKey,
- type CheckoutStatusPayload,
-} from "@/hooks/use-checkout-status-query";
-import { queryClient } from "@/query/query-client";
import {
clearCommandCenterFocusRestoreElement,
takeCommandCenterFocusRestoreElement,
} from "@/utils/command-center-focus-restore";
import {
- buildNewAgentRoute,
- resolveNewAgentWorkingDir,
-} from "@/utils/new-agent-routing";
-import {
+ buildHostNewAgentRoute,
buildHostWorkspaceAgentTabRoute,
buildHostSettingsRoute,
parseHostAgentRouteFromPathname,
@@ -63,12 +54,6 @@ function parseAgentKeyFromPathname(pathname: string): string | null {
return `${match.serverId}:${match.agentId}`;
}
-function parseAgentRouteFromPathname(
- pathname: string
-): { serverId: string; agentId: string } | null {
- return parseHostAgentRouteFromPathname(pathname);
-}
-
type CommandCenterActionDefinition = {
id: string;
title: string;
@@ -157,24 +142,7 @@ export function useCommandCenter() {
const newAgentRoute = useMemo(() => {
const serverIdFromPath =
parseServerIdFromPathname(pathname) ?? fallbackServerId;
- const routeAgent = parseAgentRouteFromPathname(pathname);
- if (!routeAgent) {
- return serverIdFromPath ? (buildNewAgentRoute(serverIdFromPath) as Href) : "/";
- }
-
- const { serverId, agentId } = routeAgent;
- const currentAgent = useSessionStore.getState().sessions[serverId]?.agents?.get(agentId);
- const cwd = currentAgent?.cwd?.trim();
- if (!cwd) {
- return buildNewAgentRoute(serverId) as Href;
- }
-
- const checkout =
- queryClient.getQueryData(
- checkoutStatusQueryKey(serverId, cwd)
- ) ?? null;
- const workingDir = resolveNewAgentWorkingDir(cwd, checkout);
- return buildNewAgentRoute(serverId, workingDir) as Href;
+ return serverIdFromPath ? (buildHostNewAgentRoute(serverIdFromPath) as Href) : "/";
}, [fallbackServerId, pathname]);
const settingsRoute = useMemo(() => {
diff --git a/packages/app/src/hooks/use-keyboard-shortcuts.ts b/packages/app/src/hooks/use-keyboard-shortcuts.ts
index 4de792dba..846f56564 100644
--- a/packages/app/src/hooks/use-keyboard-shortcuts.ts
+++ b/packages/app/src/hooks/use-keyboard-shortcuts.ts
@@ -6,16 +6,7 @@ import { useSessionStore } from "@/stores/session-store";
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
import { setCommandCenterFocusRestoreElement } from "@/utils/command-center-focus-restore";
import {
- checkoutStatusQueryKey,
- type CheckoutStatusPayload,
-} from "@/hooks/use-checkout-status-query";
-import { queryClient } from "@/query/query-client";
-import {
- buildNewAgentRoute,
- resolveSelectedAgentForNewAgent,
- resolveNewAgentWorkingDir,
-} from "@/utils/new-agent-routing";
-import {
+ buildHostNewAgentRoute,
buildHostWorkspaceRoute,
parseHostAgentRouteFromPathname,
parseHostWorkspaceRouteFromPathname,
@@ -85,26 +76,6 @@ export function useKeyboardShortcuts({
const navigateToNewAgent = (): boolean => {
let targetServerId = parseServerIdFromPathname(pathname);
- let targetWorkingDir: string | null = null;
- const selectedAgent = resolveSelectedAgentForNewAgent({
- pathname,
- selectedAgentId,
- });
- if (selectedAgent) {
- targetServerId = selectedAgent.serverId;
- const agent = useSessionStore
- .getState()
- .sessions[selectedAgent.serverId]
- ?.agents?.get(selectedAgent.agentId);
- const cwd = agent?.cwd?.trim();
- if (cwd) {
- const checkout =
- queryClient.getQueryData(
- checkoutStatusQueryKey(selectedAgent.serverId, cwd)
- ) ?? null;
- targetWorkingDir = resolveNewAgentWorkingDir(cwd, checkout);
- }
- }
if (!targetServerId) {
const sessionServerIds = Object.keys(useSessionStore.getState().sessions);
@@ -115,7 +86,7 @@ export function useKeyboardShortcuts({
return false;
}
- router.push(buildNewAgentRoute(targetServerId, targetWorkingDir) as any);
+ router.push(buildHostNewAgentRoute(targetServerId) as any);
return true;
};
diff --git a/packages/app/src/screens/agent/agent-ready-screen.tsx b/packages/app/src/screens/agent/agent-ready-screen.tsx
index 5a16ee9e6..066676457 100644
--- a/packages/app/src/screens/agent/agent-ready-screen.tsx
+++ b/packages/app/src/screens/agent/agent-ready-screen.tsx
@@ -60,6 +60,7 @@ import { shouldClearAgentAttentionOnView } from "@/utils/agent-attention";
import type { DaemonClient } from "@server/client/daemon-client";
import { useExplorerOpenGesture } from "@/hooks/use-explorer-open-gesture";
import type { ExplorerCheckoutContext } from "@/stores/panel-store";
+import { buildHostRootRoute } from "@/utils/host-routes";
const EMPTY_STREAM_ITEMS: StreamItem[] = [];
const IS_DEV = Boolean((globalThis as { __DEV__?: boolean }).__DEV__);
@@ -528,7 +529,7 @@ function AgentScreenContent({
return;
}
hasRedirectedArchivedAgentRef.current = true;
- const route: Href = `/h/${encodeURIComponent(serverId)}` as Href;
+ const route: Href = buildHostRootRoute(serverId) as Href;
router.replace(route);
}, [agent?.archivedAt, resolvedAgentId, router, serverId]);
diff --git a/packages/app/src/screens/agents-screen.tsx b/packages/app/src/screens/agents-screen.tsx
index a63d1be5c..415d4752f 100644
--- a/packages/app/src/screens/agents-screen.tsx
+++ b/packages/app/src/screens/agents-screen.tsx
@@ -5,6 +5,7 @@ import { BackHeader } from "@/components/headers/back-header";
import { AgentList } from "@/components/agent-list";
import { useAllAgentsList } from "@/hooks/use-all-agents-list";
import { router } from "expo-router";
+import { buildHostRootRoute } from "@/utils/host-routes";
export function AgentsScreen({ serverId }: { serverId: string }) {
const { agents, isRevalidating, refreshAll } = useAllAgentsList({
@@ -43,7 +44,7 @@ export function AgentsScreen({ serverId }: { serverId: string }) {
router.replace(`/h/${encodeURIComponent(serverId)}` as any)}
+ onBack={() => router.replace(buildHostRootRoute(serverId) as any)}
/>
void) | null>(null);
const setPendingCreateAttempt = useCreateFlowStore((state) => state.setPending);
const updatePendingAgentId = useCreateFlowStore((state) => state.updateAgentId);
@@ -322,64 +325,75 @@ export function WorkspaceDraftAgentTab({
workspaceId,
]);
+ const handleFilesDropped = useCallback((files: ImageAttachment[]) => {
+ addImagesRef.current?.(files);
+ }, []);
+
+ const handleAddImagesCallback = useCallback((addImages: (images: ImageAttachment[]) => void) => {
+ addImagesRef.current = addImages;
+ }, []);
+
return (
-
-
- {machine.tag === "creating" && draftAgent ? (
-
-
-
- ) : (
-
-
-
+
+
+ {machine.tag === "creating" && draftAgent ? (
+
+
-
- {formErrorMessage ? (
-
- {formErrorMessage}
-
- ) : null}
-
- )}
-
+ ) : (
+
+
+
-
- dispatch({ type: "DRAFT_SET_PROMPT", text: next })}
- autoFocus={machine.tag === "draft"}
- commandDraftConfig={draftCommandConfig}
- draftId={draftId}
- />
+ {formErrorMessage ? (
+
+ {formErrorMessage}
+
+ ) : null}
+
+
+ )}
+
+
+
+ dispatch({ type: "DRAFT_SET_PROMPT", text: next })}
+ autoFocus={machine.tag === "draft"}
+ onAddImages={handleAddImagesCallback}
+ commandDraftConfig={draftCommandConfig}
+ draftId={draftId}
+ />
+
-
+
);
}
diff --git a/packages/app/src/utils/host-routes.test.ts b/packages/app/src/utils/host-routes.test.ts
index 173489ba5..24a075feb 100644
--- a/packages/app/src/utils/host-routes.test.ts
+++ b/packages/app/src/utils/host-routes.test.ts
@@ -1,5 +1,7 @@
import { describe, expect, it } from "vitest";
import {
+ buildHostNewAgentRoute,
+ buildHostRootRoute,
buildHostWorkspaceAgentTabRoute,
buildHostWorkspaceFileTabRoute,
buildHostWorkspaceRoute,
@@ -70,6 +72,14 @@ describe("workspace route parsing", () => {
expect(buildHostWorkspaceRoute("local", "/tmp/repo")).toBe("/h/local/workspace/L3RtcC9yZXBv");
});
+ it("builds host root routes", () => {
+ expect(buildHostRootRoute("local")).toBe("/h/local");
+ });
+
+ it("builds host new-agent routes", () => {
+ expect(buildHostNewAgentRoute("local")).toBe("/h/local/new-agent");
+ });
+
it("builds workspace agent tab routes", () => {
expect(buildHostWorkspaceAgentTabRoute("local", "/tmp/repo", "agent-1")).toBe(
"/h/local/workspace/L3RtcC9yZXBv/tab/agent_agent-1"
diff --git a/packages/app/src/utils/host-routes.ts b/packages/app/src/utils/host-routes.ts
index 42bbef691..c873be8a3 100644
--- a/packages/app/src/utils/host-routes.ts
+++ b/packages/app/src/utils/host-routes.ts
@@ -359,25 +359,41 @@ export function buildHostAgentDetailRoute(
if (!normalizedServerId || !normalizedAgentId) {
return "/";
}
- return `/h/${encodeSegment(normalizedServerId)}/agent/${encodeSegment(
+ return `${buildHostRootRoute(normalizedServerId)}/agent/${encodeSegment(
normalizedAgentId
)}`;
}
-export function buildHostAgentsRoute(serverId: string): string {
+export function buildHostRootRoute(serverId: string): string {
const normalized = trimNonEmpty(serverId);
if (!normalized) {
return "/";
}
- return `/h/${encodeSegment(normalized)}/agents`;
+ return `/h/${encodeSegment(normalized)}`;
+}
+
+export function buildHostAgentsRoute(serverId: string): string {
+ const base = buildHostRootRoute(serverId);
+ if (base === "/") {
+ return "/";
+ }
+ return `${base}/agents`;
+}
+
+export function buildHostNewAgentRoute(serverId: string): string {
+ const base = buildHostRootRoute(serverId);
+ if (base === "/") {
+ return "/";
+ }
+ return `${base}/new-agent`;
}
export function buildHostSettingsRoute(serverId: string): string {
- const normalized = trimNonEmpty(serverId);
- if (!normalized) {
+ const base = buildHostRootRoute(serverId);
+ if (base === "/") {
return "/";
}
- return `/h/${encodeSegment(normalized)}/settings`;
+ return `${base}/settings`;
}
export function mapPathnameToServer(
@@ -390,13 +406,16 @@ export function mapPathnameToServer(
}
const suffix = pathname.replace(/^\/h\/[^/]+\/?/, "");
- const base = `/h/${encodeSegment(normalized)}`;
+ const base = buildHostRootRoute(normalized);
if (suffix.startsWith("settings")) {
return `${base}/settings`;
}
if (suffix.startsWith("agents")) {
return `${base}/agents`;
}
+ if (suffix.startsWith("new-agent")) {
+ return `${base}/new-agent`;
+ }
if (suffix.startsWith("workspace/")) {
return `${base}/${suffix}`;
}
diff --git a/packages/app/src/utils/notification-routing.ts b/packages/app/src/utils/notification-routing.ts
index 6f99fe257..11befbfb9 100644
--- a/packages/app/src/utils/notification-routing.ts
+++ b/packages/app/src/utils/notification-routing.ts
@@ -1,5 +1,6 @@
import {
buildHostAgentDetailRoute,
+ buildHostRootRoute,
buildHostWorkspaceAgentTabRoute,
} from "@/utils/host-routes";
@@ -40,7 +41,7 @@ export function buildNotificationRoute(data: NotificationData): string {
return buildHostAgentDetailRoute(serverId, agentId);
}
if (serverId) {
- return `/h/${encodeURIComponent(serverId)}`;
+ return buildHostRootRoute(serverId);
}
return "/";
}
diff --git a/packages/server/src/server/worktree-bootstrap.ts b/packages/server/src/server/worktree-bootstrap.ts
index 731955301..8a233a83e 100644
--- a/packages/server/src/server/worktree-bootstrap.ts
+++ b/packages/server/src/server/worktree-bootstrap.ts
@@ -1,10 +1,14 @@
import { v4 as uuidv4 } from "uuid";
import type { Logger } from "pino";
+import { exec } from "node:child_process";
+import { promisify } from "node:util";
+import { sep } from "node:path";
import type { TerminalManager } from "../terminal/terminal-manager.js";
import type { TerminalSession } from "../terminal/terminal.js";
import {
createWorktree,
getWorktreeTerminalSpecs,
+ listPaseoWorktrees,
resolveWorktreeRuntimeEnv,
runWorktreeSetupCommands,
WorktreeSetupError,
@@ -42,6 +46,12 @@ export interface CreateAgentWorktreeOptions {
const MAX_WORKTREE_SETUP_COMMAND_OUTPUT_BYTES = 64 * 1024;
const WORKTREE_SETUP_TRUNCATION_MARKER = "\n...