1use async_trait::async_trait;
2use cookie_agent_engine::{EngineError, session::SessionError};
3use cookie_agent_protocol::{
4 AgentUsageParams, AgentUsageResult, ApprovalListParams, ApprovalListResult,
5 ApprovalRespondErrorCode, ApprovalRespondParams, ApprovalRespondResult, EventsSubscribeParams,
6 EventsSubscribeResult, GlobalUsageParams, GlobalUsageResult, McpAuthBeginParams,
7 McpAuthBeginResult, McpAuthCancelParams, McpAuthCancelResult, McpServerAddParams,
8 McpServerEditParams, McpServerListParams, McpServerListResult, McpServerMutationResult,
9 McpServerNameParams, McpServerPersistParams, McpServerSetEnabledParams, ProviderConnectParams,
10 ProviderConnectResult, ProviderDisconnectParams, ProviderDisconnectResult, RunCancelParams,
11 RunCancelResult, RunRecallSteerParams, RunRecallSteerResult, RunStartParams, RunStartResult,
12 RunSteerParams, RunSteerResult, RunToolStdinParams, RunToolStdinResult,
13 RuntimeSnapshotGetParams, RuntimeSnapshotResult, ServerContext, ServerFault, ServerProtocol,
14 SessionChildrenParams, SessionChildrenResult, SessionCompactParams, SessionCompactResult,
15 SessionCreateParams, SessionCreateResult, SessionForkParams, SessionForkResult,
16 SessionGetParams, SessionGetResult, SessionGoalGetParams, SessionGoalGetResult,
17 SessionGoalLifecycleParams, SessionGoalLifecycleResult, SessionGoalSetParams,
18 SessionGoalSetResult, SessionListParams, SessionListResult, SessionPermissionClearParams,
19 SessionPermissionGetParams, SessionPermissionGetResult, SessionPermissionMutationResult,
20 SessionPermissionSetParams, SessionProducersParams, SessionProducersResult,
21 SessionRenameErrorCode, SessionRenameParams, SessionRenameResult, SessionResumeParams,
22 SessionResumeResult, SessionRevertParams, SessionRevertResult, SessionSetPermissionModeParams,
23 SessionSetPermissionModeResult, SessionTreeParams, SessionTreeResult, SessionTreeUsageResult,
24 SessionUsageParams, SessionUsageResult, SkillsGetParams, SkillsGetResult, SkillsListParams,
25 SkillsListResult,
26};
27
28use super::Server;
29use crate::rpc::{RpcFault, engine_fault, run_start_fault};
30
31type Result<T> = std::result::Result<T, ServerFault>;
32
33fn rpc_origin() -> cookie_agent_protocol::EventOrigin {
34 cookie_agent_protocol::EventOrigin::new("client:rpc").expect("static event origin is valid")
35}
36
37#[async_trait]
38impl ServerProtocol for Server {
39 async fn connected(&self, context: ServerContext) {
40 self.start_runtime_notifications(context.clone());
41 self.start_engine_event_notifications(context);
42 }
43
44 async fn create_session(&self, params: SessionCreateParams) -> Result<SessionCreateResult> {
45 self.engine
46 .create_session(params.selection)
47 .map(|session| SessionCreateResult { session })
48 .map_err(protocol_fault)
49 }
50
51 async fn list_sessions(&self, params: SessionListParams) -> Result<SessionListResult> {
52 let sessions = self
53 .engine
54 .list_sessions()
55 .into_iter()
56 .filter(|session| {
57 params
58 .cwd_identity
59 .as_ref()
60 .is_none_or(|cwd| &session.cwd_identity == cwd)
61 })
62 .collect();
63 Ok(SessionListResult { sessions })
64 }
65
66 async fn get_session(&self, params: SessionGetParams) -> Result<SessionGetResult> {
67 self.engine
68 .get_session(params.session_id)
69 .map(|session| SessionGetResult { session })
70 .map_err(protocol_fault)
71 }
72
73 async fn get_session_goal(&self, params: SessionGoalGetParams) -> Result<SessionGoalGetResult> {
74 self.engine
75 .get_session_goal(params)
76 .await
77 .map_err(protocol_fault)
78 }
79
80 async fn set_session_goal(&self, params: SessionGoalSetParams) -> Result<SessionGoalSetResult> {
81 self.engine
82 .set_session_goal(params, rpc_origin())
83 .await
84 .map_err(protocol_fault)
85 }
86
87 async fn change_session_goal_lifecycle(
88 &self,
89 params: SessionGoalLifecycleParams,
90 ) -> Result<SessionGoalLifecycleResult> {
91 self.engine
92 .change_session_goal_lifecycle(params, rpc_origin())
93 .await
94 .map_err(protocol_fault)
95 }
96
97 async fn session_producers(
98 &self,
99 params: SessionProducersParams,
100 ) -> Result<SessionProducersResult> {
101 self.engine
102 .session_producers(params)
103 .await
104 .map_err(protocol_fault)
105 }
106
107 async fn session_usage(&self, params: SessionUsageParams) -> Result<SessionUsageResult> {
108 self.engine
109 .session_usage(params.session_id)
110 .map_err(protocol_fault)
111 }
112
113 async fn session_tree_usage(
114 &self,
115 params: SessionUsageParams,
116 ) -> Result<SessionTreeUsageResult> {
117 self.engine
118 .session_tree_usage(params.session_id)
119 .map_err(|error| RpcFault::session_tree_usage(error).into())
120 }
121
122 async fn agent_usage(&self, params: AgentUsageParams) -> Result<AgentUsageResult> {
123 Ok(self.engine.agent_usage(params.agent_id))
124 }
125
126 async fn global_usage(&self, _: GlobalUsageParams) -> Result<GlobalUsageResult> {
127 Ok(self.engine.global_usage())
128 }
129
130 async fn session_children(
131 &self,
132 params: SessionChildrenParams,
133 ) -> Result<SessionChildrenResult> {
134 Ok(SessionChildrenResult {
135 children: self
136 .engine
137 .children(params.session_id)
138 .map_err(protocol_fault)?,
139 })
140 }
141
142 async fn session_tree(&self, params: SessionTreeParams) -> Result<SessionTreeResult> {
143 self.engine
144 .tree(params.session_id)
145 .map(|tree| SessionTreeResult { tree })
146 .map_err(protocol_fault)
147 }
148
149 async fn resume_session(&self, params: SessionResumeParams) -> Result<SessionResumeResult> {
150 self.engine
151 .resume(params.session_id)
152 .await
153 .map(|session| SessionResumeResult { session })
154 .map_err(protocol_fault)
155 }
156
157 async fn rename_session(&self, params: SessionRenameParams) -> Result<SessionRenameResult> {
158 self.engine
159 .rename_session(params.clone(), rpc_origin())
160 .await
161 .map_err(|error| rename_fault(¶ms, error).into())
162 }
163
164 async fn set_permission_mode(
165 &self,
166 params: SessionSetPermissionModeParams,
167 ) -> Result<SessionSetPermissionModeResult> {
168 self.engine
169 .set_permission_mode(params.session_id, params.mode)
170 .map_err(protocol_fault)?;
171 Ok(SessionSetPermissionModeResult {})
172 }
173
174 async fn get_session_permissions(
175 &self,
176 params: SessionPermissionGetParams,
177 ) -> Result<SessionPermissionGetResult> {
178 self.engine
179 .get_session_permissions(params.session_id)
180 .map_err(protocol_fault)
181 }
182
183 async fn set_session_permission(
184 &self,
185 params: SessionPermissionSetParams,
186 ) -> Result<SessionPermissionMutationResult> {
187 self.engine
188 .set_session_permission(
189 params.session_id,
190 params.action,
191 params.resource,
192 params.effect,
193 rpc_origin(),
194 )
195 .await
196 .map_err(protocol_fault)
197 }
198
199 async fn clear_session_permission(
200 &self,
201 params: SessionPermissionClearParams,
202 ) -> Result<SessionPermissionMutationResult> {
203 self.engine
204 .clear_session_permission(
205 params.session_id,
206 params.action,
207 ¶ms.resource,
208 rpc_origin(),
209 )
210 .await
211 .map_err(protocol_fault)
212 }
213
214 async fn list_skills(&self, params: SkillsListParams) -> Result<SkillsListResult> {
215 self.engine
216 .list_skills(params.session_id)
217 .map_err(protocol_fault)
218 }
219
220 async fn get_skill(&self, params: SkillsGetParams) -> Result<SkillsGetResult> {
221 self.engine
222 .get_skill(params.session_id, ¶ms.name, ¶ms.args)
223 .map_err(protocol_fault)
224 }
225
226 async fn compact_session(&self, params: SessionCompactParams) -> Result<SessionCompactResult> {
227 self.engine
228 .compact_session_result(
229 params.session_id,
230 params.focus.as_ref().map(|focus| focus.as_str()),
231 rpc_origin(),
232 )
233 .await
234 .map_err(protocol_fault)
235 }
236
237 async fn revert_session(&self, params: SessionRevertParams) -> Result<SessionRevertResult> {
238 self.engine
239 .revert_session(params.session_id, params.through_seq, rpc_origin())
240 .await
241 .map_err(protocol_fault)
242 }
243
244 async fn fork_session(&self, params: SessionForkParams) -> Result<SessionForkResult> {
245 self.engine
246 .fork_session(params.session_id, params.through_seq, rpc_origin())
247 .await
248 .map_err(protocol_fault)
249 }
250
251 async fn start_run(&self, params: RunStartParams) -> Result<RunStartResult> {
252 match self.engine.start_run(params.clone(), rpc_origin()).await {
253 Ok(result) => Ok(result),
254 Err(EngineError::RunIdempotencyConflict) => {
255 Err(RpcFault::run_start_conflict(¶ms).into())
256 }
257 Err(error) => Err(run_start_fault(error).into()),
258 }
259 }
260
261 async fn steer_run(&self, params: RunSteerParams) -> Result<RunSteerResult> {
262 self.engine
263 .steer(params.run_id, params.input, rpc_origin())
264 .await
265 .map_err(protocol_fault)
266 }
267
268 async fn recall_steer(&self, params: RunRecallSteerParams) -> Result<RunRecallSteerResult> {
269 self.engine
270 .recall_steer(params.run_id)
271 .await
272 .map_err(protocol_fault)
273 }
274
275 async fn cancel_run(&self, params: RunCancelParams) -> Result<RunCancelResult> {
276 self.engine
277 .cancel_run(params.run_id)
278 .await
279 .map_err(protocol_fault)
280 }
281
282 async fn tool_stdin(&self, params: RunToolStdinParams) -> Result<RunToolStdinResult> {
283 self.engine.tool_stdin(params).await.map_err(protocol_fault)
284 }
285
286 async fn subscribe_events(
287 &self,
288 params: EventsSubscribeParams,
289 context: &ServerContext,
290 ) -> Result<EventsSubscribeResult> {
291 let (result, receiver) = match self
292 .engine
293 .subscribe(params.session_id, params.cursor)
294 .await
295 {
296 Ok(subscription) => subscription,
297 Err(EngineError::SessionOwnedByAnotherProcess(_)) => {
298 return self
301 .engine
302 .snapshot_events(params.session_id, params.cursor)
303 .map_err(protocol_fault);
304 }
305 Err(error) => return Err(protocol_fault(error)),
306 };
307 context.register_session_subscription(params.session_id);
308 self.start_event_tail(receiver, context.clone());
309 for event in &result.events {
310 if let cookie_agent_protocol::EventPayload::ToolCallStarted { start } = &event.payload {
311 self.start_output_tail(start.tool_call_id, context.clone());
312 }
313 }
314 Ok(result)
315 }
316
317 async fn respond_approval(
318 &self,
319 params: ApprovalRespondParams,
320 ) -> Result<ApprovalRespondResult> {
321 self.engine
322 .approval_respond(params.clone(), rpc_origin())
323 .await
324 .map_err(|error| approval_fault(¶ms, error).into())
325 }
326
327 async fn list_approvals(&self, params: ApprovalListParams) -> Result<ApprovalListResult> {
328 Ok(self
329 .engine
330 .list_approvals(params.root_session_id, params.status))
331 }
332
333 async fn begin_mcp_auth(&self, params: McpAuthBeginParams) -> Result<McpAuthBeginResult> {
334 let authorization_url = self
335 .engine
336 .begin_mcp_auth(params.server.clone())
337 .await
338 .map_err(protocol_fault)?;
339 Ok(McpAuthBeginResult {
340 server: params.server,
341 authorization_url,
342 })
343 }
344
345 async fn cancel_mcp_auth(&self, params: McpAuthCancelParams) -> Result<McpAuthCancelResult> {
346 self.engine
347 .cancel_mcp_auth(params.server.clone())
348 .await
349 .map_err(protocol_fault)?;
350 Ok(McpAuthCancelResult {
351 server: params.server,
352 })
353 }
354
355 async fn list_mcp_servers(&self, _: McpServerListParams) -> Result<McpServerListResult> {
356 Ok(McpServerListResult {
357 servers: self.engine.list_mcp_servers(),
358 })
359 }
360
361 async fn add_mcp_server(&self, params: McpServerAddParams) -> Result<McpServerMutationResult> {
362 self.engine
363 .add_mcp_server(params.name, params.definition)
364 .await
365 .map(|server| McpServerMutationResult { server })
366 .map_err(protocol_fault)
367 }
368
369 async fn edit_mcp_server(
370 &self,
371 params: McpServerEditParams,
372 ) -> Result<McpServerMutationResult> {
373 self.engine
374 .edit_mcp_server(params.name, params.definition)
375 .await
376 .map(|server| McpServerMutationResult { server })
377 .map_err(protocol_fault)
378 }
379
380 async fn remove_mcp_server(
381 &self,
382 params: McpServerNameParams,
383 ) -> Result<McpServerMutationResult> {
384 self.engine
385 .remove_mcp_server(params.name)
386 .await
387 .map(|server| McpServerMutationResult { server })
388 .map_err(protocol_fault)
389 }
390
391 async fn set_mcp_server_enabled(
392 &self,
393 params: McpServerSetEnabledParams,
394 ) -> Result<McpServerMutationResult> {
395 self.engine
396 .set_mcp_server_enabled(params.name, params.enabled)
397 .await
398 .map(|server| McpServerMutationResult { server })
399 .map_err(protocol_fault)
400 }
401
402 async fn reconnect_mcp_server(
403 &self,
404 params: McpServerNameParams,
405 ) -> Result<McpServerMutationResult> {
406 self.engine
407 .reconnect_mcp_server(params.name)
408 .await
409 .map(|server| McpServerMutationResult { server })
410 .map_err(protocol_fault)
411 }
412
413 async fn persist_mcp_server(
414 &self,
415 params: McpServerPersistParams,
416 ) -> Result<McpServerMutationResult> {
417 self.engine
418 .persist_mcp_server(params.name, params.target)
419 .await
420 .map(|server| McpServerMutationResult { server })
421 .map_err(protocol_fault)
422 }
423
424 async fn runtime_snapshot(&self, _: RuntimeSnapshotGetParams) -> Result<RuntimeSnapshotResult> {
425 self.engine.runtime_snapshot().map_err(protocol_fault)
426 }
427
428 async fn connect_provider(
429 &self,
430 params: ProviderConnectParams,
431 ) -> Result<ProviderConnectResult> {
432 let server = self.clone();
435 tokio::task::spawn_blocking(move || Server::connect_provider(&server, params))
436 .await
437 .map_err(|_| ServerFault::from(RpcFault::engine()))?
438 .map_err(Into::into)
439 }
440
441 async fn disconnect_provider(
442 &self,
443 params: ProviderDisconnectParams,
444 ) -> Result<ProviderDisconnectResult> {
445 let server = self.clone();
448 tokio::task::spawn_blocking(move || Server::disconnect_provider(&server, params))
449 .await
450 .map_err(|_| ServerFault::from(RpcFault::engine()))?
451 .map_err(Into::into)
452 }
453}
454
455fn protocol_fault(error: EngineError) -> ServerFault {
456 engine_fault(error).into()
457}
458
459fn rename_fault(request: &SessionRenameParams, error: EngineError) -> RpcFault {
460 let code = match error {
461 EngineError::RenameConflict => SessionRenameErrorCode::IdempotencyConflict,
462 EngineError::Session(SessionError::Missing(_)) | EngineError::MissingActor(_) => {
463 SessionRenameErrorCode::SessionNotFound
464 }
465 _ => return engine_fault(error),
466 };
467 RpcFault::rename(request, code)
468}
469
470fn approval_fault(request: &ApprovalRespondParams, error: EngineError) -> RpcFault {
471 match error {
472 EngineError::ApprovalResponse(failure) => RpcFault::approval_failure(request, &failure),
473 EngineError::ApprovalNotPending { .. } => RpcFault::approval(
474 request,
475 ApprovalRespondErrorCode::ApprovalNotPending,
476 None,
477 None,
478 ),
479 EngineError::Session(SessionError::Missing(_)) | EngineError::MissingActor(_) => {
480 RpcFault::approval(
481 request,
482 ApprovalRespondErrorCode::ApprovalNotFound,
483 None,
484 None,
485 )
486 }
487 _ => engine_fault(error),
488 }
489}