story-kit: queue 235_story_show_agent_logs_for_a_story_in_expanded_work_item for merge

This commit is contained in:
Dave
2026-02-28 09:28:27 +00:00
parent dca5fde2ef
commit 1d17a4d756
4 changed files with 446 additions and 13 deletions

View File

@@ -1,5 +1,7 @@
import * as React from "react";
import Markdown from "react-markdown";
import type { AgentEvent, AgentInfo, AgentStatusValue } from "../api/agents";
import { agentsApi, subscribeAgentStream } from "../api/agents";
import { api } from "../api/client";
const { useEffect, useRef, useState } = React;
@@ -13,6 +15,13 @@ const STAGE_LABELS: Record<string, string> = {
archived: "Archived",
};
const STATUS_COLORS: Record<AgentStatusValue, string> = {
running: "#3fb950",
pending: "#e3b341",
completed: "#aaa",
failed: "#f85149",
};
interface WorkItemDetailPanelProps {
storyId: string;
onClose: () => void;
@@ -27,7 +36,11 @@ export function WorkItemDetailPanel({
const [name, setName] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [agentInfo, setAgentInfo] = useState<AgentInfo | null>(null);
const [agentLog, setAgentLog] = useState<string[]>([]);
const [agentStatus, setAgentStatus] = useState<AgentStatusValue | null>(null);
const panelRef = useRef<HTMLDivElement>(null);
const cleanupRef = useRef<(() => void) | null>(null);
useEffect(() => {
setLoading(true);
@@ -47,6 +60,61 @@ export function WorkItemDetailPanel({
});
}, [storyId]);
useEffect(() => {
cleanupRef.current?.();
cleanupRef.current = null;
setAgentInfo(null);
setAgentLog([]);
setAgentStatus(null);
agentsApi
.listAgents()
.then((agents) => {
const agent = agents.find((a) => a.story_id === storyId);
if (!agent) return;
setAgentInfo(agent);
setAgentStatus(agent.status);
if (agent.status === "running" || agent.status === "pending") {
const cleanup = subscribeAgentStream(
storyId,
agent.agent_name,
(event: AgentEvent) => {
switch (event.type) {
case "status":
setAgentStatus((event.status as AgentStatusValue) ?? null);
break;
case "output":
setAgentLog((prev) => [...prev, event.text ?? ""]);
break;
case "done":
setAgentStatus("completed");
break;
case "error":
setAgentStatus("failed");
setAgentLog((prev) => [
...prev,
`[ERROR] ${event.message ?? "Unknown error"}`,
]);
break;
default:
break;
}
},
);
cleanupRef.current = cleanup;
}
})
.catch((err: unknown) => {
console.error("Failed to load agents:", err);
});
return () => {
cleanupRef.current?.();
cleanupRef.current = null;
};
}, [storyId]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
@@ -187,7 +255,6 @@ export function WorkItemDetailPanel({
</div>
)}
{/* Placeholder sections for future content */}
<div
style={{
display: "flex",
@@ -195,9 +262,80 @@ export function WorkItemDetailPanel({
gap: "8px",
}}
>
{/* Agent Logs section */}
<div
data-testid={
agentInfo ? "agent-logs-section" : "placeholder-agent-logs"
}
style={{
border: "1px solid #2a2a2a",
borderRadius: "8px",
padding: "10px 12px",
background: "#161616",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: agentInfo ? "6px" : "4px",
}}
>
<div
style={{
fontWeight: 600,
fontSize: "0.8em",
color: agentInfo ? "#888" : "#555",
}}
>
Agent Logs
</div>
{agentInfo && agentStatus && (
<div
data-testid="agent-status-badge"
style={{
fontSize: "0.7em",
color: STATUS_COLORS[agentStatus],
fontWeight: 600,
}}
>
{agentInfo.agent_name} {agentStatus}
</div>
)}
</div>
{agentInfo && agentLog.length > 0 ? (
<div
data-testid="agent-log-output"
style={{
fontSize: "0.75em",
fontFamily: "monospace",
color: "#ccc",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
lineHeight: "1.5",
maxHeight: "200px",
overflowY: "auto",
}}
>
{agentLog.join("")}
</div>
) : agentInfo ? (
<div style={{ fontSize: "0.75em", color: "#444" }}>
{agentStatus === "running" || agentStatus === "pending"
? "Waiting for output..."
: "No output."}
</div>
) : (
<div style={{ fontSize: "0.75em", color: "#444" }}>
Coming soon
</div>
)}
</div>
{/* Placeholder sections for future content */}
{(
[
{ id: "agent-logs", label: "Agent Logs" },
{ id: "test-output", label: "Test Output" },
{ id: "coverage", label: "Coverage" },
] as { id: string; label: string }[]