1import * as AcpCompat from "./compat.ts";
2import * as Rpc from "effect/rpc/Rpc";
3import * as RpcGroup from "effect/rpc/RpcGroup";
4import * as Schema from "effect/Schema";
5
6import * as AcpSchema from "./_generated/schema.gen.ts";
7import * as AcpSchemaV1 from "./_generated/schema-v1.gen.ts";
8import { AGENT_METHODS, CLIENT_METHODS } from "./_generated/meta.gen.ts";
9
10export const V1_AGENT_METHODS = {
11 authenticate: "authenticate",
12 logout: "logout",
13 session_load: "session/load",
14 session_set_model: "session/set_model",
15 session_set_mode: "session/set_mode",
16} as const;
17
18export const V1_CLIENT_METHODS = {
19 session_elicitation: "session/elicitation",
20 unstable_session_elicitation: "_session/elicitation",
21 session_elicitation_complete: "session/elicitation/complete",
22 fs_read_text_file: "fs/read_text_file",
23 fs_write_text_file: "fs/write_text_file",
24 terminal_create: "terminal/create",
25 terminal_output: "terminal/output",
26 terminal_release: "terminal/release",
27 terminal_wait_for_exit: "terminal/wait_for_exit",
28 terminal_kill: "terminal/kill",
29} as const;
30
31const InitializeRpc = Rpc.make(AGENT_METHODS.initialize, {
32 payload: AcpSchema.InitializeRequest,
33 success: AcpSchema.InitializeResponse,
34 error: AcpSchema.Error,
35});
36
37const LoginRpc = Rpc.make(AGENT_METHODS.auth_login, {
38 payload: AcpSchema.LoginAuthRequest,
39 success: AcpSchema.LoginAuthResponse,
40 error: AcpSchema.Error,
41});
42
43const LogoutRpc = Rpc.make(AGENT_METHODS.auth_logout, {
44 payload: AcpSchema.LogoutAuthRequest,
45 success: AcpSchema.LogoutAuthResponse,
46 error: AcpSchema.Error,
47});
48
49const NewSessionRpc = Rpc.make(AGENT_METHODS.session_new, {
50 payload: AcpSchema.NewSessionRequest,
51 success: AcpSchema.NewSessionResponse,
52 error: AcpSchema.Error,
53});
54
55const ListSessionsRpc = Rpc.make(AGENT_METHODS.session_list, {
56 payload: AcpSchema.ListSessionsRequest,
57 success: AcpSchema.ListSessionsResponse,
58 error: AcpSchema.Error,
59});
60
61const ForkSessionRpc = Rpc.make(AGENT_METHODS.session_fork, {
62 payload: AcpSchema.ForkSessionRequest,
63 success: AcpSchema.ForkSessionResponse,
64 error: AcpSchema.Error,
65});
66
67const ResumeSessionRpc = Rpc.make(AGENT_METHODS.session_resume, {
68 payload: AcpSchema.ResumeSessionRequest,
69 success: AcpSchema.ResumeSessionResponse,
70 error: AcpSchema.Error,
71});
72
73const CloseSessionRpc = Rpc.make(AGENT_METHODS.session_close, {
74 payload: AcpSchema.CloseSessionRequest,
75 success: AcpSchema.CloseSessionResponse,
76 error: AcpSchema.Error,
77});
78
79const DeleteSessionRpc = Rpc.make(AGENT_METHODS.session_delete, {
80 payload: AcpSchema.DeleteSessionRequest,
81 success: AcpSchema.DeleteSessionResponse,
82 error: AcpSchema.Error,
83});
84
85const ListProvidersRpc = Rpc.make(AGENT_METHODS.providers_list, {
86 payload: AcpSchema.ListProvidersRequest,
87 success: AcpSchema.ListProvidersResponse,
88 error: AcpSchema.Error,
89});
90
91const SetProviderRpc = Rpc.make(AGENT_METHODS.providers_set, {
92 payload: AcpSchema.SetProviderRequest,
93 success: AcpSchema.SetProviderResponse,
94 error: AcpSchema.Error,
95});
96
97const DisableProviderRpc = Rpc.make(AGENT_METHODS.providers_disable, {
98 payload: AcpSchema.DisableProviderRequest,
99 success: AcpSchema.DisableProviderResponse,
100 error: AcpSchema.Error,
101});
102
103const PromptRpc = Rpc.make(AGENT_METHODS.session_prompt, {
104 payload: AcpSchema.PromptRequest,
105 success: AcpSchema.PromptResponse,
106 error: AcpSchema.Error,
107});
108
109const SetSessionConfigOptionRpc = Rpc.make(AGENT_METHODS.session_set_config_option, {
110 payload: AcpSchema.SetSessionConfigOptionRequest,
111 success: AcpSchema.SetSessionConfigOptionResponse,
112 error: AcpSchema.Error,
113});
114
115const RequestPermissionRpc = Rpc.make(CLIENT_METHODS.session_request_permission, {
116 payload: AcpSchema.RequestPermissionRequest,
117 success: AcpSchema.RequestPermissionResponse,
118 error: AcpSchema.Error,
119});
120
121const ElicitationRpc = Rpc.make(CLIENT_METHODS.elicitation_create, {
122 payload: AcpSchema.CreateElicitationRequest,
123 success: AcpSchema.CreateElicitationResponse,
124 error: AcpSchema.Error,
125});
126
127export const AgentRpcs = RpcGroup.make(
128 InitializeRpc,
129 LoginRpc,
130 LogoutRpc,
131 NewSessionRpc,
132 ListSessionsRpc,
133 ForkSessionRpc,
134 ResumeSessionRpc,
135 CloseSessionRpc,
136 DeleteSessionRpc,
137 ListProvidersRpc,
138 SetProviderRpc,
139 DisableProviderRpc,
140 PromptRpc,
141 SetSessionConfigOptionRpc,
142);
143
144// Registry agents transition independently. The compatibility groups accept
145// both protocol generations on the wire while the public client normalizes
146// them into one provider-neutral surface after initialize negotiates v1 or v2.
147const CompatInitializeRpc = Rpc.make(AGENT_METHODS.initialize, {
148 // The negotiation request intentionally carries both v1 and v2 capability
149 // field names. A union encoder would select one branch and discard the
150 // other generation's fields before they reach the agent.
151 payload: Schema.Unknown,
152 success: Schema.Union([AcpSchema.InitializeResponse, AcpSchemaV1.InitializeResponse]),
153 error: AcpSchema.Error,
154});
155
156const CompatAuthenticateV1Rpc = Rpc.make(V1_AGENT_METHODS.authenticate, {
157 payload: AcpSchemaV1.AuthenticateRequest,
158 success: AcpSchemaV1.AuthenticateResponse,
159 error: AcpSchemaV1.Error,
160});
161
162const CompatLogoutV1Rpc = Rpc.make(V1_AGENT_METHODS.logout, {
163 payload: AcpSchemaV1.LogoutRequest,
164 success: AcpSchemaV1.LogoutResponse,
165 error: AcpSchemaV1.Error,
166});
167
168const CompatNewSessionRpc = Rpc.make(AGENT_METHODS.session_new, {
169 payload: Schema.Union([AcpSchema.NewSessionRequest, AcpSchemaV1.NewSessionRequest]),
170 success: Schema.Union([
171 Schema.Struct({
172 ...AcpSchemaV1.NewSessionResponse.fields,
173 models: Schema.optionalKey(Schema.NullOr(AcpCompat.SessionModelState)),
174 }),
175 AcpSchema.NewSessionResponse,
176 ]),
177 error: AcpSchema.Error,
178});
179
180const CompatLoadSessionV1Rpc = Rpc.make(V1_AGENT_METHODS.session_load, {
181 payload: AcpSchemaV1.LoadSessionRequest,
182 success: Schema.Struct({
183 ...AcpSchemaV1.LoadSessionResponse.fields,
184 models: Schema.optionalKey(Schema.NullOr(AcpCompat.SessionModelState)),
185 }),
186 error: AcpSchemaV1.Error,
187});
188
189const CompatListSessionsRpc = Rpc.make(AGENT_METHODS.session_list, {
190 payload: Schema.Union([AcpSchema.ListSessionsRequest, AcpSchemaV1.ListSessionsRequest]),
191 success: Schema.Union([AcpSchemaV1.ListSessionsResponse, AcpSchema.ListSessionsResponse]),
192 error: AcpSchema.Error,
193});
194
195const CompatForkSessionRpc = Rpc.make(AGENT_METHODS.session_fork, {
196 payload: Schema.Union([AcpSchema.ForkSessionRequest, AcpSchemaV1.ForkSessionRequest]),
197 success: Schema.Union([
198 Schema.Struct({
199 ...AcpSchemaV1.ForkSessionResponse.fields,
200 models: Schema.optionalKey(Schema.NullOr(AcpCompat.SessionModelState)),
201 }),
202 AcpSchema.ForkSessionResponse,
203 ]),
204 error: AcpSchema.Error,
205});
206
207const CompatResumeSessionRpc = Rpc.make(AGENT_METHODS.session_resume, {
208 payload: Schema.Union([AcpSchema.ResumeSessionRequest, AcpSchemaV1.ResumeSessionRequest]),
209 success: Schema.Union([
210 Schema.Struct({
211 ...AcpSchemaV1.ResumeSessionResponse.fields,
212 models: Schema.optionalKey(Schema.NullOr(AcpCompat.SessionModelState)),
213 }),
214 AcpSchema.ResumeSessionResponse,
215 ]),
216 error: AcpSchema.Error,
217});
218
219const CompatCloseSessionRpc = Rpc.make(AGENT_METHODS.session_close, {
220 payload: Schema.Union([AcpSchema.CloseSessionRequest, AcpSchemaV1.CloseSessionRequest]),
221 success: Schema.Union([AcpSchema.CloseSessionResponse, AcpSchemaV1.CloseSessionResponse]),
222 error: AcpSchema.Error,
223});
224
225const CompatDeleteSessionRpc = Rpc.make(AGENT_METHODS.session_delete, {
226 payload: AcpSchema.DeleteSessionRequest,
227 success: AcpSchema.DeleteSessionResponse,
228 error: AcpSchema.Error,
229});
230
231const CompatListProvidersRpc = Rpc.make(AGENT_METHODS.providers_list, {
232 payload: AcpSchema.ListProvidersRequest,
233 success: AcpSchema.ListProvidersResponse,
234 error: AcpSchema.Error,
235});
236
237const CompatSetProviderRpc = Rpc.make(AGENT_METHODS.providers_set, {
238 payload: AcpSchema.SetProviderRequest,
239 success: AcpSchema.SetProviderResponse,
240 error: AcpSchema.Error,
241});
242
243const CompatDisableProviderRpc = Rpc.make(AGENT_METHODS.providers_disable, {
244 payload: AcpSchema.DisableProviderRequest,
245 success: AcpSchema.DisableProviderResponse,
246 error: AcpSchema.Error,
247});
248
249const CompatPromptRpc = Rpc.make(AGENT_METHODS.session_prompt, {
250 payload: Schema.Union([AcpSchema.PromptRequest, AcpSchemaV1.PromptRequest]),
251 success: Schema.Union([AcpSchemaV1.PromptResponse, AcpSchema.PromptResponse]),
252 error: AcpSchema.Error,
253});
254
255const CompatSetSessionConfigOptionRpc = Rpc.make(AGENT_METHODS.session_set_config_option, {
256 payload: Schema.Union([
257 AcpSchema.SetSessionConfigOptionRequest,
258 AcpSchemaV1.SetSessionConfigOptionRequest,
259 ]),
260 success: Schema.Union([
261 AcpSchemaV1.SetSessionConfigOptionResponse,
262 AcpSchema.SetSessionConfigOptionResponse,
263 ]),
264 error: AcpSchema.Error,
265});
266
267const CompatSetSessionModelRpc = Rpc.make(V1_AGENT_METHODS.session_set_model, {
268 payload: AcpCompat.SetSessionModelRequest,
269 success: AcpCompat.SetSessionModelResponse,
270 error: AcpSchemaV1.Error,
271});
272
273const CompatSetSessionModeRpc = Rpc.make(V1_AGENT_METHODS.session_set_mode, {
274 payload: AcpSchemaV1.SetSessionModeRequest,
275 success: AcpSchemaV1.SetSessionModeResponse,
276 error: AcpSchemaV1.Error,
277});
278
279export const CompatAgentRpcs = RpcGroup.make(
280 CompatInitializeRpc,
281 CompatSetSessionModelRpc,
282 CompatSetSessionModeRpc,
283 LoginRpc,
284 CompatAuthenticateV1Rpc,
285 LogoutRpc,
286 CompatLogoutV1Rpc,
287 CompatNewSessionRpc,
288 CompatLoadSessionV1Rpc,
289 CompatListSessionsRpc,
290 CompatForkSessionRpc,
291 CompatResumeSessionRpc,
292 CompatCloseSessionRpc,
293 CompatDeleteSessionRpc,
294 CompatListProvidersRpc,
295 CompatSetProviderRpc,
296 CompatDisableProviderRpc,
297 CompatPromptRpc,
298 CompatSetSessionConfigOptionRpc,
299);
300
301const CompatRequestPermissionRpc = Rpc.make(CLIENT_METHODS.session_request_permission, {
302 payload: Schema.Union([AcpSchema.RequestPermissionRequest, AcpSchemaV1.RequestPermissionRequest]),
303 success: Schema.Union([
304 AcpSchema.RequestPermissionResponse,
305 AcpSchemaV1.RequestPermissionResponse,
306 ]),
307 error: AcpSchema.Error,
308});
309
310const CompatElicitationRpc = Rpc.make(CLIENT_METHODS.elicitation_create, {
311 // Decode in the handler so malformed requests receive ACP's invalid-params error.
312 payload: Schema.Unknown,
313 success: Schema.Union([
314 AcpSchema.CreateElicitationResponse,
315 AcpSchemaV1.CreateElicitationResponse,
316 ]),
317 error: AcpSchema.Error,
318});
319
320// Native ACP agents still use the older nested elicitation response.
321const legacyElicitationRpc = <const Method extends string>(method: Method) =>
322 Rpc.make(method, {
323 payload: AcpSchema.CreateElicitationRequest,
324 success: Schema.Struct({
325 action: AcpSchema.CreateElicitationResponse,
326 _meta: Schema.optionalKey(Schema.NullOr(Schema.Record(Schema.String, Schema.Json))),
327 }),
328 error: AcpSchema.Error,
329 });
330
331const ConnectMcpRpc = Rpc.make(CLIENT_METHODS.mcp_connect, {
332 payload: AcpSchema.ConnectMcpRequest,
333 success: AcpSchema.ConnectMcpResponse,
334 error: AcpSchema.Error,
335});
336
337const MessageMcpRpc = Rpc.make(CLIENT_METHODS.mcp_message, {
338 payload: AcpSchema.MessageMcpRequest,
339 success: AcpSchema.MessageMcpResponse,
340 error: AcpSchema.Error,
341});
342
343const DisconnectMcpRpc = Rpc.make(CLIENT_METHODS.mcp_disconnect, {
344 payload: AcpSchema.DisconnectMcpRequest,
345 success: AcpSchema.DisconnectMcpResponse,
346 error: AcpSchema.Error,
347});
348
349export const ClientRpcs = RpcGroup.make(
350 RequestPermissionRpc,
351 ElicitationRpc,
352 ConnectMcpRpc,
353 MessageMcpRpc,
354 DisconnectMcpRpc,
355);
356
357const ReadTextFileV1Rpc = Rpc.make(V1_CLIENT_METHODS.fs_read_text_file, {
358 payload: AcpSchemaV1.ReadTextFileRequest,
359 success: AcpSchemaV1.ReadTextFileResponse,
360 error: AcpSchemaV1.Error,
361});
362
363const WriteTextFileV1Rpc = Rpc.make(V1_CLIENT_METHODS.fs_write_text_file, {
364 payload: AcpSchemaV1.WriteTextFileRequest,
365 success: AcpSchemaV1.WriteTextFileResponse,
366 error: AcpSchemaV1.Error,
367});
368
369const CreateTerminalV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_create, {
370 payload: AcpSchemaV1.CreateTerminalRequest,
371 success: AcpSchemaV1.CreateTerminalResponse,
372 error: AcpSchemaV1.Error,
373});
374
375const TerminalOutputV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_output, {
376 payload: AcpSchemaV1.TerminalOutputRequest,
377 success: AcpSchemaV1.TerminalOutputResponse,
378 error: AcpSchemaV1.Error,
379});
380
381const ReleaseTerminalV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_release, {
382 payload: AcpSchemaV1.ReleaseTerminalRequest,
383 success: AcpSchemaV1.ReleaseTerminalResponse,
384 error: AcpSchemaV1.Error,
385});
386
387const WaitForTerminalExitV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_wait_for_exit, {
388 payload: AcpSchemaV1.WaitForTerminalExitRequest,
389 success: AcpSchemaV1.WaitForTerminalExitResponse,
390 error: AcpSchemaV1.Error,
391});
392
393const KillTerminalV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_kill, {
394 payload: AcpSchemaV1.KillTerminalRequest,
395 success: AcpSchemaV1.KillTerminalResponse,
396 error: AcpSchemaV1.Error,
397});
398
399export const CompatClientRpcs = RpcGroup.make(
400 CompatRequestPermissionRpc,
401 CompatElicitationRpc,
402 legacyElicitationRpc(V1_CLIENT_METHODS.session_elicitation),
403 legacyElicitationRpc(V1_CLIENT_METHODS.unstable_session_elicitation),
404 ConnectMcpRpc,
405 MessageMcpRpc,
406 DisconnectMcpRpc,
407 ReadTextFileV1Rpc,
408 WriteTextFileV1Rpc,
409 CreateTerminalV1Rpc,
410 TerminalOutputV1Rpc,
411 ReleaseTerminalV1Rpc,
412 WaitForTerminalExitV1Rpc,
413 KillTerminalV1Rpc,
414);