Skip to main content

cookie_agent_server/service/
routes.rs

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(&params, 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                &params.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, &params.name, &params.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(&params).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                // Protocol 17 has no separate snapshot RPC. Return only the replay
299                // half of this response and deliberately register no live tail.
300                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(&params, 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        // Provider connect takes the cross-process store lock; keep the
433        // synchronous disk work off the async worker.
434        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        // Provider disconnect takes the cross-process store lock; keep the
446        // synchronous disk work off the async worker.
447        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}