import { CommandId, ORCHESTRATION_WS_METHODS, type ClientOrchestrationCommand, } from "@t3tools/contracts"; import * as Crypto from "effect/Crypto"; import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import type { EnvironmentSupervisor } from "../connection/supervisor.ts"; import { type EnvironmentRpcFailure, type EnvironmentRpcSuccess, type EnvironmentRpcUnavailableError, request, } from "../rpc/client.ts"; type CommandType = ClientOrchestrationCommand["type"]; type CommandOf = Extract; type CommandInput = Omit< CommandOf, "type" | "commandId" | "createdAt" > & { readonly commandId?: CommandId; } & ("createdAt" extends keyof CommandOf ? { readonly createdAt?: CommandOf["createdAt"]; } : {}); export type CreateProjectInput = CommandInput<"project.create">; export type UpdateProjectInput = CommandInput<"project.meta.update">; export type DeleteProjectInput = CommandInput<"project.delete">; export type CreateThreadInput = CommandInput<"thread.create">; export type DeleteThreadInput = CommandInput<"thread.delete">; export type ArchiveThreadInput = CommandInput<"thread.archive">; export type UnarchiveThreadInput = CommandInput<"thread.unarchive">; export type UpdateThreadMetadataInput = CommandInput<"thread.meta.update">; export type SetThreadRuntimeModeInput = CommandInput<"thread.runtime-mode.set">; export type SetThreadInteractionModeInput = CommandInput<"thread.interaction-mode.set">; export type StartThreadTurnInput = CommandInput<"thread.turn.start">; export type InterruptThreadTurnInput = CommandInput<"thread.turn.interrupt">; export type RespondToThreadApprovalInput = CommandInput<"thread.approval.respond">; export type RespondToThreadUserInputInput = CommandInput<"thread.user-input.respond">; export type RevertThreadCheckpointInput = CommandInput<"thread.checkpoint.revert">; export type StopThreadSessionInput = CommandInput<"thread.session.stop">; type DispatchTag = typeof ORCHESTRATION_WS_METHODS.dispatchCommand; type CommandEffect = Effect.Effect< EnvironmentRpcSuccess, EnvironmentRpcFailure | EnvironmentRpcUnavailableError, Crypto.Crypto | EnvironmentSupervisor >; function commandId(input: { readonly commandId?: CommandId }) { return Effect.gen(function* () { if (input.commandId !== undefined) { return input.commandId; } const crypto = yield* Crypto.Crypto; return yield* crypto.randomUUIDv4.pipe(Effect.orDie, Effect.map(CommandId.make)); }); } function timestampedCommandMetadata(input: { readonly commandId?: CommandId; readonly createdAt?: string; }) { return Effect.all({ commandId: commandId(input), createdAt: input.createdAt === undefined ? DateTime.now.pipe(Effect.map(DateTime.formatIso)) : Effect.succeed(input.createdAt), }); } function dispatch(command: ClientOrchestrationCommand) { return request(ORCHESTRATION_WS_METHODS.dispatchCommand, command); } export const createProject: (input: CreateProjectInput) => CommandEffect = Effect.fn( "EnvironmentCommands.createProject", )(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "project.create", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const updateProject: (input: UpdateProjectInput) => CommandEffect = Effect.fn( "EnvironmentCommands.updateProject", )(function* (input) { return yield* dispatch({ ...input, type: "project.meta.update", commandId: yield* commandId(input), }); }); export const deleteProject: (input: DeleteProjectInput) => CommandEffect = Effect.fn( "EnvironmentCommands.deleteProject", )(function* (input) { return yield* dispatch({ ...input, type: "project.delete", commandId: yield* commandId(input), }); }); export const createThread: (input: CreateThreadInput) => CommandEffect = Effect.fn( "EnvironmentCommands.createThread", )(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.create", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const deleteThread: (input: DeleteThreadInput) => CommandEffect = Effect.fn( "EnvironmentCommands.deleteThread", )(function* (input) { return yield* dispatch({ ...input, type: "thread.delete", commandId: yield* commandId(input), }); }); export const archiveThread: (input: ArchiveThreadInput) => CommandEffect = Effect.fn( "EnvironmentCommands.archiveThread", )(function* (input) { return yield* dispatch({ ...input, type: "thread.archive", commandId: yield* commandId(input), }); }); export const unarchiveThread: (input: UnarchiveThreadInput) => CommandEffect = Effect.fn( "EnvironmentCommands.unarchiveThread", )(function* (input) { return yield* dispatch({ ...input, type: "thread.unarchive", commandId: yield* commandId(input), }); }); export const updateThreadMetadata: (input: UpdateThreadMetadataInput) => CommandEffect = Effect.fn( "EnvironmentCommands.updateThreadMetadata", )(function* (input) { return yield* dispatch({ ...input, type: "thread.meta.update", commandId: yield* commandId(input), }); }); export const setThreadRuntimeMode: (input: SetThreadRuntimeModeInput) => CommandEffect = Effect.fn( "EnvironmentCommands.setThreadRuntimeMode", )(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.runtime-mode.set", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const setThreadInteractionMode: (input: SetThreadInteractionModeInput) => CommandEffect = Effect.fn("EnvironmentCommands.setThreadInteractionMode")(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.interaction-mode.set", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const startThreadTurn: (input: StartThreadTurnInput) => CommandEffect = Effect.fn( "EnvironmentCommands.startThreadTurn", )(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.turn.start", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const interruptThreadTurn: (input: InterruptThreadTurnInput) => CommandEffect = Effect.fn( "EnvironmentCommands.interruptThreadTurn", )(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.turn.interrupt", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const respondToThreadApproval: (input: RespondToThreadApprovalInput) => CommandEffect = Effect.fn("EnvironmentCommands.respondToThreadApproval")(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.approval.respond", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const respondToThreadUserInput: (input: RespondToThreadUserInputInput) => CommandEffect = Effect.fn("EnvironmentCommands.respondToThreadUserInput")(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.user-input.respond", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const revertThreadCheckpoint: (input: RevertThreadCheckpointInput) => CommandEffect = Effect.fn("EnvironmentCommands.revertThreadCheckpoint")(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.checkpoint.revert", commandId: metadata.commandId, createdAt: metadata.createdAt, }); }); export const stopThreadSession: (input: StopThreadSessionInput) => CommandEffect = Effect.fn( "EnvironmentCommands.stopThreadSession", )(function* (input) { const metadata = yield* timestampedCommandMetadata(input); return yield* dispatch({ ...input, type: "thread.session.stop", commandId: metadata.commandId, createdAt: metadata.createdAt, }); });