Files
template-app/modules/agent/ui/run-controls.tsx
T

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>
)
}