import { BusEvent } from "@/bus/bus-event" import { Bus } from "@/bus" import { InstanceState } from "@/effect" import { SessionID } from "./schema" import { zod } from "@/util/effect-zod" import { withStatics } from "@/util/schema" import { Effect, Layer, Context, Schema } from "effect" import z from "zod" export const Info = Schema.Union([ Schema.Struct({ type: Schema.Literal("idle"), }), Schema.Struct({ type: Schema.Literal("retry"), attempt: Schema.Number, message: Schema.String, next: Schema.Number, }), Schema.Struct({ type: Schema.Literal("busy"), }), ]) .annotate({ identifier: "SessionStatus" }) .pipe(withStatics((s) => ({ zod: zod(s) }))) export type Info = Schema.Schema.Type export const Event = { Status: BusEvent.define( "session.status", z.object({ sessionID: SessionID.zod, status: Info.zod, }), ), // deprecated Idle: BusEvent.define( "session.idle", z.object({ sessionID: SessionID.zod, }), ), } export interface Interface { readonly get: (sessionID: SessionID) => Effect.Effect readonly list: () => Effect.Effect> readonly set: (sessionID: SessionID, status: Info) => Effect.Effect } export class Service extends Context.Service()("@opencode/SessionStatus") {} export const layer = Layer.effect( Service, Effect.gen(function* () { const bus = yield* Bus.Service const state = yield* InstanceState.make( Effect.fn("SessionStatus.state")(() => Effect.succeed(new Map())), ) const get = Effect.fn("SessionStatus.get")(function* (sessionID: SessionID) { const data = yield* InstanceState.get(state) return data.get(sessionID) ?? { type: "idle" as const } }) const list = Effect.fn("SessionStatus.list")(function* () { return new Map(yield* InstanceState.get(state)) }) const set = Effect.fn("SessionStatus.set")(function* (sessionID: SessionID, status: Info) { const data = yield* InstanceState.get(state) yield* bus.publish(Event.Status, { sessionID, status }) if (status.type === "idle") { yield* bus.publish(Event.Idle, { sessionID }) data.delete(sessionID) return } data.set(sessionID, status) }) return Service.of({ get, list, set }) }), ) export const defaultLayer = layer.pipe(Layer.provide(Bus.layer)) export * as SessionStatus from "./status"