feat(app): scope sdk/sync hooks per-route so /new-session targets its draft server (#32290)
This commit is contained in:
parent
c81cd3202c
commit
010b456dfd
53 changed files with 524 additions and 458 deletions
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue