authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-19 21:11:33-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-19 21:11:33-07:00
log742dd7af0dcb54c32774f87fd354105726c7a9db
tree45d2a18f157ea43645a48bf8aa3bda0cf56647d7
parent5c1ac922574340be4560eacf928237929497d11d
signature Signed by PGP key B5690EEEBB952194

[codex] Standardize Effect protocol adapter services (#3201)

Co-authored-by: codex <codex@users.noreply.github.com>

14 files changed, 661 insertions(+), 544 deletions(-)

apps/server/src/provider/Layers/CodexProvider.ts+3-2
......@@ -7,7 +7,8 @@ import * as Result from "effect/Result";
77import * as Schema from "effect/Schema";
88import * as Scope from "effect/Scope";
99import * as Types from "effect/Types";
10import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
10import * as ChildProcess from "effect/unstable/process/ChildProcess";
11import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
1112import * as CodexClient from "effect-codex-app-server/client";
1213import * as CodexSchema from "effect-codex-app-server/schema";
1314import * as CodexErrors from "effect-codex-app-server/errors";
......@@ -253,7 +254,7 @@ function parseCodexSkillsListResponse(
253254}
254255
255256const requestAllCodexModels = Effect.fn("requestAllCodexModels")(function* (
256 client: CodexClient.CodexAppServerClientShape,
257 client: CodexClient.CodexAppServerClient["Service"],
257258) {
258259 const models: ServerProviderModel[] = [];
259260 let cursor: string | null | undefined = undefined;
apps/server/src/provider/Layers/CursorAdapter.ts+4-4
......@@ -36,7 +36,7 @@ import * as Scope from "effect/Scope";
3636import * as Semaphore from "effect/Semaphore";
3737import * as Stream from "effect/Stream";
3838import * as SynchronizedRef from "effect/SynchronizedRef";
39import { ChildProcessSpawner } from "effect/unstable/process";
39import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
4040import * as EffectAcpErrors from "effect-acp/errors";
4141import type * as EffectAcpSchema from "effect-acp/schema";
4242
......@@ -50,7 +50,7 @@ import {
5050 ProviderAdapterValidationError,
5151} from "../Errors.ts";
5252import { acpPermissionOutcome, mapAcpToAdapterError } from "../acp/AcpAdapterSupport.ts";
53import { type AcpSessionRuntimeShape } from "../acp/AcpSessionRuntime.ts";
53import type * as AcpSessionRuntime from "../acp/AcpSessionRuntime.ts";
5454import {
5555 makeAcpAssistantItemEvent,
5656 makeAcpContentDeltaEvent,
......@@ -126,7 +126,7 @@ interface CursorSessionContext {
126126 readonly threadId: ThreadId;
127127 session: ProviderSession;
128128 readonly scope: Scope.Closeable;
129 readonly acp: AcpSessionRuntimeShape;
129 readonly acp: AcpSessionRuntime.AcpSessionRuntime["Service"];
130130 notificationFiber: Fiber.Fiber<void, never> | undefined;
131131 readonly pendingApprovals: Map<ApprovalRequestId, PendingApproval>;
132132 readonly pendingUserInputs: Map<ApprovalRequestId, PendingUserInput>;
......@@ -246,7 +246,7 @@ function resolveRequestedModeId(input: {
246246}
247247
248248function applyRequestedSessionConfiguration<E>(input: {
249 readonly runtime: AcpSessionRuntimeShape;
249 readonly runtime: AcpSessionRuntime.AcpSessionRuntime["Service"];
250250 readonly runtimeMode: RuntimeMode;
251251 readonly interactionMode: ProviderInteractionMode | undefined;
252252 readonly modelSelection:
apps/server/src/provider/Layers/CursorProvider.ts+7-4
......@@ -21,7 +21,8 @@ import * as Path from "effect/Path";
2121import * as Result from "effect/Result";
2222import * as Schema from "effect/Schema";
2323import { HttpClient } from "effect/unstable/http";
24import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
24import * as ChildProcess from "effect/unstable/process/ChildProcess";
25import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
2526import {
2627 createModelCapabilities,
2728 getProviderOptionBooleanSelectionValue,
......@@ -43,7 +44,7 @@ import {
4344 enrichProviderSnapshotWithVersionAdvisory,
4445 type ProviderMaintenanceCapabilities,
4546} from "../providerMaintenance.ts";
46import { AcpSessionRuntime } from "../acp/AcpSessionRuntime.ts";
47import * as AcpSessionRuntime from "../acp/AcpSessionRuntime.ts";
4748import { CursorListAvailableModelsResponse } from "../acp/CursorAcpExtension.ts";
4849
4950const decodeCursorListAvailableModelsResponse = Schema.decodeUnknownEffect(
......@@ -416,12 +417,14 @@ const makeCursorAcpProbeRuntime = (
416417 clientCapabilities: CURSOR_PARAMETERIZED_MODEL_PICKER_CAPABILITIES,
417418 }).pipe(Layer.provide(Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner))),
418419 );
419 return yield* Effect.service(AcpSessionRuntime).pipe(Effect.provide(acpContext));
420 return yield* Effect.service(AcpSessionRuntime.AcpSessionRuntime).pipe(
421 Effect.provide(acpContext),
422 );
420423 });
421424
422425const withCursorAcpProbeRuntime = <A, E, R>(
423426 cursorSettings: CursorSettings,
424 useRuntime: (acp: AcpSessionRuntime["Service"]) => Effect.Effect<A, E, R>,
427 useRuntime: (acp: AcpSessionRuntime.AcpSessionRuntime["Service"]) => Effect.Effect<A, E, R>,
425428 environment?: NodeJS.ProcessEnv,
426429) =>
427430 makeCursorAcpProbeRuntime(cursorSettings, environment).pipe(
apps/server/src/provider/Layers/GrokAdapter.ts+3-3
......@@ -27,7 +27,7 @@ import * as Scope from "effect/Scope";
2727import * as Semaphore from "effect/Semaphore";
2828import * as Stream from "effect/Stream";
2929import * as SynchronizedRef from "effect/SynchronizedRef";
30import { ChildProcessSpawner } from "effect/unstable/process";
30import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
3131import * as EffectAcpErrors from "effect-acp/errors";
3232import type * as EffectAcpSchema from "effect-acp/schema";
3333
......@@ -41,7 +41,7 @@ import {
4141 ProviderAdapterValidationError,
4242} from "../Errors.ts";
4343import { mapAcpToAdapterError } from "../acp/AcpAdapterSupport.ts";
44import { type AcpSessionRuntimeShape } from "../acp/AcpSessionRuntime.ts";
44import type * as AcpSessionRuntime from "../acp/AcpSessionRuntime.ts";
4545import {
4646 makeAcpAssistantItemEvent,
4747 makeAcpContentDeltaEvent,
......@@ -101,7 +101,7 @@ interface GrokSessionContext {
101101 readonly acpSessionId: string;
102102 session: ProviderSession;
103103 readonly scope: Scope.Closeable;
104 readonly acp: AcpSessionRuntimeShape;
104 readonly acp: AcpSessionRuntime.AcpSessionRuntime["Service"];
105105 notificationFiber: Fiber.Fiber<void, never> | undefined;
106106 readonly pendingApprovals: Map<ApprovalRequestId, PendingApproval>;
107107 readonly pendingUserInputs: Map<ApprovalRequestId, PendingUserInput>;
apps/server/src/provider/acp/AcpJsonRpcConnection.test.ts+12-12
......@@ -10,7 +10,7 @@ import * as Effect from "effect/Effect";
1010import * as Stream from "effect/Stream";
1111import { describe, expect } from "vite-plus/test";
1212
13import { AcpSessionRuntime, type AcpSessionRequestLogEvent } from "./AcpSessionRuntime.ts";
13import * as AcpSessionRuntime from "./AcpSessionRuntime.ts";
1414import type * as EffectAcpProtocol from "effect-acp/protocol";
1515
1616const __dirname = path.dirname(fileURLToPath(import.meta.url));
......@@ -20,9 +20,9 @@ const mockAgentArgs = [mockAgentPath];
2020
2121describe("AcpSessionRuntime", () => {
2222 it.effect("merges custom initialize client capabilities into the ACP handshake", () => {
23 const requestEvents: Array<AcpSessionRequestLogEvent> = [];
23 const requestEvents: Array<AcpSessionRuntime.AcpSessionRequestLogEvent> = [];
2424 return Effect.gen(function* () {
25 const runtime = yield* AcpSessionRuntime;
25 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
2626 yield* runtime.start();
2727
2828 const initializeStarted = requestEvents.find(
......@@ -64,7 +64,7 @@ describe("AcpSessionRuntime", () => {
6464
6565 it.effect("starts a session, prompts, and emits normalized events against the mock agent", () =>
6666 Effect.gen(function* () {
67 const runtime = yield* AcpSessionRuntime;
67 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
6868 const started = yield* runtime.start();
6969
7070 expect(started.initializeResult).toMatchObject({ protocolVersion: 1 });
......@@ -115,7 +115,7 @@ describe("AcpSessionRuntime", () => {
115115
116116 it.effect("segments assistant text around ACP tool calls", () =>
117117 Effect.gen(function* () {
118 const runtime = yield* AcpSessionRuntime;
118 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
119119 yield* runtime.start();
120120
121121 const promptResult = yield* runtime.prompt({
......@@ -176,7 +176,7 @@ describe("AcpSessionRuntime", () => {
176176
177177 it.effect("suppresses generic placeholder tool updates until completion", () =>
178178 Effect.gen(function* () {
179 const runtime = yield* AcpSessionRuntime;
179 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
180180 yield* runtime.start();
181181
182182 const promptResult = yield* runtime.prompt({
......@@ -213,9 +213,9 @@ describe("AcpSessionRuntime", () => {
213213 );
214214
215215 it.effect("logs ACP requests from the shared runtime", () => {
216 const requestEvents: Array<AcpSessionRequestLogEvent> = [];
216 const requestEvents: Array<AcpSessionRuntime.AcpSessionRequestLogEvent> = [];
217217 return Effect.gen(function* () {
218 const runtime = yield* AcpSessionRuntime;
218 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
219219 yield* runtime.start();
220220
221221 yield* runtime.setModel("composer-2");
......@@ -265,9 +265,9 @@ describe("AcpSessionRuntime", () => {
265265 });
266266
267267 it.effect("skips no-op session config writes when the requested value is already active", () => {
268 const requestEvents: Array<AcpSessionRequestLogEvent> = [];
268 const requestEvents: Array<AcpSessionRuntime.AcpSessionRequestLogEvent> = [];
269269 return Effect.gen(function* () {
270 const runtime = yield* AcpSessionRuntime;
270 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
271271 yield* runtime.start();
272272
273273 yield* runtime.setConfigOption("model", "default");
......@@ -302,7 +302,7 @@ describe("AcpSessionRuntime", () => {
302302 it.effect("emits low-level ACP protocol logs for raw and decoded messages", () => {
303303 const protocolEvents: Array<EffectAcpProtocol.AcpProtocolLogEvent> = [];
304304 return Effect.gen(function* () {
305 const runtime = yield* AcpSessionRuntime;
305 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
306306 yield* runtime.start();
307307
308308 yield* runtime.prompt({
......@@ -350,7 +350,7 @@ describe("AcpSessionRuntime", () => {
350350 const tempDir = mkdtempSync(path.join(os.tmpdir(), "acp-runtime-"));
351351 const requestLogPath = path.join(tempDir, "requests.ndjson");
352352 return Effect.gen(function* () {
353 const runtime = yield* AcpSessionRuntime;
353 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
354354 yield* runtime.start();
355355
356356 const error = yield* runtime.setModel("composer-2[fast=false]").pipe(Effect.flip);
apps/server/src/provider/acp/AcpNativeLogging.ts+5-5
......@@ -6,9 +6,9 @@ import * as Effect from "effect/Effect";
66import type * as EffectAcpProtocol from "effect-acp/protocol";
77
88import type { EventNdjsonLogger } from "../Layers/EventNdjsonLogger.ts";
9import type { AcpSessionRequestLogEvent, AcpSessionRuntimeOptions } from "./AcpSessionRuntime.ts";
9import type * as AcpSessionRuntime from "./AcpSessionRuntime.ts";
1010
11function formatRequestLogPayload(event: AcpSessionRequestLogEvent) {
11function formatRequestLogPayload(event: AcpSessionRuntime.AcpSessionRequestLogEvent) {
1212 return {
1313 method: event.method,
1414 status: event.status,
......@@ -24,7 +24,7 @@ export const makeAcpNativeLoggerFactory = Effect.fn("makeAcpNativeLoggerFactory"
2424 readonly nativeEventLogger: EventNdjsonLogger | undefined;
2525 readonly provider: ProviderDriverKind;
2626 readonly threadId: ThreadId;
27 }): Pick<AcpSessionRuntimeOptions, "requestLogger" | "protocolLogging"> => {
27 }): Pick<AcpSessionRuntime.AcpSessionRuntimeOptions, "requestLogger" | "protocolLogging"> => {
2828 const writeNativeAcpLog = (logInput: {
2929 readonly kind: "request" | "protocol";
3030 readonly payload: unknown;
......@@ -57,7 +57,7 @@ export const makeAcpNativeLoggerFactory = Effect.fn("makeAcpNativeLoggerFactory"
5757 );
5858
5959 return {
60 requestLogger: (event: AcpSessionRequestLogEvent) =>
60 requestLogger: (event: AcpSessionRuntime.AcpSessionRequestLogEvent) =>
6161 writeNativeAcpLog({
6262 kind: "request",
6363 payload: formatRequestLogPayload(event),
......@@ -72,7 +72,7 @@ export const makeAcpNativeLoggerFactory = Effect.fn("makeAcpNativeLoggerFactory"
7272 kind: "protocol",
7373 payload: event,
7474 }),
75 } satisfies NonNullable<AcpSessionRuntimeOptions["protocolLogging"]>,
75 } satisfies NonNullable<AcpSessionRuntime.AcpSessionRuntimeOptions["protocolLogging"]>,
7676 }
7777 : {}),
7878 };
apps/server/src/provider/acp/AcpSessionRuntime.ts+161-63
......@@ -1,4 +1,5 @@
11import * as Cause from "effect/Cause";
2import * as Context from "effect/Context";
23import * as Deferred from "effect/Deferred";
34import * as Effect from "effect/Effect";
45import * as Exit from "effect/Exit";
......@@ -6,9 +7,9 @@ import * as Layer from "effect/Layer";
67import * as Queue from "effect/Queue";
78import * as Ref from "effect/Ref";
89import * as Scope from "effect/Scope";
9import * as Context from "effect/Context";
1010import * as Stream from "effect/Stream";
11import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
11import * as ChildProcess from "effect/unstable/process/ChildProcess";
12import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
1213import * as EffectAcpClient from "effect-acp/client";
1314import * as EffectAcpErrors from "effect-acp/errors";
1415import type * as EffectAcpSchema from "effect-acp/schema";
......@@ -75,50 +76,153 @@ export interface AcpSessionRuntimeStartResult {
7576 readonly modelConfigId: string | undefined;
7677}
7778
78export interface AcpSessionRuntimeShape {
79 readonly handleRequestPermission: EffectAcpClient.AcpClientShape["handleRequestPermission"];
80 readonly handleElicitation: EffectAcpClient.AcpClientShape["handleElicitation"];
81 readonly handleReadTextFile: EffectAcpClient.AcpClientShape["handleReadTextFile"];
82 readonly handleWriteTextFile: EffectAcpClient.AcpClientShape["handleWriteTextFile"];
83 readonly handleCreateTerminal: EffectAcpClient.AcpClientShape["handleCreateTerminal"];
84 readonly handleTerminalOutput: EffectAcpClient.AcpClientShape["handleTerminalOutput"];
85 readonly handleTerminalWaitForExit: EffectAcpClient.AcpClientShape["handleTerminalWaitForExit"];
86 readonly handleTerminalKill: EffectAcpClient.AcpClientShape["handleTerminalKill"];
87 readonly handleTerminalRelease: EffectAcpClient.AcpClientShape["handleTerminalRelease"];
88 readonly handleSessionUpdate: EffectAcpClient.AcpClientShape["handleSessionUpdate"];
89 readonly handleElicitationComplete: EffectAcpClient.AcpClientShape["handleElicitationComplete"];
90 readonly handleUnknownExtRequest: EffectAcpClient.AcpClientShape["handleUnknownExtRequest"];
91 readonly handleUnknownExtNotification: EffectAcpClient.AcpClientShape["handleUnknownExtNotification"];
92 readonly handleExtRequest: EffectAcpClient.AcpClientShape["handleExtRequest"];
93 readonly handleExtNotification: EffectAcpClient.AcpClientShape["handleExtNotification"];
94 readonly start: () => Effect.Effect<AcpSessionRuntimeStartResult, EffectAcpErrors.AcpError>;
95 readonly getEvents: () => Stream.Stream<AcpParsedSessionEvent, never>;
96 readonly getModeState: Effect.Effect<AcpSessionModeState | undefined>;
97 readonly getConfigOptions: Effect.Effect<ReadonlyArray<EffectAcpSchema.SessionConfigOption>>;
98 readonly prompt: (
99 payload: Omit<EffectAcpSchema.PromptRequest, "sessionId">,
100 ) => Effect.Effect<EffectAcpSchema.PromptResponse, EffectAcpErrors.AcpError>;
101 readonly cancel: Effect.Effect<void, EffectAcpErrors.AcpError>;
102 readonly setMode: (
103 modeId: string,
104 ) => Effect.Effect<EffectAcpSchema.SetSessionModeResponse, EffectAcpErrors.AcpError>;
105 readonly setConfigOption: (
106 configId: string,
107 value: string | boolean,
108 ) => Effect.Effect<EffectAcpSchema.SetSessionConfigOptionResponse, EffectAcpErrors.AcpError>;
109 readonly setModel: (model: string) => Effect.Effect<void, EffectAcpErrors.AcpError>;
110 readonly setSessionModel: (
111 modelId: string,
112 ) => Effect.Effect<EffectAcpSchema.SetSessionModelResponse, EffectAcpErrors.AcpError>;
113 readonly request: (
114 method: string,
115 payload: unknown,
116 ) => Effect.Effect<unknown, EffectAcpErrors.AcpError>;
117 readonly notify: (
118 method: string,
119 payload: unknown,
120 ) => Effect.Effect<void, EffectAcpErrors.AcpError>;
121}
79export class AcpSessionRuntime extends Context.Service<
80 AcpSessionRuntime,
81 {
82 /**
83 * Registers a handler for `session/request_permission`.
84 * @see https://agentclientprotocol.com/protocol/schema#session/request_permission
85 */
86 readonly handleRequestPermission: EffectAcpClient.AcpClient["Service"]["handleRequestPermission"];
87 /**
88 * Registers a handler for `session/elicitation`.
89 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation
90 */
91 readonly handleElicitation: EffectAcpClient.AcpClient["Service"]["handleElicitation"];
92 /**
93 * Registers a handler for `fs/read_text_file`.
94 * @see https://agentclientprotocol.com/protocol/schema#fs/read_text_file
95 */
96 readonly handleReadTextFile: EffectAcpClient.AcpClient["Service"]["handleReadTextFile"];
97 /**
98 * Registers a handler for `fs/write_text_file`.
99 * @see https://agentclientprotocol.com/protocol/schema#fs/write_text_file
100 */
101 readonly handleWriteTextFile: EffectAcpClient.AcpClient["Service"]["handleWriteTextFile"];
102 /**
103 * Registers a handler for `terminal/create`.
104 * @see https://agentclientprotocol.com/protocol/schema#terminal/create
105 */
106 readonly handleCreateTerminal: EffectAcpClient.AcpClient["Service"]["handleCreateTerminal"];
107 /**
108 * Registers a handler for `terminal/output`.
109 * @see https://agentclientprotocol.com/protocol/schema#terminal/output
110 */
111 readonly handleTerminalOutput: EffectAcpClient.AcpClient["Service"]["handleTerminalOutput"];
112 /**
113 * Registers a handler for `terminal/wait_for_exit`.
114 * @see https://agentclientprotocol.com/protocol/schema#terminal/wait_for_exit
115 */
116 readonly handleTerminalWaitForExit: EffectAcpClient.AcpClient["Service"]["handleTerminalWaitForExit"];
117 /**
118 * Registers a handler for `terminal/kill`.
119 * @see https://agentclientprotocol.com/protocol/schema#terminal/kill
120 */
121 readonly handleTerminalKill: EffectAcpClient.AcpClient["Service"]["handleTerminalKill"];
122 /**
123 * Registers a handler for `terminal/release`.
124 * @see https://agentclientprotocol.com/protocol/schema#terminal/release
125 */
126 readonly handleTerminalRelease: EffectAcpClient.AcpClient["Service"]["handleTerminalRelease"];
127 /**
128 * Registers a handler for `session/update`.
129 * @see https://agentclientprotocol.com/protocol/schema#session/update
130 */
131 readonly handleSessionUpdate: EffectAcpClient.AcpClient["Service"]["handleSessionUpdate"];
132 /**
133 * Registers a handler for `session/elicitation/complete`.
134 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation/complete
135 */
136 readonly handleElicitationComplete: EffectAcpClient.AcpClient["Service"]["handleElicitationComplete"];
137 /**
138 * Registers a fallback extension request handler.
139 * @see https://agentclientprotocol.com/protocol/extensibility
140 */
141 readonly handleUnknownExtRequest: EffectAcpClient.AcpClient["Service"]["handleUnknownExtRequest"];
142 /**
143 * Registers a fallback extension notification handler.
144 * @see https://agentclientprotocol.com/protocol/extensibility
145 */
146 readonly handleUnknownExtNotification: EffectAcpClient.AcpClient["Service"]["handleUnknownExtNotification"];
147 /**
148 * Registers a typed extension request handler.
149 * @see https://agentclientprotocol.com/protocol/extensibility
150 */
151 readonly handleExtRequest: EffectAcpClient.AcpClient["Service"]["handleExtRequest"];
152 /**
153 * Registers a typed extension notification handler.
154 * @see https://agentclientprotocol.com/protocol/extensibility
155 */
156 readonly handleExtNotification: EffectAcpClient.AcpClient["Service"]["handleExtNotification"];
157 /**
158 * Initializes the ACP connection, authenticates, and loads, resumes, or creates the session.
159 * Concurrent calls share the same in-flight startup and a failed startup may be retried.
160 */
161 readonly start: () => Effect.Effect<AcpSessionRuntimeStartResult, EffectAcpErrors.AcpError>;
162 /** Stream of parsed ACP session events emitted after startup. */
163 readonly getEvents: () => Stream.Stream<AcpParsedSessionEvent, never>;
164 /** Latest mode state observed from session setup and `session/update` notifications. */
165 readonly getModeState: Effect.Effect<AcpSessionModeState | undefined>;
166 /** Latest configuration options observed from session setup and configuration writes. */
167 readonly getConfigOptions: Effect.Effect<ReadonlyArray<EffectAcpSchema.SessionConfigOption>>;
168 /**
169 * Sends a prompt turn to the active session.
170 * @see https://agentclientprotocol.com/protocol/schema#session/prompt
171 */
172 readonly prompt: (
173 payload: Omit<EffectAcpSchema.PromptRequest, "sessionId">,
174 ) => Effect.Effect<EffectAcpSchema.PromptResponse, EffectAcpErrors.AcpError>;
175 /**
176 * Sends a real ACP `session/cancel` notification for the active session.
177 * @see https://agentclientprotocol.com/protocol/schema#session/cancel
178 */
179 readonly cancel: Effect.Effect<void, EffectAcpErrors.AcpError>;
180 /**
181 * Selects the active mode through the negotiated `mode` configuration option.
182 * This is a no-op when the requested mode is already active.
183 * @see https://agentclientprotocol.com/protocol/schema#session/set_config_option
184 */
185 readonly setMode: (
186 modeId: string,
187 ) => Effect.Effect<EffectAcpSchema.SetSessionModeResponse, EffectAcpErrors.AcpError>;
188 /**
189 * Updates a session configuration option and the runtime configuration snapshot.
190 * @see https://agentclientprotocol.com/protocol/schema#session/set_config_option
191 */
192 readonly setConfigOption: (
193 configId: string,
194 value: string | boolean,
195 ) => Effect.Effect<EffectAcpSchema.SetSessionConfigOptionResponse, EffectAcpErrors.AcpError>;
196 /**
197 * Selects the base model through the negotiated model configuration option.
198 * @see https://agentclientprotocol.com/protocol/schema#session/set_config_option
199 */
200 readonly setModel: (model: string) => Effect.Effect<void, EffectAcpErrors.AcpError>;
201 /**
202 * Selects the active model through the unstable ACP `session/set_model` capability.
203 * @see https://agentclientprotocol.com/protocol/schema#session/set_model
204 */
205 readonly setSessionModel: (
206 modelId: string,
207 ) => Effect.Effect<EffectAcpSchema.SetSessionModelResponse, EffectAcpErrors.AcpError>;
208 /**
209 * Sends a generic ACP extension request and records it through the request logger.
210 * @see https://agentclientprotocol.com/protocol/extensibility
211 */
212 readonly request: (
213 method: string,
214 payload: unknown,
215 ) => Effect.Effect<unknown, EffectAcpErrors.AcpError>;
216 /**
217 * Sends a generic ACP extension notification.
218 * @see https://agentclientprotocol.com/protocol/extensibility
219 */
220 readonly notify: (
221 method: string,
222 payload: unknown,
223 ) => Effect.Effect<void, EffectAcpErrors.AcpError>;
224 }
225>()("t3/provider/acp/AcpSessionRuntime") {}
122226
123227interface AcpStartedState extends AcpSessionRuntimeStartResult {}
124228
......@@ -140,24 +244,10 @@ interface EnsureActiveAssistantSegmentResult {
140244 readonly startedEvent?: Extract<AcpParsedSessionEvent, { readonly _tag: "AssistantItemStarted" }>;
141245}
142246
143export class AcpSessionRuntime extends Context.Service<AcpSessionRuntime, AcpSessionRuntimeShape>()(
144 "t3/provider/acp/AcpSessionRuntime",
145) {
146 static layer(
147 options: AcpSessionRuntimeOptions,
148 ): Layer.Layer<
149 AcpSessionRuntime,
150 EffectAcpErrors.AcpError,
151 ChildProcessSpawner.ChildProcessSpawner
152 > {
153 return Layer.effect(AcpSessionRuntime, makeAcpSessionRuntime(options));
154 }
155}
156
157const makeAcpSessionRuntime = (
247export const make = (
158248 options: AcpSessionRuntimeOptions,
159249): Effect.Effect<
160 AcpSessionRuntimeShape,
250 AcpSessionRuntime["Service"],
161251 EffectAcpErrors.AcpError,
162252 ChildProcessSpawner.ChildProcessSpawner | Scope.Scope
163253> =>
......@@ -573,9 +663,17 @@ const makeAcpSessionRuntime = (
573663 request: (method, payload) =>
574664 runLoggedRequest(method, payload, acp.raw.request(method, payload)),
575665 notify: acp.raw.notify,
576 } satisfies AcpSessionRuntimeShape;
666 } satisfies AcpSessionRuntime["Service"];
577667 });
578668
669export const layer = (
670 options: AcpSessionRuntimeOptions,
671): Layer.Layer<
672 AcpSessionRuntime,
673 EffectAcpErrors.AcpError,
674 ChildProcessSpawner.ChildProcessSpawner
675> => Layer.effect(AcpSessionRuntime, make(options));
676
579677function sessionConfigOptionsFromSetup(
580678 response:
581679 | {
apps/server/src/provider/acp/CursorAcpCliProbe.test.ts+4-4
......@@ -9,12 +9,12 @@ import * as Effect from "effect/Effect";
99import { describe, expect } from "vite-plus/test";
1010import type * as EffectAcpSchema from "effect-acp/schema";
1111
12import { AcpSessionRuntime } from "./AcpSessionRuntime.ts";
12import * as AcpSessionRuntime from "./AcpSessionRuntime.ts";
1313
1414describe.runIf(process.env.T3_CURSOR_ACP_PROBE === "1")("Cursor ACP CLI probe", () => {
1515 it.effect("initialize and authenticate against real agent acp", () =>
1616 Effect.gen(function* () {
17 const runtime = yield* AcpSessionRuntime;
17 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
1818 const started = yield* runtime.start();
1919 expect(started.initializeResult).toBeDefined();
2020 }).pipe(
......@@ -42,7 +42,7 @@ describe.runIf(process.env.T3_CURSOR_ACP_PROBE === "1")("Cursor ACP CLI probe",
4242
4343 it.effect("session/new returns configOptions with a model selector", () =>
4444 Effect.gen(function* () {
45 const runtime = yield* AcpSessionRuntime;
45 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
4646 const started = yield* runtime.start();
4747 const result = started.sessionSetupResult;
4848 // @effect-diagnostics-next-line preferSchemaOverJson:off
......@@ -97,7 +97,7 @@ describe.runIf(process.env.T3_CURSOR_ACP_PROBE === "1")("Cursor ACP CLI probe",
9797
9898 it.effect("session/set_config_option switches the model in-session", () =>
9999 Effect.gen(function* () {
100 const runtime = yield* AcpSessionRuntime;
100 const runtime = yield* AcpSessionRuntime.AcpSessionRuntime;
101101 const started = yield* runtime.start();
102102 const newResult = started.sessionSetupResult;
103103
apps/server/src/provider/acp/CursorAcpSupport.ts+13-12
......@@ -2,7 +2,7 @@ import { type CursorSettings, type ProviderOptionSelection } from "@t3tools/cont
22import * as Effect from "effect/Effect";
33import * as Layer from "effect/Layer";
44import * as Scope from "effect/Scope";
5import { ChildProcessSpawner } from "effect/unstable/process";
5import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
66import type * as EffectAcpErrors from "effect-acp/errors";
77
88import {
......@@ -10,17 +10,12 @@ import {
1010 resolveCursorAcpBaseModelId,
1111 resolveCursorAcpConfigUpdates,
1212} from "../Layers/CursorProvider.ts";
13import {
14 AcpSessionRuntime,
15 type AcpSessionRuntimeOptions,
16 type AcpSessionRuntimeShape,
17 type AcpSpawnInput,
18} from "./AcpSessionRuntime.ts";
13import * as AcpSessionRuntime from "./AcpSessionRuntime.ts";
1914
2015type CursorAcpRuntimeCursorSettings = Pick<CursorSettings, "apiEndpoint" | "binaryPath">;
2116
2217export interface CursorAcpRuntimeInput extends Omit<
23 AcpSessionRuntimeOptions,
18 AcpSessionRuntime.AcpSessionRuntimeOptions,
2419 "authMethodId" | "clientCapabilities" | "spawn"
2520> {
2621 readonly childProcessSpawner: ChildProcessSpawner.ChildProcessSpawner["Service"];
......@@ -38,7 +33,7 @@ export function buildCursorAcpSpawnInput(
3833 cursorSettings: CursorAcpRuntimeCursorSettings | null | undefined,
3934 cwd: string,
4035 environment?: NodeJS.ProcessEnv,
41): AcpSpawnInput {
36): AcpSessionRuntime.AcpSpawnInput {
4237 return {
4338 command: cursorSettings?.binaryPath || "agent",
4439 args: [
......@@ -52,7 +47,11 @@ export function buildCursorAcpSpawnInput(
5247
5348export const makeCursorAcpRuntime = (
5449 input: CursorAcpRuntimeInput,
55): Effect.Effect<AcpSessionRuntimeShape, EffectAcpErrors.AcpError, Scope.Scope> =>
50): Effect.Effect<
51 AcpSessionRuntime.AcpSessionRuntime["Service"],
52 EffectAcpErrors.AcpError,
53 Scope.Scope
54> =>
5655 Effect.gen(function* () {
5756 const acpContext = yield* Layer.build(
5857 AcpSessionRuntime.layer({
......@@ -66,11 +65,13 @@ export const makeCursorAcpRuntime = (
6665 ),
6766 ),
6867 );
69 return yield* Effect.service(AcpSessionRuntime).pipe(Effect.provide(acpContext));
68 return yield* Effect.service(AcpSessionRuntime.AcpSessionRuntime).pipe(
69 Effect.provide(acpContext),
70 );
7071 });
7172
7273interface CursorAcpModelSelectionRuntime {
73 readonly getConfigOptions: AcpSessionRuntimeShape["getConfigOptions"];
74 readonly getConfigOptions: AcpSessionRuntime.AcpSessionRuntime["Service"]["getConfigOptions"];
7475 readonly setConfigOption: (
7576 configId: string,
7677 value: string | boolean,
apps/server/src/provider/acp/GrokAcpSupport.ts+13-12
......@@ -2,17 +2,12 @@ import { type GrokSettings, ProviderDriverKind } from "@t3tools/contracts";
22import * as Effect from "effect/Effect";
33import * as Layer from "effect/Layer";
44import * as Scope from "effect/Scope";
5import { ChildProcessSpawner } from "effect/unstable/process";
5import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
66import * as EffectAcpErrors from "effect-acp/errors";
77import type * as EffectAcpSchema from "effect-acp/schema";
88import { normalizeModelSlug } from "@t3tools/shared/model";
99
10import {
11 AcpSessionRuntime,
12 type AcpSessionRuntimeOptions,
13 type AcpSessionRuntimeShape,
14 type AcpSpawnInput,
15} from "./AcpSessionRuntime.ts";
10import * as AcpSessionRuntime from "./AcpSessionRuntime.ts";
1611
1712const GROK_API_KEY_ENV = "XAI_API_KEY";
1813const GROK_OAUTH2_REFERRER_ENV = "GROK_OAUTH2_REFERRER";
......@@ -24,7 +19,7 @@ const GROK_DRIVER_KIND = ProviderDriverKind.make("grok");
2419type GrokAcpRuntimeGrokSettings = Pick<GrokSettings, "binaryPath">;
2520
2621interface GrokAcpRuntimeInput extends Omit<
27 AcpSessionRuntimeOptions,
22 AcpSessionRuntime.AcpSessionRuntimeOptions,
2823 "authMethodId" | "clientCapabilities" | "spawn"
2924> {
3025 readonly childProcessSpawner: ChildProcessSpawner.ChildProcessSpawner["Service"];
......@@ -36,7 +31,7 @@ export function buildGrokAcpSpawnInput(
3631 grokSettings: GrokAcpRuntimeGrokSettings | null | undefined,
3732 cwd: string,
3833 environment?: NodeJS.ProcessEnv,
39): AcpSpawnInput {
34): AcpSessionRuntime.AcpSpawnInput {
4035 return {
4136 command: grokSettings?.binaryPath || "grok",
4237 args: ["agent", "stdio"],
......@@ -56,7 +51,11 @@ function resolveGrokAuthMethodId(environment: NodeJS.ProcessEnv | undefined): st
5651
5752export const makeGrokAcpRuntime = (
5853 input: GrokAcpRuntimeInput,
59): Effect.Effect<AcpSessionRuntimeShape, EffectAcpErrors.AcpError, Scope.Scope> =>
54): Effect.Effect<
55 AcpSessionRuntime.AcpSessionRuntime["Service"],
56 EffectAcpErrors.AcpError,
57 Scope.Scope
58> =>
6059 Effect.gen(function* () {
6160 const acpContext = yield* Layer.build(
6261 AcpSessionRuntime.layer({
......@@ -69,7 +68,9 @@ export const makeGrokAcpRuntime = (
6968 ),
7069 ),
7170 );
72 return yield* Effect.service(AcpSessionRuntime).pipe(Effect.provide(acpContext));
71 return yield* Effect.service(AcpSessionRuntime.AcpSessionRuntime).pipe(
72 Effect.provide(acpContext),
73 );
7374 });
7475
7576export function resolveGrokAcpBaseModelId(model: string | null | undefined): string {
......@@ -88,7 +89,7 @@ export function currentGrokModelIdFromSessionSetup(
8889}
8990
9091export function applyGrokAcpModelSelection<E>(input: {
91 readonly runtime: Pick<AcpSessionRuntimeShape, "setSessionModel">;
92 readonly runtime: Pick<AcpSessionRuntime.AcpSessionRuntime["Service"], "setSessionModel">;
9293 readonly currentModelId: string | undefined;
9394 readonly requestedModelId: string | undefined;
9495 readonly mapError: (cause: EffectAcpErrors.AcpError) => E;
packages/effect-acp/src/agent.ts+177-176
......@@ -27,189 +27,190 @@ export interface AcpAgentOptions {
2727 readonly logger?: (event: AcpProtocol.AcpProtocolLogEvent) => Effect.Effect<void, never>;
2828}
2929
30export interface AcpAgentShape {
31 readonly raw: {
30export class AcpAgent extends Context.Service<
31 AcpAgent,
32 {
33 readonly raw: {
34 /**
35 * Stream of inbound ACP notifications observed on the connection.
36 */
37 readonly notifications: Stream.Stream<AcpProtocol.AcpIncomingNotification>;
38 /**
39 * Sends a generic ACP extension request.
40 * @see https://agentclientprotocol.com/protocol/extensibility
41 */
42 readonly request: (
43 method: string,
44 payload: unknown,
45 ) => Effect.Effect<unknown, AcpError.AcpError>;
46 /**
47 * Sends a generic ACP extension notification.
48 * @see https://agentclientprotocol.com/protocol/extensibility
49 */
50 readonly notify: (method: string, payload: unknown) => Effect.Effect<void, AcpError.AcpError>;
51 };
52 readonly client: {
53 /**
54 * Requests client permission for an operation.
55 * @see https://agentclientprotocol.com/protocol/schema#session/request_permission
56 */
57 readonly requestPermission: (
58 payload: AcpSchema.RequestPermissionRequest,
59 ) => Effect.Effect<AcpSchema.RequestPermissionResponse, AcpError.AcpError>;
60 /**
61 * Requests structured user input from the client.
62 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation
63 */
64 readonly elicit: (
65 payload: AcpSchema.ElicitationRequest,
66 ) => Effect.Effect<AcpSchema.ElicitationResponse, AcpError.AcpError>;
67 /**
68 * Requests file contents from the client.
69 * @see https://agentclientprotocol.com/protocol/schema#fs/read_text_file
70 */
71 readonly readTextFile: (
72 payload: AcpSchema.ReadTextFileRequest,
73 ) => Effect.Effect<AcpSchema.ReadTextFileResponse, AcpError.AcpError>;
74 /**
75 * Writes a text file through the client.
76 * @see https://agentclientprotocol.com/protocol/schema#fs/write_text_file
77 */
78 readonly writeTextFile: (
79 payload: AcpSchema.WriteTextFileRequest,
80 ) => Effect.Effect<AcpSchema.WriteTextFileResponse, AcpError.AcpError>;
81 /**
82 * Creates a terminal on the client side.
83 * @see https://agentclientprotocol.com/protocol/schema#terminal/create
84 */
85 readonly createTerminal: (
86 payload: AcpSchema.CreateTerminalRequest,
87 ) => Effect.Effect<AcpTerminal.AcpTerminal, AcpError.AcpError>;
88 /**
89 * Sends a `session/update` notification to the client.
90 * @see https://agentclientprotocol.com/protocol/schema#session/update
91 */
92 readonly sessionUpdate: (
93 payload: AcpSchema.SessionNotification,
94 ) => Effect.Effect<void, AcpError.AcpError>;
95 /**
96 * Sends a `session/elicitation/complete` notification to the client.
97 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation/complete
98 */
99 readonly elicitationComplete: (
100 payload: AcpSchema.ElicitationCompleteNotification,
101 ) => Effect.Effect<void, AcpError.AcpError>;
102 /**
103 * Sends an ACP extension request to the client.
104 * @see https://agentclientprotocol.com/protocol/extensibility
105 */
106 readonly extRequest: (
107 method: string,
108 payload: unknown,
109 ) => Effect.Effect<unknown, AcpError.AcpError>;
110 /**
111 * Sends an ACP extension notification to the client.
112 * @see https://agentclientprotocol.com/protocol/extensibility
113 */
114 readonly extNotification: (
115 method: string,
116 payload: unknown,
117 ) => Effect.Effect<void, AcpError.AcpError>;
118 };
32119 /**
33 * Stream of inbound ACP notifications observed on the connection.
120 * Registers a handler for `initialize`.
121 * @see https://agentclientprotocol.com/protocol/schema#initialize
34122 */
35 readonly notifications: Stream.Stream<AcpProtocol.AcpIncomingNotification>;
123 readonly handleInitialize: (
124 handler: (
125 request: AcpSchema.InitializeRequest,
126 ) => Effect.Effect<AcpSchema.InitializeResponse, AcpError.AcpError>,
127 ) => Effect.Effect<void>;
36128 /**
37 * Sends a generic ACP extension request.
38 * @see https://agentclientprotocol.com/protocol/extensibility
129 * Registers a handler for `authenticate`.
130 * @see https://agentclientprotocol.com/protocol/schema#authenticate
39131 */
40 readonly request: (
41 method: string,
42 payload: unknown,
43 ) => Effect.Effect<unknown, AcpError.AcpError>;
44 /**
45 * Sends a generic ACP extension notification.
46 * @see https://agentclientprotocol.com/protocol/extensibility
47 */
48 readonly notify: (method: string, payload: unknown) => Effect.Effect<void, AcpError.AcpError>;
49 };
50 readonly client: {
51 /**
52 * Requests client permission for an operation.
53 * @see https://agentclientprotocol.com/protocol/schema#session/request_permission
54 */
55 readonly requestPermission: (
56 payload: AcpSchema.RequestPermissionRequest,
57 ) => Effect.Effect<AcpSchema.RequestPermissionResponse, AcpError.AcpError>;
58 /**
59 * Requests structured user input from the client.
60 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation
61 */
62 readonly elicit: (
63 payload: AcpSchema.ElicitationRequest,
64 ) => Effect.Effect<AcpSchema.ElicitationResponse, AcpError.AcpError>;
65 /**
66 * Requests file contents from the client.
67 * @see https://agentclientprotocol.com/protocol/schema#fs/read_text_file
68 */
69 readonly readTextFile: (
70 payload: AcpSchema.ReadTextFileRequest,
71 ) => Effect.Effect<AcpSchema.ReadTextFileResponse, AcpError.AcpError>;
72 /**
73 * Writes a text file through the client.
74 * @see https://agentclientprotocol.com/protocol/schema#fs/write_text_file
75 */
76 readonly writeTextFile: (
77 payload: AcpSchema.WriteTextFileRequest,
78 ) => Effect.Effect<AcpSchema.WriteTextFileResponse, AcpError.AcpError>;
132 readonly handleAuthenticate: (
133 handler: (
134 request: AcpSchema.AuthenticateRequest,
135 ) => Effect.Effect<AcpSchema.AuthenticateResponse, AcpError.AcpError>,
136 ) => Effect.Effect<void>;
137 readonly handleLogout: (
138 handler: (
139 request: AcpSchema.LogoutRequest,
140 ) => Effect.Effect<AcpSchema.LogoutResponse, AcpError.AcpError>,
141 ) => Effect.Effect<void>;
142 readonly handleCreateSession: (
143 handler: (
144 request: AcpSchema.NewSessionRequest,
145 ) => Effect.Effect<AcpSchema.NewSessionResponse, AcpError.AcpError>,
146 ) => Effect.Effect<void>;
147 readonly handleLoadSession: (
148 handler: (
149 request: AcpSchema.LoadSessionRequest,
150 ) => Effect.Effect<AcpSchema.LoadSessionResponse, AcpError.AcpError>,
151 ) => Effect.Effect<void>;
152 readonly handleListSessions: (
153 handler: (
154 request: AcpSchema.ListSessionsRequest,
155 ) => Effect.Effect<AcpSchema.ListSessionsResponse, AcpError.AcpError>,
156 ) => Effect.Effect<void>;
157 readonly handleForkSession: (
158 handler: (
159 request: AcpSchema.ForkSessionRequest,
160 ) => Effect.Effect<AcpSchema.ForkSessionResponse, AcpError.AcpError>,
161 ) => Effect.Effect<void>;
162 readonly handleResumeSession: (
163 handler: (
164 request: AcpSchema.ResumeSessionRequest,
165 ) => Effect.Effect<AcpSchema.ResumeSessionResponse, AcpError.AcpError>,
166 ) => Effect.Effect<void>;
167 readonly handleCloseSession: (
168 handler: (
169 request: AcpSchema.CloseSessionRequest,
170 ) => Effect.Effect<AcpSchema.CloseSessionResponse, AcpError.AcpError>,
171 ) => Effect.Effect<void>;
172 readonly handleSetSessionModel: (
173 handler: (
174 request: AcpSchema.SetSessionModelRequest,
175 ) => Effect.Effect<AcpSchema.SetSessionModelResponse, AcpError.AcpError>,
176 ) => Effect.Effect<void>;
177 readonly handleSetSessionConfigOption: (
178 handler: (
179 request: AcpSchema.SetSessionConfigOptionRequest,
180 ) => Effect.Effect<AcpSchema.SetSessionConfigOptionResponse, AcpError.AcpError>,
181 ) => Effect.Effect<void>;
182 readonly handlePrompt: (
183 handler: (
184 request: AcpSchema.PromptRequest,
185 ) => Effect.Effect<AcpSchema.PromptResponse, AcpError.AcpError>,
186 ) => Effect.Effect<void>;
79187 /**
80 * Creates a terminal on the client side.
81 * @see https://agentclientprotocol.com/protocol/schema#terminal/create
188 * Registers a handler for `session/cancel`.
189 * @see https://agentclientprotocol.com/protocol/schema#session/cancel
82190 */
83 readonly createTerminal: (
84 payload: AcpSchema.CreateTerminalRequest,
85 ) => Effect.Effect<AcpTerminal.AcpTerminal, AcpError.AcpError>;
86 /**
87 * Sends a `session/update` notification to the client.
88 * @see https://agentclientprotocol.com/protocol/schema#session/update
89 */
90 readonly sessionUpdate: (
91 payload: AcpSchema.SessionNotification,
92 ) => Effect.Effect<void, AcpError.AcpError>;
93 /**
94 * Sends a `session/elicitation/complete` notification to the client.
95 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation/complete
96 */
97 readonly elicitationComplete: (
98 payload: AcpSchema.ElicitationCompleteNotification,
99 ) => Effect.Effect<void, AcpError.AcpError>;
100 /**
101 * Sends an ACP extension request to the client.
102 * @see https://agentclientprotocol.com/protocol/extensibility
103 */
104 readonly extRequest: (
191 readonly handleCancel: (
192 handler: (
193 notification: AcpSchema.CancelNotification,
194 ) => Effect.Effect<void, AcpError.AcpError>,
195 ) => Effect.Effect<void>;
196 readonly handleUnknownExtRequest: (
197 handler: (method: string, params: unknown) => Effect.Effect<unknown, AcpError.AcpError>,
198 ) => Effect.Effect<void>;
199 readonly handleUnknownExtNotification: (
200 handler: (method: string, params: unknown) => Effect.Effect<void, AcpError.AcpError>,
201 ) => Effect.Effect<void>;
202 readonly handleExtRequest: <A, I>(
105203 method: string,
106 payload: unknown,
107 ) => Effect.Effect<unknown, AcpError.AcpError>;
108 /**
109 * Sends an ACP extension notification to the client.
110 * @see https://agentclientprotocol.com/protocol/extensibility
111 */
112 readonly extNotification: (
204 payload: Schema.Codec<A, I>,
205 handler: (payload: A) => Effect.Effect<unknown, AcpError.AcpError>,
206 ) => Effect.Effect<void>;
207 readonly handleExtNotification: <A, I>(
113208 method: string,
114 payload: unknown,
115 ) => Effect.Effect<void, AcpError.AcpError>;
116 };
117 /**
118 * Registers a handler for `initialize`.
119 * @see https://agentclientprotocol.com/protocol/schema#initialize
120 */
121 readonly handleInitialize: (
122 handler: (
123 request: AcpSchema.InitializeRequest,
124 ) => Effect.Effect<AcpSchema.InitializeResponse, AcpError.AcpError>,
125 ) => Effect.Effect<void>;
126 /**
127 * Registers a handler for `authenticate`.
128 * @see https://agentclientprotocol.com/protocol/schema#authenticate
129 */
130 readonly handleAuthenticate: (
131 handler: (
132 request: AcpSchema.AuthenticateRequest,
133 ) => Effect.Effect<AcpSchema.AuthenticateResponse, AcpError.AcpError>,
134 ) => Effect.Effect<void>;
135 readonly handleLogout: (
136 handler: (
137 request: AcpSchema.LogoutRequest,
138 ) => Effect.Effect<AcpSchema.LogoutResponse, AcpError.AcpError>,
139 ) => Effect.Effect<void>;
140 readonly handleCreateSession: (
141 handler: (
142 request: AcpSchema.NewSessionRequest,
143 ) => Effect.Effect<AcpSchema.NewSessionResponse, AcpError.AcpError>,
144 ) => Effect.Effect<void>;
145 readonly handleLoadSession: (
146 handler: (
147 request: AcpSchema.LoadSessionRequest,
148 ) => Effect.Effect<AcpSchema.LoadSessionResponse, AcpError.AcpError>,
149 ) => Effect.Effect<void>;
150 readonly handleListSessions: (
151 handler: (
152 request: AcpSchema.ListSessionsRequest,
153 ) => Effect.Effect<AcpSchema.ListSessionsResponse, AcpError.AcpError>,
154 ) => Effect.Effect<void>;
155 readonly handleForkSession: (
156 handler: (
157 request: AcpSchema.ForkSessionRequest,
158 ) => Effect.Effect<AcpSchema.ForkSessionResponse, AcpError.AcpError>,
159 ) => Effect.Effect<void>;
160 readonly handleResumeSession: (
161 handler: (
162 request: AcpSchema.ResumeSessionRequest,
163 ) => Effect.Effect<AcpSchema.ResumeSessionResponse, AcpError.AcpError>,
164 ) => Effect.Effect<void>;
165 readonly handleCloseSession: (
166 handler: (
167 request: AcpSchema.CloseSessionRequest,
168 ) => Effect.Effect<AcpSchema.CloseSessionResponse, AcpError.AcpError>,
169 ) => Effect.Effect<void>;
170 readonly handleSetSessionModel: (
171 handler: (
172 request: AcpSchema.SetSessionModelRequest,
173 ) => Effect.Effect<AcpSchema.SetSessionModelResponse, AcpError.AcpError>,
174 ) => Effect.Effect<void>;
175 readonly handleSetSessionConfigOption: (
176 handler: (
177 request: AcpSchema.SetSessionConfigOptionRequest,
178 ) => Effect.Effect<AcpSchema.SetSessionConfigOptionResponse, AcpError.AcpError>,
179 ) => Effect.Effect<void>;
180 readonly handlePrompt: (
181 handler: (
182 request: AcpSchema.PromptRequest,
183 ) => Effect.Effect<AcpSchema.PromptResponse, AcpError.AcpError>,
184 ) => Effect.Effect<void>;
185 /**
186 * Registers a handler for `session/cancel`.
187 * @see https://agentclientprotocol.com/protocol/schema#session/cancel
188 */
189 readonly handleCancel: (
190 handler: (notification: AcpSchema.CancelNotification) => Effect.Effect<void, AcpError.AcpError>,
191 ) => Effect.Effect<void>;
192 readonly handleUnknownExtRequest: (
193 handler: (method: string, params: unknown) => Effect.Effect<unknown, AcpError.AcpError>,
194 ) => Effect.Effect<void>;
195 readonly handleUnknownExtNotification: (
196 handler: (method: string, params: unknown) => Effect.Effect<void, AcpError.AcpError>,
197 ) => Effect.Effect<void>;
198 readonly handleExtRequest: <A, I>(
199 method: string,
200 payload: Schema.Codec<A, I>,
201 handler: (payload: A) => Effect.Effect<unknown, AcpError.AcpError>,
202 ) => Effect.Effect<void>;
203 readonly handleExtNotification: <A, I>(
204 method: string,
205 payload: Schema.Codec<A, I>,
206 handler: (payload: A) => Effect.Effect<void, AcpError.AcpError>,
207 ) => Effect.Effect<void>;
208}
209
210export class AcpAgent extends Context.Service<AcpAgent, AcpAgentShape>()(
211 "effect-acp/agent/AcpAgent",
212) {}
209 payload: Schema.Codec<A, I>,
210 handler: (payload: A) => Effect.Effect<void, AcpError.AcpError>,
211 ) => Effect.Effect<void>;
212 }
213>()("effect-acp/agent/AcpAgent") {}
213214
214215interface AcpCoreAgentRequestHandlers {
215216 initialize?: (
......@@ -255,7 +256,7 @@ const decodeCancelNotification = Schema.decodeUnknownEffect(AcpSchema.CancelNoti
255256export const make = Effect.fn("effect-acp/AcpAgent.make")(function* (
256257 stdio: Stdio.Stdio,
257258 options: AcpAgentOptions = {},
258): Effect.fn.Return<AcpAgentShape, never, Scope.Scope> {
259): Effect.fn.Return<AcpAgent["Service"], never, Scope.Scope> {
259260 const coreHandlers: AcpCoreAgentRequestHandlers = {};
260261 const cancelHandlers: Array<
261262 (notification: AcpSchema.CancelNotification) => Effect.Effect<void, AcpError.AcpError>
packages/effect-acp/src/client.ts+209-207
......@@ -7,7 +7,7 @@ import * as Scope from "effect/Scope";
77import * as Stream from "effect/Stream";
88import * as RpcClient from "effect/unstable/rpc/RpcClient";
99import * as RpcServer from "effect/unstable/rpc/RpcServer";
10import { ChildProcessSpawner } from "effect/unstable/process";
10import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
1111
1212import * as AcpError from "./errors.ts";
1313import * as AcpProtocol from "./protocol.ts";
......@@ -34,237 +34,236 @@ type AcpClientRaw = {
3434 readonly notify: (method: string, payload: unknown) => Effect.Effect<void, AcpError.AcpError>;
3535};
3636
37export interface AcpClientShape {
38 readonly raw: AcpClientRaw;
39 readonly agent: {
37export class AcpClient extends Context.Service<
38 AcpClient,
39 {
40 readonly raw: AcpClientRaw;
41 readonly agent: {
42 /**
43 * Initializes the ACP session and negotiates capabilities.
44 * @see https://agentclientprotocol.com/protocol/schema#initialize
45 */
46 readonly initialize: (
47 payload: AcpSchema.InitializeRequest,
48 ) => Effect.Effect<AcpSchema.InitializeResponse, AcpError.AcpError>;
49 /**
50 * Performs ACP authentication when the agent requires it.
51 * @see https://agentclientprotocol.com/protocol/schema#authenticate
52 */
53 readonly authenticate: (
54 payload: AcpSchema.AuthenticateRequest,
55 ) => Effect.Effect<AcpSchema.AuthenticateResponse, AcpError.AcpError>;
56 /**
57 * Logs out the current ACP identity.
58 * @see https://agentclientprotocol.com/protocol/schema#logout
59 */
60 readonly logout: (
61 payload: AcpSchema.LogoutRequest,
62 ) => Effect.Effect<AcpSchema.LogoutResponse, AcpError.AcpError>;
63 /**
64 * Starts a new ACP session.
65 * @see https://agentclientprotocol.com/protocol/schema#session/new
66 */
67 readonly createSession: (
68 payload: AcpSchema.NewSessionRequest,
69 ) => Effect.Effect<AcpSchema.NewSessionResponse, AcpError.AcpError>;
70 /**
71 * Loads a previously saved ACP session.
72 * @see https://agentclientprotocol.com/protocol/schema#session/load
73 */
74 readonly loadSession: (
75 payload: AcpSchema.LoadSessionRequest,
76 ) => Effect.Effect<AcpSchema.LoadSessionResponse, AcpError.AcpError>;
77 /**
78 * Lists available ACP sessions.
79 * @see https://agentclientprotocol.com/protocol/schema#session/list
80 */
81 readonly listSessions: (
82 payload: AcpSchema.ListSessionsRequest,
83 ) => Effect.Effect<AcpSchema.ListSessionsResponse, AcpError.AcpError>;
84 /**
85 * Forks an ACP session.
86 * @see https://agentclientprotocol.com/protocol/schema#session/fork
87 */
88 readonly forkSession: (
89 payload: AcpSchema.ForkSessionRequest,
90 ) => Effect.Effect<AcpSchema.ForkSessionResponse, AcpError.AcpError>;
91 /**
92 * Resumes an ACP session.
93 * @see https://agentclientprotocol.com/protocol/schema#session/resume
94 */
95 readonly resumeSession: (
96 payload: AcpSchema.ResumeSessionRequest,
97 ) => Effect.Effect<AcpSchema.ResumeSessionResponse, AcpError.AcpError>;
98 /**
99 * Closes an ACP session.
100 * @see https://agentclientprotocol.com/protocol/schema#session/close
101 */
102 readonly closeSession: (
103 payload: AcpSchema.CloseSessionRequest,
104 ) => Effect.Effect<AcpSchema.CloseSessionResponse, AcpError.AcpError>;
105 /**
106 * Selects the active model for a session.
107 * @see https://agentclientprotocol.com/protocol/schema#session/set_model
108 */
109 readonly setSessionModel: (
110 payload: AcpSchema.SetSessionModelRequest,
111 ) => Effect.Effect<AcpSchema.SetSessionModelResponse, AcpError.AcpError>;
112 /**
113 * Updates a session configuration option.
114 * @see https://agentclientprotocol.com/protocol/schema#session/set_config_option
115 */
116 readonly setSessionConfigOption: (
117 payload: AcpSchema.SetSessionConfigOptionRequest,
118 ) => Effect.Effect<AcpSchema.SetSessionConfigOptionResponse, AcpError.AcpError>;
119 /**
120 * Sends a prompt turn to the agent.
121 * @see https://agentclientprotocol.com/protocol/schema#session/prompt
122 */
123 readonly prompt: (
124 payload: AcpSchema.PromptRequest,
125 ) => Effect.Effect<AcpSchema.PromptResponse, AcpError.AcpError>;
126 /**
127 * Sends a real ACP `session/cancel` notification.
128 * @see https://agentclientprotocol.com/protocol/schema#session/cancel
129 */
130 readonly cancel: (
131 payload: AcpSchema.CancelNotification,
132 ) => Effect.Effect<void, AcpError.AcpError>;
133 };
40134 /**
41 * Initializes the ACP session and negotiates capabilities.
42 * @see https://agentclientprotocol.com/protocol/schema#initialize
135 * Registers a handler for `session/request_permission`.
136 * @see https://agentclientprotocol.com/protocol/schema#session/request_permission
43137 */
44 readonly initialize: (
45 payload: AcpSchema.InitializeRequest,
46 ) => Effect.Effect<AcpSchema.InitializeResponse, AcpError.AcpError>;
138 readonly handleRequestPermission: (
139 handler: (
140 request: AcpSchema.RequestPermissionRequest,
141 ) => Effect.Effect<AcpSchema.RequestPermissionResponse, AcpError.AcpError>,
142 ) => Effect.Effect<void>;
47143 /**
48 * Performs ACP authentication when the agent requires it.
49 * @see https://agentclientprotocol.com/protocol/schema#authenticate
144 * Registers a handler for `session/elicitation`.
145 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation
50146 */
51 readonly authenticate: (
52 payload: AcpSchema.AuthenticateRequest,
53 ) => Effect.Effect<AcpSchema.AuthenticateResponse, AcpError.AcpError>;
147 readonly handleElicitation: (
148 handler: (
149 request: AcpSchema.ElicitationRequest,
150 ) => Effect.Effect<AcpSchema.ElicitationResponse, AcpError.AcpError>,
151 ) => Effect.Effect<void>;
54152 /**
55 * Logs out the current ACP identity.
56 * @see https://agentclientprotocol.com/protocol/schema#logout
153 * Registers a handler for `fs/read_text_file`.
154 * @see https://agentclientprotocol.com/protocol/schema#fs/read_text_file
57155 */
58 readonly logout: (
59 payload: AcpSchema.LogoutRequest,
60 ) => Effect.Effect<AcpSchema.LogoutResponse, AcpError.AcpError>;
156 readonly handleReadTextFile: (
157 handler: (
158 request: AcpSchema.ReadTextFileRequest,
159 ) => Effect.Effect<AcpSchema.ReadTextFileResponse, AcpError.AcpError>,
160 ) => Effect.Effect<void>;
61161 /**
62 * Starts a new ACP session.
63 * @see https://agentclientprotocol.com/protocol/schema#session/new
162 * Registers a handler for `fs/write_text_file`.
163 * @see https://agentclientprotocol.com/protocol/schema#fs/write_text_file
64164 */
65 readonly createSession: (
66 payload: AcpSchema.NewSessionRequest,
67 ) => Effect.Effect<AcpSchema.NewSessionResponse, AcpError.AcpError>;
165 readonly handleWriteTextFile: (
166 handler: (
167 request: AcpSchema.WriteTextFileRequest,
168 ) => Effect.Effect<AcpSchema.WriteTextFileResponse | void, AcpError.AcpError>,
169 ) => Effect.Effect<void>;
68170 /**
69 * Loads a previously saved ACP session.
70 * @see https://agentclientprotocol.com/protocol/schema#session/load
171 * Registers a handler for `terminal/create`.
172 * @see https://agentclientprotocol.com/protocol/schema#terminal/create
71173 */
72 readonly loadSession: (
73 payload: AcpSchema.LoadSessionRequest,
74 ) => Effect.Effect<AcpSchema.LoadSessionResponse, AcpError.AcpError>;
174 readonly handleCreateTerminal: (
175 handler: (
176 request: AcpSchema.CreateTerminalRequest,
177 ) => Effect.Effect<AcpSchema.CreateTerminalResponse, AcpError.AcpError>,
178 ) => Effect.Effect<void>;
75179 /**
76 * Lists available ACP sessions.
77 * @see https://agentclientprotocol.com/protocol/schema#session/list
180 * Registers a handler for `terminal/output`.
181 * @see https://agentclientprotocol.com/protocol/schema#terminal/output
78182 */
79 readonly listSessions: (
80 payload: AcpSchema.ListSessionsRequest,
81 ) => Effect.Effect<AcpSchema.ListSessionsResponse, AcpError.AcpError>;
183 readonly handleTerminalOutput: (
184 handler: (
185 request: AcpSchema.TerminalOutputRequest,
186 ) => Effect.Effect<AcpSchema.TerminalOutputResponse, AcpError.AcpError>,
187 ) => Effect.Effect<void>;
82188 /**
83 * Forks an ACP session.
84 * @see https://agentclientprotocol.com/protocol/schema#session/fork
189 * Registers a handler for `terminal/wait_for_exit`.
190 * @see https://agentclientprotocol.com/protocol/schema#terminal/wait_for_exit
85191 */
86 readonly forkSession: (
87 payload: AcpSchema.ForkSessionRequest,
88 ) => Effect.Effect<AcpSchema.ForkSessionResponse, AcpError.AcpError>;
192 readonly handleTerminalWaitForExit: (
193 handler: (
194 request: AcpSchema.WaitForTerminalExitRequest,
195 ) => Effect.Effect<AcpSchema.WaitForTerminalExitResponse, AcpError.AcpError>,
196 ) => Effect.Effect<void>;
89197 /**
90 * Resumes an ACP session.
91 * @see https://agentclientprotocol.com/protocol/schema#session/resume
198 * Registers a handler for `terminal/kill`.
199 * @see https://agentclientprotocol.com/protocol/schema#terminal/kill
92200 */
93 readonly resumeSession: (
94 payload: AcpSchema.ResumeSessionRequest,
95 ) => Effect.Effect<AcpSchema.ResumeSessionResponse, AcpError.AcpError>;
201 readonly handleTerminalKill: (
202 handler: (
203 request: AcpSchema.KillTerminalRequest,
204 ) => Effect.Effect<AcpSchema.KillTerminalResponse | void, AcpError.AcpError>,
205 ) => Effect.Effect<void>;
96206 /**
97 * Closes an ACP session.
98 * @see https://agentclientprotocol.com/protocol/schema#session/close
207 * Registers a handler for `terminal/release`.
208 * @see https://agentclientprotocol.com/protocol/schema#terminal/release
99209 */
100 readonly closeSession: (
101 payload: AcpSchema.CloseSessionRequest,
102 ) => Effect.Effect<AcpSchema.CloseSessionResponse, AcpError.AcpError>;
210 readonly handleTerminalRelease: (
211 handler: (
212 request: AcpSchema.ReleaseTerminalRequest,
213 ) => Effect.Effect<AcpSchema.ReleaseTerminalResponse | void, AcpError.AcpError>,
214 ) => Effect.Effect<void>;
103215 /**
104 * Selects the active model for a session.
105 * @see https://agentclientprotocol.com/protocol/schema#session/set_model
216 * Registers a handler for `session/update`.
217 * @see https://agentclientprotocol.com/protocol/schema#session/update
106218 */
107 readonly setSessionModel: (
108 payload: AcpSchema.SetSessionModelRequest,
109 ) => Effect.Effect<AcpSchema.SetSessionModelResponse, AcpError.AcpError>;
219 readonly handleSessionUpdate: (
220 handler: (
221 notification: AcpSchema.SessionNotification,
222 ) => Effect.Effect<void, AcpError.AcpError>,
223 ) => Effect.Effect<void>;
110224 /**
111 * Updates a session configuration option.
112 * @see https://agentclientprotocol.com/protocol/schema#session/set_config_option
225 * Registers a handler for `session/elicitation/complete`.
226 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation/complete
113227 */
114 readonly setSessionConfigOption: (
115 payload: AcpSchema.SetSessionConfigOptionRequest,
116 ) => Effect.Effect<AcpSchema.SetSessionConfigOptionResponse, AcpError.AcpError>;
228 readonly handleElicitationComplete: (
229 handler: (
230 notification: AcpSchema.ElicitationCompleteNotification,
231 ) => Effect.Effect<void, AcpError.AcpError>,
232 ) => Effect.Effect<void>;
117233 /**
118 * Sends a prompt turn to the agent.
119 * @see https://agentclientprotocol.com/protocol/schema#session/prompt
234 * Registers a fallback extension request handler.
235 * @see https://agentclientprotocol.com/protocol/extensibility
120236 */
121 readonly prompt: (
122 payload: AcpSchema.PromptRequest,
123 ) => Effect.Effect<AcpSchema.PromptResponse, AcpError.AcpError>;
237 readonly handleUnknownExtRequest: (
238 handler: (method: string, params: unknown) => Effect.Effect<unknown, AcpError.AcpError>,
239 ) => Effect.Effect<void>;
124240 /**
125 * Sends a real ACP `session/cancel` notification.
126 * @see https://agentclientprotocol.com/protocol/schema#session/cancel
241 * Registers a fallback extension notification handler.
242 * @see https://agentclientprotocol.com/protocol/extensibility
127243 */
128 readonly cancel: (
129 payload: AcpSchema.CancelNotification,
130 ) => Effect.Effect<void, AcpError.AcpError>;
131 };
132 /**
133 * Registers a handler for `session/request_permission`.
134 * @see https://agentclientprotocol.com/protocol/schema#session/request_permission
135 */
136 readonly handleRequestPermission: (
137 handler: (
138 request: AcpSchema.RequestPermissionRequest,
139 ) => Effect.Effect<AcpSchema.RequestPermissionResponse, AcpError.AcpError>,
140 ) => Effect.Effect<void>;
141 /**
142 * Registers a handler for `session/elicitation`.
143 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation
144 */
145 readonly handleElicitation: (
146 handler: (
147 request: AcpSchema.ElicitationRequest,
148 ) => Effect.Effect<AcpSchema.ElicitationResponse, AcpError.AcpError>,
149 ) => Effect.Effect<void>;
150 /**
151 * Registers a handler for `fs/read_text_file`.
152 * @see https://agentclientprotocol.com/protocol/schema#fs/read_text_file
153 */
154 readonly handleReadTextFile: (
155 handler: (
156 request: AcpSchema.ReadTextFileRequest,
157 ) => Effect.Effect<AcpSchema.ReadTextFileResponse, AcpError.AcpError>,
158 ) => Effect.Effect<void>;
159 /**
160 * Registers a handler for `fs/write_text_file`.
161 * @see https://agentclientprotocol.com/protocol/schema#fs/write_text_file
162 */
163 readonly handleWriteTextFile: (
164 handler: (
165 request: AcpSchema.WriteTextFileRequest,
166 ) => Effect.Effect<AcpSchema.WriteTextFileResponse | void, AcpError.AcpError>,
167 ) => Effect.Effect<void>;
168 /**
169 * Registers a handler for `terminal/create`.
170 * @see https://agentclientprotocol.com/protocol/schema#terminal/create
171 */
172 readonly handleCreateTerminal: (
173 handler: (
174 request: AcpSchema.CreateTerminalRequest,
175 ) => Effect.Effect<AcpSchema.CreateTerminalResponse, AcpError.AcpError>,
176 ) => Effect.Effect<void>;
177 /**
178 * Registers a handler for `terminal/output`.
179 * @see https://agentclientprotocol.com/protocol/schema#terminal/output
180 */
181 readonly handleTerminalOutput: (
182 handler: (
183 request: AcpSchema.TerminalOutputRequest,
184 ) => Effect.Effect<AcpSchema.TerminalOutputResponse, AcpError.AcpError>,
185 ) => Effect.Effect<void>;
186 /**
187 * Registers a handler for `terminal/wait_for_exit`.
188 * @see https://agentclientprotocol.com/protocol/schema#terminal/wait_for_exit
189 */
190 readonly handleTerminalWaitForExit: (
191 handler: (
192 request: AcpSchema.WaitForTerminalExitRequest,
193 ) => Effect.Effect<AcpSchema.WaitForTerminalExitResponse, AcpError.AcpError>,
194 ) => Effect.Effect<void>;
195 /**
196 * Registers a handler for `terminal/kill`.
197 * @see https://agentclientprotocol.com/protocol/schema#terminal/kill
198 */
199 readonly handleTerminalKill: (
200 handler: (
201 request: AcpSchema.KillTerminalRequest,
202 ) => Effect.Effect<AcpSchema.KillTerminalResponse | void, AcpError.AcpError>,
203 ) => Effect.Effect<void>;
204 /**
205 * Registers a handler for `terminal/release`.
206 * @see https://agentclientprotocol.com/protocol/schema#terminal/release
207 */
208 readonly handleTerminalRelease: (
209 handler: (
210 request: AcpSchema.ReleaseTerminalRequest,
211 ) => Effect.Effect<AcpSchema.ReleaseTerminalResponse | void, AcpError.AcpError>,
212 ) => Effect.Effect<void>;
213 /**
214 * Registers a handler for `session/update`.
215 * @see https://agentclientprotocol.com/protocol/schema#session/update
216 */
217 readonly handleSessionUpdate: (
218 handler: (
219 notification: AcpSchema.SessionNotification,
220 ) => Effect.Effect<void, AcpError.AcpError>,
221 ) => Effect.Effect<void>;
222 /**
223 * Registers a handler for `session/elicitation/complete`.
224 * @see https://agentclientprotocol.com/protocol/schema#session/elicitation/complete
225 */
226 readonly handleElicitationComplete: (
227 handler: (
228 notification: AcpSchema.ElicitationCompleteNotification,
229 ) => Effect.Effect<void, AcpError.AcpError>,
230 ) => Effect.Effect<void>;
231 /**
232 * Registers a fallback extension request handler.
233 * @see https://agentclientprotocol.com/protocol/extensibility
234 */
235 readonly handleUnknownExtRequest: (
236 handler: (method: string, params: unknown) => Effect.Effect<unknown, AcpError.AcpError>,
237 ) => Effect.Effect<void>;
238 /**
239 * Registers a fallback extension notification handler.
240 * @see https://agentclientprotocol.com/protocol/extensibility
241 */
242 readonly handleUnknownExtNotification: (
243 handler: (method: string, params: unknown) => Effect.Effect<void, AcpError.AcpError>,
244 ) => Effect.Effect<void>;
245 /**
246 * Registers a typed extension request handler.
247 * @see https://agentclientprotocol.com/protocol/extensibility
248 */
249 readonly handleExtRequest: <A, I>(
250 method: string,
251 payload: Schema.Codec<A, I>,
252 handler: (payload: A) => Effect.Effect<unknown, AcpError.AcpError>,
253 ) => Effect.Effect<void>;
254 /**
255 * Registers a typed extension notification handler.
256 * @see https://agentclientprotocol.com/protocol/extensibility
257 */
258 readonly handleExtNotification: <A, I>(
259 method: string,
260 payload: Schema.Codec<A, I>,
261 handler: (payload: A) => Effect.Effect<void, AcpError.AcpError>,
262 ) => Effect.Effect<void>;
263}
264
265export class AcpClient extends Context.Service<AcpClient, AcpClientShape>()(
266 "effect-acp/client/AcpClient",
267) {}
244 readonly handleUnknownExtNotification: (
245 handler: (method: string, params: unknown) => Effect.Effect<void, AcpError.AcpError>,
246 ) => Effect.Effect<void>;
247 /**
248 * Registers a typed extension request handler.
249 * @see https://agentclientprotocol.com/protocol/extensibility
250 */
251 readonly handleExtRequest: <A, I>(
252 method: string,
253 payload: Schema.Codec<A, I>,
254 handler: (payload: A) => Effect.Effect<unknown, AcpError.AcpError>,
255 ) => Effect.Effect<void>;
256 /**
257 * Registers a typed extension notification handler.
258 * @see https://agentclientprotocol.com/protocol/extensibility
259 */
260 readonly handleExtNotification: <A, I>(
261 method: string,
262 payload: Schema.Codec<A, I>,
263 handler: (payload: A) => Effect.Effect<void, AcpError.AcpError>,
264 ) => Effect.Effect<void>;
265 }
266>()("effect-acp/client/AcpClient") {}
268267
269268interface AcpCoreRequestHandlers {
270269 requestPermission?: (
......@@ -310,7 +309,7 @@ export const make = Effect.fn("effect-acp/AcpClient.make")(function* (
310309 stdio: Stdio.Stdio,
311310 options: AcpClientOptions = {},
312311 terminationError?: Effect.Effect<AcpError.AcpError>,
313): Effect.fn.Return<AcpClientShape, never, Scope.Scope> {
312): Effect.fn.Return<AcpClient["Service"], never, Scope.Scope> {
314313 const coreHandlers: AcpCoreRequestHandlers = {};
315314 const notificationHandlers: AcpNotificationHandlers = {
316315 sessionUpdate: { handlers: [], pending: [] },
......@@ -559,6 +558,9 @@ export const make = Effect.fn("effect-acp/AcpClient.make")(function* (
559558 });
560559});
561560
561export const layer = (stdio: Stdio.Stdio, options: AcpClientOptions = {}): Layer.Layer<AcpClient> =>
562 Layer.effect(AcpClient, make(stdio, options));
563
562564export const layerChildProcess = (
563565 handle: ChildProcessSpawner.ChildProcessHandle,
564566 options: AcpClientOptions = {},
packages/effect-acp/src/errors.ts+5-1
......@@ -45,7 +45,11 @@ export class AcpTransportError extends Schema.TaggedErrorClass<AcpTransportError
4545 detail: Schema.String,
4646 cause: Schema.Defect(),
4747 },
48) {}
48) {
49 override get message() {
50 return this.detail;
51 }
52}
4953
5054export class AcpRequestError extends Schema.TaggedErrorClass<AcpRequestError>()("AcpRequestError", {
5155 code: AcpSchema.ErrorCode,
packages/effect-codex-app-server/src/client.ts+45-39
......@@ -5,7 +5,7 @@ import * as Schema from "effect/Schema";
55import * as Scope from "effect/Scope";
66import * as Stdio from "effect/Stdio";
77import * as Stream from "effect/Stream";
8import { ChildProcessSpawner } from "effect/unstable/process";
8import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
99
1010import * as CodexRpc from "./_generated/meta.gen.ts";
1111import * as CodexError from "./errors.ts";
......@@ -35,45 +35,46 @@ interface CodexAppServerClientRaw {
3535 readonly respondError: CodexProtocol.CodexAppServerPatchedProtocol["respondError"];
3636}
3737
38export interface CodexAppServerClientShape {
39 readonly raw: CodexAppServerClientRaw;
40 readonly request: <M extends CodexRpc.ClientRequestMethod>(
41 method: M,
42 payload: CodexRpc.ClientRequestParamsByMethod[M],
43 ) => Effect.Effect<CodexRpc.ClientRequestResponsesByMethod[M], CodexError.CodexAppServerError>;
44 readonly notify: <M extends CodexRpc.ClientNotificationMethod>(
45 method: M,
46 payload: CodexRpc.ClientNotificationParamsByMethod[M],
47 ) => Effect.Effect<void, CodexError.CodexAppServerError>;
48 readonly handleServerRequest: <M extends CodexRpc.ServerRequestMethod>(
49 method: M,
50 handler: (
51 payload: CodexRpc.ServerRequestParamsByMethod[M],
52 ) => Effect.Effect<CodexRpc.ServerRequestResponsesByMethod[M], CodexError.CodexAppServerError>,
53 ) => Effect.Effect<void>;
54 readonly handleServerNotification: <M extends CodexRpc.ServerNotificationMethod>(
55 method: M,
56 handler: (
57 payload: CodexRpc.ServerNotificationParamsByMethod[M],
58 ) => Effect.Effect<void, CodexError.CodexAppServerError>,
59 ) => Effect.Effect<void>;
60 readonly handleUnknownServerRequest: (
61 handler: (
62 method: string,
63 params: unknown,
64 ) => Effect.Effect<unknown, CodexError.CodexAppServerError>,
65 ) => Effect.Effect<void>;
66 readonly handleUnknownServerNotification: (
67 handler: (
68 method: string,
69 params: unknown,
70 ) => Effect.Effect<void, CodexError.CodexAppServerError>,
71 ) => Effect.Effect<void>;
72}
73
7438export class CodexAppServerClient extends Context.Service<
7539 CodexAppServerClient,
76 CodexAppServerClientShape
40 {
41 readonly raw: CodexAppServerClientRaw;
42 readonly request: <M extends CodexRpc.ClientRequestMethod>(
43 method: M,
44 payload: CodexRpc.ClientRequestParamsByMethod[M],
45 ) => Effect.Effect<CodexRpc.ClientRequestResponsesByMethod[M], CodexError.CodexAppServerError>;
46 readonly notify: <M extends CodexRpc.ClientNotificationMethod>(
47 method: M,
48 payload: CodexRpc.ClientNotificationParamsByMethod[M],
49 ) => Effect.Effect<void, CodexError.CodexAppServerError>;
50 readonly handleServerRequest: <M extends CodexRpc.ServerRequestMethod>(
51 method: M,
52 handler: (
53 payload: CodexRpc.ServerRequestParamsByMethod[M],
54 ) => Effect.Effect<
55 CodexRpc.ServerRequestResponsesByMethod[M],
56 CodexError.CodexAppServerError
57 >,
58 ) => Effect.Effect<void>;
59 readonly handleServerNotification: <M extends CodexRpc.ServerNotificationMethod>(
60 method: M,
61 handler: (
62 payload: CodexRpc.ServerNotificationParamsByMethod[M],
63 ) => Effect.Effect<void, CodexError.CodexAppServerError>,
64 ) => Effect.Effect<void>;
65 readonly handleUnknownServerRequest: (
66 handler: (
67 method: string,
68 params: unknown,
69 ) => Effect.Effect<unknown, CodexError.CodexAppServerError>,
70 ) => Effect.Effect<void>;
71 readonly handleUnknownServerNotification: (
72 handler: (
73 method: string,
74 params: unknown,
75 ) => Effect.Effect<void, CodexError.CodexAppServerError>,
76 ) => Effect.Effect<void>;
77 }
7778>()("effect-codex-app-server/client/CodexAppServerClient") {}
7879
7980type ServerRequestHandler = (
......@@ -87,7 +88,7 @@ export const make = Effect.fn("effect-codex-app-server/CodexAppServerClient.make
8788 stdio: Stdio.Stdio,
8889 options: CodexAppServerClientOptions = {},
8990 terminationError?: Effect.Effect<CodexError.CodexAppServerError>,
90): Effect.fn.Return<CodexAppServerClientShape, never, Scope.Scope> {
91): Effect.fn.Return<CodexAppServerClient["Service"], never, Scope.Scope> {
9192 const requestHandlers = new Map<string, ServerRequestHandler>();
9293 const notificationHandlers = new Map<string, Array<ServerNotificationHandler>>();
9394 let unknownRequestHandler:
......@@ -249,6 +250,11 @@ export const make = Effect.fn("effect-codex-app-server/CodexAppServerClient.make
249250 });
250251});
251252
253export const layer = (
254 stdio: Stdio.Stdio,
255 options: CodexAppServerClientOptions = {},
256): Layer.Layer<CodexAppServerClient> => Layer.effect(CodexAppServerClient, make(stdio, options));
257
252258export const layerChildProcess = (
253259 handle: ChildProcessSpawner.ChildProcessHandle,
254260 options: CodexAppServerClientOptions = {},