History

refactor(server): use idiomatic Effect for OpenCode SDK errors

Cursor Agent committed 3 months ago

Commit 654d5521 parent 8b546984 files · +126 −132Browse files

Unified · Split

4 files
  1. apps/server/src/provider/Layers/OpenCodeAdapter.ts +2 −1
  2. apps/server/src/provider/opencodeRuntime.ts +12 −13
  3. apps/server/src/textGeneration/OpenCodeTextGeneration.test.ts +80 −90
  4. apps/server/src/textGeneration/OpenCodeTextGeneration.ts +32 −28
apps/server/src/provider/Layers/OpenCodeAdapter.ts+2−1
@@ -38,6 +38,7 @@ import { · 37 unmodified lines
38import { type OpenCodeAdapterShape } from "../Services/OpenCodeAdapter.ts";38import { type OpenCodeAdapterShape } from "../Services/OpenCodeAdapter.ts";
39import {39import {
40 buildOpenCodePermissionRules,40 buildOpenCodePermissionRules,
41 isOpenCodeRuntimeError,
41 OpenCodeRuntime,42 OpenCodeRuntime,
42 OpenCodeRuntimeError,43 OpenCodeRuntimeError,
43 openCodeQuestionId,44 openCodeQuestionId,
@@ -131,7 +132,7 @@ const toProcessError = (threadId: ThreadId, cause: unknown): ProviderAdapterProc · 87 unmodified lines
131 new ProviderAdapterProcessError({132 new ProviderAdapterProcessError({
132 provider: PROVIDER,133 provider: PROVIDER,
133 threadId,134 threadId,
134 detail: OpenCodeRuntimeError.is(cause) ? cause.detail : openCodeRuntimeErrorDetail(cause),135 detail: isOpenCodeRuntimeError(cause) ? cause.detail : openCodeRuntimeErrorDetail(cause),
135 cause,136 cause,
136 });137 });
137 138
apps/server/src/provider/opencodeRuntime.ts+12−13
@@ -13,14 +13,12 @@ import { · 12 unmodified lines
13} from "@opencode-ai/sdk/v2";13} from "@opencode-ai/sdk/v2";
14import * as Cause from "effect/Cause";14import * as Cause from "effect/Cause";
15import * as Context from "effect/Context";15import * as Context from "effect/Context";
16import * as Data from "effect/Data";
17import * as Deferred from "effect/Deferred";16import * as Deferred from "effect/Deferred";
18import * as Effect from "effect/Effect";17import * as Effect from "effect/Effect";
19import * as Exit from "effect/Exit";18import * as Exit from "effect/Exit";
20import * as Fiber from "effect/Fiber";19import * as Fiber from "effect/Fiber";
21import * as Layer from "effect/Layer";20import * as Layer from "effect/Layer";
22import * as Option from "effect/Option";21import * as Option from "effect/Option";
23import * as P from "effect/Predicate";
24import * as Ref from "effect/Ref";22import * as Ref from "effect/Ref";
25import * as Result from "effect/Result";23import * as Result from "effect/Result";
26import * as Scope from "effect/Scope";24import * as Scope from "effect/Scope";
@@ -50,15 +48,16 @@ export interface OpenCodeServerConnection { · 23 unmodified lines
50 readonly external: boolean;48 readonly external: boolean;
51}49}
52 50
53const OPENCODE_RUNTIME_ERROR_TAG = "OpenCodeRuntimeError";51export class OpenCodeRuntimeError extends Schema.TaggedErrorClass<OpenCodeRuntimeError>()(
54export class OpenCodeRuntimeError extends Data.TaggedError(OPENCODE_RUNTIME_ERROR_TAG)<{52 "OpenCodeRuntimeError",
55 readonly operation: string;53 {
56 readonly cause?: unknown;54 operation: Schema.String,
57 readonly detail: string;55 cause: Schema.optional(Schema.Defect()),
58}> {56 detail: Schema.String,
59 static readonly is = (u: unknown): u is OpenCodeRuntimeError =>57 },
60 P.isTagged(u, OPENCODE_RUNTIME_ERROR_TAG);58) {}
61}59
60export const isOpenCodeRuntimeError = Schema.is(OpenCodeRuntimeError);
62 61
63function encodeJsonStringForDiagnostics(input: unknown): string | undefined {62function encodeJsonStringForDiagnostics(input: unknown): string | undefined {
64 const result = encodeUnknownJsonStringExit(input);63 const result = encodeUnknownJsonStringExit(input);
@@ -66,7 +65,7 @@ function encodeJsonStringForDiagnostics(input: unknown): string | undefined { · 1 unmodified line
66}65}
67 66
68export function openCodeRuntimeErrorDetail(cause: unknown): string {67export function openCodeRuntimeErrorDetail(cause: unknown): string {
69 if (OpenCodeRuntimeError.is(cause)) return cause.detail;68 if (isOpenCodeRuntimeError(cause)) return cause.detail;
70 if (cause instanceof Error && cause.message.trim().length > 0) return cause.message.trim();69 if (cause instanceof Error && cause.message.trim().length > 0) return cause.message.trim();
71 if (cause && typeof cause === "object") {70 if (cause && typeof cause === "object") {
72 // SDK v2 throws { response, request, error? } shapes — extract what's useful71 // SDK v2 throws { response, request, error? } shapes — extract what's useful
@@ -270,7 +269,7 @@ function ensureRuntimeError( · 197 unmodified lines
270 detail: string,269 detail: string,
271 cause: unknown,270 cause: unknown,
272): OpenCodeRuntimeError {271): OpenCodeRuntimeError {
273 return OpenCodeRuntimeError.is(cause)272 return isOpenCodeRuntimeError(cause)
274 ? cause273 ? cause
275 : new OpenCodeRuntimeError({ operation, detail, cause });274 : new OpenCodeRuntimeError({ operation, detail, cause });
276}275}
apps/server/src/textGeneration/OpenCodeTextGeneration.test.ts+80−90
@@ -1,13 +1,13 @@
1import { OpenCodeSettings, ProviderInstanceId, TextGenerationError } from "@t3tools/contracts";1import { OpenCodeSettings, ProviderInstanceId, TextGenerationError } from "@t3tools/contracts";
2import * as NodeServices from "@effect/platform-node/NodeServices";2import * as NodeServices from "@effect/platform-node/NodeServices";
3import { it } from "@effect/vitest";3import { assert, it } from "@effect/vitest";
4import * as Duration from "effect/Duration";4import * as Duration from "effect/Duration";
5import * as Effect from "effect/Effect";5import * as Effect from "effect/Effect";
6import * as Layer from "effect/Layer";6import * as Layer from "effect/Layer";
7import * as Schema from "effect/Schema";7import * as Schema from "effect/Schema";
8import * as TestClock from "effect/testing/TestClock";8import * as TestClock from "effect/testing/TestClock";
9import * as NetService from "@t3tools/shared/Net";9import * as NetService from "@t3tools/shared/Net";
10import { beforeEach, expect } from "vite-plus/test";10import { beforeEach } from "vite-plus/test";
11 11
12import * as ServerConfig from "../config.ts";12import * as ServerConfig from "../config.ts";
13import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts";13import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts";
@@ -39,7 +39,7 @@ const runtimeMock = { · 25 unmodified lines
39 },39 },
40};40};
41 41
42const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntimeShape = {42const OpenCodeRuntimeTestLayer = Layer.mock(OpenCodeRuntime.OpenCodeRuntime)({
43 startOpenCodeServerProcess: ({ binaryPath }) =>43 startOpenCodeServerProcess: ({ binaryPath }) =>
44 Effect.gen(function* () {44 Effect.gen(function* () {
45 const index = runtimeMock.state.startCalls.length + 1;45 const index = runtimeMock.state.startCalls.length + 1;
@@ -57,13 +57,6 @@ const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntimeShape = { · 11 unmodified lines
57 exitCode: Effect.never,57 exitCode: Effect.never,
58 };58 };
59 }),59 }),
60 connectToOpenCodeServer: ({ serverUrl }) =>
61 Effect.succeed({
62 url: serverUrl ?? "http://127.0.0.1:4301",
63 exitCode: null,
64 external: Boolean(serverUrl),
65 }),
66 runOpenCodeCommand: () => Effect.succeed({ stdout: "", stderr: "", code: 0 }),
67 createOpenCodeSdkClient: ({ baseUrl, serverPassword }) =>60 createOpenCodeSdkClient: ({ baseUrl, serverPassword }) =>
68 ({61 ({
69 session: {62 session: {
@@ -99,15 +92,7 @@ const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntimeShape = { · 29 unmodified lines
99 },92 },
100 },93 },
101 }) as unknown as ReturnType<OpenCodeRuntime.OpenCodeRuntimeShape["createOpenCodeSdkClient"]>,94 }) as unknown as ReturnType<OpenCodeRuntime.OpenCodeRuntimeShape["createOpenCodeSdkClient"]>,
102 loadOpenCodeInventory: () =>95});
103 Effect.fail(
104 new OpenCodeRuntime.OpenCodeRuntimeError({
105 operation: "loadOpenCodeInventory",
106 detail: "OpenCodeRuntimeTestDouble.loadOpenCodeInventory not used in this test",
107 cause: null,
108 }),
109 ),
110};
111 96
112const DEFAULT_TEST_MODEL_SELECTION = {97const DEFAULT_TEST_MODEL_SELECTION = {
113 instanceId: ProviderInstanceId.make("opencode"),98 instanceId: ProviderInstanceId.make("opencode"),
@@ -123,10 +108,7 @@ const DEFAULT_COMMIT_MESSAGE_INPUT = { · 9 unmodified lines
123 108
124const OPENCODE_TEXT_GENERATION_IDLE_TTL_MS = 30_000;109const OPENCODE_TEXT_GENERATION_IDLE_TTL_MS = 30_000;
125 110
126const OpenCodeTextGenerationTestLayer = Layer.succeed(111const OpenCodeTextGenerationTestLayer = OpenCodeRuntimeTestLayer.pipe(
127 OpenCodeRuntime.OpenCodeRuntime,
128 OpenCodeRuntimeTestDouble,
129).pipe(
130 Layer.provideMerge(112 Layer.provideMerge(
131 ServerConfig.ServerConfig.layerTest(process.cwd(), {113 ServerConfig.ServerConfig.layerTest(process.cwd(), {
132 prefix: "t3code-opencode-text-generation-test-",114 prefix: "t3code-opencode-text-generation-test-",
@@ -136,10 +118,7 @@ const OpenCodeTextGenerationTestLayer = Layer.succeed( · 3 unmodified lines
136 Layer.provideMerge(NodeServices.layer),118 Layer.provideMerge(NodeServices.layer),
137);119);
138 120
139const OpenCodeTextGenerationExistingServerTestLayer = Layer.succeed(121const OpenCodeTextGenerationExistingServerTestLayer = OpenCodeRuntimeTestLayer.pipe(
140 OpenCodeRuntime.OpenCodeRuntime,
141 OpenCodeRuntimeTestDouble,
142).pipe(
143 Layer.provideMerge(122 Layer.provideMerge(
144 ServerConfig.ServerConfig.layerTest(process.cwd(), {123 ServerConfig.ServerConfig.layerTest(process.cwd(), {
145 prefix: "t3code-opencode-text-generation-existing-server-test-",124 prefix: "t3code-opencode-text-generation-existing-server-test-",
@@ -197,16 +176,16 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { · 51 unmodified lines
197 modelSelection: DEFAULT_TEST_MODEL_SELECTION,176 modelSelection: DEFAULT_TEST_MODEL_SELECTION,
198 });177 });
199 178
200 expect(runtimeMock.state.startCalls).toEqual(["fake-opencode"]);179 assert.deepStrictEqual(runtimeMock.state.startCalls, ["fake-opencode"]);
201 expect(runtimeMock.state.promptUrls).toEqual([180 assert.deepStrictEqual(runtimeMock.state.promptUrls, [
202 "http://127.0.0.1:4301",181 "http://127.0.0.1:4301",
203 "http://127.0.0.1:4301",182 "http://127.0.0.1:4301",
204 ]);183 ]);
205 expect(runtimeMock.state.closeCalls).toEqual([]);184 assert.deepStrictEqual(runtimeMock.state.closeCalls, []);
206 185
207 yield* advanceIdleClock;186 yield* advanceIdleClock;
208 187
209 expect(runtimeMock.state.closeCalls).toEqual(["http://127.0.0.1:4301"]);188 assert.deepStrictEqual(runtimeMock.state.closeCalls, ["http://127.0.0.1:4301"]);
210 }),189 }),
211 ).pipe(Effect.provide(TestClock.layer())),190 ).pipe(Effect.provide(TestClock.layer())),
212 );191 );
@@ -232,12 +211,15 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { · 19 unmodified lines
232 modelSelection: DEFAULT_TEST_MODEL_SELECTION,211 modelSelection: DEFAULT_TEST_MODEL_SELECTION,
233 });212 });
234 213
235 expect(runtimeMock.state.startCalls).toEqual(["fake-opencode", "fake-opencode"]);214 assert.deepStrictEqual(runtimeMock.state.startCalls, [
236 expect(runtimeMock.state.promptUrls).toEqual([215 "fake-opencode",
216 "fake-opencode",
217 ]);
218 assert.deepStrictEqual(runtimeMock.state.promptUrls, [
237 "http://127.0.0.1:4301",219 "http://127.0.0.1:4301",
238 "http://127.0.0.1:4302",220 "http://127.0.0.1:4302",
239 ]);221 ]);
240 expect(runtimeMock.state.closeCalls).toEqual(["http://127.0.0.1:4301"]);222 assert.deepStrictEqual(runtimeMock.state.closeCalls, ["http://127.0.0.1:4301"]);
241 }),223 }),
242 ).pipe(Effect.provide(TestClock.layer())),224 ).pipe(Effect.provide(TestClock.layer())),
243 );225 );
@@ -252,15 +234,16 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { · 8 unmodified lines
252 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)234 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)
253 .pipe(Effect.flip);235 .pipe(Effect.flip);
254 236
255 expect(error).toBeInstanceOf(TextGenerationError);237 assert.instanceOf(error, TextGenerationError);
256 expect(error.message).toContain("OpenCode session.create request failed.");238 assert.include(error.message, "OpenCode session.create request failed.");
257 expect(error.cause).toMatchObject({239 const cause = error.cause;
258 _tag: "OpenCodeTextGenerationSessionRequestError",240 assert.instanceOf(
259 operation: "generateCommitMessage",241 cause,
260 cwd: process.cwd(),242 OpenCodeTextGeneration.OpenCodeTextGenerationSessionRequestError,
261 cause: sdkCause,243 );
262 });244 assert.strictEqual(cause.operation, "generateCommitMessage");
263 expect((error.cause as { cause: unknown }).cause).toBe(sdkCause);245 assert.strictEqual(cause.cwd, process.cwd());
246 assert.strictEqual(cause.cause, sdkCause);
264 }),247 }),
265 ),248 ),
266 );249 );
@@ -274,13 +257,15 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { · 7 unmodified lines
274 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)257 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)
275 .pipe(Effect.flip);258 .pipe(Effect.flip);
276 259
277 expect(error.message).toContain("OpenCode session.create returned no session payload.");260 assert.include(error.message, "OpenCode session.create returned no session payload.");
278 expect(error.cause).toMatchObject({261 const cause = error.cause;
279 _tag: "OpenCodeTextGenerationSessionPayloadError",262 assert.instanceOf(
280 operation: "generateCommitMessage",263 cause,
281 cwd: process.cwd(),264 OpenCodeTextGeneration.OpenCodeTextGenerationSessionPayloadError,
282 });265 );
283 expect(error.cause).not.toHaveProperty("cause");266 assert.strictEqual(cause.operation, "generateCommitMessage");
267 assert.strictEqual(cause.cwd, process.cwd());
268 assert.notProperty(cause, "cause");
284 }),269 }),
285 ),270 ),
286 );271 );
@@ -295,17 +280,18 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { · 8 unmodified lines
295 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)280 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)
296 .pipe(Effect.flip);281 .pipe(Effect.flip);
297 282
298 expect(error.message).toContain("OpenCode session.prompt request failed.");283 assert.include(error.message, "OpenCode session.prompt request failed.");
299 expect(error.cause).toMatchObject({284 const cause = error.cause;
300 _tag: "OpenCodeTextGenerationPromptRequestError",285 assert.instanceOf(
301 operation: "generateCommitMessage",286 cause,
302 cwd: process.cwd(),287 OpenCodeTextGeneration.OpenCodeTextGenerationPromptRequestError,
303 sessionId: "http://127.0.0.1:4301/session",288 );
304 providerId: "openai",289 assert.strictEqual(cause.operation, "generateCommitMessage");
305 modelId: "gpt-5",290 assert.strictEqual(cause.cwd, process.cwd());
306 cause: sdkCause,291 assert.strictEqual(cause.sessionId, "http://127.0.0.1:4301/session");
307 });292 assert.strictEqual(cause.providerId, "openai");
308 expect((error.cause as { cause: unknown }).cause).toBe(sdkCause);293 assert.strictEqual(cause.modelId, "gpt-5");
294 assert.strictEqual(cause.cause, sdkCause);
309 }),295 }),
310 ),296 ),
311 );297 );
@@ -323,18 +309,20 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { · 11 unmodified lines
323 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)309 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)
324 .pipe(Effect.flip);310 .pipe(Effect.flip);
325 311
326 expect(error.message).toContain("OpenCode returned empty output.");312 assert.include(error.message, "OpenCode returned empty output.");
327 expect(error.cause).toMatchObject({313 const cause = error.cause;
328 _tag: "OpenCodeTextGenerationEmptyOutputError",314 assert.instanceOf(
329 operation: "generateCommitMessage",315 cause,
330 cwd: process.cwd(),316 OpenCodeTextGeneration.OpenCodeTextGenerationEmptyOutputError,
331 sessionId: "http://127.0.0.1:4301/session",317 );
332 providerId: "openai",318 assert.strictEqual(cause.operation, "generateCommitMessage");
333 modelId: "gpt-5",319 assert.strictEqual(cause.cwd, process.cwd());
334 responsePartCount: 3,320 assert.strictEqual(cause.sessionId, "http://127.0.0.1:4301/session");
335 textPartCount: 1,321 assert.strictEqual(cause.providerId, "openai");
336 });322 assert.strictEqual(cause.modelId, "gpt-5");
337 expect(error.cause).not.toHaveProperty("cause");323 assert.strictEqual(cause.responsePartCount, 3);
324 assert.strictEqual(cause.textPartCount, 1);
325 assert.notProperty(cause, "cause");
338 }),326 }),
339 ),327 ),
340 );328 );
@@ -361,7 +349,7 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { · 20 unmodified lines
361 modelSelection: DEFAULT_TEST_MODEL_SELECTION,349 modelSelection: DEFAULT_TEST_MODEL_SELECTION,
362 });350 });
363 351
364 expect(result).toEqual({352 assert.deepStrictEqual(result, {
365 subject: "Tighten OpenCode parsing",353 subject: "Tighten OpenCode parsing",
366 body: "Handle JSON text output locally.",354 body: "Handle JSON text output locally.",
367 });355 });
@@ -390,18 +378,20 @@ it.layer(OpenCodeTextGenerationTestLayer)("OpenCodeTextGeneration", (it) => { · 22 unmodified lines
390 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)378 .generateCommitMessage(DEFAULT_COMMIT_MESSAGE_INPUT)
391 .pipe(Effect.flip);379 .pipe(Effect.flip);
392 380
393 expect(error.message).toContain("Model did not produce structured output");381 assert.include(error.message, "Model did not produce structured output");
394 expect(error.cause).toMatchObject({382 const cause = error.cause;
395 _tag: "OpenCodeTextGenerationPromptResponseError",383 assert.instanceOf(
396 operation: "generateCommitMessage",384 cause,
397 cwd: process.cwd(),385 OpenCodeTextGeneration.OpenCodeTextGenerationPromptResponseError,
398 sessionId: "http://127.0.0.1:4301/session",386 );
399 providerId: "openai",387 assert.strictEqual(cause.operation, "generateCommitMessage");
400 modelId: "gpt-5",388 assert.strictEqual(cause.cwd, process.cwd());
401 providerErrorName: "StructuredOutputError",389 assert.strictEqual(cause.sessionId, "http://127.0.0.1:4301/session");
402 providerMessage: "Model did not produce structured output",390 assert.strictEqual(cause.providerId, "openai");
403 });391 assert.strictEqual(cause.modelId, "gpt-5");
404 expect(error.cause).not.toHaveProperty("cause");392 assert.strictEqual(cause.providerErrorName, "StructuredOutputError");
393 assert.strictEqual(cause.providerMessage, "Model did not produce structured output");
394 assert.notProperty(cause, "cause");
405 }),395 }),
406 ),396 ),
407 );397 );
@@ -428,19 +418,19 @@ it.layer(OpenCodeTextGenerationExistingServerTestLayer)( · 20 unmodified lines
428 modelSelection: DEFAULT_TEST_MODEL_SELECTION,418 modelSelection: DEFAULT_TEST_MODEL_SELECTION,
429 });419 });
430 420
431 expect(runtimeMock.state.startCalls).toEqual([]);421 assert.deepStrictEqual(runtimeMock.state.startCalls, []);
432 expect(runtimeMock.state.promptUrls).toEqual([422 assert.deepStrictEqual(runtimeMock.state.promptUrls, [
433 "http://127.0.0.1:9999",423 "http://127.0.0.1:9999",
434 "http://127.0.0.1:9999",424 "http://127.0.0.1:9999",
435 ]);425 ]);
436 expect(runtimeMock.state.authHeaders).toEqual([426 assert.deepStrictEqual(runtimeMock.state.authHeaders, [
437 `Basic ${btoa("opencode:secret-password")}`,427 `Basic ${btoa("opencode:secret-password")}`,
438 `Basic ${btoa("opencode:secret-password")}`,428 `Basic ${btoa("opencode:secret-password")}`,
439 ]);429 ]);
440 430
441 yield* advanceIdleClock;431 yield* advanceIdleClock;
442 432
443 expect(runtimeMock.state.closeCalls).toEqual([]);433 assert.deepStrictEqual(runtimeMock.state.closeCalls, []);
444 }),434 }),
445 ).pipe(Effect.provide(TestClock.layer())),435 ).pipe(Effect.provide(TestClock.layer())),
446 );436 );
apps/server/src/textGeneration/OpenCodeTextGeneration.ts+32−28
@@ -389,19 +389,21 @@ export const makeOpenCodeTextGeneration = Effect.fn("makeOpenCodeTextGeneration" · 388 unmodified lines
389 ? { serverPassword: openCodeSettings.serverPassword }389 ? { serverPassword: openCodeSettings.serverPassword }
390 : {}),390 : {}),
391 });391 });
392 const session = yield* Effect.tryPromise({392 const session = yield* OpenCodeRuntime.runOpenCodeSdk("session.create", () =>
393 try: () =>393 client.session.create({
394 client.session.create({394 title: `T3 Code ${input.operation}`,
395 title: `T3 Code ${input.operation}`,395 permission: [{ permission: "*", pattern: "*", action: "deny" }],
396 permission: [{ permission: "*", pattern: "*", action: "deny" }],396 }),
397 }),397 ).pipe(
398 catch: (cause) =>398 Effect.mapError(
399 new OpenCodeTextGenerationSessionRequestError({399 (cause) =>
400 operation: input.operation,400 new OpenCodeTextGenerationSessionRequestError({
401 cwd: input.cwd,401 operation: input.operation,
402 cause,402 cwd: input.cwd,
403 }),403 cause: cause.cause,
404 });404 }),
405 ),
406 );
405 if (!session.data) {407 if (!session.data) {
406 return yield* new OpenCodeTextGenerationSessionPayloadError({408 return yield* new OpenCodeTextGenerationSessionPayloadError({
407 operation: input.operation,409 operation: input.operation,
@@ -418,21 +420,23 @@ export const makeOpenCodeTextGeneration = Effect.fn("makeOpenCodeTextGeneration" · 10 unmodified lines
418 modelId: parsedModel.modelID,420 modelId: parsedModel.modelID,
419 };421 };
420 422
421 const result = yield* Effect.tryPromise({423 const result = yield* OpenCodeRuntime.runOpenCodeSdk("session.prompt", () =>
422 try: () =>424 client.session.prompt({
423 client.session.prompt({425 sessionID: session.data.id,
424 sessionID: session.data.id,426 model: parsedModel,
425 model: parsedModel,427 ...(selectedAgent ? { agent: selectedAgent } : {}),
426 ...(selectedAgent ? { agent: selectedAgent } : {}),428 ...(selectedVariant ? { variant: selectedVariant } : {}),
427 ...(selectedVariant ? { variant: selectedVariant } : {}),429 parts: [{ type: "text", text: input.prompt }, ...fileParts],
428 parts: [{ type: "text", text: input.prompt }, ...fileParts],430 }),
429 }),431 ).pipe(
430 catch: (cause) =>432 Effect.mapError(
431 new OpenCodeTextGenerationPromptRequestError({433 (cause) =>
432 ...promptContext,434 new OpenCodeTextGenerationPromptRequestError({
433 cause,435 ...promptContext,
434 }),436 cause: cause.cause,
435 });437 }),
438 ),
439 );
436 const promptFailure = getOpenCodePromptFailure(result.data?.info?.error);440 const promptFailure = getOpenCodePromptFailure(result.data?.info?.error);
437 if (promptFailure) {441 if (promptFailure) {
438 return yield* new OpenCodeTextGenerationPromptResponseError({442 return yield* new OpenCodeTextGenerationPromptResponseError({