arkaos 4.20.0 → 4.22.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.
Files changed (80) hide show
  1. package/README.md +1 -1
  2. package/VERSION +1 -1
  3. package/arka/SKILL.md +2 -1
  4. package/config/claude-agents/marta-cqo.md +9 -0
  5. package/config/standards/{claude-md-overlays → stack-rules}/laravel.md +11 -0
  6. package/config/standards/{claude-md-overlays → stack-rules}/node.md +10 -1
  7. package/config/standards/{claude-md-overlays → stack-rules}/nuxt.md +12 -1
  8. package/config/standards/stack-rules/php.md +13 -0
  9. package/config/standards/{claude-md-overlays → stack-rules}/python.md +5 -0
  10. package/config/standards/stack-rules/react.md +14 -0
  11. package/config/standards/stack-rules/vue.md +13 -0
  12. package/core/cognition/evolve.py +299 -0
  13. package/core/cognition/evolve_cli.py +55 -0
  14. package/core/governance/stop_lint.py +266 -0
  15. package/core/governance/stop_lint_telemetry.py +132 -0
  16. package/core/governance/tool_loop_check.py +111 -0
  17. package/core/hooks/stop.py +73 -22
  18. package/core/runtime/pricing.py +53 -19
  19. package/core/synapse/layers.py +49 -476
  20. package/core/synapse/layers_base.py +99 -0
  21. package/core/synapse/layers_kb.py +395 -0
  22. package/core/sync/content_syncer.py +79 -10
  23. package/dashboard/app/components/AgentDependencyGraph.vue +5 -5
  24. package/dashboard/app/components/AgentEditDrawer.vue +120 -52
  25. package/dashboard/app/components/AgentSuggestionsCard.vue +4 -2
  26. package/dashboard/app/components/ConfirmDialog.vue +2 -2
  27. package/dashboard/app/components/DashboardState.vue +5 -3
  28. package/dashboard/app/components/GlobalSearch.vue +11 -7
  29. package/dashboard/app/components/KeyboardShortcutsHelp.vue +8 -6
  30. package/dashboard/app/components/KnowledgeSourcesList.vue +13 -9
  31. package/dashboard/app/components/MarkdownEditor.vue +1 -1
  32. package/dashboard/app/components/NotificationsBell.vue +8 -4
  33. package/dashboard/app/components/OnboardingTour.vue +24 -11
  34. package/dashboard/app/components/PersonaCloneDialog.vue +15 -8
  35. package/dashboard/app/components/PersonaWizard.vue +105 -72
  36. package/dashboard/app/components/SidebarFavoritesWidget.vue +5 -5
  37. package/dashboard/app/components/SidebarStatsWidget.vue +3 -1
  38. package/dashboard/app/components/Terminal.vue +9 -8
  39. package/dashboard/app/components/TextDiff.vue +27 -15
  40. package/dashboard/app/composables/useActivityFeed.ts +3 -3
  41. package/dashboard/app/composables/useApi.ts +4 -2
  42. package/dashboard/app/composables/useConfirmDialog.ts +1 -1
  43. package/dashboard/app/composables/useFavorites.ts +8 -8
  44. package/dashboard/app/composables/useTerminalThemes.ts +13 -13
  45. package/dashboard/app/composables/useThemeColor.ts +7 -4
  46. package/dashboard/app/pages/agents/[id].vue +319 -159
  47. package/dashboard/app/pages/agents/compare.vue +152 -52
  48. package/dashboard/app/pages/agents/index.vue +68 -55
  49. package/dashboard/app/pages/agents/new.vue +125 -58
  50. package/dashboard/app/pages/audit.vue +13 -6
  51. package/dashboard/app/pages/budget.vue +20 -18
  52. package/dashboard/app/pages/commands.vue +26 -22
  53. package/dashboard/app/pages/departments/[dept].vue +67 -31
  54. package/dashboard/app/pages/departments/compare.vue +85 -27
  55. package/dashboard/app/pages/departments/index.vue +31 -7
  56. package/dashboard/app/pages/health.vue +19 -12
  57. package/dashboard/app/pages/knowledge/index.vue +190 -78
  58. package/dashboard/app/pages/models.vue +74 -28
  59. package/dashboard/app/pages/personas/[id].vue +322 -219
  60. package/dashboard/app/pages/personas/archetypes.vue +20 -6
  61. package/dashboard/app/pages/personas/compare-with-agent.vue +109 -39
  62. package/dashboard/app/pages/personas/compare.vue +196 -124
  63. package/dashboard/app/pages/personas/index.vue +67 -60
  64. package/dashboard/app/pages/personas/new.vue +1 -1
  65. package/dashboard/app/pages/settings.vue +1 -0
  66. package/dashboard/app/pages/tasks.vue +80 -38
  67. package/dashboard/app/pages/trash.vue +11 -9
  68. package/dashboard/app/pages/workflows.vue +29 -19
  69. package/dashboard/nuxt.config.ts +2 -1
  70. package/dashboard/package.json +5 -0
  71. package/departments/kb/skills/doc-extraction/SKILL.md +99 -0
  72. package/departments/kb/skills/doc-redaction/SKILL.md +103 -0
  73. package/departments/kb/skills/knowledge-ops/SKILL.md +88 -0
  74. package/departments/ops/skills/github-ops/SKILL.md +89 -0
  75. package/departments/ops/skills/terminal-ops/SKILL.md +83 -0
  76. package/installer/index.js +1 -1
  77. package/knowledge/commands-registry.json +16 -3
  78. package/knowledge/skills-manifest.json +66 -1
  79. package/package.json +1 -1
  80. package/pyproject.toml +1 -1
@@ -6,12 +6,72 @@
6
6
  // - Activity stats row pulled from PR69 /api/agents/activity
7
7
  // - Edit toggle wired to AgentEditDrawer (PUT /api/agents/{id})
8
8
 
9
+ // PR86d v3.18.0 — render Markdown bio.
10
+ import { marked } from 'marked'
11
+
12
+ // Shape of the GET /api/agents/{id} payload as consumed by this page.
13
+ // All keys optional except id — the backend only includes populated ones.
14
+ interface AgentDetail {
15
+ id: string
16
+ name?: string
17
+ role?: string
18
+ department?: string
19
+ tier?: number
20
+ mbti?: string
21
+ disc?: {
22
+ primary?: string
23
+ secondary?: string
24
+ label?: string
25
+ communication_style?: string
26
+ under_pressure?: string
27
+ motivator?: string
28
+ }
29
+ enneagram?: {
30
+ type?: string | number
31
+ wing?: string | number
32
+ label?: string
33
+ core_fear?: string
34
+ core_motivation?: string
35
+ }
36
+ big_five?: {
37
+ O?: number
38
+ C?: number
39
+ E?: number
40
+ A?: number
41
+ N?: number
42
+ }
43
+ communication?: {
44
+ tone?: string
45
+ vocabulary_level?: string
46
+ language?: string
47
+ preferred_format?: string
48
+ avoid?: string[]
49
+ }
50
+ mental_models?: { primary?: string[], secondary?: string[] }
51
+ authority?: {
52
+ veto?: boolean
53
+ approve_budget?: boolean
54
+ approve_architecture?: boolean
55
+ approve_quality?: boolean
56
+ orchestrate?: boolean
57
+ block_release?: boolean
58
+ delegates_to?: string[]
59
+ escalates_to?: string | string[]
60
+ }
61
+ expertise_domains?: string[]
62
+ expertise_depth?: string
63
+ expertise_years?: number
64
+ frameworks?: string[]
65
+ linked_personas?: string[]
66
+ bio_md?: string
67
+ }
68
+
9
69
  const route = useRoute()
10
70
  const agentId = route.params.id as string
11
71
 
12
72
  const { fetchApi, apiBase } = useApi()
13
73
  const toast = useToast()
14
- const { data: agent, status, error, refresh } = fetchApi<any>(`/api/agents/${agentId}`)
74
+ const { data: agent, status, error, refresh } = fetchApi<AgentDetail>(`/api/agents/${agentId}`)
15
75
 
16
76
  // Per-department activity (PR69 endpoint) for the stats row.
17
77
  interface ActivityRow {
@@ -25,7 +85,7 @@ const { data: activityData } = fetchApi<{
25
85
  period: string
26
86
  }>('/api/agents/activity?period=week')
27
87
  const deptActivity = computed<ActivityRow | null>(() =>
28
- (activityData.value?.by_department?.[agent.value?.department ?? ''] ?? null),
88
+ (activityData.value?.by_department?.[agent.value?.department ?? ''] ?? null)
29
89
  )
30
90
 
31
91
  // PR96d v3.58.0 — 30d activity sparkline (calls per day).
@@ -45,7 +105,7 @@ const sparklineMaxCalls = computed(() => {
45
105
  return Math.max(max, 1)
46
106
  })
47
107
  const sparklineTotalCalls = computed(() =>
48
- sparkline.value.reduce((acc, d) => acc + d.calls, 0),
108
+ sparkline.value.reduce((acc, d) => acc + d.calls, 0)
49
109
  )
50
110
 
51
111
  // PR88d v3.26.0 — agent history (git log + trash entries)
@@ -57,7 +117,7 @@ interface HistoryEvent {
57
117
  author?: string
58
118
  }
59
119
  const { data: historyData } = fetchApi<{ events: HistoryEvent[] }>(
60
- `/api/agents/${agentId}/history?limit=20`,
120
+ `/api/agents/${agentId}/history?limit=20`
61
121
  )
62
122
 
63
123
  const historyEvents = computed<HistoryEvent[]>(() => historyData.value?.events ?? [])
@@ -66,14 +126,14 @@ function historyKindIcon(kind: string): string {
66
126
  return ({
67
127
  'git-commit': 'i-lucide-git-commit',
68
128
  'agent-delete': 'i-lucide-trash-2',
69
- 'agent-move': 'i-lucide-folder-tree',
129
+ 'agent-move': 'i-lucide-folder-tree'
70
130
  } as Record<string, string>)[kind] ?? 'i-lucide-circle'
71
131
  }
72
132
  function historyKindColor(kind: string): string {
73
133
  return ({
74
134
  'git-commit': 'text-blue-500',
75
135
  'agent-delete': 'text-red-500',
76
- 'agent-move': 'text-amber-500',
136
+ 'agent-move': 'text-amber-500'
77
137
  } as Record<string, string>)[kind] ?? 'text-muted'
78
138
  }
79
139
 
@@ -91,7 +151,7 @@ interface ActivityStrip {
91
151
  dept_count: number
92
152
  }
93
153
  const { data: activityStrip } = fetchApi<ActivityStrip>(
94
- `/api/agents/${agentId}/activity-strip?period=month`,
154
+ `/api/agents/${agentId}/activity-strip?period=month`
95
155
  )
96
156
 
97
157
  function formatRelative(iso: string | null): string {
@@ -113,9 +173,6 @@ function formatRelative(iso: string | null): string {
113
173
  // PR86a v3.15.0 — favorites.
114
174
  const favs = useFavorites()
115
175
  await favs.load()
116
-
117
- // PR86d v3.18.0 — render Markdown bio.
118
- import { marked } from 'marked'
119
176
  function markedHtml(src: string): string {
120
177
  if (!src?.trim()) return ''
121
178
  try {
@@ -136,7 +193,7 @@ async function openYamlEditor() {
136
193
  try {
137
194
  const blob = await $fetch<Blob>(
138
195
  `${apiBase}/api/agents/${agentId}/yaml`,
139
- { responseType: 'blob' },
196
+ { responseType: 'blob' }
140
197
  )
141
198
  yamlEditorDraft.value = await blob.text()
142
199
  yamlEditorOpen.value = true
@@ -144,7 +201,7 @@ async function openYamlEditor() {
144
201
  toast.add({
145
202
  title: 'Failed to load YAML',
146
203
  description: err instanceof Error ? err.message : 'unknown error',
147
- color: 'error',
204
+ color: 'error'
148
205
  })
149
206
  }
150
207
  }
@@ -155,14 +212,14 @@ async function saveYamlEditor() {
155
212
  try {
156
213
  const res = await $fetch<{ updated?: boolean, error?: string }>(
157
214
  `${apiBase}/api/agents/${agentId}/yaml`,
158
- { method: 'PUT', body: { content: yamlEditorDraft.value } },
215
+ { method: 'PUT', body: { content: yamlEditorDraft.value } }
159
216
  )
160
217
  if (res.error) throw new Error(res.error)
161
218
  toast.add({
162
219
  title: 'YAML updated',
163
220
  description: agentId,
164
221
  color: 'success',
165
- icon: 'i-lucide-check',
222
+ icon: 'i-lucide-check'
166
223
  })
167
224
  yamlEditorOpen.value = false
168
225
  await refresh()
@@ -170,61 +227,13 @@ async function saveYamlEditor() {
170
227
  toast.add({
171
228
  title: 'Save failed',
172
229
  description: err instanceof Error ? err.message : 'unknown error',
173
- color: 'error',
230
+ color: 'error'
174
231
  })
175
232
  } finally {
176
233
  yamlEditorSaving.value = false
177
234
  }
178
235
  }
179
236
 
180
- // PR97d v3.62.0 — inline edit for name / role.
181
- type InlineField = 'name' | 'role'
182
- const inlineField = ref<InlineField | null>(null)
183
- const inlineDraft = ref('')
184
- const inlineSaving = ref(false)
185
-
186
- function startInline(field: InlineField, current: string | undefined) {
187
- inlineField.value = field
188
- inlineDraft.value = current ?? ''
189
- }
190
- function cancelInline() {
191
- inlineField.value = null
192
- inlineDraft.value = ''
193
- }
194
- async function commitInline(field: InlineField) {
195
- if (!agent.value || inlineField.value !== field) return
196
- const next = inlineDraft.value.trim()
197
- const current = (field === 'name' ? agent.value.name : agent.value.role) ?? ''
198
- if (!next || next === current) {
199
- cancelInline()
200
- return
201
- }
202
- inlineSaving.value = true
203
- try {
204
- const res = await $fetch<{ updated?: boolean, error?: string }>(
205
- `${apiBase}/api/agents/${agentId}`,
206
- { method: 'PUT', body: { [field]: next } },
207
- )
208
- if (res.error) throw new Error(res.error)
209
- toast.add({
210
- title: field === 'name' ? 'Name updated' : 'Role updated',
211
- description: next,
212
- color: 'success',
213
- icon: 'i-lucide-check',
214
- })
215
- await refresh()
216
- } catch (err) {
217
- toast.add({
218
- title: 'Save failed',
219
- description: err instanceof Error ? err.message : 'unknown error',
220
- color: 'error',
221
- })
222
- } finally {
223
- inlineSaving.value = false
224
- inlineField.value = null
225
- }
226
- }
227
-
228
237
  // PR89d v3.30.0 — download YAML.
229
238
  const downloadingYaml = ref(false)
230
239
  async function downloadYaml() {
@@ -233,7 +242,7 @@ async function downloadYaml() {
233
242
  try {
234
243
  const blob = await $fetch<Blob>(
235
244
  `${apiBase}/api/agents/${agentId}/yaml`,
236
- { responseType: 'blob' },
245
+ { responseType: 'blob' }
237
246
  )
238
247
  const url = URL.createObjectURL(blob)
239
248
  const a = document.createElement('a')
@@ -247,13 +256,13 @@ async function downloadYaml() {
247
256
  title: 'YAML downloaded',
248
257
  description: `${agentId}.yaml`,
249
258
  color: 'success',
250
- icon: 'i-lucide-download',
259
+ icon: 'i-lucide-download'
251
260
  })
252
261
  } catch (err) {
253
262
  toast.add({
254
263
  title: 'Download failed',
255
264
  description: err instanceof Error ? err.message : 'unknown error',
256
- color: 'error',
265
+ color: 'error'
257
266
  })
258
267
  } finally {
259
268
  downloadingYaml.value = false
@@ -268,20 +277,20 @@ async function exportToVault() {
268
277
  try {
269
278
  const res = await $fetch<{ exported?: boolean, path?: string, error?: string }>(
270
279
  `${apiBase}/api/agents/${agentId}/export`,
271
- { method: 'POST' },
280
+ { method: 'POST' }
272
281
  )
273
282
  if (res.error) throw new Error(res.error)
274
283
  toast.add({
275
284
  title: 'Exported to Obsidian',
276
285
  description: res.path ? res.path.split('/').slice(-3).join('/') : undefined,
277
286
  color: 'success',
278
- icon: 'i-lucide-file-text',
287
+ icon: 'i-lucide-file-text'
279
288
  })
280
289
  } catch (err) {
281
290
  toast.add({
282
291
  title: 'Export failed',
283
292
  description: err instanceof Error ? err.message : 'unknown error',
284
- color: 'error',
293
+ color: 'error'
285
294
  })
286
295
  } finally {
287
296
  exporting.value = false
@@ -305,21 +314,21 @@ const tierLabel: Record<number, string> = {
305
314
  0: 'C-Suite',
306
315
  1: 'Squad Lead',
307
316
  2: 'Specialist',
308
- 3: 'Support',
317
+ 3: 'Support'
309
318
  }
310
319
 
311
320
  const tierColor: Record<number, string> = {
312
321
  0: 'error',
313
322
  1: 'warning',
314
323
  2: 'primary',
315
- 3: 'neutral',
324
+ 3: 'neutral'
316
325
  }
317
326
 
318
327
  const depthColor: Record<string, string> = {
319
328
  master: 'error',
320
329
  expert: 'warning',
321
330
  advanced: 'primary',
322
- intermediate: 'neutral',
331
+ intermediate: 'neutral'
323
332
  }
324
333
 
325
334
  const bigFiveLabels: Record<string, string> = {
@@ -327,7 +336,7 @@ const bigFiveLabels: Record<string, string> = {
327
336
  C: 'Conscientiousness',
328
337
  E: 'Extraversion',
329
338
  A: 'Agreeableness',
330
- N: 'Neuroticism',
339
+ N: 'Neuroticism'
331
340
  }
332
341
 
333
342
  const bigFiveKeys = ['O', 'C', 'E', 'A', 'N'] as const
@@ -348,7 +357,7 @@ const mbtiDescriptions: Record<string, string> = {
348
357
  ISTP: 'Ti-Se-Ni-Fe — The Virtuoso',
349
358
  ISFP: 'Fi-Se-Ni-Te — The Adventurer',
350
359
  ESTP: 'Se-Ti-Fe-Ni — The Entrepreneur',
351
- ESFP: 'Se-Fi-Te-Ni — The Entertainer',
360
+ ESFP: 'Se-Fi-Te-Ni — The Entertainer'
352
361
  }
353
362
 
354
363
  // --- DISC bar values ---
@@ -367,7 +376,7 @@ function discBarColor(letter: string): string {
367
376
  D: 'bg-red-500',
368
377
  I: 'bg-yellow-500',
369
378
  S: 'bg-green-500',
370
- C: 'bg-blue-500',
379
+ C: 'bg-blue-500'
371
380
  }
372
381
  return colors[letter] ?? 'bg-primary'
373
382
  }
@@ -386,7 +395,7 @@ const tabs = [
386
395
  { label: 'Communication', value: 'communication', icon: 'i-lucide-message-square' },
387
396
  { label: 'Mental Models', value: 'models', icon: 'i-lucide-brain' },
388
397
  { label: 'Authority', value: 'authority', icon: 'i-lucide-shield' },
389
- { label: 'Expertise', value: 'expertise', icon: 'i-lucide-award' },
398
+ { label: 'Expertise', value: 'expertise', icon: 'i-lucide-award' }
390
399
  ]
391
400
 
392
401
  // PR76 — hero helpers
@@ -402,22 +411,22 @@ const initials = computed<string>(() => {
402
411
  // Per-department gradient hex pair (from + to). Picked once per dept
403
412
  // so the same dept always renders the same hero tint.
404
413
  const DEPT_GRADIENTS: Record<string, [string, string]> = {
405
- brand: ['from-fuchsia-500/30', 'to-purple-600/10'],
406
- marketing: ['from-pink-500/30', 'to-rose-600/10'],
407
- dev: ['from-blue-500/30', 'to-cyan-600/10'],
408
- ecom: ['from-amber-500/30', 'to-orange-600/10'],
409
- finance: ['from-emerald-500/30', 'to-green-600/10'],
410
- strategy: ['from-indigo-500/30', 'to-violet-600/10'],
411
- kb: ['from-teal-500/30', 'to-cyan-600/10'],
412
- ops: ['from-slate-500/30', 'to-gray-600/10'],
413
- pm: ['from-sky-500/30', 'to-blue-600/10'],
414
- saas: ['from-violet-500/30', 'to-indigo-600/10'],
415
- landing: ['from-orange-500/30', 'to-red-600/10'],
416
- content: ['from-rose-500/30', 'to-pink-600/10'],
417
- community: ['from-yellow-500/30', 'to-amber-600/10'],
418
- sales: ['from-red-500/30', 'to-orange-600/10'],
419
- leadership: ['from-purple-500/30', 'to-pink-600/10'],
420
- org: ['from-gray-500/30', 'to-slate-600/10'],
414
+ brand: ['from-fuchsia-500/30', 'to-purple-600/10'],
415
+ marketing: ['from-pink-500/30', 'to-rose-600/10'],
416
+ dev: ['from-blue-500/30', 'to-cyan-600/10'],
417
+ ecom: ['from-amber-500/30', 'to-orange-600/10'],
418
+ finance: ['from-emerald-500/30', 'to-green-600/10'],
419
+ strategy: ['from-indigo-500/30', 'to-violet-600/10'],
420
+ kb: ['from-teal-500/30', 'to-cyan-600/10'],
421
+ ops: ['from-slate-500/30', 'to-gray-600/10'],
422
+ pm: ['from-sky-500/30', 'to-blue-600/10'],
423
+ saas: ['from-violet-500/30', 'to-indigo-600/10'],
424
+ landing: ['from-orange-500/30', 'to-red-600/10'],
425
+ content: ['from-rose-500/30', 'to-pink-600/10'],
426
+ community: ['from-yellow-500/30', 'to-amber-600/10'],
427
+ sales: ['from-red-500/30', 'to-orange-600/10'],
428
+ leadership: ['from-purple-500/30', 'to-pink-600/10'],
429
+ org: ['from-gray-500/30', 'to-slate-600/10']
421
430
  }
422
431
 
423
432
  const heroGradientClasses = computed(() => {
@@ -467,15 +476,29 @@ function formatTokens(n: number): string {
467
476
  <!-- Error -->
468
477
  <div v-else-if="error" class="flex flex-col items-center justify-center gap-4 py-24" role="alert">
469
478
  <UIcon name="i-lucide-alert-triangle" class="size-12 text-red-500" />
470
- <p class="text-sm text-muted">Failed to load agent data.</p>
471
- <UButton label="Back to Agents" variant="outline" icon="i-lucide-arrow-left" to="/agents" />
479
+ <p class="text-sm text-muted">
480
+ Failed to load agent data.
481
+ </p>
482
+ <UButton
483
+ label="Back to Agents"
484
+ variant="outline"
485
+ icon="i-lucide-arrow-left"
486
+ to="/agents"
487
+ />
472
488
  </div>
473
489
 
474
490
  <!-- Not found -->
475
491
  <div v-else-if="!agent" class="flex flex-col items-center justify-center gap-4 py-24">
476
492
  <UIcon name="i-lucide-user-x" class="size-12 text-muted" />
477
- <p class="text-sm text-muted">Agent not found.</p>
478
- <UButton label="Back to Agents" variant="outline" icon="i-lucide-arrow-left" to="/agents" />
493
+ <p class="text-sm text-muted">
494
+ Agent not found.
495
+ </p>
496
+ <UButton
497
+ label="Back to Agents"
498
+ variant="outline"
499
+ icon="i-lucide-arrow-left"
500
+ to="/agents"
501
+ />
479
502
  </div>
480
503
 
481
504
  <!-- Content -->
@@ -495,7 +518,9 @@ function formatTokens(n: number): string {
495
518
  <h1 class="text-3xl md:text-4xl font-bold tracking-tight text-highlighted">
496
519
  {{ agent.name }}
497
520
  </h1>
498
- <p class="text-base md:text-lg text-muted mt-0.5">{{ agent.role }}</p>
521
+ <p class="text-base md:text-lg text-muted mt-0.5">
522
+ {{ agent.role }}
523
+ </p>
499
524
  </div>
500
525
  <div class="flex items-center gap-2">
501
526
  <UButton
@@ -540,9 +565,9 @@ function formatTokens(n: number): string {
540
565
  <div class="flex flex-wrap items-center gap-2 pt-1">
541
566
  <UBadge :label="agent.department" variant="subtle" />
542
567
  <UBadge
543
- :label="`Tier ${agent.tier} — ${tierLabel[agent.tier] ?? ''}`"
568
+ :label="`Tier ${agent.tier} — ${tierLabel[agent.tier!] ?? ''}`"
544
569
  variant="subtle"
545
- :color="(tierColor[agent.tier] ?? 'neutral') as any"
570
+ :color="(tierColor[agent.tier!] ?? 'neutral') as any"
546
571
  />
547
572
  <UBadge
548
573
  v-if="agent.expertise_depth"
@@ -555,9 +580,16 @@ function formatTokens(n: number): string {
555
580
  :label="`${agent.expertise_years}y experience`"
556
581
  variant="outline"
557
582
  />
558
- <UBadge v-if="agent.mbti" :label="agent.mbti" variant="soft" size="sm" />
583
+ <UBadge
584
+ v-if="agent.mbti"
585
+ :label="agent.mbti"
586
+ variant="soft"
587
+ size="sm"
588
+ />
559
589
  </div>
560
- <p class="text-xs text-muted/60 font-mono select-all pt-2">{{ agent.id }}</p>
590
+ <p class="text-xs text-muted/60 font-mono select-all pt-2">
591
+ {{ agent.id }}
592
+ </p>
561
593
  </div>
562
594
  </div>
563
595
  </section>
@@ -565,23 +597,37 @@ function formatTokens(n: number): string {
565
597
  <!-- ===== STATS ROW ===== -->
566
598
  <section class="grid grid-cols-2 md:grid-cols-4 gap-3">
567
599
  <div class="rounded-xl border border-default p-4 bg-elevated/20">
568
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">7d calls (dept)</p>
569
- <p class="text-2xl font-bold">{{ deptActivity?.call_count ?? 0 }}</p>
600
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
601
+ 7d calls (dept)
602
+ </p>
603
+ <p class="text-2xl font-bold">
604
+ {{ deptActivity?.call_count ?? 0 }}
605
+ </p>
570
606
  </div>
571
607
  <div class="rounded-xl border border-default p-4 bg-elevated/20">
572
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">7d cost</p>
573
- <p class="text-2xl font-bold">{{ formatCost(deptActivity?.total_cost_usd) }}</p>
608
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
609
+ 7d cost
610
+ </p>
611
+ <p class="text-2xl font-bold">
612
+ {{ formatCost(deptActivity?.total_cost_usd) }}
613
+ </p>
574
614
  </div>
575
615
  <div class="rounded-xl border border-default p-4 bg-elevated/20">
576
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Tokens (in/out)</p>
616
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
617
+ Tokens (in/out)
618
+ </p>
577
619
  <p class="text-lg font-semibold">
578
620
  {{ formatTokens(deptActivity?.total_tokens_in ?? 0) }} /
579
621
  {{ formatTokens(deptActivity?.total_tokens_out ?? 0) }}
580
622
  </p>
581
623
  </div>
582
624
  <div class="rounded-xl border border-default p-4 bg-elevated/20">
583
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Linked personas</p>
584
- <p class="text-2xl font-bold">{{ agent.linked_personas?.length ?? 0 }}</p>
625
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
626
+ Linked personas
627
+ </p>
628
+ <p class="text-2xl font-bold">
629
+ {{ agent.linked_personas?.length ?? 0 }}
630
+ </p>
585
631
  </div>
586
632
  </section>
587
633
 
@@ -662,13 +708,13 @@ function formatTokens(n: number): string {
662
708
  <AgentDependencyGraph
663
709
  v-if="agent.linked_personas && agent.linked_personas.length > 0"
664
710
  :agent-id="agent.id"
665
- :agent-name="agent.name"
711
+ :agent-name="agent.name ?? ''"
666
712
  :linked-personas="agent.linked_personas"
667
713
  />
668
714
 
669
715
  <!-- ===== BIO (PR86d) ===== -->
670
716
  <section
671
- v-if="(agent as any).bio_md"
717
+ v-if="agent.bio_md"
672
718
  class="rounded-xl border border-default bg-elevated/10 p-5"
673
719
  >
674
720
  <h3 class="text-sm font-semibold uppercase tracking-wide text-muted mb-3">
@@ -676,7 +722,7 @@ function formatTokens(n: number): string {
676
722
  </h3>
677
723
  <div
678
724
  class="prose prose-sm dark:prose-invert max-w-none"
679
- v-html="markedHtml((agent as any).bio_md)"
725
+ v-html="markedHtml(agent.bio_md)"
680
726
  />
681
727
  </section>
682
728
 
@@ -711,7 +757,9 @@ function formatTokens(n: number): string {
711
757
  <code v-if="ev.ref" class="font-mono text-muted">{{ ev.ref }}</code>
712
758
  <span v-if="ev.author" class="text-muted">· {{ ev.author }}</span>
713
759
  </div>
714
- <p class="text-sm mt-1">{{ ev.summary }}</p>
760
+ <p class="text-sm mt-1">
761
+ {{ ev.summary }}
762
+ </p>
715
763
  </div>
716
764
  </li>
717
765
  </ol>
@@ -724,8 +772,12 @@ function formatTokens(n: number): string {
724
772
  <template #header>
725
773
  <div class="flex items-center justify-between gap-3">
726
774
  <div>
727
- <h2 class="text-lg font-bold">Edit agent YAML</h2>
728
- <p class="text-xs text-muted mt-0.5 font-mono">{{ agent?.id }}</p>
775
+ <h2 class="text-lg font-bold">
776
+ Edit agent YAML
777
+ </h2>
778
+ <p class="text-xs text-muted mt-0.5 font-mono">
779
+ {{ agent?.id }}
780
+ </p>
729
781
  </div>
730
782
  <UButton
731
783
  icon="i-lucide-x"
@@ -784,7 +836,9 @@ function formatTokens(n: number): string {
784
836
  <!-- Card 1: MBTI -->
785
837
  <UCard>
786
838
  <div class="space-y-2">
787
- <p class="text-sm font-semibold text-muted uppercase tracking-wide">MBTI</p>
839
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide">
840
+ MBTI
841
+ </p>
788
842
  <p class="text-4xl font-bold font-mono tracking-widest">
789
843
  {{ agent.mbti || '----' }}
790
844
  </p>
@@ -797,7 +851,9 @@ function formatTokens(n: number): string {
797
851
  <!-- Card 2: Enneagram -->
798
852
  <UCard>
799
853
  <div class="space-y-3">
800
- <p class="text-sm font-semibold text-muted uppercase tracking-wide">Enneagram</p>
854
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide">
855
+ Enneagram
856
+ </p>
801
857
  <div>
802
858
  <p class="text-3xl font-bold">
803
859
  Type {{ agent.enneagram?.type ?? '-' }}
@@ -812,13 +868,17 @@ function formatTokens(n: number): string {
812
868
 
813
869
  <div class="grid grid-cols-2 gap-2 pt-1">
814
870
  <div class="rounded-lg bg-red-500/10 border border-red-500/20 p-2.5">
815
- <p class="text-[10px] font-bold text-red-400 uppercase tracking-wider mb-1">Fear</p>
871
+ <p class="text-[10px] font-bold text-red-400 uppercase tracking-wider mb-1">
872
+ Fear
873
+ </p>
816
874
  <p class="text-xs text-muted leading-snug">
817
875
  {{ agent.enneagram?.core_fear ?? 'Unknown' }}
818
876
  </p>
819
877
  </div>
820
878
  <div class="rounded-lg bg-emerald-500/10 border border-emerald-500/20 p-2.5">
821
- <p class="text-[10px] font-bold text-emerald-400 uppercase tracking-wider mb-1">Drive</p>
879
+ <p class="text-[10px] font-bold text-emerald-400 uppercase tracking-wider mb-1">
880
+ Drive
881
+ </p>
822
882
  <p class="text-xs text-muted leading-snug">
823
883
  {{ agent.enneagram?.core_motivation ?? 'Unknown' }}
824
884
  </p>
@@ -830,7 +890,9 @@ function formatTokens(n: number): string {
830
890
  <!-- Card 3: DISC -->
831
891
  <UCard>
832
892
  <div class="space-y-3">
833
- <p class="text-sm font-semibold text-muted uppercase tracking-wide">DISC</p>
893
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide">
894
+ DISC
895
+ </p>
834
896
  <div>
835
897
  <p class="text-3xl font-bold font-mono">
836
898
  {{ agent.disc?.primary ?? '' }}{{ agent.disc?.secondary ?? '' }}
@@ -884,7 +946,9 @@ function formatTokens(n: number): string {
884
946
  </span>
885
947
  </div>
886
948
  </div>
887
- <p v-else class="text-sm text-muted">No Big Five data available.</p>
949
+ <p v-else class="text-sm text-muted">
950
+ No Big Five data available.
951
+ </p>
888
952
  </div>
889
953
  </UCard>
890
954
  </div>
@@ -895,16 +959,26 @@ function formatTokens(n: number): string {
895
959
  <UCard>
896
960
  <div class="space-y-4">
897
961
  <div>
898
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Tone</p>
899
- <p class="text-sm">{{ agent.communication.tone ?? '-' }}</p>
962
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
963
+ Tone
964
+ </p>
965
+ <p class="text-sm">
966
+ {{ agent.communication.tone ?? '-' }}
967
+ </p>
900
968
  </div>
901
969
  <div>
902
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Vocabulary Level</p>
970
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
971
+ Vocabulary Level
972
+ </p>
903
973
  <UBadge :label="agent.communication.vocabulary_level ?? '-'" variant="subtle" />
904
974
  </div>
905
975
  <div>
906
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Language</p>
907
- <p class="text-sm font-mono">{{ agent.communication.language ?? '-' }}</p>
976
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
977
+ Language
978
+ </p>
979
+ <p class="text-sm font-mono">
980
+ {{ agent.communication.language ?? '-' }}
981
+ </p>
908
982
  </div>
909
983
  </div>
910
984
  </UCard>
@@ -912,11 +986,17 @@ function formatTokens(n: number): string {
912
986
  <UCard>
913
987
  <div class="space-y-4">
914
988
  <div>
915
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Preferred Format</p>
916
- <p class="text-sm">{{ agent.communication.preferred_format ?? '-' }}</p>
989
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
990
+ Preferred Format
991
+ </p>
992
+ <p class="text-sm">
993
+ {{ agent.communication.preferred_format ?? '-' }}
994
+ </p>
917
995
  </div>
918
996
  <div v-if="agent.communication.avoid?.length">
919
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-2">Avoids</p>
997
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-2">
998
+ Avoids
999
+ </p>
920
1000
  <ul class="space-y-1.5">
921
1001
  <li
922
1002
  v-for="item in agent.communication.avoid"
@@ -935,26 +1015,42 @@ function formatTokens(n: number): string {
935
1015
  <!-- DISC Communication Details -->
936
1016
  <UCard v-if="agent.disc?.communication_style || agent.disc?.under_pressure || agent.disc?.motivator">
937
1017
  <div class="space-y-4">
938
- <p class="text-sm font-semibold text-muted uppercase tracking-wide">DISC Communication Profile</p>
1018
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide">
1019
+ DISC Communication Profile
1020
+ </p>
939
1021
  <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
940
1022
  <div v-if="agent.disc.communication_style">
941
- <p class="text-xs text-muted mb-1">Communication Style</p>
942
- <p class="text-sm">{{ agent.disc.communication_style }}</p>
1023
+ <p class="text-xs text-muted mb-1">
1024
+ Communication Style
1025
+ </p>
1026
+ <p class="text-sm">
1027
+ {{ agent.disc.communication_style }}
1028
+ </p>
943
1029
  </div>
944
1030
  <div v-if="agent.disc.under_pressure">
945
- <p class="text-xs text-muted mb-1">Under Pressure</p>
946
- <p class="text-sm">{{ agent.disc.under_pressure }}</p>
1031
+ <p class="text-xs text-muted mb-1">
1032
+ Under Pressure
1033
+ </p>
1034
+ <p class="text-sm">
1035
+ {{ agent.disc.under_pressure }}
1036
+ </p>
947
1037
  </div>
948
1038
  <div v-if="agent.disc.motivator">
949
- <p class="text-xs text-muted mb-1">Motivator</p>
950
- <p class="text-sm">{{ agent.disc.motivator }}</p>
1039
+ <p class="text-xs text-muted mb-1">
1040
+ Motivator
1041
+ </p>
1042
+ <p class="text-sm">
1043
+ {{ agent.disc.motivator }}
1044
+ </p>
951
1045
  </div>
952
1046
  </div>
953
1047
  </div>
954
1048
  </UCard>
955
1049
 
956
1050
  <div v-if="!agent.communication && !agent.disc?.communication_style" class="py-8 text-center">
957
- <p class="text-sm text-muted">No communication data available.</p>
1051
+ <p class="text-sm text-muted">
1052
+ No communication data available.
1053
+ </p>
958
1054
  </div>
959
1055
  </div>
960
1056
 
@@ -963,7 +1059,9 @@ function formatTokens(n: number): string {
963
1059
  <div v-if="agent.mental_models" class="grid grid-cols-1 md:grid-cols-2 gap-4">
964
1060
  <UCard>
965
1061
  <div class="space-y-3">
966
- <p class="text-sm font-semibold text-muted uppercase tracking-wide">Primary Models</p>
1062
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide">
1063
+ Primary Models
1064
+ </p>
967
1065
  <ul v-if="agent.mental_models.primary?.length" class="space-y-2">
968
1066
  <li
969
1067
  v-for="model in agent.mental_models.primary"
@@ -974,13 +1072,17 @@ function formatTokens(n: number): string {
974
1072
  <span class="text-sm font-medium">{{ model }}</span>
975
1073
  </li>
976
1074
  </ul>
977
- <p v-else class="text-sm text-muted">None listed.</p>
1075
+ <p v-else class="text-sm text-muted">
1076
+ None listed.
1077
+ </p>
978
1078
  </div>
979
1079
  </UCard>
980
1080
 
981
1081
  <UCard>
982
1082
  <div class="space-y-3">
983
- <p class="text-sm font-semibold text-muted uppercase tracking-wide">Secondary Models</p>
1083
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide">
1084
+ Secondary Models
1085
+ </p>
984
1086
  <ul v-if="agent.mental_models.secondary?.length" class="space-y-2">
985
1087
  <li
986
1088
  v-for="model in agent.mental_models.secondary"
@@ -991,13 +1093,17 @@ function formatTokens(n: number): string {
991
1093
  <span class="text-sm text-muted">{{ model }}</span>
992
1094
  </li>
993
1095
  </ul>
994
- <p v-else class="text-sm text-muted">None listed.</p>
1096
+ <p v-else class="text-sm text-muted">
1097
+ None listed.
1098
+ </p>
995
1099
  </div>
996
1100
  </UCard>
997
1101
  </div>
998
1102
 
999
1103
  <div v-else class="py-8 text-center">
1000
- <p class="text-sm text-muted">No mental model data available.</p>
1104
+ <p class="text-sm text-muted">
1105
+ No mental model data available.
1106
+ </p>
1001
1107
  </div>
1002
1108
  </div>
1003
1109
 
@@ -1007,19 +1113,53 @@ function formatTokens(n: number): string {
1007
1113
  <UCard>
1008
1114
  <div class="space-y-5">
1009
1115
  <div>
1010
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-3">Permissions</p>
1116
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-3">
1117
+ Permissions
1118
+ </p>
1011
1119
  <div class="flex flex-wrap gap-2">
1012
- <UBadge v-if="agent.authority.veto" label="Veto" color="error" variant="subtle" />
1013
- <UBadge v-if="agent.authority.approve_architecture" label="Approve Architecture" color="success" variant="subtle" />
1014
- <UBadge v-if="agent.authority.approve_budget" label="Approve Budget" color="success" variant="subtle" />
1015
- <UBadge v-if="agent.authority.approve_quality" label="Approve Quality" color="success" variant="subtle" />
1016
- <UBadge v-if="agent.authority.block_release" label="Block Release" color="error" variant="subtle" />
1017
- <UBadge v-if="agent.authority.orchestrate" label="Orchestrate" color="primary" variant="subtle" />
1120
+ <UBadge
1121
+ v-if="agent.authority.veto"
1122
+ label="Veto"
1123
+ color="error"
1124
+ variant="subtle"
1125
+ />
1126
+ <UBadge
1127
+ v-if="agent.authority.approve_architecture"
1128
+ label="Approve Architecture"
1129
+ color="success"
1130
+ variant="subtle"
1131
+ />
1132
+ <UBadge
1133
+ v-if="agent.authority.approve_budget"
1134
+ label="Approve Budget"
1135
+ color="success"
1136
+ variant="subtle"
1137
+ />
1138
+ <UBadge
1139
+ v-if="agent.authority.approve_quality"
1140
+ label="Approve Quality"
1141
+ color="success"
1142
+ variant="subtle"
1143
+ />
1144
+ <UBadge
1145
+ v-if="agent.authority.block_release"
1146
+ label="Block Release"
1147
+ color="error"
1148
+ variant="subtle"
1149
+ />
1150
+ <UBadge
1151
+ v-if="agent.authority.orchestrate"
1152
+ label="Orchestrate"
1153
+ color="primary"
1154
+ variant="subtle"
1155
+ />
1018
1156
  </div>
1019
1157
  </div>
1020
1158
 
1021
1159
  <div v-if="agent.authority.delegates_to?.length">
1022
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-2">Delegates To</p>
1160
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-2">
1161
+ Delegates To
1162
+ </p>
1023
1163
  <div class="flex flex-wrap gap-2">
1024
1164
  <UBadge
1025
1165
  v-for="d in agent.authority.delegates_to"
@@ -1032,19 +1172,27 @@ function formatTokens(n: number): string {
1032
1172
  </div>
1033
1173
 
1034
1174
  <div v-if="agent.authority.escalates_to">
1035
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Escalates To</p>
1036
- <p class="text-sm font-mono">{{ agent.authority.escalates_to }}</p>
1175
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
1176
+ Escalates To
1177
+ </p>
1178
+ <p class="text-sm font-mono">
1179
+ {{ agent.authority.escalates_to }}
1180
+ </p>
1037
1181
  </div>
1038
1182
 
1039
1183
  <div v-if="!agent.authority.escalates_to && !agent.authority.delegates_to?.length && !agent.authority.veto">
1040
- <p class="text-sm text-muted">Standard execution authority.</p>
1184
+ <p class="text-sm text-muted">
1185
+ Standard execution authority.
1186
+ </p>
1041
1187
  </div>
1042
1188
  </div>
1043
1189
  </UCard>
1044
1190
  </div>
1045
1191
 
1046
1192
  <div v-else class="py-8 text-center">
1047
- <p class="text-sm text-muted">No authority data available.</p>
1193
+ <p class="text-sm text-muted">
1194
+ No authority data available.
1195
+ </p>
1048
1196
  </div>
1049
1197
  </div>
1050
1198
 
@@ -1053,7 +1201,9 @@ function formatTokens(n: number): string {
1053
1201
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
1054
1202
  <UCard>
1055
1203
  <div class="space-y-3">
1056
- <p class="text-sm font-semibold text-muted uppercase tracking-wide">Domains</p>
1204
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide">
1205
+ Domains
1206
+ </p>
1057
1207
  <div v-if="agent.expertise_domains?.length" class="flex flex-wrap gap-2">
1058
1208
  <UBadge
1059
1209
  v-for="d in agent.expertise_domains"
@@ -1064,13 +1214,17 @@ function formatTokens(n: number): string {
1064
1214
  size="sm"
1065
1215
  />
1066
1216
  </div>
1067
- <p v-else class="text-sm text-muted">No domains listed.</p>
1217
+ <p v-else class="text-sm text-muted">
1218
+ No domains listed.
1219
+ </p>
1068
1220
  </div>
1069
1221
  </UCard>
1070
1222
 
1071
1223
  <UCard>
1072
1224
  <div class="space-y-3">
1073
- <p class="text-sm font-semibold text-muted uppercase tracking-wide">Frameworks</p>
1225
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide">
1226
+ Frameworks
1227
+ </p>
1074
1228
  <ul v-if="agent.frameworks?.length" class="space-y-2">
1075
1229
  <li
1076
1230
  v-for="f in agent.frameworks"
@@ -1081,7 +1235,9 @@ function formatTokens(n: number): string {
1081
1235
  {{ f }}
1082
1236
  </li>
1083
1237
  </ul>
1084
- <p v-else class="text-sm text-muted">No frameworks listed.</p>
1238
+ <p v-else class="text-sm text-muted">
1239
+ No frameworks listed.
1240
+ </p>
1085
1241
  </div>
1086
1242
  </UCard>
1087
1243
  </div>
@@ -1089,7 +1245,9 @@ function formatTokens(n: number): string {
1089
1245
  <UCard v-if="agent.expertise_depth || agent.expertise_years">
1090
1246
  <div class="flex flex-wrap gap-6">
1091
1247
  <div v-if="agent.expertise_depth">
1092
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Depth</p>
1248
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
1249
+ Depth
1250
+ </p>
1093
1251
  <UBadge
1094
1252
  :label="agent.expertise_depth"
1095
1253
  :color="(depthColor[agent.expertise_depth] ?? 'neutral') as any"
@@ -1098,7 +1256,9 @@ function formatTokens(n: number): string {
1098
1256
  />
1099
1257
  </div>
1100
1258
  <div v-if="agent.expertise_years">
1101
- <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">Experience</p>
1259
+ <p class="text-sm font-semibold text-muted uppercase tracking-wide mb-1">
1260
+ Experience
1261
+ </p>
1102
1262
  <p class="text-2xl font-bold">
1103
1263
  {{ agent.expertise_years }}
1104
1264
  <span class="text-sm font-normal text-muted">years</span>