@openclawcity/become 0.1.0

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.
@@ -0,0 +1,806 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/dashboard/index.ts
21
+ var dashboard_exports = {};
22
+ __export(dashboard_exports, {
23
+ ACCENT: () => ACCENT,
24
+ BACKGROUND: () => BACKGROUND,
25
+ BORDER: () => BORDER,
26
+ CELEBRATION_CONFIG: () => CELEBRATION_CONFIG,
27
+ GOLD: () => GOLD,
28
+ GrowthCard: () => GrowthCard,
29
+ MilestoneTimeline: () => MilestoneTimeline,
30
+ PeerGraph: () => PeerGraph,
31
+ PopulationView: () => PopulationView,
32
+ STAGE_COLORS: () => STAGE_COLORS,
33
+ STAGE_LABELS: () => STAGE_LABELS,
34
+ SkillRing: () => SkillRing,
35
+ Sparkline: () => Sparkline
36
+ });
37
+ module.exports = __toCommonJS(dashboard_exports);
38
+
39
+ // src/dashboard/theme.ts
40
+ var STAGE_COLORS = {
41
+ novice: "#64748b",
42
+ beginner: "#22d3ee",
43
+ competent: "#34d399",
44
+ proficient: "#a78bfa",
45
+ expert: "#fbbf24"
46
+ };
47
+ var STAGE_LABELS = {
48
+ novice: "Novice",
49
+ beginner: "Beginner",
50
+ competent: "Competent",
51
+ proficient: "Proficient",
52
+ expert: "Expert"
53
+ };
54
+ var BACKGROUND = {
55
+ panel: "rgba(10, 12, 20, 0.94)",
56
+ card: "rgba(255, 255, 255, 0.03)",
57
+ hover: "rgba(255, 255, 255, 0.06)"
58
+ };
59
+ var BORDER = {
60
+ subtle: "rgba(0, 212, 255, 0.15)",
61
+ accent: "rgba(0, 212, 255, 0.3)"
62
+ };
63
+ var ACCENT = "#00d4ff";
64
+ var GOLD = "rgba(255, 215, 0, 0.8)";
65
+ var CELEBRATION_CONFIG = {
66
+ micro: { particles: 0, spread: 0, duration: 0 },
67
+ small: { particles: 20, spread: 50, duration: 1e3 },
68
+ medium: { particles: 60, spread: 70, duration: 1500 },
69
+ large: { particles: 80, spread: 90, duration: 2e3 },
70
+ epic: { particles: 120, spread: 120, duration: 4e3 }
71
+ };
72
+
73
+ // src/dashboard/components/SkillRing.tsx
74
+ var import_jsx_runtime = require("react/jsx-runtime");
75
+ function SkillRing({
76
+ skill,
77
+ score,
78
+ stage,
79
+ size = 80,
80
+ showLabel = true,
81
+ className
82
+ }) {
83
+ const safeSize = Math.max(24, size);
84
+ const strokeWidth = Math.max(4, safeSize * 0.08);
85
+ const radius = (safeSize - strokeWidth) / 2;
86
+ const circumference = 2 * Math.PI * radius;
87
+ const progress = Math.min(100, Math.max(0, score)) / 100;
88
+ const offset = circumference * (1 - progress);
89
+ const color = STAGE_COLORS[stage];
90
+ const center = safeSize / 2;
91
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
92
+ "div",
93
+ {
94
+ className,
95
+ style: {
96
+ display: "inline-flex",
97
+ flexDirection: "column",
98
+ alignItems: "center",
99
+ gap: 4
100
+ },
101
+ children: [
102
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
103
+ "svg",
104
+ {
105
+ width: safeSize,
106
+ height: safeSize,
107
+ viewBox: `0 0 ${safeSize} ${safeSize}`,
108
+ role: "img",
109
+ "aria-label": `${skill}: ${score}/100, ${STAGE_LABELS[stage]}`,
110
+ children: [
111
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
112
+ "circle",
113
+ {
114
+ cx: center,
115
+ cy: center,
116
+ r: radius,
117
+ fill: "none",
118
+ stroke: "rgba(255,255,255,0.08)",
119
+ strokeWidth
120
+ }
121
+ ),
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
123
+ "circle",
124
+ {
125
+ cx: center,
126
+ cy: center,
127
+ r: radius,
128
+ fill: "none",
129
+ stroke: color,
130
+ strokeWidth,
131
+ strokeDasharray: circumference,
132
+ strokeDashoffset: offset,
133
+ strokeLinecap: "round",
134
+ transform: `rotate(-90 ${center} ${center})`,
135
+ style: { transition: "stroke-dashoffset 0.6s ease" }
136
+ }
137
+ ),
138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
139
+ "text",
140
+ {
141
+ x: center,
142
+ y: center,
143
+ textAnchor: "middle",
144
+ dominantBaseline: "central",
145
+ fill: "white",
146
+ fontSize: safeSize * 0.28,
147
+ fontWeight: "bold",
148
+ fontFamily: "system-ui, sans-serif",
149
+ children: score
150
+ }
151
+ )
152
+ ]
153
+ }
154
+ ),
155
+ showLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { textAlign: "center" }, children: [
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
157
+ "div",
158
+ {
159
+ style: {
160
+ color: "rgba(255,255,255,0.9)",
161
+ fontSize: Math.max(10, safeSize * 0.14),
162
+ fontWeight: 500,
163
+ fontFamily: "system-ui, sans-serif"
164
+ },
165
+ children: skill
166
+ }
167
+ ),
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
169
+ "div",
170
+ {
171
+ style: {
172
+ color,
173
+ fontSize: Math.max(9, safeSize * 0.12),
174
+ fontFamily: "system-ui, sans-serif",
175
+ textTransform: "uppercase",
176
+ letterSpacing: "0.05em"
177
+ },
178
+ children: STAGE_LABELS[stage]
179
+ }
180
+ )
181
+ ] })
182
+ ]
183
+ }
184
+ );
185
+ }
186
+
187
+ // src/dashboard/components/Sparkline.tsx
188
+ var import_react = require("react");
189
+ var import_jsx_runtime2 = require("react/jsx-runtime");
190
+ function Sparkline({
191
+ data,
192
+ width = 300,
193
+ height = 40,
194
+ color = "#22d3ee",
195
+ showDots = false,
196
+ className
197
+ }) {
198
+ if (data.length < 2) {
199
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width, height, className, role: "img", "aria-label": "Insufficient data", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: width / 2, y: height / 2, textAnchor: "middle", dominantBaseline: "central", fill: "rgba(255,255,255,0.3)", fontSize: 10, children: "Not enough data" }) });
200
+ }
201
+ const strokeColor = Object.prototype.hasOwnProperty.call(STAGE_COLORS, color) ? STAGE_COLORS[color] : color;
202
+ const padding = 4;
203
+ const chartWidth = width - padding * 2;
204
+ const chartHeight = height - padding * 2;
205
+ const scores = data.map((d) => d.score);
206
+ let minScore = scores[0];
207
+ let maxScore = scores[0];
208
+ for (let i = 1; i < scores.length; i++) {
209
+ if (scores[i] < minScore) minScore = scores[i];
210
+ if (scores[i] > maxScore) maxScore = scores[i];
211
+ }
212
+ const range = maxScore - minScore || 1;
213
+ const points = data.map((d, i) => {
214
+ const x = padding + i / (data.length - 1) * chartWidth;
215
+ const y = padding + chartHeight - (d.score - minScore) / range * chartHeight;
216
+ return { x, y };
217
+ });
218
+ const polyline = points.map((p) => `${p.x},${p.y}`).join(" ");
219
+ const fillPoints = [
220
+ ...points.map((p) => `${p.x},${p.y}`),
221
+ `${points[points.length - 1].x},${height - padding}`,
222
+ `${points[0].x},${height - padding}`
223
+ ].join(" ");
224
+ const reactId = (0, import_react.useId)();
225
+ const gradientId = `sparkline-grad-${reactId.replace(/:/g, "")}`;
226
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
227
+ "svg",
228
+ {
229
+ width,
230
+ height,
231
+ viewBox: `0 0 ${width} ${height}`,
232
+ className,
233
+ role: "img",
234
+ "aria-label": `Trend: ${scores[0]} to ${scores[scores.length - 1]}`,
235
+ children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("linearGradient", { id: gradientId, x1: "0%", y1: "0%", x2: "0%", y2: "100%", children: [
237
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: "0%", stopColor: strokeColor, stopOpacity: "0.2" }),
238
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: "100%", stopColor: strokeColor, stopOpacity: "0" })
239
+ ] }) }),
240
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: fillPoints, fill: `url(#${gradientId})` }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
242
+ "polyline",
243
+ {
244
+ points: polyline,
245
+ fill: "none",
246
+ stroke: strokeColor,
247
+ strokeWidth: 1.5,
248
+ strokeLinejoin: "round",
249
+ strokeLinecap: "round"
250
+ }
251
+ ),
252
+ showDots && points.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
253
+ "circle",
254
+ {
255
+ cx: p.x,
256
+ cy: p.y,
257
+ r: 2,
258
+ fill: strokeColor
259
+ },
260
+ i
261
+ )),
262
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
263
+ "circle",
264
+ {
265
+ cx: points[points.length - 1].x,
266
+ cy: points[points.length - 1].y,
267
+ r: 2.5,
268
+ fill: strokeColor
269
+ }
270
+ )
271
+ ]
272
+ }
273
+ );
274
+ }
275
+
276
+ // src/core/validation.ts
277
+ var MAX_AGENT_ID_LENGTH = 200;
278
+ var AGENT_ID_REGEX = /^[a-zA-Z0-9_.:@/-]+$/;
279
+ function validateAgentId(agentId) {
280
+ if (!agentId || typeof agentId !== "string") {
281
+ throw new Error("agentId is required and must be a non-empty string");
282
+ }
283
+ if (agentId.length > MAX_AGENT_ID_LENGTH) {
284
+ throw new Error(`agentId too long (max ${MAX_AGENT_ID_LENGTH} chars)`);
285
+ }
286
+ if (!AGENT_ID_REGEX.test(agentId)) {
287
+ throw new Error("agentId contains invalid characters (allowed: alphanumeric, _ . : @ / -)");
288
+ }
289
+ }
290
+
291
+ // src/core/milestones.ts
292
+ var BUILT_IN = {
293
+ skill_discovered: { threshold: 1, description: "First score for a skill" },
294
+ skill_competent: { threshold: 36, description: "Reached competent stage" },
295
+ skill_proficient: { threshold: 56, description: "Reached proficient stage" },
296
+ skill_expert: { threshold: 76, description: "Reached expert stage" },
297
+ first_artifact: { threshold: 1, description: "Created first output" },
298
+ ten_artifacts: { threshold: 10, description: "Created 10 outputs" },
299
+ first_collab: { threshold: 1, description: "Completed first collaboration" },
300
+ first_teaching: { threshold: 1, description: "Taught another agent for the first time" },
301
+ first_peer_review: { threshold: 1, description: "Gave first peer review" },
302
+ identity_shift: { threshold: 1, description: "Agent evolved its identity" },
303
+ norm_setter: { threshold: 1, description: "Started a cultural norm adopted by 3+ agents" }
304
+ };
305
+ var MilestoneDetector = class {
306
+ constructor(adapter) {
307
+ this.adapter = adapter;
308
+ }
309
+ custom = {};
310
+ register(type, config) {
311
+ this.custom[type] = config;
312
+ }
313
+ async check(agentId, scores) {
314
+ validateAgentId(agentId);
315
+ const awarded = [];
316
+ const now = (/* @__PURE__ */ new Date()).toISOString();
317
+ const globalChecked = /* @__PURE__ */ new Set();
318
+ for (const score of scores) {
319
+ if (score.score > 0) {
320
+ const type = `skill_discovered:${score.skill}`;
321
+ if (await this.tryAward(agentId, type, 1, score.skill, now)) {
322
+ awarded.push({ agent_id: agentId, milestone_type: type, threshold: 1, skill: score.skill, achieved_at: now });
323
+ }
324
+ }
325
+ const stageChecks = [
326
+ ["skill_competent", 36],
327
+ ["skill_proficient", 56],
328
+ ["skill_expert", 76]
329
+ ];
330
+ for (const [prefix, threshold] of stageChecks) {
331
+ if (score.score >= threshold) {
332
+ const type = `${prefix}:${score.skill}`;
333
+ if (await this.tryAward(agentId, type, threshold, score.skill, now)) {
334
+ awarded.push({ agent_id: agentId, milestone_type: type, threshold, skill: score.skill, achieved_at: now });
335
+ }
336
+ }
337
+ }
338
+ const globalMilestones = [
339
+ ["first_artifact", 1, score.evidence.artifact_count >= 1],
340
+ ["ten_artifacts", 10, score.evidence.artifact_count >= 10],
341
+ ["first_collab", 1, score.evidence.collab_count >= 1],
342
+ ["first_teaching", 1, score.evidence.teaching_events >= 1],
343
+ ["first_peer_review", 1, score.evidence.peer_reviews_given >= 1]
344
+ ];
345
+ for (const [type, threshold, eligible] of globalMilestones) {
346
+ if (eligible && !globalChecked.has(type)) {
347
+ globalChecked.add(type);
348
+ if (await this.tryAward(agentId, type, threshold, void 0, now)) {
349
+ awarded.push({ agent_id: agentId, milestone_type: type, threshold, achieved_at: now });
350
+ }
351
+ }
352
+ }
353
+ }
354
+ return awarded;
355
+ }
356
+ async tryAward(agentId, milestoneType, threshold, skill, now) {
357
+ const exists = await this.adapter.hasMilestone(agentId, milestoneType, skill);
358
+ if (exists) return false;
359
+ return this.adapter.saveMilestone({
360
+ agent_id: agentId,
361
+ milestone_type: milestoneType,
362
+ threshold,
363
+ skill,
364
+ achieved_at: now
365
+ });
366
+ }
367
+ static celebrationTier(milestoneType, threshold) {
368
+ if (milestoneType.startsWith("skill_expert")) return "epic";
369
+ if (milestoneType.startsWith("skill_proficient")) return "large";
370
+ if (milestoneType.startsWith("skill_competent")) return "medium";
371
+ if (milestoneType.startsWith("skill_discovered")) return "small";
372
+ if (threshold !== void 0) {
373
+ if (threshold >= 50) return "large";
374
+ if (threshold >= 10) return "medium";
375
+ }
376
+ return "small";
377
+ }
378
+ static getBuiltInMilestones() {
379
+ return { ...BUILT_IN };
380
+ }
381
+ };
382
+
383
+ // src/dashboard/components/MilestoneTimeline.tsx
384
+ var import_jsx_runtime3 = require("react/jsx-runtime");
385
+ function formatMilestoneLabel(type) {
386
+ if (type.includes(":")) {
387
+ const [prefix, skill] = type.split(":");
388
+ const stage = prefix.replace("skill_", "");
389
+ return `${stage.charAt(0).toUpperCase() + stage.slice(1)}: ${skill}`;
390
+ }
391
+ return type.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
392
+ }
393
+ function tierColor(tier) {
394
+ switch (tier) {
395
+ case "epic":
396
+ return GOLD;
397
+ case "large":
398
+ return STAGE_COLORS.proficient;
399
+ case "medium":
400
+ return STAGE_COLORS.competent;
401
+ case "small":
402
+ return STAGE_COLORS.beginner;
403
+ default:
404
+ return "rgba(255,255,255,0.3)";
405
+ }
406
+ }
407
+ function formatDate(iso) {
408
+ try {
409
+ const d = new Date(iso);
410
+ return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
411
+ } catch {
412
+ return "";
413
+ }
414
+ }
415
+ function MilestoneTimeline({
416
+ milestones,
417
+ limit = 10,
418
+ className
419
+ }) {
420
+ const sorted = [...milestones].sort((a, b) => b.achieved_at.localeCompare(a.achieved_at)).slice(0, limit);
421
+ if (sorted.length === 0) {
422
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: { color: "rgba(255,255,255,0.4)", fontSize: 13, fontFamily: "system-ui, sans-serif", padding: 16 }, children: "No milestones yet" });
423
+ }
424
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className, style: { position: "relative", paddingLeft: 20 }, role: "list", "aria-label": "Milestone timeline", children: [
425
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
426
+ "div",
427
+ {
428
+ style: {
429
+ position: "absolute",
430
+ left: 7,
431
+ top: 4,
432
+ bottom: 4,
433
+ width: 2,
434
+ background: "rgba(255,255,255,0.1)",
435
+ borderRadius: 1
436
+ }
437
+ }
438
+ ),
439
+ sorted.map((m, i) => {
440
+ const tier = MilestoneDetector.celebrationTier(m.milestone_type, m.threshold);
441
+ const color = tierColor(tier);
442
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
443
+ "div",
444
+ {
445
+ role: "listitem",
446
+ style: {
447
+ display: "flex",
448
+ alignItems: "flex-start",
449
+ gap: 10,
450
+ marginBottom: 12,
451
+ position: "relative"
452
+ },
453
+ children: [
454
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
455
+ "div",
456
+ {
457
+ style: {
458
+ position: "absolute",
459
+ left: -16,
460
+ top: 4,
461
+ width: 10,
462
+ height: 10,
463
+ borderRadius: "50%",
464
+ background: color,
465
+ border: `2px solid ${color}`,
466
+ boxShadow: tier === "epic" ? `0 0 8px ${color}` : void 0
467
+ }
468
+ }
469
+ ),
470
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1 }, children: [
471
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
472
+ "div",
473
+ {
474
+ style: {
475
+ color: "rgba(255,255,255,0.9)",
476
+ fontSize: 13,
477
+ fontWeight: 500,
478
+ fontFamily: "system-ui, sans-serif"
479
+ },
480
+ children: formatMilestoneLabel(m.milestone_type)
481
+ }
482
+ ),
483
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
484
+ "div",
485
+ {
486
+ style: {
487
+ color: "rgba(255,255,255,0.4)",
488
+ fontSize: 11,
489
+ fontFamily: "system-ui, sans-serif",
490
+ marginTop: 2
491
+ },
492
+ children: formatDate(m.achieved_at)
493
+ }
494
+ )
495
+ ] })
496
+ ]
497
+ },
498
+ `${m.milestone_type}-${m.achieved_at}-${i}`
499
+ );
500
+ })
501
+ ] });
502
+ }
503
+
504
+ // src/dashboard/components/GrowthCard.tsx
505
+ var import_jsx_runtime4 = require("react/jsx-runtime");
506
+ function GrowthCard({
507
+ agentId,
508
+ agentName,
509
+ scores,
510
+ scoreHistory,
511
+ milestones,
512
+ className
513
+ }) {
514
+ const sortedScores = [...scores].sort((a, b) => b.score - a.score);
515
+ const avgScore = scores.length > 0 ? Math.round(scores.reduce((sum, s) => sum + s.score, 0) / scores.length) : 0;
516
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
517
+ "div",
518
+ {
519
+ className,
520
+ style: {
521
+ background: BACKGROUND.panel,
522
+ border: `1px solid ${BORDER.subtle}`,
523
+ borderRadius: 12,
524
+ padding: 20,
525
+ fontFamily: "system-ui, sans-serif",
526
+ color: "white",
527
+ maxWidth: 380,
528
+ backdropFilter: "blur(20px) saturate(1.5)"
529
+ },
530
+ children: [
531
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: 16 }, children: [
532
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 16, fontWeight: 600, color: "rgba(255,255,255,0.95)" }, children: agentName ?? agentId }),
533
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: 12, color: "rgba(255,255,255,0.4)", marginTop: 2 }, children: [
534
+ scores.length,
535
+ " skill",
536
+ scores.length !== 1 ? "s" : "",
537
+ " \xB7 avg score ",
538
+ avgScore
539
+ ] })
540
+ ] }),
541
+ sortedScores.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
542
+ "div",
543
+ {
544
+ style: {
545
+ display: "flex",
546
+ flexWrap: "wrap",
547
+ gap: 16,
548
+ justifyContent: "center",
549
+ marginBottom: 20,
550
+ padding: "12px 0",
551
+ borderTop: `1px solid ${BORDER.subtle}`,
552
+ borderBottom: `1px solid ${BORDER.subtle}`
553
+ },
554
+ children: sortedScores.slice(0, 5).map((s) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
555
+ SkillRing,
556
+ {
557
+ skill: s.skill,
558
+ score: s.score,
559
+ stage: s.dreyfus_stage,
560
+ size: 64
561
+ },
562
+ s.skill
563
+ ))
564
+ }
565
+ ),
566
+ scoreHistory && sortedScores.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginBottom: 16 }, children: sortedScores.slice(0, 3).map((s) => {
567
+ const history = scoreHistory.get(s.skill);
568
+ if (!history || history.length < 2) return null;
569
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: 8 }, children: [
570
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", marginBottom: 2 }, children: s.skill }),
571
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
572
+ Sparkline,
573
+ {
574
+ data: history,
575
+ width: 340,
576
+ height: 32,
577
+ color: s.dreyfus_stage
578
+ }
579
+ )
580
+ ] }, s.skill);
581
+ }) }),
582
+ milestones && milestones.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
583
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 12, color: "rgba(255,255,255,0.5)", marginBottom: 8, fontWeight: 500 }, children: "Recent Milestones" }),
584
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MilestoneTimeline, { milestones, limit: 5 })
585
+ ] }),
586
+ scores.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { textAlign: "center", padding: 24, color: "rgba(255,255,255,0.3)", fontSize: 13 }, children: "No skills scored yet" })
587
+ ]
588
+ }
589
+ );
590
+ }
591
+
592
+ // src/dashboard/components/PeerGraph.tsx
593
+ var import_react2 = require("react");
594
+ var import_jsx_runtime5 = require("react/jsx-runtime");
595
+ function PeerGraph({
596
+ nodes,
597
+ edges,
598
+ width = 400,
599
+ height = 300,
600
+ className
601
+ }) {
602
+ const layout = (0, import_react2.useMemo)(() => {
603
+ const cx = width / 2;
604
+ const cy = height / 2;
605
+ const radius = Math.min(cx, cy) - 40;
606
+ return nodes.map((node, i) => {
607
+ const angle = 2 * Math.PI * i / nodes.length - Math.PI / 2;
608
+ return {
609
+ ...node,
610
+ x: cx + radius * Math.cos(angle),
611
+ y: cy + radius * Math.sin(angle)
612
+ };
613
+ });
614
+ }, [nodes, width, height]);
615
+ const nodeMap = (0, import_react2.useMemo)(() => {
616
+ const map = /* @__PURE__ */ new Map();
617
+ for (const n of layout) map.set(n.id, n);
618
+ return map;
619
+ }, [layout]);
620
+ const { edgeCounts, dedupedEdges, nodeEdgeCounts } = (0, import_react2.useMemo)(() => {
621
+ const counts = /* @__PURE__ */ new Map();
622
+ const deduped = [];
623
+ const seen = /* @__PURE__ */ new Set();
624
+ const nodeCounts = /* @__PURE__ */ new Map();
625
+ for (const e of edges) {
626
+ const key = [e.from_agent, e.to_agent].sort().join("\u2194");
627
+ counts.set(key, (counts.get(key) ?? 0) + 1);
628
+ nodeCounts.set(e.from_agent, (nodeCounts.get(e.from_agent) ?? 0) + 1);
629
+ nodeCounts.set(e.to_agent, (nodeCounts.get(e.to_agent) ?? 0) + 1);
630
+ }
631
+ for (const e of edges) {
632
+ const key = [e.from_agent, e.to_agent].sort().join("\u2194");
633
+ if (!seen.has(key)) {
634
+ seen.add(key);
635
+ deduped.push({ key, edge: e, count: counts.get(key) ?? 1 });
636
+ }
637
+ }
638
+ return { edgeCounts: counts, dedupedEdges: deduped, nodeEdgeCounts: nodeCounts };
639
+ }, [edges]);
640
+ if (nodes.length === 0) {
641
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width, height, className, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: width / 2, y: height / 2, textAnchor: "middle", fill: "rgba(255,255,255,0.3)", fontSize: 13, children: "No agents to display" }) });
642
+ }
643
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
644
+ "svg",
645
+ {
646
+ width,
647
+ height,
648
+ viewBox: `0 0 ${width} ${height}`,
649
+ className,
650
+ role: "img",
651
+ "aria-label": `Learning network: ${nodes.length} agents, ${edges.length} connections`,
652
+ children: [
653
+ dedupedEdges.map(({ key, edge, count }) => {
654
+ const from = nodeMap.get(edge.from_agent);
655
+ const to = nodeMap.get(edge.to_agent);
656
+ if (!from || !to) return null;
657
+ const strokeWidth = Math.min(4, 0.5 + count * 0.5);
658
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
659
+ "line",
660
+ {
661
+ x1: from.x,
662
+ y1: from.y,
663
+ x2: to.x,
664
+ y2: to.y,
665
+ stroke: ACCENT,
666
+ strokeWidth,
667
+ strokeOpacity: 0.3 + Math.min(0.4, count * 0.1)
668
+ },
669
+ `edge-${key}`
670
+ );
671
+ }),
672
+ layout.map((node) => {
673
+ const color = node.stage ? STAGE_COLORS[node.stage] : ACCENT;
674
+ const edgeCount = nodeEdgeCounts.get(node.id) ?? 0;
675
+ const radius = Math.min(20, 8 + edgeCount * 1.5);
676
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { children: [
677
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: node.x, cy: node.y, r: radius + 4, fill: color, opacity: 0.1 }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: node.x, cy: node.y, r: radius, fill: color, opacity: 0.8 }),
679
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
680
+ "text",
681
+ {
682
+ x: node.x,
683
+ y: node.y + radius + 14,
684
+ textAnchor: "middle",
685
+ fill: "rgba(255,255,255,0.7)",
686
+ fontSize: 10,
687
+ fontFamily: "system-ui, sans-serif",
688
+ children: node.label ?? node.id
689
+ }
690
+ )
691
+ ] }, node.id);
692
+ })
693
+ ]
694
+ }
695
+ );
696
+ }
697
+
698
+ // src/dashboard/components/PopulationView.tsx
699
+ var import_react3 = require("react");
700
+ var import_jsx_runtime6 = require("react/jsx-runtime");
701
+ function PopulationView({ agents, className }) {
702
+ const { allScores, avgScore, stageDist, totalSkills, topSkills } = (0, import_react3.useMemo)(() => {
703
+ const scores = agents.flatMap((a) => a.scores);
704
+ const avg = scores.length > 0 ? Math.round(scores.reduce((s, sc) => s + sc.score, 0) / scores.length) : 0;
705
+ const dist = {
706
+ novice: 0,
707
+ beginner: 0,
708
+ competent: 0,
709
+ proficient: 0,
710
+ expert: 0
711
+ };
712
+ for (const s of scores) {
713
+ dist[s.dreyfus_stage]++;
714
+ }
715
+ const skillCounts = /* @__PURE__ */ new Map();
716
+ for (const s of scores) {
717
+ skillCounts.set(s.skill, (skillCounts.get(s.skill) ?? 0) + 1);
718
+ }
719
+ const top = [...skillCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8);
720
+ return { allScores: scores, avgScore: avg, stageDist: dist, totalSkills: scores.length, topSkills: top };
721
+ }, [agents]);
722
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
723
+ "div",
724
+ {
725
+ className,
726
+ style: {
727
+ background: BACKGROUND.panel,
728
+ border: `1px solid ${BORDER.subtle}`,
729
+ borderRadius: 12,
730
+ padding: 20,
731
+ fontFamily: "system-ui, sans-serif",
732
+ color: "white",
733
+ maxWidth: 500,
734
+ backdropFilter: "blur(20px) saturate(1.5)"
735
+ },
736
+ children: [
737
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 16, fontWeight: 600, marginBottom: 16 }, children: "Population Overview" }),
738
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 24, marginBottom: 20 }, children: [
739
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Stat, { label: "Agents", value: agents.length }),
740
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Stat, { label: "Skills tracked", value: totalSkills }),
741
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Stat, { label: "Avg score", value: avgScore })
742
+ ] }),
743
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginBottom: 20 }, children: [
744
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 12, color: "rgba(255,255,255,0.5)", marginBottom: 8 }, children: "Stage Distribution" }),
745
+ totalSkills > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", height: 20, borderRadius: 4, overflow: "hidden" }, children: Object.entries(stageDist).filter(([, count]) => count > 0).map(([stage, count]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
747
+ "div",
748
+ {
749
+ style: {
750
+ width: `${count / totalSkills * 100}%`,
751
+ background: STAGE_COLORS[stage],
752
+ minWidth: 2
753
+ },
754
+ title: `${STAGE_LABELS[stage]}: ${count}`
755
+ },
756
+ stage
757
+ )) }),
758
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 12, marginTop: 6, flexWrap: "wrap" }, children: Object.entries(stageDist).filter(([, count]) => count > 0).map(([stage, count]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
759
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { width: 8, height: 8, borderRadius: 2, background: STAGE_COLORS[stage] } }),
760
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: 11, color: "rgba(255,255,255,0.6)" }, children: [
761
+ STAGE_LABELS[stage],
762
+ " (",
763
+ count,
764
+ ")"
765
+ ] })
766
+ ] }, stage)) })
767
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { color: "rgba(255,255,255,0.3)", fontSize: 12 }, children: "No data" })
768
+ ] }),
769
+ topSkills.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 12, color: "rgba(255,255,255,0.5)", marginBottom: 8 }, children: "Most Popular Skills" }),
771
+ topSkills.map(([skill, count]) => {
772
+ const pct = totalSkills > 0 ? count / totalSkills * 100 : 0;
773
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }, children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { width: 80, fontSize: 12, color: "rgba(255,255,255,0.7)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: skill }),
775
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1, height: 6, background: "rgba(255,255,255,0.06)", borderRadius: 3 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { width: `${pct}%`, height: "100%", background: "#22d3ee", borderRadius: 3, minWidth: 2 } }) }),
776
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { width: 24, fontSize: 11, color: "rgba(255,255,255,0.4)", textAlign: "right" }, children: count })
777
+ ] }, skill);
778
+ })
779
+ ] })
780
+ ]
781
+ }
782
+ );
783
+ }
784
+ function Stat({ label, value }) {
785
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
786
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 22, fontWeight: 700, color: "rgba(255,255,255,0.95)" }, children: value }),
787
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.4)" }, children: label })
788
+ ] });
789
+ }
790
+ // Annotate the CommonJS export names for ESM import in node:
791
+ 0 && (module.exports = {
792
+ ACCENT,
793
+ BACKGROUND,
794
+ BORDER,
795
+ CELEBRATION_CONFIG,
796
+ GOLD,
797
+ GrowthCard,
798
+ MilestoneTimeline,
799
+ PeerGraph,
800
+ PopulationView,
801
+ STAGE_COLORS,
802
+ STAGE_LABELS,
803
+ SkillRing,
804
+ Sparkline
805
+ });
806
+ //# sourceMappingURL=dashboard.cjs.map