Skip to main content

uarp_sdk/generated/api/
sessions.rs

1// Code generated by @uarp/codegen from spec/openapi.json. DO NOT EDIT.
2//!
3//! Conversation sessions
4
5#![allow(unused_imports, clippy::too_many_arguments)]
6
7use reqwest::Method;
8use serde::{Deserialize, Serialize};
9use futures_core::Stream;
10
11use crate::client::{Client, Request, NO_BODY, NO_QUERY};
12use crate::error::Result;
13use crate::generated::models;
14use crate::multipart::{field_text, FilePart};
15use crate::pagination::CursorGuard;
16use crate::sse::EventStream;
17use crate::util::encode_path;
18
19/// Query and header parameters for `exportSession`.
20#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
21pub struct ExportSessionParams {
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub format: Option<models::ExportSessionFormat>,
24}
25
26/// Query and header parameters for `getSessionRunFeedback`.
27#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
28pub struct GetSessionRunFeedbackParams {
29    #[serde(default, skip_serializing_if = "Option::is_none")]
30    pub message_id: Option<String>,
31}
32
33/// Query and header parameters for `listSessions`.
34#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
35pub struct ListSessionsParams {
36    /// Filter by agent ID
37    #[serde(default, skip_serializing_if = "Option::is_none")]
38    pub agent_id: Option<String>,
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub limit: Option<i64>,
41    /// Opaque pagination cursor returned by previous page response.
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub cursor: Option<String>,
44}
45
46/// Query and header parameters for `listSessionTodos`.
47#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
48pub struct ListSessionTodosParams {
49    #[serde(default, skip_serializing_if = "Option::is_none")]
50    pub from: Option<String>,
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub to: Option<String>,
53}
54
55/// Query and header parameters for `streamSessionEvents`.
56#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
57pub struct StreamSessionEventsParams {
58    /// Short-lived SSE/WebSocket token (mint via `POST /api/v1/auth/sse-tokens`) or full API key.
59    /// Used by browser EventSource which cannot set Authorization header.
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub token: Option<String>,
62    /// SSE resumption cursor. The browser's EventSource sets this automatically on reconnect;
63    /// servers replay events strictly after this id.
64    #[serde(skip)]
65    pub last_event_id: Option<String>,
66}
67
68/// Conversation sessions
69#[derive(Debug, Clone)]
70pub struct SessionsApi {
71    pub(crate) client: Client,
72}
73
74impl Client {
75    /// Conversation sessions
76    pub fn sessions(&self) -> SessionsApi {
77        SessionsApi { client: self.clone() }
78    }
79}
80
81impl SessionsApi {
82    /// Switch active branch
83    ///
84    /// `PUT /api/v1/sessions/{sessionId}/branches/{branchId}/activate`
85    ///
86    /// Required scopes: `sessions:write`.
87    pub async fn activate_session_branch(&self, session_id: &str, branch_id: &str) -> Result<models::ActivateSessionBranchResponse> {
88        self.client
89            .request_json(Request {
90                method: Method::PUT,
91                path: format!("/api/v1/sessions/{}/branches/{}/activate", encode_path(session_id), encode_path(branch_id)),
92                query: NO_QUERY,
93                body: NO_BODY,
94                headers: Vec::new(),
95                idempotent: true,
96            })
97            .await
98    }
99
100    /// Delete many sessions in one request
101    ///
102    /// Cascades each session in turn and writes one audit entry for the batch. Ids that no longer
103    /// exist come back in `failed`, not as an error — the caller's intent for them is already met.
104    /// Not JSON → 400; an empty list, or one over 200 ids → 400.
105    ///
106    /// `POST /api/v1/sessions/bulk-delete`
107    ///
108    /// Required scopes: `sessions:write`.
109    pub async fn bulk_delete_sessions(&self, body: &models::BulkDeleteSessionsRequest) -> Result<models::BulkDeleteSessionsResponse> {
110        self.client
111            .request_json(Request {
112                method: Method::POST,
113                path: "/api/v1/sessions/bulk-delete".to_string(),
114                query: NO_QUERY,
115                body: Some(body),
116                headers: Vec::new(),
117                idempotent: true,
118            })
119            .await
120    }
121
122    /// Close a session
123    ///
124    /// `DELETE /api/v1/sessions/{sessionId}`
125    ///
126    /// Required scopes: `sessions:write`.
127    pub async fn close_session(&self, session_id: &str) -> Result<models::CloseSessionResponse> {
128        self.client
129            .request_json(Request {
130                method: Method::DELETE,
131                path: format!("/api/v1/sessions/{}", encode_path(session_id)),
132                query: NO_QUERY,
133                body: NO_BODY,
134                headers: Vec::new(),
135                idempotent: true,
136            })
137            .await
138    }
139
140    /// Confirm or cancel a todo execution
141    ///
142    /// `POST /api/v1/sessions/{sessionId}/todos/{todoId}/confirm`
143    ///
144    /// Required scopes: `sessions:write`.
145    pub async fn confirm_session_todo(&self, session_id: &str, todo_id: &str, body: &serde_json::Map<String, serde_json::Value>) -> Result<models::Todo> {
146        self.client
147            .request_json(Request {
148                method: Method::POST,
149                path: format!("/api/v1/sessions/{}/todos/{}/confirm", encode_path(session_id), encode_path(todo_id)),
150                query: NO_QUERY,
151                body: Some(body),
152                headers: Vec::new(),
153                idempotent: true,
154            })
155            .await
156    }
157
158    /// Create a session
159    ///
160    /// `POST /api/v1/sessions`
161    ///
162    /// Required scopes: `sessions:write`.
163    pub async fn create(&self, body: &models::CreateSessionRequest) -> Result<models::Session> {
164        self.client
165            .request_json(Request {
166                method: Method::POST,
167                path: "/api/v1/sessions".to_string(),
168                query: NO_QUERY,
169                body: Some(body),
170                headers: Vec::new(),
171                idempotent: true,
172            })
173            .await
174    }
175
176    /// Create annotation
177    ///
178    /// `POST /api/v1/sessions/{sessionId}/annotations`
179    ///
180    /// Required scopes: `sessions:write`.
181    pub async fn create_session_annotation(&self, session_id: &str, body: &models::CreateSessionAnnotationRequest) -> Result<models::CreateSessionAnnotationResponse> {
182        self.client
183            .request_json(Request {
184                method: Method::POST,
185                path: format!("/api/v1/sessions/{}/annotations", encode_path(session_id)),
186                query: NO_QUERY,
187                body: Some(body),
188                headers: Vec::new(),
189                idempotent: true,
190            })
191            .await
192    }
193
194    /// Create a branch point in session
195    ///
196    /// `POST /api/v1/sessions/{sessionId}/branch`
197    ///
198    /// Required scopes: `sessions:write`.
199    pub async fn create_session_branch(&self, session_id: &str, body: &models::CreateSessionBranchRequest) -> Result<models::SessionBranch> {
200        self.client
201            .request_json(Request {
202                method: Method::POST,
203                path: format!("/api/v1/sessions/{}/branch", encode_path(session_id)),
204                query: NO_QUERY,
205                body: Some(body),
206                headers: Vec::new(),
207                idempotent: true,
208            })
209            .await
210    }
211
212    /// Create session share link
213    ///
214    /// `POST /api/v1/sessions/{sessionId}/share`
215    ///
216    /// Required scopes: `sessions:write`.
217    pub async fn create_session_share(&self, session_id: &str, body: &models::CreateSessionShareRequest) -> Result<models::CreateSessionShareResponse> {
218        self.client
219            .request_json(Request {
220                method: Method::POST,
221                path: format!("/api/v1/sessions/{}/share", encode_path(session_id)),
222                query: NO_QUERY,
223                body: Some(body),
224                headers: Vec::new(),
225                idempotent: true,
226            })
227            .await
228    }
229
230    /// Create a todo in session
231    ///
232    /// `POST /api/v1/sessions/{sessionId}/todos`
233    ///
234    /// Required scopes: `sessions:write`.
235    pub async fn create_session_todo(&self, session_id: &str, body: &models::CreateSessionTodoRequest) -> Result<models::Todo> {
236        self.client
237            .request_json(Request {
238                method: Method::POST,
239                path: format!("/api/v1/sessions/{}/todos", encode_path(session_id)),
240                query: NO_QUERY,
241                body: Some(body),
242                headers: Vec::new(),
243                idempotent: true,
244            })
245            .await
246    }
247
248    /// Create a standalone task
249    ///
250    /// Addresses one agent, several agents (fan-out, one session each, shared parent_task_id), or a
251    /// squad. The server bootstraps the session(s). `due_at` omitted fires immediately; a timestamp
252    /// schedules it; explicit `null` files it in the backlog with no schedule at all.
253    ///
254    /// `POST /api/v1/todos`
255    pub async fn create_task(&self, body: &serde_json::Value) -> Result<serde_json::Map<String, serde_json::Value>> {
256        self.client
257            .request_json(Request {
258                method: Method::POST,
259                path: "/api/v1/todos".to_string(),
260                query: NO_QUERY,
261                body: Some(body),
262                headers: Vec::new(),
263                idempotent: true,
264            })
265            .await
266    }
267
268    /// Delete annotation
269    ///
270    /// `DELETE /api/v1/sessions/{sessionId}/annotations/{annotationId}`
271    ///
272    /// Required scopes: `sessions:write`.
273    pub async fn delete_session_annotation(&self, session_id: &str, annotation_id: &str) -> Result<models::DeleteSessionAnnotationResponse> {
274        self.client
275            .request_json(Request {
276                method: Method::DELETE,
277                path: format!("/api/v1/sessions/{}/annotations/{}", encode_path(session_id), encode_path(annotation_id)),
278                query: NO_QUERY,
279                body: NO_BODY,
280                headers: Vec::new(),
281                idempotent: true,
282            })
283            .await
284    }
285
286    /// Delete a single todo
287    ///
288    /// Deletes ONE todo and its background schedule row. The session and its other todos are
289    /// untouched.
290    ///
291    /// `DELETE /api/v1/sessions/{sessionId}/todos/{todoId}`
292    ///
293    /// Required scopes: `sessions:write`.
294    pub async fn delete_session_todo(&self, session_id: &str, todo_id: &str) -> Result<models::DeleteSessionTodoResponse> {
295        self.client
296            .request_json(Request {
297                method: Method::DELETE,
298                path: format!("/api/v1/sessions/{}/todos/{}", encode_path(session_id), encode_path(todo_id)),
299                query: NO_QUERY,
300                body: NO_BODY,
301                headers: Vec::new(),
302                idempotent: true,
303            })
304            .await
305    }
306
307    /// Export a conversation
308    ///
309    /// Two formats and no others: `md` (the default, `text/markdown`) and `json`
310    /// (`application/json`, the `snaga.chat.v1` envelope). Any other value is 400 with the two
311    /// names in the sentence — it is not silently coerced to the default, because a client asking
312    /// for `html` and receiving markdown would render it as text.
313    ///
314    /// `GET /api/v1/sessions/{sessionId}/export`
315    ///
316    /// Required scopes: `sessions:read`.
317    pub async fn export(&self, session_id: &str, params: &ExportSessionParams) -> Result<models::SessionExport> {
318        self.client
319            .request_json(Request {
320                method: Method::GET,
321                path: format!("/api/v1/sessions/{}/export", encode_path(session_id)),
322                query: Some(params),
323                body: NO_BODY,
324                headers: Vec::new(),
325                idempotent: false,
326            })
327            .await
328    }
329
330    /// Get a session
331    ///
332    /// `GET /api/v1/sessions/{sessionId}`
333    ///
334    /// Required scopes: `sessions:read`.
335    pub async fn get(&self, session_id: &str) -> Result<models::Session> {
336        self.client
337            .request_json(Request {
338                method: Method::GET,
339                path: format!("/api/v1/sessions/{}", encode_path(session_id)),
340                query: NO_QUERY,
341                body: NO_BODY,
342                headers: Vec::new(),
343                idempotent: false,
344            })
345            .await
346    }
347
348    /// Get audit log scoped to session
349    ///
350    /// `GET /api/v1/sessions/{sessionId}/audit-log`
351    ///
352    /// Required scopes: `sessions:read`.
353    pub async fn get_session_audit_log(&self, session_id: &str) -> Result<models::GetSessionAuditLogResponse> {
354        self.client
355            .request_json(Request {
356                method: Method::GET,
357                path: format!("/api/v1/sessions/{}/audit-log", encode_path(session_id)),
358                query: NO_QUERY,
359                body: NO_BODY,
360                headers: Vec::new(),
361                idempotent: false,
362            })
363            .await
364    }
365
366    /// The conversation transcript
367    ///
368    /// The transcript this session's clients render. Undescribed until 2026-09-10 and, until the
369    /// same day, not a route at all: a GET here fell through to the bare-session branch and was
370    /// answered with the SESSION record, whose `conversation_history` carries the same list.
371    /// Closing that fall-through took the Android chat screen down with it, which is how the gap
372    /// was found.
373    ///
374    /// `messages` and `items` carry the SAME list — a client reads whichever it already reads. The
375    /// `active_run_*` fields describe a run still in flight, so a cold launch into a chat the agent
376    /// is still working in can attach to it rather than render an idle screen.
377    ///
378    /// A session that does not exist is 404, not an empty list: "no messages yet" and "no such
379    /// session" must not render the same.
380    ///
381    /// `GET /api/v1/sessions/{sessionId}/messages`
382    ///
383    /// Required scopes: `sessions:read`.
384    pub async fn get_session_messages(&self, session_id: &str) -> Result<models::GetSessionMessagesResponse> {
385        self.client
386            .request_json(Request {
387                method: Method::GET,
388                path: format!("/api/v1/sessions/{}/messages", encode_path(session_id)),
389                query: NO_QUERY,
390                body: NO_BODY,
391                headers: Vec::new(),
392                idempotent: false,
393            })
394            .await
395    }
396
397    /// Get feedback for a run in session
398    ///
399    /// `GET /api/v1/sessions/{sessionId}/runs/{runId}/feedback`
400    ///
401    /// Required scopes: `sessions:read`.
402    pub async fn get_session_run_feedback(&self, session_id: &str, run_id: &str, params: &GetSessionRunFeedbackParams) -> Result<serde_json::Value> {
403        self.client
404            .request_json(Request {
405                method: Method::GET,
406                path: format!("/api/v1/sessions/{}/runs/{}/feedback", encode_path(session_id), encode_path(run_id)),
407                query: Some(params),
408                body: NO_BODY,
409                headers: Vec::new(),
410                idempotent: false,
411            })
412            .await
413    }
414
415    /// Get session share link status
416    ///
417    /// `GET /api/v1/sessions/{sessionId}/share`
418    ///
419    /// Required scopes: `sessions:read`.
420    pub async fn get_session_share(&self, session_id: &str) -> Result<models::GetSessionShareResponse> {
421        self.client
422            .request_json(Request {
423                method: Method::GET,
424                path: format!("/api/v1/sessions/{}/share", encode_path(session_id)),
425                query: NO_QUERY,
426                body: NO_BODY,
427                headers: Vec::new(),
428                idempotent: false,
429            })
430            .await
431    }
432
433    /// List sessions
434    ///
435    /// `GET /api/v1/sessions`
436    ///
437    /// Required scopes: `sessions:read`.
438    pub async fn list(&self, params: &ListSessionsParams) -> Result<models::ListSessionsResponse> {
439        self.client
440            .request_json(Request {
441                method: Method::GET,
442                path: "/api/v1/sessions".to_string(),
443                query: Some(params),
444                body: NO_BODY,
445                headers: Vec::new(),
446                idempotent: false,
447            })
448            .await
449    }
450
451    /// Stream every item returned by `listSessions`, following the `cursor` cursor until the server
452    /// reports no further pages.
453    pub fn list_all<'a>(&'a self, params: &'a ListSessionsParams) -> impl Stream<Item = Result<models::ListSessionsResponseItem>> + 'a {
454        async_stream::try_stream! {
455            let mut guard = CursorGuard::new();
456            let mut cursor = params.cursor.clone();
457            loop {
458                let mut page_params = params.clone();
459                page_params.cursor = cursor.clone();
460                let page = self.list(&page_params).await?;
461                let items = page.items;
462                let was_empty = items.is_empty();
463                for item in items {
464                    yield item;
465                }
466                match guard.advance(page.cursor, Some(page.has_more), was_empty) {
467                    Some(next) => cursor = Some(next),
468                    None => break,
469                }
470            }
471        }
472    }
473
474    /// List session annotations
475    ///
476    /// `GET /api/v1/sessions/{sessionId}/annotations`
477    ///
478    /// Required scopes: `sessions:read`.
479    pub async fn list_session_annotations(&self, session_id: &str) -> Result<models::ListSessionAnnotationsResponse> {
480        self.client
481            .request_json(Request {
482                method: Method::GET,
483                path: format!("/api/v1/sessions/{}/annotations", encode_path(session_id)),
484                query: NO_QUERY,
485                body: NO_BODY,
486                headers: Vec::new(),
487                idempotent: false,
488            })
489            .await
490    }
491
492    /// List artifacts across all runs in session
493    ///
494    /// `GET /api/v1/sessions/{sessionId}/artifacts`
495    ///
496    /// Required scopes: `sessions:read`.
497    pub async fn list_session_artifacts(&self, session_id: &str) -> Result<models::ListSessionArtifactsResponse> {
498        self.client
499            .request_json(Request {
500                method: Method::GET,
501                path: format!("/api/v1/sessions/{}/artifacts", encode_path(session_id)),
502                query: NO_QUERY,
503                body: NO_BODY,
504                headers: Vec::new(),
505                idempotent: false,
506            })
507            .await
508    }
509
510    /// List session branches
511    ///
512    /// `GET /api/v1/sessions/{sessionId}/branches`
513    ///
514    /// Required scopes: `sessions:read`.
515    pub async fn list_session_branches(&self, session_id: &str) -> Result<models::ListSessionBranchesResponse> {
516        self.client
517            .request_json(Request {
518                method: Method::GET,
519                path: format!("/api/v1/sessions/{}/branches", encode_path(session_id)),
520                query: NO_QUERY,
521                body: NO_BODY,
522                headers: Vec::new(),
523                idempotent: false,
524            })
525            .await
526    }
527
528    /// List session todos
529    ///
530    /// `GET /api/v1/sessions/{sessionId}/todos`
531    ///
532    /// Required scopes: `sessions:read`.
533    pub async fn list_session_todos(&self, session_id: &str, params: &ListSessionTodosParams) -> Result<models::ListSessionTodosResponse> {
534        self.client
535            .request_json(Request {
536                method: Method::GET,
537                path: format!("/api/v1/sessions/{}/todos", encode_path(session_id)),
538                query: Some(params),
539                body: NO_BODY,
540                headers: Vec::new(),
541                idempotent: false,
542            })
543            .await
544    }
545
546    /// List all todos across sessions
547    ///
548    /// `GET /api/v1/todos`
549    pub async fn list_todos(&self) -> Result<models::ListTodosResponse> {
550        self.client
551            .request_json(Request {
552                method: Method::GET,
553                path: "/api/v1/todos".to_string(),
554                query: NO_QUERY,
555                body: NO_BODY,
556                headers: Vec::new(),
557                idempotent: false,
558            })
559            .await
560    }
561
562    /// Resolve shared session (no auth)
563    ///
564    /// `GET /api/v1/shared/{shareId}`
565    pub async fn resolve_shared_session(&self, share_id: &str) -> Result<models::ResolveSharedSessionResponse> {
566        self.client
567            .request_json(Request {
568                method: Method::GET,
569                path: format!("/api/v1/shared/{}", encode_path(share_id)),
570                query: NO_QUERY,
571                body: NO_BODY,
572                headers: Vec::new(),
573                idempotent: false,
574            })
575            .await
576    }
577
578    /// Revoke session share link
579    ///
580    /// `DELETE /api/v1/sessions/{sessionId}/share`
581    ///
582    /// Required scopes: `sessions:write`.
583    pub async fn revoke_session_share(&self, session_id: &str) -> Result<models::RevokeSessionShareResponse> {
584        self.client
585            .request_json(Request {
586                method: Method::DELETE,
587                path: format!("/api/v1/sessions/{}/share", encode_path(session_id)),
588                query: NO_QUERY,
589                body: NO_BODY,
590                headers: Vec::new(),
591                idempotent: true,
592            })
593            .await
594    }
595
596    /// Run a pending todo immediately
597    ///
598    /// Dispatches the todo's run now instead of waiting for its due time. Only `pending` /
599    /// `pending_confirmation` todos can be run; anything else answers 409. A recurring todo keeps
600    /// its cron (next occurrence recomputed from now); a one-shot todo's schedule entry is dropped
601    /// so it cannot fire twice.
602    ///
603    /// `POST /api/v1/sessions/{sessionId}/todos/{todoId}/run`
604    ///
605    /// Required scopes: `sessions:write`.
606    pub async fn run_session_todo_now(&self, session_id: &str, todo_id: &str) -> Result<models::Todo> {
607        self.client
608            .request_json(Request {
609                method: Method::POST,
610                path: format!("/api/v1/sessions/{}/todos/{}/run", encode_path(session_id), encode_path(todo_id)),
611                query: NO_QUERY,
612                body: NO_BODY,
613                headers: Vec::new(),
614                idempotent: true,
615            })
616            .await
617    }
618
619    /// Send message
620    ///
621    /// Submits a user message to the session and schedules a run on the bound agent. Returns `202
622    /// Accepted` with the new run id; subscribe to `/api/v1/runs/{runId}/events` (SSE) for
623    /// streaming output.
624    ///
625    /// `POST /api/v1/sessions/{sessionId}/messages`
626    ///
627    /// Required scopes: `sessions:write`.
628    pub async fn send_session_message(&self, session_id: &str, body: &models::SendSessionMessageRequest) -> Result<models::SendSessionMessageResponse> {
629        self.client
630            .request_json(Request {
631                method: Method::POST,
632                path: format!("/api/v1/sessions/{}/messages", encode_path(session_id)),
633                query: NO_QUERY,
634                body: Some(body),
635                headers: Vec::new(),
636                idempotent: true,
637            })
638            .await
639    }
640
641    /// Save feedback/reaction for a session run
642    ///
643    /// `PUT /api/v1/sessions/{sessionId}/runs/{runId}/feedback`
644    ///
645    /// Required scopes: `sessions:write`.
646    pub async fn set_session_run_feedback(&self, session_id: &str, run_id: &str, body: &serde_json::Map<String, serde_json::Value>) -> Result<models::RunFeedbackSet> {
647        self.client
648            .request_json(Request {
649                method: Method::PUT,
650                path: format!("/api/v1/sessions/{}/runs/{}/feedback", encode_path(session_id), encode_path(run_id)),
651                query: NO_QUERY,
652                body: Some(body),
653                headers: Vec::new(),
654                idempotent: true,
655            })
656            .await
657    }
658
659    /// Stream session events (SSE)
660    ///
661    /// `GET /api/v1/sessions/{sessionId}/events`
662    ///
663    /// Required scopes: `events:read`.
664    ///
665    /// Returns a server-sent event stream.
666    pub fn stream_session_events(&self, session_id: &str, params: &StreamSessionEventsParams) -> EventStream {
667        let mut headers: Vec<(&'static str, String)> = Vec::new();
668        if let Some(value) = &params.last_event_id {
669            headers.push(("Last-Event-ID", value.clone()));
670        }
671        self.client.request_stream(
672            &format!("/api/v1/sessions/{}/events", encode_path(session_id)),
673            Some(params),
674            headers,
675        )
676    }
677
678    /// Update session metadata
679    ///
680    /// `PUT /api/v1/sessions/{sessionId}`
681    ///
682    /// Required scopes: `sessions:write`.
683    pub async fn update(&self, session_id: &str, body: &models::UpdateSessionRequest) -> Result<models::Session> {
684        self.client
685            .request_json(Request {
686                method: Method::PUT,
687                path: format!("/api/v1/sessions/{}", encode_path(session_id)),
688                query: NO_QUERY,
689                body: Some(body),
690                headers: Vec::new(),
691                idempotent: true,
692            })
693            .await
694    }
695
696    /// Update annotation (e.g. resolve)
697    ///
698    /// `PATCH /api/v1/sessions/{sessionId}/annotations/{annotationId}`
699    ///
700    /// Required scopes: `sessions:write`.
701    pub async fn update_session_annotation(&self, session_id: &str, annotation_id: &str, body: &models::UpdateSessionAnnotationRequest) -> Result<serde_json::Value> {
702        self.client
703            .request_json(Request {
704                method: Method::PATCH,
705                path: format!("/api/v1/sessions/{}/annotations/{}", encode_path(session_id), encode_path(annotation_id)),
706                query: NO_QUERY,
707                body: Some(body),
708                headers: Vec::new(),
709                idempotent: true,
710            })
711            .await
712    }
713
714    /// Update a todo
715    ///
716    /// `PATCH /api/v1/sessions/{sessionId}/todos/{todoId}`
717    ///
718    /// Required scopes: `sessions:write`.
719    pub async fn update_session_todo(&self, session_id: &str, todo_id: &str, body: &serde_json::Map<String, serde_json::Value>) -> Result<models::Todo> {
720        self.client
721            .request_json(Request {
722                method: Method::PATCH,
723                path: format!("/api/v1/sessions/{}/todos/{}", encode_path(session_id), encode_path(todo_id)),
724                query: NO_QUERY,
725                body: Some(body),
726                headers: Vec::new(),
727                idempotent: true,
728            })
729            .await
730    }
731}