diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx
index c785066f0..34cde2bd7 100644
--- a/packages/app/src/app/_layout.tsx
+++ b/packages/app/src/app/_layout.tsx
@@ -608,12 +608,7 @@ export default function RootLayout() {
>
-
- `${params?.serverId}:${params?.workspaceId}`
- }
- />
+
()
+ const globalParams = useGlobalSearchParams<{
open?: string | string[]
}>()
const serverValue = Array.isArray(params.serverId) ? params.serverId[0] : params.serverId
@@ -17,7 +19,7 @@ export default function HostWorkspaceLayout() {
: params.workspaceId
const serverId = serverValue?.trim() ?? ''
const workspaceId = workspaceValue ? (decodeWorkspaceIdFromPathSegment(workspaceValue) ?? '') : ''
- const openValue = Array.isArray(params.open) ? params.open[0] : params.open
+ const openValue = Array.isArray(globalParams.open) ? globalParams.open[0] : globalParams.open
const openIntent = parseWorkspaceOpenIntent(openValue)
return (
diff --git a/packages/app/src/components/agent-list.tsx b/packages/app/src/components/agent-list.tsx
index 1f86f3dab..bcce7fdef 100644
--- a/packages/app/src/components/agent-list.tsx
+++ b/packages/app/src/components/agent-list.tsx
@@ -9,7 +9,7 @@ import {
} from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { useCallback, useMemo, useState, type ReactElement } from 'react'
-import { router, usePathname, type Href } from 'expo-router'
+import { router, type Href } from 'expo-router'
import { StyleSheet, UnistylesRuntime, useUnistyles } from 'react-native-unistyles'
import { formatTimeAgo } from '@/utils/time'
import { shortenPath } from '@/utils/shorten-path'
@@ -250,7 +250,6 @@ export function AgentList({
showAttentionIndicator = true,
}: AgentListProps) {
const { theme } = useUnistyles()
- const pathname = usePathname()
const insets = useSafeAreaInsets()
const [actionAgent, setActionAgent] = useState(null)
const isMobile = UnistylesRuntime.breakpoint === 'xs' || UnistylesRuntime.breakpoint === 'sm'
@@ -270,15 +269,13 @@ export function AgentList({
const serverId = agent.serverId
const agentId = agent.id
- const shouldReplace = pathname.startsWith('/h/')
- const navigate = shouldReplace ? router.replace : router.push
onAgentSelect?.()
const route: Href = buildHostWorkspaceAgentRoute(serverId, agent.cwd, agentId) as Href
- navigate(route)
+ router.navigate(route)
},
- [isActionSheetVisible, pathname, onAgentSelect]
+ [isActionSheetVisible, onAgentSelect]
)
const handleAgentLongPress = useCallback((agent: AggregatedAgent) => {
diff --git a/packages/app/src/hooks/use-command-center.ts b/packages/app/src/hooks/use-command-center.ts
index 921b192ea..7e140dfc9 100644
--- a/packages/app/src/hooks/use-command-center.ts
+++ b/packages/app/src/hooks/use-command-center.ts
@@ -14,7 +14,6 @@ import {
import {
buildHostWorkspaceAgentRoute,
buildHostSettingsRoute,
- parseHostAgentRouteFromPathname,
parseServerIdFromPathname,
} from "@/utils/host-routes";
import type { ShortcutKey } from "@/utils/format-shortcut";
@@ -196,8 +195,6 @@ export function useCommandCenter() {
const handleSelectAgent = useCallback(
(agent: AggregatedAgent) => {
didNavigateRef.current = true;
- const shouldReplace = Boolean(parseHostAgentRouteFromPathname(pathname));
- const navigate = shouldReplace ? router.replace : router.push;
// Don't restore focus back to the prior element after we navigate.
clearCommandCenterFocusRestoreElement();
@@ -207,9 +204,9 @@ export function useCommandCenter() {
agent.cwd,
agent.id
) as Href;
- navigate(route);
+ router.navigate(route);
},
- [pathname, setOpen]
+ [setOpen]
);
const openProjectPicker = useOpenProjectPicker(activeServerId);
diff --git a/packages/app/src/screens/workspace/workspace-agent-visibility.test.ts b/packages/app/src/screens/workspace/workspace-agent-visibility.test.ts
index a13b6d0b4..80b61dfa8 100644
--- a/packages/app/src/screens/workspace/workspace-agent-visibility.test.ts
+++ b/packages/app/src/screens/workspace/workspace-agent-visibility.test.ts
@@ -3,6 +3,7 @@ import type { Agent } from "@/stores/session-store";
import {
canOpenAgentTabFromRoute,
deriveWorkspaceAgentVisibility,
+ shouldPruneWorkspaceAgentTab,
} from "@/screens/workspace/workspace-agent-visibility";
function makeAgent(input: {
@@ -104,6 +105,33 @@ describe("workspace agent visibility", () => {
).toBe(true);
});
+ it("does not prune archived agent tabs when the workspace lookup still contains the agent", () => {
+ const archivedAgent = makeAgent({
+ id: "archived-agent",
+ cwd: "/repo/worktree",
+ archivedAt: new Date("2026-03-04T00:01:00.000Z"),
+ });
+ const lookup = new Map([[archivedAgent.id, archivedAgent]]);
+
+ expect(
+ shouldPruneWorkspaceAgentTab({
+ agentId: "archived-agent",
+ agentsHydrated: true,
+ workspaceAgentLookup: lookup,
+ })
+ ).toBe(false);
+ });
+
+ it("prunes agent tabs once agents are hydrated and the agent is missing from the workspace lookup", () => {
+ expect(
+ shouldPruneWorkspaceAgentTab({
+ agentId: "missing-agent",
+ agentsHydrated: true,
+ workspaceAgentLookup: new Map(),
+ })
+ ).toBe(true);
+ });
+
it("sorts same-createdAt agents by lastActivityAt descending", () => {
const workspaceId = "/repo/worktree";
const createdAt = new Date("2026-03-04T00:00:00.000Z");
diff --git a/packages/app/src/screens/workspace/workspace-agent-visibility.ts b/packages/app/src/screens/workspace/workspace-agent-visibility.ts
index 65ff212f2..db79bd9f9 100644
--- a/packages/app/src/screens/workspace/workspace-agent-visibility.ts
+++ b/packages/app/src/screens/workspace/workspace-agent-visibility.ts
@@ -72,3 +72,17 @@ export function canOpenAgentTabFromRoute(input: {
}
return input.workspaceAgentLookup.has(input.agentId);
}
+
+export function shouldPruneWorkspaceAgentTab(input: {
+ agentId: string;
+ agentsHydrated: boolean;
+ workspaceAgentLookup: Map;
+}): boolean {
+ if (!input.agentId.trim()) {
+ return false;
+ }
+ if (!input.agentsHydrated) {
+ return false;
+ }
+ return !input.workspaceAgentLookup.has(input.agentId);
+}
diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx
index 5254a2d6c..274536eec 100644
--- a/packages/app/src/screens/workspace/workspace-screen.tsx
+++ b/packages/app/src/screens/workspace/workspace-screen.tsx
@@ -95,8 +95,12 @@ import {
} from "@/screens/workspace/workspace-header-source";
import {
deriveWorkspaceAgentVisibility,
+ shouldPruneWorkspaceAgentTab,
} from "@/screens/workspace/workspace-agent-visibility";
-import { deriveWorkspacePaneState } from "@/screens/workspace/workspace-pane-state";
+import {
+ deriveWorkspacePaneState,
+ type WorkspaceDerivedTab,
+} from "@/screens/workspace/workspace-pane-state";
import {
buildWorkspacePaneContentModel,
WorkspacePaneContent,
@@ -148,6 +152,34 @@ function buildOpenIntentKey(input: {
return `${input.serverId}:${input.workspaceId}:${openParam}`;
}
+function openIntentMatchesActiveTab(input: {
+ openIntent?: WorkspaceOpenIntent | null;
+ activeTab: WorkspaceDerivedTab | null;
+}): boolean {
+ const { openIntent, activeTab } = input;
+ if (!openIntent || !activeTab) {
+ return false;
+ }
+
+ const target = activeTab.descriptor.target;
+ if (openIntent.kind !== target.kind) {
+ return false;
+ }
+ if (openIntent.kind === "agent" && target.kind === "agent") {
+ return target.agentId === openIntent.agentId;
+ }
+ if (openIntent.kind === "terminal" && target.kind === "terminal") {
+ return target.terminalId === openIntent.terminalId;
+ }
+ if (openIntent.kind === "file" && target.kind === "file") {
+ return target.path === openIntent.path;
+ }
+ if (openIntent.kind === "draft" && target.kind === "draft") {
+ return openIntent.draftId === "new" || target.draftId === openIntent.draftId;
+ }
+ return false;
+}
+
function getFallbackTabOptionLabel(tab: WorkspaceTabDescriptor): string {
if (tab.target.kind === "draft") {
return "New Agent";
@@ -796,6 +828,7 @@ function WorkspaceScreenContent({
const reorderWorkspaceTabsInPane = useWorkspaceLayoutStore((state) => state.reorderTabsInPane);
const pendingByDraftId = useCreateFlowStore((state) => state.pendingByDraftId);
const consumedOpenIntentsRef = useRef(new Set());
+ const reopenedArchivedOpenIntentsRef = useRef(new Set());
const pendingCloseTabIdsRef = useRef(new Set());
const [resolvedOpenIntentKey, setResolvedOpenIntentKey] = useState(null);
const currentOpenIntentKey = useMemo(
@@ -881,6 +914,43 @@ function WorkspaceScreenContent({
}
}, [currentOpenIntentKey, resolvedOpenIntentKey]);
+ useEffect(() => {
+ if (!client || !isConnected || openIntent?.kind !== "agent" || !currentOpenIntentKey) {
+ return;
+ }
+
+ const agentId = openIntent.agentId.trim();
+ if (!agentId) {
+ return;
+ }
+ if (reopenedArchivedOpenIntentsRef.current.has(currentOpenIntentKey)) {
+ return;
+ }
+
+ const agent = agentsById.get(agentId) ?? null;
+ if (!agent?.archivedAt) {
+ return;
+ }
+
+ reopenedArchivedOpenIntentsRef.current.add(currentOpenIntentKey);
+ void client.refreshAgent(agentId).catch((error) => {
+ console.warn("[WorkspaceScreen] Failed to reopen archived agent", {
+ serverId: normalizedServerId,
+ workspaceId: normalizedWorkspaceId,
+ agentId,
+ error,
+ });
+ });
+ }, [
+ agentsById,
+ client,
+ currentOpenIntentKey,
+ isConnected,
+ normalizedServerId,
+ normalizedWorkspaceId,
+ openIntent,
+ ]);
+
useEffect(() => {
if (!openIntent || !persistenceKey) {
return;
@@ -904,9 +974,6 @@ function WorkspaceScreenContent({
...(draftId === "new" ? {} : { draftId }),
focus: true,
});
- if (tabId) {
- setResolvedOpenIntentKey(intentKey);
- }
return;
}
@@ -918,15 +985,15 @@ function WorkspaceScreenContent({
: { kind: "file", path: openIntent.path };
const tabId = openWorkspaceTab(persistenceKey, target);
if (tabId) {
- setResolvedOpenIntentKey(intentKey);
+ focusWorkspaceTab(persistenceKey, tabId);
}
}, [
currentOpenIntentKey,
+ focusWorkspaceTab,
openIntent,
openWorkspaceDraftTab,
openWorkspaceTab,
persistenceKey,
- resolvedOpenIntentKey,
]);
const unresolvedOpenIntent = currentOpenIntentKey && resolvedOpenIntentKey !== currentOpenIntentKey
@@ -956,7 +1023,6 @@ function WorkspaceScreenContent({
return;
}
- const agentIds = new Set(workspaceAgents.map((agent) => agent.id));
const terminalIds = new Set(terminals.map((terminal) => terminal.id));
const hasActivePendingDraftCreateInWorkspace = uiTabs.some((tab) => {
if (tab.target.kind !== "draft") {
@@ -989,7 +1055,15 @@ function WorkspaceScreenContent({
const canPruneAgentTabs = hasHydratedAgents;
const canPruneTerminalTabs = terminalsQuery.isSuccess;
for (const tab of uiTabs) {
- if (canPruneAgentTabs && tab.target.kind === "agent" && !agentIds.has(tab.target.agentId)) {
+ if (
+ canPruneAgentTabs &&
+ tab.target.kind === "agent" &&
+ shouldPruneWorkspaceAgentTab({
+ agentId: tab.target.agentId,
+ agentsHydrated: hasHydratedAgents,
+ workspaceAgentLookup: agentsById,
+ })
+ ) {
closeWorkspaceTab(persistenceKey, tab.tabId);
}
if (
@@ -1013,6 +1087,24 @@ function WorkspaceScreenContent({
]);
const activeTabId = resolvedPaneTabState.activeTabId;
+ const activeTab = resolvedPaneTabState.activeTab;
+
+ useEffect(() => {
+ if (!openIntent || !currentOpenIntentKey) {
+ return;
+ }
+ if (resolvedOpenIntentKey === currentOpenIntentKey) {
+ return;
+ }
+ if (
+ openIntentMatchesActiveTab({
+ openIntent,
+ activeTab,
+ })
+ ) {
+ setResolvedOpenIntentKey(currentOpenIntentKey);
+ }
+ }, [activeTab, currentOpenIntentKey, openIntent, resolvedOpenIntentKey]);
useEffect(() => {
if (!activeTabId || !persistenceKey) {
@@ -1021,8 +1113,6 @@ function WorkspaceScreenContent({
focusWorkspaceTab(persistenceKey, activeTabId);
}, [activeTabId, focusWorkspaceTab, persistenceKey]);
- const activeTab = resolvedPaneTabState.activeTab;
-
const tabs = useMemo(
() => resolvedPaneTabState.tabs.map((tab) => tab.descriptor),
[resolvedPaneTabState.tabs]