story-kit: merge 337_story_web_ui_button_to_stop_an_agent_on_a_story

This commit is contained in:
Dave
2026-03-20 08:57:41 +00:00
parent 8c3e92f936
commit 3778162920
2 changed files with 53 additions and 1 deletions

View File

@@ -798,6 +798,12 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
setQueuedMessages([...queuedMessagesRef.current]);
}, []);
const handleStopAgent = useCallback((storyId: string, agentName: string) => {
agentsApi.stopAgent(storyId, agentName).catch((err: unknown) => {
console.error("Failed to stop agent:", err);
});
}, []);
return (
<div
className="chat-container"
@@ -1077,18 +1083,21 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
items={pipeline.done ?? []}
costs={storyTokenCosts}
onItemClick={(item) => setSelectedWorkItemId(item.story_id)}
onStopAgent={handleStopAgent}
/>
<StagePanel
title="To Merge"
items={pipeline.merge}
costs={storyTokenCosts}
onItemClick={(item) => setSelectedWorkItemId(item.story_id)}
onStopAgent={handleStopAgent}
/>
<StagePanel
title="QA"
items={pipeline.qa}
costs={storyTokenCosts}
onItemClick={(item) => setSelectedWorkItemId(item.story_id)}
onStopAgent={handleStopAgent}
/>
<StagePanel
title="Current"
@@ -1098,6 +1107,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
agentRoster={agentRoster}
busyAgentNames={busyAgentNames}
onStartAgent={handleStartAgent}
onStopAgent={handleStopAgent}
/>
<StagePanel
title="Backlog"
@@ -1107,6 +1117,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
agentRoster={agentRoster}
busyAgentNames={busyAgentNames}
onStartAgent={handleStartAgent}
onStopAgent={handleStopAgent}
/>
<ServerLogsPanel logs={serverLogs} />
</>

View File

@@ -43,6 +43,7 @@ interface StagePanelProps {
items: PipelineStageItem[];
emptyMessage?: string;
onItemClick?: (item: PipelineStageItem) => void;
onStopAgent?: (storyId: string, agentName: string) => void;
/** Map of story_id → total_cost_usd for displaying cost badges. */
costs?: Map<string, number>;
/** Agent roster to populate the start agent dropdown. */
@@ -56,9 +57,11 @@ interface StagePanelProps {
function AgentLozenge({
agent,
storyId,
onStop,
}: {
agent: AgentAssignment;
storyId: string;
onStop?: () => void;
}) {
const { saveSlotRect, pendingFlyIns } = useLozengeFly();
const lozengeRef = useRef<HTMLDivElement>(null);
@@ -128,6 +131,31 @@ function AgentLozenge({
/>
)}
{label}
{isRunning && onStop && (
<button
type="button"
data-testid={`stop-agent-${storyId}`}
onClick={(e) => {
e.stopPropagation();
onStop();
}}
title="Stop agent"
style={{
marginLeft: "4px",
padding: "0 3px",
background: "transparent",
border: "none",
color,
cursor: "pointer",
fontSize: "0.9em",
lineHeight: 1,
opacity: 0.8,
flexShrink: 0,
}}
>
</button>
)}
</div>
);
}
@@ -224,6 +252,7 @@ export function StagePanel({
items,
emptyMessage = "Empty.",
onItemClick,
onStopAgent,
costs,
agentRoster,
busyAgentNames,
@@ -391,7 +420,19 @@ export function StagePanel({
)}
</div>
{item.agent && (
<AgentLozenge agent={item.agent} storyId={item.story_id} />
<AgentLozenge
agent={item.agent}
storyId={item.story_id}
onStop={
onStopAgent && item.agent.status === "running"
? () =>
onStopAgent(
item.story_id,
item.agent?.agent_name ?? "",
)
: undefined
}
/>
)}
{canStart && onStartAgent && (
<StartAgentControl