arkaos 3.59.0 → 3.60.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.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.59.0
1
+ 3.60.0
@@ -226,6 +226,19 @@ function csvToList(value: string): string[] {
226
226
  type SuggestField = 'mental_models' | 'frameworks' | 'expertise_domains' | 'communication_avoid' | 'key_quotes'
227
227
  const suggestingField = ref<SuggestField | null>(null)
228
228
 
229
+ // PR97b v3.60.0 — weekly usage timeline (when did agents clone from here).
230
+ interface UsageWeek { week_start: string, count: number }
231
+ const { data: usageTimelineData } = fetchApi<{
232
+ weeks: UsageWeek[]
233
+ total_agents: number
234
+ period_weeks: number
235
+ }>(`/api/personas/${personaId}/usage-timeline?weeks=12`)
236
+ const usageWeeks = computed<UsageWeek[]>(() => usageTimelineData.value?.weeks ?? [])
237
+ const usageMaxCount = computed(() =>
238
+ Math.max(1, usageWeeks.value.reduce((acc, w) => Math.max(acc, w.count), 0)),
239
+ )
240
+ const usageTotalLinks = computed(() => usageTimelineData.value?.total_agents ?? 0)
241
+
229
242
  // PR86a v3.15.0 — favorites.
230
243
  const favs = useFavorites()
231
244
  await favs.load()
@@ -650,6 +663,44 @@ const vocabOptions = [
650
663
  </div>
651
664
  </section>
652
665
 
666
+ <!-- PR97b v3.60.0 — usage timeline (when agents linked) -->
667
+ <section
668
+ v-if="usageTotalLinks > 0"
669
+ class="rounded-xl border border-default bg-elevated/10 p-5"
670
+ >
671
+ <div class="flex items-center justify-between text-xs mb-2">
672
+ <span class="font-semibold text-muted uppercase tracking-wide">
673
+ Usage timeline (12 weeks)
674
+ </span>
675
+ <span class="font-mono text-muted">
676
+ {{ usageTotalLinks }} agent{{ usageTotalLinks === 1 ? '' : 's' }} linking · peak {{ usageMaxCount }}/wk
677
+ </span>
678
+ </div>
679
+ <svg
680
+ :viewBox="`0 0 ${usageWeeks.length * 16} 48`"
681
+ class="w-full h-12"
682
+ preserveAspectRatio="none"
683
+ >
684
+ <rect
685
+ v-for="(w, idx) in usageWeeks"
686
+ :key="w.week_start"
687
+ :x="idx * 16 + 2"
688
+ :y="48 - (w.count / usageMaxCount) * 46"
689
+ width="12"
690
+ :height="(w.count / usageMaxCount) * 46"
691
+ class="fill-primary"
692
+ :class="w.count === 0 ? 'opacity-20' : 'opacity-90'"
693
+ >
694
+ <title>{{ w.week_start }} · {{ w.count }} agent{{ w.count === 1 ? '' : 's' }} linked</title>
695
+ </rect>
696
+ </svg>
697
+ <p class="text-xs text-muted mt-1.5">
698
+ Buckets reflect the YAML mtime of agents that currently link to
699
+ this persona — approximation of when they were cloned / edited
700
+ to depend on this profile.
701
+ </p>
702
+ </section>
703
+
653
704
  <!-- BIO (PR86d) -->
654
705
  <section
655
706
  v-if="(detail as any).bio_md"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "3.59.0",
3
+ "version": "3.60.0",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "3.59.0"
3
+ version = "3.60.0"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -1371,6 +1371,74 @@ def _obsidian_store_available() -> bool:
1371
1371
  return False
1372
1372
 
1373
1373
 
1374
+ @app.get("/api/personas/{persona_id}/usage-timeline")
1375
+ def persona_usage_timeline(persona_id: str, weeks: int = 12):
1376
+ """PR97b v3.60.0 — histogram of agent YAML mtimes for agents that
1377
+ link to this persona. Approximation of "when did people clone /
1378
+ create agents from this persona over time".
1379
+
1380
+ Returns ``{weeks: [{week_start, count}], total_agents, period_weeks}``
1381
+ bucketed by ISO week start (Monday). Capped at 52 weeks.
1382
+
1383
+ Uses filesystem mtime for the agent YAML — works even without a
1384
+ git history.
1385
+ """
1386
+ try:
1387
+ weeks_int = int(weeks) if weeks is not None else 12
1388
+ except (TypeError, ValueError):
1389
+ weeks_int = 12
1390
+ capped_weeks = max(1, min(weeks_int, 52))
1391
+
1392
+ try:
1393
+ import yaml as _yaml
1394
+ except ImportError:
1395
+ return {"weeks": [], "total_agents": 0, "period_weeks": capped_weeks}
1396
+
1397
+ dept_root = ARKAOS_ROOT / "departments"
1398
+ if not dept_root.exists():
1399
+ return {"weeks": [], "total_agents": 0, "period_weeks": capped_weeks}
1400
+
1401
+ from datetime import datetime, timedelta, timezone
1402
+ now = datetime.now(timezone.utc)
1403
+ today = now.date()
1404
+ # Monday of current ISO week.
1405
+ current_monday = today - timedelta(days=today.weekday())
1406
+ buckets: dict[str, int] = {}
1407
+ for offset in range(capped_weeks):
1408
+ m = current_monday - timedelta(weeks=capped_weeks - 1 - offset)
1409
+ buckets[m.isoformat()] = 0
1410
+ cutoff_monday = current_monday - timedelta(weeks=capped_weeks - 1)
1411
+
1412
+ linked_total = 0
1413
+ for path in dept_root.glob("*/agents/*.yaml"):
1414
+ try:
1415
+ raw = _yaml.safe_load(path.read_text(encoding="utf-8")) or {}
1416
+ except Exception: # noqa: BLE001
1417
+ continue
1418
+ if not isinstance(raw, dict):
1419
+ continue
1420
+ linked = raw.get("linked_personas") or []
1421
+ if not isinstance(linked, list) or persona_id not in linked:
1422
+ continue
1423
+ linked_total += 1
1424
+ try:
1425
+ mtime = datetime.fromtimestamp(path.stat().st_mtime, tz=timezone.utc).date()
1426
+ except OSError:
1427
+ continue
1428
+ if mtime < cutoff_monday:
1429
+ continue
1430
+ monday = mtime - timedelta(days=mtime.weekday())
1431
+ key = monday.isoformat()
1432
+ if key in buckets:
1433
+ buckets[key] += 1
1434
+
1435
+ out = [
1436
+ {"week_start": k, "count": buckets[k]}
1437
+ for k in sorted(buckets.keys())
1438
+ ]
1439
+ return {"weeks": out, "total_agents": linked_total, "period_weeks": capped_weeks}
1440
+
1441
+
1374
1442
  @app.get("/api/personas/usage")
1375
1443
  def personas_usage():
1376
1444
  """PR77 v2.95.0 — reverse lookup: how many agents link to each