Files
paseo/packages/app/src/utils/sidebar-agent-state.ts
T
Mohamed Boudra 25fbc939e0 feat(app): give composer track pills a segment per state
A mixed fan-out used to collapse the pill to its single most urgent
state, so a red dot beside "1 failed" over a child that was still
working read as the whole fan-out having stopped. Every state present
now gets its own mark and count, in the sidebar's status-group order.
2026-08-18 21:51:25 +02:00

60 lines
2.0 KiB
TypeScript

import {
deriveAgentStateBucket,
type AgentAttentionReason,
type AgentStateBucketInput,
} from "@getpaseo/protocol/agent-state-bucket";
export type SidebarStateBucket = "needs_input" | "failed" | "running" | "attention" | "done";
export type SidebarAttentionReason = AgentAttentionReason;
export function deriveSidebarStateBucket(input: AgentStateBucketInput): SidebarStateBucket {
return deriveAgentStateBucket(input);
}
export function isSidebarActiveAgent(input: AgentStateBucketInput): boolean {
return deriveSidebarStateBucket(input) !== "done";
}
// Most urgent first, for collapsing a project's workspaces into one badge. This is
// deliberately NOT STATUS_BUCKET_ORDER below, which ranks "attention" above "running": on a
// collapsed project row we want an actively-working project to keep showing the loader,
// so "running" outranks "attention" here. needs_input and failed still win over both;
// done stays last.
const STATUS_BUCKET_PRIORITY: readonly SidebarStateBucket[] = [
"needs_input",
"failed",
"running",
"attention",
"done",
];
/**
* The order states are listed in when all of them are shown side by side rather than collapsed
* into one — the sidebar's status groups, and the subagent pill's segments. Anything the user has
* to act on comes before anything that is still moving on its own.
*/
export const STATUS_BUCKET_ORDER: readonly SidebarStateBucket[] = [
"needs_input",
"failed",
"attention",
"running",
"done",
] as const;
/**
* Collapses many workspace status buckets into the single most urgent one, so a
* collapsed project row can stand in for the child rows it hides.
*/
export function aggregateSidebarStateBuckets(
buckets: Iterable<SidebarStateBucket>,
): SidebarStateBucket {
let bestRank = STATUS_BUCKET_PRIORITY.length - 1;
for (const bucket of buckets) {
const rank = STATUS_BUCKET_PRIORITY.indexOf(bucket);
if (rank !== -1 && rank < bestRank) {
bestRank = rank;
}
}
return STATUS_BUCKET_PRIORITY[bestRank] ?? "done";
}