fix(app): own v2 rendering projections
This commit is contained in:
parent
689fb214e0
commit
d4105e04e5
6 changed files with 209 additions and 45 deletions
7
bun.lock
7
bun.lock
|
|
@ -64,7 +64,6 @@
|
|||
"@opencode-ai/client": "workspace:*",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@opencode-ai/sdk": "file:vendor/opencode-ai-sdk-1.18.8-dev.tgz",
|
||||
"@opencode-ai/session-ui": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@pierre/trees": "1.0.0-beta.4",
|
||||
|
|
@ -4069,10 +4068,10 @@
|
|||
|
||||
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||
|
||||
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
|
||||
|
||||
"ffi-rs": ["ffi-rs@1.3.2", "", { "optionalDependencies": { "@yuuang/ffi-rs-android-arm64": "1.3.2", "@yuuang/ffi-rs-darwin-arm64": "1.3.2", "@yuuang/ffi-rs-darwin-x64": "1.3.2", "@yuuang/ffi-rs-linux-arm-gnueabihf": "1.3.2", "@yuuang/ffi-rs-linux-arm64-gnu": "1.3.2", "@yuuang/ffi-rs-linux-arm64-musl": "1.3.2", "@yuuang/ffi-rs-linux-x64-gnu": "1.3.2", "@yuuang/ffi-rs-linux-x64-musl": "1.3.2", "@yuuang/ffi-rs-win32-arm64-msvc": "1.3.2", "@yuuang/ffi-rs-win32-ia32-msvc": "1.3.2", "@yuuang/ffi-rs-win32-x64-msvc": "1.3.2" } }, "sha512-4s8dX9VbBw/jd5NOuE3EJRqXaIVdjMyiumeeDzrOhtjQRwp6Bz2za7iksWXTnvTQKV/tTdm1s1w7mObe92zPjQ=="],
|
||||
|
||||
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
|
||||
|
||||
"filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
|
@ -6327,8 +6326,6 @@
|
|||
|
||||
"@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
|
||||
|
||||
"@opencode-ai/app/@opencode-ai/sdk": ["@opencode-ai/sdk@vendor/opencode-ai-sdk-1.18.8-dev.tgz", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-C2nfk4x0sPINwE5V6DPkFSuH3PkUmKPWHPzxpXC1j+3Ui5hslLCWJbkk8WcOG1Lyt3C0+yp4ea64v/kmtYCO4w=="],
|
||||
|
||||
"@opencode-ai/cli/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"@opencode-ai/console-app/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.7", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ=="],
|
||||
|
|
|
|||
|
|
@ -2,16 +2,8 @@ import { base64Encode } from "@opencode-ai/core/util/encode"
|
|||
import { Event } from "@opencode-ai/schema/event"
|
||||
import { SessionStatusEvent } from "@opencode-ai/schema/session-status-event"
|
||||
import { SessionV1 } from "@opencode-ai/schema/session-v1"
|
||||
import type {
|
||||
AssistantMessage,
|
||||
Message,
|
||||
Part,
|
||||
SessionV1Info,
|
||||
SessionStatus,
|
||||
ToolPart,
|
||||
ToolState,
|
||||
UserMessage,
|
||||
} from "@opencode-ai/client/promise"
|
||||
import type { SessionV1Info, SessionStatus } from "@opencode-ai/client/promise"
|
||||
import type { AssistantMessage, Message, Part, ToolPart, ToolState, UserMessage } from "../../../src/types"
|
||||
import { expect, type Page } from "@playwright/test"
|
||||
import { Schema } from "effect"
|
||||
import { mockOpenCodeServer } from "../../utils/mock-server"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"./performance/unit/visual-stability.test.ts",
|
||||
"./reproduction/timeline-suspense/**/*.ts",
|
||||
"./reproduction/timeline-suspense/**/*.tsx",
|
||||
"../src/types.ts",
|
||||
"../src/pages/session/timeline/observe-element-offset.ts",
|
||||
"./regression/new-session-panel-corner.spec.ts",
|
||||
"./regression/session-timeline-context-resize.spec.ts",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { OpenCodeEvent } from "@opencode-ai/client/promise"
|
||||
import type { Event } from "@/types"
|
||||
import type { Event, PermissionRequest } from "@/types"
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
||||
|
|
@ -39,9 +39,9 @@ export function adaptServerEvent(event: OpenCodeEvent): ServerEvent {
|
|||
event.data.source?.type === "tool"
|
||||
? { messageID: event.data.source.messageID, callID: event.data.source.callID }
|
||||
: undefined,
|
||||
},
|
||||
} satisfies PermissionRequest,
|
||||
current: event,
|
||||
} as ServerEvent
|
||||
}
|
||||
}
|
||||
return { id: event.id, type: event.type, properties: event.data, current: event } as ServerEvent
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const session = (id: string, parentID?: string): Session => ({
|
|||
type UserMessage = Extract<Message, { role: "user" }>
|
||||
type AssistantMessage = Extract<Message, { role: "assistant" }>
|
||||
type TextPart = Extract<Part, { type: "text" }>
|
||||
type CurrentToolObject = Extract<SessionMessageAssistantTool["state"], { status: "running" }>["input"]
|
||||
type MessageResponse = {
|
||||
data: { info: Message; parts: Part[] }[]
|
||||
response: { headers: Headers }
|
||||
|
|
@ -97,21 +98,21 @@ function currentMessages(data: MessageResponse["data"]): SessionMessageInfo[] {
|
|||
if (part.state.status === "running")
|
||||
return {
|
||||
status: "running" as const,
|
||||
input: part.state.input,
|
||||
metadata: part.state.metadata ?? {},
|
||||
input: part.state.input as CurrentToolObject,
|
||||
metadata: (part.state.metadata ?? {}) as CurrentToolObject,
|
||||
}
|
||||
if (part.state.status === "error")
|
||||
return {
|
||||
status: "error" as const,
|
||||
input: part.state.input,
|
||||
input: part.state.input as CurrentToolObject,
|
||||
error: { type: "tool_error", message: part.state.error },
|
||||
metadata: part.state.metadata,
|
||||
metadata: part.state.metadata as CurrentToolObject | undefined,
|
||||
}
|
||||
return {
|
||||
status: "completed" as const,
|
||||
input: part.state.input,
|
||||
input: part.state.input as CurrentToolObject,
|
||||
content: [{ type: "text" as const, text: part.state.output }],
|
||||
metadata: part.state.metadata,
|
||||
metadata: part.state.metadata as CurrentToolObject,
|
||||
}
|
||||
})()
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -1,59 +1,45 @@
|
|||
import type {
|
||||
AgentPart,
|
||||
AssistantMessage,
|
||||
EventSubscribeOutput,
|
||||
FileDiffInfo,
|
||||
FileDiffLegacyInfo,
|
||||
FilePart,
|
||||
FilePartSource,
|
||||
Message,
|
||||
Part,
|
||||
Project,
|
||||
QuestionAnswer,
|
||||
QuestionInfo,
|
||||
QuestionV2Request,
|
||||
QuestionRequest,
|
||||
ReferenceInfo,
|
||||
SessionInfo,
|
||||
SessionNotFoundError,
|
||||
SessionStatus,
|
||||
SessionV1Info,
|
||||
SessionsResponse,
|
||||
TextPart,
|
||||
ToolPart,
|
||||
UserMessage,
|
||||
} from "@opencode-ai/client/promise"
|
||||
import type { NormalizedProviderListResponse } from "@opencode-ai/session-ui/context"
|
||||
|
||||
export type {
|
||||
AgentPart,
|
||||
AssistantMessage,
|
||||
FilePart,
|
||||
FilePartSource,
|
||||
Message,
|
||||
Part,
|
||||
Project,
|
||||
QuestionAnswer,
|
||||
QuestionRequest,
|
||||
ReferenceInfo,
|
||||
SessionNotFoundError,
|
||||
SessionStatus,
|
||||
TextPart,
|
||||
ToolPart,
|
||||
UserMessage,
|
||||
}
|
||||
|
||||
export type Session = SessionV1Info
|
||||
export type SessionV2Info = SessionInfo
|
||||
export type V2SessionListResponse = SessionsResponse
|
||||
export type QuestionRequest = QuestionV2Request
|
||||
export type SnapshotFileDiff = FileDiffLegacyInfo
|
||||
export type VcsFileDiff = FileDiffInfo
|
||||
|
||||
export type Event = EventSubscribeOutput extends infer Item
|
||||
type CurrentEvent = EventSubscribeOutput extends infer Item
|
||||
? Item extends { type: infer Type extends string; data: infer Data }
|
||||
? { type: Type; properties: Data }
|
||||
: never
|
||||
: never
|
||||
|
||||
export type Event =
|
||||
| Exclude<CurrentEvent, { type: "permission.asked" }>
|
||||
| { type: "permission.asked"; properties: PermissionRequest }
|
||||
|
||||
export type EventSessionError = Extract<Event, { type: "session.error" }>
|
||||
|
||||
export type PermissionRequest = {
|
||||
|
|
@ -66,6 +52,193 @@ export type PermissionRequest = {
|
|||
tool?: { messageID: string; callID: string }
|
||||
}
|
||||
|
||||
type MessageError =
|
||||
| { name: "ProviderAuthError"; data: { providerID: string; message: string } }
|
||||
| { name: "UnknownError"; data: { message: string; ref?: string } }
|
||||
| { name: "MessageOutputLengthError"; data: Record<string, unknown> }
|
||||
| { name: "MessageAbortedError"; data: { message: string } }
|
||||
| { name: "StructuredOutputError"; data: { message: string; retries: number } }
|
||||
| { name: "ContextOverflowError"; data: { message: string; responseBody?: string } }
|
||||
| { name: "ContentFilterError"; data: { message: string } }
|
||||
| {
|
||||
name: "APIError"
|
||||
data: {
|
||||
message: string
|
||||
statusCode?: number
|
||||
isRetryable: boolean
|
||||
responseHeaders?: Record<string, string>
|
||||
responseBody?: string
|
||||
metadata?: Record<string, string>
|
||||
}
|
||||
}
|
||||
|
||||
export type UserMessage = {
|
||||
id: string
|
||||
sessionID: string
|
||||
role: "user"
|
||||
time: { created: number }
|
||||
format?: { type: "text" } | { type: "json_schema"; schema: Record<string, unknown>; retryCount?: number }
|
||||
summary?: { title?: string; body?: string; diffs: SnapshotFileDiff[] }
|
||||
agent: string
|
||||
model: { providerID: string; modelID: string; variant?: string }
|
||||
system?: string
|
||||
tools?: Record<string, boolean>
|
||||
}
|
||||
|
||||
export type AssistantMessage = {
|
||||
id: string
|
||||
sessionID: string
|
||||
role: "assistant"
|
||||
time: { created: number; completed?: number }
|
||||
error?: MessageError
|
||||
parentID: string
|
||||
modelID: string
|
||||
providerID: string
|
||||
mode: string
|
||||
agent: string
|
||||
path: { cwd: string; root: string }
|
||||
summary?: boolean
|
||||
cost: number
|
||||
tokens: {
|
||||
total?: number
|
||||
input: number
|
||||
output: number
|
||||
reasoning: number
|
||||
cache: { read: number; write: number }
|
||||
}
|
||||
structured?: unknown
|
||||
variant?: string
|
||||
finish?: string
|
||||
}
|
||||
|
||||
export type Message = UserMessage | AssistantMessage
|
||||
|
||||
type PartBase = { id: string; sessionID: string; messageID: string }
|
||||
|
||||
export type TextPart = PartBase & {
|
||||
type: "text"
|
||||
text: string
|
||||
synthetic?: boolean
|
||||
ignored?: boolean
|
||||
time?: { start: number; end?: number }
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
type FilePartSourceText = { value: string; start: number; end: number }
|
||||
type FileSource = { text: FilePartSourceText; type: "file"; path: string }
|
||||
type SymbolSource = {
|
||||
text: FilePartSourceText
|
||||
type: "symbol"
|
||||
path: string
|
||||
range: { start: { line: number; character: number }; end: { line: number; character: number } }
|
||||
name: string
|
||||
kind: number
|
||||
}
|
||||
type ResourceSource = { text: FilePartSourceText; type: "resource"; clientName: string; uri: string }
|
||||
|
||||
export type FilePartSource = FileSource | SymbolSource | ResourceSource
|
||||
|
||||
export type FilePart = PartBase & {
|
||||
type: "file"
|
||||
mime: string
|
||||
filename?: string
|
||||
url: string
|
||||
source?: FilePartSource
|
||||
}
|
||||
|
||||
export type ToolState =
|
||||
| { status: "pending"; input: Record<string, unknown>; raw: string }
|
||||
| {
|
||||
status: "running"
|
||||
input: Record<string, unknown>
|
||||
title?: string
|
||||
metadata?: Record<string, unknown>
|
||||
time: { start: number }
|
||||
}
|
||||
| {
|
||||
status: "completed"
|
||||
input: Record<string, unknown>
|
||||
output: string
|
||||
title: string
|
||||
metadata: Record<string, unknown>
|
||||
time: { start: number; end: number; compacted?: number }
|
||||
attachments?: FilePart[]
|
||||
}
|
||||
| {
|
||||
status: "error"
|
||||
input: Record<string, unknown>
|
||||
error: string
|
||||
metadata?: Record<string, unknown>
|
||||
time: { start: number; end: number }
|
||||
}
|
||||
|
||||
export type ToolPart = PartBase & {
|
||||
type: "tool"
|
||||
callID: string
|
||||
tool: string
|
||||
state: ToolState
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type AgentPart = PartBase & {
|
||||
type: "agent"
|
||||
name: string
|
||||
source?: { value: string; start: number; end: number }
|
||||
}
|
||||
|
||||
type ReasoningPart = PartBase & {
|
||||
type: "reasoning"
|
||||
text: string
|
||||
metadata?: Record<string, unknown>
|
||||
time: { start: number; end?: number }
|
||||
}
|
||||
|
||||
type SubtaskPart = PartBase & {
|
||||
type: "subtask"
|
||||
prompt: string
|
||||
description: string
|
||||
agent: string
|
||||
model?: { providerID: string; modelID: string }
|
||||
command?: string
|
||||
}
|
||||
|
||||
type StepStartPart = PartBase & { type: "step-start"; snapshot?: string }
|
||||
type StepFinishPart = PartBase & {
|
||||
type: "step-finish"
|
||||
reason: string
|
||||
snapshot?: string
|
||||
cost: number
|
||||
tokens: AssistantMessage["tokens"]
|
||||
}
|
||||
type SnapshotPart = PartBase & { type: "snapshot"; snapshot: string }
|
||||
type PatchPart = PartBase & { type: "patch"; hash: string; files: string[] }
|
||||
type RetryPart = PartBase & {
|
||||
type: "retry"
|
||||
attempt: number
|
||||
error: Extract<MessageError, { name: "APIError" }>
|
||||
time: { created: number }
|
||||
}
|
||||
type CompactionPart = PartBase & {
|
||||
type: "compaction"
|
||||
auto: boolean
|
||||
overflow?: boolean
|
||||
tail_start_id?: string
|
||||
}
|
||||
|
||||
export type Part =
|
||||
| TextPart
|
||||
| SubtaskPart
|
||||
| ReasoningPart
|
||||
| FilePart
|
||||
| ToolPart
|
||||
| StepStartPart
|
||||
| StepFinishPart
|
||||
| SnapshotPart
|
||||
| PatchPart
|
||||
| AgentPart
|
||||
| RetryPart
|
||||
| CompactionPart
|
||||
|
||||
export type Todo = {
|
||||
content: string
|
||||
status: string
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue