fix: cross-directory session visibility and API routing

fix: remove roots filter from session list to show all sessions
This commit is contained in:
Den
2026-05-26 01:30:00 -07:00
committed by GitHub
4 changed files with 50 additions and 37 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useCallback, useState, useRef } from "react" import { useCallback, useState, useRef } from "react"
import { import {
View, View,
Text, Text,
@@ -14,10 +14,11 @@ import {
KeyboardAvoidingView, KeyboardAvoidingView,
Platform, Platform,
} from "react-native" } from "react-native"
import { router } from "expo-router" import { router, useFocusEffect } from "expo-router"
import { Ionicons } from "@expo/vector-icons" import { Ionicons } from "@expo/vector-icons"
import { useSessions } from "../../src/stores/sessions" import { useSessions } from "../../src/stores/sessions"
import { useConnections } from "../../src/stores/connections" import { useConnections } from "../../src/stores/connections"
import { useCatalog } from "../../src/stores/catalog"
import type BottomSheet from "@gorhom/bottom-sheet" import type BottomSheet from "@gorhom/bottom-sheet"
import type { Session } from "../../src/lib/sdk" import type { Session } from "../../src/lib/sdk"
import { DirectorySwitcher } from "../../src/components/chat" import { DirectorySwitcher } from "../../src/components/chat"
@@ -123,6 +124,7 @@ export default function SessionsScreen() {
addRecentDirectory, addRecentDirectory,
recentDirectories, recentDirectories,
} = useConnections() } = useConnections()
const loadCatalog = useCatalog((s) => s.load)
const dirSheetRef = useRef<BottomSheet>(null) const dirSheetRef = useRef<BottomSheet>(null)
const [refreshing, setRefreshing] = useState(false) const [refreshing, setRefreshing] = useState(false)
@@ -131,16 +133,19 @@ export default function SessionsScreen() {
await switchDirectory(dir) await switchDirectory(dir)
loadSessions() loadSessions()
refreshProject() refreshProject()
loadCatalog()
}, },
[switchDirectory, loadSessions, refreshProject], [switchDirectory, loadSessions, refreshProject, loadCatalog],
) )
useEffect(() => { useFocusEffect(
useCallback(() => {
if (client) { if (client) {
loadSessions() loadSessions()
refreshProject() refreshProject()
} }
}, [client]) }, [client, loadSessions, refreshProject]),
)
const onRefresh = useCallback(async () => { const onRefresh = useCallback(async () => {
setRefreshing(true) setRefreshing(true)
@@ -155,12 +160,14 @@ export default function SessionsScreen() {
const submitRename = useCallback(async () => { const submitRename = useCallback(async () => {
const title = renameText.trim() const title = renameText.trim()
if (!title || !renaming || !client) return if (!title || !renaming) return
await client.session.update(renaming.id, { title }) const renameClient = renaming.directory ? (clientForDirectory(renaming.directory) ?? client) : client
if (!renameClient) return
await renameClient.session.update(renaming.id, { title })
setRenaming(null) setRenaming(null)
setRenameText("") setRenameText("")
loadSessions() loadSessions()
}, [renaming, renameText, client, loadSessions]) }, [renaming, renameText, client, clientForDirectory, loadSessions])
const handleDelete = useCallback( const handleDelete = useCallback(
(session: Session) => { (session: Session) => {

View File

@@ -107,7 +107,13 @@ export default function SessionScreen() {
const isSending = useSessions((s) => !!(currentSession && s.sending[currentSession.id])) const isSending = useSessions((s) => !!(currentSession && s.sending[currentSession.id]))
const { authenticateForMessage } = useAuth() const { authenticateForMessage } = useAuth()
const { client } = useConnections() const { client, clientForDirectory } = useConnections()
// Use directory-aware client for sessions that belong to a project other than the active one
const sessionClient = useMemo(
() => (currentSession?.directory ? (clientForDirectory(currentSession.directory) ?? client) : client),
[currentSession?.directory, clientForDirectory, client],
)
// Catalog // Catalog
const catalog = useCatalog() const catalog = useCatalog()
@@ -170,9 +176,11 @@ export default function SessionScreen() {
selectSession(id, directory).then(() => { selectSession(id, directory).then(() => {
// Re-fetch pending permissions/questions from the server to recover from // Re-fetch pending permissions/questions from the server to recover from
// missed SSE events or failed optimistic removals // missed SSE events or failed optimistic removals
if (client) refreshPending(client, id) const connState = useConnections.getState()
const c = directory ? (connState.clientForDirectory(directory) ?? connState.client) : connState.client
if (c) refreshPending(c, id)
}) })
}, [id]) }, [id, directory])
// Sync model chip from latest assistant message // Sync model chip from latest assistant message
useEffect(() => { useEffect(() => {
@@ -320,8 +328,8 @@ export default function SessionScreen() {
const [cmdName, ...args] = text.split(" ") const [cmdName, ...args] = text.split(" ")
const name = cmdName.slice(1) const name = cmdName.slice(1)
const match = serverCommands.find((c) => c.name === name) const match = serverCommands.find((c) => c.name === name)
if (match && client && currentSession) { if (match && sessionClient && currentSession) {
client.session sessionClient.session
.command(currentSession.id, { .command(currentSession.id, {
command: name, command: name,
arguments: args.join(" "), arguments: args.join(" "),
@@ -359,7 +367,7 @@ export default function SessionScreen() {
}, [loadingMore]) }, [loadingMore])
const handlePermissionReply = async (requestID: string, reply: "once" | "always" | "reject") => { const handlePermissionReply = async (requestID: string, reply: "once" | "always" | "reject") => {
if (!client || !sessionID) return if (!sessionClient || !sessionID) return
// Snapshot for rollback // Snapshot for rollback
const snapshot = useEvents.getState().permissions[sessionID] || [] const snapshot = useEvents.getState().permissions[sessionID] || []
// Optimistically remove from UI // Optimistically remove from UI
@@ -370,7 +378,7 @@ export default function SessionScreen() {
}, },
})) }))
try { try {
await client.permission.reply(requestID, reply) await sessionClient.permission.reply(requestID, reply)
} catch (err) { } catch (err) {
console.error("Permission reply failed:", err) console.error("Permission reply failed:", err)
// Restore the prompt so the user can retry // Restore the prompt so the user can retry
@@ -382,7 +390,7 @@ export default function SessionScreen() {
} }
const handleQuestionReply = async (requestID: string, answers: string[][]) => { const handleQuestionReply = async (requestID: string, answers: string[][]) => {
if (!client || !sessionID) return if (!sessionClient || !sessionID) return
const snapshot = useEvents.getState().questions[sessionID] || [] const snapshot = useEvents.getState().questions[sessionID] || []
useEvents.setState((state) => ({ useEvents.setState((state) => ({
questions: { questions: {
@@ -391,7 +399,7 @@ export default function SessionScreen() {
}, },
})) }))
try { try {
await client.question.reply(requestID, answers) await sessionClient.question.reply(requestID, answers)
} catch (err) { } catch (err) {
console.error("Question reply failed:", err) console.error("Question reply failed:", err)
useEvents.setState((state) => ({ useEvents.setState((state) => ({
@@ -402,7 +410,7 @@ export default function SessionScreen() {
} }
const handleQuestionReject = async (requestID: string) => { const handleQuestionReject = async (requestID: string) => {
if (!client || !sessionID) return if (!sessionClient || !sessionID) return
const snapshot = useEvents.getState().questions[sessionID] || [] const snapshot = useEvents.getState().questions[sessionID] || []
useEvents.setState((state) => ({ useEvents.setState((state) => ({
questions: { questions: {
@@ -411,7 +419,7 @@ export default function SessionScreen() {
}, },
})) }))
try { try {
await client.question.reject(requestID) await sessionClient.question.reject(requestID)
} catch (err) { } catch (err) {
console.error("Question reject failed:", err) console.error("Question reject failed:", err)
useEvents.setState((state) => ({ useEvents.setState((state) => ({

View File

@@ -85,16 +85,6 @@ export const useCatalog = create<CatalogState>((set, get) => ({
.filter((p) => p.models.length > 0) .filter((p) => p.models.length > 0)
: [] : []
console.log(
"[catalog] loaded:",
agents.length,
"agents,",
commands.length,
"commands,",
providers.length,
"providers (" + providers.reduce((n, p) => n + p.models.length, 0) + " models)",
)
// Filter out hidden agents // Filter out hidden agents
const visible = agents.filter((a) => !a.hidden) const visible = agents.filter((a) => !a.hidden)

View File

@@ -73,15 +73,22 @@ export const useSessions = create<SessionsState>((set, get) => ({
error: null, error: null,
loadSessions: async () => { loadSessions: async () => {
const client = useConnections.getState().client const connState = useConnections.getState()
if (!client) { if (!connState.client) {
set({ error: "No active connection" }) set({ error: "No active connection" })
return return
} }
// When no project directory is explicitly selected, use the server home path so the
// session list shows all recent sessions across all projects — not just the server's CWD.
const listClient =
!connState.activeConnection?.directory && connState.serverHome
? connState.clientForDirectory(connState.serverHome)
: connState.client
try { try {
set({ isLoading: true, error: null }) set({ isLoading: true, error: null })
const sessions = await client.session.list({ roots: true, limit: 50 }) const sessions = await (listClient || connState.client).session.list({ roots: true, limit: 50 })
set({ sessions, isLoading: false }) set({ sessions, isLoading: false })
} catch (error) { } catch (error) {
set({ error: "Failed to load sessions", isLoading: false }) set({ error: "Failed to load sessions", isLoading: false })
@@ -186,7 +193,8 @@ export const useSessions = create<SessionsState>((set, get) => ({
}, },
deleteSession: async (sessionID) => { deleteSession: async (sessionID) => {
const client = useConnections.getState().client const session = get().sessions.find((s) => s.id === sessionID)
const client = clientFor(session?.directory)
if (!client) { if (!client) {
set({ error: "No active connection" }) set({ error: "No active connection" })
return return