@brainpilot/web 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-Br55rkHb.css +1 -0
- package/dist/assets/index-CeUzk-ej.js +445 -0
- package/dist/index.html +2 -2
- package/index.html +13 -0
- package/package.json +12 -3
- package/src/App.tsx +10 -0
- package/src/__tests__/agentsReducer.test.ts +67 -0
- package/src/__tests__/api.test.ts +221 -0
- package/src/__tests__/chatScrollMemory.test.ts +49 -0
- package/src/__tests__/demoConversation.test.ts +73 -0
- package/src/__tests__/demoReset.test.ts +24 -0
- package/src/__tests__/messageGroups.test.ts +80 -0
- package/src/__tests__/newUiComponents.test.tsx +101 -0
- package/src/__tests__/newUiEvents.test.ts +236 -0
- package/src/__tests__/runningToast.test.ts +29 -0
- package/src/__tests__/tokenUsage.test.ts +48 -0
- package/src/__tests__/toolDisplay.test.ts +55 -0
- package/src/__tests__/traceReducer.test.ts +62 -0
- package/src/components/chat/AskUserCard.tsx +123 -0
- package/src/components/chat/AutoRetryIndicator.tsx +71 -0
- package/src/components/chat/ComposerInput.tsx +73 -0
- package/src/components/chat/ComposerSendButton.tsx +26 -0
- package/src/components/chat/MarkdownMessage.tsx +24 -0
- package/src/components/chat/MessageStream.tsx +505 -0
- package/src/components/chat/PromptComposer.tsx +489 -0
- package/src/components/chat/SystemMessageBubble.tsx +46 -0
- package/src/components/chat/chatScrollMemory.ts +49 -0
- package/src/components/demo/DemoFileTree.tsx +146 -0
- package/src/components/demo/DemoView.tsx +730 -0
- package/src/components/demo/TraceNodeModal.tsx +80 -0
- package/src/components/demo/demoBundle.ts +223 -0
- package/src/components/demo/demoCache.ts +42 -0
- package/src/components/demo/demoReset.ts +16 -0
- package/src/components/files/FilePreviewView.tsx +153 -0
- package/src/components/files/FileSidebar.tsx +664 -0
- package/src/components/files/filePreview.ts +113 -0
- package/src/components/primitives/CustomSelect.tsx +200 -0
- package/src/components/primitives/IconButton.tsx +27 -0
- package/src/components/quota/DiskQuotaCriticalDialog.tsx +56 -0
- package/src/components/quota/DiskQuotaWarningDialog.tsx +65 -0
- package/src/components/quota/QuotaFileManager.tsx +197 -0
- package/src/components/search/SearchDialog.tsx +101 -0
- package/src/components/session/AgentNetwork.tsx +1233 -0
- package/src/components/session/AgentTraceViews.tsx +346 -0
- package/src/components/session/AnalyticsTab.tsx +220 -0
- package/src/components/session/GlobalOverview.tsx +108 -0
- package/src/components/session/NodeTooltip.tsx +127 -0
- package/src/components/session/TimelineTab.tsx +320 -0
- package/src/components/session/TraceGraphView.tsx +307 -0
- package/src/components/session/TraceNodeDetail.tsx +179 -0
- package/src/components/session/agentAnalytics.ts +397 -0
- package/src/components/session/agentNetworkShared.ts +339 -0
- package/src/components/session/traceLayout.ts +182 -0
- package/src/components/settings/SettingsDialog.tsx +737 -0
- package/src/components/shell/DesktopShell.tsx +261 -0
- package/src/components/shell/SandboxBuildingOverlay.tsx +73 -0
- package/src/components/shell/SandboxStatus.tsx +287 -0
- package/src/components/shell/TerminalDrawer.tsx +387 -0
- package/src/components/sidebar/Sidebar.tsx +191 -0
- package/src/config.ts +10 -0
- package/src/contexts/AppProviders.tsx +20 -0
- package/src/contexts/AuthContext.tsx +61 -0
- package/src/contexts/PreferencesContext.tsx +125 -0
- package/src/contexts/SSEContext.tsx +264 -0
- package/src/contexts/SandboxContext.tsx +310 -0
- package/src/contexts/SessionContext.tsx +919 -0
- package/src/contexts/agentsReducer.ts +49 -0
- package/src/contexts/draftStore.ts +103 -0
- package/src/contexts/messageFilters.ts +29 -0
- package/src/contexts/messageGroups.ts +77 -0
- package/src/contexts/messageReducer.ts +401 -0
- package/src/contexts/newUiEvents.ts +190 -0
- package/src/contexts/runningToast.ts +33 -0
- package/src/contexts/traceReducer.ts +62 -0
- package/src/contexts/turnTimer.test.ts +97 -0
- package/src/contexts/turnTimer.ts +108 -0
- package/src/contexts/useTurnTimer.ts +104 -0
- package/src/contracts/backend.ts +897 -0
- package/src/contracts/demoBundle.ts +83 -0
- package/src/i18n/messages/analytics.ts +106 -0
- package/src/i18n/messages/chat.ts +130 -0
- package/src/i18n/messages/contexts.ts +42 -0
- package/src/i18n/messages/demo.ts +80 -0
- package/src/i18n/messages/files.ts +82 -0
- package/src/i18n/messages/network.ts +190 -0
- package/src/i18n/messages/profile.ts +44 -0
- package/src/i18n/messages/quota.ts +36 -0
- package/src/i18n/messages/sandbox.ts +116 -0
- package/src/i18n/messages/search.ts +16 -0
- package/src/i18n/messages/settings.ts +188 -0
- package/src/i18n/messages/shell.ts +38 -0
- package/src/i18n/messages/sidebar.ts +52 -0
- package/src/i18n/messages/terminal.ts +22 -0
- package/src/i18n/messages/trace.ts +136 -0
- package/src/i18n/messages.ts +32 -0
- package/src/i18n/translate.ts +46 -0
- package/src/i18n/types.ts +15 -0
- package/src/i18n/useT.ts +15 -0
- package/src/main.tsx +13 -0
- package/src/mocks/backend.ts +729 -0
- package/src/styles/global.css +7578 -0
- package/src/styles/tokens.css +161 -0
- package/src/utils/api.ts +724 -0
- package/src/utils/download.ts +18 -0
- package/src/utils/format.ts +7 -0
- package/src/utils/toolDisplay.ts +74 -0
- package/src/utils/zip.ts +119 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.app.json +22 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +13 -0
- package/vite.config.ts +13 -0
- package/dist/assets/index-Cd0Mi_WU.css +0 -1
- package/dist/assets/index-FGg-DeYR.js +0 -448
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/* --------------------------------------------------------------------------
|
|
2
|
+
* agentAnalytics — pure derivation functions over the session's ChatMessage[]
|
|
3
|
+
* and the derived AgentEdge[]. No React, no DOM: easy to unit-test and reuse
|
|
4
|
+
* across GlobalOverview / AnalyticsTab / TimelineTab.
|
|
5
|
+
*
|
|
6
|
+
* All inter-agent semantics go through the SAME helpers used to draw the
|
|
7
|
+
* network graph (`getMessageEdge` / `msgTypeKind`) so analytics never drifts
|
|
8
|
+
* from what the graph shows.
|
|
9
|
+
* ------------------------------------------------------------------------ */
|
|
10
|
+
import { ChatMessage } from "../../contracts/backend";
|
|
11
|
+
import { AgentEdge, getMessageEdge, msgTypeKind } from "./agentNetworkShared";
|
|
12
|
+
|
|
13
|
+
export interface TrendPoint {
|
|
14
|
+
time: number; // bucket start, ms epoch
|
|
15
|
+
count: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AgentLoad {
|
|
19
|
+
name: string;
|
|
20
|
+
sent: number;
|
|
21
|
+
received: number;
|
|
22
|
+
total: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TypeDistribution {
|
|
26
|
+
delegate: number;
|
|
27
|
+
result: number;
|
|
28
|
+
other: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface LatencyStats {
|
|
32
|
+
count: number;
|
|
33
|
+
min: number;
|
|
34
|
+
q1: number;
|
|
35
|
+
median: number;
|
|
36
|
+
q3: number;
|
|
37
|
+
max: number;
|
|
38
|
+
mean: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TokenRow {
|
|
42
|
+
name: string;
|
|
43
|
+
sentMsgs: number;
|
|
44
|
+
avgLen: number;
|
|
45
|
+
tokens: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Heatmap {
|
|
49
|
+
agents: string[];
|
|
50
|
+
buckets: number;
|
|
51
|
+
bucketMs: number;
|
|
52
|
+
startMs: number;
|
|
53
|
+
/** counts[agentIndex][bucketIndex] */
|
|
54
|
+
counts: number[][];
|
|
55
|
+
max: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const tsOf = (iso: string): number => {
|
|
59
|
+
const t = new Date(iso).getTime();
|
|
60
|
+
return Number.isFinite(t) ? t : 0;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Inter-agent messages only (the same set the graph draws edges from). */
|
|
64
|
+
function interAgentMessages(messages: ChatMessage[]) {
|
|
65
|
+
return messages
|
|
66
|
+
.map((m) => getMessageEdge(m))
|
|
67
|
+
.filter((e): e is NonNullable<ReturnType<typeof getMessageEdge>> => e !== null);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Message volume bucketed over [now - windowMs, now]. */
|
|
71
|
+
export function computeMessageTrend(
|
|
72
|
+
messages: ChatMessage[],
|
|
73
|
+
nowMs: number,
|
|
74
|
+
windowMs = 3_600_000,
|
|
75
|
+
buckets = 30,
|
|
76
|
+
): TrendPoint[] {
|
|
77
|
+
const start = nowMs - windowMs;
|
|
78
|
+
const bucketMs = windowMs / buckets;
|
|
79
|
+
const counts = new Array(buckets).fill(0);
|
|
80
|
+
for (const e of interAgentMessages(messages)) {
|
|
81
|
+
const ts = tsOf(e.timestamp);
|
|
82
|
+
if (ts < start || ts > nowMs) continue;
|
|
83
|
+
const idx = Math.min(buckets - 1, Math.floor((ts - start) / bucketMs));
|
|
84
|
+
counts[idx] += 1;
|
|
85
|
+
}
|
|
86
|
+
return counts.map((count, i) => ({ time: start + i * bucketMs, count }));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function computeAgentLoad(edges: AgentEdge[]): AgentLoad[] {
|
|
90
|
+
const map = new Map<string, AgentLoad>();
|
|
91
|
+
const ensure = (name: string) => {
|
|
92
|
+
let row = map.get(name);
|
|
93
|
+
if (!row) {
|
|
94
|
+
row = { name, sent: 0, received: 0, total: 0 };
|
|
95
|
+
map.set(name, row);
|
|
96
|
+
}
|
|
97
|
+
return row;
|
|
98
|
+
};
|
|
99
|
+
for (const edge of edges) {
|
|
100
|
+
ensure(edge.from).sent += edge.messages.length;
|
|
101
|
+
ensure(edge.to).received += edge.messages.length;
|
|
102
|
+
}
|
|
103
|
+
const rows = Array.from(map.values());
|
|
104
|
+
rows.forEach((r) => (r.total = r.sent + r.received));
|
|
105
|
+
rows.sort((a, b) => b.total - a.total);
|
|
106
|
+
return rows;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function computeTypeDistribution(messages: ChatMessage[]): TypeDistribution {
|
|
110
|
+
const dist: TypeDistribution = { delegate: 0, result: 0, other: 0 };
|
|
111
|
+
for (const e of interAgentMessages(messages)) {
|
|
112
|
+
const kind = msgTypeKind(e.msgType);
|
|
113
|
+
if (kind === "delegate") dist.delegate += 1;
|
|
114
|
+
else if (kind === "result") dist.result += 1;
|
|
115
|
+
else dist.other += 1;
|
|
116
|
+
}
|
|
117
|
+
return dist;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Pair each delegate with the next result that the delegated-to agent sends
|
|
122
|
+
* back (after the delegate's timestamp). Returns raw latencies in ms.
|
|
123
|
+
*/
|
|
124
|
+
export function computeResponseLatencies(messages: ChatMessage[]): number[] {
|
|
125
|
+
const inter = interAgentMessages(messages).sort(
|
|
126
|
+
(a, b) => tsOf(a.timestamp) - tsOf(b.timestamp),
|
|
127
|
+
);
|
|
128
|
+
const latencies: number[] = [];
|
|
129
|
+
const usedResultIdx = new Set<number>();
|
|
130
|
+
|
|
131
|
+
inter.forEach((msg) => {
|
|
132
|
+
if (msgTypeKind(msg.msgType) !== "delegate") return;
|
|
133
|
+
const delegateTs = tsOf(msg.timestamp);
|
|
134
|
+
// Find earliest unused result FROM the delegated-to agent, after this time.
|
|
135
|
+
for (let i = 0; i < inter.length; i++) {
|
|
136
|
+
if (usedResultIdx.has(i)) continue;
|
|
137
|
+
const cand = inter[i];
|
|
138
|
+
if (msgTypeKind(cand.msgType) !== "result") continue;
|
|
139
|
+
if (cand.from !== msg.to) continue;
|
|
140
|
+
const candTs = tsOf(cand.timestamp);
|
|
141
|
+
if (candTs <= delegateTs) continue;
|
|
142
|
+
usedResultIdx.add(i);
|
|
143
|
+
latencies.push(candTs - delegateTs);
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return latencies;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function summarizeLatencies(latencies: number[]): LatencyStats | null {
|
|
151
|
+
if (latencies.length === 0) return null;
|
|
152
|
+
const sorted = [...latencies].sort((a, b) => a - b);
|
|
153
|
+
const q = (p: number) => {
|
|
154
|
+
const idx = (sorted.length - 1) * p;
|
|
155
|
+
const lo = Math.floor(idx);
|
|
156
|
+
const hi = Math.ceil(idx);
|
|
157
|
+
if (lo === hi) return sorted[lo];
|
|
158
|
+
return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo);
|
|
159
|
+
};
|
|
160
|
+
const mean = sorted.reduce((s, v) => s + v, 0) / sorted.length;
|
|
161
|
+
return {
|
|
162
|
+
count: sorted.length,
|
|
163
|
+
min: sorted[0],
|
|
164
|
+
q1: q(0.25),
|
|
165
|
+
median: q(0.5),
|
|
166
|
+
q3: q(0.75),
|
|
167
|
+
max: sorted[sorted.length - 1],
|
|
168
|
+
mean,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Rough per-agent token estimate from the content the agent SENT. */
|
|
173
|
+
export function estimateTokens(messages: ChatMessage[]): TokenRow[] {
|
|
174
|
+
const map = new Map<string, { chars: number; msgs: number }>();
|
|
175
|
+
for (const e of interAgentMessages(messages)) {
|
|
176
|
+
const row = map.get(e.from) ?? { chars: 0, msgs: 0 };
|
|
177
|
+
row.chars += e.content.length;
|
|
178
|
+
row.msgs += 1;
|
|
179
|
+
map.set(e.from, row);
|
|
180
|
+
}
|
|
181
|
+
const rows: TokenRow[] = Array.from(map.entries()).map(([name, { chars, msgs }]) => ({
|
|
182
|
+
name,
|
|
183
|
+
sentMsgs: msgs,
|
|
184
|
+
avgLen: msgs ? Math.round(chars / msgs) : 0,
|
|
185
|
+
tokens: Math.ceil(chars / 4),
|
|
186
|
+
}));
|
|
187
|
+
rows.sort((a, b) => b.tokens - a.tokens);
|
|
188
|
+
return rows;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function computeLifecycleHeatmap(
|
|
192
|
+
messages: ChatMessage[],
|
|
193
|
+
agentNames: string[],
|
|
194
|
+
nowMs: number,
|
|
195
|
+
buckets = 20,
|
|
196
|
+
): Heatmap {
|
|
197
|
+
const inter = interAgentMessages(messages);
|
|
198
|
+
const times = inter.map((e) => tsOf(e.timestamp)).filter((t) => t > 0);
|
|
199
|
+
const startMs = times.length ? Math.min(...times) : nowMs;
|
|
200
|
+
const span = Math.max(1, nowMs - startMs);
|
|
201
|
+
const bucketMs = span / buckets;
|
|
202
|
+
|
|
203
|
+
const agents = agentNames.filter((name) =>
|
|
204
|
+
inter.some((e) => e.from === name || e.to === name),
|
|
205
|
+
);
|
|
206
|
+
const indexOf = new Map(agents.map((a, i) => [a, i]));
|
|
207
|
+
const counts = agents.map(() => new Array(buckets).fill(0));
|
|
208
|
+
let max = 0;
|
|
209
|
+
|
|
210
|
+
for (const e of inter) {
|
|
211
|
+
const ts = tsOf(e.timestamp);
|
|
212
|
+
const bIdx = Math.min(buckets - 1, Math.max(0, Math.floor((ts - startMs) / bucketMs)));
|
|
213
|
+
for (const who of [e.from, e.to]) {
|
|
214
|
+
const aIdx = indexOf.get(who);
|
|
215
|
+
if (aIdx === undefined) continue;
|
|
216
|
+
counts[aIdx][bIdx] += 1;
|
|
217
|
+
if (counts[aIdx][bIdx] > max) max = counts[aIdx][bIdx];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return { agents, buckets, bucketMs, startMs, counts, max };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function computeErrorCount(messages: ChatMessage[]): number {
|
|
225
|
+
return messages.filter((m) => m.kind === "error").length;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Human-friendly duration formatter for latency values. */
|
|
229
|
+
export function formatDuration(ms: number): string {
|
|
230
|
+
if (!Number.isFinite(ms)) return "—";
|
|
231
|
+
if (ms < 1000) return `${Math.round(ms)}ms`;
|
|
232
|
+
if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
|
|
233
|
+
return `${(ms / 60_000).toFixed(1)}m`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* --------------------------------------------------------------------------
|
|
237
|
+
* Per-agent activity statistics (all messages, not just send_message)
|
|
238
|
+
* ------------------------------------------------------------------------ */
|
|
239
|
+
|
|
240
|
+
export interface AgentActivity {
|
|
241
|
+
name: string;
|
|
242
|
+
// Message output by role
|
|
243
|
+
assistantMessages: number;
|
|
244
|
+
reasoningMessages: number;
|
|
245
|
+
toolMessages: number;
|
|
246
|
+
systemMessages: number;
|
|
247
|
+
totalMessages: number;
|
|
248
|
+
// Content volume
|
|
249
|
+
totalChars: number;
|
|
250
|
+
estimatedTokens: number;
|
|
251
|
+
// Tool usage
|
|
252
|
+
toolCalls: number;
|
|
253
|
+
toolCallsByName: Record<string, number>;
|
|
254
|
+
topTools: Array<{ name: string; count: number }>;
|
|
255
|
+
// Communication (send_message only)
|
|
256
|
+
sentMessages: number;
|
|
257
|
+
receivedMessages: number;
|
|
258
|
+
communicationPartners: string[];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Compute comprehensive activity stats for a specific agent.
|
|
263
|
+
* Includes all message types, tool calls, and communication patterns.
|
|
264
|
+
*/
|
|
265
|
+
export function computeAgentActivity(
|
|
266
|
+
agentName: string,
|
|
267
|
+
messages: ChatMessage[],
|
|
268
|
+
edges: AgentEdge[],
|
|
269
|
+
): AgentActivity {
|
|
270
|
+
// Filter messages from this agent
|
|
271
|
+
const agentMessages = messages.filter((m) => m.agent === agentName);
|
|
272
|
+
|
|
273
|
+
// Count by role
|
|
274
|
+
let assistantMessages = 0;
|
|
275
|
+
let reasoningMessages = 0;
|
|
276
|
+
let toolMessages = 0;
|
|
277
|
+
let systemMessages = 0;
|
|
278
|
+
let totalChars = 0;
|
|
279
|
+
|
|
280
|
+
for (const msg of agentMessages) {
|
|
281
|
+
if (msg.role === "assistant") assistantMessages++;
|
|
282
|
+
else if (msg.role === "system") systemMessages++;
|
|
283
|
+
// Tool messages are identified by kind, not role
|
|
284
|
+
if (msg.kind === "thinking") reasoningMessages++;
|
|
285
|
+
else if (msg.kind === "tool") toolMessages++;
|
|
286
|
+
|
|
287
|
+
totalChars += (msg.content || "").length;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Tool call statistics
|
|
291
|
+
const toolCallsByName: Record<string, number> = {};
|
|
292
|
+
let toolCalls = 0;
|
|
293
|
+
|
|
294
|
+
for (const msg of agentMessages) {
|
|
295
|
+
if (msg.kind === "tool" && msg.toolName) {
|
|
296
|
+
toolCalls++;
|
|
297
|
+
toolCallsByName[msg.toolName] = (toolCallsByName[msg.toolName] || 0) + 1;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const topTools = Object.entries(toolCallsByName)
|
|
302
|
+
.map(([name, count]) => ({ name, count }))
|
|
303
|
+
.sort((a, b) => b.count - a.count)
|
|
304
|
+
.slice(0, 3);
|
|
305
|
+
|
|
306
|
+
// Communication statistics (from edges)
|
|
307
|
+
let sentMessages = 0;
|
|
308
|
+
let receivedMessages = 0;
|
|
309
|
+
const partners = new Set<string>();
|
|
310
|
+
|
|
311
|
+
for (const edge of edges) {
|
|
312
|
+
if (edge.from === agentName) {
|
|
313
|
+
sentMessages += edge.messages.length;
|
|
314
|
+
partners.add(edge.to);
|
|
315
|
+
}
|
|
316
|
+
if (edge.to === agentName) {
|
|
317
|
+
receivedMessages += edge.messages.length;
|
|
318
|
+
partners.add(edge.from);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
name: agentName,
|
|
324
|
+
assistantMessages,
|
|
325
|
+
reasoningMessages,
|
|
326
|
+
toolMessages,
|
|
327
|
+
systemMessages,
|
|
328
|
+
totalMessages: agentMessages.length,
|
|
329
|
+
totalChars,
|
|
330
|
+
estimatedTokens: Math.ceil(totalChars / 4),
|
|
331
|
+
toolCalls,
|
|
332
|
+
toolCallsByName,
|
|
333
|
+
topTools,
|
|
334
|
+
sentMessages,
|
|
335
|
+
receivedMessages,
|
|
336
|
+
communicationPartners: Array.from(partners).sort(),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Compute activity stats for all agents in the session.
|
|
342
|
+
* Returns a map of agent name -> activity stats.
|
|
343
|
+
*/
|
|
344
|
+
export function computeAllAgentActivities(
|
|
345
|
+
messages: ChatMessage[],
|
|
346
|
+
edges: AgentEdge[],
|
|
347
|
+
): Map<string, AgentActivity> {
|
|
348
|
+
const agentNames = new Set<string>();
|
|
349
|
+
|
|
350
|
+
// Collect all agent names from messages
|
|
351
|
+
for (const msg of messages) {
|
|
352
|
+
if (msg.agent) agentNames.add(msg.agent);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Also collect from edges
|
|
356
|
+
for (const edge of edges) {
|
|
357
|
+
agentNames.add(edge.from);
|
|
358
|
+
agentNames.add(edge.to);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const activities = new Map<string, AgentActivity>();
|
|
362
|
+
for (const name of agentNames) {
|
|
363
|
+
activities.set(name, computeAgentActivity(name, messages, edges));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return activities;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Compute global activity percentages for an agent.
|
|
371
|
+
*/
|
|
372
|
+
export interface AgentActivityPercentages {
|
|
373
|
+
messagePercent: number; // This agent's messages / total messages
|
|
374
|
+
toolCallPercent: number; // This agent's tool calls / total tool calls
|
|
375
|
+
tokenPercent: number; // This agent's tokens / total tokens
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function computeAgentActivityPercentages(
|
|
379
|
+
activity: AgentActivity,
|
|
380
|
+
allActivities: Map<string, AgentActivity>,
|
|
381
|
+
): AgentActivityPercentages {
|
|
382
|
+
let totalMessages = 0;
|
|
383
|
+
let totalToolCalls = 0;
|
|
384
|
+
let totalTokens = 0;
|
|
385
|
+
|
|
386
|
+
for (const a of allActivities.values()) {
|
|
387
|
+
totalMessages += a.totalMessages;
|
|
388
|
+
totalToolCalls += a.toolCalls;
|
|
389
|
+
totalTokens += a.estimatedTokens;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
messagePercent: totalMessages > 0 ? (activity.totalMessages / totalMessages) * 100 : 0,
|
|
394
|
+
toolCallPercent: totalToolCalls > 0 ? (activity.toolCalls / totalToolCalls) * 100 : 0,
|
|
395
|
+
tokenPercent: totalTokens > 0 ? (activity.estimatedTokens / totalTokens) * 100 : 0,
|
|
396
|
+
};
|
|
397
|
+
}
|