82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
"use client"
|
|
|
|
// One run's controls: cancel while it runs, and recovery from its terminal states. The recovery
|
|
// copy tells the person what the state means and what they can do — failed runs can be started
|
|
// again from the same plan, cancelled runs are simply over, and an uncertain run (a timeout after
|
|
// an external effect) is reconciled by a person, not retried blindly.
|
|
|
|
import { type ProjectSlugs, useTransport } from "@conusai-kit/ui"
|
|
import { type ReactElement, useState } from "react"
|
|
import type { RunView } from "../contract/index.js"
|
|
|
|
export interface RunControlsLabels {
|
|
readonly cancel: string
|
|
readonly cancelling: string
|
|
readonly refresh: string
|
|
readonly failedHint: string
|
|
readonly cancelledHint: string
|
|
readonly uncertainHint: string
|
|
readonly budgetExhausted: string
|
|
readonly approvalUnavailable: string
|
|
readonly toolError: string
|
|
readonly planRefused: string
|
|
readonly internal: string
|
|
}
|
|
|
|
export function RunControls({
|
|
slugs,
|
|
run,
|
|
labels,
|
|
}: {
|
|
slugs: ProjectSlugs
|
|
run: Pick<RunView, "runId" | "status" | "failureCode">
|
|
labels: RunControlsLabels
|
|
}): ReactElement {
|
|
const { call } = useTransport()
|
|
const [busy, setBusy] = useState(false)
|
|
|
|
const cancel = async (): Promise<void> => {
|
|
setBusy(true)
|
|
try {
|
|
await call("agent.run.cancel", { ...slugs, runId: run.runId })
|
|
window.location.reload()
|
|
} finally {
|
|
setBusy(false)
|
|
}
|
|
}
|
|
|
|
const active = run.status === "queued" || run.status === "running" || run.status === "awaiting_approval"
|
|
const failureText = (code: RunView["failureCode"]): string => {
|
|
switch (code) {
|
|
case "budget_exhausted":
|
|
return labels.budgetExhausted
|
|
case "approval_unavailable":
|
|
return labels.approvalUnavailable
|
|
case "tool_error":
|
|
return labels.toolError
|
|
case "plan_refused":
|
|
return labels.planRefused
|
|
default:
|
|
return labels.internal
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{active ? (
|
|
<button type="button" disabled={busy} onClick={() => void cancel()}>
|
|
{busy ? labels.cancelling : labels.cancel}
|
|
</button>
|
|
) : null}
|
|
{run.status === "failed" ? (
|
|
<p role="alert">
|
|
{failureText(run.failureCode)} {labels.failedHint}
|
|
</p>
|
|
) : null}
|
|
{run.status === "cancelled" ? <p role="status">{labels.cancelledHint}</p> : null}
|
|
{run.status === "uncertain" ? <p role="alert">{labels.uncertainHint}</p> : null}
|
|
<a href="?">{labels.refresh}</a>
|
|
</div>
|
|
)
|
|
}
|