Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/opencode/specs/effect-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ Done now:
- [x] `Discovery`
- [x] `File`
- [x] `Snapshot`
- [x] `SessionStatus`

Still open and likely worth migrating:

Expand Down
3 changes: 3 additions & 0 deletions packages/opencode/src/effect/instances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Instance } from "@/project/instance"
import { Vcs } from "@/project/vcs"
import { ProviderAuth } from "@/provider/auth-service"
import { Question } from "@/question/service"
import { SessionStatus } from "@/session/status"
import { Skill } from "@/skill/service"
import { Snapshot } from "@/snapshot/service"
import { InstanceContext } from "./instance-context"
Expand All @@ -24,6 +25,7 @@ export type InstanceServices =
| FileTime.Service
| Format.Service
| File.Service
| SessionStatus.Service
| Skill.Service
| Snapshot.Service

Expand All @@ -44,6 +46,7 @@ function lookup(_key: string) {
FileTime.layer,
Format.layer,
File.layer,
SessionStatus.layer,
Skill.defaultLayer,
Snapshot.defaultLayer,
).pipe(Layer.provide(ctx))
Expand Down
4 changes: 4 additions & 0 deletions packages/opencode/src/effect/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,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()
}
68 changes: 42 additions & 26 deletions packages/opencode/src/session/status.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -42,36 +43,51 @@ export namespace SessionStatus {
),
}

const state = Instance.state(() => {
const data: Record<string, Info> = {}
return data
})
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 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)
})

export function get(sessionID: SessionID) {
return (
state()[sessionID] ?? {
type: "idle",
}
)
return Service.of({ get, list, set })
}),
)

export function get(sessionID: SessionID): Info {
return runSyncInstance(Service.use((svc) => svc.get(sessionID)))
}

export function list() {
return state()
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)))
}
}
Loading