import assert from "node:assert/strict";import { ApprovalRequestId, EventId, ProviderItemId, type ProviderApprovalDecision, type ProviderEvent, type ProviderSession, type ProviderTurnStartResult, type ProviderUserInputAnswers, ThreadId, TurnId,} from "@t3tools/contracts";import * as NodeServices from "@effect/platform-node/NodeServices";import { afterAll, it, vi } from "@effect/vitest"; import { Effect, Fiber, Layer, Option, Stream } from "effect"; import { CodexAppServerManager, type CodexAppServerStartSessionInput, type CodexAppServerSendTurnInput,} from "../../codexAppServerManager.ts";import { ServerConfig } from "../../config.ts";import { ProviderAdapterValidationError } from "../Errors.ts";import { CodexAdapter } from "../Services/CodexAdapter.ts";import { ProviderSessionDirectory } from "../Services/ProviderSessionDirectory.ts";import { makeCodexAdapterLive } from "./CodexAdapter.ts"; const asThreadId = (value: string): ThreadId => ThreadId.makeUnsafe(value);const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value);const asEventId = (value: string): EventId => EventId.makeUnsafe(value);const asItemId = (value: string): ProviderItemId => ProviderItemId.makeUnsafe(value); class FakeCodexManager extends CodexAppServerManager { public startSessionImpl = vi.fn( async (input: CodexAppServerStartSessionInput): Promise<ProviderSession> => { const now = new Date().toISOString(); return { provider: "codex", status: "ready", runtimeMode: input.runtimeMode, threadId: input.threadId, cwd: input.cwd, createdAt: now, updatedAt: now, }; }, ); public sendTurnImpl = vi.fn( async (_input: CodexAppServerSendTurnInput): Promise<ProviderTurnStartResult> => ({ threadId: asThreadId("thread-1"), turnId: asTurnId("turn-1"), }), ); public interruptTurnImpl = vi.fn( async (_threadId: ThreadId, _turnId?: TurnId): Promise<void> => undefined, ); public readThreadImpl = vi.fn(async (_threadId: ThreadId) => ({ threadId: asThreadId("thread-1"), turns: [], })); public rollbackThreadImpl = vi.fn(async (_threadId: ThreadId, _numTurns: number) => ({ threadId: asThreadId("thread-1"), turns: [], })); public respondToRequestImpl = vi.fn( async ( _threadId: ThreadId, _requestId: ApprovalRequestId, _decision: ProviderApprovalDecision, ): Promise<void> => undefined, ); public respondToUserInputImpl = vi.fn( async ( _threadId: ThreadId, _requestId: ApprovalRequestId, _answers: ProviderUserInputAnswers, ): Promise<void> => undefined, ); public stopAllImpl = vi.fn(() => undefined); override startSession(input: CodexAppServerStartSessionInput): Promise<ProviderSession> { return this.startSessionImpl(input); } override sendTurn(input: CodexAppServerSendTurnInput): Promise<ProviderTurnStartResult> { return this.sendTurnImpl(input); } override interruptTurn(threadId: ThreadId, turnId?: TurnId): Promise<void> { return this.interruptTurnImpl(threadId, turnId); } override readThread(threadId: ThreadId) { return this.readThreadImpl(threadId); } override rollbackThread(threadId: ThreadId, numTurns: number) { return this.rollbackThreadImpl(threadId, numTurns); } override respondToRequest( threadId: ThreadId, requestId: ApprovalRequestId, decision: ProviderApprovalDecision, ): Promise<void> { return this.respondToRequestImpl(threadId, requestId, decision); } override respondToUserInput( threadId: ThreadId, requestId: ApprovalRequestId, answers: ProviderUserInputAnswers, ): Promise<void> { return this.respondToUserInputImpl(threadId, requestId, answers); } override stopSession(_threadId: ThreadId): void {} override listSessions(): ProviderSession[] { return []; } override hasSession(_threadId: ThreadId): boolean { return false; } override stopAll(): void { this.stopAllImpl(); }} const providerSessionDirectoryTestLayer = Layer.succeed(ProviderSessionDirectory, { upsert: () => Effect.void, getProvider: () => Effect.die(new Error("ProviderSessionDirectory.getProvider is not used in test")), getBinding: () => Effect.succeed(Option.none()), remove: () => Effect.void, listThreadIds: () => Effect.succeed([]),}); const validationManager = new FakeCodexManager();const validationLayer = it.layer( makeCodexAdapterLive({ manager: validationManager }).pipe( Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), Layer.provideMerge(providerSessionDirectoryTestLayer), Layer.provideMerge(NodeServices.layer), ),); validationLayer("CodexAdapterLive validation", (it) => { it.effect("returns validation error for non-codex provider on startSession", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const result = yield* adapter .startSession({ provider: "claudeAgent", threadId: asThreadId("thread-1"), runtimeMode: "full-access", }) .pipe(Effect.result); assert.equal(result._tag, "Failure"); assert.deepStrictEqual( result.failure, new ProviderAdapterValidationError({ provider: "codex", operation: "startSession", issue: "Expected provider 'codex' but received 'claudeAgent'.", }), ); assert.equal(validationManager.startSessionImpl.mock.calls.length, 0); }), ); it.effect("maps codex model options before starting a session", () => Effect.gen(function* () { validationManager.startSessionImpl.mockClear(); const adapter = yield* CodexAdapter; yield* adapter.startSession({ provider: "codex", threadId: asThreadId("thread-1"), model: "gpt-5.3-codex", modelOptions: { codex: { fastMode: true, }, }, runtimeMode: "full-access", }); assert.deepStrictEqual(validationManager.startSessionImpl.mock.calls[0]?.[0], { provider: "codex", threadId: asThreadId("thread-1"), model: "gpt-5.3-codex", serviceTier: "fast", runtimeMode: "full-access", }); }), );}); const sessionErrorManager = new FakeCodexManager();sessionErrorManager.sendTurnImpl.mockImplementation(async () => { throw new Error("Unknown session: sess-missing");});const sessionErrorLayer = it.layer( makeCodexAdapterLive({ manager: sessionErrorManager }).pipe( Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), Layer.provideMerge(providerSessionDirectoryTestLayer), Layer.provideMerge(NodeServices.layer), ),); sessionErrorLayer("CodexAdapterLive session errors", (it) => { it.effect("maps unknown-session sendTurn errors to ProviderAdapterSessionNotFoundError", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const result = yield* adapter .sendTurn({ threadId: asThreadId("sess-missing"), input: "hello", attachments: [], }) .pipe(Effect.result); assert.equal(result._tag, "Failure"); if (result._tag !== "Failure") { return; } assert.equal(result.failure._tag, "ProviderAdapterSessionNotFoundError"); if (result.failure._tag !== "ProviderAdapterSessionNotFoundError") { return; } assert.equal(result.failure.provider, "codex"); assert.equal(result.failure.threadId, "sess-missing"); assert.equal(result.failure.cause instanceof Error, true); }), ); it.effect("maps codex model options before sending a turn", () => Effect.gen(function* () { sessionErrorManager.sendTurnImpl.mockClear(); const adapter = yield* CodexAdapter; yield* Effect.ignore( adapter.sendTurn({ threadId: asThreadId("sess-missing"), input: "hello", model: "gpt-5.3-codex", modelOptions: { codex: { reasoningEffort: "high", fastMode: true, }, }, attachments: [], }), ); assert.deepStrictEqual(sessionErrorManager.sendTurnImpl.mock.calls[0]?.[0], { threadId: asThreadId("sess-missing"), input: "hello", model: "gpt-5.3-codex", effort: "high", serviceTier: "fast", }); }), );}); const lifecycleManager = new FakeCodexManager();const lifecycleLayer = it.layer( makeCodexAdapterLive({ manager: lifecycleManager }).pipe( Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), Layer.provideMerge(providerSessionDirectoryTestLayer), Layer.provideMerge(NodeServices.layer), ),); lifecycleLayer("CodexAdapterLive lifecycle", (it) => { it.effect("maps completed agent message items to canonical item.completed events", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); const event: ProviderEvent = { id: asEventId("evt-msg-complete"), kind: "notification", provider: "codex", createdAt: new Date().toISOString(), method: "item/completed", threadId: asThreadId("thread-1"), turnId: asTurnId("turn-1"), itemId: asItemId("msg_1"), payload: { item: { type: "agentMessage", id: "msg_1", }, }, }; lifecycleManager.emit("event", event); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "item.completed"); if (firstEvent.value.type !== "item.completed") { return; } assert.equal(firstEvent.value.itemId, "msg_1"); assert.equal(firstEvent.value.turnId, "turn-1"); assert.equal(firstEvent.value.payload.itemType, "assistant_message"); }), ); it.effect("maps completed plan items to canonical proposed-plan completion events", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); const event: ProviderEvent = { id: asEventId("evt-plan-complete"), kind: "notification", provider: "codex", createdAt: new Date().toISOString(), method: "item/completed", threadId: asThreadId("thread-1"), turnId: asTurnId("turn-1"), itemId: asItemId("plan_1"), payload: { item: { type: "Plan", id: "plan_1", text: "## Final plan\n\n- one\n- two", }, }, }; lifecycleManager.emit("event", event); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "turn.proposed.completed"); if (firstEvent.value.type !== "turn.proposed.completed") { return; } assert.equal(firstEvent.value.turnId, "turn-1"); assert.equal(firstEvent.value.payload.planMarkdown, "## Final plan\n\n- one\n- two"); }), ); it.effect("maps plan deltas to canonical proposed-plan delta events", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); lifecycleManager.emit("event", { id: asEventId("evt-plan-delta"), kind: "notification", provider: "codex", createdAt: new Date().toISOString(), method: "item/plan/delta", threadId: asThreadId("thread-1"), turnId: asTurnId("turn-1"), itemId: asItemId("plan_1"), payload: { delta: "## Final plan", }, } satisfies ProviderEvent); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "turn.proposed.delta"); if (firstEvent.value.type !== "turn.proposed.delta") { return; } assert.equal(firstEvent.value.turnId, "turn-1"); assert.equal(firstEvent.value.payload.delta, "## Final plan"); }), ); it.effect("maps session/closed lifecycle events to canonical session.exited runtime events", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); const event: ProviderEvent = { id: asEventId("evt-session-closed"), kind: "session", provider: "codex", threadId: asThreadId("thread-1"), createdAt: new Date().toISOString(), method: "session/closed", message: "Session stopped", }; lifecycleManager.emit("event", event); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "session.exited"); if (firstEvent.value.type !== "session.exited") { return; } assert.equal(firstEvent.value.threadId, "thread-1"); assert.equal(firstEvent.value.payload.reason, "Session stopped"); }), ); it.effect("maps retryable Codex error notifications to runtime.warning", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); lifecycleManager.emit("event", { id: asEventId("evt-retryable-error"), kind: "notification", provider: "codex", threadId: asThreadId("thread-1"), createdAt: new Date().toISOString(), method: "error", turnId: asTurnId("turn-1"), payload: { error: { message: "Reconnecting... 2/5", }, willRetry: true, }, } satisfies ProviderEvent); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "runtime.warning"); if (firstEvent.value.type !== "runtime.warning") { return; } assert.equal(firstEvent.value.turnId, "turn-1"); assert.equal(firstEvent.value.payload.message, "Reconnecting... 2/5"); }), ); it.effect("preserves request type when mapping serverRequest/resolved", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); const event: ProviderEvent = { id: asEventId("evt-request-resolved"), kind: "notification", provider: "codex", threadId: asThreadId("thread-1"), createdAt: new Date().toISOString(), method: "serverRequest/resolved", requestId: ApprovalRequestId.makeUnsafe("req-1"), payload: { request: { method: "item/commandExecution/requestApproval", }, decision: "accept", }, }; lifecycleManager.emit("event", event); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "request.resolved"); if (firstEvent.value.type !== "request.resolved") { return;Lines 1–500 of 849. Load more
import assert from "node:assert/strict"; import { ApprovalRequestId, EventId, ProviderItemId, type ProviderApprovalDecision, type ProviderEvent, type ProviderSession, type ProviderTurnStartResult, type ProviderUserInputAnswers, ThreadId, TurnId, } from "@t3tools/contracts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { afterAll, it, vi } from "@effect/vitest"; import { Effect, Fiber, Layer, Option, Stream } from "effect"; import { CodexAppServerManager, type CodexAppServerStartSessionInput, type CodexAppServerSendTurnInput, } from "../../codexAppServerManager.ts"; import { ServerConfig } from "../../config.ts"; import { ProviderAdapterValidationError } from "../Errors.ts"; import { CodexAdapter } from "../Services/CodexAdapter.ts"; import { ProviderSessionDirectory } from "../Services/ProviderSessionDirectory.ts"; import { makeCodexAdapterLive } from "./CodexAdapter.ts"; const asThreadId = (value: string): ThreadId => ThreadId.makeUnsafe(value); const asTurnId = (value: string): TurnId => TurnId.makeUnsafe(value); const asEventId = (value: string): EventId => EventId.makeUnsafe(value); const asItemId = (value: string): ProviderItemId => ProviderItemId.makeUnsafe(value); class FakeCodexManager extends CodexAppServerManager { public startSessionImpl = vi.fn( async (input: CodexAppServerStartSessionInput): Promise<ProviderSession> => { const now = new Date().toISOString(); return { provider: "codex", status: "ready", runtimeMode: input.runtimeMode, threadId: input.threadId, cwd: input.cwd, createdAt: now, updatedAt: now, }; }, ); public sendTurnImpl = vi.fn( async (_input: CodexAppServerSendTurnInput): Promise<ProviderTurnStartResult> => ({ threadId: asThreadId("thread-1"), turnId: asTurnId("turn-1"), }), ); public interruptTurnImpl = vi.fn( async (_threadId: ThreadId, _turnId?: TurnId): Promise<void> => undefined, ); public readThreadImpl = vi.fn(async (_threadId: ThreadId) => ({ threadId: asThreadId("thread-1"), turns: [], })); public rollbackThreadImpl = vi.fn(async (_threadId: ThreadId, _numTurns: number) => ({ threadId: asThreadId("thread-1"), turns: [], })); public respondToRequestImpl = vi.fn( async ( _threadId: ThreadId, _requestId: ApprovalRequestId, _decision: ProviderApprovalDecision, ): Promise<void> => undefined, ); public respondToUserInputImpl = vi.fn( async ( _threadId: ThreadId, _requestId: ApprovalRequestId, _answers: ProviderUserInputAnswers, ): Promise<void> => undefined, ); public stopAllImpl = vi.fn(() => undefined); override startSession(input: CodexAppServerStartSessionInput): Promise<ProviderSession> { return this.startSessionImpl(input); } override sendTurn(input: CodexAppServerSendTurnInput): Promise<ProviderTurnStartResult> { return this.sendTurnImpl(input); } override interruptTurn(threadId: ThreadId, turnId?: TurnId): Promise<void> { return this.interruptTurnImpl(threadId, turnId); } override readThread(threadId: ThreadId) { return this.readThreadImpl(threadId); } override rollbackThread(threadId: ThreadId, numTurns: number) { return this.rollbackThreadImpl(threadId, numTurns); } override respondToRequest( threadId: ThreadId, requestId: ApprovalRequestId, decision: ProviderApprovalDecision, ): Promise<void> { return this.respondToRequestImpl(threadId, requestId, decision); } override respondToUserInput( threadId: ThreadId, requestId: ApprovalRequestId, answers: ProviderUserInputAnswers, ): Promise<void> { return this.respondToUserInputImpl(threadId, requestId, answers); } override stopSession(_threadId: ThreadId): void {} override listSessions(): ProviderSession[] { return []; } override hasSession(_threadId: ThreadId): boolean { return false; } override stopAll(): void { this.stopAllImpl(); } } const providerSessionDirectoryTestLayer = Layer.succeed(ProviderSessionDirectory, { upsert: () => Effect.void, getProvider: () => Effect.die(new Error("ProviderSessionDirectory.getProvider is not used in test")), getBinding: () => Effect.succeed(Option.none()), remove: () => Effect.void, listThreadIds: () => Effect.succeed([]), }); const validationManager = new FakeCodexManager(); const validationLayer = it.layer( makeCodexAdapterLive({ manager: validationManager }).pipe( Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), Layer.provideMerge(providerSessionDirectoryTestLayer), Layer.provideMerge(NodeServices.layer), ), ); validationLayer("CodexAdapterLive validation", (it) => { it.effect("returns validation error for non-codex provider on startSession", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const result = yield* adapter .startSession({ provider: "claudeAgent", threadId: asThreadId("thread-1"), runtimeMode: "full-access", }) .pipe(Effect.result); assert.equal(result._tag, "Failure"); assert.deepStrictEqual( result.failure, new ProviderAdapterValidationError({ provider: "codex", operation: "startSession", issue: "Expected provider 'codex' but received 'claudeAgent'.", }), ); assert.equal(validationManager.startSessionImpl.mock.calls.length, 0); }), ); it.effect("maps codex model options before starting a session", () => Effect.gen(function* () { validationManager.startSessionImpl.mockClear(); const adapter = yield* CodexAdapter; yield* adapter.startSession({ provider: "codex", threadId: asThreadId("thread-1"), model: "gpt-5.3-codex", modelOptions: { codex: { fastMode: true, }, }, runtimeMode: "full-access", }); assert.deepStrictEqual(validationManager.startSessionImpl.mock.calls[0]?.[0], { provider: "codex", threadId: asThreadId("thread-1"), model: "gpt-5.3-codex", serviceTier: "fast", runtimeMode: "full-access", }); }), ); }); const sessionErrorManager = new FakeCodexManager(); sessionErrorManager.sendTurnImpl.mockImplementation(async () => { throw new Error("Unknown session: sess-missing"); }); const sessionErrorLayer = it.layer( makeCodexAdapterLive({ manager: sessionErrorManager }).pipe( Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), Layer.provideMerge(providerSessionDirectoryTestLayer), Layer.provideMerge(NodeServices.layer), ), ); sessionErrorLayer("CodexAdapterLive session errors", (it) => { it.effect("maps unknown-session sendTurn errors to ProviderAdapterSessionNotFoundError", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const result = yield* adapter .sendTurn({ threadId: asThreadId("sess-missing"), input: "hello", attachments: [], }) .pipe(Effect.result); assert.equal(result._tag, "Failure"); if (result._tag !== "Failure") { return; } assert.equal(result.failure._tag, "ProviderAdapterSessionNotFoundError"); if (result.failure._tag !== "ProviderAdapterSessionNotFoundError") { return; } assert.equal(result.failure.provider, "codex"); assert.equal(result.failure.threadId, "sess-missing"); assert.equal(result.failure.cause instanceof Error, true); }), ); it.effect("maps codex model options before sending a turn", () => Effect.gen(function* () { sessionErrorManager.sendTurnImpl.mockClear(); const adapter = yield* CodexAdapter; yield* Effect.ignore( adapter.sendTurn({ threadId: asThreadId("sess-missing"), input: "hello", model: "gpt-5.3-codex", modelOptions: { codex: { reasoningEffort: "high", fastMode: true, }, }, attachments: [], }), ); assert.deepStrictEqual(sessionErrorManager.sendTurnImpl.mock.calls[0]?.[0], { threadId: asThreadId("sess-missing"), input: "hello", model: "gpt-5.3-codex", effort: "high", serviceTier: "fast", }); }), ); }); const lifecycleManager = new FakeCodexManager(); const lifecycleLayer = it.layer( makeCodexAdapterLive({ manager: lifecycleManager }).pipe( Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())), Layer.provideMerge(providerSessionDirectoryTestLayer), Layer.provideMerge(NodeServices.layer), ), ); lifecycleLayer("CodexAdapterLive lifecycle", (it) => { it.effect("maps completed agent message items to canonical item.completed events", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); const event: ProviderEvent = { id: asEventId("evt-msg-complete"), kind: "notification", provider: "codex", createdAt: new Date().toISOString(), method: "item/completed", threadId: asThreadId("thread-1"), turnId: asTurnId("turn-1"), itemId: asItemId("msg_1"), payload: { item: { type: "agentMessage", id: "msg_1", }, }, }; lifecycleManager.emit("event", event); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "item.completed"); if (firstEvent.value.type !== "item.completed") { return; } assert.equal(firstEvent.value.itemId, "msg_1"); assert.equal(firstEvent.value.turnId, "turn-1"); assert.equal(firstEvent.value.payload.itemType, "assistant_message"); }), ); it.effect("maps completed plan items to canonical proposed-plan completion events", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); const event: ProviderEvent = { id: asEventId("evt-plan-complete"), kind: "notification", provider: "codex", createdAt: new Date().toISOString(), method: "item/completed", threadId: asThreadId("thread-1"), turnId: asTurnId("turn-1"), itemId: asItemId("plan_1"), payload: { item: { type: "Plan", id: "plan_1", text: "## Final plan\n\n- one\n- two", }, }, }; lifecycleManager.emit("event", event); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "turn.proposed.completed"); if (firstEvent.value.type !== "turn.proposed.completed") { return; } assert.equal(firstEvent.value.turnId, "turn-1"); assert.equal(firstEvent.value.payload.planMarkdown, "## Final plan\n\n- one\n- two"); }), ); it.effect("maps plan deltas to canonical proposed-plan delta events", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); lifecycleManager.emit("event", { id: asEventId("evt-plan-delta"), kind: "notification", provider: "codex", createdAt: new Date().toISOString(), method: "item/plan/delta", threadId: asThreadId("thread-1"), turnId: asTurnId("turn-1"), itemId: asItemId("plan_1"), payload: { delta: "## Final plan", }, } satisfies ProviderEvent); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "turn.proposed.delta"); if (firstEvent.value.type !== "turn.proposed.delta") { return; } assert.equal(firstEvent.value.turnId, "turn-1"); assert.equal(firstEvent.value.payload.delta, "## Final plan"); }), ); it.effect("maps session/closed lifecycle events to canonical session.exited runtime events", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); const event: ProviderEvent = { id: asEventId("evt-session-closed"), kind: "session", provider: "codex", threadId: asThreadId("thread-1"), createdAt: new Date().toISOString(), method: "session/closed", message: "Session stopped", }; lifecycleManager.emit("event", event); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "session.exited"); if (firstEvent.value.type !== "session.exited") { return; } assert.equal(firstEvent.value.threadId, "thread-1"); assert.equal(firstEvent.value.payload.reason, "Session stopped"); }), ); it.effect("maps retryable Codex error notifications to runtime.warning", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); lifecycleManager.emit("event", { id: asEventId("evt-retryable-error"), kind: "notification", provider: "codex", threadId: asThreadId("thread-1"), createdAt: new Date().toISOString(), method: "error", turnId: asTurnId("turn-1"), payload: { error: { message: "Reconnecting... 2/5", }, willRetry: true, }, } satisfies ProviderEvent); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "runtime.warning"); if (firstEvent.value.type !== "runtime.warning") { return; } assert.equal(firstEvent.value.turnId, "turn-1"); assert.equal(firstEvent.value.payload.message, "Reconnecting... 2/5"); }), ); it.effect("preserves request type when mapping serverRequest/resolved", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild); const event: ProviderEvent = { id: asEventId("evt-request-resolved"), kind: "notification", provider: "codex", threadId: asThreadId("thread-1"), createdAt: new Date().toISOString(), method: "serverRequest/resolved", requestId: ApprovalRequestId.makeUnsafe("req-1"), payload: { request: { method: "item/commandExecution/requestApproval", }, decision: "accept", }, }; lifecycleManager.emit("event", event); const firstEvent = yield* Fiber.join(firstEventFiber); assert.equal(firstEvent._tag, "Some"); if (firstEvent._tag !== "Some") { return; } assert.equal(firstEvent.value.type, "request.resolved"); if (firstEvent.value.type !== "request.resolved") { return;