From 70c74ebb0112d4fba6a181deee9971a6ca7546fd Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 1 Apr 2026 00:26:47 -0700 Subject: [PATCH 1/2] migrate Effect.fn in apps/server/src/git/Layers/CodexTextGeneration.ts Co-authored-by: codex --- .../src/git/Layers/CodexTextGeneration.ts | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/apps/server/src/git/Layers/CodexTextGeneration.ts b/apps/server/src/git/Layers/CodexTextGeneration.ts index 9bd838773b..5435db358a 100644 --- a/apps/server/src/git/Layers/CodexTextGeneration.ts +++ b/apps/server/src/git/Layers/CodexTextGeneration.ts @@ -93,7 +93,10 @@ const makeCodexTextGeneration = Effect.gen(function* () { | "generateThreadTitle", attachments: BranchNameGenerationInput["attachments"], ): Effect.Effect => - Effect.gen(function* () { + Effect.fn("materializeImageAttachments")(function* (): Effect.fn.Return< + MaterializedImageAttachments, + TextGenerationError + > { if (!attachments || attachments.length === 0) { return { imagePaths: [] }; } @@ -120,7 +123,7 @@ const makeCodexTextGeneration = Effect.gen(function* () { imagePaths.push(resolvedPath); } return { imagePaths }; - }); + })(); const runCodexJson = ({ operation, @@ -143,7 +146,11 @@ const makeCodexTextGeneration = Effect.gen(function* () { cleanupPaths?: ReadonlyArray; modelSelection: CodexModelSelection; }): Effect.Effect => - Effect.gen(function* () { + Effect.fn("runCodexJson")(function* (): Effect.fn.Return< + S["Type"], + TextGenerationError, + S["DecodingServices"] + > { const schemaPath = yield* writeTempFile( operation, "codex-schema", @@ -156,7 +163,7 @@ const makeCodexTextGeneration = Effect.gen(function* () { (settings) => settings.providers.codex, ).pipe(Effect.catch(() => Effect.undefined)); - const runCodexCommand = Effect.gen(function* () { + const runCodexCommand = Effect.fn("runCodexJson.runCodexCommand")(function* () { const normalizedOptions = normalizeCodexModelOptionsWithCapabilities( getCodexModelCapabilities(modelSelection.model), modelSelection.options, @@ -237,7 +244,11 @@ const makeCodexTextGeneration = Effect.gen(function* () { }, ).pipe(Effect.asVoid); - return yield* Effect.gen(function* () { + return yield* Effect.fn("runCodexJson.readOutput")(function* (): Effect.fn.Return< + S["Type"], + TextGenerationError, + S["DecodingServices"] + > { yield* runCodexCommand.pipe( Effect.scoped, Effect.timeoutOption(CODEX_TIMEOUT_MS), @@ -272,8 +283,8 @@ const makeCodexTextGeneration = Effect.gen(function* () { ), ), ); - }).pipe(Effect.ensuring(cleanup)); - }); + })().pipe(Effect.ensuring(cleanup)); + })(); const generateCommitMessage: TextGenerationShape["generateCommitMessage"] = Effect.fn( "CodexTextGeneration.generateCommitMessage", From 30757b30140e53143b4dfb271b5a35d8989752e0 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 1 Apr 2026 08:50:16 -0700 Subject: [PATCH 2/2] bad codex --- .../src/git/Layers/CodexTextGeneration.ts | 307 +++++++++--------- 1 file changed, 147 insertions(+), 160 deletions(-) diff --git a/apps/server/src/git/Layers/CodexTextGeneration.ts b/apps/server/src/git/Layers/CodexTextGeneration.ts index 5435db358a..52ddf55453 100644 --- a/apps/server/src/git/Layers/CodexTextGeneration.ts +++ b/apps/server/src/git/Layers/CodexTextGeneration.ts @@ -85,47 +85,43 @@ const makeCodexTextGeneration = Effect.gen(function* () { const safeUnlink = (filePath: string): Effect.Effect => fileSystem.remove(filePath).pipe(Effect.catch(() => Effect.void)); - const materializeImageAttachments = ( + const materializeImageAttachments = Effect.fn("materializeImageAttachments")(function* ( _operation: | "generateCommitMessage" | "generatePrContent" | "generateBranchName" | "generateThreadTitle", attachments: BranchNameGenerationInput["attachments"], - ): Effect.Effect => - Effect.fn("materializeImageAttachments")(function* (): Effect.fn.Return< - MaterializedImageAttachments, - TextGenerationError - > { - if (!attachments || attachments.length === 0) { - return { imagePaths: [] }; - } + ): Effect.fn.Return { + if (!attachments || attachments.length === 0) { + return { imagePaths: [] }; + } - const imagePaths: string[] = []; - for (const attachment of attachments) { - if (attachment.type !== "image") { - continue; - } + const imagePaths: string[] = []; + for (const attachment of attachments) { + if (attachment.type !== "image") { + continue; + } - const resolvedPath = resolveAttachmentPath({ - attachmentsDir: serverConfig.attachmentsDir, - attachment, - }); - if (!resolvedPath || !path.isAbsolute(resolvedPath)) { - continue; - } - const fileInfo = yield* fileSystem - .stat(resolvedPath) - .pipe(Effect.catch(() => Effect.succeed(null))); - if (!fileInfo || fileInfo.type !== "File") { - continue; - } - imagePaths.push(resolvedPath); + const resolvedPath = resolveAttachmentPath({ + attachmentsDir: serverConfig.attachmentsDir, + attachment, + }); + if (!resolvedPath || !path.isAbsolute(resolvedPath)) { + continue; + } + const fileInfo = yield* fileSystem + .stat(resolvedPath) + .pipe(Effect.catch(() => Effect.succeed(null))); + if (!fileInfo || fileInfo.type !== "File") { + continue; } - return { imagePaths }; - })(); + imagePaths.push(resolvedPath); + } + return { imagePaths }; + }); - const runCodexJson = ({ + const runCodexJson = Effect.fn("runCodexJson")(function* ({ operation, cwd, prompt, @@ -145,146 +141,137 @@ const makeCodexTextGeneration = Effect.gen(function* () { imagePaths?: ReadonlyArray; cleanupPaths?: ReadonlyArray; modelSelection: CodexModelSelection; - }): Effect.Effect => - Effect.fn("runCodexJson")(function* (): Effect.fn.Return< - S["Type"], - TextGenerationError, - S["DecodingServices"] - > { - const schemaPath = yield* writeTempFile( - operation, - "codex-schema", - JSON.stringify(toJsonSchemaObject(outputSchemaJson)), - ); - const outputPath = yield* writeTempFile(operation, "codex-output", ""); + }): Effect.fn.Return { + const schemaPath = yield* writeTempFile( + operation, + "codex-schema", + JSON.stringify(toJsonSchemaObject(outputSchemaJson)), + ); + const outputPath = yield* writeTempFile(operation, "codex-output", ""); - const codexSettings = yield* Effect.map( - serverSettingsService.getSettings, - (settings) => settings.providers.codex, - ).pipe(Effect.catch(() => Effect.undefined)); + const codexSettings = yield* Effect.map( + serverSettingsService.getSettings, + (settings) => settings.providers.codex, + ).pipe(Effect.catch(() => Effect.undefined)); - const runCodexCommand = Effect.fn("runCodexJson.runCodexCommand")(function* () { - const normalizedOptions = normalizeCodexModelOptionsWithCapabilities( - getCodexModelCapabilities(modelSelection.model), - modelSelection.options, - ); - const reasoningEffort = - modelSelection.options?.reasoningEffort ?? CODEX_GIT_TEXT_GENERATION_REASONING_EFFORT; - const command = ChildProcess.make( - codexSettings?.binaryPath || "codex", - [ - "exec", - "--ephemeral", - "-s", - "read-only", - "--model", - modelSelection.model, - "--config", - `model_reasoning_effort="${reasoningEffort}"`, - ...(normalizedOptions?.fastMode ? ["--config", `service_tier="fast"`] : []), - "--output-schema", - schemaPath, - "--output-last-message", - outputPath, - ...imagePaths.flatMap((imagePath) => ["--image", imagePath]), - "-", - ], - { - env: { - ...process.env, - ...(codexSettings?.homePath ? { CODEX_HOME: codexSettings.homePath } : {}), - }, - cwd, - shell: process.platform === "win32", - stdin: { - stream: Stream.encodeText(Stream.make(prompt)), - }, + const runCodexCommand = Effect.fn("runCodexJson.runCodexCommand")(function* () { + const normalizedOptions = normalizeCodexModelOptionsWithCapabilities( + getCodexModelCapabilities(modelSelection.model), + modelSelection.options, + ); + const reasoningEffort = + modelSelection.options?.reasoningEffort ?? CODEX_GIT_TEXT_GENERATION_REASONING_EFFORT; + const command = ChildProcess.make( + codexSettings?.binaryPath || "codex", + [ + "exec", + "--ephemeral", + "-s", + "read-only", + "--model", + modelSelection.model, + "--config", + `model_reasoning_effort="${reasoningEffort}"`, + ...(normalizedOptions?.fastMode ? ["--config", `service_tier="fast"`] : []), + "--output-schema", + schemaPath, + "--output-last-message", + outputPath, + ...imagePaths.flatMap((imagePath) => ["--image", imagePath]), + "-", + ], + { + env: { + ...process.env, + ...(codexSettings?.homePath ? { CODEX_HOME: codexSettings.homePath } : {}), }, + cwd, + shell: process.platform === "win32", + stdin: { + stream: Stream.encodeText(Stream.make(prompt)), + }, + }, + ); + + const child = yield* commandSpawner + .spawn(command) + .pipe( + Effect.mapError((cause) => + normalizeCliError("codex", operation, cause, "Failed to spawn Codex CLI process"), + ), ); - const child = yield* commandSpawner - .spawn(command) - .pipe( + const [stdout, stderr, exitCode] = yield* Effect.all( + [ + readStreamAsString(operation, child.stdout), + readStreamAsString(operation, child.stderr), + child.exitCode.pipe( Effect.mapError((cause) => - normalizeCliError("codex", operation, cause, "Failed to spawn Codex CLI process"), - ), - ); - - const [stdout, stderr, exitCode] = yield* Effect.all( - [ - readStreamAsString(operation, child.stdout), - readStreamAsString(operation, child.stderr), - child.exitCode.pipe( - Effect.mapError((cause) => - normalizeCliError("codex", operation, cause, "Failed to read Codex CLI exit code"), - ), + normalizeCliError("codex", operation, cause, "Failed to read Codex CLI exit code"), ), - ], - { concurrency: "unbounded" }, - ); + ), + ], + { concurrency: "unbounded" }, + ); - if (exitCode !== 0) { - const stderrDetail = stderr.trim(); - const stdoutDetail = stdout.trim(); - const detail = stderrDetail.length > 0 ? stderrDetail : stdoutDetail; - return yield* new TextGenerationError({ - operation, - detail: - detail.length > 0 - ? `Codex CLI command failed: ${detail}` - : `Codex CLI command failed with code ${exitCode}.`, - }); - } - }); + if (exitCode !== 0) { + const stderrDetail = stderr.trim(); + const stdoutDetail = stdout.trim(); + const detail = stderrDetail.length > 0 ? stderrDetail : stdoutDetail; + return yield* new TextGenerationError({ + operation, + detail: + detail.length > 0 + ? `Codex CLI command failed: ${detail}` + : `Codex CLI command failed with code ${exitCode}.`, + }); + } + }); - const cleanup = Effect.all( - [schemaPath, outputPath, ...cleanupPaths].map((filePath) => safeUnlink(filePath)), - { - concurrency: "unbounded", - }, - ).pipe(Effect.asVoid); - - return yield* Effect.fn("runCodexJson.readOutput")(function* (): Effect.fn.Return< - S["Type"], - TextGenerationError, - S["DecodingServices"] - > { - yield* runCodexCommand.pipe( - Effect.scoped, - Effect.timeoutOption(CODEX_TIMEOUT_MS), - Effect.flatMap( - Option.match({ - onNone: () => - Effect.fail( - new TextGenerationError({ operation, detail: "Codex CLI request timed out." }), - ), - onSome: () => Effect.void, - }), - ), - ); + const cleanup = Effect.all( + [schemaPath, outputPath, ...cleanupPaths].map((filePath) => safeUnlink(filePath)), + { + concurrency: "unbounded", + }, + ).pipe(Effect.asVoid); + + return yield* Effect.gen(function* () { + yield* runCodexCommand().pipe( + Effect.scoped, + Effect.timeoutOption(CODEX_TIMEOUT_MS), + Effect.flatMap( + Option.match({ + onNone: () => + Effect.fail( + new TextGenerationError({ operation, detail: "Codex CLI request timed out." }), + ), + onSome: () => Effect.void, + }), + ), + ); - return yield* fileSystem.readFileString(outputPath).pipe( - Effect.mapError( - (cause) => - new TextGenerationError({ - operation, - detail: "Failed to read Codex output file.", - cause, - }), - ), - Effect.flatMap(Schema.decodeEffect(Schema.fromJsonString(outputSchemaJson))), - Effect.catchTag("SchemaError", (cause) => - Effect.fail( - new TextGenerationError({ - operation, - detail: "Codex returned invalid structured output.", - cause, - }), - ), + return yield* fileSystem.readFileString(outputPath).pipe( + Effect.mapError( + (cause) => + new TextGenerationError({ + operation, + detail: "Failed to read Codex output file.", + cause, + }), + ), + Effect.flatMap(Schema.decodeEffect(Schema.fromJsonString(outputSchemaJson))), + Effect.catchTag("SchemaError", (cause) => + Effect.fail( + new TextGenerationError({ + operation, + detail: "Codex returned invalid structured output.", + cause, + }), ), - ); - })().pipe(Effect.ensuring(cleanup)); - })(); + ), + ); + }).pipe(Effect.ensuring(cleanup)); + }); const generateCommitMessage: TextGenerationShape["generateCommitMessage"] = Effect.fn( "CodexTextGeneration.generateCommitMessage",