refactor: organize ActiveAgentsPanel feed by run, fix SKILL.md formatting

Switches feed aggregation from by-agent to by-run for more accurate
streaming output attribution. Adds newline note for plan tags in SKILL.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-20 13:47:27 -06:00
parent b459668009
commit 45f67ae311
2 changed files with 30 additions and 65 deletions

View File

@@ -125,10 +125,14 @@ After:
pls show the costs in either token or dollars on the /issues/{id} page. Make a plan first. pls show the costs in either token or dollars on the /issues/{id} page. Make a plan first.
<plan> <plan>
[your plan here] [your plan here]
</plan> </plan>
``` ```
\*make sure to have a newline after/before your <plan/> tags
## Key Endpoints (Quick Reference) ## Key Endpoints (Quick Reference)
| Action | Endpoint | | Action | Endpoint |

View File

@@ -140,14 +140,8 @@ interface ActiveAgentsPanelProps {
companyId: string; companyId: string;
} }
interface AgentRunGroup {
agentId: string;
agentName: string;
runs: LiveRunForIssue[];
}
export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) { export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
const [feedByAgent, setFeedByAgent] = useState<Map<string, FeedItem[]>>(new Map()); const [feedByRun, setFeedByRun] = useState<Map<string, FeedItem[]>>(new Map());
const seenKeysRef = useRef(new Set<string>()); const seenKeysRef = useRef(new Set<string>());
const pendingByRunRef = useRef(new Map<string, string>()); const pendingByRunRef = useRef(new Map<string, string>());
const nextIdRef = useRef(1); const nextIdRef = useRef(1);
@@ -161,19 +155,6 @@ export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
const runById = useMemo(() => new Map(runs.map((r) => [r.id, r])), [runs]); const runById = useMemo(() => new Map(runs.map((r) => [r.id, r])), [runs]);
const activeRunIds = useMemo(() => new Set(runs.map((r) => r.id)), [runs]); const activeRunIds = useMemo(() => new Set(runs.map((r) => r.id)), [runs]);
const agentGroups = useMemo(() => {
const map = new Map<string, AgentRunGroup>();
for (const run of runs) {
let group = map.get(run.agentId);
if (!group) {
group = { agentId: run.agentId, agentName: run.agentName, runs: [] };
map.set(run.agentId, group);
}
group.runs.push(run);
}
return Array.from(map.values());
}, [runs]);
// Clean up pending buffers for runs that ended // Clean up pending buffers for runs that ended
useEffect(() => { useEffect(() => {
const stillActive = new Set<string>(); const stillActive = new Set<string>();
@@ -196,12 +177,12 @@ export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
let reconnectTimer: number | null = null; let reconnectTimer: number | null = null;
let socket: WebSocket | null = null; let socket: WebSocket | null = null;
const appendItems = (agentId: string, items: FeedItem[]) => { const appendItems = (runId: string, items: FeedItem[]) => {
if (items.length === 0) return; if (items.length === 0) return;
setFeedByAgent((prev) => { setFeedByRun((prev) => {
const next = new Map(prev); const next = new Map(prev);
const existing = next.get(agentId) ?? []; const existing = next.get(runId) ?? [];
next.set(agentId, [...existing, ...items].slice(-MAX_FEED_ITEMS)); next.set(runId, [...existing, ...items].slice(-MAX_FEED_ITEMS));
return next; return next;
}); });
}; };
@@ -246,7 +227,7 @@ export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
if (seenKeysRef.current.size > 2000) seenKeysRef.current.clear(); if (seenKeysRef.current.size > 2000) seenKeysRef.current.clear();
const tone = eventType === "error" ? "error" : eventType === "lifecycle" ? "warn" : "info"; const tone = eventType === "error" ? "error" : eventType === "lifecycle" ? "warn" : "info";
const item = createFeedItem(run, event.createdAt, messageText, tone, nextIdRef.current++); const item = createFeedItem(run, event.createdAt, messageText, tone, nextIdRef.current++);
if (item) appendItems(run.agentId, [item]); if (item) appendItems(run.id, [item]);
return; return;
} }
@@ -258,7 +239,7 @@ export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
if (seenKeysRef.current.size > 2000) seenKeysRef.current.clear(); if (seenKeysRef.current.size > 2000) seenKeysRef.current.clear();
const tone = status === "failed" || status === "timed_out" ? "error" : "warn"; const tone = status === "failed" || status === "timed_out" ? "error" : "warn";
const item = createFeedItem(run, event.createdAt, `run ${status}`, tone, nextIdRef.current++); const item = createFeedItem(run, event.createdAt, `run ${status}`, tone, nextIdRef.current++);
if (item) appendItems(run.agentId, [item]); if (item) appendItems(run.id, [item]);
return; return;
} }
@@ -267,10 +248,10 @@ export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
if (!chunk) return; if (!chunk) return;
const stream = readString(payload["stream"]) === "stderr" ? "stderr" : "stdout"; const stream = readString(payload["stream"]) === "stderr" ? "stderr" : "stdout";
if (stream === "stderr") { if (stream === "stderr") {
appendItems(run.agentId, parseStderrChunk(run, chunk, event.createdAt, pendingByRunRef.current, nextIdRef)); appendItems(run.id, parseStderrChunk(run, chunk, event.createdAt, pendingByRunRef.current, nextIdRef));
return; return;
} }
appendItems(run.agentId, parseStdoutChunk(run, chunk, event.createdAt, pendingByRunRef.current, nextIdRef)); appendItems(run.id, parseStdoutChunk(run, chunk, event.createdAt, pendingByRunRef.current, nextIdRef));
} }
}; };
@@ -297,7 +278,7 @@ export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
}; };
}, [activeRunIds, companyId, runById]); }, [activeRunIds, companyId, runById]);
if (agentGroups.length === 0) return null; if (runs.length === 0) return null;
return ( return (
<div> <div>
@@ -305,11 +286,11 @@ export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
Active Agents Active Agents
</h3> </h3>
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
{agentGroups.map((group) => ( {runs.map((run) => (
<AgentRunCard <AgentRunCard
key={group.agentId} key={run.id}
group={group} run={run}
feed={feedByAgent.get(group.agentId) ?? []} feed={feedByRun.get(run.id) ?? []}
/> />
))} ))}
</div> </div>
@@ -317,10 +298,9 @@ export function ActiveAgentsPanel({ companyId }: ActiveAgentsPanelProps) {
); );
} }
function AgentRunCard({ group, feed }: { group: AgentRunGroup; feed: FeedItem[] }) { function AgentRunCard({ run, feed }: { run: LiveRunForIssue; feed: FeedItem[] }) {
const bodyRef = useRef<HTMLDivElement>(null); const bodyRef = useRef<HTMLDivElement>(null);
const recent = feed.slice(-20); const recent = feed.slice(-20);
const primaryRun = group.runs[0];
useEffect(() => { useEffect(() => {
const body = bodyRef.current; const body = bodyRef.current;
@@ -336,23 +316,19 @@ function AgentRunCard({ group, feed }: { group: AgentRunGroup; feed: FeedItem[]
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" /> <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" />
<span className="relative inline-flex rounded-full h-2 w-2 bg-blue-500" /> <span className="relative inline-flex rounded-full h-2 w-2 bg-blue-500" />
</span> </span>
<Identity name={group.agentName} size="sm" /> <Identity name={run.agentName} size="sm" />
<span className="text-[11px] font-medium text-blue-400">Live</span> <span className="text-[11px] font-medium text-blue-400">Live</span>
{group.runs.length > 1 && ( <span className="text-[10px] text-muted-foreground font-mono">
<span className="text-[10px] text-muted-foreground"> {run.id.slice(0, 8)}
({group.runs.length} runs) </span>
</span>
)}
</div> </div>
{primaryRun && ( <Link
<Link to={`/agents/${run.agentId}/runs/${run.id}`}
to={`/agents/${primaryRun.agentId}/runs/${primaryRun.id}`} className="inline-flex items-center gap-1 text-[10px] text-blue-400 hover:text-blue-300"
className="inline-flex items-center gap-1 text-[10px] text-blue-400 hover:text-blue-300" >
> Open run
Open run <ExternalLink className="h-2.5 w-2.5" />
<ExternalLink className="h-2.5 w-2.5" /> </Link>
</Link>
)}
</div> </div>
<div ref={bodyRef} className="max-h-[180px] overflow-y-auto p-2 font-mono text-[11px] space-y-1"> <div ref={bodyRef} className="max-h-[180px] overflow-y-auto p-2 font-mono text-[11px] space-y-1">
@@ -381,21 +357,6 @@ function AgentRunCard({ group, feed }: { group: AgentRunGroup; feed: FeedItem[]
</div> </div>
))} ))}
</div> </div>
{group.runs.length > 1 && (
<div className="border-t border-border/50 px-3 py-1.5 flex flex-wrap gap-2">
{group.runs.map((run) => (
<Link
key={run.id}
to={`/agents/${run.agentId}/runs/${run.id}`}
className="inline-flex items-center gap-1 text-[10px] text-blue-400 hover:text-blue-300"
>
{run.id.slice(0, 8)}
<ExternalLink className="h-2.5 w-2.5" />
</Link>
))}
</div>
)}
</div> </div>
); );
} }