import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Schema from "effect/Schema";import * as Scope from "effect/Scope";import * as Stream from "effect/Stream";import * as Stdio from "effect/Stdio";import * as RpcClient from "effect/unstable/rpc/RpcClient";import * as RpcMessage from "effect/unstable/rpc/RpcMessage";import * as RpcServer from "effect/unstable/rpc/RpcServer"; import * as AcpSchema from "./_generated/schema.gen.ts";import { AGENT_METHODS, CLIENT_METHODS } from "./_generated/meta.gen.ts";import * as AcpError from "./errors.ts";import * as AcpProtocol from "./protocol.ts";import * as AcpRpcs from "./rpc.ts";import { callRpc, decodeExtNotificationRegistration, decodeExtRequestRegistration, runHandler,} from "./_internal/shared.ts";import * as AcpTerminal from "./terminal.ts"; export interface AcpAgentOptions { readonly logIncoming?: boolean; readonly logOutgoing?: boolean; readonly logger?: (event: AcpProtocol.AcpProtocolLogEvent) => Effect.Effect<void, never>;} export class AcpAgent extends Context.Service< AcpAgent, { readonly raw: { /** * Stream of inbound ACP notifications observed on the connection. */ readonly notifications: Stream.Stream<AcpProtocol.AcpIncomingNotification>; /** * Sends a generic ACP extension request. * @see https://agentclientprotocol.com/protocol/extensibility */ readonly request: ( method: string, payload: unknown, ) => Effect.Effect<unknown, AcpError.AcpError>; /** * Sends a generic ACP extension notification. * @see https://agentclientprotocol.com/protocol/extensibility */ readonly notify: (method: string, payload: unknown) => Effect.Effect<void, AcpError.AcpError>; }; readonly client: { /** * Requests client permission for an operation. * @see https://agentclientprotocol.com/protocol/schema#session/request_permission */ readonly requestPermission: ( payload: AcpSchema.RequestPermissionRequest, ) => Effect.Effect<AcpSchema.RequestPermissionResponse, AcpError.AcpError>; /** * Requests structured user input from the client. * @see https://agentclientprotocol.com/protocol/schema#session/elicitation */ readonly elicit: ( payload: AcpSchema.ElicitationRequest, ) => Effect.Effect<AcpSchema.ElicitationResponse, AcpError.AcpError>; /** * Requests file contents from the client. * @see https://agentclientprotocol.com/protocol/schema#fs/read_text_file */ readonly readTextFile: ( payload: AcpSchema.ReadTextFileRequest, ) => Effect.Effect<AcpSchema.ReadTextFileResponse, AcpError.AcpError>; /** * Writes a text file through the client. * @see https://agentclientprotocol.com/protocol/schema#fs/write_text_file */ readonly writeTextFile: ( payload: AcpSchema.WriteTextFileRequest, ) => Effect.Effect<AcpSchema.WriteTextFileResponse, AcpError.AcpError>; /** * Creates a terminal on the client side. * @see https://agentclientprotocol.com/protocol/schema#terminal/create */ readonly createTerminal: ( payload: AcpSchema.CreateTerminalRequest, ) => Effect.Effect<AcpTerminal.AcpTerminal, AcpError.AcpError>; /** * Sends a `session/update` notification to the client. * @see https://agentclientprotocol.com/protocol/schema#session/update */ readonly sessionUpdate: ( payload: AcpSchema.SessionNotification, ) => Effect.Effect<void, AcpError.AcpError>; /** * Sends a `session/elicitation/complete` notification to the client. * @see https://agentclientprotocol.com/protocol/schema#session/elicitation/complete */ readonly elicitationComplete: ( payload: AcpSchema.ElicitationCompleteNotification, ) => Effect.Effect<void, AcpError.AcpError>; /** * Sends an ACP extension request to the client. * @see https://agentclientprotocol.com/protocol/extensibility */ readonly extRequest: ( method: string, payload: unknown, ) => Effect.Effect<unknown, AcpError.AcpError>; /** * Sends an ACP extension notification to the client. * @see https://agentclientprotocol.com/protocol/extensibility */ readonly extNotification: ( method: string, payload: unknown, ) => Effect.Effect<void, AcpError.AcpError>; }; /** * Registers a handler for `initialize`. * @see https://agentclientprotocol.com/protocol/schema#initialize */ readonly handleInitialize: ( handler: ( request: AcpSchema.InitializeRequest, ) => Effect.Effect<AcpSchema.InitializeResponse, AcpError.AcpError>, ) => Effect.Effect<void>; /** * Registers a handler for `authenticate`. * @see https://agentclientprotocol.com/protocol/schema#authenticate */ readonly handleAuthenticate: ( handler: ( request: AcpSchema.AuthenticateRequest, ) => Effect.Effect<AcpSchema.AuthenticateResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleLogout: ( handler: ( request: AcpSchema.LogoutRequest, ) => Effect.Effect<AcpSchema.LogoutResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleCreateSession: ( handler: ( request: AcpSchema.NewSessionRequest, ) => Effect.Effect<AcpSchema.NewSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleLoadSession: ( handler: ( request: AcpSchema.LoadSessionRequest, ) => Effect.Effect<AcpSchema.LoadSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleListSessions: ( handler: ( request: AcpSchema.ListSessionsRequest, ) => Effect.Effect<AcpSchema.ListSessionsResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleForkSession: ( handler: ( request: AcpSchema.ForkSessionRequest, ) => Effect.Effect<AcpSchema.ForkSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleResumeSession: ( handler: ( request: AcpSchema.ResumeSessionRequest, ) => Effect.Effect<AcpSchema.ResumeSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleCloseSession: ( handler: ( request: AcpSchema.CloseSessionRequest, ) => Effect.Effect<AcpSchema.CloseSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleSetSessionModel: ( handler: ( request: AcpSchema.SetSessionModelRequest, ) => Effect.Effect<AcpSchema.SetSessionModelResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleSetSessionConfigOption: ( handler: ( request: AcpSchema.SetSessionConfigOptionRequest, ) => Effect.Effect<AcpSchema.SetSessionConfigOptionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handlePrompt: ( handler: ( request: AcpSchema.PromptRequest, ) => Effect.Effect<AcpSchema.PromptResponse, AcpError.AcpError>, ) => Effect.Effect<void>; /** * Registers a handler for `session/cancel`. * @see https://agentclientprotocol.com/protocol/schema#session/cancel */ readonly handleCancel: ( handler: ( notification: AcpSchema.CancelNotification, ) => Effect.Effect<void, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleUnknownExtRequest: ( handler: (method: string, params: unknown) => Effect.Effect<unknown, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleUnknownExtNotification: ( handler: (method: string, params: unknown) => Effect.Effect<void, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleExtRequest: <A, I>( method: string, payload: Schema.Codec<A, I>, handler: (payload: A) => Effect.Effect<unknown, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleExtNotification: <A, I>( method: string, payload: Schema.Codec<A, I>, handler: (payload: A) => Effect.Effect<void, AcpError.AcpError>, ) => Effect.Effect<void>; }>()("effect-acp/agent/AcpAgent") {} interface AcpCoreAgentRequestHandlers { initialize?: ( request: AcpSchema.InitializeRequest, ) => Effect.Effect<AcpSchema.InitializeResponse, AcpError.AcpError>; authenticate?: ( request: AcpSchema.AuthenticateRequest, ) => Effect.Effect<AcpSchema.AuthenticateResponse, AcpError.AcpError>; logout?: ( request: AcpSchema.LogoutRequest, ) => Effect.Effect<AcpSchema.LogoutResponse, AcpError.AcpError>; createSession?: ( request: AcpSchema.NewSessionRequest, ) => Effect.Effect<AcpSchema.NewSessionResponse, AcpError.AcpError>; loadSession?: ( request: AcpSchema.LoadSessionRequest, ) => Effect.Effect<AcpSchema.LoadSessionResponse, AcpError.AcpError>; listSessions?: ( request: AcpSchema.ListSessionsRequest, ) => Effect.Effect<AcpSchema.ListSessionsResponse, AcpError.AcpError>; forkSession?: ( request: AcpSchema.ForkSessionRequest, ) => Effect.Effect<AcpSchema.ForkSessionResponse, AcpError.AcpError>; resumeSession?: ( request: AcpSchema.ResumeSessionRequest, ) => Effect.Effect<AcpSchema.ResumeSessionResponse, AcpError.AcpError>; closeSession?: ( request: AcpSchema.CloseSessionRequest, ) => Effect.Effect<AcpSchema.CloseSessionResponse, AcpError.AcpError>; setSessionModel?: ( request: AcpSchema.SetSessionModelRequest, ) => Effect.Effect<AcpSchema.SetSessionModelResponse, AcpError.AcpError>; setSessionConfigOption?: ( request: AcpSchema.SetSessionConfigOptionRequest, ) => Effect.Effect<AcpSchema.SetSessionConfigOptionResponse, AcpError.AcpError>; prompt?: ( request: AcpSchema.PromptRequest, ) => Effect.Effect<AcpSchema.PromptResponse, AcpError.AcpError>;} const decodeCancelNotification = Schema.decodeUnknownEffect(AcpSchema.CancelNotification); /** @public Service construction is part of the canonical Effect module API. */export const make = Effect.fn("effect-acp/AcpAgent.make")(function* ( stdio: Stdio.Stdio, options: AcpAgentOptions = {},): Effect.fn.Return<AcpAgent["Service"], never, Scope.Scope> { const coreHandlers: AcpCoreAgentRequestHandlers = {}; const cancelHandlers: Array< (notification: AcpSchema.CancelNotification) => Effect.Effect<void, AcpError.AcpError> > = []; const extRequestHandlers = new Map< string, (params: unknown) => Effect.Effect<unknown, AcpError.AcpError> >(); const extNotificationHandlers = new Map< string, (params: unknown) => Effect.Effect<void, AcpError.AcpError> >(); let unknownExtRequestHandler: | ((method: string, params: unknown) => Effect.Effect<unknown, AcpError.AcpError>) | undefined; let unknownExtNotificationHandler: | ((method: string, params: unknown) => Effect.Effect<void, AcpError.AcpError>) | undefined; const transport = yield* AcpProtocol.makeAcpPatchedProtocol({ stdio, serverRequestMethods: new Set(AcpRpcs.AgentRpcs.requests.keys()), ...(options.logIncoming !== undefined ? { logIncoming: options.logIncoming } : {}), ...(options.logOutgoing !== undefined ? { logOutgoing: options.logOutgoing } : {}), ...(options.logger ? { logger: options.logger } : {}), onNotification: (notification) => { if ( notification._tag === "ExtNotification" && notification.method === AGENT_METHODS.session_cancel ) { return decodeCancelNotification(notification.params).pipe( Effect.mapError((error) => AcpError.AcpProtocolParseError.fromSchemaError( "decode-notification-payload", AGENT_METHODS.session_cancel, error, ), ), Effect.flatMap((decoded) => Effect.forEach(cancelHandlers, (handler) => handler(decoded), { discard: true }), ), ); } if (notification._tag !== "ExtNotification") { return Effect.void; } const handler = extNotificationHandlers.get(notification.method); if (handler) { return handler(notification.params); } return unknownExtNotificationHandler ? unknownExtNotificationHandler(notification.method, notification.params) : Effect.void; }, onExtRequest: (method, params) => { const handler = extRequestHandlers.get(method); if (handler) { return handler(params); } return unknownExtRequestHandler ? unknownExtRequestHandler(method, params) : Effect.fail(AcpError.AcpRequestError.methodNotFound(method)); }, }); const agentHandlerLayer = AcpRpcs.AgentRpcs.toLayer( AcpRpcs.AgentRpcs.of({ [AGENT_METHODS.initialize]: (payload) => runHandler(coreHandlers.initialize, payload, AGENT_METHODS.initialize), [AGENT_METHODS.authenticate]: (payload) => runHandler(coreHandlers.authenticate, payload, AGENT_METHODS.authenticate), [AGENT_METHODS.logout]: (payload) => runHandler(coreHandlers.logout, payload, AGENT_METHODS.logout), [AGENT_METHODS.session_new]: (payload) => runHandler(coreHandlers.createSession, payload, AGENT_METHODS.session_new), [AGENT_METHODS.session_load]: (payload) => runHandler(coreHandlers.loadSession, payload, AGENT_METHODS.session_load), [AGENT_METHODS.session_list]: (payload) => runHandler(coreHandlers.listSessions, payload, AGENT_METHODS.session_list), [AGENT_METHODS.session_fork]: (payload) => runHandler(coreHandlers.forkSession, payload, AGENT_METHODS.session_fork), [AGENT_METHODS.session_resume]: (payload) => runHandler(coreHandlers.resumeSession, payload, AGENT_METHODS.session_resume), [AGENT_METHODS.session_close]: (payload) => runHandler(coreHandlers.closeSession, payload, AGENT_METHODS.session_close), [AGENT_METHODS.session_set_model]: (payload) => runHandler(coreHandlers.setSessionModel, payload, AGENT_METHODS.session_set_model), [AGENT_METHODS.session_set_config_option]: (payload) => runHandler( coreHandlers.setSessionConfigOption, payload, AGENT_METHODS.session_set_config_option, ), [AGENT_METHODS.session_prompt]: (payload) => runHandler(coreHandlers.prompt, payload, AGENT_METHODS.session_prompt), }), ); yield* RpcServer.make(AcpRpcs.AgentRpcs).pipe( Effect.provideService(RpcServer.Protocol, transport.serverProtocol), Effect.provide(agentHandlerLayer), Effect.forkScoped, ); let nextRpcRequestId = 2 ** 32; const rpc = yield* RpcClient.make(AcpRpcs.ClientRpcs, { generateRequestId: () => RpcMessage.RequestId(nextRpcRequestId++), }).pipe(Effect.provideService(RpcClient.Protocol, transport.clientProtocol)); return AcpAgent.of({ raw: { notifications: transport.incoming, request: transport.request, notify: transport.notify, }, client: { requestPermission: (payload) => callRpc( CLIENT_METHODS.session_request_permission, rpc[CLIENT_METHODS.session_request_permission](payload), ), elicit: (payload) => callRpc( CLIENT_METHODS.session_elicitation, rpc[CLIENT_METHODS.session_elicitation](payload), ), readTextFile: (payload) => callRpc(CLIENT_METHODS.fs_read_text_file, rpc[CLIENT_METHODS.fs_read_text_file](payload)), writeTextFile: (payload) => callRpc(CLIENT_METHODS.fs_write_text_file, rpc[CLIENT_METHODS.fs_write_text_file](payload)), createTerminal: (payload) => callRpc(CLIENT_METHODS.terminal_create, rpc[CLIENT_METHODS.terminal_create](payload)).pipe( Effect.map( (response) => ({ sessionId: payload.sessionId, terminalId: response.terminalId, output: callRpc( CLIENT_METHODS.terminal_output, rpc[CLIENT_METHODS.terminal_output]({ sessionId: payload.sessionId, terminalId: response.terminalId, }), ), waitForExit: callRpc( CLIENT_METHODS.terminal_wait_for_exit, rpc[CLIENT_METHODS.terminal_wait_for_exit]({ sessionId: payload.sessionId, terminalId: response.terminalId, }), ), kill: callRpc( CLIENT_METHODS.terminal_kill, rpc[CLIENT_METHODS.terminal_kill]({ sessionId: payload.sessionId, terminalId: response.terminalId, }), ), release: callRpc( CLIENT_METHODS.terminal_release, rpc[CLIENT_METHODS.terminal_release]({ sessionId: payload.sessionId, terminalId: response.terminalId, }), ), }) satisfies AcpTerminal.AcpTerminal, ), ), sessionUpdate: (payload) => transport.notify(CLIENT_METHODS.session_update, payload), elicitationComplete: (payload) => transport.notify(CLIENT_METHODS.session_elicitation_complete, payload), extRequest: transport.request, extNotification: transport.notify, }, handleInitialize: (handler) => Effect.suspend(() => { coreHandlers.initialize = handler; return Effect.void; }), handleAuthenticate: (handler) => Effect.suspend(() => { coreHandlers.authenticate = handler; return Effect.void; }), handleLogout: (handler) => Effect.suspend(() => { coreHandlers.logout = handler; return Effect.void; }), handleCreateSession: (handler) => Effect.suspend(() => { coreHandlers.createSession = handler; return Effect.void; }), handleLoadSession: (handler) => Effect.suspend(() => { coreHandlers.loadSession = handler; return Effect.void; }), handleListSessions: (handler) => Effect.suspend(() => { coreHandlers.listSessions = handler; return Effect.void; }), handleForkSession: (handler) => Effect.suspend(() => { coreHandlers.forkSession = handler; return Effect.void; }), handleResumeSession: (handler) => Effect.suspend(() => { coreHandlers.resumeSession = handler; return Effect.void; }), handleCloseSession: (handler) => Effect.suspend(() => { coreHandlers.closeSession = handler; return Effect.void; }), handleSetSessionModel: (handler) => Effect.suspend(() => { coreHandlers.setSessionModel = handler; return Effect.void; }), handleSetSessionConfigOption: (handler) => Effect.suspend(() => { coreHandlers.setSessionConfigOption = handler; return Effect.void; }), handlePrompt: (handler) => Effect.suspend(() => { coreHandlers.prompt = handler; return Effect.void; }), handleCancel: (handler) => Effect.suspend(() => { cancelHandlers.push(handler);Lines 1–500 of 538. Load more
import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; import * as Stdio from "effect/Stdio"; import * as RpcClient from "effect/unstable/rpc/RpcClient"; import * as RpcMessage from "effect/unstable/rpc/RpcMessage"; import * as RpcServer from "effect/unstable/rpc/RpcServer"; import * as AcpSchema from "./_generated/schema.gen.ts"; import { AGENT_METHODS, CLIENT_METHODS } from "./_generated/meta.gen.ts"; import * as AcpError from "./errors.ts"; import * as AcpProtocol from "./protocol.ts"; import * as AcpRpcs from "./rpc.ts"; import { callRpc, decodeExtNotificationRegistration, decodeExtRequestRegistration, runHandler, } from "./_internal/shared.ts"; import * as AcpTerminal from "./terminal.ts"; export interface AcpAgentOptions { readonly logIncoming?: boolean; readonly logOutgoing?: boolean; readonly logger?: (event: AcpProtocol.AcpProtocolLogEvent) => Effect.Effect<void, never>; } export class AcpAgent extends Context.Service< AcpAgent, { readonly raw: { /** * Stream of inbound ACP notifications observed on the connection. */ readonly notifications: Stream.Stream<AcpProtocol.AcpIncomingNotification>; /** * Sends a generic ACP extension request. * @see https://agentclientprotocol.com/protocol/extensibility */ readonly request: ( method: string, payload: unknown, ) => Effect.Effect<unknown, AcpError.AcpError>; /** * Sends a generic ACP extension notification. * @see https://agentclientprotocol.com/protocol/extensibility */ readonly notify: (method: string, payload: unknown) => Effect.Effect<void, AcpError.AcpError>; }; readonly client: { /** * Requests client permission for an operation. * @see https://agentclientprotocol.com/protocol/schema#session/request_permission */ readonly requestPermission: ( payload: AcpSchema.RequestPermissionRequest, ) => Effect.Effect<AcpSchema.RequestPermissionResponse, AcpError.AcpError>; /** * Requests structured user input from the client. * @see https://agentclientprotocol.com/protocol/schema#session/elicitation */ readonly elicit: ( payload: AcpSchema.ElicitationRequest, ) => Effect.Effect<AcpSchema.ElicitationResponse, AcpError.AcpError>; /** * Requests file contents from the client. * @see https://agentclientprotocol.com/protocol/schema#fs/read_text_file */ readonly readTextFile: ( payload: AcpSchema.ReadTextFileRequest, ) => Effect.Effect<AcpSchema.ReadTextFileResponse, AcpError.AcpError>; /** * Writes a text file through the client. * @see https://agentclientprotocol.com/protocol/schema#fs/write_text_file */ readonly writeTextFile: ( payload: AcpSchema.WriteTextFileRequest, ) => Effect.Effect<AcpSchema.WriteTextFileResponse, AcpError.AcpError>; /** * Creates a terminal on the client side. * @see https://agentclientprotocol.com/protocol/schema#terminal/create */ readonly createTerminal: ( payload: AcpSchema.CreateTerminalRequest, ) => Effect.Effect<AcpTerminal.AcpTerminal, AcpError.AcpError>; /** * Sends a `session/update` notification to the client. * @see https://agentclientprotocol.com/protocol/schema#session/update */ readonly sessionUpdate: ( payload: AcpSchema.SessionNotification, ) => Effect.Effect<void, AcpError.AcpError>; /** * Sends a `session/elicitation/complete` notification to the client. * @see https://agentclientprotocol.com/protocol/schema#session/elicitation/complete */ readonly elicitationComplete: ( payload: AcpSchema.ElicitationCompleteNotification, ) => Effect.Effect<void, AcpError.AcpError>; /** * Sends an ACP extension request to the client. * @see https://agentclientprotocol.com/protocol/extensibility */ readonly extRequest: ( method: string, payload: unknown, ) => Effect.Effect<unknown, AcpError.AcpError>; /** * Sends an ACP extension notification to the client. * @see https://agentclientprotocol.com/protocol/extensibility */ readonly extNotification: ( method: string, payload: unknown, ) => Effect.Effect<void, AcpError.AcpError>; }; /** * Registers a handler for `initialize`. * @see https://agentclientprotocol.com/protocol/schema#initialize */ readonly handleInitialize: ( handler: ( request: AcpSchema.InitializeRequest, ) => Effect.Effect<AcpSchema.InitializeResponse, AcpError.AcpError>, ) => Effect.Effect<void>; /** * Registers a handler for `authenticate`. * @see https://agentclientprotocol.com/protocol/schema#authenticate */ readonly handleAuthenticate: ( handler: ( request: AcpSchema.AuthenticateRequest, ) => Effect.Effect<AcpSchema.AuthenticateResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleLogout: ( handler: ( request: AcpSchema.LogoutRequest, ) => Effect.Effect<AcpSchema.LogoutResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleCreateSession: ( handler: ( request: AcpSchema.NewSessionRequest, ) => Effect.Effect<AcpSchema.NewSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleLoadSession: ( handler: ( request: AcpSchema.LoadSessionRequest, ) => Effect.Effect<AcpSchema.LoadSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleListSessions: ( handler: ( request: AcpSchema.ListSessionsRequest, ) => Effect.Effect<AcpSchema.ListSessionsResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleForkSession: ( handler: ( request: AcpSchema.ForkSessionRequest, ) => Effect.Effect<AcpSchema.ForkSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleResumeSession: ( handler: ( request: AcpSchema.ResumeSessionRequest, ) => Effect.Effect<AcpSchema.ResumeSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleCloseSession: ( handler: ( request: AcpSchema.CloseSessionRequest, ) => Effect.Effect<AcpSchema.CloseSessionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleSetSessionModel: ( handler: ( request: AcpSchema.SetSessionModelRequest, ) => Effect.Effect<AcpSchema.SetSessionModelResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleSetSessionConfigOption: ( handler: ( request: AcpSchema.SetSessionConfigOptionRequest, ) => Effect.Effect<AcpSchema.SetSessionConfigOptionResponse, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handlePrompt: ( handler: ( request: AcpSchema.PromptRequest, ) => Effect.Effect<AcpSchema.PromptResponse, AcpError.AcpError>, ) => Effect.Effect<void>; /** * Registers a handler for `session/cancel`. * @see https://agentclientprotocol.com/protocol/schema#session/cancel */ readonly handleCancel: ( handler: ( notification: AcpSchema.CancelNotification, ) => Effect.Effect<void, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleUnknownExtRequest: ( handler: (method: string, params: unknown) => Effect.Effect<unknown, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleUnknownExtNotification: ( handler: (method: string, params: unknown) => Effect.Effect<void, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleExtRequest: <A, I>( method: string, payload: Schema.Codec<A, I>, handler: (payload: A) => Effect.Effect<unknown, AcpError.AcpError>, ) => Effect.Effect<void>; readonly handleExtNotification: <A, I>( method: string, payload: Schema.Codec<A, I>, handler: (payload: A) => Effect.Effect<void, AcpError.AcpError>, ) => Effect.Effect<void>; } >()("effect-acp/agent/AcpAgent") {} interface AcpCoreAgentRequestHandlers { initialize?: ( request: AcpSchema.InitializeRequest, ) => Effect.Effect<AcpSchema.InitializeResponse, AcpError.AcpError>; authenticate?: ( request: AcpSchema.AuthenticateRequest, ) => Effect.Effect<AcpSchema.AuthenticateResponse, AcpError.AcpError>; logout?: ( request: AcpSchema.LogoutRequest, ) => Effect.Effect<AcpSchema.LogoutResponse, AcpError.AcpError>; createSession?: ( request: AcpSchema.NewSessionRequest, ) => Effect.Effect<AcpSchema.NewSessionResponse, AcpError.AcpError>; loadSession?: ( request: AcpSchema.LoadSessionRequest, ) => Effect.Effect<AcpSchema.LoadSessionResponse, AcpError.AcpError>; listSessions?: ( request: AcpSchema.ListSessionsRequest, ) => Effect.Effect<AcpSchema.ListSessionsResponse, AcpError.AcpError>; forkSession?: ( request: AcpSchema.ForkSessionRequest, ) => Effect.Effect<AcpSchema.ForkSessionResponse, AcpError.AcpError>; resumeSession?: ( request: AcpSchema.ResumeSessionRequest, ) => Effect.Effect<AcpSchema.ResumeSessionResponse, AcpError.AcpError>; closeSession?: ( request: AcpSchema.CloseSessionRequest, ) => Effect.Effect<AcpSchema.CloseSessionResponse, AcpError.AcpError>; setSessionModel?: ( request: AcpSchema.SetSessionModelRequest, ) => Effect.Effect<AcpSchema.SetSessionModelResponse, AcpError.AcpError>; setSessionConfigOption?: ( request: AcpSchema.SetSessionConfigOptionRequest, ) => Effect.Effect<AcpSchema.SetSessionConfigOptionResponse, AcpError.AcpError>; prompt?: ( request: AcpSchema.PromptRequest, ) => Effect.Effect<AcpSchema.PromptResponse, AcpError.AcpError>; } const decodeCancelNotification = Schema.decodeUnknownEffect(AcpSchema.CancelNotification); /** @public Service construction is part of the canonical Effect module API. */ export const make = Effect.fn("effect-acp/AcpAgent.make")(function* ( stdio: Stdio.Stdio, options: AcpAgentOptions = {}, ): Effect.fn.Return<AcpAgent["Service"], never, Scope.Scope> { const coreHandlers: AcpCoreAgentRequestHandlers = {}; const cancelHandlers: Array< (notification: AcpSchema.CancelNotification) => Effect.Effect<void, AcpError.AcpError> > = []; const extRequestHandlers = new Map< string, (params: unknown) => Effect.Effect<unknown, AcpError.AcpError> >(); const extNotificationHandlers = new Map< string, (params: unknown) => Effect.Effect<void, AcpError.AcpError> >(); let unknownExtRequestHandler: | ((method: string, params: unknown) => Effect.Effect<unknown, AcpError.AcpError>) | undefined; let unknownExtNotificationHandler: | ((method: string, params: unknown) => Effect.Effect<void, AcpError.AcpError>) | undefined; const transport = yield* AcpProtocol.makeAcpPatchedProtocol({ stdio, serverRequestMethods: new Set(AcpRpcs.AgentRpcs.requests.keys()), ...(options.logIncoming !== undefined ? { logIncoming: options.logIncoming } : {}), ...(options.logOutgoing !== undefined ? { logOutgoing: options.logOutgoing } : {}), ...(options.logger ? { logger: options.logger } : {}), onNotification: (notification) => { if ( notification._tag === "ExtNotification" && notification.method === AGENT_METHODS.session_cancel ) { return decodeCancelNotification(notification.params).pipe( Effect.mapError((error) => AcpError.AcpProtocolParseError.fromSchemaError( "decode-notification-payload", AGENT_METHODS.session_cancel, error, ), ), Effect.flatMap((decoded) => Effect.forEach(cancelHandlers, (handler) => handler(decoded), { discard: true }), ), ); } if (notification._tag !== "ExtNotification") { return Effect.void; } const handler = extNotificationHandlers.get(notification.method); if (handler) { return handler(notification.params); } return unknownExtNotificationHandler ? unknownExtNotificationHandler(notification.method, notification.params) : Effect.void; }, onExtRequest: (method, params) => { const handler = extRequestHandlers.get(method); if (handler) { return handler(params); } return unknownExtRequestHandler ? unknownExtRequestHandler(method, params) : Effect.fail(AcpError.AcpRequestError.methodNotFound(method)); }, }); const agentHandlerLayer = AcpRpcs.AgentRpcs.toLayer( AcpRpcs.AgentRpcs.of({ [AGENT_METHODS.initialize]: (payload) => runHandler(coreHandlers.initialize, payload, AGENT_METHODS.initialize), [AGENT_METHODS.authenticate]: (payload) => runHandler(coreHandlers.authenticate, payload, AGENT_METHODS.authenticate), [AGENT_METHODS.logout]: (payload) => runHandler(coreHandlers.logout, payload, AGENT_METHODS.logout), [AGENT_METHODS.session_new]: (payload) => runHandler(coreHandlers.createSession, payload, AGENT_METHODS.session_new), [AGENT_METHODS.session_load]: (payload) => runHandler(coreHandlers.loadSession, payload, AGENT_METHODS.session_load), [AGENT_METHODS.session_list]: (payload) => runHandler(coreHandlers.listSessions, payload, AGENT_METHODS.session_list), [AGENT_METHODS.session_fork]: (payload) => runHandler(coreHandlers.forkSession, payload, AGENT_METHODS.session_fork), [AGENT_METHODS.session_resume]: (payload) => runHandler(coreHandlers.resumeSession, payload, AGENT_METHODS.session_resume), [AGENT_METHODS.session_close]: (payload) => runHandler(coreHandlers.closeSession, payload, AGENT_METHODS.session_close), [AGENT_METHODS.session_set_model]: (payload) => runHandler(coreHandlers.setSessionModel, payload, AGENT_METHODS.session_set_model), [AGENT_METHODS.session_set_config_option]: (payload) => runHandler( coreHandlers.setSessionConfigOption, payload, AGENT_METHODS.session_set_config_option, ), [AGENT_METHODS.session_prompt]: (payload) => runHandler(coreHandlers.prompt, payload, AGENT_METHODS.session_prompt), }), ); yield* RpcServer.make(AcpRpcs.AgentRpcs).pipe( Effect.provideService(RpcServer.Protocol, transport.serverProtocol), Effect.provide(agentHandlerLayer), Effect.forkScoped, ); let nextRpcRequestId = 2 ** 32; const rpc = yield* RpcClient.make(AcpRpcs.ClientRpcs, { generateRequestId: () => RpcMessage.RequestId(nextRpcRequestId++), }).pipe(Effect.provideService(RpcClient.Protocol, transport.clientProtocol)); return AcpAgent.of({ raw: { notifications: transport.incoming, request: transport.request, notify: transport.notify, }, client: { requestPermission: (payload) => callRpc( CLIENT_METHODS.session_request_permission, rpc[CLIENT_METHODS.session_request_permission](payload), ), elicit: (payload) => callRpc( CLIENT_METHODS.session_elicitation, rpc[CLIENT_METHODS.session_elicitation](payload), ), readTextFile: (payload) => callRpc(CLIENT_METHODS.fs_read_text_file, rpc[CLIENT_METHODS.fs_read_text_file](payload)), writeTextFile: (payload) => callRpc(CLIENT_METHODS.fs_write_text_file, rpc[CLIENT_METHODS.fs_write_text_file](payload)), createTerminal: (payload) => callRpc(CLIENT_METHODS.terminal_create, rpc[CLIENT_METHODS.terminal_create](payload)).pipe( Effect.map( (response) => ({ sessionId: payload.sessionId, terminalId: response.terminalId, output: callRpc( CLIENT_METHODS.terminal_output, rpc[CLIENT_METHODS.terminal_output]({ sessionId: payload.sessionId, terminalId: response.terminalId, }), ), waitForExit: callRpc( CLIENT_METHODS.terminal_wait_for_exit, rpc[CLIENT_METHODS.terminal_wait_for_exit]({ sessionId: payload.sessionId, terminalId: response.terminalId, }), ), kill: callRpc( CLIENT_METHODS.terminal_kill, rpc[CLIENT_METHODS.terminal_kill]({ sessionId: payload.sessionId, terminalId: response.terminalId, }), ), release: callRpc( CLIENT_METHODS.terminal_release, rpc[CLIENT_METHODS.terminal_release]({ sessionId: payload.sessionId, terminalId: response.terminalId, }), ), }) satisfies AcpTerminal.AcpTerminal, ), ), sessionUpdate: (payload) => transport.notify(CLIENT_METHODS.session_update, payload), elicitationComplete: (payload) => transport.notify(CLIENT_METHODS.session_elicitation_complete, payload), extRequest: transport.request, extNotification: transport.notify, }, handleInitialize: (handler) => Effect.suspend(() => { coreHandlers.initialize = handler; return Effect.void; }), handleAuthenticate: (handler) => Effect.suspend(() => { coreHandlers.authenticate = handler; return Effect.void; }), handleLogout: (handler) => Effect.suspend(() => { coreHandlers.logout = handler; return Effect.void; }), handleCreateSession: (handler) => Effect.suspend(() => { coreHandlers.createSession = handler; return Effect.void; }), handleLoadSession: (handler) => Effect.suspend(() => { coreHandlers.loadSession = handler; return Effect.void; }), handleListSessions: (handler) => Effect.suspend(() => { coreHandlers.listSessions = handler; return Effect.void; }), handleForkSession: (handler) => Effect.suspend(() => { coreHandlers.forkSession = handler; return Effect.void; }), handleResumeSession: (handler) => Effect.suspend(() => { coreHandlers.resumeSession = handler; return Effect.void; }), handleCloseSession: (handler) => Effect.suspend(() => { coreHandlers.closeSession = handler; return Effect.void; }), handleSetSessionModel: (handler) => Effect.suspend(() => { coreHandlers.setSessionModel = handler; return Effect.void; }), handleSetSessionConfigOption: (handler) => Effect.suspend(() => { coreHandlers.setSessionConfigOption = handler; return Effect.void; }), handlePrompt: (handler) => Effect.suspend(() => { coreHandlers.prompt = handler; return Effect.void; }), handleCancel: (handler) => Effect.suspend(() => { cancelHandlers.push(handler);