arkaos 3.9.0 → 3.10.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.
|
|
1
|
+
3.10.0
|
|
@@ -29,6 +29,23 @@ interface QuickAction {
|
|
|
29
29
|
description: string
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
interface TopDeptRow {
|
|
33
|
+
department: string
|
|
34
|
+
calls: number
|
|
35
|
+
cost_usd: number | null
|
|
36
|
+
tokens_in: number
|
|
37
|
+
tokens_out: number
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface RecentPersonaRow {
|
|
41
|
+
id: string
|
|
42
|
+
name: string
|
|
43
|
+
title: string
|
|
44
|
+
mbti: string
|
|
45
|
+
source_store: string
|
|
46
|
+
created_at: string | null
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
interface CommandCenterPayload {
|
|
33
50
|
greeting: {
|
|
34
51
|
name: string
|
|
@@ -45,6 +62,8 @@ interface CommandCenterPayload {
|
|
|
45
62
|
}
|
|
46
63
|
projects: ProjectRow[]
|
|
47
64
|
recent_incidents: IncidentRow[]
|
|
65
|
+
top_departments_30d: TopDeptRow[]
|
|
66
|
+
recent_personas: RecentPersonaRow[]
|
|
48
67
|
quick_actions: QuickAction[]
|
|
49
68
|
}
|
|
50
69
|
|
|
@@ -185,6 +204,82 @@ function copyCommand(cmd: string) {
|
|
|
185
204
|
</div>
|
|
186
205
|
</UCard>
|
|
187
206
|
|
|
207
|
+
<!-- PR84d v3.10.0 — Top departments + Recent personas row -->
|
|
208
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
|
209
|
+
<div>
|
|
210
|
+
<h2 class="text-sm font-semibold uppercase tracking-wider text-muted mb-3">
|
|
211
|
+
Top departments (30d)
|
|
212
|
+
</h2>
|
|
213
|
+
<DashboardState
|
|
214
|
+
:status="status"
|
|
215
|
+
:empty="!data?.top_departments_30d?.length"
|
|
216
|
+
empty-title="No telemetry yet"
|
|
217
|
+
empty-description="Department spend will appear once agents start running."
|
|
218
|
+
empty-icon="i-lucide-bar-chart"
|
|
219
|
+
>
|
|
220
|
+
<div class="space-y-2">
|
|
221
|
+
<div
|
|
222
|
+
v-for="(d, idx) in data?.top_departments_30d"
|
|
223
|
+
:key="d.department"
|
|
224
|
+
class="rounded-lg border border-default p-3 flex items-center gap-3"
|
|
225
|
+
>
|
|
226
|
+
<span class="text-xs font-mono font-semibold text-muted w-6">#{{ idx + 1 }}</span>
|
|
227
|
+
<span class="flex-1 font-semibold capitalize">{{ d.department }}</span>
|
|
228
|
+
<span class="text-xs font-mono text-muted">{{ d.calls }} calls</span>
|
|
229
|
+
<span class="text-sm font-mono font-semibold">
|
|
230
|
+
{{ d.cost_usd === null ? '—' : `$${d.cost_usd.toFixed(2)}` }}
|
|
231
|
+
</span>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</DashboardState>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<div>
|
|
238
|
+
<h2 class="text-sm font-semibold uppercase tracking-wider text-muted mb-3">
|
|
239
|
+
Recent personas
|
|
240
|
+
</h2>
|
|
241
|
+
<DashboardState
|
|
242
|
+
:status="status"
|
|
243
|
+
:empty="!data?.recent_personas?.length"
|
|
244
|
+
empty-title="No personas yet"
|
|
245
|
+
empty-description="Create one from /personas → New Persona."
|
|
246
|
+
empty-icon="i-lucide-user-plus"
|
|
247
|
+
>
|
|
248
|
+
<div class="space-y-2">
|
|
249
|
+
<NuxtLink
|
|
250
|
+
v-for="p in data?.recent_personas"
|
|
251
|
+
:key="p.id"
|
|
252
|
+
:to="`/personas/${p.id}`"
|
|
253
|
+
class="block rounded-lg border border-default p-3 hover:border-primary/40 transition-colors"
|
|
254
|
+
>
|
|
255
|
+
<div class="flex items-center justify-between gap-3">
|
|
256
|
+
<div class="min-w-0">
|
|
257
|
+
<p class="text-sm font-semibold truncate">{{ p.name }}</p>
|
|
258
|
+
<p class="text-xs text-muted truncate">{{ p.title || '—' }}</p>
|
|
259
|
+
</div>
|
|
260
|
+
<div class="flex items-center gap-2 shrink-0">
|
|
261
|
+
<UBadge
|
|
262
|
+
v-if="p.mbti"
|
|
263
|
+
:label="p.mbti"
|
|
264
|
+
variant="subtle"
|
|
265
|
+
size="xs"
|
|
266
|
+
/>
|
|
267
|
+
<UBadge
|
|
268
|
+
v-if="p.source_store === 'obsidian'"
|
|
269
|
+
icon="i-lucide-file-text"
|
|
270
|
+
label="Obsidian"
|
|
271
|
+
color="primary"
|
|
272
|
+
variant="soft"
|
|
273
|
+
size="xs"
|
|
274
|
+
/>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
</NuxtLink>
|
|
278
|
+
</div>
|
|
279
|
+
</DashboardState>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
188
283
|
<!-- Two columns: projects + incidents -->
|
|
189
284
|
<div class="grid grid-cols-1 lg:grid-cols-[2fr_1fr] gap-6">
|
|
190
285
|
<!-- Projects -->
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
Binary file
|
package/scripts/dashboard-api.py
CHANGED
|
@@ -1361,6 +1361,9 @@ def overview_command_center():
|
|
|
1361
1361
|
},
|
|
1362
1362
|
"projects": _scan_projects(parse_projects_dirs(profile.projectsDir)),
|
|
1363
1363
|
"recent_incidents": _recent_incidents(limit=8),
|
|
1364
|
+
# PR84d v3.10.0 — extra command-center cards
|
|
1365
|
+
"top_departments_30d": _top_departments_by_cost(period="month", top_n=5),
|
|
1366
|
+
"recent_personas": _recent_personas(limit=5),
|
|
1364
1367
|
"quick_actions": [
|
|
1365
1368
|
{"command": "/arka update", "description": "Sync projects + skills"},
|
|
1366
1369
|
{"command": "/arka costs", "description": "View detailed LLM cost breakdown"},
|
|
@@ -1370,6 +1373,62 @@ def overview_command_center():
|
|
|
1370
1373
|
}
|
|
1371
1374
|
|
|
1372
1375
|
|
|
1376
|
+
def _top_departments_by_cost(period: str = "month", top_n: int = 5) -> list[dict]:
|
|
1377
|
+
"""Top-N departments ranked by LLM spend over the period."""
|
|
1378
|
+
try:
|
|
1379
|
+
from core.runtime.llm_cost_telemetry import summarise, VALID_PERIODS
|
|
1380
|
+
except Exception:
|
|
1381
|
+
return []
|
|
1382
|
+
if period not in VALID_PERIODS:
|
|
1383
|
+
period = "month"
|
|
1384
|
+
summary = summarise(period=period)
|
|
1385
|
+
rows: list[dict] = []
|
|
1386
|
+
for category, row in (summary.by_category or {}).items():
|
|
1387
|
+
if not isinstance(category, str) or not category.startswith("subagent:"):
|
|
1388
|
+
continue
|
|
1389
|
+
dept = category.split(":", 1)[1] or "unknown"
|
|
1390
|
+
cost = row.get("total_cost_usd")
|
|
1391
|
+
rows.append({
|
|
1392
|
+
"department": dept,
|
|
1393
|
+
"calls": int(row.get("call_count", 0)),
|
|
1394
|
+
"cost_usd": float(cost) if isinstance(cost, (int, float)) else None,
|
|
1395
|
+
"tokens_in": int(row.get("total_tokens_in", 0)),
|
|
1396
|
+
"tokens_out": int(row.get("total_tokens_out", 0)),
|
|
1397
|
+
})
|
|
1398
|
+
rows.sort(key=lambda r: r["cost_usd"] or 0.0, reverse=True)
|
|
1399
|
+
return rows[: max(0, int(top_n))]
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
def _recent_personas(limit: int = 5) -> list[dict]:
|
|
1403
|
+
"""Return the N most recently created/modified personas.
|
|
1404
|
+
|
|
1405
|
+
Reads both the JSON store and the Obsidian vault; returns the
|
|
1406
|
+
union sorted by created_at desc (best-effort — entries without
|
|
1407
|
+
a timestamp sort last).
|
|
1408
|
+
"""
|
|
1409
|
+
mgr = _get_persona_manager()
|
|
1410
|
+
if not mgr:
|
|
1411
|
+
return []
|
|
1412
|
+
try:
|
|
1413
|
+
personas = mgr.list() or []
|
|
1414
|
+
except Exception:
|
|
1415
|
+
return []
|
|
1416
|
+
def _ts(p: dict) -> str:
|
|
1417
|
+
return str(p.get("created_at") or p.get("_created_at") or "")
|
|
1418
|
+
sorted_p = sorted(personas, key=_ts, reverse=True)
|
|
1419
|
+
out: list[dict] = []
|
|
1420
|
+
for p in sorted_p[: max(0, int(limit))]:
|
|
1421
|
+
out.append({
|
|
1422
|
+
"id": p.get("id"),
|
|
1423
|
+
"name": p.get("name"),
|
|
1424
|
+
"title": p.get("title"),
|
|
1425
|
+
"mbti": p.get("mbti"),
|
|
1426
|
+
"source_store": (p.get("_source_store") or ""),
|
|
1427
|
+
"created_at": _ts(p) or None,
|
|
1428
|
+
})
|
|
1429
|
+
return out
|
|
1430
|
+
|
|
1431
|
+
|
|
1373
1432
|
def _scan_projects(projects_dirs: list[str]) -> list[dict]:
|
|
1374
1433
|
"""Read each project descriptor and enrich with last-commit info.
|
|
1375
1434
|
|