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

@ -147,12 +147,12 @@ beforeAll(async () => {
return clientFor(opts.directory)
},
}
return sdk
return () => sdk
},
}))
mock.module("@/context/sync", () => ({
useSync: () => ({
useSync: () => () => ({
data: { command: [] },
session: {
optimistic: {
@ -176,7 +176,7 @@ beforeAll(async () => {
}))
mock.module("@/context/server-sync", () => ({
useServerSync: () => ({
useServerSync: () => () => ({
child: (directory: string) => {
syncedDirectories.push(directory)
storedSessions[directory] ??= []

View file

@ -7,14 +7,14 @@ import { batch, type Accessor } from "solid-js"
import type { FileSelection } from "@/context/file"
import { useServer } from "@/context/server"
import { useTabs } from "@/context/tabs"
import { useServerSync } from "@/context/server-sync"
import { useServerSync, type ServerSync } from "@/context/server-sync"
import { useLanguage } from "@/context/language"
import { useLayout } from "@/context/layout"
import { useLocal } from "@/context/local"
import { usePermission } from "@/context/permission"
import { type ContextItem, type ImageAttachmentPart, type Prompt, usePrompt } from "@/context/prompt"
import { useSDK } from "@/context/sdk"
import { useSync } from "@/context/sync"
import { useSDK, type DirectorySDK } from "@/context/sdk"
import { useSync, type DirectorySync } from "@/context/sync"
import { Identifier } from "@/utils/id"
import { Worktree as WorktreeState } from "@/utils/worktree"
import { buildRequestParts } from "./build-request-parts"
@ -40,9 +40,9 @@ export type FollowupDraft = {
}
type FollowupSendInput = {
client: ReturnType<typeof useSDK>["client"]
serverSync: ReturnType<typeof useServerSync>
sync: ReturnType<typeof useSync>
client: DirectorySDK["client"]
serverSync: ServerSync
sync: DirectorySync
draft: FollowupDraft
messageID?: string
optimisticBusy?: boolean
@ -218,7 +218,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
const [search] = useSearchParams<{ draftId?: string }>()
const server = useServer()
const tabs = useTabs()
const pendingKey = (sessionID: string) => ScopedKey.from(sdk.scope, sessionID)
const pendingKey = (sessionID: string) => ScopedKey.from(sdk().scope, sessionID)
const errorMessage = (err: unknown) => {
if (err && typeof err === "object" && "data" in err) {
@ -233,8 +233,8 @@ export function createPromptSubmit(input: PromptSubmitInput) {
const sessionID = params.id
if (!sessionID) return Promise.resolve()
serverSync.todo.set(sessionID, [])
const [, setStore] = serverSync.child(sdk.directory)
serverSync().todo.set(sessionID, [])
const [, setStore] = serverSync().child(sdk().directory)
setStore("todo", sessionID, [])
input.onAbort?.()
@ -247,7 +247,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
pending.delete(key)
return Promise.resolve()
}
return sdk.client.session
return sdk().client.session
.abort({
sessionID,
})
@ -281,7 +281,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
}
const seed = (dir: string, info: Session) => {
const [, setStore] = serverSync.child(dir)
const [, setStore] = serverSync().child(dir)
setStore("session", (list: Session[]) => {
const result = Binary.search(list, info.id, (item) => item.id)
const next = [...list]
@ -321,13 +321,13 @@ export function createPromptSubmit(input: PromptSubmitInput) {
input.addToHistory(currentPrompt, mode)
input.resetHistoryNavigation()
const projectDirectory = sdk.directory
const projectDirectory = sdk().directory
const isNewSession = !params.id
const shouldAutoAccept = isNewSession && input.autoAccept()
const worktreeSelection = input.newSessionWorktree?.() || "main"
let sessionDirectory = projectDirectory
let client = sdk.client
let client = sdk().client
if (isNewSession) {
if (worktreeSelection === "create") {
@ -349,7 +349,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
})
return
}
WorktreeState.pending(sdk.scope, createdWorktree.directory)
WorktreeState.pending(sdk().scope, createdWorktree.directory)
sessionDirectory = createdWorktree.directory
}
@ -358,11 +358,11 @@ export function createPromptSubmit(input: PromptSubmitInput) {
}
if (sessionDirectory !== projectDirectory) {
client = sdk.createClient({
client = sdk().createClient({
directory: sessionDirectory,
throwOnError: true,
})
serverSync.child(sessionDirectory)
serverSync().child(sessionDirectory)
}
input.onNewSessionWorktreeReset?.()
@ -470,7 +470,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
if (text.startsWith("/")) {
const [cmdName, ...args] = text.split(" ")
const commandName = cmdName.slice(1)
const customCommand = sync.data.command.find((c) => c.name === commandName)
const customCommand = sync().data.command.find((c) => c.name === commandName)
if (customCommand) {
clearInput()
client.session
@ -504,7 +504,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
const messageID = Identifier.ascending("message")
const removeOptimisticMessage = () => {
sync.session.optimistic.remove({
sync().session.optimistic.remove({
directory: sessionDirectory,
sessionID: session.id,
messageID,
@ -515,17 +515,17 @@ export function createPromptSubmit(input: PromptSubmitInput) {
clearInput()
const waitForWorktree = async () => {
const worktree = WorktreeState.get(sdk.scope, sessionDirectory)
const worktree = WorktreeState.get(sdk().scope, sessionDirectory)
if (!worktree || worktree.status !== "pending") return true
if (sessionDirectory === projectDirectory) {
sync.set("session_status", session.id, { type: "busy" })
sync().set("session_status", session.id, { type: "busy" })
}
const controller = new AbortController()
const cleanup = () => {
if (sessionDirectory === projectDirectory) {
sync.set("session_status", session.id, { type: "idle" })
sync().set("session_status", session.id, { type: "idle" })
}
removeOptimisticMessage()
restoreCommentItems(commentItems)
@ -559,7 +559,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
}, timeoutMs)
})
const result = await Promise.race([WorktreeState.wait(sdk.scope, sessionDirectory), abortWait, timeout]).finally(
const result = await Promise.race([WorktreeState.wait(sdk().scope, sessionDirectory), abortWait, timeout]).finally(
() => {
if (timer.id === undefined) return
clearTimeout(timer.id)
@ -573,8 +573,8 @@ export function createPromptSubmit(input: PromptSubmitInput) {
void sendFollowupDraft({
client,
sync,
serverSync,
sync: sync(),
serverSync: serverSync(),
draft,
messageID,
optimisticBusy: sessionDirectory === projectDirectory,
@ -582,7 +582,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
}).catch((err) => {
pending.delete(pendingKey(session.id))
if (sessionDirectory === projectDirectory) {
sync.set("session_status", session.id, { type: "idle" })
sync().set("session_status", session.id, { type: "idle" })
}
showToast({
title: language.t("prompt.toast.promptSendFailed.title"),