refactor(core): move v1 schemas into core (#30473)

This commit is contained in:
Dax
2026-06-03 02:42:13 +00:00
committed by GitHub
parent 0543fd29c8
commit 83452558f7
129 changed files with 1578 additions and 1227 deletions
+47 -47
View File
@@ -1,6 +1,6 @@
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import path from "path"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import os from "os"
import { SessionID, MessageID, PartID } from "./schema"
import { MessageV2 } from "./message-v2"
@@ -66,8 +66,8 @@ import { LLMEvent } from "@opencode-ai/llm"
// @ts-ignore
globalThis.AI_SDK_LOG_WARNINGS = false
const decodeMessageInfo = Schema.decodeUnknownExit(SessionLegacy.Info)
const decodeMessagePart = Schema.decodeUnknownExit(SessionLegacy.Part)
const decodeMessageInfo = Schema.decodeUnknownExit(SessionV1.Info)
const decodeMessagePart = Schema.decodeUnknownExit(SessionV1.Part)
const STRUCTURED_OUTPUT_DESCRIPTION = `Use this tool to return your final response in the requested structured format.
@@ -82,7 +82,7 @@ const STRUCTURED_OUTPUT_SYSTEM_PROMPT = `IMPORTANT: The user has requested struc
const log = Log.create({ service: "session.prompt" })
const elog = EffectLogger.create({ service: "session.prompt" })
function isOrphanedInterruptedTool(part: SessionLegacy.ToolPart) {
function isOrphanedInterruptedTool(part: SessionV1.ToolPart) {
// cleanup() marks abandoned tool_use blocks this way after retries/aborts.
// They are not pending work and must not trigger an assistant-prefill request.
return part.state.status === "error" && part.state.metadata?.interrupted === true
@@ -90,10 +90,10 @@ function isOrphanedInterruptedTool(part: SessionLegacy.ToolPart) {
export interface Interface {
readonly cancel: (sessionID: SessionID) => Effect.Effect<void>
readonly prompt: (input: PromptInput) => Effect.Effect<SessionLegacy.WithParts, Image.Error>
readonly loop: (input: LoopInput) => Effect.Effect<SessionLegacy.WithParts>
readonly shell: (input: ShellInput) => Effect.Effect<SessionLegacy.WithParts, Session.BusyError>
readonly command: (input: CommandInput) => Effect.Effect<SessionLegacy.WithParts, Image.Error>
readonly prompt: (input: PromptInput) => Effect.Effect<SessionV1.WithParts, Image.Error>
readonly loop: (input: LoopInput) => Effect.Effect<SessionV1.WithParts>
readonly shell: (input: ShellInput) => Effect.Effect<SessionV1.WithParts, Session.BusyError>
readonly command: (input: CommandInput) => Effect.Effect<SessionV1.WithParts, Image.Error>
readonly resolvePromptParts: (template: string) => Effect.Effect<PromptInput["parts"]>
}
@@ -239,14 +239,14 @@ export const layer = Layer.effect(
const title = Effect.fn("SessionPrompt.ensureTitle")(function* (input: {
session: Session.Info
history: SessionLegacy.WithParts[]
history: SessionV1.WithParts[]
providerID: ProviderV2.ID
modelID: ProviderV2.ModelID
}) {
if (input.session.parentID) return
if (!Session.isDefaultTitle(input.session.title)) return
const real = (m: SessionLegacy.WithParts) =>
const real = (m: SessionV1.WithParts) =>
m.info.role === "user" && !m.parts.every((p) => "synthetic" in p && p.synthetic)
const idx = input.history.findIndex(real)
if (idx === -1) return
@@ -257,7 +257,7 @@ export const layer = Layer.effect(
if (!firstUser || firstUser.info.role !== "user") return
const firstInfo = firstUser.info
const subtasks = firstUser.parts.filter((p): p is SessionLegacy.SubtaskPart => p.type === "subtask")
const subtasks = firstUser.parts.filter((p): p is SessionV1.SubtaskPart => p.type === "subtask")
const onlySubtasks = subtasks.length > 0 && firstUser.parts.every((p) => p.type === "subtask")
const ag = yield* agents.get("title")
@@ -300,19 +300,19 @@ export const layer = Layer.effect(
})
const handleSubtask = Effect.fn("SessionPrompt.handleSubtask")(function* (input: {
task: SessionLegacy.SubtaskPart
task: SessionV1.SubtaskPart
model: Provider.Model
lastUser: SessionLegacy.User
lastUser: SessionV1.User
sessionID: SessionID
session: Session.Info
msgs: SessionLegacy.WithParts[]
msgs: SessionV1.WithParts[]
}) {
const { task, model, lastUser, sessionID, session, msgs } = input
const ctx = yield* InstanceState.context
const promptOps = yield* ops()
const { task: taskTool } = yield* registry.named()
const taskModel = task.model ? yield* getModel(task.model.providerID, task.model.modelID, sessionID) : model
const assistantMessage: SessionLegacy.Assistant = yield* sessions.updateMessage({
const assistantMessage: SessionV1.Assistant = yield* sessions.updateMessage({
id: MessageID.ascending(),
role: "assistant",
parentID: lastUser.id,
@@ -327,7 +327,7 @@ export const layer = Layer.effect(
providerID: taskModel.providerID,
time: { created: Date.now() },
})
let part: SessionLegacy.ToolPart = yield* sessions.updatePart({
let part: SessionV1.ToolPart = yield* sessions.updatePart({
id: PartID.ascending(),
messageID: assistantMessage.id,
sessionID: assistantMessage.sessionID,
@@ -383,7 +383,7 @@ export const layer = Layer.effect(
...part,
type: "tool",
state: { ...part.state, ...val },
} satisfies SessionLegacy.ToolPart)
} satisfies SessionV1.ToolPart)
}),
ask: (req: any) =>
permission
@@ -417,7 +417,7 @@ export const layer = Layer.effect(
metadata: part.state.metadata,
input: part.state.input,
},
} satisfies SessionLegacy.ToolPart)
} satisfies SessionV1.ToolPart)
}
}),
),
@@ -452,7 +452,7 @@ export const layer = Layer.effect(
attachments,
time: { ...part.state.time, end: Date.now() },
},
} satisfies SessionLegacy.ToolPart)
} satisfies SessionV1.ToolPart)
}
if (!result) {
@@ -468,12 +468,12 @@ export const layer = Layer.effect(
metadata: part.state.status === "pending" ? undefined : part.state.metadata,
input: part.state.input,
},
} satisfies SessionLegacy.ToolPart)
} satisfies SessionV1.ToolPart)
}
if (!task.command) return
const summaryUserMsg: SessionLegacy.User = {
const summaryUserMsg: SessionV1.User = {
id: MessageID.ascending(),
sessionID,
role: "user",
@@ -489,7 +489,7 @@ export const layer = Layer.effect(
type: "text",
text: "Summarize the task tool output above and continue with your task.",
synthetic: true,
} satisfies SessionLegacy.TextPart)
} satisfies SessionV1.TextPart)
})
const shellImpl = Effect.fn("SessionPrompt.shellImpl")(function* (input: ShellInput, ready?: Latch.Latch) {
@@ -511,7 +511,7 @@ export const layer = Layer.effect(
throw error
}
const model = input.model ?? agent.model ?? (yield* currentModel(input.sessionID))
const userMsg: SessionLegacy.User = {
const userMsg: SessionV1.User = {
id: input.messageID ?? MessageID.ascending(),
sessionID: input.sessionID,
time: { created: Date.now() },
@@ -520,7 +520,7 @@ export const layer = Layer.effect(
model: { providerID: model.providerID, modelID: model.modelID },
}
yield* sessions.updateMessage(userMsg)
const userPart: SessionLegacy.Part = {
const userPart: SessionV1.Part = {
type: "text",
id: PartID.ascending(),
messageID: userMsg.id,
@@ -530,7 +530,7 @@ export const layer = Layer.effect(
}
yield* sessions.updatePart(userPart)
const msg: SessionLegacy.Assistant = {
const msg: SessionV1.Assistant = {
id: MessageID.ascending(),
sessionID: input.sessionID,
parentID: userMsg.id,
@@ -546,7 +546,7 @@ export const layer = Layer.effect(
}
yield* sessions.updateMessage(msg)
const started = Date.now()
const part: SessionLegacy.ToolPart = {
const part: SessionV1.ToolPart = {
type: "tool",
id: PartID.ascending(),
messageID: msg.id,
@@ -719,7 +719,7 @@ export const layer = Layer.effect(
: undefined
const variant = input.variant ?? (ag.variant && full?.variants?.[ag.variant] ? ag.variant : undefined)
const info: SessionLegacy.User = {
const info: SessionV1.User = {
id: input.messageID ?? MessageID.ascending(),
role: "user",
sessionID: input.sessionID,
@@ -760,8 +760,8 @@ export const layer = Layer.effect(
yield* Effect.addFinalizer(() => instruction.clear(info.id))
type Draft<T> = T extends SessionLegacy.Part ? Omit<T, "id"> & { id?: string } : never
const assign = (part: Draft<SessionLegacy.Part>): SessionLegacy.Part => ({
type Draft<T> = T extends SessionV1.Part ? Omit<T, "id"> & { id?: string } : never
const assign = (part: Draft<SessionV1.Part>): SessionV1.Part => ({
...part,
id: part.id ? PartID.make(part.id) : PartID.ascending(),
})
@@ -790,14 +790,14 @@ export const layer = Layer.effect(
})
})
const resolvePart: (part: PromptInput["parts"][number]) => Effect.Effect<Draft<SessionLegacy.Part>[]> = Effect.fn(
const resolvePart: (part: PromptInput["parts"][number]) => Effect.Effect<Draft<SessionV1.Part>[]> = Effect.fn(
"SessionPrompt.resolveUserPart",
)(function* (part) {
if (part.type === "file") {
if (part.source?.type === "resource") {
const { clientName, uri } = part.source
log.info("mcp resource", { clientName, uri, mime: part.mime })
const pieces: Draft<SessionLegacy.Part>[] = [
const pieces: Draft<SessionV1.Part>[] = [
{
messageID: info.id,
sessionID: input.sessionID,
@@ -917,7 +917,7 @@ export const layer = Layer.effect(
if (end) limit = end - (offset - 1)
}
const args = { filePath: filepath, offset, limit }
const pieces: Draft<SessionLegacy.Part>[] = [
const pieces: Draft<SessionV1.Part>[] = [
...(referenceContext
? [{ ...referenceContext, messageID: info.id, sessionID: input.sessionID }]
: []),
@@ -1213,7 +1213,7 @@ export const layer = Layer.effect(
return { info, parts }
}, Effect.scoped)
const prompt: (input: PromptInput) => Effect.Effect<SessionLegacy.WithParts, Image.Error> = Effect.fn(
const prompt: (input: PromptInput) => Effect.Effect<SessionV1.WithParts, Image.Error> = Effect.fn(
"SessionPrompt.prompt",
)(function* (input: PromptInput) {
const session = yield* sessions.get(input.sessionID).pipe(Effect.orDie)
@@ -1221,7 +1221,7 @@ export const layer = Layer.effect(
const message = yield* createUserMessage(input)
yield* sessions.touch(input.sessionID)
const permissions: PermissionLegacy.Rule[] = []
const permissions: PermissionV1.Rule[] = []
for (const [t, enabled] of Object.entries(input.tools ?? {})) {
permissions.push({ permission: t, action: enabled ? "allow" : "deny", pattern: "*" })
}
@@ -1242,7 +1242,7 @@ export const layer = Layer.effect(
throw new Error("Impossible")
})
const runLoop: (sessionID: SessionID) => Effect.Effect<SessionLegacy.WithParts> = Effect.fn("SessionPrompt.run")(
const runLoop: (sessionID: SessionID) => Effect.Effect<SessionV1.WithParts> = Effect.fn("SessionPrompt.run")(
function* (sessionID: SessionID) {
const ctx = yield* InstanceState.context
const slog = elog.with({ sessionID })
@@ -1280,7 +1280,7 @@ export const layer = Layer.effect(
lastUser.id < lastAssistant.id
) {
const orphan = lastAssistantMsg?.parts.find(
(part): part is SessionLegacy.ToolPart => part.type === "tool" && isOrphanedInterruptedTool(part),
(part): part is SessionV1.ToolPart => part.type === "tool" && isOrphanedInterruptedTool(part),
)
if (orphan) {
yield* slog.warn("loop exit with orphaned interrupted tool", {
@@ -1347,7 +1347,7 @@ export const layer = Layer.effect(
Effect.provideService(Session.Service, sessions),
)
const msg: SessionLegacy.Assistant = {
const msg: SessionV1.Assistant = {
id: MessageID.ascending(),
parentID: lastUser.id,
role: "assistant",
@@ -1467,7 +1467,7 @@ export const layer = Layer.effect(
const finished = handle.message.finish && !["tool-calls", "unknown"].includes(handle.message.finish)
if (finished && !handle.message.error) {
if (format.type === "json_schema") {
handle.message.error = new SessionLegacy.StructuredOutputError({
handle.message.error = new SessionV1.StructuredOutputError({
message: "Model did not produce structured output",
retries: 0,
}).toObject()
@@ -1500,13 +1500,13 @@ export const layer = Layer.effect(
},
)
const loop: (input: LoopInput) => Effect.Effect<SessionLegacy.WithParts> = Effect.fn("SessionPrompt.loop")(
const loop: (input: LoopInput) => Effect.Effect<SessionV1.WithParts> = Effect.fn("SessionPrompt.loop")(
function* (input: LoopInput) {
return yield* state.ensureRunning(input.sessionID, lastAssistant(input.sessionID), runLoop(input.sessionID))
},
)
const shell: (input: ShellInput) => Effect.Effect<SessionLegacy.WithParts, Session.BusyError> = Effect.fn(
const shell: (input: ShellInput) => Effect.Effect<SessionV1.WithParts, Session.BusyError> = Effect.fn(
"SessionPrompt.shell",
)(function* (input: ShellInput) {
const ready = yield* Latch.make()
@@ -1691,15 +1691,15 @@ export const PromptInput = Schema.Struct({
description:
"@deprecated tools and permissions have been merged, you can set permissions on the session itself now",
}),
format: Schema.optional(SessionLegacy.Format),
format: Schema.optional(SessionV1.Format),
system: Schema.optional(Schema.String),
variant: Schema.optional(Schema.String),
parts: Schema.Array(
Schema.Union([
SessionLegacy.TextPartInput,
SessionLegacy.FilePartInput,
SessionLegacy.AgentPartInput,
SessionLegacy.SubtaskPartInput,
SessionV1.TextPartInput,
SessionV1.FilePartInput,
SessionV1.AgentPartInput,
SessionV1.SubtaskPartInput,
]).annotate({ discriminator: "type" }),
),
})
@@ -1738,7 +1738,7 @@ export const CommandInput = Schema.Struct({
mime: Schema.String,
filename: Schema.optional(Schema.String),
url: Schema.String,
source: Schema.optional(SessionLegacy.FilePartSource),
source: Schema.optional(SessionV1.FilePartSource),
}),
]).annotate({ discriminator: "type" }),
),