import type { OrchestrationV2ThreadProjection, ScopedThreadRef } from "@t3tools/contracts"; import * as Option from "effect/Option"; import { AsyncResult, Atom } from "effect/unstable/reactivity"; import type { EnvironmentThread } from "./models.ts"; import { EMPTY_ENVIRONMENT_THREAD_STATE, type EnvironmentThreadState } from "./threadState.ts"; import { parseThreadKey, threadKey } from "./entities.ts"; const EMPTY_VISIBLE_TURN_ITEMS: OrchestrationV2ThreadProjection["visibleTurnItems"] = Object.freeze( [], ); export function createEnvironmentThreadDetailAtoms( threadStateAtom: ( environmentId: ScopedThreadRef["environmentId"], threadId: ScopedThreadRef["threadId"], ) => Atom.Atom>, ) { const threadStateValueAtomFamily = Atom.family((key: string) => { const ref = parseThreadKey(key); return Atom.make((get) => Option.getOrElse( AsyncResult.value(get(threadStateAtom(ref.environmentId, ref.threadId))), () => EMPTY_ENVIRONMENT_THREAD_STATE, ), ).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-state-value:${key}`)); }); const threadAtomFamily = Atom.family((key: string) => { const ref = parseThreadKey(key); let previousProjection: OrchestrationV2ThreadProjection | null = null; let previousValue: EnvironmentThread | null = null; return Atom.make((get) => { const projection = Option.getOrNull(get(threadStateValueAtomFamily(key)).data); if (projection === previousProjection) return previousValue; previousProjection = projection; previousValue = projection === null ? null : { environmentId: ref.environmentId, projection }; return previousValue; }).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread:${key}`)); }); const visibleTurnItemsAtomFamily = Atom.family((key: string) => Atom.make( (get): OrchestrationV2ThreadProjection["visibleTurnItems"] => Option.getOrNull(get(threadStateValueAtomFamily(key)).data)?.visibleTurnItems ?? EMPTY_VISIBLE_TURN_ITEMS, ).pipe(Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-visible-turn-items:${key}`)), ); const statusAtomFamily = Atom.family((key: string) => Atom.make((get) => get(threadStateValueAtomFamily(key)).status).pipe( Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-status:${key}`), ), ); const errorAtomFamily = Atom.family((key: string) => Atom.make((get) => Option.getOrNull(get(threadStateValueAtomFamily(key)).error)).pipe( Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-error:${key}`), ), ); const historyAtomFamily = Atom.family((key: string) => Atom.make((get) => get(threadStateValueAtomFamily(key)).history).pipe( Atom.setIdleTTL(0), Atom.withLabel(`environment-thread-history:${key}`), ), ); return { stateAtom: (ref: ScopedThreadRef) => threadStateValueAtomFamily(threadKey(ref)), threadAtom: (ref: ScopedThreadRef) => threadAtomFamily(threadKey(ref)), visibleTurnItemsAtom: (ref: ScopedThreadRef) => visibleTurnItemsAtomFamily(threadKey(ref)), statusAtom: (ref: ScopedThreadRef) => statusAtomFamily(threadKey(ref)), errorAtom: (ref: ScopedThreadRef) => errorAtomFamily(threadKey(ref)), historyAtom: (ref: ScopedThreadRef) => historyAtomFamily(threadKey(ref)), }; }