story-kit: merge 336_story_web_ui_button_to_start_a_coder_on_a_story

This commit is contained in:
Dave
2026-03-20 08:49:35 +00:00
parent 31e2f823f7
commit e33979aacb
2 changed files with 165 additions and 1 deletions
+50
View File
@@ -2,6 +2,8 @@ import * as React from "react";
import Markdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
import type { AgentConfigInfo } from "../api/agents";
import { agentsApi } from "../api/agents";
import type { PipelineState } from "../api/client";
import { api, ChatWebSocket } from "../api/client";
import { useChatHistory } from "../hooks/useChatHistory";
@@ -202,6 +204,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
const [agentConfigVersion, setAgentConfigVersion] = useState(0);
const [agentStateVersion, setAgentStateVersion] = useState(0);
const [pipelineVersion, setPipelineVersion] = useState(0);
const [agentRoster, setAgentRoster] = useState<AgentConfigInfo[]>([]);
const [storyTokenCosts, setStoryTokenCosts] = useState<Map<string, number>>(
new Map(),
);
@@ -237,6 +240,26 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
const userScrolledUpRef = useRef(false);
const pendingMessageRef = useRef<string>("");
// Agents currently running or pending across all pipeline stages.
const busyAgentNames = useMemo(() => {
const busy = new Set<string>();
const allItems = [
...pipeline.backlog,
...pipeline.current,
...pipeline.qa,
...pipeline.merge,
];
for (const item of allItems) {
if (
item.agent &&
(item.agent.status === "running" || item.agent.status === "pending")
) {
busy.add(item.agent.agent_name);
}
}
return busy;
}, [pipeline]);
const contextUsage = useMemo(() => {
let totalTokens = 0;
totalTokens += 200;
@@ -504,6 +527,27 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
return () => window.removeEventListener("resize", handleResize);
}, []);
// Fetch agent roster whenever the config changes so the start button knows available agents.
useEffect(() => {
agentsApi
.getAgentConfig()
.then(setAgentRoster)
.catch(() => {
// Silently ignore — roster unavailable.
});
}, [agentConfigVersion]);
const handleStartAgent = useCallback(
async (storyId: string, agentName?: string) => {
try {
await agentsApi.startAgent(storyId, agentName);
} catch (err) {
console.error("Failed to start agent:", err);
}
},
[],
);
const cancelGeneration = async () => {
// Preserve queued messages by appending them to the chat input box
if (queuedMessagesRef.current.length > 0) {
@@ -1051,12 +1095,18 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
items={pipeline.current}
costs={storyTokenCosts}
onItemClick={(item) => setSelectedWorkItemId(item.story_id)}
agentRoster={agentRoster}
busyAgentNames={busyAgentNames}
onStartAgent={handleStartAgent}
/>
<StagePanel
title="Backlog"
items={pipeline.backlog}
costs={storyTokenCosts}
onItemClick={(item) => setSelectedWorkItemId(item.story_id)}
agentRoster={agentRoster}
busyAgentNames={busyAgentNames}
onStartAgent={handleStartAgent}
/>
<ServerLogsPanel logs={serverLogs} />
</>