fix: revert dashboard stop run controls (#1461)

Revert the dashboard stop-button behavior from #1433 because it allows duplicate submissions while optimistic prompts are still pending.
This commit is contained in:
Johannes du Plessis 2026-06-08 21:47:53 -07:00 • committed by GitHub
parent 11042175c6
commit 5430672edb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 19 additions and 67 deletions

View file

@ -223,9 +223,7 @@ def _metadata_repo(metadata: dict[str, Any]) -> tuple[str, str, str]:
def _run_status_to_agent_status(thread_status: str | None, run_status: str | None) -> str:
if thread_status == "busy" or run_status in {"pending", "running"}:
return "running"
if run_status in {"interrupted", "cancelled"}:
return "interrupted"
if run_status in {"error", "failed", "timeout"}:
if run_status in {"error", "failed", "timeout", "interrupted"}:
return "error"
if run_status == "success":
return "finished"
@ -672,7 +670,7 @@ async def cancel_dashboard_thread(
run_id = metadata.get("latest_run_id")
if isinstance(run_id, str) and run_id:
try:
await client.runs.cancel(thread_id, run_id, wait=False, action="interrupt")
await client.runs.cancel(thread_id, run_id, wait=False)
except Exception:
logger.debug("Could not cancel run %s for thread %s", run_id, thread_id, exc_info=True)

View file

@ -42,13 +42,6 @@ def test_thread_summary_keeps_repo_when_present() -> None:
assert summary["repoFullName"] == "octo/repo"
def test_thread_summary_reports_interrupted_run_status() -> None:
summary = thread_api._thread_summary(
{"thread_id": "t3", "metadata": {"latest_run_status": "interrupted"}}
)
assert summary["status"] == "interrupted"
class _FakeThreadsClient:
async def create(
self, *, thread_id: str, metadata: dict[str, Any], if_exists: str

View file

@ -6,11 +6,7 @@ import type { AgentThread, Message } from "@/lib/agents/types"
import type { ModelSelection } from "@/lib/agents/useModelOptions"
import { AgentPromptBar } from "@/components/agents/AgentPromptBar"
import { MessageView } from "@/components/agents/ported"
import {
agentThreadKeys,
useCancelAgentThread,
useSendAgentMessage,
} from "@/lib/agents/queries"
import { agentThreadKeys, useSendAgentMessage } from "@/lib/agents/queries"
import {
dropPendingPrompts,
getPendingPrompts,
@ -58,7 +54,6 @@ function isPendingPromptConfirmed(
export function AgentThreadView({ thread }: AgentThreadViewProps) {
const queryClient = useQueryClient()
const sendMessage = useSendAgentMessage(thread.id)
const cancelThread = useCancelAgentThread(thread.id)
useAgentThreadStream(thread.id, thread.status === "running")
const [pendingPrompts, setPendingPrompts] = useState<Array<PendingPrompt>>(
() => getPendingPrompts(thread.id)
@ -139,7 +134,7 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
<AgentPromptBar
placeholder="Add a follow up"
compact
busy={hasActiveRun}
busy={isStreaming}
disabled={sendMessage.isPending}
onSubmit={(content, images) =>
sendMessage.mutate({
@ -149,8 +144,6 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
effort: activeSelection?.effort ?? null,
})
}
onStop={() => cancelThread.mutate()}
stopping={cancelThread.isPending}
models={models}
selection={activeSelection}
onSelectionChange={setSelection}
@ -167,7 +160,7 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
<AgentPromptBar
placeholder="Send the first message"
compact
busy={hasActiveRun}
busy={isStreaming}
disabled={sendMessage.isPending}
onSubmit={(content, images) =>
sendMessage.mutate({
@ -177,8 +170,6 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
effort: activeSelection?.effort ?? null,
})
}
onStop={() => cancelThread.mutate()}
stopping={cancelThread.isPending}
models={models}
selection={activeSelection}
onSelectionChange={setSelection}

View file

@ -1,11 +1,4 @@
import {
ArrowUp,
ChevronDown,
ImagePlus,
LoaderCircle,
Square,
X,
} from "lucide-react"
import { ArrowUp, ChevronDown, ImagePlus, LoaderCircle, X } from "lucide-react"
import {
memo,
useCallback,
@ -39,9 +32,6 @@ export interface CloudPromptBarProps {
disabled?: boolean
busy?: boolean
onSubmit?: (value: string, images: Array<ImageChunk>) => void
/** Called to stop the running agent. When set, the send button becomes a stop button while busy and the input is empty. */
onStop?: () => void
stopping?: boolean
models?: Array<ModelOption>
selection?: ModelSelection | null
onSelectionChange?: (next: ModelSelection) => void
@ -84,8 +74,6 @@ export const CloudPromptBar = memo(function CloudPromptBarComponent({
disabled = false,
busy = false,
onSubmit,
onStop,
stopping = false,
models = [],
selection = null,
onSelectionChange,
@ -211,8 +199,6 @@ export const CloudPromptBar = memo(function CloudPromptBarComponent({
}
const pickerDisabled = combos.length === 0 || !onSelectionChange
const showStop =
busy && !disabled && !value.trim() && pendingImages.length === 0 && !!onStop
return (
<div
@ -360,35 +346,19 @@ export const CloudPromptBar = memo(function CloudPromptBarComponent({
<ImagePlus className="size-4" />
</button>
{showStop ? (
<button
type="button"
onClick={onStop}
disabled={stopping}
aria-label="Stop run"
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-40"
>
{stopping ? (
<LoaderCircle className="size-3.5 animate-spin" />
) : (
<Square className="size-3 fill-current" strokeWidth={0} />
)}
</button>
) : (
<button
type="button"
onClick={handleSubmit}
disabled={!canSubmit}
aria-label="Send message"
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-40"
>
{disabled ? (
<LoaderCircle className="size-3.5 animate-spin" />
) : (
<ArrowUp className="size-3.5" strokeWidth={2.5} />
)}
</button>
)}
<button
type="button"
onClick={handleSubmit}
disabled={!canSubmit}
aria-label="Send message"
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-40"
>
{disabled ? (
<LoaderCircle className="size-3.5 animate-spin" />
) : (
<ArrowUp className="size-3.5" strokeWidth={2.5} />
)}
</button>
</div>
</div>
</div>