| author | |
| committer | |
| log | bd8e3ee008e4c78085ebfbf47bf216389a92ac75 |
| tree | 63c9fa1697a3fa8d931167505fb7aab682dc710e |
| parent | c1d8a22f073e36768a8af5b0ef6472d802cbfa4d |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: codex <codex@users.noreply.github.com>5 files changed, 91 insertions(+), 114 deletions(-)
apps/server/src/textGeneration/ClaudeTextGeneration.ts+20-18| ... | ... | @@ -234,28 +234,30 @@ export const makeClaudeTextGeneration = Effect.fn("makeClaudeTextGeneration")(fu |
| 234 | 234 | ); |
| 235 | 235 | |
| 236 | 236 | const envelope = yield* decodeClaudeOutputEnvelope(rawStdout).pipe( |
| 237 | Effect.catchTag("SchemaError", (cause) => | |
| 238 | Effect.fail( | |
| 239 | new TextGenerationError({ | |
| 240 | operation, | |
| 241 | detail: "Claude CLI returned unexpected output format.", | |
| 242 | cause, | |
| 243 | }), | |
| 244 | ), | |
| 245 | ), | |
| 237 | Effect.catchTags({ | |
| 238 | SchemaError: (cause) => | |
| 239 | Effect.fail( | |
| 240 | new TextGenerationError({ | |
| 241 | operation, | |
| 242 | detail: "Claude CLI returned unexpected output format.", | |
| 243 | cause, | |
| 244 | }), | |
| 245 | ), | |
| 246 | }), | |
| 246 | 247 | ); |
| 247 | 248 | |
| 248 | 249 | const decodeOutput = Schema.decodeEffect(outputSchemaJson); |
| 249 | 250 | return yield* decodeOutput(envelope.structured_output).pipe( |
| 250 | Effect.catchTag("SchemaError", (cause) => | |
| 251 | Effect.fail( | |
| 252 | new TextGenerationError({ | |
| 253 | operation, | |
| 254 | detail: "Claude returned invalid structured output.", | |
| 255 | cause, | |
| 256 | }), | |
| 257 | ), | |
| 258 | ), | |
| 251 | Effect.catchTags({ | |
| 252 | SchemaError: (cause) => | |
| 253 | Effect.fail( | |
| 254 | new TextGenerationError({ | |
| 255 | operation, | |
| 256 | detail: "Claude returned invalid structured output.", | |
| 257 | cause, | |
| 258 | }), | |
| 259 | ), | |
| 260 | }), | |
| 259 | 261 | ); |
| 260 | 262 | }); |
| 261 | 263 |
apps/server/src/textGeneration/CodexTextGeneration.ts+10-9| ... | ... | @@ -281,15 +281,16 @@ export const makeCodexTextGeneration = Effect.fn("makeCodexTextGeneration")(func |
| 281 | 281 | }), |
| 282 | 282 | ), |
| 283 | 283 | Effect.flatMap(decodeOutput), |
| 284 | Effect.catchTag("SchemaError", (cause) => | |
| 285 | Effect.fail( | |
| 286 | new TextGenerationError({ | |
| 287 | operation, | |
| 288 | detail: "Codex returned invalid structured output.", | |
| 289 | cause, | |
| 290 | }), | |
| 291 | ), | |
| 292 | ), | |
| 284 | Effect.catchTags({ | |
| 285 | SchemaError: (cause) => | |
| 286 | Effect.fail( | |
| 287 | new TextGenerationError({ | |
| 288 | operation, | |
| 289 | detail: "Codex returned invalid structured output.", | |
| 290 | cause, | |
| 291 | }), | |
| 292 | ), | |
| 293 | }), | |
| 293 | 294 | ); |
| 294 | 295 | }).pipe(Effect.ensuring(cleanup)); |
| 295 | 296 | }); |
apps/server/src/textGeneration/CursorTextGeneration.ts+27-40| ... | ... | @@ -28,30 +28,7 @@ import { |
| 28 | 28 | |
| 29 | 29 | const CURSOR_TIMEOUT_MS = 180_000; |
| 30 | 30 | |
| 31 | function mapCursorAcpError( | |
| 32 | operation: | |
| 33 | | "generateCommitMessage" | |
| 34 | | "generatePrContent" | |
| 35 | | "generateBranchName" | |
| 36 | | "generateThreadTitle", | |
| 37 | detail: string, | |
| 38 | cause: unknown, | |
| 39 | ): TextGenerationError { | |
| 40 | return new TextGenerationError({ | |
| 41 | operation, | |
| 42 | detail, | |
| 43 | ...(cause !== undefined ? { cause } : {}), | |
| 44 | }); | |
| 45 | } | |
| 46 | ||
| 47 | function isTextGenerationError(error: unknown): error is TextGenerationError { | |
| 48 | return ( | |
| 49 | typeof error === "object" && | |
| 50 | error !== null && | |
| 51 | "_tag" in error && | |
| 52 | error._tag === "TextGenerationError" | |
| 53 | ); | |
| 54 | } | |
| 31 | const isTextGenerationError = Schema.is(TextGenerationError); | |
| 55 | 32 | |
| 56 | 33 | /** |
| 57 | 34 | * Build a Cursor text-generation closure bound to a specific `CursorSettings` |
| ... | ... | @@ -111,13 +88,14 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu |
| 111 | 88 | model: modelSelection.model, |
| 112 | 89 | selections: modelSelection.options, |
| 113 | 90 | mapError: ({ cause, configId, step }) => |
| 114 | mapCursorAcpError( | |
| 91 | new TextGenerationError({ | |
| 115 | 92 | operation, |
| 116 | step === "set-config-option" | |
| 117 | ? `Failed to set Cursor ACP config option "${configId}" for text generation.` | |
| 118 | : "Failed to set Cursor ACP base model for text generation.", | |
| 93 | detail: | |
| 94 | step === "set-config-option" | |
| 95 | ? `Failed to set Cursor ACP config option "${configId}" for text generation.` | |
| 96 | : "Failed to set Cursor ACP base model for text generation.", | |
| 119 | 97 | cause, |
| 120 | ), | |
| 98 | }), | |
| 121 | 99 | }); |
| 122 | 100 | |
| 123 | 101 | return yield* runtime.prompt({ |
| ... | ... | @@ -140,7 +118,11 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu |
| 140 | 118 | Effect.mapError((cause) => |
| 141 | 119 | isTextGenerationError(cause) |
| 142 | 120 | ? cause |
| 143 | : mapCursorAcpError(operation, "Cursor ACP request failed.", cause), | |
| 121 | : new TextGenerationError({ | |
| 122 | operation, | |
| 123 | detail: "Cursor ACP request failed.", | |
| 124 | cause, | |
| 125 | }), | |
| 144 | 126 | ), |
| 145 | 127 | ); |
| 146 | 128 | |
| ... | ... | @@ -157,21 +139,26 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu |
| 157 | 139 | |
| 158 | 140 | const decodeOutput = Schema.decodeEffect(Schema.fromJsonString(outputSchemaJson)); |
| 159 | 141 | return yield* decodeOutput(extractJsonObject(rawResult)).pipe( |
| 160 | Effect.catchTag("SchemaError", (cause) => | |
| 161 | Effect.fail( | |
| 162 | new TextGenerationError({ | |
| 163 | operation, | |
| 164 | detail: "Cursor Agent returned invalid structured output.", | |
| 165 | cause, | |
| 166 | }), | |
| 167 | ), | |
| 168 | ), | |
| 142 | Effect.catchTags({ | |
| 143 | SchemaError: (cause) => | |
| 144 | Effect.fail( | |
| 145 | new TextGenerationError({ | |
| 146 | operation, | |
| 147 | detail: "Cursor Agent returned invalid structured output.", | |
| 148 | cause, | |
| 149 | }), | |
| 150 | ), | |
| 151 | }), | |
| 169 | 152 | ); |
| 170 | 153 | }).pipe( |
| 171 | 154 | Effect.mapError((cause) => |
| 172 | 155 | isTextGenerationError(cause) |
| 173 | 156 | ? cause |
| 174 | : mapCursorAcpError(operation, "Cursor ACP text generation failed.", cause), | |
| 157 | : new TextGenerationError({ | |
| 158 | operation, | |
| 159 | detail: "Cursor ACP text generation failed.", | |
| 160 | cause, | |
| 161 | }), | |
| 175 | 162 | ), |
| 176 | 163 | Effect.scoped, |
| 177 | 164 | ); |
apps/server/src/textGeneration/GrokTextGeneration.ts+24-38| ... | ... | @@ -31,30 +31,7 @@ import { |
| 31 | 31 | |
| 32 | 32 | const GROK_TIMEOUT_MS = 180_000; |
| 33 | 33 | |
| 34 | function mapGrokAcpError( | |
| 35 | operation: | |
| 36 | | "generateCommitMessage" | |
| 37 | | "generatePrContent" | |
| 38 | | "generateBranchName" | |
| 39 | | "generateThreadTitle", | |
| 40 | detail: string, | |
| 41 | cause: unknown, | |
| 42 | ): TextGenerationError { | |
| 43 | return new TextGenerationError({ | |
| 44 | operation, | |
| 45 | detail, | |
| 46 | ...(cause !== undefined ? { cause } : {}), | |
| 47 | }); | |
| 48 | } | |
| 49 | ||
| 50 | function isTextGenerationError(error: unknown): error is TextGenerationError { | |
| 51 | return ( | |
| 52 | typeof error === "object" && | |
| 53 | error !== null && | |
| 54 | "_tag" in error && | |
| 55 | error._tag === "TextGenerationError" | |
| 56 | ); | |
| 57 | } | |
| 34 | const isTextGenerationError = Schema.is(TextGenerationError); | |
| 58 | 35 | |
| 59 | 36 | export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(function* ( |
| 60 | 37 | grokSettings: GrokSettings, |
| ... | ... | @@ -109,11 +86,11 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi |
| 109 | 86 | currentModelId: currentGrokModelIdFromSessionSetup(started.sessionSetupResult), |
| 110 | 87 | requestedModelId: resolvedModel, |
| 111 | 88 | mapError: (cause) => |
| 112 | mapGrokAcpError( | |
| 89 | new TextGenerationError({ | |
| 113 | 90 | operation, |
| 114 | "Failed to set Grok ACP base model for text generation.", | |
| 91 | detail: "Failed to set Grok ACP base model for text generation.", | |
| 115 | 92 | cause, |
| 116 | ), | |
| 93 | }), | |
| 117 | 94 | }); |
| 118 | 95 | |
| 119 | 96 | return yield* runtime.prompt({ |
| ... | ... | @@ -133,7 +110,11 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi |
| 133 | 110 | Effect.mapError((cause: EffectAcpErrors.AcpError | TextGenerationError) => |
| 134 | 111 | isTextGenerationError(cause) |
| 135 | 112 | ? cause |
| 136 | : mapGrokAcpError(operation, "Grok ACP request failed.", cause), | |
| 113 | : new TextGenerationError({ | |
| 114 | operation, | |
| 115 | detail: "Grok ACP request failed.", | |
| 116 | cause, | |
| 117 | }), | |
| 137 | 118 | ), |
| 138 | 119 | ); |
| 139 | 120 | |
| ... | ... | @@ -150,21 +131,26 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi |
| 150 | 131 | |
| 151 | 132 | const decodeOutput = Schema.decodeEffect(Schema.fromJsonString(outputSchemaJson)); |
| 152 | 133 | return yield* decodeOutput(extractJsonObject(trimmed)).pipe( |
| 153 | Effect.catchTag("SchemaError", (cause) => | |
| 154 | Effect.fail( | |
| 155 | new TextGenerationError({ | |
| 156 | operation, | |
| 157 | detail: "Grok Agent returned invalid structured output.", | |
| 158 | cause, | |
| 159 | }), | |
| 160 | ), | |
| 161 | ), | |
| 134 | Effect.catchTags({ | |
| 135 | SchemaError: (cause) => | |
| 136 | Effect.fail( | |
| 137 | new TextGenerationError({ | |
| 138 | operation, | |
| 139 | detail: "Grok Agent returned invalid structured output.", | |
| 140 | cause, | |
| 141 | }), | |
| 142 | ), | |
| 143 | }), | |
| 162 | 144 | ); |
| 163 | 145 | }).pipe( |
| 164 | 146 | Effect.mapError((cause) => |
| 165 | 147 | isTextGenerationError(cause) |
| 166 | 148 | ? cause |
| 167 | : mapGrokAcpError(operation, "Grok ACP text generation failed.", cause), | |
| 149 | : new TextGenerationError({ | |
| 150 | operation, | |
| 151 | detail: "Grok ACP text generation failed.", | |
| 152 | cause, | |
| 153 | }), | |
| 168 | 154 | ), |
| 169 | 155 | Effect.scoped, |
| 170 | 156 | ); |
apps/server/src/textGeneration/OpenCodeTextGeneration.ts+10-9| ... | ... | @@ -348,15 +348,16 @@ export const makeOpenCodeTextGeneration = Effect.fn("makeOpenCodeTextGeneration" |
| 348 | 348 | |
| 349 | 349 | const decodeOutput = Schema.decodeEffect(Schema.fromJsonString(input.outputSchemaJson)); |
| 350 | 350 | return yield* decodeOutput(extractJsonObject(rawOutput)).pipe( |
| 351 | Effect.catchTag("SchemaError", (cause) => | |
| 352 | Effect.fail( | |
| 353 | new TextGenerationError({ | |
| 354 | operation: input.operation, | |
| 355 | detail: "OpenCode returned invalid structured output.", | |
| 356 | cause, | |
| 357 | }), | |
| 358 | ), | |
| 359 | ), | |
| 351 | Effect.catchTags({ | |
| 352 | SchemaError: (cause) => | |
| 353 | Effect.fail( | |
| 354 | new TextGenerationError({ | |
| 355 | operation: input.operation, | |
| 356 | detail: "OpenCode returned invalid structured output.", | |
| 357 | cause, | |
| 358 | }), | |
| 359 | ), | |
| 360 | }), | |
| 360 | 361 | ); |
| 361 | 362 | }); |
| 362 | 363 |