uarp_sdk/generated/api/runs.rs
1// Code generated by @uarp/codegen from spec/openapi.json. DO NOT EDIT.
2//!
3//! Agent execution runs, SSE streaming, HITL, continuation
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 `getRun`.
20#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
21pub struct GetRunParams {
22 /// Set to `true` to include `changed_files` in the response. Opt-in because this endpoint is
23 /// polled and the paths live in their own key range: serving them unconditionally would add a
24 /// KV list to a hot read for every caller that never looks at them.
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub changed_files: Option<models::GetRunChangedFiles>,
27}
28
29/// Query and header parameters for `getRunFeedback`.
30#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
31pub struct GetRunFeedbackParams {
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub message_id: Option<String>,
34}
35
36/// Query and header parameters for `listRuns`.
37#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
38pub struct ListRunsParams {
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub agent_id: Option<String>,
41 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub session_id: Option<String>,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub status: Option<String>,
45 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub limit: Option<i64>,
47 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub cursor: Option<String>,
49 /// `desc` (default) newest first, `asc` oldest first. Any other value is a 400 rather than a
50 /// silent default, so a typo surfaces as an error instead of as plausible-looking data.
51 ///
52 /// Use `asc` instead of paging toward the end: the cursor is opaque, so there is no way to jump
53 /// to the far end, but `asc` puts that end on page one.
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub order: Option<models::ListRunsOrder>,
56}
57
58/// Query and header parameters for `streamRunEvents`.
59#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
60pub struct StreamRunEventsParams {
61 /// Short-lived SSE/WebSocket token (mint via `POST /api/v1/auth/sse-tokens`) or full API key.
62 /// Used by browser EventSource which cannot set Authorization header.
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub token: Option<String>,
65 /// SSE resumption cursor. The browser's EventSource sets this automatically on reconnect;
66 /// servers replay events strictly after this id.
67 #[serde(skip)]
68 pub last_event_id: Option<String>,
69}
70
71/// Query and header parameters for `waitRun`.
72#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
73pub struct WaitRunParams {
74 /// Max seconds to wait. Default from server config (e.g. 60).
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub timeout_sec: Option<i64>,
77}
78
79/// Agent execution runs, SSE streaming, HITL, continuation
80#[derive(Debug, Clone)]
81pub struct RunsApi {
82 pub(crate) client: Client,
83}
84
85impl Client {
86 /// Agent execution runs, SSE streaming, HITL, continuation
87 pub fn runs(&self) -> RunsApi {
88 RunsApi { client: self.clone() }
89 }
90}
91
92impl RunsApi {
93 /// Approve a pending tool call (HITL)
94 ///
95 /// The body is optional; sending none approves without a message. `reject` has always taken a
96 /// body, and the asymmetry was an omission rather than a design.
97 ///
98 /// `POST /api/v1/runs/{runId}/approve`
99 ///
100 /// Required scopes: `runs:create`.
101 pub async fn approve_run(&self, run_id: &str, body: &models::RunApproveRequest) -> Result<models::ApproveRunResponse> {
102 self.client
103 .request_json(Request {
104 method: Method::POST,
105 path: format!("/api/v1/runs/{}/approve", encode_path(run_id)),
106 query: NO_QUERY,
107 body: Some(body),
108 headers: Vec::new(),
109 idempotent: true,
110 })
111 .await
112 }
113
114 /// Cancel a run
115 ///
116 /// `POST /api/v1/runs/{runId}/cancel`
117 ///
118 /// Required scopes: `runs:create`.
119 pub async fn cancel(&self, run_id: &str) -> Result<models::CancelRunResponse> {
120 self.client
121 .request_json(Request {
122 method: Method::POST,
123 path: format!("/api/v1/runs/{}/cancel", encode_path(run_id)),
124 query: NO_QUERY,
125 body: NO_BODY,
126 headers: Vec::new(),
127 idempotent: true,
128 })
129 .await
130 }
131
132 /// Continue a run from a continuation token
133 ///
134 /// Decodes the token, loads the checkpoint, and re-schedules the run.
135 ///
136 /// `POST /api/v1/runs/{runId}/continue`
137 ///
138 /// Required scopes: `runs:create`.
139 pub async fn continue_run(&self, run_id: &str, body: &models::ContinueRunRequest) -> Result<models::ContinueRunResponse> {
140 self.client
141 .request_json(Request {
142 method: Method::POST,
143 path: format!("/api/v1/runs/{}/continue", encode_path(run_id)),
144 query: NO_QUERY,
145 body: Some(body),
146 headers: Vec::new(),
147 idempotent: true,
148 })
149 .await
150 }
151
152 /// Create and schedule a run
153 ///
154 /// Creates a run and schedules it for execution. Recommended: send Idempotency-Key header to
155 /// avoid duplicate runs on retries; a repeated request with the same key returns the cached 202
156 /// response (same run_id). Pin a specific agent version with `version` to bypass the head
157 /// pointer.
158 ///
159 /// `POST /api/v1/runs`
160 ///
161 /// Required scopes: `runs:create`.
162 pub async fn create(&self, body: &models::CreateRunRequest) -> Result<models::Run> {
163 self.client
164 .request_json(Request {
165 method: Method::POST,
166 path: "/api/v1/runs".to_string(),
167 query: NO_QUERY,
168 body: Some(body),
169 headers: Vec::new(),
170 idempotent: true,
171 })
172 .await
173 }
174
175 /// Create checkpoint for a run
176 ///
177 /// `POST /api/v1/runs/{runId}/checkpoint`
178 ///
179 /// Required scopes: `runs:create`.
180 pub async fn create_run_checkpoint(&self, run_id: &str) -> Result<models::RunCheckpoint> {
181 self.client
182 .request_json(Request {
183 method: Method::POST,
184 path: format!("/api/v1/runs/{}/checkpoint", encode_path(run_id)),
185 query: NO_QUERY,
186 body: NO_BODY,
187 headers: Vec::new(),
188 idempotent: true,
189 })
190 .await
191 }
192
193 /// What will this run cost
194 ///
195 /// Prices a run before it happens, from the agent's own recent runs. Read-only: it dispatches
196 /// nothing and stores nothing, and it needs only `runs:read`.
197 ///
198 /// When the model has no known rate the answer is still 200 with `estimated_cost_usd: 0` and
199 /// `pricing: "unknown"` — read `basis.pricing` before showing the figure, or a client will
200 /// present “free” for “we have no idea”.
201 ///
202 /// `POST /api/v1/runs/estimate`
203 ///
204 /// Required scopes: `runs:read`.
205 pub async fn estimate_run_cost(&self, body: &models::EstimateRunCostRequest) -> Result<models::RunCostEstimate> {
206 self.client
207 .request_json(Request {
208 method: Method::POST,
209 path: "/api/v1/runs/estimate".to_string(),
210 query: NO_QUERY,
211 body: Some(body),
212 headers: Vec::new(),
213 idempotent: true,
214 })
215 .await
216 }
217
218 /// Export run events as JSONL
219 ///
220 /// `GET /api/v1/runs/{runId}/events/export`
221 ///
222 /// Required scopes: `runs:read`.
223 pub async fn export_run_events(&self, run_id: &str) -> Result<String> {
224 self.client
225 .request_text(Request {
226 method: Method::GET,
227 path: format!("/api/v1/runs/{}/events/export", encode_path(run_id)),
228 query: NO_QUERY,
229 body: NO_BODY,
230 headers: Vec::new(),
231 idempotent: false,
232 })
233 .await
234 }
235
236 /// Get run status and result
237 ///
238 /// `GET /api/v1/runs/{runId}`
239 ///
240 /// Required scopes: `runs:read`.
241 pub async fn get(&self, run_id: &str, params: &GetRunParams) -> Result<models::GetRunResponse> {
242 self.client
243 .request_json(Request {
244 method: Method::GET,
245 path: format!("/api/v1/runs/{}", encode_path(run_id)),
246 query: Some(params),
247 body: NO_BODY,
248 headers: Vec::new(),
249 idempotent: false,
250 })
251 .await
252 }
253
254 /// Get audit trail for a run
255 ///
256 /// `GET /api/v1/runs/{runId}/audit-log`
257 ///
258 /// Required scopes: `runs:read`.
259 pub async fn get_run_audit_log(&self, run_id: &str) -> Result<models::GetRunAuditLogResponse> {
260 self.client
261 .request_json(Request {
262 method: Method::GET,
263 path: format!("/api/v1/runs/{}/audit-log", encode_path(run_id)),
264 query: NO_QUERY,
265 body: NO_BODY,
266 headers: Vec::new(),
267 idempotent: false,
268 })
269 .await
270 }
271
272 /// Get user feedback for a run
273 ///
274 /// `GET /api/v1/runs/{runId}/feedback`
275 ///
276 /// Required scopes: `runs:read`.
277 pub async fn get_run_feedback(&self, run_id: &str, params: &GetRunFeedbackParams) -> Result<serde_json::Value> {
278 self.client
279 .request_json(Request {
280 method: Method::GET,
281 path: format!("/api/v1/runs/{}/feedback", encode_path(run_id)),
282 query: Some(params),
283 body: NO_BODY,
284 headers: Vec::new(),
285 idempotent: false,
286 })
287 .await
288 }
289
290 /// Get run queue position
291 ///
292 /// `GET /api/v1/runs/{runId}/queue-position`
293 ///
294 /// Required scopes: `runs:read`.
295 pub async fn get_run_queue_position(&self, run_id: &str) -> Result<models::GetRunQueuePositionResponse> {
296 self.client
297 .request_json(Request {
298 method: Method::GET,
299 path: format!("/api/v1/runs/{}/queue-position", encode_path(run_id)),
300 query: NO_QUERY,
301 body: NO_BODY,
302 headers: Vec::new(),
303 idempotent: false,
304 })
305 .await
306 }
307
308 /// List steps for a run
309 ///
310 /// Returns the ordered list of steps executed during a run, with per-step metrics including
311 /// tokens, cost, and tool calls.
312 ///
313 /// `GET /api/v1/runs/{runId}/steps`
314 ///
315 /// Required scopes: `runs:read`.
316 pub async fn get_run_steps(&self, run_id: &str) -> Result<models::GetRunStepsResponse> {
317 self.client
318 .request_json(Request {
319 method: Method::GET,
320 path: format!("/api/v1/runs/{}/steps", encode_path(run_id)),
321 query: NO_QUERY,
322 body: NO_BODY,
323 headers: Vec::new(),
324 idempotent: false,
325 })
326 .await
327 }
328
329 /// List all runs for tenant
330 ///
331 /// Ordered NEWEST FIRST, and that is a guarantee, not an accident of storage: page one is the
332 /// most recent runs. Do not page toward the end to find recent activity — a client that walks
333 /// `has_more` looking for the newest page now walks away from it. This was previously true only
334 /// of the handler, so clients hedged by paging or by re-sorting, and one shipped a twelve-hop
335 /// walk that reversed meaning the day the order changed. Note the sibling
336 /// `/api/v1/teams/{teamId}/runs` is deliberately the other way round — oldest first — because a
337 /// team transcript reads forward.
338 ///
339 /// `GET /api/v1/runs`
340 ///
341 /// Required scopes: `runs:read`.
342 pub async fn list(&self, params: &ListRunsParams) -> Result<models::ListRunsResponse> {
343 self.client
344 .request_json(Request {
345 method: Method::GET,
346 path: "/api/v1/runs".to_string(),
347 query: Some(params),
348 body: NO_BODY,
349 headers: Vec::new(),
350 idempotent: false,
351 })
352 .await
353 }
354
355 /// Stream every item returned by `listRuns`, following the `cursor` cursor until the server
356 /// reports no further pages.
357 pub fn list_all<'a>(&'a self, params: &'a ListRunsParams) -> impl Stream<Item = Result<models::Run>> + 'a {
358 async_stream::try_stream! {
359 let mut guard = CursorGuard::new();
360 let mut cursor = params.cursor.clone();
361 loop {
362 let mut page_params = params.clone();
363 page_params.cursor = cursor.clone();
364 let page = self.list(&page_params).await?;
365 let items = page.items;
366 let was_empty = items.is_empty();
367 for item in items {
368 yield item;
369 }
370 match guard.advance(page.cursor, Some(page.has_more), was_empty) {
371 Some(next) => cursor = Some(next),
372 None => break,
373 }
374 }
375 }
376 }
377
378 /// List run artifacts
379 ///
380 /// `GET /api/v1/runs/{runId}/artifacts`
381 ///
382 /// Required scopes: `runs:read`.
383 pub async fn list_run_artifacts(&self, run_id: &str) -> Result<models::ListRunArtifactsResponse> {
384 self.client
385 .request_json(Request {
386 method: Method::GET,
387 path: format!("/api/v1/runs/{}/artifacts", encode_path(run_id)),
388 query: NO_QUERY,
389 body: NO_BODY,
390 headers: Vec::new(),
391 idempotent: false,
392 })
393 .await
394 }
395
396 /// List checkpoints for a run
397 ///
398 /// `GET /api/v1/runs/{runId}/checkpoints`
399 ///
400 /// Required scopes: `runs:read`.
401 pub async fn list_run_checkpoints(&self, run_id: &str) -> Result<models::ListRunCheckpointsResponse> {
402 self.client
403 .request_json(Request {
404 method: Method::GET,
405 path: format!("/api/v1/runs/{}/checkpoints", encode_path(run_id)),
406 query: NO_QUERY,
407 body: NO_BODY,
408 headers: Vec::new(),
409 idempotent: false,
410 })
411 .await
412 }
413
414 /// Pause a run
415 ///
416 /// `POST /api/v1/runs/{runId}/pause`
417 ///
418 /// Required scopes: `runs:create`.
419 pub async fn pause_run(&self, run_id: &str) -> Result<models::PauseRunResponse> {
420 self.client
421 .request_json(Request {
422 method: Method::POST,
423 path: format!("/api/v1/runs/{}/pause", encode_path(run_id)),
424 query: NO_QUERY,
425 body: NO_BODY,
426 headers: Vec::new(),
427 idempotent: true,
428 })
429 .await
430 }
431
432 /// Reject a pending tool call (HITL)
433 ///
434 /// `POST /api/v1/runs/{runId}/reject`
435 ///
436 /// Required scopes: `runs:create`.
437 pub async fn reject_run(&self, run_id: &str, body: &models::RejectRunRequest) -> Result<models::RejectRunResponse> {
438 self.client
439 .request_json(Request {
440 method: Method::POST,
441 path: format!("/api/v1/runs/{}/reject", encode_path(run_id)),
442 query: NO_QUERY,
443 body: Some(body),
444 headers: Vec::new(),
445 idempotent: true,
446 })
447 .await
448 }
449
450 /// Replay a run for determinism check
451 ///
452 /// `POST /api/v1/runs/{runId}/replay`
453 ///
454 /// Required scopes: `runs:read`.
455 pub async fn replay_run(&self, run_id: &str) -> Result<serde_json::Map<String, serde_json::Value>> {
456 self.client
457 .request_json(Request {
458 method: Method::POST,
459 path: format!("/api/v1/runs/{}/replay", encode_path(run_id)),
460 query: NO_QUERY,
461 body: NO_BODY,
462 headers: Vec::new(),
463 idempotent: true,
464 })
465 .await
466 }
467
468 /// Send user input response to a paused run
469 ///
470 /// `POST /api/v1/runs/{runId}/respond`
471 ///
472 /// Required scopes: `runs:create`.
473 pub async fn respond_to_run(&self, run_id: &str, body: &models::RespondToRunRequest) -> Result<models::RespondToRunResponse> {
474 self.client
475 .request_json(Request {
476 method: Method::POST,
477 path: format!("/api/v1/runs/{}/respond", encode_path(run_id)),
478 query: NO_QUERY,
479 body: Some(body),
480 headers: Vec::new(),
481 idempotent: true,
482 })
483 .await
484 }
485
486 /// Resume a run
487 ///
488 /// `POST /api/v1/runs/{runId}/resume`
489 ///
490 /// Required scopes: `runs:create`.
491 pub async fn resume(&self, run_id: &str) -> Result<models::ResumeRunResponse> {
492 self.client
493 .request_json(Request {
494 method: Method::POST,
495 path: format!("/api/v1/runs/{}/resume", encode_path(run_id)),
496 query: NO_QUERY,
497 body: NO_BODY,
498 headers: Vec::new(),
499 idempotent: true,
500 })
501 .await
502 }
503
504 /// Save user feedback/reaction for a run
505 ///
506 /// One reaction per (message, caller); a second PUT for the same `message_id` replaces the
507 /// first. `message_id` is whatever string the client attaches to a message — the platform
508 /// stores it verbatim (max 256 chars) and does not check it against the transcript, which today
509 /// carries no message identifier (see `getSessionMessages`). Unknown body fields are dropped.
510 /// There is no way to remove a reaction: `null` and `""` are rejected with 422 and DELETE is
511 /// 405 (measured 2026-09-10).
512 ///
513 /// `PUT /api/v1/runs/{runId}/feedback`
514 ///
515 /// Required scopes: `runs:create`.
516 pub async fn set_run_feedback(&self, run_id: &str, body: &models::SetRunFeedbackRequest) -> Result<models::RunFeedbackSet> {
517 self.client
518 .request_json(Request {
519 method: Method::PUT,
520 path: format!("/api/v1/runs/{}/feedback", encode_path(run_id)),
521 query: NO_QUERY,
522 body: Some(body),
523 headers: Vec::new(),
524 idempotent: true,
525 })
526 .await
527 }
528
529 /// Stream run events via SSE
530 ///
531 /// Real-time event stream for a run. Supports `Last-Event-ID` header (or `?last_event_id=`) for
532 /// reconnection. Stream closes when run reaches terminal status. Each event payload includes
533 /// `stream_type`: lifecycle (run.*), assistant (llm.chunk), tool (tool.*), or other — use it to
534 /// filter client-side. Browsers using EventSource (which cannot set Authorization headers)
535 /// should mint a 60-s SSE token via `POST /api/v1/auth/sse-tokens` and pass it as `?token=`.
536 ///
537 /// `GET /api/v1/runs/{runId}/events`
538 ///
539 /// Required scopes: `events:read`.
540 ///
541 /// Returns a server-sent event stream.
542 pub fn stream_run_events(&self, run_id: &str, params: &StreamRunEventsParams) -> EventStream {
543 let mut headers: Vec<(&'static str, String)> = Vec::new();
544 if let Some(value) = ¶ms.last_event_id {
545 headers.push(("Last-Event-ID", value.clone()));
546 }
547 self.client.request_stream(
548 &format!("/api/v1/runs/{}/events", encode_path(run_id)),
549 Some(params),
550 headers,
551 )
552 }
553
554 /// Wait for run to reach terminal status
555 ///
556 /// If the run is already completed, failed, cancelled, timeout, or guardrail_blocked, returns
557 /// 200 with the run immediately. Otherwise polls until terminal status or timeout_sec. On
558 /// timeout returns 202 with status still_running.
559 ///
560 /// `GET /api/v1/runs/{runId}/wait`
561 ///
562 /// Required scopes: `runs:read`.
563 pub async fn wait_run(&self, run_id: &str, params: &WaitRunParams) -> Result<models::Run> {
564 self.client
565 .request_json(Request {
566 method: Method::GET,
567 path: format!("/api/v1/runs/{}/wait", encode_path(run_id)),
568 query: Some(params),
569 body: NO_BODY,
570 headers: Vec::new(),
571 idempotent: false,
572 })
573 .await
574 }
575}