Add pause/unpause run endpoints

Add POST /runs/{id}/pause and POST /runs/{id}/unpause endpoints
following the cancel endpoint conventions, with paused RunStatus variant,
demo stubs, and regenerated TypeScript client.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-08 14:36:36 -04:00
parent be358944c0
commit e73f2ab00e
6 changed files with 315 additions and 47 deletions

View file

@ -78,19 +78,13 @@ pub async fn get_stage_turns(
paginated_response(runs::turns(), &pagination)
}
#[derive(serde::Deserialize)]
pub struct CompareQuery {
#[allow(dead_code)]
checkpoint: Option<String>,
}
pub async fn get_run_compare(
pub async fn get_run_files(
_auth: AuthenticatedService,
State(_state): State<Arc<AppState>>,
Path(_id): Path<String>,
Query(_q): Query<CompareQuery>,
Query(pagination): Query<PaginationParams>,
) -> Response {
(StatusCode::OK, Json(runs::compare())).into_response()
paginated_response(runs::files(), &pagination)
}
pub async fn get_run_usage(
@ -208,6 +202,22 @@ pub async fn cancel_stub(
(StatusCode::OK, Json(serde_json::json!({"id": _id, "status": "cancelled", "created_at": "2026-03-06T14:30:00Z"}))).into_response()
}
pub async fn pause_stub(
_auth: AuthenticatedService,
State(_state): State<Arc<AppState>>,
Path(_id): Path<String>,
) -> Response {
(StatusCode::OK, Json(serde_json::json!({"id": _id, "status": "paused", "created_at": "2026-03-06T14:30:00Z"}))).into_response()
}
pub async fn unpause_stub(
_auth: AuthenticatedService,
State(_state): State<Arc<AppState>>,
Path(_id): Path<String>,
) -> Response {
(StatusCode::OK, Json(serde_json::json!({"id": _id, "status": "running", "created_at": "2026-03-06T14:30:00Z"}))).into_response()
}
pub async fn get_run_graph(
_auth: AuthenticatedService,
State(_state): State<Arc<AppState>>,
@ -1071,15 +1081,8 @@ mod runs {
]
}
pub fn compare() -> RunCompare {
RunCompare {
checkpoints: vec![
FileCheckpoint { id: "cp-4".into(), label: "Checkpoint 4 — Apply Changes".into() },
FileCheckpoint { id: "cp-3".into(), label: "Checkpoint 3 — Review Changes".into() },
FileCheckpoint { id: "cp-2".into(), label: "Checkpoint 2 — Propose Changes".into() },
FileCheckpoint { id: "cp-1".into(), label: "Checkpoint 1 — Detect Drift".into() },
],
files: vec![
pub fn files() -> Vec<FileDiff> {
vec![
FileDiff {
old_file: DiffFile { name: "src/commands/run.ts".into(), contents: "import { parseArgs } from \"node:util\";\nimport { loadConfig } from \"../config.js\";\nimport { execute } from \"../executor.js\";\n\ninterface RunOptions {\n config: string;\n dryRun: boolean;\n}\n\nexport async function run(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n config: { type: \"string\", short: \"c\", default: \"arc.toml\" },\n \"dry-run\": { type: \"boolean\", default: false },\n },\n });\n\n const opts: RunOptions = {\n config: values.config ?? \"arc.toml\",\n dryRun: values[\"dry-run\"] ?? false,\n };\n\n const config = await loadConfig(opts.config);\n const result = await execute(config, { dryRun: opts.dryRun });\n\n if (result.success) {\n console.log(\"Run completed successfully.\");\n } else {\n console.error(\"Run failed:\", result.error);\n process.exitCode = 1;\n }\n}\n".into() },
new_file: DiffFile { name: "src/commands/run.ts".into(), contents: "import { parseArgs } from \"node:util\";\nimport { loadConfig } from \"../config.js\";\nimport { execute } from \"../executor.js\";\nimport { createLogger, type Logger } from \"../logger.js\";\n\ninterface RunOptions {\n config: string;\n dryRun: boolean;\n verbose: boolean;\n}\n\nexport async function run(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n config: { type: \"string\", short: \"c\", default: \"arc.toml\" },\n \"dry-run\": { type: \"boolean\", default: false },\n verbose: { type: \"boolean\", short: \"v\", default: false },\n },\n });\n\n const opts: RunOptions = {\n config: values.config ?? \"arc.toml\",\n dryRun: values[\"dry-run\"] ?? false,\n verbose: values.verbose ?? false,\n };\n\n const logger: Logger = createLogger({ verbose: opts.verbose });\n\n const config = await loadConfig(opts.config);\n logger.debug(\"Loaded config from %s\", opts.config);\n\n const result = await execute(config, { dryRun: opts.dryRun, logger });\n logger.debug(\"Execution finished in %dms\", result.elapsed);\n\n if (result.success) {\n console.log(\"Run completed successfully.\");\n } else {\n console.error(\"Run failed:\", result.error);\n process.exitCode = 1;\n }\n}\n".into() },
@ -1092,9 +1095,7 @@ mod runs {
old_file: DiffFile { name: "src/executor.ts".into(), contents: "import type { Config } from \"./config.js\";\n\ninterface ExecuteOptions {\n dryRun: boolean;\n}\n\ninterface ExecuteResult {\n success: boolean;\n error?: string;\n}\n\nexport async function execute(\n config: Config,\n options: ExecuteOptions,\n): Promise<ExecuteResult> {\n if (options.dryRun) {\n console.log(\"Dry run — skipping execution.\");\n return { success: true };\n }\n\n try {\n for (const step of config.steps) {\n await step.run();\n }\n return { success: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: message };\n }\n}\n".into() },
new_file: DiffFile { name: "src/executor.ts".into(), contents: "import type { Config } from \"./config.js\";\nimport type { Logger } from \"./logger.js\";\n\ninterface ExecuteOptions {\n dryRun: boolean;\n logger: Logger;\n}\n\ninterface ExecuteResult {\n success: boolean;\n elapsed: number;\n error?: string;\n}\n\nexport async function execute(\n config: Config,\n options: ExecuteOptions,\n): Promise<ExecuteResult> {\n const start = performance.now();\n\n if (options.dryRun) {\n options.logger.info(\"Dry run — skipping execution.\");\n return { success: true, elapsed: performance.now() - start };\n }\n\n try {\n for (const step of config.steps) {\n options.logger.debug(\"Running step: %s\", step.name);\n await step.run();\n }\n return { success: true, elapsed: performance.now() - start };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, elapsed: performance.now() - start, error: message };\n }\n}\n".into() },
},
],
stats: DiffStats { additions: 567, deletions: 234 },
}
]
}
pub fn usage() -> RunUsage {

View file

@ -164,6 +164,8 @@ fn demo_routes() -> Router<Arc<AppState>> {
.route("/runs/{id}/checkpoint", get(crate::demo::checkpoint_stub))
.route("/runs/{id}/context", get(crate::demo::context_stub))
.route("/runs/{id}/cancel", post(crate::demo::cancel_stub))
.route("/runs/{id}/pause", post(crate::demo::pause_stub))
.route("/runs/{id}/unpause", post(crate::demo::unpause_stub))
.route("/runs/{id}/graph", get(crate::demo::get_run_graph))
.route("/runs/{id}/retro", get(crate::demo::get_run_retro))
.route("/runs/{id}/stages", get(crate::demo::get_run_stages))
@ -171,7 +173,7 @@ fn demo_routes() -> Router<Arc<AppState>> {
"/runs/{id}/stages/{stageId}/turns",
get(crate::demo::get_stage_turns),
)
.route("/runs/{id}/compare", get(crate::demo::get_run_compare))
.route("/runs/{id}/files", get(crate::demo::get_run_files))
.route("/runs/{id}/usage", get(crate::demo::get_run_usage))
.route(
"/runs/{id}/verification",
@ -251,11 +253,13 @@ fn real_routes() -> Router<Arc<AppState>> {
.route("/runs/{id}/checkpoint", get(get_checkpoint))
.route("/runs/{id}/context", get(get_context))
.route("/runs/{id}/cancel", post(cancel_run))
.route("/runs/{id}/pause", post(pause_run))
.route("/runs/{id}/unpause", post(unpause_run))
.route("/runs/{id}/graph", get(get_graph))
.route("/runs/{id}/retro", get(get_retro))
.route("/runs/{id}/stages", get(not_implemented))
.route("/runs/{id}/stages/{stageId}/turns", get(not_implemented))
.route("/runs/{id}/compare", get(not_implemented))
.route("/runs/{id}/files", get(not_implemented))
.route("/runs/{id}/usage", get(not_implemented))
.route("/runs/{id}/verification", get(not_implemented))
.route("/runs/{id}/configuration", get(not_implemented))
@ -457,7 +461,7 @@ async fn start_run(
Json(req): Json<StartRunRequest>,
) -> Response {
// Parse the DOT source
let graph = match arc_workflows::workflow::prepare_workflow(&req.dot_source) {
let graph = match arc_workflows::workflow::prepare_from_source(&req.dot_source) {
Ok(g) => g,
Err(e) => {
return ApiError::bad_request(e.to_string()).into_response();
@ -964,6 +968,64 @@ async fn cancel_run(
}
}
async fn pause_run(
_auth: AuthenticatedService,
State(state): State<Arc<AppState>>,
Path(id): Path<String>,
) -> Response {
let mut runs = state.runs.lock().expect("runs lock poisoned");
match runs.get_mut(&id) {
Some(managed_run) => match managed_run.status {
RunStatus::Running => {
managed_run.status = RunStatus::Paused;
let created_at = managed_run.created_at;
(
StatusCode::OK,
Json(RunStatusResponse {
id: id.clone(),
status: RunStatus::Paused,
error: None,
queue_position: None,
created_at,
}),
)
.into_response()
}
_ => ApiError::new(StatusCode::CONFLICT, "Run is not pausable.").into_response(),
},
None => ApiError::not_found("Run not found.").into_response(),
}
}
async fn unpause_run(
_auth: AuthenticatedService,
State(state): State<Arc<AppState>>,
Path(id): Path<String>,
) -> Response {
let mut runs = state.runs.lock().expect("runs lock poisoned");
match runs.get_mut(&id) {
Some(managed_run) => match managed_run.status {
RunStatus::Paused => {
managed_run.status = RunStatus::Running;
let created_at = managed_run.created_at;
(
StatusCode::OK,
Json(RunStatusResponse {
id: id.clone(),
status: RunStatus::Running,
error: None,
queue_position: None,
created_at,
}),
)
.into_response()
}
_ => ApiError::new(StatusCode::CONFLICT, "Run is not paused.").into_response(),
},
None => ApiError::not_found("Run not found.").into_response(),
}
}
async fn test_model(
_auth: AuthenticatedService,
State(state): State<Arc<AppState>>,

View file

@ -199,6 +199,62 @@ paths:
schema:
$ref: "#/components/schemas/ErrorResponse"
/runs/{id}/pause:
post:
operationId: pauseRun
tags: [Runs]
summary: Pause Run
description: Pauses a running run. Returns 409 if the run is not running.
parameters:
- $ref: "#/components/parameters/RunId"
responses:
"200":
description: Run paused
content:
application/json:
schema:
$ref: "#/components/schemas/RunStatusResponse"
"404":
description: Run not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Run is not running
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/runs/{id}/unpause:
post:
operationId: unpauseRun
tags: [Runs]
summary: Unpause Run
description: Resumes a paused run. Returns 409 if the run is not paused.
parameters:
- $ref: "#/components/parameters/RunId"
responses:
"200":
description: Run unpaused
content:
application/json:
schema:
$ref: "#/components/schemas/RunStatusResponse"
"404":
description: Run not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Run is not paused
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/runs/{id}/graph:
get:
operationId: retrieveRunGraph
@ -436,22 +492,24 @@ paths:
schema:
$ref: "#/components/schemas/ErrorResponse"
/runs/{id}/compare:
/runs/{id}/files:
get:
operationId: retrieveRunCompare
operationId: retrieveRunFiles
tags: [Run Outputs]
summary: Retrieve Run Compare
description: Returns file-level diffs produced by the run, optionally filtered to a specific checkpoint.
summary: Retrieve Run Files
description: Returns a paginated list of file-level diffs produced by the run, optionally filtered to a specific checkpoint.
parameters:
- $ref: "#/components/parameters/RunId"
- $ref: "#/components/parameters/CheckpointFilter"
- $ref: "#/components/parameters/PageLimit"
- $ref: "#/components/parameters/PageOffset"
responses:
"200":
description: File changes with checkpoint metadata
description: Paginated list of file diffs
content:
application/json:
schema:
$ref: "#/components/schemas/RunCompare"
$ref: "#/components/schemas/PaginatedRunFileList"
"404":
description: Run not found
content:
@ -1680,6 +1738,7 @@ components:
- completed
- failed
- cancelled
- paused
StartRunRequest:
description: Request body for starting a new run from a DOT graph source.
@ -2367,7 +2426,7 @@ components:
items:
$ref: "#/components/schemas/ToolUse"
# ── Compare / Diff Schemas ───────────────────────────────────────────
# ── File Diff Schemas ──────────────────────────────────────────────
FileCheckpoint:
description: A named checkpoint within a run, used to filter file diffs.
@ -2429,26 +2488,19 @@ components:
description: Total lines deleted.
example: 234
RunCompare:
description: File-level diff output for a run, with checkpoint filtering support.
PaginatedRunFileList:
description: Paginated list of file diffs produced by a run.
type: object
required:
- checkpoints
- files
- stats
- data
- meta
properties:
checkpoints:
data:
type: array
description: Available checkpoints for filtering.
items:
$ref: "#/components/schemas/FileCheckpoint"
files:
type: array
description: File diffs, optionally filtered by checkpoint.
items:
$ref: "#/components/schemas/FileDiff"
stats:
$ref: "#/components/schemas/DiffStats"
meta:
$ref: "#/components/schemas/PaginationMeta"
# ── Usage Schemas ────────────────────────────────────────────────────

View file

@ -180,6 +180,8 @@
"POST /runs",
"GET /runs/{id}",
"POST /runs/{id}/cancel",
"POST /runs/{id}/pause",
"POST /runs/{id}/unpause",
"GET /runs/{id}/graph",
"GET /runs/{id}/events"
]
@ -198,7 +200,7 @@
"group": "Run Outputs",
"icon": "file-export",
"pages": [
"GET /runs/{id}/compare",
"GET /runs/{id}/files",
"GET /runs/{id}/usage",
{
"group": "Run Internals",

View file

@ -122,6 +122,47 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
options: localVarRequestOptions,
};
},
/**
* Pauses a running run. Returns 409 if the run is not running.
* @summary Pause Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
pauseRun: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('pauseRun', 'id', id)
const localVarPath = `/runs/{id}/pause`
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication mTLS required
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
localVarHeaderParameter['Accept'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Returns the current status of a run, including error details and queue position if applicable.
* @summary Retrieve Run
@ -282,6 +323,47 @@ export const RunsApiAxiosParamCreator = function (configuration?: Configuration)
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Resumes a paused run. Returns 409 if the run is not paused.
* @summary Unpause Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
unpauseRun: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'id' is not null or undefined
assertParamExists('unpauseRun', 'id', id)
const localVarPath = `/runs/{id}/unpause`
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication mTLS required
await setApiKeyToObject(localVarHeaderParameter, "X-mTLS-Client-CN", configuration)
// authentication BearerAuth required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
localVarHeaderParameter['Accept'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
@ -323,6 +405,19 @@ export const RunsApiFp = function(configuration?: Configuration) {
const localVarOperationServerBasePath = operationServerMap['RunsApi.listRuns']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Pauses a running run. Returns 409 if the run is not running.
* @summary Pause Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async pauseRun(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RunStatusResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.pauseRun(id, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['RunsApi.pauseRun']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Returns the current status of a run, including error details and queue position if applicable.
* @summary Retrieve Run
@ -375,6 +470,19 @@ export const RunsApiFp = function(configuration?: Configuration) {
const localVarOperationServerBasePath = operationServerMap['RunsApi.streamRunEvents']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
/**
* Resumes a paused run. Returns 409 if the run is not paused.
* @summary Unpause Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async unpauseRun(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RunStatusResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.unpauseRun(id, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['RunsApi.unpauseRun']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
}
};
@ -405,6 +513,16 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
listRuns(pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise<PaginatedRunList> {
return localVarFp.listRuns(pageLimit, pageOffset, options).then((request) => request(axios, basePath));
},
/**
* Pauses a running run. Returns 409 if the run is not running.
* @summary Pause Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
pauseRun(id: string, options?: RawAxiosRequestConfig): AxiosPromise<RunStatusResponse> {
return localVarFp.pauseRun(id, options).then((request) => request(axios, basePath));
},
/**
* Returns the current status of a run, including error details and queue position if applicable.
* @summary Retrieve Run
@ -445,6 +563,16 @@ export const RunsApiFactory = function (configuration?: Configuration, basePath?
streamRunEvents(id: string, options?: RawAxiosRequestConfig): AxiosPromise<string> {
return localVarFp.streamRunEvents(id, options).then((request) => request(axios, basePath));
},
/**
* Resumes a paused run. Returns 409 if the run is not paused.
* @summary Unpause Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
unpauseRun(id: string, options?: RawAxiosRequestConfig): AxiosPromise<RunStatusResponse> {
return localVarFp.unpauseRun(id, options).then((request) => request(axios, basePath));
},
};
};
@ -475,6 +603,17 @@ export class RunsApi extends BaseAPI {
return RunsApiFp(this.configuration).listRuns(pageLimit, pageOffset, options).then((request) => request(this.axios, this.basePath));
}
/**
* Pauses a running run. Returns 409 if the run is not running.
* @summary Pause Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public pauseRun(id: string, options?: RawAxiosRequestConfig) {
return RunsApiFp(this.configuration).pauseRun(id, options).then((request) => request(this.axios, this.basePath));
}
/**
* Returns the current status of a run, including error details and queue position if applicable.
* @summary Retrieve Run
@ -518,5 +657,16 @@ export class RunsApi extends BaseAPI {
public streamRunEvents(id: string, options?: RawAxiosRequestConfig) {
return RunsApiFp(this.configuration).streamRunEvents(id, options).then((request) => request(this.axios, this.basePath));
}
/**
* Resumes a paused run. Returns 409 if the run is not paused.
* @summary Unpause Run
* @param {string} id Unique run identifier (ULID).
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
public unpauseRun(id: string, options?: RawAxiosRequestConfig) {
return RunsApiFp(this.configuration).unpauseRun(id, options).then((request) => request(this.axios, this.basePath));
}
}

View file

@ -24,7 +24,8 @@ export const RunStatus = {
RUNNING: 'running',
COMPLETED: 'completed',
FAILED: 'failed',
CANCELLED: 'cancelled'
CANCELLED: 'cancelled',
PAUSED: 'paused'
} as const;
export type RunStatus = typeof RunStatus[keyof typeof RunStatus];