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

This commit is contained in:
Dax
2026-06-02 22:42:13 -04:00
committed by GitHub
parent 0543fd29c8
commit 83452558f7
129 changed files with 1578 additions and 1227 deletions
+22 -21
View File
@@ -1,4 +1,5 @@
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
import { Session } from "./session"
import { SessionID, MessageID, PartID } from "./schema"
import { Provider } from "@/provider/provider"
@@ -93,9 +94,9 @@ type CompletedCompaction = {
summary: string | undefined
}
function summaryText(message: SessionLegacy.WithParts) {
function summaryText(message: SessionV1.WithParts) {
const text = message.parts
.filter((part): part is SessionLegacy.TextPart => part.type === "text")
.filter((part): part is SessionV1.TextPart => part.type === "text")
.map((part) => part.text.trim())
.filter(Boolean)
.join("\n\n")
@@ -103,7 +104,7 @@ function summaryText(message: SessionLegacy.WithParts) {
return text || undefined
}
function completedCompactions(messages: SessionLegacy.WithParts[]) {
function completedCompactions(messages: SessionV1.WithParts[]) {
const users = new Map<MessageID, number>()
for (let i = 0; i < messages.length; i++) {
const msg = messages[i]
@@ -134,14 +135,14 @@ function buildPrompt(input: { previousSummary?: string; context: string[] }) {
return [anchor, SUMMARY_TEMPLATE, ...input.context].join("\n\n")
}
function preserveRecentBudget(input: { cfg: Config.Info; model: Provider.Model }) {
function preserveRecentBudget(input: { cfg: ConfigV1.Info; model: Provider.Model }) {
return (
input.cfg.compaction?.preserve_recent_tokens ??
Math.min(MAX_PRESERVE_RECENT_TOKENS, Math.max(MIN_PRESERVE_RECENT_TOKENS, Math.floor(usable(input) * 0.25)))
)
}
function turns(messages: SessionLegacy.WithParts[]) {
function turns(messages: SessionV1.WithParts[]) {
const result: Turn[] = []
for (let i = 0; i < messages.length; i++) {
const msg = messages[i]
@@ -160,11 +161,11 @@ function turns(messages: SessionLegacy.WithParts[]) {
}
function splitTurn(input: {
messages: SessionLegacy.WithParts[]
messages: SessionV1.WithParts[]
turn: Turn
model: Provider.Model
budget: number
estimate: (input: { messages: SessionLegacy.WithParts[]; model: Provider.Model }) => Effect.Effect<number>
estimate: (input: { messages: SessionV1.WithParts[]; model: Provider.Model }) => Effect.Effect<number>
}) {
return Effect.gen(function* () {
if (input.budget <= 0) return undefined
@@ -186,13 +187,13 @@ function splitTurn(input: {
export interface Interface {
readonly isOverflow: (input: {
tokens: SessionLegacy.Assistant["tokens"]
tokens: SessionV1.Assistant["tokens"]
model: Provider.Model
}) => Effect.Effect<boolean>
readonly prune: (input: { sessionID: SessionID }) => Effect.Effect<void>
readonly process: (input: {
parentID: MessageID
messages: SessionLegacy.WithParts[]
messages: SessionV1.WithParts[]
sessionID: SessionID
auto: boolean
overflow?: boolean
@@ -223,7 +224,7 @@ export const layer = Layer.effect(
const flags = yield* RuntimeFlags.Service
const isOverflow = Effect.fn("SessionCompaction.isOverflow")(function* (input: {
tokens: SessionLegacy.Assistant["tokens"]
tokens: SessionV1.Assistant["tokens"]
model: Provider.Model
}) {
return overflow({
@@ -235,7 +236,7 @@ export const layer = Layer.effect(
})
const estimate = Effect.fn("SessionCompaction.estimate")(function* (input: {
messages: SessionLegacy.WithParts[]
messages: SessionV1.WithParts[]
model: Provider.Model
}) {
const msgs = yield* MessageV2.toModelMessagesEffect(input.messages, input.model)
@@ -243,8 +244,8 @@ export const layer = Layer.effect(
})
const select = Effect.fn("SessionCompaction.select")(function* (input: {
messages: SessionLegacy.WithParts[]
cfg: Config.Info
messages: SessionV1.WithParts[]
cfg: ConfigV1.Info
model: Provider.Model
}) {
const limit = input.cfg.compaction?.tail_turns ?? DEFAULT_TAIL_TURNS
@@ -307,7 +308,7 @@ export const layer = Layer.effect(
let total = 0
let pruned = 0
const toPrune: SessionLegacy.ToolPart[] = []
const toPrune: SessionV1.ToolPart[] = []
let turns = 0
loop: for (let msgIndex = msgs.length - 1; msgIndex >= 0; msgIndex--) {
@@ -343,7 +344,7 @@ export const layer = Layer.effect(
const processCompaction = Effect.fn("SessionCompaction.process")(function* (input: {
parentID: MessageID
messages: SessionLegacy.WithParts[]
messages: SessionV1.WithParts[]
sessionID: SessionID
auto: boolean
overflow?: boolean
@@ -354,14 +355,14 @@ export const layer = Layer.effect(
}
const userMessage = parent.info
const compactionPart = parent.parts.find(
(part): part is SessionLegacy.CompactionPart => part.type === "compaction",
(part): part is SessionV1.CompactionPart => part.type === "compaction",
)
let messages = input.messages
let replay:
| {
info: SessionLegacy.User
parts: SessionLegacy.Part[]
info: SessionV1.User
parts: SessionV1.Part[]
}
| undefined
if (input.overflow) {
@@ -410,7 +411,7 @@ export const layer = Layer.effect(
toolOutputMaxChars: TOOL_OUTPUT_MAX_CHARS,
})
const ctx = yield* InstanceState.context
const msg: SessionLegacy.Assistant = {
const msg: SessionV1.Assistant = {
id: MessageID.ascending(),
role: "assistant",
parentID: input.parentID,
@@ -459,7 +460,7 @@ export const layer = Layer.effect(
})
if (result === "compact") {
processor.message.error = new SessionLegacy.ContextOverflowError({
processor.message.error = new SessionV1.ContextOverflowError({
message: replay
? "Conversation history too large to compact - exceeds model context limit"
: "Session too large to compact - context exceeds model limit even after stripping media",
+5 -5
View File
@@ -1,5 +1,5 @@
import path from "path"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { Effect, Layer, Context } from "effect"
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"
import { Config } from "@/config/config"
@@ -12,7 +12,7 @@ import { Global } from "@opencode-ai/core/global"
import type { MessageV2 } from "./message-v2"
import type { MessageID } from "./schema"
function extract(messages: SessionLegacy.WithParts[]) {
function extract(messages: SessionV1.WithParts[]) {
const paths = new Set<string>()
for (const msg of messages) {
for (const part of msg.parts) {
@@ -35,7 +35,7 @@ export interface Interface {
readonly system: () => Effect.Effect<string[], FSUtil.Error>
readonly find: (dir: string) => Effect.Effect<string | undefined, FSUtil.Error>
readonly resolve: (
messages: SessionLegacy.WithParts[],
messages: SessionV1.WithParts[],
filepath: string,
messageID: MessageID,
) => Effect.Effect<{ filepath: string; content: string }[], FSUtil.Error>
@@ -175,7 +175,7 @@ export const layer: Layer.Layer<
})
const resolve = Effect.fn("Instruction.resolve")(function* (
messages: SessionLegacy.WithParts[],
messages: SessionV1.WithParts[],
filepath: string,
messageID: MessageID,
) {
@@ -230,7 +230,7 @@ export const defaultLayer = layer.pipe(
Layer.provide(RuntimeFlags.defaultLayer),
)
export function loaded(messages: SessionLegacy.WithParts[]) {
export function loaded(messages: SessionV1.WithParts[]) {
return extract(messages)
}
+5 -5
View File
@@ -1,6 +1,6 @@
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import { Provider } from "@/provider/provider"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { serviceUse } from "@opencode-ai/core/effect/service-use"
import { Log } from "@opencode-ai/core/util/log"
import { Context, Effect, Layer } from "effect"
@@ -33,12 +33,12 @@ const log = Log.create({ service: "llm" })
export const OUTPUT_TOKEN_MAX = ProviderTransform.OUTPUT_TOKEN_MAX
export type StreamInput = {
user: SessionLegacy.User
user: SessionV1.User
sessionID: string
parentSessionID?: string
model: Provider.Model
agent: Agent.Info
permission?: PermissionLegacy.Ruleset
permission?: PermissionV1.Ruleset
system: string[]
messages: ModelMessage[]
small?: boolean
@@ -165,7 +165,7 @@ const live: Layer.Layer<
return { approved: true }
}
const id = PermissionLegacy.ID.ascending()
const id = PermissionV1.ID.ascending()
let unsub: EventV2.Unsubscribe | undefined
try {
unsub = await bridge.promise(
+4 -4
View File
@@ -1,6 +1,6 @@
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import type { Auth } from "@/auth"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import type { RuntimeFlags } from "@/effect/runtime-flags"
import { InstanceState } from "@/effect/instance-state"
import { Permission } from "@/permission"
@@ -18,12 +18,12 @@ import { mergeDeep } from "remeda"
const USER_AGENT = `opencode/${InstallationVersion}`
type PrepareInput = {
readonly user: SessionLegacy.User
readonly user: SessionV1.User
readonly sessionID: string
readonly parentSessionID?: string
readonly model: Provider.Model
readonly agent: Agent.Info
readonly permission?: PermissionLegacy.Ruleset
readonly permission?: PermissionV1.Ruleset
readonly system: string[]
readonly messages: ModelMessage[]
readonly small?: boolean
+6 -6
View File
@@ -1,6 +1,6 @@
import { EventV2 } from "@opencode-ai/core/event"
import { SessionID, MessageID, PartID } from "./schema"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { ProviderV2 } from "@opencode-ai/core/provider"
import {
APIError,
@@ -17,7 +17,7 @@ import {
User,
WithParts,
type ToolPart,
} from "@opencode-ai/core/session/legacy"
} from "@opencode-ai/core/v1/session"
import { NamedError } from "@opencode-ai/core/util/error"
import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai"
@@ -56,9 +56,9 @@ function truncateToolOutput(text: string, maxChars?: number) {
}
export const Event = {
Updated: SessionLegacy.Event.MessageUpdated,
Removed: SessionLegacy.Event.MessageRemoved,
PartUpdated: SessionLegacy.Event.PartUpdated,
Updated: SessionV1.Event.MessageUpdated,
Removed: SessionV1.Event.MessageRemoved,
PartUpdated: SessionV1.Event.PartUpdated,
PartDelta: EventV2.define({
type: "message.part.delta",
schema: {
@@ -69,7 +69,7 @@ export const Event = {
delta: Schema.String,
},
}),
PartRemoved: SessionLegacy.Event.PartRemoved,
PartRemoved: SessionV1.Event.PartRemoved,
}
const Cursor = Schema.Struct({
+5 -4
View File
@@ -1,12 +1,13 @@
import type { Config } from "@/config/config"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import type { Provider } from "@/provider/provider"
import { ProviderTransform } from "@/provider/transform"
import type { MessageV2 } from "./message-v2"
const COMPACTION_BUFFER = 20_000
export function usable(input: { cfg: Config.Info; model: Provider.Model; outputTokenMax?: number }) {
export function usable(input: { cfg: ConfigV1.Info; model: Provider.Model; outputTokenMax?: number }) {
const context = input.model.limit.context
if (context === 0) return 0
@@ -19,8 +20,8 @@ export function usable(input: { cfg: Config.Info; model: Provider.Model; outputT
}
export function isOverflow(input: {
cfg: Config.Info
tokens: SessionLegacy.Assistant["tokens"]
cfg: ConfigV1.Info
tokens: SessionV1.Assistant["tokens"]
model: Provider.Model
outputTokenMax?: number
}) {
+21 -21
View File
@@ -1,6 +1,6 @@
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import { Image } from "@/image/image"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { Cause, Deferred, Effect, Exit, Layer, Context, Scope, Schema } from "effect"
import * as Stream from "effect/Stream"
import { Agent } from "@/agent/agent"
@@ -37,25 +37,25 @@ const log = Log.create({ service: "session.processor" })
export type Result = "compact" | "stop" | "continue"
export interface Handle {
readonly message: SessionLegacy.Assistant
readonly message: SessionV1.Assistant
readonly updateToolCall: (
toolCallID: string,
update: (part: SessionLegacy.ToolPart) => SessionLegacy.ToolPart,
) => Effect.Effect<SessionLegacy.ToolPart | undefined>
update: (part: SessionV1.ToolPart) => SessionV1.ToolPart,
) => Effect.Effect<SessionV1.ToolPart | undefined>
readonly completeToolCall: (
toolCallID: string,
output: {
title: string
metadata: Record<string, any>
output: string
attachments?: SessionLegacy.FilePart[]
attachments?: SessionV1.FilePart[]
},
) => Effect.Effect<void>
readonly process: (streamInput: LLM.StreamInput) => Effect.Effect<Result>
}
type Input = {
assistantMessage: SessionLegacy.Assistant
assistantMessage: SessionV1.Assistant
sessionID: SessionID
model: Provider.Model
}
@@ -65,9 +65,9 @@ export interface Interface {
}
type ToolCall = {
partID: SessionLegacy.ToolPart["id"]
messageID: SessionLegacy.ToolPart["messageID"]
sessionID: SessionLegacy.ToolPart["sessionID"]
partID: SessionV1.ToolPart["id"]
messageID: SessionV1.ToolPart["messageID"]
sessionID: SessionV1.ToolPart["sessionID"]
done: Deferred.Deferred<void>
inputEnded: boolean
}
@@ -78,8 +78,8 @@ interface ProcessorContext extends Input {
snapshot: string | undefined
blocked: boolean
needsCompaction: boolean
currentText: SessionLegacy.TextPart | undefined
reasoningMap: Record<string, SessionLegacy.ReasoningPart>
currentText: SessionV1.TextPart | undefined
reasoningMap: Record<string, SessionV1.ReasoningPart>
}
type StreamEvent = LLMEvent
@@ -153,7 +153,7 @@ export const layer = Layer.effect(
const updateToolCall = Effect.fn("SessionProcessor.updateToolCall")(function* (
toolCallID: string,
update: (part: SessionLegacy.ToolPart) => SessionLegacy.ToolPart,
update: (part: SessionV1.ToolPart) => SessionV1.ToolPart,
) {
const match = yield* readToolCall(toolCallID)
if (!match) return undefined
@@ -173,7 +173,7 @@ export const layer = Layer.effect(
title: string
metadata: Record<string, any>
output: string
attachments?: SessionLegacy.FilePart[]
attachments?: SessionV1.FilePart[]
},
) {
const match = yield* readToolCall(toolCallID)
@@ -205,7 +205,7 @@ export const layer = Layer.effect(
time: { start: match.part.state.time.start, end: Date.now() },
},
})
if (error instanceof PermissionLegacy.RejectedError || error instanceof Question.RejectedError) {
if (error instanceof PermissionV1.RejectedError || error instanceof Question.RejectedError) {
ctx.blocked = ctx.shouldBreak
}
yield* settleToolCall(toolCallID)
@@ -268,7 +268,7 @@ export const layer = Layer.effect(
callID: input.id,
state: { status: "pending", input: {}, raw: "" },
metadata: input.providerExecuted ? { providerExecuted: true } : undefined,
} satisfies SessionLegacy.ToolPart)
} satisfies SessionV1.ToolPart)
ctx.toolcalls[input.id] = {
done: yield* Deferred.make<void>(),
partID: part.id,
@@ -279,11 +279,11 @@ export const layer = Layer.effect(
return { call: ctx.toolcalls[input.id], part }
})
const isFilePart = (value: unknown): value is SessionLegacy.FilePart => Schema.is(SessionLegacy.FilePart)(value)
const isFilePart = (value: unknown): value is SessionV1.FilePart => Schema.is(SessionV1.FilePart)(value)
const toolResultOutput = (
value: Extract<StreamEvent, { type: "tool-result" }>,
): { title: string; metadata: Record<string, any>; output: string; attachments?: SessionLegacy.FilePart[] } => {
): { title: string; metadata: Record<string, any>; output: string; attachments?: SessionV1.FilePart[] } => {
if (isRecord(value.result.value) && typeof value.result.value.output === "string") {
return {
title: typeof value.result.value.title === "string" ? value.result.value.title : value.name,
@@ -463,7 +463,7 @@ export const layer = Layer.effect(
),
Effect.exit,
)
: Effect.succeed(Exit.succeed<SessionLegacy.FilePart>(attachment)),
: Effect.succeed(Exit.succeed<SessionV1.FilePart>(attachment)),
)
const omitted = normalized.filter(Exit.isFailure).length
const attachments = normalized.filter(Exit.isSuccess).map((item) => item.value)
@@ -486,7 +486,7 @@ export const layer = Layer.effect(
type: "text",
text: output.output,
},
...(output.attachments?.map((item: SessionLegacy.FilePart) => ({
...(output.attachments?.map((item: SessionV1.FilePart) => ({
type: "file" as const,
uri: item.url,
mime: item.mime,
@@ -753,7 +753,7 @@ export const layer = Layer.effect(
const halt = Effect.fn("SessionProcessor.halt")(function* (e: unknown) {
slog.error("process", { error: errorMessage(e), stack: e instanceof Error ? e.stack : undefined })
const error = parse(e)
if (SessionLegacy.ContextOverflowError.isInstance(error)) {
if (SessionV1.ContextOverflowError.isInstance(error)) {
ctx.needsCompaction = true
yield* events.publish(Session.Event.Error, { sessionID: ctx.sessionID, error })
return
+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" }),
),
@@ -1,5 +1,5 @@
import { Option, Schema } from "effect"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { MessageV2 } from "../message-v2"
import { Reference } from "@/reference/reference"
@@ -34,7 +34,7 @@ export function referenceTextPart(input: {
target?: string
targetPath?: string
problem?: string
}): SessionLegacy.TextPartInput {
}): SessionV1.TextPartInput {
const metadata: ReferencePromptMetadata = {
name: input.reference.name,
kind: input.reference.kind,
+2 -2
View File
@@ -1,5 +1,5 @@
import path from "path"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { Effect } from "effect"
import { Agent } from "@/agent/agent"
import { FSUtil } from "@opencode-ai/core/fs-util"
@@ -13,7 +13,7 @@ import BUILD_SWITCH from "./prompt/build-switch.txt"
import PLAN_MODE from "./prompt/plan-mode.txt"
export const apply = Effect.fn("SessionReminders.apply")(function* (input: {
messages: SessionLegacy.WithParts[]
messages: SessionV1.WithParts[]
agent: Agent.Info
session: Session.Info
}) {
+5 -5
View File
@@ -1,5 +1,5 @@
import type { NamedError } from "@opencode-ai/core/util/error"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { Cause, Clock, Duration, Effect, Schedule } from "effect"
import { MessageV2 } from "./message-v2"
import { iife } from "@/util/iife"
@@ -32,7 +32,7 @@ function cap(ms: number) {
return Math.min(ms, RETRY_MAX_DELAY)
}
export function delay(attempt: number, error?: SessionLegacy.APIError) {
export function delay(attempt: number, error?: SessionV1.APIError) {
if (error) {
const headers = error.data.responseHeaders
if (headers) {
@@ -67,8 +67,8 @@ export function delay(attempt: number, error?: SessionLegacy.APIError) {
export function retryable(error: Err, provider: string) {
// context overflow errors should not be retried
if (SessionLegacy.ContextOverflowError.isInstance(error)) return undefined
if (SessionLegacy.APIError.isInstance(error)) {
if (SessionV1.ContextOverflowError.isInstance(error)) return undefined
if (SessionV1.APIError.isInstance(error)) {
const status = error.data.statusCode
// 5xx errors are transient server failures and should always be retried,
// even when the provider SDK doesn't explicitly mark them as retryable.
@@ -184,7 +184,7 @@ export function policy(opts: {
const retry = retryable(error, opts.provider)
if (!retry) return Cause.done(meta.attempt)
return Effect.gen(function* () {
const wait = delay(meta.attempt, SessionLegacy.APIError.isInstance(error) ? error : undefined)
const wait = delay(meta.attempt, SessionV1.APIError.isInstance(error) ? error : undefined)
const now = yield* Clock.currentTimeMillis
yield* opts.set({
attempt: meta.attempt,
+4 -4
View File
@@ -1,5 +1,5 @@
import { Effect, Layer, Context, Schema } from "effect"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { EventV2Bridge } from "@/event-v2-bridge"
import { Snapshot } from "../snapshot"
import { Storage } from "@/storage/storage"
@@ -40,7 +40,7 @@ export const layer = Layer.effect(
const revert = Effect.fn("SessionRevert.revert")(function* (input: RevertInput) {
yield* state.assertNotBusy(input.sessionID)
const all = yield* sessions.messages({ sessionID: input.sessionID }).pipe(Effect.orDie)
let lastUser: SessionLegacy.User | undefined
let lastUser: SessionV1.User | undefined
const session = yield* sessions.get(input.sessionID).pipe(Effect.orDie)
let rev: Session.Info["revert"]
@@ -104,8 +104,8 @@ export const layer = Layer.effect(
const sessionID = session.id
const msgs = yield* sessions.messages({ sessionID }).pipe(Effect.orDie)
const messageID = session.revert.messageID
const remove = [] as SessionLegacy.WithParts[]
let target: SessionLegacy.WithParts | undefined
const remove = [] as SessionV1.WithParts[]
let target: SessionV1.WithParts | undefined
for (const msg of msgs) {
if (msg.info.id < messageID) continue
if (msg.info.id > messageID) {
+14 -14
View File
@@ -1,5 +1,5 @@
import { InstanceState } from "@/effect/instance-state"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { Runner } from "@/effect/runner"
import { BackgroundJob } from "@/background/job"
import { Effect, Latch, Layer, Scope, Context } from "effect"
@@ -13,15 +13,15 @@ export interface Interface {
readonly cancel: (sessionID: SessionID) => Effect.Effect<void>
readonly ensureRunning: (
sessionID: SessionID,
onInterrupt: Effect.Effect<SessionLegacy.WithParts>,
work: Effect.Effect<SessionLegacy.WithParts>,
) => Effect.Effect<SessionLegacy.WithParts>
onInterrupt: Effect.Effect<SessionV1.WithParts>,
work: Effect.Effect<SessionV1.WithParts>,
) => Effect.Effect<SessionV1.WithParts>
readonly startShell: (
sessionID: SessionID,
onInterrupt: Effect.Effect<SessionLegacy.WithParts>,
work: Effect.Effect<SessionLegacy.WithParts>,
onInterrupt: Effect.Effect<SessionV1.WithParts>,
work: Effect.Effect<SessionV1.WithParts>,
ready?: Latch.Latch,
) => Effect.Effect<SessionLegacy.WithParts, Session.BusyError>
) => Effect.Effect<SessionV1.WithParts, Session.BusyError>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionRunState") {}
@@ -35,7 +35,7 @@ export const layer = Layer.effect(
const state = yield* InstanceState.make(
Effect.fn("SessionRunState.state")(function* () {
const scope = yield* Scope.Scope
const runners = new Map<SessionID, Runner.Runner<SessionLegacy.WithParts>>()
const runners = new Map<SessionID, Runner.Runner<SessionV1.WithParts>>()
yield* Effect.addFinalizer(
Effect.fnUntraced(function* () {
yield* Effect.forEach(runners.values(), (runner) => runner.cancel, {
@@ -51,12 +51,12 @@ export const layer = Layer.effect(
const runner = Effect.fn("SessionRunState.runner")(function* (
sessionID: SessionID,
onInterrupt: Effect.Effect<SessionLegacy.WithParts>,
onInterrupt: Effect.Effect<SessionV1.WithParts>,
) {
const data = yield* InstanceState.get(state)
const existing = data.runners.get(sessionID)
if (existing) return existing
const next = Runner.make<SessionLegacy.WithParts>(data.scope, {
const next = Runner.make<SessionV1.WithParts>(data.scope, {
onIdle: Effect.gen(function* () {
data.runners.delete(sessionID)
yield* status.set(sessionID, { type: "idle" })
@@ -87,16 +87,16 @@ export const layer = Layer.effect(
const ensureRunning = Effect.fn("SessionRunState.ensureRunning")(function* (
sessionID: SessionID,
onInterrupt: Effect.Effect<SessionLegacy.WithParts>,
work: Effect.Effect<SessionLegacy.WithParts>,
onInterrupt: Effect.Effect<SessionV1.WithParts>,
work: Effect.Effect<SessionV1.WithParts>,
) {
return yield* (yield* runner(sessionID, onInterrupt)).ensureRunning(work)
})
const startShell = Effect.fn("SessionRunState.startShell")(function* (
sessionID: SessionID,
onInterrupt: Effect.Effect<SessionLegacy.WithParts>,
work: Effect.Effect<SessionLegacy.WithParts>,
onInterrupt: Effect.Effect<SessionV1.WithParts>,
work: Effect.Effect<SessionV1.WithParts>,
ready?: Latch.Latch,
) {
return yield* (yield* runner(sessionID, onInterrupt))
+35 -35
View File
@@ -1,6 +1,6 @@
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import { Slug } from "@opencode-ai/core/util/slug"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { serviceUse } from "@opencode-ai/core/effect/service-use"
import path from "path"
import { BackgroundJob } from "@/background/job"
@@ -234,7 +234,7 @@ export const Info = Schema.Struct({
version: Schema.String,
metadata: optionalOmitUndefined(Metadata),
time: Time,
permission: optionalOmitUndefined(PermissionLegacy.Ruleset),
permission: optionalOmitUndefined(PermissionV1.Ruleset),
revert: optionalOmitUndefined(Revert),
}).annotate({ identifier: "Session" })
export type Info = Types.DeepMutable<Schema.Schema.Type<typeof Info>>
@@ -259,7 +259,7 @@ export const CreateInput = Schema.optional(
agent: Schema.optional(Schema.String),
model: Schema.optional(Model),
metadata: Schema.optional(Metadata),
permission: Schema.optional(PermissionLegacy.Ruleset),
permission: Schema.optional(PermissionV1.Ruleset),
workspaceID: Schema.optional(WorkspaceV2.ID),
}),
)
@@ -283,7 +283,7 @@ export const SetMetadataInput = Schema.Struct({
})
export const SetPermissionInput = Schema.Struct({
sessionID: SessionID,
permission: PermissionLegacy.Ruleset,
permission: PermissionV1.Ruleset,
})
export const SetRevertInput = Schema.Struct({
sessionID: SessionID,
@@ -349,7 +349,7 @@ const UpdatedInfo = Schema.Struct({
version: Schema.optional(Schema.NullOr(Schema.String)),
metadata: Schema.optional(Schema.NullOr(Metadata)),
time: Schema.optional(UpdatedTime),
permission: Schema.optional(Schema.NullOr(PermissionLegacy.Ruleset)),
permission: Schema.optional(Schema.NullOr(PermissionV1.Ruleset)),
revert: Schema.optional(Schema.NullOr(Revert)),
})
@@ -359,9 +359,9 @@ const UpdatedEventSchema = Schema.Struct({
})
export const Event = {
Created: SessionLegacy.Event.Created,
Updated: SessionLegacy.Event.Updated,
Deleted: SessionLegacy.Event.Deleted,
Created: SessionV1.Event.Created,
Updated: SessionV1.Event.Updated,
Deleted: SessionV1.Event.Deleted,
Diff: EventV2.define({
type: "session.diff",
schema: {
@@ -373,9 +373,9 @@ export const Event = {
type: "session.error",
schema: {
sessionID: Schema.optional(SessionID),
// Reuses SessionLegacy.Assistant.fields.error (already Schema.optional) so
// Reuses SessionV1.Assistant.fields.error (already Schema.optional) so
// the derived schema keeps the same discriminated-union shape on the event stream.
error: SessionLegacy.Assistant.fields.error,
error: SessionV1.Assistant.fields.error,
},
}),
}
@@ -473,7 +473,7 @@ export interface Interface {
agent?: string
model?: Schema.Schema.Type<typeof Model>
metadata?: typeof Metadata.Type
permission?: PermissionLegacy.Ruleset
permission?: PermissionV1.Ruleset
workspaceID?: WorkspaceV2.ID
}) => Effect.Effect<Info>
readonly fork: (input: { sessionID: SessionID; messageID?: MessageID }) => Effect.Effect<Info, NotFound>
@@ -482,7 +482,7 @@ export interface Interface {
readonly setTitle: (input: { sessionID: SessionID; title: string }) => Effect.Effect<void>
readonly setArchived: (input: { sessionID: SessionID; time?: number }) => Effect.Effect<void>
readonly setMetadata: (input: typeof SetMetadataInput.Type) => Effect.Effect<void>
readonly setPermission: (input: { sessionID: SessionID; permission: PermissionLegacy.Ruleset }) => Effect.Effect<void>
readonly setPermission: (input: { sessionID: SessionID; permission: PermissionV1.Ruleset }) => Effect.Effect<void>
readonly setRevert: (input: {
sessionID: SessionID
revert: Info["revert"]
@@ -496,18 +496,18 @@ export interface Interface {
readonly messages: (input: {
sessionID: SessionID
limit?: number
}) => Effect.Effect<SessionLegacy.WithParts[], NotFound>
}) => Effect.Effect<SessionV1.WithParts[], NotFound>
readonly children: (parentID: SessionID) => Effect.Effect<Info[]>
readonly remove: (sessionID: SessionID) => Effect.Effect<void, NotFound>
readonly updateMessage: <T extends SessionLegacy.Info>(msg: T) => Effect.Effect<T>
readonly updateMessage: <T extends SessionV1.Info>(msg: T) => Effect.Effect<T>
readonly removeMessage: (input: { sessionID: SessionID; messageID: MessageID }) => Effect.Effect<MessageID>
readonly removePart: (input: { sessionID: SessionID; messageID: MessageID; partID: PartID }) => Effect.Effect<PartID>
readonly getPart: (input: {
sessionID: SessionID
messageID: MessageID
partID: PartID
}) => Effect.Effect<SessionLegacy.Part | undefined>
readonly updatePart: <T extends SessionLegacy.Part>(part: T) => Effect.Effect<T>
}) => Effect.Effect<SessionV1.Part | undefined>
readonly updatePart: <T extends SessionV1.Part>(part: T) => Effect.Effect<T>
readonly updatePartDelta: (input: {
sessionID: SessionID
messageID: MessageID
@@ -518,8 +518,8 @@ export interface Interface {
/** Finds the first message matching the predicate, searching newest-first. */
readonly findMessage: (
sessionID: SessionID,
predicate: (msg: SessionLegacy.WithParts) => boolean,
) => Effect.Effect<Option.Option<SessionLegacy.WithParts>, NotFound>
predicate: (msg: SessionV1.WithParts) => boolean,
) => Effect.Effect<Option.Option<SessionV1.WithParts>, NotFound>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/Session") {}
@@ -571,7 +571,7 @@ export const layer: Layer.Layer<
directory: string
path?: string
metadata?: typeof Metadata.Type
permission?: PermissionLegacy.Ruleset
permission?: PermissionV1.Ruleset
}) {
const ctx = yield* InstanceState.context
const result: Info = {
@@ -598,7 +598,7 @@ export const layer: Layer.Layer<
log.info("created", result)
yield* events.publish(
SessionLegacy.Event.Created,
SessionV1.Event.Created,
{ sessionID: result.id, info: result },
{ location: eventLocation(result) },
)
@@ -689,7 +689,7 @@ export const layer: Layer.Layer<
}
yield* events.publish(
SessionLegacy.Event.Deleted,
SessionV1.Event.Deleted,
{ sessionID, info: session },
{ location: eventLocation(session) },
)
@@ -699,18 +699,18 @@ export const layer: Layer.Layer<
}
})
const updateMessage = <T extends SessionLegacy.Info>(msg: T): Effect.Effect<T> =>
const updateMessage = <T extends SessionV1.Info>(msg: T): Effect.Effect<T> =>
Effect.gen(function* () {
const location = yield* locationForSession(msg.sessionID)
yield* events.publish(SessionLegacy.Event.MessageUpdated, { sessionID: msg.sessionID, info: msg }, { location })
yield* events.publish(SessionV1.Event.MessageUpdated, { sessionID: msg.sessionID, info: msg }, { location })
return msg
}).pipe(Effect.withSpan("Session.updateMessage"))
const updatePart = <T extends SessionLegacy.Part>(part: T): Effect.Effect<T> =>
const updatePart = <T extends SessionV1.Part>(part: T): Effect.Effect<T> =>
Effect.gen(function* () {
const location = yield* locationForSession(part.sessionID)
yield* events.publish(
SessionLegacy.Event.PartUpdated,
SessionV1.Event.PartUpdated,
{
sessionID: part.sessionID,
part: structuredClone(part),
@@ -740,7 +740,7 @@ export const layer: Layer.Layer<
id: row.id,
sessionID: row.session_id,
messageID: row.message_id,
} as SessionLegacy.Part
} as SessionV1.Part
})
const create = Effect.fn("Session.create")(function* (input?: {
@@ -749,7 +749,7 @@ export const layer: Layer.Layer<
agent?: string
model?: Schema.Schema.Type<typeof Model>
metadata?: typeof Metadata.Type
permission?: PermissionLegacy.Ruleset
permission?: PermissionV1.Ruleset
workspaceID?: WorkspaceV2.ID
}) {
const ctx = yield* InstanceState.context
@@ -795,7 +795,7 @@ export const layer: Layer.Layer<
})
for (const part of msg.parts) {
const p: SessionLegacy.Part = {
const p: SessionV1.Part = {
...part,
id: PartID.ascending(),
messageID: cloned.id,
@@ -822,7 +822,7 @@ export const layer: Layer.Layer<
revert: info.revert === null ? undefined : (info.revert ?? current.revert),
permission: info.permission === null ? undefined : (info.permission ?? current.permission),
} as Info
yield* events.publish(SessionLegacy.Event.Updated, { sessionID, info: next }, { location: eventLocation(next) })
yield* events.publish(SessionV1.Event.Updated, { sessionID, info: next }, { location: eventLocation(next) })
})
const touch = Effect.fn("Session.touch")(function* (sessionID: SessionID) {
@@ -843,7 +843,7 @@ export const layer: Layer.Layer<
const setPermission = Effect.fn("Session.setPermission")(function* (input: {
sessionID: SessionID
permission: PermissionLegacy.Ruleset
permission: PermissionV1.Ruleset
}) {
yield* patch(input.sessionID, { permission: [...input.permission], time: { updated: Date.now() } }).pipe(
Effect.orDie,
@@ -899,7 +899,7 @@ export const layer: Layer.Layer<
}
const size = 50
const result = [] as SessionLegacy.WithParts[]
const result = [] as SessionV1.WithParts[]
let before: string | undefined
while (true) {
const page = yield* MessageV2.page({ sessionID: input.sessionID, limit: size, before }).pipe(
@@ -922,7 +922,7 @@ export const layer: Layer.Layer<
}) {
const location = yield* locationForSession(input.sessionID)
yield* events.publish(
SessionLegacy.Event.MessageRemoved,
SessionV1.Event.MessageRemoved,
{
sessionID: input.sessionID,
messageID: input.messageID,
@@ -939,7 +939,7 @@ export const layer: Layer.Layer<
}) {
const location = yield* locationForSession(input.sessionID)
yield* events.publish(
SessionLegacy.Event.PartRemoved,
SessionV1.Event.PartRemoved,
{
sessionID: input.sessionID,
messageID: input.messageID,
@@ -976,7 +976,7 @@ export const layer: Layer.Layer<
if (!page.more || !page.cursor) break
before = page.cursor
}
return Option.none<SessionLegacy.WithParts>()
return Option.none<SessionV1.WithParts>()
})
return Service.of({
+3 -3
View File
@@ -1,5 +1,5 @@
import { Effect, Layer, Context, Schema } from "effect"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { EventV2Bridge } from "@/event-v2-bridge"
import { Snapshot } from "@/snapshot"
import { Session } from "./session"
@@ -65,7 +65,7 @@ function unquoteGitPath(input: string) {
export interface Interface {
readonly summarize: (input: { sessionID: SessionID; messageID: MessageID }) => Effect.Effect<void>
readonly diff: (input: { sessionID: SessionID; messageID?: MessageID }) => Effect.Effect<Snapshot.FileDiff[]>
readonly computeDiff: (input: { messages: SessionLegacy.WithParts[] }) => Effect.Effect<Snapshot.FileDiff[]>
readonly computeDiff: (input: { messages: SessionV1.WithParts[] }) => Effect.Effect<Snapshot.FileDiff[]>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionSummary") {}
@@ -79,7 +79,7 @@ export const layer = Layer.effect(
const config = yield* Config.Service
const computeDiff = Effect.fn("SessionSummary.computeDiff")(function* (input: {
messages: SessionLegacy.WithParts[]
messages: SessionV1.WithParts[]
}) {
let from: string | undefined
let to: string | undefined
+3 -3
View File
@@ -1,5 +1,5 @@
import { Agent } from "@/agent/agent"
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { Provider } from "@/provider/provider"
import { ProviderTransform } from "@/provider/transform"
import { MCP } from "@/mcp"
@@ -29,7 +29,7 @@ export const resolve = Effect.fn("SessionTools.resolve")(function* (input: {
session: Session.Info
processor: Pick<SessionProcessor.Handle, "message" | "updateToolCall" | "completeToolCall">
bypassAgentCheck: boolean
messages: SessionLegacy.WithParts[]
messages: SessionV1.WithParts[]
promptOps: TaskPromptOps
}) {
using _ = log.time("resolveTools")
@@ -153,7 +153,7 @@ export const resolve = Effect.fn("SessionTools.resolve")(function* (input: {
)
const textParts: string[] = []
const attachments: Omit<SessionLegacy.FilePart, "id" | "sessionID" | "messageID">[] = []
const attachments: Omit<SessionV1.FilePart, "id" | "sessionID" | "messageID">[] = []
for (const contentItem of result.content) {
if (contentItem.type === "text") textParts.push(contentItem.text)
else if (contentItem.type === "image") {