From 5592f5225b52aaa85f6e6129944e142e8d970f02 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 26 Jul 2026 20:15:43 -0400 Subject: [PATCH] fix(app): update remote sdk contracts --- .../app/src/context/global-sync/event-reducer.ts | 4 ++-- .../src/context/global-sync/session-cache.test.ts | 6 +++--- .../app/src/context/global-sync/session-cache.ts | 4 ++-- packages/app/src/context/global-sync/types.ts | 4 ++-- packages/app/src/context/server-sdk.tsx | 14 +++++++++++--- packages/app/src/context/server-session.ts | 6 +++--- packages/app/src/pages/session.tsx | 9 ++------- packages/app/src/pages/session/review-tab.tsx | 5 +++-- .../app/src/pages/session/session-side-panel.tsx | 5 +++-- .../app/src/pages/session/v2/review-diff-kinds.ts | 5 +++-- .../app/src/pages/session/v2/review-panel-v2.tsx | 5 +++-- packages/app/src/utils/diffs.test.ts | 4 ++-- packages/app/src/utils/diffs.ts | 8 ++++---- packages/enterprise/src/core/share.ts | 6 ++++-- packages/enterprise/src/routes/share/[shareID].tsx | 4 ++-- packages/session-ui/src/components/session-diff.ts | 4 ++-- .../session-ui/src/components/session-review.tsx | 8 +++++--- .../session-ui/src/components/session-turn.tsx | 4 ++-- packages/session-ui/src/context/data.tsx | 4 ++-- .../components/session-review-file-preview-v2.tsx | 4 ++-- 20 files changed, 62 insertions(+), 51 deletions(-) diff --git a/packages/app/src/context/global-sync/event-reducer.ts b/packages/app/src/context/global-sync/event-reducer.ts index 935fc14e336..bd1cd4788ad 100644 --- a/packages/app/src/context/global-sync/event-reducer.ts +++ b/packages/app/src/context/global-sync/event-reducer.ts @@ -8,8 +8,8 @@ import type { QuestionRequest, Session, SessionStatus, - FileDiffInfo, } from "@opencode-ai/sdk/v2/client" +import type { SessionDiff } from "@/utils/diffs" import type { State, VcsCache } from "./types" import { trimSessions } from "./session-trim" import { dropSessionCaches } from "./session-cache" @@ -171,7 +171,7 @@ export function applyDirectoryEvent(input: { break } case "session.diff": { - const props = event.properties as { sessionID: string; diff: FileDiffInfo[] } + const props = event.properties as { sessionID: string; diff: SessionDiff[] } input.setStore("session_diff", props.sessionID, reconcile(list(props.diff), { key: "file" })) break } diff --git a/packages/app/src/context/global-sync/session-cache.test.ts b/packages/app/src/context/global-sync/session-cache.test.ts index 41034186e6f..fbe1ccf1b1a 100644 --- a/packages/app/src/context/global-sync/session-cache.test.ts +++ b/packages/app/src/context/global-sync/session-cache.test.ts @@ -5,8 +5,8 @@ import type { PermissionRequest, QuestionRequest, SessionStatus, - FileDiffInfo, } from "@opencode-ai/sdk/v2/client" +import type { SessionDiff } from "@/utils/diffs" import { dropSessionCaches, pickSessionCacheEvictions } from "./session-cache" const msg = (id: string, sessionID: string) => @@ -32,7 +32,7 @@ describe("app session cache", () => { test("dropSessionCaches clears orphaned parts without message rows", () => { const store: { session_status: Record - session_diff: Record + session_diff: Record message: Record part: Record permission: Record @@ -63,7 +63,7 @@ describe("app session cache", () => { const m = msg("msg_1", "ses_1") const store: { session_status: Record - session_diff: Record + session_diff: Record message: Record part: Record permission: Record diff --git a/packages/app/src/context/global-sync/session-cache.ts b/packages/app/src/context/global-sync/session-cache.ts index 6f2c3dc540b..4255b5586d7 100644 --- a/packages/app/src/context/global-sync/session-cache.ts +++ b/packages/app/src/context/global-sync/session-cache.ts @@ -4,14 +4,14 @@ import type { PermissionRequest, QuestionRequest, SessionStatus, - FileDiffInfo, } from "@opencode-ai/sdk/v2/client" +import type { SessionDiff } from "@/utils/diffs" export const SESSION_CACHE_LIMIT = 40 type SessionCache = { session_status: Record - session_diff: Record + session_diff: Record message: Record part: Record permission: Record diff --git a/packages/app/src/context/global-sync/types.ts b/packages/app/src/context/global-sync/types.ts index 53191b756de..698942efd92 100644 --- a/packages/app/src/context/global-sync/types.ts +++ b/packages/app/src/context/global-sync/types.ts @@ -13,9 +13,9 @@ import type { ReferenceInfo, Session, SessionStatus, - FileDiffInfo, VcsInfo, } from "@opencode-ai/sdk/v2/client" +import type { SessionDiff } from "@/utils/diffs" import { NormalizedProviderListResponse } from "@opencode-ai/session-ui/context" import type { Accessor } from "solid-js" import type { SetStoreFunction, Store } from "solid-js/store" @@ -50,7 +50,7 @@ export type State = { } session_working(id: string): boolean session_diff: { - [sessionID: string]: FileDiffInfo[] + [sessionID: string]: SessionDiff[] } permission: { [sessionID: string]: PermissionRequest[] diff --git a/packages/app/src/context/server-sdk.tsx b/packages/app/src/context/server-sdk.tsx index 4606363b7ed..b7fd0410dc0 100644 --- a/packages/app/src/context/server-sdk.tsx +++ b/packages/app/src/context/server-sdk.tsx @@ -1,5 +1,5 @@ import type { OpenCodeEvent } from "@opencode-ai/client/promise" -import type { Event } from "@opencode-ai/sdk/v2/client" +import type { Event, PermissionRequest } from "@opencode-ai/sdk/v2/client" import { createSimpleContext } from "@opencode-ai/ui/context" import { createGlobalEmitter } from "@solid-primitives/event-bus" import { makeEventListener } from "@solid-primitives/event-listener" @@ -18,7 +18,15 @@ const isAbortError = (error: unknown) => error !== null && typeof error === "object" && "name" in error && error.name === "AbortError" const isStreamClosed = (error: unknown, signal?: AbortSignal) => isAbortError(error) || signal?.aborted === true -export type ServerEvent = Event & { current?: OpenCodeEvent } +type PermissionEvent = { + id: string + type: "permission.v2.asked" + properties: PermissionRequest + current?: OpenCodeEvent +} +export type ServerEvent = (Event | PermissionEvent) & { + current?: OpenCodeEvent +} type QueuedServerEvent = { directory: string; payload: ServerEvent } type CurrentDelta = Extract< OpenCodeEvent, @@ -43,7 +51,7 @@ export function adaptServerEvent(event: OpenCodeEvent): ServerEvent { : undefined, }, current: event, - } as ServerEvent + } } if (event.type === "permission.v2.replied") return { id: event.id, type: "permission.v2.replied", properties: event.data, current: event } as ServerEvent diff --git a/packages/app/src/context/server-session.ts b/packages/app/src/context/server-session.ts index 623c95eb4da..b2db827620a 100644 --- a/packages/app/src/context/server-session.ts +++ b/packages/app/src/context/server-session.ts @@ -8,8 +8,8 @@ import type { QuestionRequest, Session, SessionStatus, - FileDiffInfo, } from "@opencode-ai/sdk/v2/client" +import type { SessionDiff } from "@/utils/diffs" import { batch } from "solid-js" import { createStore, produce, reconcile } from "solid-js/store" import { diffs as cleanDiffs, message as cleanMessage } from "@/utils/diffs" @@ -140,7 +140,7 @@ export function createServerSession(client: OpencodeClient, options?: { retry?: const [data, setData] = createStore({ info: {} as Record, session_status: {} as Record, - session_diff: {} as Record, + session_diff: {} as Record, permission: {} as Record, question: {} as Record, message: {} as Record, @@ -773,7 +773,7 @@ export function createServerSession(client: OpencodeClient, options?: { retry?: return } case "session.diff": { - const props = event.properties as { sessionID: string; diff: FileDiffInfo[] } + const props = event.properties as { sessionID: string; diff: SessionDiff[] } setData("session_diff", props.sessionID, reconcile(cleanDiffs(props.diff), { key: "file" })) return } diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 2fade924c41..f7fe46922e2 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -909,13 +909,8 @@ export default function Page() { ) const stopVcs = sdk().event.listen((evt) => { - if (evt.details.type !== "filesystem.changed") return - const props = - typeof evt.details.properties === "object" && evt.details.properties - ? (evt.details.properties as Record) - : undefined - const file = typeof props?.file === "string" ? props.file : undefined - if (!file || file.startsWith(".git/")) return + if (evt.details.type !== "file.watcher.updated") return + if (evt.details.properties.file.startsWith(".git/")) return refreshVcs() }) onCleanup(stopVcs) diff --git a/packages/app/src/pages/session/review-tab.tsx b/packages/app/src/pages/session/review-tab.tsx index 586942399d8..65b0ebdfb6d 100644 --- a/packages/app/src/pages/session/review-tab.tsx +++ b/packages/app/src/pages/session/review-tab.tsx @@ -1,6 +1,7 @@ import { createEffect, onCleanup, type JSX } from "solid-js" import { makeEventListener } from "@solid-primitives/event-listener" -import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { SessionDiff } from "@/utils/diffs" import { SessionReview } from "@opencode-ai/session-ui/session-review" import type { SessionReviewCommentActions, @@ -14,7 +15,7 @@ import type { LineComment } from "@/context/comments" export type DiffStyle = "unified" | "split" -type ReviewDiff = FileDiffInfo | VcsFileDiff +type ReviewDiff = SessionDiff | VcsFileDiff export interface SessionReviewTabProps { title?: JSX.Element diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index 7bba5c803a3..cd20f56bbc9 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -23,7 +23,8 @@ import { Mark } from "@opencode-ai/ui/logo" import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2" import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2" import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2" -import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { SessionDiff } from "@/utils/diffs" import { ConstrainDragYAxis, getDraggableId } from "@/utils/solid-dnd" import { useDialog } from "@opencode-ai/ui/context/dialog" @@ -59,7 +60,7 @@ import type { RenderDiff } from "@/pages/session/v2/review-diff-kinds" export function SessionSidePanel(props: { canReview: () => boolean - diffs: () => (FileDiffInfo | VcsFileDiff)[] + diffs: () => (SessionDiff | VcsFileDiff)[] diffsReady: () => boolean empty: () => string hasReview: () => boolean diff --git a/packages/app/src/pages/session/v2/review-diff-kinds.ts b/packages/app/src/pages/session/v2/review-diff-kinds.ts index 8b252b02586..2e68d9f3dd2 100644 --- a/packages/app/src/pages/session/v2/review-diff-kinds.ts +++ b/packages/app/src/pages/session/v2/review-diff-kinds.ts @@ -1,8 +1,9 @@ -import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { SessionDiff } from "@/utils/diffs" import type { Kind } from "@/components/file-tree-v2" import { normalizeFileTreeV2Path } from "@/components/file-tree-v2-model" -export type RenderDiff = FileDiffInfo | VcsFileDiff +export type RenderDiff = SessionDiff | VcsFileDiff export function normalizePath(p: string) { return normalizeFileTreeV2Path(p) diff --git a/packages/app/src/pages/session/v2/review-panel-v2.tsx b/packages/app/src/pages/session/v2/review-panel-v2.tsx index bcf53c1d707..8aae1ba7761 100644 --- a/packages/app/src/pages/session/v2/review-panel-v2.tsx +++ b/packages/app/src/pages/session/v2/review-panel-v2.tsx @@ -1,5 +1,6 @@ import { createMemo, createResource, createSignal, Show, type JSX } from "solid-js" -import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { SessionDiff } from "@/utils/diffs" import { SESSION_REVIEW_V2_SIDEBAR_WIDTH_MAX, SESSION_REVIEW_V2_SIDEBAR_WIDTH_MIN, @@ -29,7 +30,7 @@ import { import type { ReviewPanelV2State } from "@/pages/session/v2/review-panel-v2-state" import { applyFileListKeyDown, SessionFileListV2 } from "@/pages/session/v2/session-file-list-v2" -type ReviewDiff = FileDiffInfo | VcsFileDiff +type ReviewDiff = SessionDiff | VcsFileDiff export type ReviewPanelV2Props = { title?: JSX.Element diff --git a/packages/app/src/utils/diffs.test.ts b/packages/app/src/utils/diffs.test.ts index f6d768e1de2..5fbca469b71 100644 --- a/packages/app/src/utils/diffs.test.ts +++ b/packages/app/src/utils/diffs.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { FileDiffInfo } from "@opencode-ai/sdk/v2" +import type { SnapshotFileDiff } from "@opencode-ai/sdk/v2" import type { Message } from "@opencode-ai/sdk/v2/client" import { diffs, message } from "./diffs" @@ -9,7 +9,7 @@ const item = { additions: 1, deletions: 1, status: "modified", -} satisfies FileDiffInfo +} satisfies SnapshotFileDiff describe("diffs", () => { test("keeps valid arrays", () => { diff --git a/packages/app/src/utils/diffs.ts b/packages/app/src/utils/diffs.ts index 60df039410b..b477a33f12a 100644 --- a/packages/app/src/utils/diffs.ts +++ b/packages/app/src/utils/diffs.ts @@ -1,9 +1,9 @@ -import type { FileDiffInfo } from "@opencode-ai/sdk/v2" +import type { SnapshotFileDiff } from "@opencode-ai/sdk/v2" import type { Message } from "@opencode-ai/sdk/v2/client" -type Diff = FileDiffInfo +export type SessionDiff = SnapshotFileDiff & { file: string; patch: string } -function diff(value: unknown): value is Diff { +function diff(value: unknown): value is SessionDiff { if (!value || typeof value !== "object" || Array.isArray(value)) return false if (!("file" in value) || typeof value.file !== "string") return false if (!("patch" in value) || typeof value.patch !== "string") return false @@ -17,7 +17,7 @@ function object(value: unknown): value is Record { return !!value && typeof value === "object" && !Array.isArray(value) } -export function diffs(value: unknown): Diff[] { +export function diffs(value: unknown): SessionDiff[] { if (Array.isArray(value) && value.every(diff)) return value if (Array.isArray(value)) return value.filter(diff) if (diff(value)) return [value] diff --git a/packages/enterprise/src/core/share.ts b/packages/enterprise/src/core/share.ts index ce429323d88..0232feff646 100644 --- a/packages/enterprise/src/core/share.ts +++ b/packages/enterprise/src/core/share.ts @@ -1,4 +1,4 @@ -import { FileDiffInfo, Message, Model, Part, Session } from "@opencode-ai/sdk/v2" +import { Message, Model, Part, Session, SnapshotFileDiff } from "@opencode-ai/sdk/v2" import { iife } from "@opencode-ai/core/util/iife" import z from "zod" import { Storage } from "./storage" @@ -8,6 +8,8 @@ function fn(schema: T, cb: (input: z.infer) => R } export namespace Share { + export type SessionDiff = SnapshotFileDiff & { file: string; patch: string } + export const Info = z.object({ id: z.string(), secret: z.string(), @@ -30,7 +32,7 @@ export namespace Share { }), z.object({ type: z.literal("session_diff"), - data: z.custom(), + data: z.custom(), }), z.object({ type: z.literal("model"), diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index 91cb50891de..dea83faed16 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -1,4 +1,4 @@ -import { FileDiffInfo, Message, Model, Part, Session, SessionStatus, UserMessage } from "@opencode-ai/sdk/v2" +import { Message, Model, Part, Session, SessionStatus, UserMessage } from "@opencode-ai/sdk/v2" import { SessionTurn } from "@opencode-ai/session-ui/session-turn" import { SessionReview } from "@opencode-ai/session-ui/session-review" import { DataProvider } from "@opencode-ai/session-ui/context" @@ -65,7 +65,7 @@ const getData = query(async (shareID) => { shareID: string session: Session[] session_diff: { - [sessionID: string]: FileDiffInfo[] + [sessionID: string]: Share.SessionDiff[] } session_status: { [sessionID: string]: SessionStatus diff --git a/packages/session-ui/src/components/session-diff.ts b/packages/session-ui/src/components/session-diff.ts index 6fccfc305de..ba0d5097cd7 100644 --- a/packages/session-ui/src/components/session-diff.ts +++ b/packages/session-ui/src/components/session-diff.ts @@ -1,6 +1,6 @@ import { parseDiffFromFile, parsePatchFiles, type FileDiffMetadata } from "@pierre/diffs" import { parsePatch } from "diff" -import type { FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2" type LegacyDiff = { file: string @@ -12,7 +12,7 @@ type LegacyDiff = { status?: "added" | "deleted" | "modified" } -type ReviewDiff = FileDiffInfo | VcsFileDiff | LegacyDiff +type ReviewDiff = (SnapshotFileDiff | VcsFileDiff | LegacyDiff) & { file: string } export type DiffSource = Pick export type ViewDiff = { diff --git a/packages/session-ui/src/components/session-review.tsx b/packages/session-ui/src/components/session-review.tsx index ec94af2f2d0..07ac0156d90 100644 --- a/packages/session-ui/src/components/session-review.tsx +++ b/packages/session-ui/src/components/session-review.tsx @@ -15,7 +15,7 @@ import { getDirectory, getFilename } from "@opencode-ai/core/util/path" import { checksum } from "@opencode-ai/core/util/encode" import { createEffect, createMemo, For, Match, onCleanup, Show, Switch, untrack, type JSX } from "solid-js" import { createStore } from "solid-js/store" -import { type FileContent, type FileDiffInfo, type VcsFileDiff } from "@opencode-ai/sdk/v2" +import { type FileContent, type SnapshotFileDiff, type VcsFileDiff } from "@opencode-ai/sdk/v2" import { PreloadMultiFileDiffResult } from "@pierre/diffs/ssr" import { type SelectedLineRange } from "@pierre/diffs" import { Dynamic } from "solid-js/web" @@ -62,10 +62,12 @@ export type SessionReviewCommentActions = { export type SessionReviewFocus = { file: string; id: string } -type RawReviewDiff = (FileDiffInfo | VcsFileDiff) & { +type RawReviewDiff = (SnapshotFileDiff | VcsFileDiff) & { + file: string preloaded?: PreloadMultiFileDiffResult } -type ReviewDiff = (FileDiffInfo | VcsFileDiff) & { +type ReviewDiff = (SnapshotFileDiff | VcsFileDiff) & { + file: string preloaded?: PreloadMultiFileDiffResult } function diff(value: unknown): value is ReviewDiff { diff --git a/packages/session-ui/src/components/session-turn.tsx b/packages/session-ui/src/components/session-turn.tsx index a6d0f11efbc..e52d050a9a8 100644 --- a/packages/session-ui/src/components/session-turn.tsx +++ b/packages/session-ui/src/components/session-turn.tsx @@ -1,6 +1,6 @@ import { AssistantMessage, - type FileDiffInfo, + type SnapshotFileDiff, Message as MessageType, Part as PartType, type UserMessage, @@ -92,7 +92,7 @@ function list(value: T[] | undefined | null, fallback: T[]) { } type SummaryDiffInput = NonNullable["diffs"]>[number] -type SummaryDiff = FileDiffInfo +type SummaryDiff = SnapshotFileDiff & { file: string; patch: string } function summaryDiff(value: SummaryDiffInput): value is SummaryDiff { return ( diff --git a/packages/session-ui/src/context/data.tsx b/packages/session-ui/src/context/data.tsx index d505249931f..9d9dbbb4342 100644 --- a/packages/session-ui/src/context/data.tsx +++ b/packages/session-ui/src/context/data.tsx @@ -1,4 +1,4 @@ -import type { FileDiffInfo, Message, Part, Provider, Session, SessionStatus } from "@opencode-ai/sdk/v2" +import type { Message, Part, Provider, Session, SessionStatus, SnapshotFileDiff } from "@opencode-ai/sdk/v2" import { createSimpleContext } from "@opencode-ai/ui/context" import { PreloadMultiFileDiffResult } from "@pierre/diffs/ssr" @@ -21,7 +21,7 @@ type Data = { [sessionID: string]: SessionStatus } session_diff: { - [sessionID: string]: FileDiffInfo[] + [sessionID: string]: SnapshotFileDiff[] } session_diff_preload?: { [sessionID: string]: PreloadMultiFileDiffResult[] diff --git a/packages/session-ui/src/v2/components/session-review-file-preview-v2.tsx b/packages/session-ui/src/v2/components/session-review-file-preview-v2.tsx index deb1cabf212..81f4238ab43 100644 --- a/packages/session-ui/src/v2/components/session-review-file-preview-v2.tsx +++ b/packages/session-ui/src/v2/components/session-review-file-preview-v2.tsx @@ -6,7 +6,7 @@ import { useFileComponent } from "@opencode-ai/ui/context/file" import { useI18n } from "@opencode-ai/ui/context/i18n" import { mediaKindFromPath } from "../../pierre/media" import { cloneSelectedLineRange, previewSelectedLines } from "../../pierre/selection-bridge" -import type { FileContent, FileDiffInfo, VcsFileDiff } from "@opencode-ai/sdk/v2" +import type { FileContent, SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2" import { createEffect, createMemo, onCleanup, Show, untrack } from "solid-js" import { createStore } from "solid-js/store" import { Dynamic } from "solid-js/web" @@ -27,7 +27,7 @@ import { LineCommentV2OverflowIcon } from "@opencode-ai/ui/v2/line-comment-v2" import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2" import "./session-review-v2.css" -type ReviewDiff = FileDiffInfo | VcsFileDiff +type ReviewDiff = (SnapshotFileDiff | VcsFileDiff) & { file: string } export type SessionReviewFilePreviewV2Props = { file: string