Live run progress: watch the agent work in real time
Clicking Run on a task now keeps the drawer open and streams the agent's
progress instead of just closing. A new RunProgress panel polls the run
(GET /api/assembler/runs/{id}) and shows a live timeline — Queued -> Thinking
-> Delivered — with elapsed time, any MCP tool calls, the action produced and
its risk tag, and an expandable output. When the run settles it refreshes the
board and pulses the review badge. The panel resets when the drawer switches
tasks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import { Bot, Play, Plus, Trash2 } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { AppShell, REVIEWS_CHANGED } from '@/components/AppShell'
|
||||
import { LivePreview } from '@/components/LivePreview'
|
||||
import { RunProgress } from '@/components/RunProgress'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
@@ -407,8 +408,15 @@ function TaskDrawer({
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [seatId, setSeatId] = useState<string>('')
|
||||
const [preview, setPreview] = useState(false)
|
||||
const [runId, setRunId] = useState<string | null>(null)
|
||||
const aiSeats = seats.filter((s) => s.state === 'Ai')
|
||||
|
||||
// Switching to a different task clears the live run panel so it never shows a stale run.
|
||||
const taskId = task?.id
|
||||
useEffect(() => {
|
||||
setRunId(null)
|
||||
}, [taskId])
|
||||
|
||||
if (!task) {
|
||||
return null
|
||||
}
|
||||
@@ -513,26 +521,38 @@ function TaskDrawer({
|
||||
</Select>
|
||||
<Button
|
||||
disabled={busy || !seatId}
|
||||
onClick={() =>
|
||||
onClick={() => {
|
||||
let started: string | null = null
|
||||
act(async () => {
|
||||
await api.post('/api/assembler/runs', { seatId, workItemId: task.id })
|
||||
const run = await api.post<{ id: string }>('/api/assembler/runs', { seatId, workItemId: task.id })
|
||||
started = run?.id ?? null
|
||||
// Visible feedback: the task leaves Backlog for In Progress while the agent works.
|
||||
if (task.status === 'Backlog') {
|
||||
await api.patch(`/api/orgboard/tasks/${task.id}/move`, { status: 'InProgress' })
|
||||
}
|
||||
}, 'Sent to the agent — moved to In Progress; its result will land in the review inbox.').then(() => {
|
||||
// The proposal lands a few seconds later; nudge the review badge, then close.
|
||||
}, 'Agent started — watch it work below.').then(() => {
|
||||
if (started) setRunId(started)
|
||||
window.dispatchEvent(new Event(REVIEWS_CHANGED))
|
||||
setTimeout(() => window.dispatchEvent(new Event(REVIEWS_CHANGED)), 4000)
|
||||
setTimeout(() => window.dispatchEvent(new Event(REVIEWS_CHANGED)), 9000)
|
||||
onClose()
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Bot data-icon="inline-start" />
|
||||
Run
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Live process: watch the agent move through Queued → Thinking → Delivered in real time. */}
|
||||
{runId && (
|
||||
<RunProgress
|
||||
key={runId}
|
||||
runId={runId}
|
||||
onSettled={() => {
|
||||
// Result is in the review inbox and the board has moved — refresh both.
|
||||
window.dispatchEvent(new Event(REVIEWS_CHANGED))
|
||||
onChanged()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user