fix(ui): make autonomous control cockpit visual
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 39s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 0s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Failing after 1m14s

This commit is contained in:
Your Name
2026-07-01 23:12:09 +08:00
parent c90f1ced2f
commit 530270c339
4 changed files with 663 additions and 112 deletions

View File

@@ -3852,6 +3852,45 @@
"overrideTitle": "舊規範覆寫",
"hardBlockerTitle": "仍維持硬阻擋",
"hardBlockerDetail": "需 break-glass 或專案級合約,不由一般自動化靜默執行。",
"cockpit": {
"liveLabel": "LIVE PRODUCTION",
"title": "AI Agent Cockpit",
"subtitle": "Production readback for the active control layer, closed loop, receipts, and hard boundaries.",
"production": "Production",
"dbOk": "DB readback OK",
"dbReview": "DB readback review",
"workItems": "Work items",
"consumer": "LOG consumer",
"apply": "Apply",
"verifier": "Verifier",
"km": "KM",
"telegram": "Telegram",
"receiptDetail": "controlled runtime receipt",
"verifierDetail": "post-apply verifier",
"kmDetail": "KM / PlayBook writeback",
"telegramDetail": "Gateway receipt",
"loopTitle": "Runtime loop",
"closed": "closed",
"review": "review",
"missing": "missing",
"runtimeWrite": "runtime write",
"noRuntimeWrite": "no runtime write",
"riskTitle": "Controlled risk lanes",
"on": "ON",
"off": "OFF",
"ownerRequired": "owner review required",
"ownerNotRequired": "owner review not required",
"criticalBreakGlass": "critical break-glass",
"criticalReview": "critical review"
},
"flow": {
"candidate": "Candidate",
"checkMode": "Check-mode",
"apply": "Controlled apply",
"verifier": "Verifier",
"learning": "KM learning",
"telegram": "Telegram"
},
"readback": {
"marker": "Production v2 marker",
"markerDetail": "{task} · {status}",

View File

@@ -3852,6 +3852,45 @@
"overrideTitle": "舊規範覆寫",
"hardBlockerTitle": "仍維持硬阻擋",
"hardBlockerDetail": "需 break-glass 或專案級合約,不由一般自動化靜默執行。",
"cockpit": {
"liveLabel": "LIVE PRODUCTION",
"title": "AI Agent Cockpit",
"subtitle": "用 production readback 呈現控制層、閉環、收據與硬邊界。",
"production": "Production",
"dbOk": "DB readback OK",
"dbReview": "DB readback review",
"workItems": "Work items",
"consumer": "LOG consumer",
"apply": "Apply",
"verifier": "Verifier",
"km": "KM",
"telegram": "Telegram",
"receiptDetail": "controlled runtime receipt",
"verifierDetail": "post-apply verifier",
"kmDetail": "KM / PlayBook writeback",
"telegramDetail": "Gateway receipt",
"loopTitle": "Runtime loop",
"closed": "closed",
"review": "review",
"missing": "missing",
"runtimeWrite": "runtime write",
"noRuntimeWrite": "no runtime write",
"riskTitle": "Controlled risk lanes",
"on": "ON",
"off": "OFF",
"ownerRequired": "owner review required",
"ownerNotRequired": "owner review not required",
"criticalBreakGlass": "critical break-glass",
"criticalReview": "critical review"
},
"flow": {
"candidate": "Candidate",
"checkMode": "Check-mode",
"apply": "Controlled apply",
"verifier": "Verifier",
"learning": "KM learning",
"telegram": "Telegram"
},
"readback": {
"marker": "Production v2 marker",
"markerDetail": "{task} · {status}",

View File

@@ -853,6 +853,237 @@ function GateMatrixRow({
)
}
function AutonomyCockpitMetric({
label,
value,
detail,
tone = 'neutral',
icon,
}: {
label: string
value: number | string
detail: string
tone?: 'ok' | 'warn' | 'danger' | 'neutral'
icon: ReactNode
}) {
const color = toneColor(tone)
return (
<div style={{
padding: 12,
border: `0.5px solid ${color}40`,
borderRadius: 7,
background: '#fff',
display: 'grid',
gridTemplateColumns: '32px minmax(0, 1fr)',
gap: 10,
alignItems: 'center',
minWidth: 0,
}}>
<div style={{
width: 32,
height: 32,
borderRadius: 7,
border: `0.5px solid ${color}55`,
background: `${color}12`,
color,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
{icon}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 5, minWidth: 0 }}>
<SmallLabel>{label}</SmallLabel>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 26, fontWeight: 780, color, lineHeight: 1, overflowWrap: 'anywhere' }}>
{value}
</span>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#6b675f', lineHeight: 1.35, overflowWrap: 'anywhere' }}>
{redactPublicText(detail)}
</span>
</div>
</div>
)
}
function AutonomyFlowStep({
label,
status,
detail,
tone = 'neutral',
icon,
}: {
label: string
status: string
detail: string
tone?: 'ok' | 'warn' | 'danger' | 'neutral'
icon: ReactNode
}) {
const color = toneColor(tone)
return (
<div style={{
padding: 11,
border: `0.5px solid ${color}40`,
borderRadius: 7,
background: '#fff',
display: 'flex',
flexDirection: 'column',
gap: 9,
minWidth: 0,
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8, minWidth: 0 }}>
<div style={{
width: 30,
height: 30,
borderRadius: 7,
border: `0.5px solid ${color}55`,
background: `${color}12`,
color,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
}}>
{icon}
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, fontWeight: 800, color, textAlign: 'right', overflowWrap: 'anywhere' }}>
{redactPublicText(status)}
</span>
</div>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 760, color: '#141413', lineHeight: 1.15, overflowWrap: 'anywhere' }}>
{redactPublicText(label)}
</span>
<div style={{ height: 4, borderRadius: 999, background: '#eee9dd', overflow: 'hidden' }}>
<div style={{ width: tone === 'danger' ? '40%' : tone === 'warn' ? '70%' : '100%', height: '100%', background: color }} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#706d65', lineHeight: 1.35, overflowWrap: 'anywhere' }}>
{redactPublicText(detail)}
</span>
</div>
)
}
function autonomyStageTone(stage: { present: boolean; status: string } | undefined, loopClosed: boolean): 'ok' | 'warn' | 'danger' | 'neutral' {
if (!stage?.present) return 'danger'
if (['success', 'sent', 'REVIEW', 'inferred_from_check_mode'].includes(stage.status)) return 'ok'
if (stage.status === 'failed' && loopClosed) return 'warn'
if (stage.status === 'failed') return 'danger'
return 'neutral'
}
function AutonomyWorkspaceScene({
stations,
completion,
status,
}: {
stations: Array<{
key: string
label: string
value: number | string
tone: 'ok' | 'warn' | 'danger' | 'neutral'
icon: ReactNode
}>
completion: number
status: string
}) {
const safeCompletion = Math.max(0, Math.min(100, completion))
return (
<div className="automation-inventory-autonomy-workspace-scene" style={{
position: 'relative',
overflow: 'hidden',
border: '0.5px solid #cfe7d7',
borderRadius: 8,
background: '#f7fff9',
minHeight: 238,
padding: 14,
}}>
<div className="automation-inventory-autonomy-workspace-rail" aria-hidden="true" />
<span className="automation-inventory-autonomy-workspace-packet automation-inventory-autonomy-workspace-packet-a" aria-hidden="true" />
<span className="automation-inventory-autonomy-workspace-packet automation-inventory-autonomy-workspace-packet-b" aria-hidden="true" />
<span className="automation-inventory-autonomy-workspace-packet automation-inventory-autonomy-workspace-packet-c" aria-hidden="true" />
<div style={{
position: 'absolute',
inset: '50% auto auto 50%',
transform: 'translate(-50%, -50%)',
width: 132,
height: 132,
borderRadius: 8,
border: '0.5px solid #15803d33',
background: 'rgba(255,255,255,0.9)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 7,
zIndex: 2,
boxShadow: '0 12px 30px rgba(20, 20, 19, 0.08)',
}} className="automation-inventory-autonomy-workspace-console">
<Gauge size={20} style={{ color: '#15803d' }} />
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 34, fontWeight: 800, lineHeight: 1, color: '#15803d' }}>
{safeCompletion}%
</span>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#4f6156', textAlign: 'center', lineHeight: 1.3, overflowWrap: 'anywhere' }}>
{redactPublicText(status)}
</span>
</div>
<div style={{
position: 'relative',
zIndex: 3,
display: 'grid',
gridTemplateColumns: 'repeat(4, minmax(0, 1fr))',
gap: 10,
minHeight: 210,
alignItems: 'stretch',
}} className="automation-inventory-autonomy-workspace-grid">
{stations.map((station, index) => {
const color = toneColor(station.tone)
return (
<div
key={station.key}
className={`automation-inventory-autonomy-workspace-station automation-inventory-autonomy-workspace-station-${index}`}
style={{
padding: 11,
border: `0.5px solid ${color}44`,
borderRadius: 8,
background: 'rgba(255,255,255,0.93)',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
gap: 10,
minWidth: 0,
minHeight: 92,
boxShadow: '0 8px 20px rgba(20, 20, 19, 0.05)',
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, minWidth: 0 }}>
<div style={{
width: 30,
height: 30,
borderRadius: 7,
border: `0.5px solid ${color}55`,
background: `${color}12`,
color,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
}}>
{station.icon}
</div>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 22, fontWeight: 800, lineHeight: 1, color }}>
{station.value}
</span>
</div>
<SmallLabel>{station.label}</SmallLabel>
</div>
)
})}
</div>
</div>
)
}
function AutonomousRuntimeControlReadbackGrid({
control,
t,
@@ -5782,6 +6013,99 @@ export function AutomationInventoryTab() {
const currentAutonomyHardBlockers = autonomousRuntimeControl?.hard_blockers ?? []
const currentAutonomyPolicy = autonomousRuntimeControl?.current_policy
const currentAutonomySwitches = autonomousRuntimeControl?.runtime_switches
const currentAutonomyRuntimeReadback = autonomousRuntimeControl?.runtime_receipt_readback
const currentAutonomyLoopLedger = currentAutonomyRuntimeReadback?.autonomous_execution_loop_ledger
const currentAutonomyLoopClosed = currentAutonomyLoopLedger?.closed === true
const currentAutonomyStageById = new Map((currentAutonomyLoopLedger?.stages ?? []).map(stage => [stage.stage_id, stage]))
const currentAutonomyFlowStages = [
{
key: 'candidate',
label: t('globalControl.currentAutonomy.flow.candidate'),
stage: currentAutonomyStageById.get('candidate'),
icon: <Target size={15} />,
},
{
key: 'checkMode',
label: t('globalControl.currentAutonomy.flow.checkMode'),
stage: currentAutonomyStageById.get('check_mode'),
icon: <ShieldCheck size={15} />,
},
{
key: 'apply',
label: t('globalControl.currentAutonomy.flow.apply'),
stage: currentAutonomyStageById.get('controlled_apply'),
icon: <ClipboardCheck size={15} />,
},
{
key: 'verifier',
label: t('globalControl.currentAutonomy.flow.verifier'),
stage: currentAutonomyStageById.get('post_apply_verifier'),
icon: <Gauge size={15} />,
},
{
key: 'learning',
label: t('globalControl.currentAutonomy.flow.learning'),
stage: currentAutonomyStageById.get('km_playbook_writeback'),
icon: <BookOpenCheck size={15} />,
},
{
key: 'telegram',
label: t('globalControl.currentAutonomy.flow.telegram'),
stage: currentAutonomyStageById.get('telegram_receipt'),
icon: <BellRing size={15} />,
},
]
const currentAutonomyReceiptStats = [
{
key: 'apply',
label: t('globalControl.currentAutonomy.cockpit.apply'),
value: autonomousRuntimeControl?.rollups.live_ansible_apply_executed_count ?? 0,
detail: t('globalControl.currentAutonomy.cockpit.receiptDetail'),
tone: (autonomousRuntimeControl?.rollups.live_ansible_apply_executed_count ?? 0) > 0 ? 'ok' as const : 'warn' as const,
icon: <ClipboardCheck size={15} />,
},
{
key: 'verifier',
label: t('globalControl.currentAutonomy.cockpit.verifier'),
value: autonomousRuntimeControl?.rollups.live_post_apply_verifier_count ?? 0,
detail: t('globalControl.currentAutonomy.cockpit.verifierDetail'),
tone: (autonomousRuntimeControl?.rollups.live_post_apply_verifier_count ?? 0) > 0 ? 'ok' as const : 'warn' as const,
icon: <Gauge size={15} />,
},
{
key: 'km',
label: t('globalControl.currentAutonomy.cockpit.km'),
value: autonomousRuntimeControl?.rollups.live_km_writeback_count ?? 0,
detail: t('globalControl.currentAutonomy.cockpit.kmDetail'),
tone: (autonomousRuntimeControl?.rollups.live_km_writeback_count ?? 0) > 0 ? 'ok' as const : 'warn' as const,
icon: <BookOpenCheck size={15} />,
},
{
key: 'telegram',
label: t('globalControl.currentAutonomy.cockpit.telegram'),
value: autonomousRuntimeControl?.rollups.live_telegram_receipt_count ?? 0,
detail: t('globalControl.currentAutonomy.cockpit.telegramDetail'),
tone: (autonomousRuntimeControl?.rollups.live_telegram_receipt_count ?? 0) > 0 ? 'ok' as const : 'warn' as const,
icon: <BellRing size={15} />,
},
]
const currentAutonomyRiskRows = [
{
key: 'low',
label: t('globalControl.currentAutonomy.policy.low'),
enabled: currentAutonomyPolicy?.low_risk_controlled_apply_allowed === true,
},
{
key: 'medium',
label: t('globalControl.currentAutonomy.policy.medium'),
enabled: currentAutonomyPolicy?.medium_risk_controlled_apply_allowed === true,
},
{
key: 'high',
label: t('globalControl.currentAutonomy.policy.high'),
enabled: currentAutonomyPolicy?.high_risk_controlled_apply_allowed === true,
},
]
const globalControlRunwayRows: Array<{
key: string
label: string
@@ -6592,9 +6916,19 @@ export function AutomationInventoryTab() {
{autonomousRuntimeControl ? (
<GlassCard variant="subtle" padding="md">
<div style={{ display: 'flex', flexDirection: 'column', gap: 14, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, minWidth: 0 }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 }} className="automation-inventory-autonomy-cockpit">
<div style={{
display: 'grid',
gridTemplateColumns: 'minmax(0, 1fr) auto',
gap: 12,
alignItems: 'start',
padding: 14,
border: '0.5px solid #cfe7d7',
borderRadius: 8,
background: 'linear-gradient(135deg, rgba(248,255,250,0.96), rgba(255,255,255,0.98))',
minWidth: 0,
}} className="automation-inventory-autonomy-cockpit-header">
<div style={{ display: 'grid', gridTemplateColumns: '38px minmax(0, 1fr)', gap: 10, alignItems: 'start', minWidth: 0 }}>
<div style={{
width: 38,
height: 38,
@@ -6610,138 +6944,143 @@ export function AutomationInventoryTab() {
<BellRing size={18} />
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 5, minWidth: 0 }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 18, fontWeight: 760, color: '#141413', lineHeight: 1.15, overflowWrap: 'anywhere' }}>
{t('globalControl.currentAutonomy.title')}
<SmallLabel>{t('globalControl.currentAutonomy.cockpit.liveLabel')}</SmallLabel>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 22, fontWeight: 780, color: '#141413', lineHeight: 1.05, overflowWrap: 'anywhere' }}>
{t('globalControl.currentAutonomy.cockpit.title')}
</span>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, color: '#4f6156', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
{autonomousRuntimeControl.program_status.status_note}
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, color: '#4f6156', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{t('globalControl.currentAutonomy.cockpit.subtitle')}
</span>
</div>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6, minWidth: 0 }}>
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6, minWidth: 0, maxWidth: 460 }}>
<Chip value={autonomousRuntimeControl.program_status.current_task_id} />
<Chip value={t('globalControl.currentAutonomy.badges.override')} />
<Chip value={currentAutonomyRuntimeReadback?.db_read_status === 'ok' ? t('globalControl.currentAutonomy.cockpit.dbOk') : t('globalControl.currentAutonomy.cockpit.dbReview')} />
<Chip value={t('globalControl.currentAutonomy.badges.gateway')} muted={autonomousRuntimeControl.rollups.telegram_gateway_delivery_enabled_count === 0} />
<Chip value={`${t('globalControl.currentAutonomy.badges.deployMarker')}: ${autonomousRuntimeControl.program_status.deploy_readback_marker}`} muted />
<Chip value={t('globalControl.currentAutonomy.badges.override')} muted />
</div>
</div>
<AutonomousRuntimeControlReadbackGrid control={autonomousRuntimeControl} t={t} />
<AutonomyWorkspaceScene
stations={currentAutonomyReceiptStats}
completion={autonomousRuntimeControl.program_status.implementation_completion_percent}
status={currentAutonomyLoopClosed ? t('globalControl.currentAutonomy.cockpit.closed') : t('globalControl.currentAutonomy.cockpit.review')}
/>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(128px, 1fr))', gap: 10 }} className="automation-inventory-global-control-kpi-grid">
<MetricCard label={t('globalControl.currentAutonomy.metrics.completion')} value={`${autonomousRuntimeControl.program_status.implementation_completion_percent}%`} tone="ok" icon={<Gauge size={16} />} />
<MetricCard label={t('globalControl.currentAutonomy.metrics.riskTiers')} value={`${autonomousRuntimeControl.rollups.automated_risk_tier_count}/3`} tone={autonomousRuntimeControl.rollups.automated_risk_tier_count === 3 ? 'ok' : 'warn'} icon={<ShieldCheck size={16} />} />
<MetricCard label={t('globalControl.currentAutonomy.metrics.reports')} value={autonomousRuntimeControl.rollups.report_cadence_enabled_count} tone="ok" icon={<CalendarClock size={16} />} />
<MetricCard label={t('globalControl.currentAutonomy.metrics.gateway')} value={autonomousRuntimeControl.rollups.telegram_gateway_delivery_enabled_count} tone="ok" icon={<BellRing size={16} />} />
<MetricCard label={t('globalControl.currentAutonomy.metrics.executor')} value={autonomousRuntimeControl.rollups.controlled_executor_operation_receipt_count} tone="ok" icon={<ClipboardCheck size={16} />} />
<MetricCard label={t('globalControl.currentAutonomy.metrics.hardBlockers')} value={autonomousRuntimeControl.rollups.hard_blocker_count} tone="warn" icon={<ShieldAlert size={16} />} />
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(280px, 0.9fr) minmax(0, 1.1fr)', gap: 12 }} className="automation-inventory-autonomy-cockpit-hero-grid">
<div style={{ padding: 14, border: '0.5px solid #d9eadf', borderRadius: 8, background: '#fff', display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
<SmallLabel>{t('globalControl.currentAutonomy.metrics.completion')}</SmallLabel>
<Chip value={t('globalControl.currentAutonomy.cockpit.production')} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 0.75fr) minmax(0, 1.25fr)', gap: 14, alignItems: 'end' }} className="automation-inventory-autonomy-cockpit-score-grid">
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 52, fontWeight: 800, color: '#15803d', lineHeight: 0.95 }}>
{autonomousRuntimeControl.program_status.implementation_completion_percent}%
</span>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<div style={{ height: 8, borderRadius: 999, background: '#edf3ea', overflow: 'hidden' }}>
<div style={{ width: `${Math.min(100, autonomousRuntimeControl.program_status.implementation_completion_percent)}%`, height: '100%', background: '#15803d' }} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#5c675d', lineHeight: 1.35, overflowWrap: 'anywhere' }}>
{autonomousRuntimeControl.program_status.deploy_readback_marker}
</span>
</div>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={`${t('globalControl.currentAutonomy.cockpit.workItems')}: ${autonomousRuntimeControl.rollups.live_work_item_completed_count ?? 0}`} />
<Chip value={`${t('globalControl.currentAutonomy.metrics.hardBlockers')}: ${autonomousRuntimeControl.rollups.hard_blocker_count}`} muted />
<Chip value={`${t('globalControl.currentAutonomy.cockpit.consumer')}: ${autonomousRuntimeControl.rollups.live_log_controlled_writeback_consumer_apply_receipt_count ?? 0}`} muted />
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 10 }} className="automation-inventory-autonomy-cockpit-metric-grid">
{currentAutonomyReceiptStats.map(stat => (
<AutonomyCockpitMetric
key={stat.key}
label={stat.label}
value={stat.value}
detail={stat.detail}
tone={stat.tone}
icon={stat.icon}
/>
))}
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.2fr) minmax(260px, 0.8fr)', gap: 12 }} className="automation-inventory-global-control-grid">
<div style={{ padding: 12, border: '0.5px solid #cfe7d7', borderRadius: 7, background: '#f8fffa', display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
<div style={{ padding: 12, border: '0.5px solid #d7e5d8', borderRadius: 8, background: '#f9fff9', display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
<SmallLabel>{t('globalControl.currentAutonomy.cockpit.loopTitle')}</SmallLabel>
<Chip value={currentAutonomyLoopClosed ? t('globalControl.currentAutonomy.cockpit.closed') : t('globalControl.currentAutonomy.cockpit.review')} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, minmax(0, 1fr))', gap: 9 }} className="automation-inventory-autonomy-cockpit-flow-grid">
{currentAutonomyFlowStages.map(row => (
<AutonomyFlowStep
key={row.key}
label={row.label}
status={row.stage?.status ?? t('globalControl.currentAutonomy.cockpit.missing')}
detail={row.stage?.writes_runtime_state ? t('globalControl.currentAutonomy.cockpit.runtimeWrite') : t('globalControl.currentAutonomy.cockpit.noRuntimeWrite')}
tone={autonomyStageTone(row.stage, currentAutonomyLoopClosed)}
icon={row.icon}
/>
))}
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 12 }} className="automation-inventory-autonomy-cockpit-control-grid">
<div style={{ padding: 12, border: '0.5px solid #d7e2ea', borderRadius: 8, background: '#f9fcff', display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
<SmallLabel>{t('globalControl.currentAutonomy.policyTitle')}</SmallLabel>
<Chip value={autonomousRuntimeControl.program_status.runtime_authority} muted />
<SmallLabel>{t('globalControl.currentAutonomy.cockpit.riskTitle')}</SmallLabel>
<Chip value={`${autonomousRuntimeControl.rollups.automated_risk_tier_count}/3`} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 8 }} className="automation-inventory-global-control-pipeline-grid">
<GateMatrixRow
label={t('globalControl.currentAutonomy.policy.low')}
value={currentAutonomyPolicy?.low_risk_controlled_apply_allowed ? 'on' : 'off'}
detail={t('globalControl.currentAutonomy.policy.noOwnerReview', { value: String(currentAutonomyPolicy?.owner_review_required_for_low_medium_high) })}
tone={currentAutonomyPolicy?.low_risk_controlled_apply_allowed ? 'ok' : 'warn'}
/>
<GateMatrixRow
label={t('globalControl.currentAutonomy.policy.medium')}
value={currentAutonomyPolicy?.medium_risk_controlled_apply_allowed ? 'on' : 'off'}
detail={t('globalControl.currentAutonomy.policy.verifier', { value: String(currentAutonomyPolicy?.post_apply_verifier_required === true) })}
tone={currentAutonomyPolicy?.medium_risk_controlled_apply_allowed ? 'ok' : 'warn'}
/>
<GateMatrixRow
label={t('globalControl.currentAutonomy.policy.high')}
value={currentAutonomyPolicy?.high_risk_controlled_apply_allowed ? 'on' : 'off'}
detail={t('globalControl.currentAutonomy.policy.km', { value: String(currentAutonomyPolicy?.km_learning_writeback_required === true) })}
tone={currentAutonomyPolicy?.high_risk_controlled_apply_allowed ? 'ok' : 'warn'}
/>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 8 }}>
{currentAutonomyCadences.map(cadence => (
<div key={cadence.cadence} style={{ padding: 10, border: '0.5px solid #d8e8df', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 6, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'flex-start' }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 760, color: '#141413', lineHeight: 1.2, overflowWrap: 'anywhere' }}>
{cadence.display_name}
</span>
<Chip value={cadence.telegram_gateway_delivery_enabled ? 'Gateway on' : 'Gateway off'} muted={!cadence.telegram_gateway_delivery_enabled} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#50665a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{cadence.schedule}
</span>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#50665a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{cadence.worker}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 8 }} className="automation-inventory-autonomy-cockpit-risk-grid">
{currentAutonomyRiskRows.map(row => (
<div key={row.key} style={{ padding: 10, border: `0.5px solid ${row.enabled ? '#22C55E44' : '#F59E0B44'}`, borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
<SmallLabel>{row.label}</SmallLabel>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 20, fontWeight: 780, color: row.enabled ? '#22C55E' : '#F59E0B', lineHeight: 1 }}>
{row.enabled ? t('globalControl.currentAutonomy.cockpit.on') : t('globalControl.currentAutonomy.cockpit.off')}
</span>
</div>
))}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={currentAutonomyPolicy?.owner_review_required_for_low_medium_high ? t('globalControl.currentAutonomy.cockpit.ownerRequired') : t('globalControl.currentAutonomy.cockpit.ownerNotRequired')} muted />
<Chip value={currentAutonomyPolicy?.critical_break_glass_required ? t('globalControl.currentAutonomy.cockpit.criticalBreakGlass') : t('globalControl.currentAutonomy.cockpit.criticalReview')} muted />
<Chip value={`${t('globalControl.currentAutonomy.overrideTitle')}: ${currentAutonomyOverrides.length}`} muted />
</div>
</div>
<div style={{ padding: 12, border: '0.5px solid #e5dbc7', borderRadius: 7, background: '#fffdf7', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('globalControl.currentAutonomy.runtimeTitle')}</SmallLabel>
<GateMatrixRow
label={t('globalControl.currentAutonomy.runtime.checkMode')}
value={currentAutonomySwitches?.ansible_check_mode_worker_enabled ? 'on' : 'off'}
detail={`interval=${currentAutonomySwitches?.ansible_check_mode_interval_seconds ?? '--'}s batch=${currentAutonomySwitches?.ansible_check_mode_batch_limit ?? '--'}`}
tone={currentAutonomySwitches?.ansible_check_mode_worker_enabled ? 'ok' : 'warn'}
/>
<GateMatrixRow
label={t('globalControl.currentAutonomy.runtime.apply')}
value={currentAutonomySwitches?.ansible_controlled_apply_enabled ? 'on' : 'off'}
detail={(currentAutonomySwitches?.ansible_controlled_apply_allowed_risk_levels ?? []).join(', ') || '--'}
tone={currentAutonomySwitches?.ansible_controlled_apply_enabled ? 'ok' : 'warn'}
/>
<GateMatrixRow
label={t('globalControl.currentAutonomy.runtime.botApi')}
value={currentAutonomyPolicy?.direct_bot_api_allowed ? 'on' : 'off'}
detail={t('globalControl.currentAutonomy.runtime.gatewayOnly')}
tone={currentAutonomyPolicy?.direct_bot_api_allowed ? 'danger' : 'ok'}
/>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 10 }} className="automation-inventory-global-control-runway-grid">
<div style={{ padding: 12, border: '0.5px solid #d7d2f4', borderRadius: 7, background: '#fbfaff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('globalControl.currentAutonomy.executorTitle')}</SmallLabel>
{currentAutonomyExecutorReceipts.slice(0, 5).map(receipt => (
<GateMatrixRow
key={receipt.operation_type}
label={receipt.operation_type}
value={receipt.writes_runtime_state ? 'write' : 'dry'}
detail={`${receipt.owner_agent}: ${receipt.purpose}`}
tone={receipt.writes_runtime_state ? 'warn' : 'ok'}
<div style={{ padding: 12, border: '0.5px solid #e5dbc7', borderRadius: 8, background: '#fffdf7', display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
<SmallLabel>{t('globalControl.currentAutonomy.runtimeTitle')}</SmallLabel>
<Chip value={(currentAutonomySwitches?.ansible_controlled_apply_allowed_risk_levels ?? []).join(', ') || '--'} muted />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 8 }} className="automation-inventory-autonomy-cockpit-runtime-grid">
<AutonomyCockpitMetric
label={t('globalControl.currentAutonomy.runtime.checkMode')}
value={currentAutonomySwitches?.ansible_check_mode_worker_enabled ? t('globalControl.currentAutonomy.cockpit.on') : t('globalControl.currentAutonomy.cockpit.off')}
detail={`${currentAutonomySwitches?.ansible_check_mode_interval_seconds ?? '--'}s`}
tone={currentAutonomySwitches?.ansible_check_mode_worker_enabled ? 'ok' : 'warn'}
icon={<ShieldCheck size={15} />}
/>
))}
</div>
<div style={{ padding: 12, border: '0.5px solid #d7e2ea', borderRadius: 7, background: '#f9fcff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('globalControl.currentAutonomy.overrideTitle')}</SmallLabel>
{currentAutonomyOverrides.slice(0, 4).map(row => (
<GateMatrixRow
key={row.legacy_area}
label={row.legacy_area}
value={row.current_effect}
detail={row.new_behavior}
tone="ok"
<AutonomyCockpitMetric
label={t('globalControl.currentAutonomy.runtime.apply')}
value={currentAutonomySwitches?.ansible_controlled_apply_enabled ? t('globalControl.currentAutonomy.cockpit.on') : t('globalControl.currentAutonomy.cockpit.off')}
detail={`${t('globalControl.currentAutonomy.executorTitle')}: ${currentAutonomyExecutorReceipts.length}`}
tone={currentAutonomySwitches?.ansible_controlled_apply_enabled ? 'ok' : 'warn'}
icon={<Route size={15} />}
/>
))}
</div>
<div style={{ padding: 12, border: '0.5px solid #f1d4d4', borderRadius: 7, background: '#fffafa', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('globalControl.currentAutonomy.hardBlockerTitle')}</SmallLabel>
{currentAutonomyHardBlockers.slice(0, 5).map(blocker => (
<GateMatrixRow
key={blocker}
label={blocker}
value="blocked"
detail={t('globalControl.currentAutonomy.hardBlockerDetail')}
tone="warn"
<AutonomyCockpitMetric
label={t('globalControl.currentAutonomy.metrics.reports')}
value={currentAutonomyCadences.length}
detail={currentAutonomyCadences.map(cadence => cadence.cadence).join(' / ') || '--'}
tone={currentAutonomyCadences.length > 0 ? 'ok' : 'warn'}
icon={<CalendarClock size={15} />}
/>
))}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={`${t('globalControl.currentAutonomy.hardBlockerTitle')}: ${currentAutonomyHardBlockers.length}`} muted />
<Chip value={currentAutonomyPolicy?.direct_bot_api_allowed ? t('globalControl.currentAutonomy.runtime.botApi') : t('globalControl.currentAutonomy.runtime.gatewayOnly')} muted={!currentAutonomyPolicy?.direct_bot_api_allowed} />
</div>
</div>
</div>
</div>
@@ -20187,7 +20526,117 @@ export function AutomationInventoryTab() {
white-space: normal !important;
}
.automation-inventory-autonomy-workspace-rail {
position: absolute;
inset: 28px;
border: 0.5px solid rgba(21, 128, 61, 0.18);
border-radius: 8px;
background:
linear-gradient(90deg, rgba(21, 128, 61, 0.08) 1px, transparent 1px),
linear-gradient(0deg, rgba(21, 128, 61, 0.06) 1px, transparent 1px);
background-size: 44px 44px;
opacity: 0.88;
}
.automation-inventory-autonomy-workspace-packet {
position: absolute;
z-index: 1;
width: 10px;
height: 10px;
border-radius: 3px;
background: #15803d;
box-shadow: 0 0 0 4px rgba(21, 128, 61, 0.12), 0 8px 18px rgba(21, 128, 61, 0.22);
}
.automation-inventory-autonomy-workspace-packet-a {
top: 48%;
left: 10%;
animation: autonomy-packet-x 4.8s linear infinite;
}
.automation-inventory-autonomy-workspace-packet-b {
top: 22%;
left: 48%;
animation: autonomy-packet-y 5.4s linear infinite;
animation-delay: 0.8s;
}
.automation-inventory-autonomy-workspace-packet-c {
top: 72%;
left: 16%;
animation: autonomy-packet-x 6.2s linear infinite;
animation-delay: 1.6s;
}
.automation-inventory-autonomy-workspace-station {
animation: autonomy-station-breathe 3.8s ease-in-out infinite;
}
.automation-inventory-autonomy-workspace-station-1 {
animation-delay: 0.25s;
}
.automation-inventory-autonomy-workspace-station-2 {
animation-delay: 0.5s;
}
.automation-inventory-autonomy-workspace-station-3 {
animation-delay: 0.75s;
}
@keyframes autonomy-packet-x {
0% {
left: 10%;
opacity: 0;
}
12% {
opacity: 1;
}
88% {
opacity: 1;
}
100% {
left: 88%;
opacity: 0;
}
}
@keyframes autonomy-packet-y {
0% {
top: 18%;
opacity: 0;
}
12% {
opacity: 1;
}
88% {
opacity: 1;
}
100% {
top: 78%;
opacity: 0;
}
}
@keyframes autonomy-station-breathe {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-2px);
}
}
@media (max-width: 900px) {
.automation-inventory-autonomy-cockpit-header,
.automation-inventory-autonomy-cockpit-hero-grid,
.automation-inventory-autonomy-cockpit-score-grid,
.automation-inventory-autonomy-cockpit-metric-grid,
.automation-inventory-autonomy-cockpit-flow-grid,
.automation-inventory-autonomy-cockpit-control-grid,
.automation-inventory-autonomy-cockpit-risk-grid,
.automation-inventory-autonomy-cockpit-runtime-grid,
.automation-inventory-autonomy-workspace-grid,
.automation-inventory-visual-grid,
.automation-inventory-stage-grid,
.automation-inventory-visual-factor-grid,
@@ -20269,6 +20718,25 @@ export function AutomationInventoryTab() {
overflow-x: hidden;
}
.automation-inventory-autonomy-workspace-scene {
min-height: 430px !important;
}
.automation-inventory-autonomy-workspace-console {
position: relative !important;
inset: auto !important;
transform: none !important;
width: 100% !important;
height: auto !important;
min-height: 104px;
margin-bottom: 10px;
}
.automation-inventory-autonomy-workspace-rail,
.automation-inventory-autonomy-workspace-packet {
display: none;
}
.automation-inventory-tab-root button,
.automation-inventory-tab-root [role='button'],
.automation-inventory-tab-root span,

View File

@@ -2213,6 +2213,11 @@ export interface AiAgentAutonomousRuntimeControlSnapshot {
live_km_writeback_count?: number
live_post_apply_verifier_count?: number
live_telegram_receipt_count?: number
live_log_controlled_writeback_consumer_apply_receipt_count?: number
live_log_controlled_writeback_consumer_dispatch_ledger_count?: number
live_log_controlled_writeback_runtime_target_write_count?: number
live_work_item_completed_count?: number
live_work_item_blocked_count?: number
mcp_sensor_count?: number
rag_context_query_count?: number
playbook_decision_class_count?: number