mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-31 13:42:50 +00:00
* fix(workspaces): show provider subagent activity Provider subagent lifecycle lives outside managed agent snapshots, so workspace aggregation dropped it after the parent turn finished. Count running provider subagents against the delegation root and emit workspace updates when their status changes. * fix(workspaces): surface cross-workspace subagents
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
import type { Agent, WorkspaceDescriptor } from "@/stores/session-store";
|
|
import { isWorkspaceRootAgent } from "@/subagents/policies";
|
|
import { deriveSidebarStateBucket } from "./sidebar-agent-state";
|
|
|
|
export interface WorkspaceAgentActivity {
|
|
agentId: string;
|
|
status: WorkspaceDescriptor["status"];
|
|
enteredAt: Date | null;
|
|
}
|
|
|
|
export function buildWorkspaceAgentActivityIndex(
|
|
agents: ReadonlyMap<string, Agent>,
|
|
previous?: ReadonlyMap<string, WorkspaceAgentActivity>,
|
|
): Map<string, WorkspaceAgentActivity> {
|
|
const activityByWorkspaceId = new Map<string, WorkspaceAgentActivity>();
|
|
const latestActivityAtByWorkspaceId = new Map<string, Date>();
|
|
|
|
for (const agent of agents.values()) {
|
|
const parentAgent = agent.parentAgentId ? agents.get(agent.parentAgentId) : undefined;
|
|
if (agent.archivedAt || !agent.workspaceId || !isWorkspaceRootAgent(agent, parentAgent)) {
|
|
continue;
|
|
}
|
|
|
|
const enteredAt = agent.attentionTimestamp ?? agent.updatedAt;
|
|
const latestActivityAt = latestActivityAtByWorkspaceId.get(agent.workspaceId);
|
|
if (latestActivityAt && enteredAt <= latestActivityAt) {
|
|
continue;
|
|
}
|
|
latestActivityAtByWorkspaceId.set(agent.workspaceId, enteredAt);
|
|
|
|
const status = deriveSidebarStateBucket({
|
|
status: agent.status,
|
|
pendingPermissionCount: agent.pendingPermissions.length,
|
|
requiresAttention: agent.requiresAttention,
|
|
attentionReason: agent.attentionReason,
|
|
});
|
|
activityByWorkspaceId.set(agent.workspaceId, {
|
|
agentId: agent.id,
|
|
status,
|
|
enteredAt,
|
|
});
|
|
}
|
|
|
|
for (const [workspaceId, activity] of activityByWorkspaceId) {
|
|
const previousActivity = previous?.get(workspaceId);
|
|
if (
|
|
previousActivity?.agentId === activity.agentId &&
|
|
previousActivity.status === activity.status
|
|
) {
|
|
activityByWorkspaceId.set(workspaceId, previousActivity);
|
|
}
|
|
}
|
|
|
|
if (previous && areWorkspaceAgentActivityIndexesIdentical(previous, activityByWorkspaceId)) {
|
|
return previous instanceof Map ? previous : new Map(previous);
|
|
}
|
|
return activityByWorkspaceId;
|
|
}
|
|
|
|
function areWorkspaceAgentActivityIndexesIdentical(
|
|
previous: ReadonlyMap<string, WorkspaceAgentActivity>,
|
|
next: ReadonlyMap<string, WorkspaceAgentActivity>,
|
|
): boolean {
|
|
if (previous.size !== next.size) {
|
|
return false;
|
|
}
|
|
for (const [workspaceId, activity] of next) {
|
|
if (previous.get(workspaceId) !== activity) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|