Files
tutti/packages/agent/gui/agent-message-center/workspaceAgentMessageCenterCollection.ts
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

114 lines
3.3 KiB
TypeScript

import type {
WorkspaceAgentMessageCenterCounts,
WorkspaceAgentMessageCenterItem,
WorkspaceAgentMessageCenterModel
} from "./workspaceAgentMessageCenterModel";
import type { AgentActivityNeedsAttentionItem } from "@tutti-os/agent-activity-core";
const EMPTY_COUNTS: WorkspaceAgentMessageCenterCounts = {
all: 0,
working: 0,
waiting: 0,
completed: 0,
failed: 0
};
export function buildWorkspaceAgentMessageCenterModelFromItems(
sourceItems: readonly WorkspaceAgentMessageCenterItem[],
itemCutoffUnixMs?: number | null
): WorkspaceAgentMessageCenterModel {
const items = sourceItems
.filter((item) => isWithinItemCutoff(item, itemCutoffUnixMs))
.sort(compareItems);
return {
waitingCount: items.filter(isWaitingMessageCenterItem).length,
items,
counts: countItems(items)
};
}
export function latestNeedsAttentionBySessionId(
items: readonly AgentActivityNeedsAttentionItem[]
): Map<string, AgentActivityNeedsAttentionItem> {
const bySessionId = new Map<string, AgentActivityNeedsAttentionItem>();
for (const item of items) {
const previous = bySessionId.get(item.agentSessionId);
if (!previous || item.occurredAtUnixMs > previous.occurredAtUnixMs) {
bySessionId.set(item.agentSessionId, item);
}
}
return bySessionId;
}
export function isWaitingMessageCenterItem(
item: WorkspaceAgentMessageCenterItem
): boolean {
return item.pendingPrompt !== null || item.needsAttentionKind !== null;
}
export function isInteractiveMessageCenterItem(
item: WorkspaceAgentMessageCenterItem
): boolean {
return item.pendingPrompt !== null;
}
export function selectMessageCenterAttentionDeckItems(
items: readonly WorkspaceAgentMessageCenterItem[]
): WorkspaceAgentMessageCenterItem[] {
return items.filter(isInteractiveMessageCenterItem);
}
export function isCompletedMessageCenterItem(
item: WorkspaceAgentMessageCenterItem
): boolean {
return (
item.status === "completed" ||
item.status === "canceled" ||
item.status === "idle"
);
}
function isWithinItemCutoff(
item: WorkspaceAgentMessageCenterItem,
cutoffUnixMs: number | null | undefined
): boolean {
if (!Number.isFinite(cutoffUnixMs)) return true;
if (isWaitingMessageCenterItem(item)) return true;
const timestamp = item.sortTimeUnixMs || item.lastAgentMessageAtUnixMs || 0;
return timestamp >= Number(cutoffUnixMs);
}
function countItems(
items: readonly WorkspaceAgentMessageCenterItem[]
): WorkspaceAgentMessageCenterCounts {
return items.reduce<WorkspaceAgentMessageCenterCounts>(
(counts, item) => {
counts.all += 1;
if (isWaitingMessageCenterItem(item)) {
counts.waiting += 1;
} else if (isCompletedMessageCenterItem(item)) {
counts.completed += 1;
} else if (item.status === "working") {
counts.working += 1;
} else if (item.status === "failed") {
counts.failed += 1;
}
return counts;
},
{ ...EMPTY_COUNTS }
);
}
function compareItems(
left: WorkspaceAgentMessageCenterItem,
right: WorkspaceAgentMessageCenterItem
): number {
const leftWaiting = isWaitingMessageCenterItem(left);
const rightWaiting = isWaitingMessageCenterItem(right);
if (leftWaiting !== rightWaiting) return leftWaiting ? -1 : 1;
return (
right.sortTimeUnixMs - left.sortTimeUnixMs ||
left.agentSessionId.localeCompare(right.agentSessionId)
);
}