import * as Effect from "effect/Effect"; import { MAX_MEMORIES } from "../shared/memory.ts"; import { MAX_TASKS } from "../shared/tasks.ts"; import { AgentFailure, agentCall } from "./agent-io.ts"; const messages = { invalid_model: "Select a supported provider and model", invalid_effort: "Select a supported effort for this model", provider_disabled: "Enable OpenRouter in Settings before using this provider", credential_unreadable: "Stored provider API key cannot be read; replace it in settings", credential_save_failed: "Provider API key could not be saved", conversation_missing: "Conversation not found", memory_deleted: "Memory was deleted", memory_full: `Memory limit reached (${MAX_MEMORIES}). Delete a fact first.`, memory_edit_conflict: "Memory changed or was deleted. Reload memories before editing.", memory_delete_conflict: "Memory changed. Reload memories before deleting.", task_deleted: "Task was deleted", task_creation_conflict: "Task creation ID was already used for different input", task_past: "New one-off task needs a future instant", task_full: `Task limit reached (${MAX_TASKS})`, }; export type OperationError = keyof typeof messages; export class OperationFailure extends AgentFailure { readonly code: OperationError; constructor(code: OperationError) { super({ message: messages[code] }); this.code = code; } } export type OperationResult = { ok: true; value: A } | { ok: false; error: OperationError }; // Like the registry boundary, transport declared failures as plain codes. // Unexpected errors stay rejected and are sanitized by the calling RPC adapter. export function operationResult( program: Effect.Effect, ): Promise> { return Effect.runPromise( program.pipe( Effect.catchDefect((error) => error instanceof OperationFailure ? Effect.fail(error) : Effect.die(error), ), Effect.map((value): OperationResult => ({ ok: true, value })), Effect.catch((error) => error instanceof OperationFailure ? Effect.succeed>({ ok: false, error: error.code }) : Effect.fail(error), ), ), ); } export function operationCall(call: () => PromiseLike>) { return agentCall(call).pipe( Effect.flatMap((result) => result.ok ? Effect.succeed(result.value) : Effect.fail(new OperationFailure(result.error)), ), ); }