Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f28c398d16 | |||
| 358ec3e65d |
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(npx tsc *)",
|
||||||
|
"Bash(npx vite *)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import type { AgentNodeData } from '../types/agentNode'
|
||||||
|
|
||||||
|
export const EXTRA_AGENT_POOL: AgentNodeData[] = [
|
||||||
|
{
|
||||||
|
id: 'qa',
|
||||||
|
name: 'QA Automator',
|
||||||
|
role: 'Test Automation',
|
||||||
|
roleBadge: 'badge-cyan',
|
||||||
|
avatar: 'QA',
|
||||||
|
status: 'idle',
|
||||||
|
statusLabel: 'Bereit',
|
||||||
|
task: 'End-to-End Tests schreiben',
|
||||||
|
goal: '100% Coverage für auth/',
|
||||||
|
progress: 0,
|
||||||
|
elapsed: '—',
|
||||||
|
next: 'Testplan erstellen',
|
||||||
|
model: 'Deepseek V4 Flash',
|
||||||
|
tokens: '0',
|
||||||
|
cost: '0.00',
|
||||||
|
think: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'devops',
|
||||||
|
name: 'DevOps',
|
||||||
|
role: 'CI/CD Pipeline',
|
||||||
|
roleBadge: 'badge-amber',
|
||||||
|
avatar: 'DO',
|
||||||
|
status: 'idle',
|
||||||
|
statusLabel: 'Bereit',
|
||||||
|
task: 'GitHub Actions Workflow',
|
||||||
|
goal: 'Automatisches Deploy auf merge',
|
||||||
|
progress: 0,
|
||||||
|
elapsed: '—',
|
||||||
|
next: 'Pipeline konfigurieren',
|
||||||
|
model: 'Deepseek V4 Pro',
|
||||||
|
tokens: '0',
|
||||||
|
cost: '0.00',
|
||||||
|
think: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'security',
|
||||||
|
name: 'Security Scanner',
|
||||||
|
role: 'Security Analysis',
|
||||||
|
roleBadge: 'badge-rose',
|
||||||
|
avatar: 'SC',
|
||||||
|
status: 'think',
|
||||||
|
statusLabel: 'Scannt',
|
||||||
|
task: 'Dependency-Audit durchführen',
|
||||||
|
goal: 'CVEs in api/ aufdecken',
|
||||||
|
progress: 18,
|
||||||
|
elapsed: '00:01:44',
|
||||||
|
next: 'Report an Iris',
|
||||||
|
model: 'Deepseek V4 Pro',
|
||||||
|
tokens: '9k',
|
||||||
|
cost: '0.18',
|
||||||
|
think: 'Analysiere package-lock.json auf bekannte Vulnerabilities…',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'pm',
|
||||||
|
name: 'Project Manager',
|
||||||
|
role: 'Coordination',
|
||||||
|
roleBadge: 'badge-purple',
|
||||||
|
avatar: 'PM',
|
||||||
|
status: 'think',
|
||||||
|
statusLabel: 'Plant',
|
||||||
|
task: 'Sprint-Retrospektive vorbereiten',
|
||||||
|
goal: 'Blockers identifizieren',
|
||||||
|
progress: 35,
|
||||||
|
elapsed: '00:05:10',
|
||||||
|
next: 'Meeting-Summary an Team',
|
||||||
|
model: 'Deepseek V4 Flash',
|
||||||
|
tokens: '14k',
|
||||||
|
cost: '0.24',
|
||||||
|
think: 'Analysiere Velocity-Daten der letzten 3 Sprints…',
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
import type { AgentNodeData } from '../types/agentNode'
|
||||||
|
import type { AgentDetailData, ThinkingItem } from '../types/agentDetail'
|
||||||
|
import type { DashboardAgentDto, ModelDto } from '../services/agentService'
|
||||||
|
|
||||||
|
const STATUS_LABELS: Record<AgentNodeData['status'], string> = {
|
||||||
|
work: 'Arbeitet',
|
||||||
|
think: 'Plant',
|
||||||
|
idle: 'Bereit',
|
||||||
|
block: 'Blockiert',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CatalogEntry {
|
||||||
|
elapsed: string
|
||||||
|
think: string | null
|
||||||
|
next: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const AGENT_CATALOG: Record<string, CatalogEntry> = {
|
||||||
|
iris: { elapsed: '--', think: null, next: 'Standby' },
|
||||||
|
programmer: { elapsed: '--', think: null, next: 'Standby' },
|
||||||
|
developer: { elapsed: '--', think: null, next: 'Standby' },
|
||||||
|
architekt: { elapsed: '--', think: null, next: 'Standby' },
|
||||||
|
reviewer: { elapsed: '--', think: null, next: 'Standby' },
|
||||||
|
executor: { elapsed: '--', think: null, next: 'Standby' },
|
||||||
|
researcher: { elapsed: '--', think: null, next: 'Standby' },
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveStatus(isActive: boolean, currentTask: string | null): AgentNodeData['status'] {
|
||||||
|
if (!isActive) return 'idle'
|
||||||
|
if (currentTask && currentTask !== 'Idle') return 'work'
|
||||||
|
return 'think'
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveAvatar(id: string, name: string): string {
|
||||||
|
if (id === 'iris') return 'IR'
|
||||||
|
if (id === 'programmer' || id === 'developer') return '</>'
|
||||||
|
return name.slice(0, 2).toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildThinkingItems(data: AgentNodeData): ThinkingItem[] {
|
||||||
|
if (!data.think) return []
|
||||||
|
const now = new Date()
|
||||||
|
const ts = (ago: number) => {
|
||||||
|
const d = new Date(now.getTime() - ago * 1000)
|
||||||
|
return d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' })
|
||||||
|
}
|
||||||
|
const sentences = data.think.split(/[.…!?]+/).filter(s => s.trim().length > 5)
|
||||||
|
|
||||||
|
if (sentences.length >= 2) {
|
||||||
|
const items: ThinkingItem[] = [
|
||||||
|
{ type: 'thought', text: sentences[0].trim() + '.', ts: ts(30) },
|
||||||
|
{ type: 'action', text: sentences[1].trim() + '…', ts: ts(18) },
|
||||||
|
]
|
||||||
|
const lastSentence = sentences.length >= 3
|
||||||
|
? sentences[sentences.length - 1].trim() + '.'
|
||||||
|
: 'Verarbeitung abgeschlossen.'
|
||||||
|
items.push({ type: 'result', text: lastSentence, ts: ts(3) })
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
if (sentences.length === 1) {
|
||||||
|
return [
|
||||||
|
{ type: 'thought', text: sentences[0].trim(), ts: ts(15) },
|
||||||
|
{ type: 'action', text: 'Analysiere Daten und erstelle nächsten Schritt…', ts: ts(6) },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return [{ type: 'thought', text: data.think, ts: ts(10) }]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toAgentNode(dto: DashboardAgentDto): AgentNodeData {
|
||||||
|
const cat = AGENT_CATALOG[dto.id] ?? AGENT_CATALOG['reviewer']!
|
||||||
|
const status = resolveStatus(dto.isActive, dto.currentTask)
|
||||||
|
return {
|
||||||
|
id: dto.id,
|
||||||
|
name: dto.name,
|
||||||
|
role: dto.role,
|
||||||
|
model: dto.model,
|
||||||
|
avatar: resolveAvatar(dto.id, dto.name),
|
||||||
|
status,
|
||||||
|
statusLabel: STATUS_LABELS[status],
|
||||||
|
task: dto.currentTask,
|
||||||
|
goal: dto.goal ?? null,
|
||||||
|
progress: dto.progress ?? 0,
|
||||||
|
elapsed: cat.elapsed,
|
||||||
|
next: cat.next,
|
||||||
|
tokens: '0',
|
||||||
|
cost: '0.00',
|
||||||
|
think: cat.think,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toModelAlias(dtos: ModelDto[]): { id: string; alias: string }[] {
|
||||||
|
return dtos.map(m => ({ id: m.id, alias: m.name }))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toAgentDetail(
|
||||||
|
data: AgentNodeData,
|
||||||
|
models: { id: string; alias: string }[]
|
||||||
|
): AgentDetailData {
|
||||||
|
const tokenNum = parseFloat(data.tokens?.replace(/[^0-9.]/g, '') || '0')
|
||||||
|
const tokenMultiplier = data.tokens?.includes('M')
|
||||||
|
? 1_000_000
|
||||||
|
: data.tokens?.includes('k') ? 1_000 : 1
|
||||||
|
const tokensToday = Math.round(tokenNum * tokenMultiplier)
|
||||||
|
|
||||||
|
const matchingModel = models.find(m => m.id === data.model || m.alias === data.model)
|
||||||
|
const displayModel = matchingModel?.alias ?? data.model
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: data.id,
|
||||||
|
name: data.name,
|
||||||
|
role: data.role,
|
||||||
|
model: displayModel,
|
||||||
|
status: data.status === 'block' ? 'idle' : data.status,
|
||||||
|
tokensToday,
|
||||||
|
costToday: parseFloat(data.cost || '0'),
|
||||||
|
workload: data.progress,
|
||||||
|
uptime: data.elapsed || '—',
|
||||||
|
lastActive: data.elapsed !== '—' ? 'Vor ' + data.elapsed : 'Nicht aktiv',
|
||||||
|
activeTaskCount: data.task ? 1 : 0,
|
||||||
|
thinking: buildThinkingItems(data),
|
||||||
|
availableModels: models,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import type { TaskItem } from '../types/task'
|
||||||
|
import type { TaskDto } from '../services/taskService'
|
||||||
|
|
||||||
|
function toPriority(raw: string): TaskItem['priority'] {
|
||||||
|
const p = raw.toLowerCase()
|
||||||
|
if (p === 'high' || p === 'critical' || p === 'urgent') return 'high'
|
||||||
|
if (p === 'low' || p === 'minor') return 'low'
|
||||||
|
return 'medium'
|
||||||
|
}
|
||||||
|
|
||||||
|
function toStatus(raw: string): TaskItem['status'] {
|
||||||
|
const s = raw.toLowerCase()
|
||||||
|
if (s === 'in progress' || s === 'active' || s === 'working') return 'active'
|
||||||
|
if (s === 'blocked' || s === 'block') return 'blocked'
|
||||||
|
return 'pending'
|
||||||
|
}
|
||||||
|
|
||||||
|
function toProgress(raw: string): number {
|
||||||
|
const s = raw.toLowerCase()
|
||||||
|
if (s === 'in progress' || s === 'active' || s === 'working') return 50
|
||||||
|
if (s === 'done') return 100
|
||||||
|
if (s === 'blocked') return 30
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toTaskItem(dto: TaskDto): TaskItem {
|
||||||
|
return {
|
||||||
|
id: dto.id,
|
||||||
|
title: dto.title,
|
||||||
|
agent: dto.assignedTo ?? '—',
|
||||||
|
priority: toPriority(dto.priority),
|
||||||
|
status: toStatus(dto.state),
|
||||||
|
progress: toProgress(dto.state),
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user