diff options
| author | Shoubhit Dash <[email protected]> | 2026-04-16 17:30:14 +0530 |
|---|---|---|
| committer | Shoubhit Dash <[email protected]> | 2026-04-16 17:30:14 +0530 |
| commit | 2e18a603f0ea24154908e748493fd4bfaa74fc00 (patch) | |
| tree | b723e00af2821b213573b16fa199b64babd5f1f5 /packages/app/src/context | |
| parent | 9819eb04614fd607cacb07d754052f1531a82331 (diff) | |
| parent | 7341718f9234b0cf3a8758c87e91d2006b71bff6 (diff) | |
| download | opencode-2e18a603f0ea24154908e748493fd4bfaa74fc00.tar.gz opencode-2e18a603f0ea24154908e748493fd4bfaa74fc00.zip | |
merge dev
Diffstat (limited to 'packages/app/src/context')
| -rw-r--r-- | packages/app/src/context/file.tsx | 2 | ||||
| -rw-r--r-- | packages/app/src/context/global-sdk.tsx | 8 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync.tsx | 103 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync/bootstrap.ts | 316 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync/child-store.ts | 5 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync/event-reducer.ts | 2 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync/queue.ts | 1 | ||||
| -rw-r--r-- | packages/app/src/context/global-sync/types.ts | 2 | ||||
| -rw-r--r-- | packages/app/src/context/layout.tsx | 6 | ||||
| -rw-r--r-- | packages/app/src/context/local.tsx | 52 | ||||
| -rw-r--r-- | packages/app/src/context/notification.tsx | 4 | ||||
| -rw-r--r-- | packages/app/src/context/permission-auto-respond.test.ts | 2 | ||||
| -rw-r--r-- | packages/app/src/context/permission-auto-respond.ts | 2 | ||||
| -rw-r--r-- | packages/app/src/context/prompt.tsx | 2 | ||||
| -rw-r--r-- | packages/app/src/context/sync.tsx | 4 | ||||
| -rw-r--r-- | packages/app/src/context/terminal.tsx | 4 |
16 files changed, 251 insertions, 264 deletions
diff --git a/packages/app/src/context/file.tsx b/packages/app/src/context/file.tsx index f8fec7142..8998731a6 100644 --- a/packages/app/src/context/file.tsx +++ b/packages/app/src/context/file.tsx @@ -3,7 +3,7 @@ import { createStore, produce, reconcile } from "solid-js/store" import { createSimpleContext } from "@opencode-ai/ui/context" import { showToast } from "@opencode-ai/ui/toast" import { useParams } from "@solidjs/router" -import { getFilename } from "@opencode-ai/util/path" +import { getFilename } from "@opencode-ai/shared/util/path" import { useSDK } from "./sdk" import { useSync } from "./sync" import { useLanguage } from "@/context/language" diff --git a/packages/app/src/context/global-sdk.tsx b/packages/app/src/context/global-sdk.tsx index 1205a8fa8..e53d60d5a 100644 --- a/packages/app/src/context/global-sdk.tsx +++ b/packages/app/src/context/global-sdk.tsx @@ -128,6 +128,7 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo if (started) return run started = true run = (async () => { + // oxlint-disable-next-line no-unmodified-loop-condition -- `started` is set to false by stop() which also aborts; both flags are checked to allow graceful exit while (!abort.signal.aborted && started) { attempt = new AbortController() lastEventAt = Date.now() @@ -155,7 +156,12 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo resetHeartbeat() streamErrorLogged = false const directory = event.directory ?? "global" - const payload = event.payload + if (event.payload.type === "sync") { + continue + } + + const payload = event.payload as Event + const k = key(directory, payload) if (k) { const i = coalesced.get(k) diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 0cf3570a8..6ff60f161 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -8,7 +8,7 @@ import type { Todo, } from "@opencode-ai/sdk/v2/client" import { showToast } from "@opencode-ai/ui/toast" -import { getFilename } from "@opencode-ai/util/path" +import { getFilename } from "@opencode-ai/shared/util/path" import { createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js" import { createStore, produce, reconcile } from "solid-js/store" import { useLanguage } from "@/context/language" @@ -26,6 +26,7 @@ import type { ProjectMeta } from "./global-sync/types" import { SESSION_RECENT_LIMIT } from "./global-sync/types" import { sanitizeProject } from "./global-sync/utils" import { formatServerError } from "@/utils/server-errors" +import { queryOptions, skipToken, useQueryClient } from "@tanstack/solid-query" type GlobalStore = { ready: boolean @@ -41,6 +42,9 @@ type GlobalStore = { reload: undefined | "pending" | "complete" } +export const loadSessionsQuery = (directory: string) => + queryOptions<null>({ queryKey: [directory, "loadSessions"], queryFn: skipToken }) + function createGlobalSync() { const globalSDK = useGlobalSDK() const language = useLanguage() @@ -67,6 +71,7 @@ function createGlobalSync() { config: {}, reload: undefined, }) + const queryClient = useQueryClient() let active = true let projectWritten = false @@ -198,46 +203,53 @@ function createGlobalSync() { } const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT) - const promise = loadRootSessionsWithFallback({ - directory, - limit, - list: (query) => globalSDK.client.session.list(query), - }) - .then((x) => { - const nonArchived = (x.data ?? []) - .filter((s) => !!s?.id) - .filter((s) => !s.time?.archived) - .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) - const limit = store.limit - const childSessions = store.session.filter((s) => !!s.parentID) - const sessions = trimSessions([...nonArchived, ...childSessions], { - limit, - permission: store.permission, - }) - setStore( - "sessionTotal", - estimateRootSessionTotal({ - count: nonArchived.length, - limit: x.limit, - limited: x.limited, - }), - ) - setStore("session", reconcile(sessions, { key: "id" })) - cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo) - sessionMeta.set(directory, { limit }) - }) - .catch((err) => { - console.error("Failed to load sessions", err) - const project = getFilename(directory) - showToast({ - variant: "error", - title: language.t("toast.session.listFailed.title", { project }), - description: formatServerError(err, language.t), - }) + const promise = queryClient + .ensureQueryData({ + ...loadSessionsQuery(directory), + queryFn: () => + loadRootSessionsWithFallback({ + directory, + limit, + list: (query) => globalSDK.client.session.list(query), + }) + .then((x) => { + const nonArchived = (x.data ?? []) + .filter((s) => !!s?.id) + .filter((s) => !s.time?.archived) + .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) + const limit = store.limit + const childSessions = store.session.filter((s) => !!s.parentID) + const sessions = trimSessions([...nonArchived, ...childSessions], { + limit, + permission: store.permission, + }) + setStore( + "sessionTotal", + estimateRootSessionTotal({ + count: nonArchived.length, + limit: x.limit, + limited: x.limited, + }), + ) + setStore("session", reconcile(sessions, { key: "id" })) + cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo) + sessionMeta.set(directory, { limit }) + }) + .catch((err) => { + console.error("Failed to load sessions", err) + const project = getFilename(directory) + showToast({ + variant: "error", + title: language.t("toast.session.listFailed.title", { project }), + description: formatServerError(err, language.t), + }) + }) + .then(() => null), }) + .then(() => {}) sessionLoads.set(directory, promise) - promise.finally(() => { + void promise.finally(() => { sessionLoads.delete(directory) children.unpin(directory) }) @@ -250,8 +262,9 @@ function createGlobalSync() { if (pending) return pending children.pin(directory) - const promise = (async () => { + const promise = Promise.resolve().then(async () => { const child = children.ensureChild(directory) + child[1]("bootstrapPromise", promise!) const cache = children.vcsCache.get(directory) if (!cache) return const sdk = sdkFor(directory) @@ -269,11 +282,12 @@ function createGlobalSync() { vcsCache: cache, loadSessions, translate: language.t, + queryClient, }) - })() + }) booting.set(directory, promise) - promise.finally(() => { + void promise.finally(() => { booting.delete(directory) children.unpin(directory) }) @@ -317,7 +331,7 @@ function createGlobalSync() { setSessionTodo, vcsCache: children.vcsCache.get(directory), loadLsp: () => { - sdkFor(directory) + void sdkFor(directory) .lsp.status() .then((x) => { setStore("lsp", x.data ?? []) @@ -346,6 +360,7 @@ function createGlobalSync() { translate: language.t, formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }), setGlobalStore: setBootStore, + queryClient, }) bootedAt = Date.now() } finally { @@ -359,13 +374,13 @@ function createGlobalSync() { eventFrame = undefined eventTimer = setTimeout(() => { eventTimer = undefined - globalSDK.event.start() + void globalSDK.event.start() }, 0) }) } else { eventTimer = setTimeout(() => { eventTimer = undefined - globalSDK.event.start() + void globalSDK.event.start() }, 0) } void bootstrap() diff --git a/packages/app/src/context/global-sync/bootstrap.ts b/packages/app/src/context/global-sync/bootstrap.ts index 7edd5a1ce..17fe726f9 100644 --- a/packages/app/src/context/global-sync/bootstrap.ts +++ b/packages/app/src/context/global-sync/bootstrap.ts @@ -11,13 +11,15 @@ import type { Todo, } from "@opencode-ai/sdk/v2/client" import { showToast } from "@opencode-ai/ui/toast" -import { getFilename } from "@opencode-ai/util/path" -import { retry } from "@opencode-ai/util/retry" +import { getFilename } from "@opencode-ai/shared/util/path" +import { retry } from "@opencode-ai/shared/util/retry" import { batch } from "solid-js" import { reconcile, type SetStoreFunction, type Store } from "solid-js/store" import type { State, VcsCache } from "./types" import { cmp, normalizeAgentList, normalizeProviderList } from "./utils" import { formatServerError } from "@/utils/server-errors" +import { QueryClient, queryOptions, skipToken } from "@tanstack/solid-query" +import { loadSessionsQuery } from "../global-sync" type GlobalStore = { ready: boolean @@ -65,28 +67,13 @@ function runAll(list: Array<() => Promise<unknown>>) { return Promise.allSettled(list.map((item) => item())) } -function showErrors(input: { - errors: unknown[] - title: string - translate: (key: string, vars?: Record<string, string | number>) => string - formatMoreCount: (count: number) => string -}) { - if (input.errors.length === 0) return - const message = formatServerError(input.errors[0], input.translate) - const more = input.errors.length > 1 ? input.formatMoreCount(input.errors.length - 1) : "" - showToast({ - variant: "error", - title: input.title, - description: message + more, - }) -} - export async function bootstrapGlobal(input: { globalSDK: OpencodeClient requestFailedTitle: string translate: (key: string, vars?: Record<string, string | number>) => string formatMoreCount: (count: number) => string setGlobalStore: SetStoreFunction<GlobalStore> + queryClient: QueryClient }) { const fast = [ () => @@ -96,11 +83,16 @@ export async function bootstrapGlobal(input: { }), ), () => - retry(() => - input.globalSDK.provider.list().then((x) => { - input.setGlobalStore("provider", normalizeProviderList(x.data!)) - }), - ), + input.queryClient.fetchQuery({ + ...loadProvidersQuery(null), + queryFn: () => + retry(() => + input.globalSDK.provider.list().then((x) => { + input.setGlobalStore("provider", normalizeProviderList(x.data!)) + return null + }), + ), + }), ] const slow = [ @@ -122,20 +114,21 @@ export async function bootstrapGlobal(input: { }), ), ] - - showErrors({ - errors: errors(await runAll(fast)), - title: input.requestFailedTitle, - translate: input.translate, - formatMoreCount: input.formatMoreCount, - }) + await runAll(fast) + // showErrors({ + // errors: errors(await runAll(fast)), + // title: input.requestFailedTitle, + // translate: input.translate, + // formatMoreCount: input.formatMoreCount, + // }) await waitForPaint() - showErrors({ - errors: errors(await runAll(slow)), - title: input.requestFailedTitle, - translate: input.translate, - formatMoreCount: input.formatMoreCount, - }) + await runAll(slow) + // showErrors({ + // errors: errors(), + // title: input.requestFailedTitle, + // translate: input.translate, + // formatMoreCount: input.formatMoreCount, + // }) input.setGlobalStore("ready", true) } @@ -187,6 +180,12 @@ function warmSessions(input: { ).then(() => undefined) } +export const loadProvidersQuery = (directory: string | null) => + queryOptions<null>({ queryKey: [directory, "providers"], queryFn: skipToken }) + +export const loadAgentsQuery = (directory: string | null) => + queryOptions<null>({ queryKey: [directory, "agents"], queryFn: skipToken }) + export async function bootstrapDirectory(input: { directory: string sdk: OpencodeClient @@ -201,6 +200,7 @@ export async function bootstrapDirectory(input: { project: Project[] provider: ProviderListResponse } + queryClient: QueryClient }) { const loading = input.store.status !== "complete" const seededProject = projectID(input.directory, input.global.project) @@ -222,97 +222,7 @@ export async function bootstrapDirectory(input: { input.setStore("lsp", []) if (loading) input.setStore("status", "partial") - const fast = [ - () => retry(() => input.sdk.app.agents().then((x) => input.setStore("agent", normalizeAgentList(x.data)))), - () => retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))), - () => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))), - ] - - const slow = [ - () => - seededProject - ? Promise.resolve() - : retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id)), - () => - seededPath - ? Promise.resolve() - : retry(() => - input.sdk.path.get().then((x) => { - input.setStore("path", x.data!) - const next = projectID(x.data?.directory ?? input.directory, input.global.project) - if (next) input.setStore("project", next) - }), - ), - () => - retry(() => - input.sdk.vcs.get().then((x) => { - const next = x.data ?? input.store.vcs - input.setStore("vcs", next) - if (next) input.vcsCache.setStore("value", next) - }), - ), - () => retry(() => input.sdk.command.list().then((x) => input.setStore("command", x.data ?? []))), - () => - retry(() => - input.sdk.permission.list().then((x) => { - const ids = (x.data ?? []).map((perm) => perm?.sessionID).filter((id): id is string => !!id) - const grouped = groupBySession( - (x.data ?? []).filter((perm): perm is PermissionRequest => !!perm?.id && !!perm.sessionID), - ) - return warmSessions({ ids, store: input.store, setStore: input.setStore, sdk: input.sdk }).then(() => - batch(() => { - for (const sessionID of Object.keys(input.store.permission)) { - if (grouped[sessionID]) continue - input.setStore("permission", sessionID, []) - } - for (const [sessionID, permissions] of Object.entries(grouped)) { - input.setStore( - "permission", - sessionID, - reconcile( - permissions.filter((p) => !!p?.id).sort((a, b) => cmp(a.id, b.id)), - { key: "id" }, - ), - ) - } - }), - ) - }), - ), - () => - retry(() => - input.sdk.question.list().then((x) => { - const ids = (x.data ?? []).map((question) => question?.sessionID).filter((id): id is string => !!id) - const grouped = groupBySession((x.data ?? []).filter((q): q is QuestionRequest => !!q?.id && !!q.sessionID)) - return warmSessions({ ids, store: input.store, setStore: input.setStore, sdk: input.sdk }).then(() => - batch(() => { - for (const sessionID of Object.keys(input.store.question)) { - if (grouped[sessionID]) continue - input.setStore("question", sessionID, []) - } - for (const [sessionID, questions] of Object.entries(grouped)) { - input.setStore( - "question", - sessionID, - reconcile( - questions.filter((q) => !!q?.id).sort((a, b) => cmp(a.id, b.id)), - { key: "id" }, - ), - ) - } - }), - ) - }), - ), - () => Promise.resolve(input.loadSessions(input.directory)), - () => - retry(() => - input.sdk.mcp.status().then((x) => { - input.setStore("mcp", x.data!) - input.setStore("mcp_ready", true) - }), - ), - ] + const fast = [() => Promise.resolve(input.loadSessions(input.directory))] const errs = errors(await runAll(fast)) if (errs.length > 0) { @@ -325,36 +235,138 @@ export async function bootstrapDirectory(input: { }) } - await waitForPaint() - const slowErrs = errors(await runAll(slow)) - if (slowErrs.length > 0) { - console.error("Failed to finish bootstrap instance", slowErrs[0]) - const project = getFilename(input.directory) - showToast({ - variant: "error", - title: input.translate("toast.project.reloadFailed.title", { project }), - description: formatServerError(slowErrs[0], input.translate), - }) - } - - if (loading && errs.length === 0 && slowErrs.length === 0) input.setStore("status", "complete") + ;(async () => { + const slow = [ + () => + input.queryClient.ensureQueryData({ + ...loadAgentsQuery(input.directory), + queryFn: () => + retry(() => input.sdk.app.agents().then((x) => input.setStore("agent", normalizeAgentList(x.data)))).then( + () => null, + ), + }), + () => retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))), + () => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))), + () => + seededProject + ? Promise.resolve() + : retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id)), + () => + seededPath + ? Promise.resolve() + : retry(() => + input.sdk.path.get().then((x) => { + input.setStore("path", x.data!) + const next = projectID(x.data?.directory ?? input.directory, input.global.project) + if (next) input.setStore("project", next) + }), + ), + () => + retry(() => + input.sdk.vcs.get().then((x) => { + const next = x.data ?? input.store.vcs + input.setStore("vcs", next) + if (next) input.vcsCache.setStore("value", next) + }), + ), + () => retry(() => input.sdk.command.list().then((x) => input.setStore("command", x.data ?? []))), + () => + retry(() => + input.sdk.permission.list().then((x) => { + const ids = (x.data ?? []).map((perm) => perm?.sessionID).filter((id): id is string => !!id) + const grouped = groupBySession( + (x.data ?? []).filter((perm): perm is PermissionRequest => !!perm?.id && !!perm.sessionID), + ) + return warmSessions({ ids, store: input.store, setStore: input.setStore, sdk: input.sdk }).then(() => + batch(() => { + for (const sessionID of Object.keys(input.store.permission)) { + if (grouped[sessionID]) continue + input.setStore("permission", sessionID, []) + } + for (const [sessionID, permissions] of Object.entries(grouped)) { + input.setStore( + "permission", + sessionID, + reconcile( + permissions.filter((p) => !!p?.id).sort((a, b) => cmp(a.id, b.id)), + { key: "id" }, + ), + ) + } + }), + ) + }), + ), + () => + retry(() => + input.sdk.question.list().then((x) => { + const ids = (x.data ?? []).map((question) => question?.sessionID).filter((id): id is string => !!id) + const grouped = groupBySession((x.data ?? []).filter((q): q is QuestionRequest => !!q?.id && !!q.sessionID)) + return warmSessions({ ids, store: input.store, setStore: input.setStore, sdk: input.sdk }).then(() => + batch(() => { + for (const sessionID of Object.keys(input.store.question)) { + if (grouped[sessionID]) continue + input.setStore("question", sessionID, []) + } + for (const [sessionID, questions] of Object.entries(grouped)) { + input.setStore( + "question", + sessionID, + reconcile( + questions.filter((q) => !!q?.id).sort((a, b) => cmp(a.id, b.id)), + { key: "id" }, + ), + ) + } + }), + ) + }), + ), + () => Promise.resolve(input.loadSessions(input.directory)), + () => + retry(() => + input.sdk.mcp.status().then((x) => { + input.setStore("mcp", x.data!) + input.setStore("mcp_ready", true) + }), + ), + ] - const rev = (providerRev.get(input.directory) ?? 0) + 1 - providerRev.set(input.directory, rev) - void retry(() => input.sdk.provider.list()) - .then((x) => { - if (providerRev.get(input.directory) !== rev) return - input.setStore("provider", normalizeProviderList(x.data!)) - input.setStore("provider_ready", true) - }) - .catch((err) => { - if (providerRev.get(input.directory) !== rev) return - console.error("Failed to refresh provider list", err) + await waitForPaint() + const slowErrs = errors(await runAll(slow)) + if (slowErrs.length > 0) { + console.error("Failed to finish bootstrap instance", slowErrs[0]) const project = getFilename(input.directory) showToast({ variant: "error", title: input.translate("toast.project.reloadFailed.title", { project }), - description: formatServerError(err, input.translate), + description: formatServerError(slowErrs[0], input.translate), }) + } + + if (loading && errs.length === 0 && slowErrs.length === 0) input.setStore("status", "complete") + + const rev = (providerRev.get(input.directory) ?? 0) + 1 + providerRev.set(input.directory, rev) + void input.queryClient.ensureQueryData({ + ...loadSessionsQuery(input.directory), + queryFn: () => + retry(() => input.sdk.provider.list()) + .then((x) => { + if (providerRev.get(input.directory) !== rev) return + input.setStore("provider", normalizeProviderList(x.data!)) + input.setStore("provider_ready", true) + }) + .catch((err) => { + if (providerRev.get(input.directory) !== rev) console.error("Failed to refresh provider list", err) + const project = getFilename(input.directory) + showToast({ + variant: "error", + title: input.translate("toast.project.reloadFailed.title", { project }), + description: formatServerError(err, input.translate), + }) + }) + .then(() => null), }) + })() } diff --git a/packages/app/src/context/global-sync/child-store.ts b/packages/app/src/context/global-sync/child-store.ts index 5678491f8..6788e8cc5 100644 --- a/packages/app/src/context/global-sync/child-store.ts +++ b/packages/app/src/context/global-sync/child-store.ts @@ -182,6 +182,7 @@ export function createChildStoreManager(input: { limit: 5, message: {}, part: {}, + bootstrapPromise: Promise.resolve(), }) children[directory] = child disposers.set(directory, dispose) @@ -243,8 +244,8 @@ export function createChildStoreManager(input: { const cached = metaCache.get(directory) if (!cached) return const previous = store.projectMeta ?? {} - const icon = patch.icon ? { ...(previous.icon ?? {}), ...patch.icon } : previous.icon - const commands = patch.commands ? { ...(previous.commands ?? {}), ...patch.commands } : previous.commands + const icon = patch.icon ? { ...previous.icon, ...patch.icon } : previous.icon + const commands = patch.commands ? { ...previous.commands, ...patch.commands } : previous.commands const next = { ...previous, ...patch, diff --git a/packages/app/src/context/global-sync/event-reducer.ts b/packages/app/src/context/global-sync/event-reducer.ts index 500013c1d..11a0cf83f 100644 --- a/packages/app/src/context/global-sync/event-reducer.ts +++ b/packages/app/src/context/global-sync/event-reducer.ts @@ -1,4 +1,4 @@ -import { Binary } from "@opencode-ai/util/binary" +import { Binary } from "@opencode-ai/shared/util/binary" import { produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store" import type { Message, diff --git a/packages/app/src/context/global-sync/queue.ts b/packages/app/src/context/global-sync/queue.ts index c3468583b..5c228dac0 100644 --- a/packages/app/src/context/global-sync/queue.ts +++ b/packages/app/src/context/global-sync/queue.ts @@ -63,6 +63,7 @@ export function createRefreshQueue(input: QueueInput) { } } finally { running = false + // oxlint-disable-next-line no-unsafe-finally -- intentional: early return skips schedule() when paused if (input.paused()) return if (root || queued.size) schedule() } diff --git a/packages/app/src/context/global-sync/types.ts b/packages/app/src/context/global-sync/types.ts index b0f340a90..28b3705d1 100644 --- a/packages/app/src/context/global-sync/types.ts +++ b/packages/app/src/context/global-sync/types.ts @@ -8,7 +8,6 @@ import type { Part, Path, PermissionRequest, - Project, ProviderListResponse, QuestionRequest, Session, @@ -73,6 +72,7 @@ export type State = { part: { [messageID: string]: Part[] } + bootstrapPromise: Promise<void> } export type VcsCache = { diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index bab3d39f3..74ea28531 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -344,7 +344,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( return } - setStore("sessionView", sessionKey, "scroll", (prev) => ({ ...(prev ?? {}), ...next })) + setStore("sessionView", sessionKey, "scroll", (prev) => ({ ...prev, ...next })) prune(keep) }, }) @@ -399,7 +399,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( local?.icon?.color !== undefined const base = { - ...(metadata ?? {}), + ...metadata, ...project, icon: { url: metadata?.icon?.url, @@ -582,7 +582,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( open(directory: string) { const root = rootFor(directory) if (server.projects.list().find((x) => x.worktree === root)) return - globalSync.project.loadSessions(root) + void globalSync.project.loadSessions(root) server.projects.open(root) }, close(directory: string) { diff --git a/packages/app/src/context/local.tsx b/packages/app/src/context/local.tsx index 1633607de..0b0972ee6 100644 --- a/packages/app/src/context/local.tsx +++ b/packages/app/src/context/local.tsx @@ -1,11 +1,10 @@ import { createSimpleContext } from "@opencode-ai/ui/context" -import { base64Encode } from "@opencode-ai/util/encode" +import { base64Encode } from "@opencode-ai/shared/util/encode" import { useParams } from "@solidjs/router" -import { batch, createEffect, createMemo, onCleanup } from "solid-js" +import { batch, createEffect, createMemo } from "solid-js" import { createStore } from "solid-js/store" import { useModels } from "@/context/models" import { useProviders } from "@/hooks/use-providers" -import { modelEnabled, modelProbe } from "@/testing/model-selection" import { Persist, persisted } from "@/utils/persist" import { cycleModelVariant, getConfiguredAgentVariant, resolveModelVariant } from "./model-variant" import { useSDK } from "./sdk" @@ -388,53 +387,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ }, }, } - - if (modelEnabled()) { - const probe = Symbol("model-probe") - - modelProbe.bind(probe, { - setAgent: agent.set, - setModel: model.set, - setVariant: model.variant.set, - }) - - createEffect(() => { - const agent = result.agent.current() - const model = result.model.current() - modelProbe.set(probe, { - dir: sdk.directory, - sessionID: id(), - last: store.last, - agent: agent?.name, - model: model - ? { - providerID: model.provider.id, - modelID: model.id, - name: model.name, - } - : undefined, - variant: result.model.variant.current() ?? null, - selected: result.model.variant.selected(), - configured: result.model.variant.configured(), - pick: scope(), - base: undefined, - current: store.current, - variants: result.model.variant.list(), - models: result.model - .list() - .filter((item) => result.model.visible({ providerID: item.provider.id, modelID: item.id })) - .map((item) => ({ - providerID: item.provider.id, - modelID: item.id, - name: item.name, - })), - agents: result.agent.list().map((item) => ({ name: item.name })), - }) - }) - - onCleanup(() => modelProbe.clear(probe)) - } - return result }, }) diff --git a/packages/app/src/context/notification.tsx b/packages/app/src/context/notification.tsx index 281a1ef33..251b67b06 100644 --- a/packages/app/src/context/notification.tsx +++ b/packages/app/src/context/notification.tsx @@ -7,8 +7,8 @@ import { useGlobalSync } from "./global-sync" import { usePlatform } from "@/context/platform" import { useLanguage } from "@/context/language" import { useSettings } from "@/context/settings" -import { Binary } from "@opencode-ai/util/binary" -import { base64Encode } from "@opencode-ai/util/encode" +import { Binary } from "@opencode-ai/shared/util/binary" +import { base64Encode } from "@opencode-ai/shared/util/encode" import { decode64 } from "@/utils/base64" import { EventSessionError } from "@opencode-ai/sdk/v2" import { Persist, persisted } from "@/utils/persist" diff --git a/packages/app/src/context/permission-auto-respond.test.ts b/packages/app/src/context/permission-auto-respond.test.ts index 755611300..2f8ca6265 100644 --- a/packages/app/src/context/permission-auto-respond.test.ts +++ b/packages/app/src/context/permission-auto-respond.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test" import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client" -import { base64Encode } from "@opencode-ai/util/encode" +import { base64Encode } from "@opencode-ai/shared/util/encode" import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond" const session = (input: { id: string; parentID?: string }) => diff --git a/packages/app/src/context/permission-auto-respond.ts b/packages/app/src/context/permission-auto-respond.ts index b206deedf..2ebca3434 100644 --- a/packages/app/src/context/permission-auto-respond.ts +++ b/packages/app/src/context/permission-auto-respond.ts @@ -1,4 +1,4 @@ -import { base64Encode } from "@opencode-ai/util/encode" +import { base64Encode } from "@opencode-ai/shared/util/encode" export function acceptKey(sessionID: string, directory?: string) { if (!directory) return sessionID diff --git a/packages/app/src/context/prompt.tsx b/packages/app/src/context/prompt.tsx index 831fdbca8..9b666e5e7 100644 --- a/packages/app/src/context/prompt.tsx +++ b/packages/app/src/context/prompt.tsx @@ -1,5 +1,5 @@ import { createSimpleContext } from "@opencode-ai/ui/context" -import { checksum } from "@opencode-ai/util/encode" +import { checksum } from "@opencode-ai/shared/util/encode" import { useParams } from "@solidjs/router" import { batch, createMemo, createRoot, getOwner, onCleanup } from "solid-js" import { createStore, type SetStoreFunction } from "solid-js/store" diff --git a/packages/app/src/context/sync.tsx b/packages/app/src/context/sync.tsx index fb02a2d2d..29b7fe68c 100644 --- a/packages/app/src/context/sync.tsx +++ b/packages/app/src/context/sync.tsx @@ -1,7 +1,7 @@ import { batch, createMemo } from "solid-js" import { createStore, produce, reconcile } from "solid-js/store" -import { Binary } from "@opencode-ai/util/binary" -import { retry } from "@opencode-ai/util/retry" +import { Binary } from "@opencode-ai/shared/util/binary" +import { retry } from "@opencode-ai/shared/util/retry" import { createSimpleContext } from "@opencode-ai/ui/context" import { clearSessionPrefetch, diff --git a/packages/app/src/context/terminal.tsx b/packages/app/src/context/terminal.tsx index 17355aab9..31d2d6e04 100644 --- a/packages/app/src/context/terminal.tsx +++ b/packages/app/src/context/terminal.tsx @@ -117,7 +117,7 @@ export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[], plat entry?.value.clear() } - removePersisted(Persist.workspace(dir, "terminal"), platform) + void removePersisted(Persist.workspace(dir, "terminal"), platform) const legacy = new Set(getLegacyTerminalStorageKeys(dir)) for (const id of sessionIDs ?? []) { @@ -126,7 +126,7 @@ export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[], plat } } for (const key of legacy) { - removePersisted({ key }, platform) + void removePersisted({ key }, platform) } } |
