|
|
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
|
|
type AiAgentLiveReadModelGateSnapshot,
|
|
|
|
|
type AiAgentOwnerApprovedFixtureDryRunSnapshot,
|
|
|
|
|
type AiAgentOwnerApprovedLearningDryRunSnapshot,
|
|
|
|
|
type AiAgentOperationPermissionModelSnapshot,
|
|
|
|
|
type AiAgentPostWriteVerifierPackageSnapshot,
|
|
|
|
|
type AiAgentProactiveOperationsContractSnapshot,
|
|
|
|
|
type AiAgentRedisDryRunGateSnapshot,
|
|
|
|
|
@@ -346,6 +347,7 @@ export function AutomationInventoryTab() {
|
|
|
|
|
const [reportRuntimeDryRun, setReportRuntimeDryRun] = useState<AiAgentReportRuntimeDryRunSnapshot | null>(null)
|
|
|
|
|
const [reportRuntimeFixtureReadback, setReportRuntimeFixtureReadback] = useState<AiAgentReportRuntimeFixtureReadbackSnapshot | null>(null)
|
|
|
|
|
const [runtimeWorkerShadowGate, setRuntimeWorkerShadowGate] = useState<AiAgentRuntimeWorkerShadowGateSnapshot | null>(null)
|
|
|
|
|
const [operationPermissionModel, setOperationPermissionModel] = useState<AiAgentOperationPermissionModelSnapshot | null>(null)
|
|
|
|
|
const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState<AiAgentReportTruthActionabilityReviewSnapshot | null>(null)
|
|
|
|
|
const [ownerDryRunPackage, setOwnerDryRunPackage] = useState<AiAgentOwnerApprovedFixtureDryRunSnapshot | null>(null)
|
|
|
|
|
const [hostStatefulInventory, setHostStatefulInventory] = useState<AiAgentHostStatefulVersionInventorySnapshot | null>(null)
|
|
|
|
|
@@ -383,6 +385,7 @@ export function AutomationInventoryTab() {
|
|
|
|
|
apiClient.getAiAgentReportRuntimeDryRun(),
|
|
|
|
|
apiClient.getAiAgentReportRuntimeFixtureReadback(),
|
|
|
|
|
apiClient.getAiAgentRuntimeWorkerShadowGate(),
|
|
|
|
|
apiClient.getAiAgentOperationPermissionModel(),
|
|
|
|
|
apiClient.getAiAgentReportTruthActionabilityReview(),
|
|
|
|
|
apiClient.getAiAgentOwnerApprovedFixtureDryRun(),
|
|
|
|
|
apiClient.getAiAgentHostStatefulVersionInventory(),
|
|
|
|
|
@@ -419,6 +422,7 @@ export function AutomationInventoryTab() {
|
|
|
|
|
reportRuntimeDryRunResult,
|
|
|
|
|
reportRuntimeFixtureReadbackResult,
|
|
|
|
|
runtimeWorkerShadowGateResult,
|
|
|
|
|
operationPermissionModelResult,
|
|
|
|
|
reportTruthActionabilityReviewResult,
|
|
|
|
|
ownerDryRunPackageResult,
|
|
|
|
|
hostStatefulInventoryResult,
|
|
|
|
|
@@ -452,6 +456,7 @@ export function AutomationInventoryTab() {
|
|
|
|
|
setReportRuntimeDryRun(reportRuntimeDryRunResult.status === 'fulfilled' ? reportRuntimeDryRunResult.value : null)
|
|
|
|
|
setReportRuntimeFixtureReadback(reportRuntimeFixtureReadbackResult.status === 'fulfilled' ? reportRuntimeFixtureReadbackResult.value : null)
|
|
|
|
|
setRuntimeWorkerShadowGate(runtimeWorkerShadowGateResult.status === 'fulfilled' ? runtimeWorkerShadowGateResult.value : null)
|
|
|
|
|
setOperationPermissionModel(operationPermissionModelResult.status === 'fulfilled' ? operationPermissionModelResult.value : null)
|
|
|
|
|
setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null)
|
|
|
|
|
setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null)
|
|
|
|
|
setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null)
|
|
|
|
|
@@ -483,6 +488,7 @@ export function AutomationInventoryTab() {
|
|
|
|
|
reportRuntimeDryRunResult,
|
|
|
|
|
reportRuntimeFixtureReadbackResult,
|
|
|
|
|
runtimeWorkerShadowGateResult,
|
|
|
|
|
operationPermissionModelResult,
|
|
|
|
|
reportTruthActionabilityReviewResult,
|
|
|
|
|
ownerDryRunPackageResult,
|
|
|
|
|
hostStatefulInventoryResult,
|
|
|
|
|
@@ -1047,6 +1053,42 @@ export function AutomationInventoryTab() {
|
|
|
|
|
.slice(0, 6)
|
|
|
|
|
}, [runtimeWorkerShadowGate])
|
|
|
|
|
|
|
|
|
|
const visibleOperationPermissionCategories = useMemo(() => {
|
|
|
|
|
if (!operationPermissionModel) return []
|
|
|
|
|
const lanePriority = {
|
|
|
|
|
explicitly_blocked: 0,
|
|
|
|
|
human_approval_required: 1,
|
|
|
|
|
proposal_only: 2,
|
|
|
|
|
no_write_replay_allowed: 3,
|
|
|
|
|
observe_only: 4,
|
|
|
|
|
} as Record<string, number>
|
|
|
|
|
const riskPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record<string, number>
|
|
|
|
|
return [...operationPermissionModel.operation_categories]
|
|
|
|
|
.sort((a, b) => {
|
|
|
|
|
const leftLane = lanePriority[a.permission_lane] ?? 5
|
|
|
|
|
const rightLane = lanePriority[b.permission_lane] ?? 5
|
|
|
|
|
if (leftLane !== rightLane) return leftLane - rightLane
|
|
|
|
|
const leftRisk = riskPriority[a.risk_tier] ?? 4
|
|
|
|
|
const rightRisk = riskPriority[b.risk_tier] ?? 4
|
|
|
|
|
if (leftRisk !== rightRisk) return leftRisk - rightRisk
|
|
|
|
|
return a.category_id.localeCompare(b.category_id)
|
|
|
|
|
})
|
|
|
|
|
.slice(0, 8)
|
|
|
|
|
}, [operationPermissionModel])
|
|
|
|
|
|
|
|
|
|
const visibleOperationPermissionGates = useMemo(() => {
|
|
|
|
|
if (!operationPermissionModel) return []
|
|
|
|
|
const statusPriority = { blocked_by_policy: 0, blocked_until_evidence: 1, ready_for_review: 2 } as Record<string, number>
|
|
|
|
|
return [...operationPermissionModel.gate_transitions]
|
|
|
|
|
.sort((a, b) => {
|
|
|
|
|
const left = statusPriority[a.current_status] ?? 3
|
|
|
|
|
const right = statusPriority[b.current_status] ?? 3
|
|
|
|
|
if (left !== right) return left - right
|
|
|
|
|
return a.gate_id.localeCompare(b.gate_id)
|
|
|
|
|
})
|
|
|
|
|
.slice(0, 8)
|
|
|
|
|
}, [operationPermissionModel])
|
|
|
|
|
|
|
|
|
|
const visibleReportTruthFindings = useMemo(() => {
|
|
|
|
|
if (!reportTruthActionabilityReview) return []
|
|
|
|
|
const priority = { critical: 0, high: 1, medium: 2, low: 3 } as Record<string, number>
|
|
|
|
|
@@ -1266,7 +1308,7 @@ export function AutomationInventoryTab() {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
|
|
|
|
|
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
|
|
|
|
|
return (
|
|
|
|
|
<div style={{ padding: 20 }}>
|
|
|
|
|
<GlassCard variant="subtle" padding="lg">
|
|
|
|
|
@@ -1460,6 +1502,20 @@ export function AutomationInventoryTab() {
|
|
|
|
|
const runtimeShadowQueueWrites = runtimeWorkerShadowGate.rollups.gateway_queue_write_count
|
|
|
|
|
const runtimeShadowSends = runtimeWorkerShadowGate.rollups.telegram_send_count
|
|
|
|
|
const runtimeShadowProductionWrites = runtimeWorkerShadowGate.rollups.production_write_count
|
|
|
|
|
const operationPermissionOverall = operationPermissionModel.program_status.overall_completion_percent
|
|
|
|
|
const operationPermissionCategories = operationPermissionModel.rollups.operation_category_count
|
|
|
|
|
const operationPermissionObserveOnly = operationPermissionModel.rollups.observe_only_category_count
|
|
|
|
|
const operationPermissionNoWrite = operationPermissionModel.rollups.no_write_replay_allowed_category_count
|
|
|
|
|
const operationPermissionProposalOnly = operationPermissionModel.rollups.proposal_only_category_count
|
|
|
|
|
const operationPermissionHumanApproval = operationPermissionModel.rollups.human_approval_required_category_count
|
|
|
|
|
const operationPermissionBlocked = operationPermissionModel.rollups.explicitly_blocked_category_count
|
|
|
|
|
const operationPermissionGates = operationPermissionModel.rollups.gate_transition_count
|
|
|
|
|
const operationPermissionRuntimeRuns = operationPermissionModel.rollups.runtime_execution_count
|
|
|
|
|
const operationPermissionQueueWrites = operationPermissionModel.rollups.gateway_queue_write_count
|
|
|
|
|
const operationPermissionSends = operationPermissionModel.rollups.telegram_send_count
|
|
|
|
|
const operationPermissionProductionWrites = operationPermissionModel.rollups.production_write_count
|
|
|
|
|
const operationPermissionSecretReads = operationPermissionModel.rollups.secret_value_read_count
|
|
|
|
|
const operationPermissionDestructive = operationPermissionModel.rollups.destructive_operation_count
|
|
|
|
|
const reportTruthOverall = reportTruthActionabilityReview.program_status.overall_completion_percent
|
|
|
|
|
const reportTruthFindings = reportTruthActionabilityReview.rollups.zero_signal_finding_count
|
|
|
|
|
const reportTruthCritical = reportTruthActionabilityReview.rollups.critical_finding_count
|
|
|
|
|
@@ -2891,6 +2947,189 @@ export function AutomationInventoryTab() {
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ padding: 12, border: '0.5px solid #bedbd0', borderRadius: 7, background: '#f7fffb', display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 }}>
|
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
|
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 7, minWidth: 0 }}>
|
|
|
|
|
<ShieldCheck size={14} style={{ color: '#0f766e' }} />
|
|
|
|
|
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 700, color: '#141413' }}>
|
|
|
|
|
{t('operationPermissionModel.title')}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<Chip
|
|
|
|
|
value={t('operationPermissionModel.source', {
|
|
|
|
|
generated: formatDateTime(operationPermissionModel.generated_at),
|
|
|
|
|
current: operationPermissionModel.program_status.current_task_id,
|
|
|
|
|
next: operationPermissionModel.program_status.next_task_id,
|
|
|
|
|
})}
|
|
|
|
|
muted
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(128px, 1fr))', gap: 10 }} className="automation-inventory-live-read-kpi-grid">
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.overall')} value={`${operationPermissionOverall}%`} tone="warn" icon={<Gauge size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.categories')} value={operationPermissionCategories} tone="ok" icon={<Boxes size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.observeOnly')} value={operationPermissionObserveOnly} tone="ok" icon={<Target size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.noWrite')} value={operationPermissionNoWrite} tone="warn" icon={<RefreshCw size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.proposals')} value={operationPermissionProposalOnly} tone="warn" icon={<FileText size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.humanApproval')} value={operationPermissionHumanApproval} tone={operationPermissionHumanApproval > 0 ? 'danger' : 'ok'} icon={<Lock size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.blocked')} value={operationPermissionBlocked} tone={operationPermissionBlocked > 0 ? 'danger' : 'ok'} icon={<ShieldAlert size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.gates')} value={operationPermissionGates} tone="warn" icon={<Route size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.runtimeRuns')} value={operationPermissionRuntimeRuns} tone={operationPermissionRuntimeRuns === 0 ? 'warn' : 'danger'} icon={<Target size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.queueWrites')} value={operationPermissionQueueWrites} tone={operationPermissionQueueWrites === 0 ? 'warn' : 'danger'} icon={<Database size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.telegramSends')} value={operationPermissionSends} tone={operationPermissionSends === 0 ? 'warn' : 'danger'} icon={<BellRing size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.productionWrites')} value={operationPermissionProductionWrites} tone={operationPermissionProductionWrites === 0 ? 'warn' : 'danger'} icon={<HardDrive size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.secretReads')} value={operationPermissionSecretReads} tone={operationPermissionSecretReads === 0 ? 'warn' : 'danger'} icon={<Lock size={16} />} />
|
|
|
|
|
<MetricCard label={t('operationPermissionModel.metrics.destructive')} value={operationPermissionDestructive} tone={operationPermissionDestructive === 0 ? 'warn' : 'danger'} icon={<ShieldAlert size={16} />} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 12 }} className="automation-inventory-live-read-grid">
|
|
|
|
|
<div style={{ padding: 11, border: '0.5px solid #cce5dc', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
|
|
|
|
|
<SmallLabel>{t('operationPermissionModel.truthTitle')}</SmallLabel>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{operationPermissionModel.operation_permission_truth.truth_note}
|
|
|
|
|
</span>
|
|
|
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.modelReady', { value: String(operationPermissionModel.operation_permission_truth.permission_model_ready) })} />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.matrixReady', { value: String(operationPermissionModel.operation_permission_truth.operation_category_matrix_ready) })} />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.agentMap', { value: String(operationPermissionModel.operation_permission_truth.agent_responsibility_mapping_ready) })} />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.p2Handoff', { value: String(operationPermissionModel.operation_permission_truth.p2_404_shadow_gate_handoff_ready) })} />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ padding: 11, border: '0.5px solid #cce5dc', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
|
|
|
|
|
<SmallLabel>{t('operationPermissionModel.boundaryTitle')}</SmallLabel>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{t('operationPermissionModel.boundarySummary', {
|
|
|
|
|
runtime: operationPermissionRuntimeRuns,
|
|
|
|
|
queue: operationPermissionQueueWrites,
|
|
|
|
|
send: operationPermissionSends,
|
|
|
|
|
write: operationPermissionProductionWrites,
|
|
|
|
|
secret: operationPermissionSecretReads,
|
|
|
|
|
})}
|
|
|
|
|
</span>
|
|
|
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.runtime', { value: String(operationPermissionModel.operation_permission_truth.runtime_execution_enabled) })} muted />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.queueWrite', { value: String(operationPermissionModel.operation_permission_truth.gateway_queue_write_enabled) })} muted />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.send', { value: String(operationPermissionModel.operation_permission_truth.telegram_send_enabled) })} muted />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.productionWrite', { value: String(operationPermissionModel.operation_permission_truth.production_write_enabled) })} muted />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.secret', { value: String(operationPermissionModel.operation_permission_truth.secret_value_read_enabled) })} muted />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.destructive', { value: String(operationPermissionModel.operation_permission_truth.destructive_operation_enabled) })} muted />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(210px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
|
|
|
|
|
{operationPermissionModel.permission_lanes.map(lane => (
|
|
|
|
|
<div key={lane.lane_id} style={{ padding: 10, border: '0.5px solid #cce5dc', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center', minWidth: 0 }}>
|
|
|
|
|
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
|
|
|
|
|
{lane.display_name}
|
|
|
|
|
</span>
|
|
|
|
|
<Chip value={t(`operationPermissionModel.lanes.${lane.lane_id}` as never)} muted />
|
|
|
|
|
</div>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{lane.summary}
|
|
|
|
|
</span>
|
|
|
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
|
|
|
{lane.allowed_outputs.slice(0, 3).map(item => (
|
|
|
|
|
<Chip key={item} value={item} />
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<Chip value={t('operationPermissionModel.labels.nextGate', { value: lane.required_gate_before_promotion })} muted />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
|
|
|
|
|
{visibleOperationPermissionCategories.map(category => (
|
|
|
|
|
<div key={category.category_id} style={{ padding: 10, border: '0.5px solid #cce5dc', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center', minWidth: 0 }}>
|
|
|
|
|
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
|
|
|
|
|
{category.display_name}
|
|
|
|
|
</span>
|
|
|
|
|
<Chip value={redisDryRunValueLabel('agents', category.primary_agent)} muted />
|
|
|
|
|
</div>
|
|
|
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
|
|
|
<Chip value={t(`operationPermissionModel.lanes.${category.permission_lane}` as never)} muted={category.permission_lane !== 'explicitly_blocked'} />
|
|
|
|
|
<Chip value={t(`operationPermissionModel.riskTiers.${category.risk_tier}` as never)} muted={category.risk_tier === 'low'} />
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.liveExecution', { value: String(category.live_execution_allowed) })} muted />
|
|
|
|
|
</div>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{t('operationPermissionModel.labels.requiredEvidence', { value: category.required_evidence.slice(0, 3).join(' / ') })}
|
|
|
|
|
</span>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#b45309', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{t('operationPermissionModel.labels.blockedActions', { value: category.blocked_actions.slice(0, 3).join(' / ') })}
|
|
|
|
|
</span>
|
|
|
|
|
<Chip value={t('operationPermissionModel.labels.nextGate', { value: category.next_gate })} muted />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
|
|
|
|
|
{operationPermissionModel.agent_permission_roles.map(role => (
|
|
|
|
|
<div key={role.agent_id} style={{ padding: 10, border: '0.5px solid #cce5dc', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center', minWidth: 0 }}>
|
|
|
|
|
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
|
|
|
|
|
{role.display_name}
|
|
|
|
|
</span>
|
|
|
|
|
<Chip value={t('operationPermissionModel.labels.liveCount', { count: role.live_action_count_24h })} muted />
|
|
|
|
|
</div>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{role.permission_responsibility}
|
|
|
|
|
</span>
|
|
|
|
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
|
|
|
{role.allowed_lanes.slice(0, 3).map(item => (
|
|
|
|
|
<Chip key={item} value={t(`operationPermissionModel.lanes.${item}` as never)} />
|
|
|
|
|
))}
|
|
|
|
|
{role.blocked_now.slice(0, 2).map(item => (
|
|
|
|
|
<Chip key={item} value={item} muted />
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
|
|
|
|
|
{visibleOperationPermissionGates.map(gate => (
|
|
|
|
|
<div key={gate.gate_id} style={{ padding: 10, border: '0.5px solid #cce5dc', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center', minWidth: 0 }}>
|
|
|
|
|
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
|
|
|
|
|
{gate.display_name}
|
|
|
|
|
</span>
|
|
|
|
|
<Chip value={t(`operationPermissionModel.statuses.${gate.current_status}` as never)} muted={gate.current_status !== 'blocked_by_policy'} />
|
|
|
|
|
</div>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{gate.required_before}
|
|
|
|
|
</span>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#2563eb', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{gate.next_safe_step}
|
|
|
|
|
</span>
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.opensLive', { value: String(gate.opens_live_execution) })} muted />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
|
|
|
|
|
{operationPermissionModel.operator_decision_templates.map(template => (
|
|
|
|
|
<div key={template.template_id} style={{ padding: 10, border: '0.5px solid #cce5dc', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
|
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center', minWidth: 0 }}>
|
|
|
|
|
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
|
|
|
|
|
{template.display_name}
|
|
|
|
|
</span>
|
|
|
|
|
<Chip value={t('operationPermissionModel.labels.reviewRequired', { value: String(template.requires_human_review) })} muted />
|
|
|
|
|
</div>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{template.when_to_use}
|
|
|
|
|
</span>
|
|
|
|
|
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#2563eb', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
|
|
|
|
{template.human_instruction}
|
|
|
|
|
</span>
|
|
|
|
|
<Chip value={t('operationPermissionModel.flags.runtimeAction', { value: String(template.creates_runtime_action) })} muted />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={{ padding: 12, border: '0.5px solid #d8c6a6', borderRadius: 7, background: '#fffdf7', display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 }}>
|
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
|
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 7, minWidth: 0 }}>
|
|
|
|
|
|