feat(team): Datenschicht für Property On

Property On wird als Funktionsbereich "Teamübersicht" in Property Match
eingebettet — sieben digitale Mitarbeiter, die ausführen, während der Mensch
freigibt, anpasst oder zurückweist.

Durchgehend Provider → Service → Hook, kein übersprungener Layer:
- domain/: teamAgent, agentWorkItem, agentProtocol, agentConnection, agentFilters
- mock-data/: 7 Personalblätter aus dem Agenten-Katalog (Aufgabenzahlen exakt:
  Ferdi 9, Sina 7, Lea 11, Bruno 9, Reto 10, Livia 11, Nora 8), 14 Vorgänge,
  38 Protokolleinträge, 9 Verbindungen
- provider/: vier Mockup-Provider mit versionierter localStorage-Persistenz
- services/: Fachlogik inkl. Freigabe, Zurückweisung, Kennzahlen, Demo-Reset
- hooks/: React Query, Mutationen entwerten Vorgänge, Kennzahlen und Protokoll
  gemeinsam — sonst zeigt die Oberfläche widersprüchliche Zahlen

Namensgebung: Der Entitätstyp heisst TeamAgent, nicht Agent. Im Repo existiert
bereits PowerOn als KI-Backend-Proxy, und Claude Code legt Arbeitskopien unter
agent-* ab; ein nackter Typ Agent wäre in Suchen nicht auffindbar. Alle
Satellitentypen sind Agent*-präfixiert, weil domain/index.ts per export *
bündelt und generische Namen dort kollidieren.

Eigener Protokolltyp statt ActivityEvent: den Namen gibt es im Repo dreifach
und gegenseitig inkompatibel (domain/activityEvent.ts, governanceService.ts,
PropertyActivityLogPanel.tsx). Ein Anschluss hätte den Konflikt zementiert.

Demo-Uhr in lib/teamClock.ts: die Mockdaten sind auf den 20.05.2026 verankert.
Gegen die Systemzeit gerechnet wären die Filter "heute / diese Woche / dieser
Monat" an jedem anderen Kalendertag leer und die Auswertung sähe kaputt aus,
obwohl sie korrekt rechnet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin SutterandClaude Opus 5 committed 2026-08-03 16:55:40 +02:00
1 parent 36ce548169
commit 52dc7de25e
38 files changed
+9055

No files matched your search

+75
View File
@@ -0,0 +1,75 @@
/**
* Property On — Subreiter «Kanäle & Systeme»: die zentralen Verbindungen der
* Organisation.
*
* Bewusst getrennt von `domain/dataSource.ts` (`DataSource`/`ConnectorRun`):
* jenes Modell beschreibt Crawler-Quellen mit AGB-Status und Crawl-Läufen, hier
* geht es um angebundene Kommunikationskanäle und Fachsysteme. Zwei fachlich
* verschiedene Dinge im selben Typ zu führen wäre eine Scheinvereinfachung.
*
* Alle Verbindungen sind Frontend-Simulation — es werden nie echte Zugangsdaten
* erfasst und nie echte Systeme kontaktiert.
*/
import type { AgentAccessLevel, AgentConnectionStatus } from './teamAgent'
export const AgentConnectionCategory = {
EMAIL_M365: 'EMAIL_M365',
WHATSAPP: 'WHATSAPP',
TEAMS: 'TEAMS',
CALENDAR: 'CALENDAR',
DOCUMENT_STORE: 'DOCUMENT_STORE',
PHONE_VOICE: 'PHONE_VOICE',
ERP: 'ERP',
CRM: 'CRM',
PUBLIC_SOURCES: 'PUBLIC_SOURCES',
} as const
export type AgentConnectionCategory = typeof AgentConnectionCategory[keyof typeof AgentConnectionCategory]
/** ERP-Auswahl aus §13.4. */
export const AgentErpVendor = {
IMMOTOP2: 'IMMOTOP2',
ABACUS_ABAIMMO: 'ABACUS_ABAIMMO',
RIMO_R5: 'RIMO_R5',
GARAIO_REM: 'GARAIO_REM',
OTHER: 'OTHER',
} as const
export type AgentErpVendor = typeof AgentErpVendor[keyof typeof AgentErpVendor]
export interface AgentConnectionPermission {
id: string
label: string
access: AgentAccessLevel
granted: boolean
}
export interface AgentConnection {
id: string
category: AgentConnectionCategory
name: string
description: string
status: AgentConnectionStatus
accountCount: number
usedByAgentIds: string[]
lastSyncAt?: string
/** Nur bei `category === ERP` gesetzt. */
vendor?: AgentErpVendor
/** Frei benannte Verbindung aus dem Assistenten, z. B. «Bewirtschaftung Zürich». */
connectionLabel?: string
permissions: AgentConnectionPermission[]
}
/** Ergebnis von «Verbindung testen» — deterministisch, kein Zufall (§19.2). */
export interface AgentConnectionTestResult {
ok: boolean
message: string
checkedAt: string
}
export interface CreateAgentConnectionInput {
connectionId: string
vendor?: AgentErpVendor
connectionLabel: string
permissionIds: string[]
agentIds: string[]
}
+64
View File
@@ -0,0 +1,64 @@
/**
* Property On — Filter- und Sortierdimensionen.
*
* Eigene Datei, weil sowohl die Auswertung auf der Teamübersicht (§4.2) als auch
* der Bearbeitungsverlauf (§5.3) und das Protokoll (§12.3) denselben Zeitraum-
* begriff teilen. Ohne gemeinsamen Typ driften die drei Filter auseinander.
*/
export const AgentPeriod = {
TODAY: 'TODAY',
WEEK: 'WEEK',
MONTH: 'MONTH',
YEAR: 'YEAR',
ALL: 'ALL',
} as const
export type AgentPeriod = typeof AgentPeriod[keyof typeof AgentPeriod]
export const AgentWorkItemSort = {
NEWEST: 'NEWEST',
OLDEST: 'OLDEST',
PRIORITY: 'PRIORITY',
AGENT: 'AGENT',
OBJECT_ID: 'OBJECT_ID',
} as const
export type AgentWorkItemSort = typeof AgentWorkItemSort[keyof typeof AgentWorkItemSort]
/**
* Grenze eines Zeitraums, gemessen am Referenzdatum.
* Gibt den frühesten noch enthaltenen Zeitpunkt zurück, oder null für ALL.
*/
export function periodStart(period: AgentPeriod, reference: Date): Date | null {
const d = new Date(reference)
switch (period) {
case AgentPeriod.TODAY:
d.setHours(0, 0, 0, 0)
return d
case AgentPeriod.WEEK: {
// Woche beginnt am Montag — Schweizer Konvention
const day = (d.getDay() + 6) % 7
d.setDate(d.getDate() - day)
d.setHours(0, 0, 0, 0)
return d
}
case AgentPeriod.MONTH:
d.setDate(1)
d.setHours(0, 0, 0, 0)
return d
case AgentPeriod.YEAR:
d.setMonth(0, 1)
d.setHours(0, 0, 0, 0)
return d
case AgentPeriod.ALL:
default:
return null
}
}
/** Liegt `isoTimestamp` im gewählten Zeitraum? */
export function isWithinPeriod(isoTimestamp: string, period: AgentPeriod, reference: Date): boolean {
const start = periodStart(period, reference)
if (!start) return true
const t = new Date(isoTimestamp).getTime()
return !Number.isNaN(t) && t >= start.getTime()
}
+86
View File
@@ -0,0 +1,86 @@
/**
* Property On — Protokolleinträge.
*
* Bewusst ein eigener Typ und nicht `ActivityEvent`: den Namen gibt es im Repo
* bereits dreifach und gegenseitig inkompatibel — in `domain/activityEvent.ts`
* (über das Domain-Barrel exportiert), lokal in `services/governanceService.ts`
* und noch einmal in `components/supply/PropertyActivityLogPanel.tsx`. Ein
* Anschluss an einen dieser Typen würde den Konflikt zementieren.
*/
import type { AgentChannelType } from './teamAgent'
import type { AgentDetailField, AgentProcessingStep, AgentSourceReference } from './agentWorkItem'
export const AgentProtocolStatus = {
SUCCESS: 'SUCCESS',
WARNING: 'WARNING',
ERROR: 'ERROR',
INFO: 'INFO',
} as const
export type AgentProtocolStatus = typeof AgentProtocolStatus[keyof typeof AgentProtocolStatus]
export const AgentProtocolEventType = {
TASK_RUN: 'TASK_RUN',
MESSAGE_PROCESSED: 'MESSAGE_PROCESSED',
REMINDER_SENT: 'REMINDER_SENT',
ESCALATION: 'ESCALATION',
APPROVAL: 'APPROVAL',
REJECTION: 'REJECTION',
EDIT: 'EDIT',
SETTINGS_SAVED: 'SETTINGS_SAVED',
TASKS_SAVED: 'TASKS_SAVED',
AGENT_PAUSED: 'AGENT_PAUSED',
AGENT_RESUMED: 'AGENT_RESUMED',
CHANNEL_TESTED: 'CHANNEL_TESTED',
CHANNEL_TOGGLED: 'CHANNEL_TOGGLED',
CONNECTION_CREATED: 'CONNECTION_CREATED',
CONNECTION_REMOVED: 'CONNECTION_REMOVED',
DEMO_RESET: 'DEMO_RESET',
} as const
export type AgentProtocolEventType = typeof AgentProtocolEventType[keyof typeof AgentProtocolEventType]
/** Wer den Eintrag ausgelöst hat — Mensch oder Zeitplan/Systemtrigger. */
export const AgentTriggerSource = {
USER: 'USER',
SYSTEM: 'SYSTEM',
} as const
export type AgentTriggerSource = typeof AgentTriggerSource[keyof typeof AgentTriggerSource]
export const AgentApprovalDecision = {
APPROVED: 'APPROVED',
REJECTED: 'REJECTED',
EDITED: 'EDITED',
} as const
export type AgentApprovalDecision = typeof AgentApprovalDecision[keyof typeof AgentApprovalDecision]
export interface AgentProtocolEntry {
id: string
/**
* Verantwortlicher digitaler Mitarbeiter. Fehlt bei organisationsweiten
* Ereignissen — eine Verbindung im Subreiter «Kanäle & Systeme» gehört keinem
* einzelnen Mitarbeiter. Solche Einträge erscheinen im Gesamtprotokoll, nicht
* im Personalblatt.
*/
agentId?: string
/** Nur bei Verbindungsereignissen gesetzt. */
connectionId?: string
timestamp: string
eventType: AgentProtocolEventType
title: string
description: string
status: AgentProtocolStatus
objectId?: string
channel?: AgentChannelType
triggeredBy: AgentTriggerSource
/** Klarname der auslösenden Person, wenn `triggeredBy === USER`. */
triggeredByName?: string
approvalDecision?: AgentApprovalDecision
workItemId?: string
// Detailansicht (§12.5)
input?: AgentDetailField[]
processingSteps?: AgentProcessingStep[]
output?: AgentDetailField[]
sourceReferences?: AgentSourceReference[]
}
export type CreateAgentProtocolEntryInput = Omit<AgentProtocolEntry, 'id'>
+185
View File
@@ -0,0 +1,185 @@
/**
* Property On — Vorgänge im «Bearbeitungsverlauf».
*
* Ein Vorgang ist entweder bereits autonom erledigt («Erledigte Aufträge») oder
* wartet auf einen Menschen («Pendente Anfragen»). Das Bedienprinzip ist
* human-led, agent-operated: der Agent arbeitet, der Mensch entscheidet.
*/
import type { AgentChannelType } from './teamAgent'
export const AgentWorkItemStatus = {
PENDING: 'PENDING',
APPROVED: 'APPROVED',
REJECTED: 'REJECTED',
EDITING: 'EDITING',
COMPLETED: 'COMPLETED',
} as const
export type AgentWorkItemStatus = typeof AgentWorkItemStatus[keyof typeof AgentWorkItemStatus]
export const AgentWorkItemPriority = {
LOW: 'LOW',
MEDIUM: 'MEDIUM',
HIGH: 'HIGH',
CRITICAL: 'CRITICAL',
} as const
export type AgentWorkItemPriority = typeof AgentWorkItemPriority[keyof typeof AgentWorkItemPriority]
/** Vorgangstyp — Filterdimension in §5.3. */
export const AgentWorkItemKind = {
MESSAGE_TRIAGE: 'MESSAGE_TRIAGE',
REMINDER_CREATED: 'REMINDER_CREATED',
CONTRACT_ANSWER: 'CONTRACT_ANSWER',
LOCATION_MODULE: 'LOCATION_MODULE',
INQUIRY_QUALIFIED: 'INQUIRY_QUALIFIED',
AUTO_REJECTION: 'AUTO_REJECTION',
APPOINTMENT: 'APPOINTMENT',
BRIEFING: 'BRIEFING',
RECAP: 'RECAP',
MARKET_SIGNAL: 'MARKET_SIGNAL',
ESCALATION: 'ESCALATION',
DOC_GAP: 'DOC_GAP',
SYSTEM_ISSUE: 'SYSTEM_ISSUE',
} as const
export type AgentWorkItemKind = typeof AgentWorkItemKind[keyof typeof AgentWorkItemKind]
/** Fachbereich — Filterdimension «Bereich» in §4.2 und §5.3. */
export const AgentDomainArea = {
CONTRACT_DEADLINES: 'CONTRACT_DEADLINES',
DOCUMENTATION: 'DOCUMENTATION',
INQUIRIES: 'INQUIRIES',
VIEWINGS: 'VIEWINGS',
LOCATION: 'LOCATION',
MARKET: 'MARKET',
} as const
export type AgentDomainArea = typeof AgentDomainArea[keyof typeof AgentDomainArea]
export const AgentWorkItemAction = {
APPROVE: 'APPROVE',
ONE_CLICK_CONFIRM: 'ONE_CLICK_CONFIRM',
DECIDE: 'DECIDE',
EDIT: 'EDIT',
REJECT: 'REJECT',
ANSWER_QUERY: 'ANSWER_QUERY',
OPEN_SOURCE: 'OPEN_SOURCE',
MARK_DONE: 'MARK_DONE',
} as const
export type AgentWorkItemAction = typeof AgentWorkItemAction[keyof typeof AgentWorkItemAction]
// ── Bestandteile der Detailansicht ────────────────────────────────────────────
/**
* Geordnetes Schlüssel-Wert-Paar. Die Spezifikation nennt
* `Record<string, string | string[]>`; ein Array erhält die fachliche
* Reihenfolge verlässlich und lässt sich direkt rendern.
*/
export interface AgentDetailField {
label: string
value: string | string[]
}
/** Fundstelle: Dokument, Seite, Zitat — Sinas Quellenzwang hängt daran. */
export interface AgentSourceReference {
id: string
label: string
documentName?: string
locator?: string
quote?: string
}
/** Ein Nachrichten- oder Mailverlauf, wie er im Detail gezeigt wird (§5.4). */
export interface AgentMessageEntry {
id: string
at: string
from: string
to?: string
channel: AgentChannelType
subject?: string
body: string
}
/** Simulierter Verarbeitungsschritt — bewusst in Alltagssprache. */
export interface AgentProcessingStep {
id: string
label: string
detail?: string
}
export interface AgentWorkItemHistoryEntry {
id: string
at: string
actor: string
action: string
note?: string
}
/** Im «Anpassen»-Dialog editierbares Feld. */
export interface AgentEditableField {
id: string
label: string
value: string
multiline?: boolean
helperText?: string
}
// ── Entität ───────────────────────────────────────────────────────────────────
export interface AgentWorkItem {
id: string
agentId: string
kind: AgentWorkItemKind
area: AgentDomainArea
title: string
summary: string
objectId?: string
objectLabel?: string
sourceChannel: AgentChannelType
targetChannel?: AgentChannelType
createdAt: string
completedAt?: string
priority: AgentWorkItemPriority
status: AgentWorkItemStatus
/** true → Reiter «Pendente Anfragen», false → «Erledigte Aufträge». */
requiresDecision: boolean
/** Grund der Rückfrage (§5.5) — nur bei pendenten Vorgängen. */
escalationReason?: string
/** Die konkrete Frage, die der Mensch beantworten muss. */
decisionQuestion?: string
inputs: AgentDetailField[]
result: AgentDetailField[]
processingSteps: AgentProcessingStep[]
sourceReferences: AgentSourceReference[]
messageThread?: AgentMessageEntry[]
availableActions: AgentWorkItemAction[]
editableFields?: AgentEditableField[]
history: AgentWorkItemHistoryEntry[]
rejectionReason?: string
}
// ── Auswertung ────────────────────────────────────────────────────────────────
/**
* Die drei Kennzahlen der Teamübersicht (§4.2).
*
* Abgrenzung der beiden pendenten Werte: Ein Vorgang zählt als Freigabeanfrage,
* wenn er dem Menschen eine Entscheidung zur Bestätigung vorlegt (Freigeben,
* 1-Klick-Bestätigung, Entscheidung treffen). Alles andere Pendente — Rückfragen,
* fehlende Angaben, Verarbeitungs- und Systemprobleme — zählt als Nachricht.
*/
export interface AgentWorkloadKpis {
completedTasks: number
pendingMessages: number
pendingApprovals: number
}
// ── Schreib-Inputs ────────────────────────────────────────────────────────────
export interface UpdateAgentWorkItemInput {
status?: AgentWorkItemStatus
result?: AgentDetailField[]
editableFields?: AgentEditableField[]
rejectionReason?: string
completedAt?: string
requiresDecision?: boolean
historyEntry?: Omit<AgentWorkItemHistoryEntry, 'id'>
}
+246
View File
@@ -0,0 +1,246 @@
/**
* Property On — «Teamübersicht»: digitale Mitarbeiter.
*
* Namensgebung: Der Entitätstyp heisst `TeamAgent`, nicht `Agent`. Im Repo existiert
* bereits `PowerOn` (AI-Backend-Proxy) und Claude-Code legt Worktrees unter `agent-*`
* ab — ein nackter Typ `Agent` wäre in Suchen praktisch nicht auffindbar. Alle
* Satellitentypen tragen deshalb das Präfix `Agent*`; generische Namen wie `Status`,
* `Priority` oder `Channel` sind tabu, weil `src/domain/index.ts` per `export *`
* bündelt und dort bereits `ReviewStatus`, `SourceType`, `RiskLevel` u. a. liegen.
*
* Fachliche Wahrheit ist der Agenten-Katalog: Rollen, Personalnummern und
* Zuständigkeiten der sieben Kernagenten werden nicht verändert.
*/
// ── Status & Autonomie ────────────────────────────────────────────────────────
export const AgentStatus = {
ACTIVE: 'ACTIVE',
BUILDING: 'BUILDING',
PAUSED: 'PAUSED',
} as const
export type AgentStatus = typeof AgentStatus[keyof typeof AgentStatus]
export const AgentAutonomyLevel = {
AUTONOMOUS: 'AUTONOMOUS',
APPROVAL_REQUIRED: 'APPROVAL_REQUIRED',
PROPOSAL_ONLY: 'PROPOSAL_ONLY',
} as const
export type AgentAutonomyLevel = typeof AgentAutonomyLevel[keyof typeof AgentAutonomyLevel]
// ── Kanäle ────────────────────────────────────────────────────────────────────
export const AgentChannelType = {
EMAIL: 'EMAIL',
TEAMS: 'TEAMS',
WHATSAPP: 'WHATSAPP',
WORKSPACE_CHAT: 'WORKSPACE_CHAT',
CALENDAR: 'CALENDAR',
WEB_FORM: 'WEB_FORM',
PORTAL_LEAD_MAIL: 'PORTAL_LEAD_MAIL',
PHONE_VOICE: 'PHONE_VOICE',
PHONE_NOTE: 'PHONE_NOTE',
AUDIO_OUTPUT: 'AUDIO_OUTPUT',
STRUCTURED_MAIL_FALLBACK: 'STRUCTURED_MAIL_FALLBACK',
} as const
export type AgentChannelType = typeof AgentChannelType[keyof typeof AgentChannelType]
export const AgentChannelDirection = {
INBOUND: 'INBOUND',
OUTBOUND: 'OUTBOUND',
BOTH: 'BOTH',
} as const
export type AgentChannelDirection = typeof AgentChannelDirection[keyof typeof AgentChannelDirection]
export const AgentConnectionStatus = {
CONNECTED: 'CONNECTED',
DISCONNECTED: 'DISCONNECTED',
ROADMAP: 'ROADMAP',
} as const
export type AgentConnectionStatus = typeof AgentConnectionStatus[keyof typeof AgentConnectionStatus]
/** Simulierte Kanalkonfiguration — es werden nie echte Zugangsdaten erfasst. */
export interface AgentChannelConfig {
displayName: string
senderAddress?: string
inboxAddress?: string
defaultRecipients: string[]
autoReplyEnabled: boolean
}
export interface AgentChannel {
id: string
type: AgentChannelType
direction: AgentChannelDirection
description: string
status: AgentConnectionStatus
enabled: boolean
/** Kennzeichnet im Katalog als «optional» geführte Kanäle. */
optional?: boolean
config: AgentChannelConfig
}
// ── Systeme ───────────────────────────────────────────────────────────────────
export const AgentSystemType = {
M365_EXCHANGE: 'M365_EXCHANGE',
OUTLOOK: 'OUTLOOK',
SHAREPOINT: 'SHAREPOINT',
ONEDRIVE: 'ONEDRIVE',
FILESHARE: 'FILESHARE',
DMS: 'DMS',
CALENDAR: 'CALENDAR',
IMMOTOP2: 'IMMOTOP2',
ABACUS_ABAIMMO: 'ABACUS_ABAIMMO',
RIMO_R5: 'RIMO_R5',
GARAIO_REM: 'GARAIO_REM',
CRM: 'CRM',
ZEFIX: 'ZEFIX',
SHAB: 'SHAB',
PUBLIC_WEB: 'PUBLIC_WEB',
PORTAL_LEAD_INBOX: 'PORTAL_LEAD_INBOX',
WORKSPACE: 'WORKSPACE',
} as const
export type AgentSystemType = typeof AgentSystemType[keyof typeof AgentSystemType]
export const AgentAccessLevel = {
READ: 'READ',
WRITE: 'WRITE',
READ_WRITE: 'READ_WRITE',
} as const
export type AgentAccessLevel = typeof AgentAccessLevel[keyof typeof AgentAccessLevel]
export interface AgentSystem {
id: string
type: AgentSystemType
access: AgentAccessLevel
status: AgentConnectionStatus
/** Wofür der Agent dieses System nutzt — in Alltagssprache, ohne KI-Jargon. */
usage: string
/** Klartext zur Berechtigung, z. B. «Schreibt erst nach Freigabe». */
permissionNote: string
}
// ── Aufgaben ──────────────────────────────────────────────────────────────────
export interface AgentTask {
id: string
title: string
description: string
enabled: boolean
/** Zeitplan oder auslösendes Ereignis, z. B. «täglich 06:00» oder «bei Eingang». */
schedule: string
requiresApproval: boolean
dependsOnChannel?: AgentChannelType
dependsOnSystem?: AgentSystemType
}
// ── Einstellungen ─────────────────────────────────────────────────────────────
export const AgentSettingKind = {
TEXT: 'TEXT',
NUMBER: 'NUMBER',
SELECT: 'SELECT',
MULTI_SELECT: 'MULTI_SELECT',
BOOLEAN: 'BOOLEAN',
TIME: 'TIME',
} as const
export type AgentSettingKind = typeof AgentSettingKind[keyof typeof AgentSettingKind]
export const AgentSettingGroup = {
GENERAL: 'GENERAL',
APPROVAL: 'APPROVAL',
DELIVERY: 'DELIVERY',
SPECIFIC: 'SPECIFIC',
} as const
export type AgentSettingGroup = typeof AgentSettingGroup[keyof typeof AgentSettingGroup]
export interface AgentSettingOption {
value: string
label: string
}
export interface AgentSetting {
id: string
label: string
description?: string
kind: AgentSettingKind
group: AgentSettingGroup
value: string | number | boolean | string[]
options?: AgentSettingOption[]
unit?: string
min?: number
max?: number
/** Fachlich nicht abschaltbar — z. B. Sinas Quellenzwang. */
locked?: boolean
lockedReason?: string
}
// ── Kennzahlen & Profil ───────────────────────────────────────────────────────
export interface AgentMetric {
id: string
label: string
value: string
hint?: string
}
/** Informationsbox unter dem Kopfbereich des Personalblatts (§7.6). */
export interface AgentProfile {
purpose: string
input: string[]
coreFlow: string[]
output: string[]
}
// ── Entität ───────────────────────────────────────────────────────────────────
/**
* Farbton des Avatars. Bewusst ein Token-Schlüssel statt eines Hex-Werts —
* `npm run check:tokens` liegt über dem Schwellwert und darf nicht wachsen.
*/
export const AgentAvatarTone = {
BRAND: 'BRAND',
SUCCESS: 'SUCCESS',
INFO: 'INFO',
SIGNAL: 'SIGNAL',
WARNING: 'WARNING',
GOLD: 'GOLD',
SLATE: 'SLATE',
} as const
export type AgentAvatarTone = typeof AgentAvatarTone[keyof typeof AgentAvatarTone]
export interface TeamAgent {
id: string
name: string
role: string
personnelNumber: string
department: string
email: string
avatarTone: AgentAvatarTone
status: AgentStatus
autonomyLevel: AgentAutonomyLevel
/** Ein Satz für Karte und Listeneintrag. */
shortDescription: string
/** Freitext zur Autonomie, wo der Katalog differenziert («autonom beim Erinnern, Freigabe bei Eskalationen»). */
autonomyNote: string
profile: AgentProfile
responsibilities: string[]
tasks: AgentTask[]
channels: AgentChannel[]
systems: AgentSystem[]
settings: AgentSetting[]
metrics: AgentMetric[]
/** Kurzkennzahl für die Agentenkarte auf der Teamübersicht. */
headlineMetricId: string
lastRun?: string
}
// ── Schreib-Inputs ────────────────────────────────────────────────────────────
export interface UpdateTeamAgentInput {
status?: AgentStatus
tasks?: AgentTask[]
channels?: AgentChannel[]
settings?: AgentSetting[]
}