feat(app): scope sdk/sync hooks per-route so /new-session targets its draft server (#32290)

This commit is contained in:
Brendan Allan 2026-06-14 19:24:10 +08:00 committed by GitHub
commit 010b456dfd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 524 additions and 458 deletions

View file

@ -20,7 +20,7 @@ export function DirectoryDataProvider(props: ParentProps<{ directory: string; dr
createEffect(() => {
// A draft lives at /new-session?draftId=… and has no directory segment to normalize.
if (props.draftID) return
const next = sync.data.path.directory
const next = sync().data.path.directory
if (!next || next === props.directory) return
const path = location.pathname.slice(slug().length + 1)
navigate(`/${base64Encode(next)}${path}${location.search}${location.hash}`, { replace: true })
@ -28,12 +28,12 @@ export function DirectoryDataProvider(props: ParentProps<{ directory: string; dr
createResource(
() => params.id,
(id) => sync.session.sync(id).catch(() => {}),
(id) => sync().session.sync(id).catch(() => {}),
)
return (
<DataProvider
data={sync.data}
data={sync().data}
directory={props.directory}
onNavigateToSession={(sessionID: string) => navigate(`/${slug()}/session/${sessionID}`)}
onSessionHref={(sessionID: string) => `/${slug()}/session/${sessionID}`}

View file

@ -24,7 +24,7 @@ import { DialogSelectServer, useServerManagementController } from "@/components/
import { DialogServerV2 } from "@/components/settings-v2/dialog-server-v2"
import { ServerConnection, useServer } from "@/context/server"
import { sessionHasOpenTab, useTabs } from "@/context/tabs"
import { useServerSync } from "@/context/server-sync"
import { useServerSync, type ServerSync } from "@/context/server-sync"
import { useLanguage } from "@/context/language"
import { useNotification } from "@/context/notification"
import {
@ -80,7 +80,7 @@ const HOME_SEARCH_RESULT_META =
let pendingHomeNavigation: { server: ServerConnection.Key; href: string } | undefined
function buildHomeSessionRecords(input: {
sync: Pick<ReturnType<typeof useServerSync>, "child">
sync: Pick<ServerSync, "child">
projectDirectories: () => string[]
projects: () => LocalProject[]
projectByID: () => Map<string, LocalProject>
@ -149,7 +149,7 @@ function HomeDesign() {
if (!conn) return
return global.createServerCtx(conn)
})
const focusedSync = () => focusedServerCtx()?.sync ?? sync
const focusedSync = () => focusedServerCtx()?.sync ?? sync()
const projects = createMemo(() => focusedServerCtx()?.projects.list() ?? layout.projects.list())
const selectedProject = createMemo(() => projects().find((project) => project.worktree === state.selection.directory))
const newSessionProject = createMemo(
@ -1102,9 +1102,9 @@ function LegacyHome() {
const global = useGlobal()
const server = useServer()
const language = useLanguage()
const homedir = createMemo(() => sync.data.path.home)
const homedir = createMemo(() => sync().data.path.home)
const recent = createMemo(() => {
return sync.data.project
return sync().data.project
.slice()
.sort((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
.slice(0, 5)
@ -1164,7 +1164,7 @@ function LegacyHome() {
{server.name}
</Button>
<Switch>
<Match when={sync.data.project.length > 0}>
<Match when={sync().data.project.length > 0}>
<div class="mt-20 w-full flex flex-col gap-4">
<div class="flex gap-2 items-center justify-between pl-3">
<div class="text-14-medium text-text-strong">{language.t("home.recentProjects")}</div>
@ -1191,7 +1191,7 @@ function LegacyHome() {
</ul>
</div>
</Match>
<Match when={!sync.ready}>
<Match when={!sync().ready}>
<div class="mt-30 mx-auto flex flex-col items-center gap-3">
<div class="text-12-regular text-text-weak">{language.t("common.loading")}</div>
<Button class="px-3" onClick={chooseProject}>

View file

@ -95,7 +95,7 @@ import { SidebarContent } from "./layout/sidebar-shell"
export default function Layout(props: ParentProps) {
const serverSDK = useServerSDK()
const [store, setStore, , ready] = persisted(
Persist.serverGlobal(serverSDK.scope, "layout.page", ["layout.page.v1"]),
Persist.serverGlobal(serverSDK().scope, "layout.page", ["layout.page.v1"]),
createStore({
lastProjectSession: {} as { [directory: string]: { directory: string; id: string; at: number } },
activeProject: undefined as string | undefined,
@ -140,7 +140,7 @@ export default function Layout(props: ParentProps) {
if (!slug) return { slug, dir: "" }
const dir = decode64(slug)
if (!dir) return { slug, dir: "" }
const store = serverSync.peek(dir, { bootstrap: false })
const store = serverSync().peek(dir, { bootstrap: false })
return {
slug,
store,
@ -213,7 +213,7 @@ export default function Layout(props: ParentProps) {
active: () => state.hoverProject,
el: () => state.nav?.querySelector<HTMLElement>("[data-component='sidebar-rail']") ?? state.nav,
onActivate: (directory) => {
serverSync.child(directory)
serverSync().child(directory)
setState("hoverProject", directory)
},
})
@ -397,17 +397,17 @@ export default function Layout(props: ParentProps) {
alertedAtBySession.delete(sessionKey)
}
const unsub = serverSDK.event.listen((e) => {
const unsub = serverSDK().event.listen((e) => {
if (e.details?.type === "worktree.ready") {
setBusy(e.name, false)
WorktreeState.ready(serverSDK.scope, e.name)
WorktreeState.ready(serverSDK().scope, e.name)
return
}
if (e.details?.type === "worktree.failed") {
setBusy(e.name, false)
WorktreeState.failed(
serverSDK.scope,
serverSDK().scope,
e.name,
e.details.properties?.message ?? language.t("common.requestFailed"),
)
@ -435,7 +435,7 @@ export default function Layout(props: ParentProps) {
const props = e.details.properties
if (e.details.type === "permission.asked" && permission.autoResponds(e.details.properties, directory)) return
const [store] = serverSync.child(directory, { bootstrap: false })
const [store] = serverSync().child(directory, { bootstrap: false })
const session = store.session.find((s) => s.id === props.sessionID)
const sessionKey = `${directory}:${props.sessionID}`
@ -498,7 +498,7 @@ export default function Layout(props: ParentProps) {
if (!currentDir() || !currentSession) return
const sessionKey = `${currentDir()}:${currentSession}`
dismissSessionAlert(sessionKey)
const [store] = serverSync.child(currentDir(), { bootstrap: false })
const [store] = serverSync().child(currentDir(), { bootstrap: false })
const childSessions = store.session.filter((s) => s.parentID === currentSession)
for (const child of childSessions) {
dismissSessionAlert(`${currentDir()}:${child.id}`)
@ -536,11 +536,11 @@ export default function Layout(props: ParentProps) {
const direct = projects.find((p) => pathKey(p.worktree) === key)
if (direct) return direct
const [child] = serverSync.child(directory, { bootstrap: false })
const [child] = serverSync().child(directory, { bootstrap: false })
const id = child.project
if (!id) return
const meta = serverSync.data.project.find((p) => p.id === id)
const meta = serverSync().data.project.find((p) => p.id === id)
const root = meta?.worktree
if (!root) return
@ -631,7 +631,7 @@ export default function Layout(props: ParentProps) {
const result: Session[] = []
for (const dir of dirs) {
const [dirStore] = serverSync.child(dir, { bootstrap: true })
const [dirStore] = serverSync().child(dir, { bootstrap: true })
const dirSessions = sortedRootSessions(dirStore, now)
result.push(...dirSessions)
}
@ -683,10 +683,10 @@ export default function Layout(props: ParentProps) {
createEffect(() => {
route()
serverSDK.url
serverSDK().url
prefetchToken.value += 1
clearSessionPrefetchInflight(serverSDK.scope)
clearSessionPrefetchInflight(serverSDK().scope)
prefetchQueues.clear()
})
@ -730,17 +730,17 @@ export default function Layout(props: ParentProps) {
}
async function prefetchMessages(directory: string, sessionID: string, token: number) {
const [store, setStore] = serverSync.child(directory, { bootstrap: false })
const [store, setStore] = serverSync().child(directory, { bootstrap: false })
return runSessionPrefetch({
scope: serverSDK.scope,
scope: serverSDK().scope,
directory,
sessionID,
task: (rev) =>
retry(() => serverSDK.client.session.messages({ directory, sessionID, limit: prefetchChunk }))
retry(() => serverSDK().client.session.messages({ directory, sessionID, limit: prefetchChunk }))
.then((messages) => {
if (prefetchToken.value !== token) return
if (!isSessionPrefetchCurrent(serverSDK.scope, directory, sessionID, rev)) return
if (!isSessionPrefetchCurrent(serverSDK().scope, directory, sessionID, rev)) return
const items = (messages.data ?? []).filter((x) => !!x?.info?.id)
const next = items.map((x) => x.info).filter((m): m is Message => !!m?.id)
@ -755,9 +755,9 @@ export default function Layout(props: ParentProps) {
}
if (stale.length > 0) {
clearSessionPrefetch(serverSDK.scope, directory, stale)
clearSessionPrefetch(serverSDK().scope, directory, stale)
for (const id of stale) {
serverSync.todo.set(id, undefined)
serverSync().todo.set(id, undefined)
}
}
@ -767,7 +767,7 @@ export default function Layout(props: ParentProps) {
sorted,
)
if (!isSessionPrefetchCurrent(serverSDK.scope, directory, sessionID, rev)) return
if (!isSessionPrefetchCurrent(serverSDK().scope, directory, sessionID, rev)) return
batch(() => {
if (stale.length > 0) {
@ -779,7 +779,7 @@ export default function Layout(props: ParentProps) {
}
setStore("message", sessionID, reconcile(merged, { key: "id" }))
setSessionPrefetch({ scope: serverSDK.scope, directory, sessionID, ...meta })
setSessionPrefetch({ scope: serverSDK().scope, directory, sessionID, ...meta })
for (const message of items) {
const currentParts = store.part[message.info.id] ?? []
@ -822,9 +822,9 @@ export default function Layout(props: ParentProps) {
const directory = session.directory
if (!directory) return
const [store] = serverSync.child(directory, { bootstrap: false })
const [store] = serverSync().child(directory, { bootstrap: false })
const cached = untrack(() => {
const info = getSessionPrefetch(serverSDK.scope, directory, session.id)
const info = getSessionPrefetch(serverSDK().scope, directory, session.id)
return shouldSkipSessionPrefetch({
message: store.message[session.id] !== undefined,
info,
@ -927,7 +927,7 @@ export default function Layout(props: ParentProps) {
if (!target) return
// warm up child store to prevent flicker
serverSync.child(target.worktree)
serverSync().child(target.worktree)
void openProject(target.worktree)
}
@ -936,7 +936,7 @@ export default function Layout(props: ParentProps) {
const target = projects[index]
if (!target) return
serverSync.child(target.worktree)
serverSync().child(target.worktree)
void openProject(target.worktree)
}
@ -965,12 +965,12 @@ export default function Layout(props: ParentProps) {
}
async function archiveSession(session: Session) {
const [store, setStore] = serverSync.child(session.directory)
const [store, setStore] = serverSync().child(session.directory)
const sessions = store.session ?? []
const index = sessions.findIndex((s) => s.id === session.id)
const nextSession = sessions[index + 1] ?? sessions[index - 1]
await serverSDK.client.session.update({
await serverSDK().client.session.update({
directory: session.directory,
sessionID: session.id,
time: { archived: Date.now() },
@ -1241,11 +1241,11 @@ export default function Layout(props: ParentProps) {
)
if (known) return known[0]
const [child] = serverSync.child(directory, { bootstrap: false })
const [child] = serverSync().child(directory, { bootstrap: false })
const id = child.project
if (!id) return directory
const meta = serverSync.data.project.find((item) => item.id === id)
const meta = serverSync().data.project.find((item) => item.id === id)
return meta?.worktree ?? directory
}
@ -1293,7 +1293,7 @@ export default function Layout(props: ParentProps) {
}
const refreshDirs = async (target?: string) => {
if (!target || target === root || canOpen(target)) return canOpen(target)
const listed = await serverSDK.client.worktree
const listed = await serverSDK().client.worktree
.list({ directory: root })
.then((x) => x.data ?? [])
.catch(() => [] as string[])
@ -1302,13 +1302,13 @@ export default function Layout(props: ParentProps) {
}
const openSession = async (target: { directory: string; id: string }) => {
if (!canOpen(target.directory)) return false
const [data] = serverSync.child(target.directory, { bootstrap: false })
const [data] = serverSync().child(target.directory, { bootstrap: false })
if (data.session.some((item) => item.id === target.id)) {
setStore("lastProjectSession", root, { directory: target.directory, id: target.id, at: Date.now() })
navigateWithSidebarReset(`/${base64Encode(target.directory)}/session/${target.id}`)
return true
}
const resolved = await serverSDK.client.session
const resolved = await serverSDK().client.session
.get({ sessionID: target.id })
.then((x) => x.data)
.catch(() => undefined)
@ -1328,7 +1328,7 @@ export default function Layout(props: ParentProps) {
}
const latest = latestRootSession(
dirs.map((item) => serverSync.child(item, { bootstrap: false })[0]),
dirs.map((item) => serverSync().child(item, { bootstrap: false })[0]),
Date.now(),
)
if (latest && (await openSession(latest))) {
@ -1339,7 +1339,7 @@ export default function Layout(props: ParentProps) {
await Promise.all(
dirs.map(async (item) => ({
path: { directory: item },
session: await serverSDK.client.session
session: await serverSDK().client.session
.list({ directory: item })
.then((x) => x.data ?? [])
.catch(() => []),
@ -1402,11 +1402,11 @@ export default function Layout(props: ParentProps) {
const name = next === getFilename(project.worktree) ? "" : next
if (project.id && project.id !== "global") {
await serverSDK.client.project.update({ projectID: project.id, directory: project.worktree, name })
await serverSDK().client.project.update({ projectID: project.id, directory: project.worktree, name })
return
}
serverSync.project.meta(project.worktree, { name })
serverSync().project.meta(project.worktree, { name })
}
const renameWorkspace = (directory: string, next: string, projectId?: string, branch?: string) => {
@ -1495,7 +1495,7 @@ export default function Layout(props: ParentProps) {
setBusy(directory, true)
const result = await serverSDK.client.worktree
const result = await serverSDK().client.worktree
.remove({ directory: root, worktreeRemoveInput: { directory } })
.then((x) => x.data)
.catch((err) => {
@ -1514,7 +1514,7 @@ export default function Layout(props: ParentProps) {
clearLastProjectSession(root)
}
serverSync.set(
serverSync().set(
"project",
produce((draft) => {
const project = draft.find((item) => item.worktree === root)
@ -1553,7 +1553,7 @@ export default function Layout(props: ParentProps) {
})
const dismiss = () => toaster.dismiss(progress)
const sessions: Session[] = await serverSDK.client.session
const sessions: Session[] = await serverSDK().client.session
.list({ directory })
.then((x) => x.data ?? [])
.catch(() => [])
@ -1562,11 +1562,11 @@ export default function Layout(props: ParentProps) {
directory,
sessions.map((s) => s.id),
platform,
serverSDK.scope,
serverSDK().scope,
)
await serverSDK.client.instance.dispose({ directory }).catch(() => undefined)
await serverSDK().client.instance.dispose({ directory }).catch(() => undefined)
const result = await serverSDK.client.worktree
const result = await serverSDK().client.worktree
.reset({ directory: root, worktreeResetInput: { directory } })
.then((x) => x.data)
.catch((err) => {
@ -1588,7 +1588,7 @@ export default function Layout(props: ParentProps) {
sessions
.filter((session) => session.time.archived === undefined)
.map((session) =>
serverSDK.client.session
serverSDK().client.session
.update({
sessionID: session.id,
directory: session.directory,
@ -1629,7 +1629,7 @@ export default function Layout(props: ParentProps) {
})
onMount(() => {
serverSDK.client.vcs
serverSDK().client.vcs
.status({ directory: props.directory })
.then((x) => {
const files = x.data ?? []
@ -1688,7 +1688,7 @@ export default function Layout(props: ParentProps) {
})
const refresh = async () => {
const sessions = await serverSDK.client.session
const sessions = await serverSDK().client.session
.list({ directory: props.directory })
.then((x) => x.data ?? [])
.catch(() => [])
@ -1697,7 +1697,7 @@ export default function Layout(props: ParentProps) {
}
onMount(() => {
serverSDK.client.vcs
serverSDK().client.vcs
.status({ directory: props.directory })
.then((x) => {
const files = x.data ?? []
@ -1831,7 +1831,7 @@ export default function Layout(props: ParentProps) {
const next = new Set(dirs)
for (const directory of next) {
if (loadedSessionDirs.has(directory)) continue
void serverSync.project.loadSessions(directory)
void serverSync().project.loadSessions(directory)
}
loadedSessionDirs.clear()
@ -1876,7 +1876,7 @@ export default function Layout(props: ParentProps) {
directory && pathKey(directory) !== pathKey(local) && !dirs.some((item) => pathKey(item) === pathKey(directory))
? directory
: undefined
const pending = extra ? WorktreeState.get(serverSDK.scope, extra)?.status === "pending" : false
const pending = extra ? WorktreeState.get(serverSDK().scope, extra)?.status === "pending" : false
const ordered = effectiveWorkspaceOrder(local, dirs, store.workspaceOrder[project.worktree])
if (pending && extra) return [local, extra, ...ordered.filter((item) => item !== local)]
@ -1928,7 +1928,7 @@ export default function Layout(props: ParentProps) {
const createWorkspace = async (project: LocalProject) => {
clearSidebarHoverState()
const created = await serverSDK.client.worktree
const created = await serverSDK().client.worktree
.create({ directory: project.worktree })
.then((x) => x.data)
.catch((err) => {
@ -1948,7 +1948,7 @@ export default function Layout(props: ParentProps) {
const root = pathKey(local)
setBusy(created.directory, true)
WorktreeState.pending(serverSDK.scope, created.directory)
WorktreeState.pending(serverSDK().scope, created.directory)
setStore("workspaceExpanded", key, true)
if (key !== created.directory) {
setStore("workspaceExpanded", created.directory, true)
@ -1962,7 +1962,7 @@ export default function Layout(props: ParentProps) {
return [created.directory, ...next]
})
serverSync.child(created.directory)
serverSync().child(created.directory)
navigateWithSidebarReset(`/${base64Encode(created.directory)}/session`)
}
@ -2067,7 +2067,7 @@ export default function Layout(props: ParentProps) {
if (!item) return false
return item.vcs === "git" || layout.sidebar.workspaces(item.worktree)()
})
const homedir = createMemo(() => serverSync.data.path.home)
const homedir = createMemo(() => serverSync().data.path.home)
return (
<div

View file

@ -14,7 +14,7 @@ export function useSessionTabAvatarState(
const permission = usePermission()
const hasPermissions = createMemo(() => {
if (!active()) return false
const [store] = globalSync.child(directory(), { bootstrap: false })
const [store] = globalSync().child(directory(), { bootstrap: false })
return !!sessionPermissionRequest(store.session, store.permission, sessionId(), (item) => {
return !permission.autoResponds(item, directory())
})
@ -23,7 +23,7 @@ export function useSessionTabAvatarState(
const loading = createMemo(() => {
if (!active()) return false
if (hasPermissions()) return false
const [store] = globalSync.child(directory(), { bootstrap: false })
const [store] = globalSync().child(directory(), { bootstrap: false })
return store.session_working(sessionId())
})
return { unread, loading }

View file

@ -33,7 +33,7 @@ export const ProjectIcon = (props: {
const hasError = createMemo(() => dirs().some((directory) => notification.project.unseenHasError(directory)))
const hasPermissions = createMemo(() =>
dirs().some((directory) => {
const [store] = serverSync.child(directory, { bootstrap: false })
const [store] = serverSync().child(directory, { bootstrap: false })
return hasProjectPermissions(store.permission, (item) => !permission.autoResponds(item, directory))
}),
)
@ -149,7 +149,7 @@ export const SessionItem = (props: SessionItemProps): JSX.Element => {
const serverSync = useServerSync()
const unseenCount = createMemo(() => notification.session.unseenCount(props.session.id))
const hasError = createMemo(() => notification.session.unseenHasError(props.session.id))
const [sessionStore] = serverSync.child(props.session.directory)
const [sessionStore] = serverSync().child(props.session.directory)
const hasPermissions = createMemo(() => {
return !!sessionPermissionRequest(sessionStore.session, sessionStore.permission, props.session.id, (item) => {
return !permission.autoResponds(item, props.session.directory)

View file

@ -294,23 +294,23 @@ export const SortableProject = (props: {
const hoverOpen = () => isHoverProject() && preview() && !selected() && !state.menu
const label = (directory: string) => {
const [data] = serverSync.child(directory, { bootstrap: false })
const [data] = serverSync().child(directory, { bootstrap: false })
const kind =
directory === props.project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
const name = props.ctx.workspaceLabel(directory, data.vcs?.branch, props.project.id)
return `${kind} : ${name}`
}
const projectStore = createMemo(() => serverSync.child(props.project.worktree, { bootstrap: false })[0])
const projectStore = createMemo(() => serverSync().child(props.project.worktree, { bootstrap: false })[0])
const isWorking = createMemo(() =>
dirs().some((directory) => {
const [store] = serverSync.child(directory, { bootstrap: false })
const [store] = serverSync().child(directory, { bootstrap: false })
return Object.keys(store.session_status).some((id) => store.session_working(id))
}),
)
const projectSessions = createMemo(() => sortedRootSessions(projectStore(), props.sortNow()))
const workspaceSessions = (directory: string) => {
const [data] = serverSync.child(directory, { bootstrap: false })
const [data] = serverSync().child(directory, { bootstrap: false })
return sortedRootSessions(data, props.sortNow())
}
const tile = () => (

View file

@ -68,7 +68,7 @@ export const WorkspaceDragOverlay = (props: {
const directory = props.activeWorkspace()
if (!directory) return
const [workspaceStore] = serverSync.child(directory, { bootstrap: false })
const [workspaceStore] = serverSync().child(directory, { bootstrap: false })
const kind =
directory === project.worktree ? language.t("workspace.type.local") : language.t("workspace.type.sandbox")
const name = props.workspaceLabel(directory, workspaceStore.vcs?.branch, project.id)
@ -303,7 +303,7 @@ export const SortableWorkspace = (props: {
const queryOptions = useQueryOptions()
const language = useLanguage()
const sortable = createSortable(props.directory)
const [workspaceStore, setWorkspaceStore] = serverSync.child(props.directory, { bootstrap: false })
const [workspaceStore, setWorkspaceStore] = serverSync().child(props.directory, { bootstrap: false })
const [menu, setMenu] = createStore({
open: false,
pendingRename: false,
@ -321,14 +321,14 @@ export const SortableWorkspace = (props: {
const boot = createMemo(() => open() || active())
const count = createMemo(() => sessions()?.length ?? 0)
const hasMore = createMemo(() => workspaceStore.sessionTotal > count())
const fetching = useIsFetching(() => queryOptions.sessions(pathKey(props.directory)))
const fetching = useIsFetching(() => queryOptions().sessions(pathKey(props.directory)))
const busy = createMemo(() => props.ctx.isBusy(props.directory))
const loading = () => fetching() > 0 && count() === 0
const touch = createMediaQuery("(hover: none)")
const showNew = createMemo(() => !loading() && (touch() || count() === 0 || (active() && !params.id)))
const loadMore = async () => {
setWorkspaceStore("limit", (limit) => (limit ?? 0) + 5)
await serverSync.project.loadSessions(props.directory)
await serverSync().project.loadSessions(props.directory)
}
const workspaceEditActive = createMemo(() => props.ctx.editorOpen(`workspace:${props.directory}`))
@ -357,7 +357,7 @@ export const SortableWorkspace = (props: {
createEffect(() => {
if (!boot()) return
serverSync.child(props.directory, { bootstrap: true })
serverSync().child(props.directory, { bootstrap: true })
})
return (
@ -450,18 +450,18 @@ export const LocalWorkspace = (props: {
const queryOptions = useQueryOptions()
const language = useLanguage()
const workspace = createMemo(() => {
const [store, setStore] = serverSync.child(props.project.worktree)
const [store, setStore] = serverSync().child(props.project.worktree)
return { store, setStore }
})
const slug = createMemo(() => base64Encode(props.project.worktree))
const sessions = createMemo(() => sortedRootSessions(workspace().store, props.sortNow()))
const count = createMemo(() => sessions()?.length ?? 0)
const fetching = useIsFetching(() => queryOptions.sessions(pathKey(props.project.worktree)))
const fetching = useIsFetching(() => queryOptions().sessions(pathKey(props.project.worktree)))
const hasMore = createMemo(() => workspace().store.sessionTotal > count())
const loading = () => fetching() > 0 && count() === 0
const loadMore = async () => {
workspace().setStore("limit", (limit) => (limit ?? 0) + 5)
await serverSync.project.loadSessions(props.project.worktree)
await serverSync().project.loadSessions(props.project.worktree)
}
return (

View file

@ -30,8 +30,8 @@ export default function NewSessionPage() {
const newSessionWorktree = createMemo(() => {
if (store.worktree === "create") return "create"
const project = sync.project
if (project && sdk.directory !== project.worktree) return sdk.directory
const project = sync().project
if (project && sdk().directory !== project.worktree) return sdk().directory
return "main"
})

View file

@ -308,10 +308,10 @@ export default function Page() {
if (!view().reviewPanel.opened()) view().reviewPanel.open()
}
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
const info = createMemo(() => (params.id ? sync().session.get(params.id) : undefined))
const isChildSession = createMemo(() => !!info()?.parentID)
const diffs = createMemo(() => (params.id ? list(sync.data.session_diff[params.id]) : []))
const canReview = createMemo(() => !!sync.project)
const diffs = createMemo(() => (params.id ? list(sync().data.session_diff[params.id]) : []))
const canReview = createMemo(() => !!sync().project)
const reviewTab = createMemo(() => isDesktop())
const tabState = createSessionTabs({
tabs,
@ -323,21 +323,21 @@ export default function Page() {
const activeTab = tabState.activeTab
const activeFileTab = tabState.activeFileTab
const revertMessageID = createMemo(() => info()?.revert?.messageID)
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
const messages = createMemo(() => (params.id ? (sync().data.message[params.id] ?? []) : []))
const messagesReady = createMemo(() => {
const id = params.id
if (!id) return true
return sync.data.message[id] !== undefined
return sync().data.message[id] !== undefined
})
const historyMore = createMemo(() => {
const id = params.id
if (!id) return false
return sync.session.history.more(id)
return sync().session.history.more(id)
})
const historyLoading = createMemo(() => {
const id = params.id
if (!id) return false
return sync.session.history.loading(id)
return sync().session.history.loading(id)
})
const userMessages = createMemo(
() => messages().filter((m) => m.role === "user") as UserMessage[],
@ -397,7 +397,7 @@ export default function Page() {
})
const [followup, setFollowup] = persisted(
Persist.serverWorkspace(serverSDK.scope, sdk.directory, "followup", ["followup.v1"]),
Persist.serverWorkspace(serverSDK().scope, sdk().directory, "followup", ["followup.v1"]),
createStore<{
items: Record<string, FollowupItem[] | undefined>
failed: Record<string, string | undefined>
@ -444,16 +444,16 @@ export default function Page() {
}, desktopReviewOpen())
const turnDiffs = createMemo(() => list(lastUserMessage()?.summary?.diffs))
const nogit = createMemo(() => !!sync.project && sync.project.vcs !== "git")
const nogit = createMemo(() => {
const project = sync().project
return !!project && project.vcs !== "git"
})
const changesOptions = createMemo<ChangeMode[]>(() => {
const list: ChangeMode[] = []
if (sync.project?.vcs === "git") list.push("git")
if (
sync.project?.vcs === "git" &&
sync.data.vcs?.branch &&
sync.data.vcs?.default_branch &&
sync.data.vcs.branch !== sync.data.vcs.default_branch
) {
const project = sync().project
const vcs = sync().data.vcs
if (project?.vcs === "git") list.push("git")
if (project?.vcs === "git" && vcs?.branch && vcs?.default_branch && vcs.branch !== vcs.default_branch) {
list.push("branch")
}
list.push("turn")
@ -469,18 +469,18 @@ export default function Page() {
if (store.changes === "git" || store.changes === "branch") return store.changes
})
const vcsKey = createMemo(
() => ["session-vcs", sdk.directory, sync.data.vcs?.branch ?? "", sync.data.vcs?.default_branch ?? ""] as const,
() => ["session-vcs", sdk().directory, sync().data.vcs?.branch ?? "", sync().data.vcs?.default_branch ?? ""] as const,
)
const vcsQuery = createQuery(() => {
const mode = vcsMode()
const enabled = wantsReview() && sync.project?.vcs === "git"
const enabled = wantsReview() && sync().project?.vcs === "git"
return {
queryKey: [...vcsKey(), mode] as const,
enabled,
queryFn: mode
? () =>
sdk.client.vcs
sdk().client.vcs
.diff({ mode })
.then((result) => list(result.data))
.catch((error) => {
@ -506,8 +506,8 @@ export default function Page() {
const newSessionWorktree = createMemo(() => {
if (store.newSessionWorktree === "create") return "create"
const project = sync.project
if (project && sdk.directory !== project.worktree) return sdk.directory
const project = sync().project
if (project && sdk().directory !== project.worktree) return sdk().directory
return "main"
})
@ -569,11 +569,11 @@ export default function Page() {
}
function upsert(next: Project) {
const list = serverSync.data.project
sync.set("project", next.id)
const list = serverSync().data.project
sync().set("project", next.id)
const idx = list.findIndex((item) => item.id === next.id)
if (idx >= 0) {
serverSync.set(
serverSync().set(
"project",
list.map((item, i) => (i === idx ? { ...item, ...next } : item)),
)
@ -581,14 +581,14 @@ export default function Page() {
}
const at = list.findIndex((item) => item.id > next.id)
if (at >= 0) {
serverSync.set("project", [...list.slice(0, at), next, ...list.slice(at)])
serverSync().set("project", [...list.slice(0, at), next, ...list.slice(at)])
return
}
serverSync.set("project", [...list, next])
serverSync().set("project", [...list, next])
}
const gitMutation = useMutation(() => ({
mutationFn: () => sdk.client.project.initGit(),
mutationFn: () => sdk().client.project.initGit(),
onSuccess: (x) => {
if (!x.data) return
upsert(x.data)
@ -632,7 +632,7 @@ export default function Page() {
const hasScrollGesture = () => Date.now() - ui.scrollGesture < scrollGestureWindowMs
const [sessionSync] = createResource(
() => [sdk.directory, params.id] as const,
() => [sdk().directory, params.id] as const,
([directory, id]) => {
if (refreshFrame !== undefined) cancelAnimationFrame(refreshFrame)
if (refreshTimer !== undefined) window.clearTimeout(refreshTimer)
@ -640,11 +640,11 @@ export default function Page() {
refreshTimer = undefined
if (!id) return
const cached = untrack(() => sync.data.message[id] !== undefined)
const cached = untrack(() => sync().data.message[id] !== undefined)
const stale = !cached
? false
: (() => {
const info = getSessionPrefetch(serverSDK.scope, directory, id)
const info = getSessionPrefetch(serverSDK().scope, directory, id)
if (!info) return true
return Date.now() - info.at > SESSION_PREFETCH_TTL
})()
@ -655,12 +655,12 @@ export default function Page() {
refreshTimer = undefined
if (params.id !== id) return
untrack(() => {
if (stale) void sync.session.sync(id, { force: true })
if (stale) void sync().session.sync(id, { force: true })
})
}, 0)
})
return sync.session.sync(id)
return sync().session.sync(id)
},
)
@ -669,9 +669,9 @@ export default function Page() {
() => {
const id = params.id
return [
sdk.directory,
sdk().directory,
id,
id ? (sync.data.session_status[id]?.type ?? "idle") : "idle",
id ? (sync().data.session_status[id]?.type ?? "idle") : "idle",
id ? composer.blocked() : false,
] as const
},
@ -682,15 +682,15 @@ export default function Page() {
todoTimer = undefined
if (!id) return
if (status === "idle" && !blocked) return
const cached = untrack(() => sync.data.todo[id] !== undefined || serverSync.data.session_todo[id] !== undefined)
const cached = untrack(() => sync().data.todo[id] !== undefined || serverSync().data.session_todo[id] !== undefined)
todoFrame = requestAnimationFrame(() => {
todoFrame = undefined
todoTimer = window.setTimeout(() => {
todoTimer = undefined
if (sdk.directory !== dir || params.id !== id) return
if (sdk().directory !== dir || params.id !== id) return
untrack(() => {
void sync.session.todo(id, cached ? { force: true } : undefined)
void sync().session.todo(id, cached ? { force: true } : undefined)
})
}, 0)
})
@ -723,7 +723,7 @@ export default function Page() {
),
)
const stopVcs = sdk.event.listen((evt) => {
const stopVcs = sdk().event.listen((evt) => {
if (evt.details.type !== "file.watcher.updated") return
const props =
typeof evt.details.properties === "object" && evt.details.properties
@ -866,7 +866,7 @@ export default function Page() {
createEffect(
on(
() => sync.data.session_status[params.id ?? ""]?.type,
() => sync().data.session_status[params.id ?? ""]?.type,
(next, prev) => {
if (next !== "idle" || prev === undefined || prev === "idle") return
refreshVcs()
@ -1137,10 +1137,10 @@ export default function Page() {
if (!id) return
if (!wantsReview()) return
if (sync.data.session_diff[id] !== undefined) return
if (sync.status === "loading") return
if (sync().data.session_diff[id] !== undefined) return
if (sync().status === "loading") return
void sync.session.diff(id)
void sync().session.diff(id)
})
createEffect(
@ -1155,14 +1155,14 @@ export default function Page() {
const id = params.id
if (!id) return
if (!untrack(() => sync.data.session_diff[id] !== undefined)) return
if (!untrack(() => sync().data.session_diff[id] !== undefined)) return
diffFrame = requestAnimationFrame(() => {
diffFrame = undefined
diffTimer = window.setTimeout(() => {
diffTimer = undefined
if (sessionKey() !== key) return
void sync.session.diff(id, { force: true })
void sync().session.diff(id, { force: true })
}, 0)
})
},
@ -1172,10 +1172,10 @@ export default function Page() {
let treeDir: string | undefined
createEffect(() => {
const dir = sdk.directory
const dir = sdk().directory
if (!isDesktop()) return
if (!layout.fileTree.opened()) return
if (sync.status === "loading") return
if (sync().status === "loading") return
fileTreeTab()
const refresh = treeDir !== dir
@ -1185,7 +1185,7 @@ export default function Page() {
createEffect(
on(
() => sdk.directory,
() => sdk().directory,
() => {
const tab = activeFileTab()
if (!tab) return
@ -1285,7 +1285,7 @@ export default function Page() {
visibleUserMessages,
historyMore,
historyLoading,
loadMore: (sessionID) => sync.session.history.loadMore(sessionID),
loadMore: (sessionID) => sync().session.history.loadMore(sessionID),
userScrolled: autoScroll.userScrolled,
scroller: () => scroller,
})
@ -1329,8 +1329,8 @@ export default function Page() {
)
const draft = (id: string) =>
extractPromptFromParts(sync.data.part[id] ?? [], {
directory: sdk.directory,
extractPromptFromParts(sync().data.part[id] ?? [], {
directory: sdk().directory,
attachmentName: language.t("common.attachment"),
})
@ -1353,7 +1353,7 @@ export default function Page() {
}
const merge = (next: NonNullable<ReturnType<typeof info>>) =>
sync.set("session", (list) => {
sync().set("session", (list) => {
const idx = list.findIndex((item) => item.id === next.id)
if (idx < 0) return list
const out = list.slice()
@ -1362,7 +1362,7 @@ export default function Page() {
})
const roll = (sessionID: string, next: NonNullable<ReturnType<typeof info>>["revert"]) =>
sync.set("session", (list) => {
sync().set("session", (list) => {
const idx = list.findIndex((item) => item.id === sessionID)
if (idx < 0) return list
const out = list.slice()
@ -1370,7 +1370,7 @@ export default function Page() {
return out
})
const busy = (sessionID: string) => sync.data.session_working(sessionID)
const busy = (sessionID: string) => sync().data.session_working(sessionID)
const queuedFollowups = createMemo(() => {
const id = params.id
@ -1393,11 +1393,11 @@ export default function Page() {
setFollowup("failed", input.sessionID, undefined)
const ok = await sendFollowupDraft({
client: sdk.client,
sync,
serverSync,
client: sdk().client,
sync: sync(),
serverSync: serverSync(),
draft: item,
optimisticBusy: item.sessionDirectory === sdk.directory,
optimisticBusy: item.sessionDirectory === sdk().directory,
}).catch((err) => {
setFollowup("failed", input.sessionID, input.id)
fail(err)
@ -1455,7 +1455,7 @@ export default function Page() {
const followupDock = createMemo(() => queuedFollowups().map((item) => ({ id: item.id, text: followupText(item) })))
const sendFollowup = (sessionID: string, id: string, opts?: { manual?: boolean }) => {
if (sync.session.get(sessionID)?.parentID) return Promise.resolve()
if (sync().session.get(sessionID)?.parentID) return Promise.resolve()
const item = (followup.items[sessionID] ?? []).find((entry) => entry.id === id)
if (!item) return Promise.resolve()
if (followupBusy(sessionID)) return Promise.resolve()
@ -1487,7 +1487,7 @@ export default function Page() {
}
const halt = (sessionID: string) =>
busy(sessionID) ? sdk.client.session.abort({ sessionID }).catch(() => {}) : Promise.resolve()
busy(sessionID) ? sdk().client.session.abort({ sessionID }).catch(() => {}) : Promise.resolve()
const revertMutation = useMutation(() => ({
mutationFn: async (input: { sessionID: string; messageID: string }) => {
@ -1499,7 +1499,7 @@ export default function Page() {
prompt.set(value)
})
await halt(input.sessionID)
.then(() => sdk.client.session.revert(input))
.then(() => sdk().client.session.revert(input))
.then((result) => {
if (result.data) merge(result.data)
})
@ -1532,9 +1532,9 @@ export default function Page() {
})
const task = !next
? halt(sessionID).then(() => sdk.client.session.unrevert({ sessionID }))
? halt(sessionID).then(() => sdk().client.session.unrevert({ sessionID }))
: halt(sessionID).then(() =>
sdk.client.session.revert({
sdk().client.session.revert({
sessionID,
messageID: next.id,
}),
@ -1622,7 +1622,7 @@ export default function Page() {
visibleUserMessages,
historyMore,
historyLoading,
loadMore: (sessionID) => sync.session.history.loadMore(sessionID),
loadMore: (sessionID) => sync().session.history.loadMore(sessionID),
currentMessageId: () => store.messageId,
pendingMessage: () => ui.pendingMessage,
setPendingMessage: (value) => setUi("pendingMessage", value),

View file

@ -57,7 +57,7 @@ export function SessionComposerRegion(props: {
const view = layout.view(route.sessionKey)
const handoffPrompt = createMemo(() => getSessionHandoff(route.sessionKey())?.prompt)
const info = createMemo(() => (route.params.id ? sync.session.get(route.params.id) : undefined))
const info = createMemo(() => (route.params.id ? sync().session.get(route.params.id) : undefined))
const parentID = createMemo(() => info()?.parentID)
const child = createMemo(() => !!parentID())
const showComposer = createMemo(() => !props.state.blocked() || child())

View file

@ -32,12 +32,12 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
const permission = usePermission()
const questionRequest = createMemo((): QuestionRequest | undefined => {
return sessionQuestionRequest(sync.data.session, sync.data.question, params.id)
return sessionQuestionRequest(sync().data.session, sync().data.question, params.id)
})
const permissionRequest = createMemo((): PermissionRequest | undefined => {
return sessionPermissionRequest(sync.data.session, sync.data.permission, params.id, (item) => {
return !permission.autoResponds(item, sdk.directory)
return sessionPermissionRequest(sync().data.session, sync().data.permission, params.id, (item) => {
return !permission.autoResponds(item, sdk().directory)
})
})
@ -50,14 +50,14 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
const todos = createMemo((): Todo[] => {
const id = params.id
if (!id) return []
return serverSync.data.session_todo[id] ?? []
return serverSync().data.session_todo[id] ?? []
})
const done = createMemo(
() => todos().length > 0 && todos().every((todo) => todo.status === "completed" || todo.status === "cancelled"),
)
const live = createMemo(() => sync.data.session_working(params.id ?? "") || blocked())
const live = createMemo(() => sync().data.session_working(params.id ?? "") || blocked())
const [store, setStore] = createStore({
responding: undefined as string | undefined,
@ -78,7 +78,7 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
if (store.responding === perm.id) return
setStore("responding", perm.id)
sdk.client.permission
sdk().client.permission
.respond({ sessionID: perm.sessionID, permissionID: perm.id, response })
.catch((err: unknown) => {
const description = err instanceof Error ? err.message : String(err)
@ -111,8 +111,8 @@ export function createSessionComposerState(options?: { closeMs?: number | (() =>
const clear = () => {
const id = params.id
if (!id) return
serverSync.todo.set(id, [])
sync.set("todo", id, [])
serverSync().todo.set(id, [])
sync().set("todo", id, [])
}
createEffect(

View file

@ -64,7 +64,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
const sdk = useSDK()
const serverSDK = useServerSDK()
const language = useLanguage()
const cacheKey = ScopedKey.from(serverSDK.scope, props.request.id)
const cacheKey = ScopedKey.from(serverSDK().scope, props.request.id)
const questions = createMemo(() => props.request.questions)
const total = createMemo(() => questions().length)
@ -209,7 +209,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
}
const replyMutation = useMutation(() => ({
mutationFn: (answers: QuestionAnswer[]) => sdk.client.question.reply({ requestID: props.request.id, answers }),
mutationFn: (answers: QuestionAnswer[]) => sdk().client.question.reply({ requestID: props.request.id, answers }),
onMutate: () => {
props.onSubmit()
},
@ -221,7 +221,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
}))
const rejectMutation = useMutation(() => ({
mutationFn: () => sdk.client.question.reject({ requestID: props.request.id }),
mutationFn: () => sdk().client.question.reject({ requestID: props.request.id }),
onMutate: () => {
props.onSubmit()
},

View file

@ -300,7 +300,7 @@ export function MessageTimeline(props: {
const sessionMessages = createMemo(() => {
const id = sessionID()
if (!id) return emptyMessages
return sync.data.message[id] ?? emptyMessages
return sync().data.message[id] ?? emptyMessages
})
const messageByID = createMemo(() => new Map(sessionMessages().map((message) => [message.id, message] as const)))
const assistantMessagesByParent = createMemo(() => {
@ -324,10 +324,10 @@ export function MessageTimeline(props: {
const sessionStatus = createMemo(() => {
const id = sessionID()
if (!id) return idle
return sync.data.session_status[id] ?? idle
return sync().data.session_status[id] ?? idle
})
const working = createMemo(() => sessionStatus().type !== "idle")
const tint = createMemo(() => messageAgentColor(sessionMessages(), sync.data.agent))
const tint = createMemo(() => messageAgentColor(sessionMessages(), sync().data.agent))
const [timeoutDone, setTimeoutDone] = createSignal(true)
@ -366,25 +366,25 @@ export function MessageTimeline(props: {
const info = createMemo(() => {
const id = sessionID()
if (!id) return
return sync.session.get(id)
return sync().session.get(id)
})
const titleValue = createMemo(() => info()?.title)
const titleLabel = createMemo(() => sessionTitle(titleValue()))
const shareUrl = createMemo(() => info()?.share?.url)
const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
const shareEnabled = createMemo(() => sync().data.config.share !== "disabled")
const parentID = createMemo(() => info()?.parentID)
const parent = createMemo(() => {
const id = parentID()
if (!id) return
return sync.session.get(id)
return sync().session.get(id)
})
const parentMessages = createMemo(() => {
const id = parentID()
if (!id) return emptyMessages
return sync.data.message[id] ?? emptyMessages
return sync().data.message[id] ?? emptyMessages
})
const parentTitle = createMemo(() => sessionTitle(parent()?.title) ?? language.t("command.session.new"))
const getMsgParts = (msgId: string) => sync.data.part[msgId] ?? emptyParts
const getMsgParts = (msgId: string) => sync().data.part[msgId] ?? emptyParts
const childTaskDescription = createMemo(() => {
const id = sessionID()
if (!id) return
@ -730,14 +730,14 @@ export function MessageTimeline(props: {
}
const shareMutation = useMutation(() => ({
mutationFn: (id: string) => serverSDK.client.session.share({ sessionID: id, directory: sdk.directory }),
mutationFn: (id: string) => serverSDK().client.session.share({ sessionID: id, directory: sdk().directory }),
onError: (err) => {
console.error("Failed to share session", err)
},
}))
const unshareMutation = useMutation(() => ({
mutationFn: (id: string) => serverSDK.client.session.unshare({ sessionID: id, directory: sdk.directory }),
mutationFn: (id: string) => serverSDK().client.session.unshare({ sessionID: id, directory: sdk().directory }),
onError: (err) => {
console.error("Failed to unshare session", err)
},
@ -745,9 +745,9 @@ export function MessageTimeline(props: {
const titleMutation = useMutation(() => ({
mutationFn: (input: { id: string; title: string }) =>
sdk.client.session.update({ sessionID: input.id, title: input.title }),
sdk().client.session.update({ sessionID: input.id, title: input.title }),
onSuccess: (_, input) => {
sync.set(
sync().set(
produce((draft) => {
const index = draft.session.findIndex((s) => s.id === input.id)
if (index !== -1) draft.session[index].title = input.title
@ -797,8 +797,8 @@ export function MessageTimeline(props: {
() => [parentID(), childTaskDescription()] as const,
([id, description]) => {
if (!id || description) return
if (sync.data.message[id] !== undefined) return
void sync.session.sync(id)
if (sync().data.message[id] !== undefined) return
void sync().session.sync(id)
},
{ defer: true },
),
@ -846,25 +846,25 @@ export function MessageTimeline(props: {
}
const archiveSession = async (sessionID: string) => {
const session = sync.session.get(sessionID)
const session = sync().session.get(sessionID)
if (!session) return
const sessions = sync.data.session ?? []
const sessions = sync().data.session ?? []
const index = sessions.findIndex((s) => s.id === sessionID)
const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1])
await sdk.client.session
await sdk().client.session
.update({ sessionID, time: { archived: Date.now() } })
.then(() => {
sync.set(
sync().set(
produce((draft) => {
const index = draft.session.findIndex((s) => s.id === sessionID)
if (index !== -1) draft.session.splice(index, 1)
}),
)
sync.session.evict(sessionID)
sync().session.evict(sessionID)
navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
notifySessionTabsRemoved({ directory: sdk.directory, sessionIDs: [sessionID] })
notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] })
})
.catch((err) => {
showToast({
@ -875,14 +875,14 @@ export function MessageTimeline(props: {
}
const deleteSession = async (sessionID: string) => {
const session = sync.session.get(sessionID)
const session = sync().session.get(sessionID)
if (!session) return false
const sessions = (sync.data.session ?? []).filter((s) => !s.parentID && !s.time?.archived)
const sessions = (sync().data.session ?? []).filter((s) => !s.parentID && !s.time?.archived)
const index = sessions.findIndex((s) => s.id === sessionID)
const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1])
const result = await sdk.client.session
const result = await sdk().client.session
.delete({ sessionID })
.then((x) => x.data)
.catch((err) => {
@ -897,7 +897,7 @@ export function MessageTimeline(props: {
const removed = new Set<string>([sessionID])
const byParent = new Map<string, string[]>()
for (const item of sync.data.session) {
for (const item of sync().data.session) {
const parentID = item.parentID
if (!parentID) continue
const existing = byParent.get(parentID)
@ -925,16 +925,16 @@ export function MessageTimeline(props: {
navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
sync.set(
sync().set(
produce((draft) => {
draft.session = draft.session.filter((s) => !removed.has(s.id))
}),
)
for (const id of removed) {
sync.session.evict(id)
sync().session.evict(id)
}
notifySessionTabsRemoved({ directory: sdk.directory, sessionIDs: [...removed] })
notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [...removed] })
return true
}
@ -946,7 +946,7 @@ export function MessageTimeline(props: {
function DialogDeleteSession(props: { sessionID: string }) {
const name = createMemo(
() => sessionTitle(sync.session.get(props.sessionID)?.title) ?? language.t("command.session.new"),
() => sessionTitle(sync().session.get(props.sessionID)?.title) ?? language.t("command.session.new"),
)
const handleDelete = async () => {
await deleteSession(props.sessionID)

View file

@ -53,7 +53,7 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
const layout = useLayout()
const readFile = async (path: string) => {
return sdk.client.file
return sdk().client.file
.read({ path })
.then((x) => x.data)
.catch((error) => {

View file

@ -51,7 +51,7 @@ export function useUsageExceededDialogs() {
)
onCleanup(
sdk.event.on("session.status", (evt) => {
sdk().event.on("session.status", (evt) => {
if (evt.properties.sessionID !== params.id) return
if (evt.properties.status.type !== "retry") return
const { action } = evt.properties.status

View file

@ -52,7 +52,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
const info = () => {
const id = params.id
if (!id) return
return sync.session.get(id)
return sync().session.get(id)
}
const hasReview = () => !!params.id
const normalizeTab = (tab: string) => {
@ -73,7 +73,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
const messages = () => {
const id = params.id
if (!id) return []
return sync.data.message[id] ?? []
return sync().data.message[id] ?? []
}
const userMessages = () => messages().filter((m) => m.role === "user") as UserMessage[]
const visibleUserMessages = () => {
@ -122,8 +122,8 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
const isAutoAcceptActive = () => {
const sessionID = params.id
if (sessionID) return permission.isAutoAccepting(sessionID, sdk.directory)
return permission.isAutoAcceptingDirectory(sdk.directory)
if (sessionID) return permission.isAutoAccepting(sessionID, sdk().directory)
return permission.isAutoAcceptingDirectory(sdk().directory)
}
const write = async (value: string) => {
const body = typeof document === "undefined" ? undefined : document.body
@ -175,7 +175,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
return
}
const url = await sdk.client.session
const url = await sdk().client.session
.share({ sessionID })
.then((res) => res.data?.share?.url)
.catch(() => undefined)
@ -195,7 +195,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
const sessionID = params.id
if (!sessionID) return
await sdk.client.session
await sdk().client.session
.unshare({ sessionID })
.then(() =>
showToast({
@ -263,12 +263,12 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
const toggleAutoAccept = () => {
const sessionID = params.id
if (sessionID) permission.toggleAutoAccept(sessionID, sdk.directory)
else permission.toggleAutoAcceptDirectory(sdk.directory)
if (sessionID) permission.toggleAutoAccept(sessionID, sdk().directory)
else permission.toggleAutoAcceptDirectory(sdk().directory)
const active = sessionID
? permission.isAutoAccepting(sessionID, sdk.directory)
: permission.isAutoAcceptingDirectory(sdk.directory)
? permission.isAutoAccepting(sessionID, sdk().directory)
: permission.isAutoAcceptingDirectory(sdk().directory)
showToast({
title: active
? language.t("toast.permissions.autoaccept.on.title")
@ -283,18 +283,18 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
const sessionID = params.id
if (!sessionID) return
if (sync.data.session_working(params.id ?? "")) {
await sdk.client.session.abort({ sessionID }).catch(() => {})
if (sync().data.session_working(params.id ?? "")) {
await sdk().client.session.abort({ sessionID }).catch(() => {})
}
const revert = info()?.revert?.messageID
const message = findLast(userMessages(), (x) => !revert || x.id < revert)
if (!message) return
await sdk.client.session.revert({ sessionID, messageID: message.id })
const parts = sync.data.part[message.id]
await sdk().client.session.revert({ sessionID, messageID: message.id })
const parts = sync().data.part[message.id]
if (parts) {
const restored = extractPromptFromParts(parts, { directory: sdk.directory })
const restored = extractPromptFromParts(parts, { directory: sdk().directory })
prompt.set(restored)
}
@ -311,14 +311,14 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
const next = userMessages().find((x) => x.id > revertMessageID)
if (!next) {
await sdk.client.session.unrevert({ sessionID })
await sdk().client.session.unrevert({ sessionID })
prompt.reset()
const last = findLast(userMessages(), (x) => x.id >= revertMessageID)
setActiveMessage(last)
return
}
await sdk.client.session.revert({ sessionID, messageID: next.id })
await sdk().client.session.revert({ sessionID, messageID: next.id })
const prev = findLast(userMessages(), (x) => x.id < next.id)
setActiveMessage(prev)
}
@ -336,7 +336,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
return
}
await sdk.client.session.summarize({
await sdk().client.session.summarize({
sessionID,
modelID: model.id,
providerID: model.provider.id,
@ -350,7 +350,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
}
const shareCmds = () => {
if (sync.data.config.share === "disabled") return []
if (sync().data.config.share === "disabled") return []
return [
sessionCommand({
id: "session.share",