| 1 | import * as AcpCompat from "./compat.ts"; |
| 2 | import * as Rpc from "effect/rpc/Rpc"; |
| 3 | import * as RpcGroup from "effect/rpc/RpcGroup"; |
| 4 | import * as Schema from "effect/Schema"; |
| 5 | |
| 6 | import * as AcpSchema from "./_generated/schema.gen.ts"; |
| 7 | import * as AcpSchemaV1 from "./_generated/schema-v1.gen.ts"; |
| 8 | import { AGENT_METHODS, CLIENT_METHODS } from "./_generated/meta.gen.ts"; |
| 9 | |
| 10 | export 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 | |
| 18 | export 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 | |
| 31 | const InitializeRpc = Rpc.make(AGENT_METHODS.initialize, { |
| 32 | payload: AcpSchema.InitializeRequest, |
| 33 | success: AcpSchema.InitializeResponse, |
| 34 | error: AcpSchema.Error, |
| 35 | }); |
| 36 | |
| 37 | const LoginRpc = Rpc.make(AGENT_METHODS.auth_login, { |
| 38 | payload: AcpSchema.LoginAuthRequest, |
| 39 | success: AcpSchema.LoginAuthResponse, |
| 40 | error: AcpSchema.Error, |
| 41 | }); |
| 42 | |
| 43 | const LogoutRpc = Rpc.make(AGENT_METHODS.auth_logout, { |
| 44 | payload: AcpSchema.LogoutAuthRequest, |
| 45 | success: AcpSchema.LogoutAuthResponse, |
| 46 | error: AcpSchema.Error, |
| 47 | }); |
| 48 | |
| 49 | const NewSessionRpc = Rpc.make(AGENT_METHODS.session_new, { |
| 50 | payload: AcpSchema.NewSessionRequest, |
| 51 | success: AcpSchema.NewSessionResponse, |
| 52 | error: AcpSchema.Error, |
| 53 | }); |
| 54 | |
| 55 | const ListSessionsRpc = Rpc.make(AGENT_METHODS.session_list, { |
| 56 | payload: AcpSchema.ListSessionsRequest, |
| 57 | success: AcpSchema.ListSessionsResponse, |
| 58 | error: AcpSchema.Error, |
| 59 | }); |
| 60 | |
| 61 | const ForkSessionRpc = Rpc.make(AGENT_METHODS.session_fork, { |
| 62 | payload: AcpSchema.ForkSessionRequest, |
| 63 | success: AcpSchema.ForkSessionResponse, |
| 64 | error: AcpSchema.Error, |
| 65 | }); |
| 66 | |
| 67 | const ResumeSessionRpc = Rpc.make(AGENT_METHODS.session_resume, { |
| 68 | payload: AcpSchema.ResumeSessionRequest, |
| 69 | success: AcpSchema.ResumeSessionResponse, |
| 70 | error: AcpSchema.Error, |
| 71 | }); |
| 72 | |
| 73 | const CloseSessionRpc = Rpc.make(AGENT_METHODS.session_close, { |
| 74 | payload: AcpSchema.CloseSessionRequest, |
| 75 | success: AcpSchema.CloseSessionResponse, |
| 76 | error: AcpSchema.Error, |
| 77 | }); |
| 78 | |
| 79 | const DeleteSessionRpc = Rpc.make(AGENT_METHODS.session_delete, { |
| 80 | payload: AcpSchema.DeleteSessionRequest, |
| 81 | success: AcpSchema.DeleteSessionResponse, |
| 82 | error: AcpSchema.Error, |
| 83 | }); |
| 84 | |
| 85 | const ListProvidersRpc = Rpc.make(AGENT_METHODS.providers_list, { |
| 86 | payload: AcpSchema.ListProvidersRequest, |
| 87 | success: AcpSchema.ListProvidersResponse, |
| 88 | error: AcpSchema.Error, |
| 89 | }); |
| 90 | |
| 91 | const SetProviderRpc = Rpc.make(AGENT_METHODS.providers_set, { |
| 92 | payload: AcpSchema.SetProviderRequest, |
| 93 | success: AcpSchema.SetProviderResponse, |
| 94 | error: AcpSchema.Error, |
| 95 | }); |
| 96 | |
| 97 | const DisableProviderRpc = Rpc.make(AGENT_METHODS.providers_disable, { |
| 98 | payload: AcpSchema.DisableProviderRequest, |
| 99 | success: AcpSchema.DisableProviderResponse, |
| 100 | error: AcpSchema.Error, |
| 101 | }); |
| 102 | |
| 103 | const PromptRpc = Rpc.make(AGENT_METHODS.session_prompt, { |
| 104 | payload: AcpSchema.PromptRequest, |
| 105 | success: AcpSchema.PromptResponse, |
| 106 | error: AcpSchema.Error, |
| 107 | }); |
| 108 | |
| 109 | const SetSessionConfigOptionRpc = Rpc.make(AGENT_METHODS.session_set_config_option, { |
| 110 | payload: AcpSchema.SetSessionConfigOptionRequest, |
| 111 | success: AcpSchema.SetSessionConfigOptionResponse, |
| 112 | error: AcpSchema.Error, |
| 113 | }); |
| 114 | |
| 115 | const RequestPermissionRpc = Rpc.make(CLIENT_METHODS.session_request_permission, { |
| 116 | payload: AcpSchema.RequestPermissionRequest, |
| 117 | success: AcpSchema.RequestPermissionResponse, |
| 118 | error: AcpSchema.Error, |
| 119 | }); |
| 120 | |
| 121 | const ElicitationRpc = Rpc.make(CLIENT_METHODS.elicitation_create, { |
| 122 | payload: AcpSchema.CreateElicitationRequest, |
| 123 | success: AcpSchema.CreateElicitationResponse, |
| 124 | error: AcpSchema.Error, |
| 125 | }); |
| 126 | |
| 127 | export 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. |
| 147 | const 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 | |
| 156 | const CompatAuthenticateV1Rpc = Rpc.make(V1_AGENT_METHODS.authenticate, { |
| 157 | payload: AcpSchemaV1.AuthenticateRequest, |
| 158 | success: AcpSchemaV1.AuthenticateResponse, |
| 159 | error: AcpSchemaV1.Error, |
| 160 | }); |
| 161 | |
| 162 | const CompatLogoutV1Rpc = Rpc.make(V1_AGENT_METHODS.logout, { |
| 163 | payload: AcpSchemaV1.LogoutRequest, |
| 164 | success: AcpSchemaV1.LogoutResponse, |
| 165 | error: AcpSchemaV1.Error, |
| 166 | }); |
| 167 | |
| 168 | const 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 | |
| 180 | const 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 | |
| 189 | const 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 | |
| 195 | const 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 | |
| 207 | const 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 | |
| 219 | const 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 | |
| 225 | const CompatDeleteSessionRpc = Rpc.make(AGENT_METHODS.session_delete, { |
| 226 | payload: AcpSchema.DeleteSessionRequest, |
| 227 | success: AcpSchema.DeleteSessionResponse, |
| 228 | error: AcpSchema.Error, |
| 229 | }); |
| 230 | |
| 231 | const CompatListProvidersRpc = Rpc.make(AGENT_METHODS.providers_list, { |
| 232 | payload: AcpSchema.ListProvidersRequest, |
| 233 | success: AcpSchema.ListProvidersResponse, |
| 234 | error: AcpSchema.Error, |
| 235 | }); |
| 236 | |
| 237 | const CompatSetProviderRpc = Rpc.make(AGENT_METHODS.providers_set, { |
| 238 | payload: AcpSchema.SetProviderRequest, |
| 239 | success: AcpSchema.SetProviderResponse, |
| 240 | error: AcpSchema.Error, |
| 241 | }); |
| 242 | |
| 243 | const CompatDisableProviderRpc = Rpc.make(AGENT_METHODS.providers_disable, { |
| 244 | payload: AcpSchema.DisableProviderRequest, |
| 245 | success: AcpSchema.DisableProviderResponse, |
| 246 | error: AcpSchema.Error, |
| 247 | }); |
| 248 | |
| 249 | const 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 | |
| 255 | const 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 | |
| 267 | const CompatSetSessionModelRpc = Rpc.make(V1_AGENT_METHODS.session_set_model, { |
| 268 | payload: AcpCompat.SetSessionModelRequest, |
| 269 | success: AcpCompat.SetSessionModelResponse, |
| 270 | error: AcpSchemaV1.Error, |
| 271 | }); |
| 272 | |
| 273 | const CompatSetSessionModeRpc = Rpc.make(V1_AGENT_METHODS.session_set_mode, { |
| 274 | payload: AcpSchemaV1.SetSessionModeRequest, |
| 275 | success: AcpSchemaV1.SetSessionModeResponse, |
| 276 | error: AcpSchemaV1.Error, |
| 277 | }); |
| 278 | |
| 279 | export 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 | |
| 301 | const 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 | |
| 310 | const 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. |
| 321 | const 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 | |
| 331 | const ConnectMcpRpc = Rpc.make(CLIENT_METHODS.mcp_connect, { |
| 332 | payload: AcpSchema.ConnectMcpRequest, |
| 333 | success: AcpSchema.ConnectMcpResponse, |
| 334 | error: AcpSchema.Error, |
| 335 | }); |
| 336 | |
| 337 | const MessageMcpRpc = Rpc.make(CLIENT_METHODS.mcp_message, { |
| 338 | payload: AcpSchema.MessageMcpRequest, |
| 339 | success: AcpSchema.MessageMcpResponse, |
| 340 | error: AcpSchema.Error, |
| 341 | }); |
| 342 | |
| 343 | const DisconnectMcpRpc = Rpc.make(CLIENT_METHODS.mcp_disconnect, { |
| 344 | payload: AcpSchema.DisconnectMcpRequest, |
| 345 | success: AcpSchema.DisconnectMcpResponse, |
| 346 | error: AcpSchema.Error, |
| 347 | }); |
| 348 | |
| 349 | export const ClientRpcs = RpcGroup.make( |
| 350 | RequestPermissionRpc, |
| 351 | ElicitationRpc, |
| 352 | ConnectMcpRpc, |
| 353 | MessageMcpRpc, |
| 354 | DisconnectMcpRpc, |
| 355 | ); |
| 356 | |
| 357 | const ReadTextFileV1Rpc = Rpc.make(V1_CLIENT_METHODS.fs_read_text_file, { |
| 358 | payload: AcpSchemaV1.ReadTextFileRequest, |
| 359 | success: AcpSchemaV1.ReadTextFileResponse, |
| 360 | error: AcpSchemaV1.Error, |
| 361 | }); |
| 362 | |
| 363 | const WriteTextFileV1Rpc = Rpc.make(V1_CLIENT_METHODS.fs_write_text_file, { |
| 364 | payload: AcpSchemaV1.WriteTextFileRequest, |
| 365 | success: AcpSchemaV1.WriteTextFileResponse, |
| 366 | error: AcpSchemaV1.Error, |
| 367 | }); |
| 368 | |
| 369 | const CreateTerminalV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_create, { |
| 370 | payload: AcpSchemaV1.CreateTerminalRequest, |
| 371 | success: AcpSchemaV1.CreateTerminalResponse, |
| 372 | error: AcpSchemaV1.Error, |
| 373 | }); |
| 374 | |
| 375 | const TerminalOutputV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_output, { |
| 376 | payload: AcpSchemaV1.TerminalOutputRequest, |
| 377 | success: AcpSchemaV1.TerminalOutputResponse, |
| 378 | error: AcpSchemaV1.Error, |
| 379 | }); |
| 380 | |
| 381 | const ReleaseTerminalV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_release, { |
| 382 | payload: AcpSchemaV1.ReleaseTerminalRequest, |
| 383 | success: AcpSchemaV1.ReleaseTerminalResponse, |
| 384 | error: AcpSchemaV1.Error, |
| 385 | }); |
| 386 | |
| 387 | const WaitForTerminalExitV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_wait_for_exit, { |
| 388 | payload: AcpSchemaV1.WaitForTerminalExitRequest, |
| 389 | success: AcpSchemaV1.WaitForTerminalExitResponse, |
| 390 | error: AcpSchemaV1.Error, |
| 391 | }); |
| 392 | |
| 393 | const KillTerminalV1Rpc = Rpc.make(V1_CLIENT_METHODS.terminal_kill, { |
| 394 | payload: AcpSchemaV1.KillTerminalRequest, |
| 395 | success: AcpSchemaV1.KillTerminalResponse, |
| 396 | error: AcpSchemaV1.Error, |
| 397 | }); |
| 398 | |
| 399 | export 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 | ); |