story-kit: merge 336_story_web_ui_button_to_start_a_coder_on_a_story
This commit is contained in:
@@ -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} />
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user