Compare commits

...

2 Commits

Author SHA1 Message Date
Kit Langton
8e8ac7abe9 Merge branch 'dev' into kit/effectify-session-status 2026-03-19 21:16:41 -04:00
Kit Langton
3217d112ec effectify SessionStatus service, add runSyncInstance helper 2026-03-19 20:24:49 -04:00
4 changed files with 51 additions and 27 deletions

View File

@@ -123,6 +123,7 @@ Done now:
- [x] `Discovery`
- [x] `File`
- [x] `Snapshot`
- [x] `SessionStatus`
Still open and likely worth migrating:

View File

@@ -8,6 +8,7 @@ import { Instance } from "@/project/instance"
import { Vcs } from "@/project/vcs"
import { ProviderAuth } from "@/provider/auth"
import { Question } from "@/question"
import { SessionStatus } from "@/session/status"
import { Skill } from "@/skill/skill"
import { Snapshot } from "@/snapshot"
import { InstanceContext } from "./instance-context"
@@ -24,6 +25,7 @@ export type InstanceServices =
| FileTime.Service
| Format.Service
| File.Service
| SessionStatus.Service
| Skill.Service
| Snapshot.Service
@@ -44,6 +46,7 @@ function lookup(_key: string) {
Layer.fresh(FileTime.layer).pipe(Layer.orDie),
Layer.fresh(Format.layer),
Layer.fresh(File.layer),
Layer.fresh(SessionStatus.layer),
Layer.fresh(Skill.defaultLayer),
Layer.fresh(Snapshot.defaultLayer),
).pipe(Layer.provide(ctx))

View File

@@ -18,6 +18,10 @@ export function runPromiseInstance<A, E>(effect: Effect.Effect<A, E, InstanceSer
return runtime.runPromise(effect.pipe(Effect.provide(Instances.get(Instance.directory))))
}
export function runSyncInstance<A, E>(effect: Effect.Effect<A, E, InstanceServices>) {
return runtime.runSync(effect.pipe(Effect.provide(Instances.get(Instance.directory))))
}
export function disposeRuntime() {
return runtime.dispose()
}

View File

@@ -1,7 +1,8 @@
import { BusEvent } from "@/bus/bus-event"
import { Bus } from "@/bus"
import { Instance } from "@/project/instance"
import { runSyncInstance } from "@/effect/runtime"
import { SessionID } from "./schema"
import { Effect, Layer, ServiceMap } from "effect"
import z from "zod"
export namespace SessionStatus {
@@ -42,36 +43,51 @@ export namespace SessionStatus {
),
}
const state = Instance.state(() => {
const data: Record<string, Info> = {}
return data
})
export function get(sessionID: SessionID) {
return (
state()[sessionID] ?? {
type: "idle",
}
)
export interface Interface {
readonly get: (sessionID: SessionID) => Effect.Effect<Info>
readonly list: () => Effect.Effect<Record<string, Info>>
readonly set: (sessionID: SessionID, status: Info) => Effect.Effect<void>
}
export function list() {
return state()
export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionStatus") {}
export const layer = Layer.effect(
Service,
Effect.gen(function* () {
const data = new Map<SessionID, Info>()
const get = Effect.fn("SessionStatus.get")(function* (sessionID: SessionID) {
return data.get(sessionID) ?? { type: "idle" as const }
})
const list = Effect.fn("SessionStatus.list")(function* () {
return Object.fromEntries(data)
})
const set = Effect.fn("SessionStatus.set")(function* (sessionID: SessionID, status: Info) {
Bus.publish(Event.Status, { sessionID, status })
if (status.type === "idle") {
// deprecated
Bus.publish(Event.Idle, { sessionID })
data.delete(sessionID)
return
}
data.set(sessionID, status)
})
return Service.of({ get, list, set })
}),
)
export function get(sessionID: SessionID): Info {
return runSyncInstance(Service.use((svc) => svc.get(sessionID)))
}
export function list(): Record<string, Info> {
return runSyncInstance(Service.use((svc) => svc.list()))
}
export function set(sessionID: SessionID, status: Info) {
Bus.publish(Event.Status, {
sessionID,
status,
})
if (status.type === "idle") {
// deprecated
Bus.publish(Event.Idle, {
sessionID,
})
delete state()[sessionID]
return
}
state()[sessionID] = status
runSyncInstance(Service.use((svc) => svc.set(sessionID, status)))
}
}