refactor(core): move v1 schemas into core (#30473)
This commit is contained in:
@@ -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" }),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user