feat(ai): 新增 P2-408 中低風險白名單
Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m33s
CD Pipeline / build-and-deploy (push) Successful in 5m2s
CD Pipeline / post-deploy-checks (push) Successful in 1m32s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m33s
CD Pipeline / build-and-deploy (push) Successful in 5m2s
CD Pipeline / post-deploy-checks (push) Successful in 1m32s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
This commit is contained in:
@@ -48,6 +48,7 @@ import {
|
||||
type AiAgentProfessionalTaskExpansionSnapshot,
|
||||
type AiAgentReceiptReadbackOwnerReviewSnapshot,
|
||||
type AiAgentReportNoWriteAnalysisRuntimeSnapshot,
|
||||
type AiAgentLowMediumRiskWhitelistSnapshot,
|
||||
type HostRunawayAiopsLoopReadinessSnapshot,
|
||||
type AiAgentCandidateOperationDryRunEvidenceSnapshot,
|
||||
type AiAgentCriticReviewerResultCaptureSnapshot,
|
||||
@@ -721,6 +722,7 @@ export function AutomationInventoryTab() {
|
||||
const [professionalTaskExpansion, setProfessionalTaskExpansion] = useState<AiAgentProfessionalTaskExpansionSnapshot | null>(null)
|
||||
const [receiptReadbackOwnerReview, setReceiptReadbackOwnerReview] = useState<AiAgentReceiptReadbackOwnerReviewSnapshot | null>(null)
|
||||
const [reportNoWriteAnalysisRuntime, setReportNoWriteAnalysisRuntime] = useState<AiAgentReportNoWriteAnalysisRuntimeSnapshot | null>(null)
|
||||
const [lowMediumRiskWhitelist, setLowMediumRiskWhitelist] = useState<AiAgentLowMediumRiskWhitelistSnapshot | null>(null)
|
||||
const [hostRunawayAiops, setHostRunawayAiops] = useState<HostRunawayAiopsLoopReadinessSnapshot | null>(null)
|
||||
const [proactiveOperations, setProactiveOperations] = useState<AiAgentProactiveOperationsContractSnapshot | null>(null)
|
||||
const [interactionLearningProof, setInteractionLearningProof] = useState<AiAgentInteractionLearningProofSnapshot | null>(null)
|
||||
@@ -814,6 +816,7 @@ export function AutomationInventoryTab() {
|
||||
apiClient.getAiAgentProfessionalTaskExpansion(),
|
||||
apiClient.getAiAgentReceiptReadbackOwnerReview(),
|
||||
apiClient.getAiAgentReportNoWriteAnalysisRuntime(),
|
||||
apiClient.getAiAgentLowMediumRiskWhitelist(),
|
||||
apiClient.getHostRunawayAiopsLoopReadiness(),
|
||||
apiClient.getAiAgentProactiveOperationsContract(),
|
||||
apiClient.getAiAgentInteractionLearningProof(),
|
||||
@@ -900,6 +903,7 @@ export function AutomationInventoryTab() {
|
||||
professionalTaskExpansionResult,
|
||||
receiptReadbackOwnerReviewResult,
|
||||
reportNoWriteAnalysisRuntimeResult,
|
||||
lowMediumRiskWhitelistResult,
|
||||
hostRunawayAiopsResult,
|
||||
proactiveOperationsResult,
|
||||
interactionLearningProofResult,
|
||||
@@ -983,6 +987,7 @@ export function AutomationInventoryTab() {
|
||||
setProfessionalTaskExpansion(professionalTaskExpansionResult.status === 'fulfilled' ? professionalTaskExpansionResult.value : null)
|
||||
setReceiptReadbackOwnerReview(receiptReadbackOwnerReviewResult.status === 'fulfilled' ? receiptReadbackOwnerReviewResult.value : null)
|
||||
setReportNoWriteAnalysisRuntime(reportNoWriteAnalysisRuntimeResult.status === 'fulfilled' ? reportNoWriteAnalysisRuntimeResult.value : null)
|
||||
setLowMediumRiskWhitelist(lowMediumRiskWhitelistResult.status === 'fulfilled' ? lowMediumRiskWhitelistResult.value : null)
|
||||
setHostRunawayAiops(hostRunawayAiopsResult.status === 'fulfilled' ? hostRunawayAiopsResult.value : null)
|
||||
setProactiveOperations(proactiveOperationsResult.status === 'fulfilled' ? proactiveOperationsResult.value : null)
|
||||
setInteractionLearningProof(interactionLearningProofResult.status === 'fulfilled' ? interactionLearningProofResult.value : null)
|
||||
@@ -1068,6 +1073,7 @@ export function AutomationInventoryTab() {
|
||||
professionalTaskExpansionResult,
|
||||
receiptReadbackOwnerReviewResult,
|
||||
reportNoWriteAnalysisRuntimeResult,
|
||||
lowMediumRiskWhitelistResult,
|
||||
hostRunawayAiopsResult,
|
||||
proactiveOperationsResult,
|
||||
interactionLearningProofResult,
|
||||
@@ -1586,6 +1592,40 @@ export function AutomationInventoryTab() {
|
||||
.slice(0, 4)
|
||||
}, [reportNoWriteAnalysisRuntime])
|
||||
|
||||
const visibleLowMediumWhitelistCandidates = useMemo(() => {
|
||||
if (!lowMediumRiskWhitelist) return []
|
||||
const riskPriority = { medium: 0, low: 1 } as Record<string, number>
|
||||
const statusPriority = {
|
||||
owner_review_required_no_live_execution: 0,
|
||||
dry_run_ready_no_live_execution: 1,
|
||||
candidate_only_no_live_execution: 2,
|
||||
} as Record<string, number>
|
||||
return [...lowMediumRiskWhitelist.whitelist_candidates]
|
||||
.sort((a, b) => {
|
||||
const leftRisk = riskPriority[a.risk_tier] ?? 2
|
||||
const rightRisk = riskPriority[b.risk_tier] ?? 2
|
||||
if (leftRisk !== rightRisk) return leftRisk - rightRisk
|
||||
const leftStatus = statusPriority[a.whitelist_status] ?? 3
|
||||
const rightStatus = statusPriority[b.whitelist_status] ?? 3
|
||||
if (leftStatus !== rightStatus) return leftStatus - rightStatus
|
||||
return a.candidate_id.localeCompare(b.candidate_id)
|
||||
})
|
||||
.slice(0, 6)
|
||||
}, [lowMediumRiskWhitelist])
|
||||
|
||||
const visibleLowMediumWhitelistGates = useMemo(() => {
|
||||
if (!lowMediumRiskWhitelist) return []
|
||||
const statusPriority = { owner_review_required: 0, blocked_by_runtime_gate: 1, draft_ready: 2 } as Record<string, number>
|
||||
return [...lowMediumRiskWhitelist.owner_review_gates]
|
||||
.sort((a, b) => {
|
||||
const leftStatus = statusPriority[a.status] ?? 3
|
||||
const rightStatus = statusPriority[b.status] ?? 3
|
||||
if (leftStatus !== rightStatus) return leftStatus - rightStatus
|
||||
return a.gate_id.localeCompare(b.gate_id)
|
||||
})
|
||||
.slice(0, 4)
|
||||
}, [lowMediumRiskWhitelist])
|
||||
|
||||
const visibleReportRuntimeLanes = useMemo(() => {
|
||||
if (!reportRuntimeReadiness) return []
|
||||
const priority = { blocked_by_runtime_gate: 0, ready_for_owner_review: 1 } as Record<string, number>
|
||||
@@ -2518,7 +2558,7 @@ export function AutomationInventoryTab() {
|
||||
)
|
||||
}
|
||||
|
||||
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !hostRunawayAiops || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
|
||||
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !lowMediumRiskWhitelist || !hostRunawayAiops || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
|
||||
return (
|
||||
<div style={{ padding: 20 }}>
|
||||
<GlassCard variant="subtle" padding="lg">
|
||||
@@ -2784,6 +2824,30 @@ export function AutomationInventoryTab() {
|
||||
+ reportNoWriteAnalysisRuntime.rollups.host_write_count
|
||||
+ reportNoWriteAnalysisRuntime.rollups.kubectl_action_count
|
||||
)
|
||||
const lowMediumWhitelistOverall = lowMediumRiskWhitelist.program_status.overall_completion_percent
|
||||
const lowMediumWhitelistCandidates = lowMediumRiskWhitelist.rollups.whitelist_candidate_count
|
||||
const lowMediumWhitelistLow = lowMediumRiskWhitelist.rollups.low_risk_candidate_count
|
||||
const lowMediumWhitelistMedium = lowMediumRiskWhitelist.rollups.medium_risk_candidate_count
|
||||
const lowMediumWhitelistVerifiers = lowMediumRiskWhitelist.rollups.dry_run_verifier_count
|
||||
const lowMediumWhitelistRollback = lowMediumRiskWhitelist.rollups.rollback_proof_count
|
||||
const lowMediumWhitelistAuditReasons = lowMediumRiskWhitelist.rollups.audit_reason_template_count
|
||||
const lowMediumWhitelistHighRedirects = lowMediumRiskWhitelist.rollups.high_risk_redirect_count
|
||||
const lowMediumWhitelistBlocked = lowMediumRiskWhitelist.rollups.blocked_runtime_action_count
|
||||
const lowMediumWhitelistLiveTotal = (
|
||||
lowMediumRiskWhitelist.rollups.auto_worker_run_count
|
||||
+ lowMediumRiskWhitelist.rollups.low_risk_execution_count
|
||||
+ lowMediumRiskWhitelist.rollups.medium_risk_execution_count
|
||||
+ lowMediumRiskWhitelist.rollups.gateway_queue_write_count
|
||||
+ lowMediumRiskWhitelist.rollups.telegram_send_count
|
||||
+ lowMediumRiskWhitelist.rollups.bot_api_call_count
|
||||
+ lowMediumRiskWhitelist.rollups.receipt_production_write_count
|
||||
+ lowMediumRiskWhitelist.rollups.production_write_count
|
||||
+ lowMediumRiskWhitelist.rollups.secret_read_count
|
||||
+ lowMediumRiskWhitelist.rollups.paid_api_call_count
|
||||
+ lowMediumRiskWhitelist.rollups.host_write_count
|
||||
+ lowMediumRiskWhitelist.rollups.kubectl_action_count
|
||||
+ lowMediumRiskWhitelist.rollups.destructive_operation_count
|
||||
)
|
||||
const hostRunawayOverall = hostRunawayAiops.program_status.overall_completion_percent
|
||||
const hostRunawayStages = hostRunawayAiops.rollups.loop_stage_count
|
||||
const hostRunawayAlertLanes = hostRunawayAiops.rollups.alert_lane_count
|
||||
@@ -4824,6 +4888,137 @@ export function AutomationInventoryTab() {
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
<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={{
|
||||
width: 38,
|
||||
height: 38,
|
||||
borderRadius: 8,
|
||||
border: '0.5px solid #16a34a35',
|
||||
background: 'rgba(22,163,74,0.08)',
|
||||
color: '#15803d',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<ShieldCheck 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('lowMediumWhitelist.title')}
|
||||
</span>
|
||||
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, color: '#5c5a55', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
|
||||
{t('lowMediumWhitelist.subtitle', {
|
||||
current: lowMediumRiskWhitelist.program_status.current_task_id,
|
||||
next: lowMediumRiskWhitelist.program_status.next_task_id,
|
||||
candidates: lowMediumWhitelistCandidates,
|
||||
blocked: lowMediumWhitelistBlocked,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6, minWidth: 0 }}>
|
||||
<Chip value={t('lowMediumWhitelist.badges.mode')} />
|
||||
<Chip value={t('lowMediumWhitelist.badges.room', { room: lowMediumRiskWhitelist.telegram_policy.canonical_room })} muted />
|
||||
<Chip value={t('lowMediumWhitelist.badges.live', { count: lowMediumWhitelistLiveTotal })} muted={lowMediumWhitelistLiveTotal === 0} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(112px, 1fr))', gap: 10 }} className="automation-inventory-kpi-grid">
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.overall')} value={`${lowMediumWhitelistOverall}%`} tone="ok" icon={<Gauge size={16} />} />
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.candidates')} value={lowMediumWhitelistCandidates} tone="ok" icon={<ShieldCheck size={16} />} />
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.low')} value={lowMediumWhitelistLow} tone="ok" icon={<ClipboardCheck size={16} />} />
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.medium')} value={lowMediumWhitelistMedium} tone="warn" icon={<Target size={16} />} />
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.verifiers')} value={lowMediumWhitelistVerifiers} tone="ok" icon={<BookOpenCheck size={16} />} />
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.rollback')} value={lowMediumWhitelistRollback} tone="ok" icon={<Undo2 size={16} />} />
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.audit')} value={lowMediumWhitelistAuditReasons} tone="ok" icon={<Fingerprint size={16} />} />
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.highRedirects')} value={lowMediumWhitelistHighRedirects} tone="danger" icon={<ShieldAlert size={16} />} />
|
||||
<MetricCard label={t('lowMediumWhitelist.metrics.live')} value={lowMediumWhitelistLiveTotal} tone={lowMediumWhitelistLiveTotal === 0 ? 'ok' : 'danger'} icon={<BellOff size={16} />} />
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.25fr) minmax(260px, 0.75fr)', gap: 12 }} className="automation-inventory-visual-grid">
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
|
||||
<SmallLabel>{t('lowMediumWhitelist.sections.candidates')}</SmallLabel>
|
||||
{visibleLowMediumWhitelistCandidates.map(candidate => {
|
||||
const tone = candidate.risk_tier === 'medium' ? 'warn' : 'ok'
|
||||
return (
|
||||
<div key={candidate.candidate_id} style={{ padding: 11, border: '0.5px solid #d8e7e3', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, flexWrap: 'wrap' }}>
|
||||
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 760, color: '#141413', overflowWrap: 'anywhere' }}>
|
||||
{redactPublicText(candidate.display_name)}
|
||||
</span>
|
||||
<Chip value={t(`lowMediumWhitelist.riskTiers.${candidate.risk_tier}` as never)} muted={tone !== 'warn'} />
|
||||
</div>
|
||||
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#5f5a4f', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
|
||||
{redactPublicText(candidate.allowed_no_write_outputs.join(' / '))}
|
||||
</span>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
||||
<Chip value={t(`lowMediumWhitelist.agents.${candidate.owner_agent}` as never)} muted />
|
||||
<Chip value={t(`lowMediumWhitelist.statuses.${candidate.whitelist_status}` as never)} />
|
||||
<Chip value={t('lowMediumWhitelist.labels.verifier', { value: candidate.dry_run_verifier_id })} muted />
|
||||
<Chip value={t('lowMediumWhitelist.labels.rollback', { value: candidate.rollback_proof_id })} muted />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
|
||||
<div style={{ padding: 12, border: '0.5px solid #d8e7e3', borderRadius: 7, background: '#fbfffd', minWidth: 0 }}>
|
||||
<SmallLabel>{t('lowMediumWhitelist.sections.ownerGates')}</SmallLabel>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
{visibleLowMediumWhitelistGates.map(gate => (
|
||||
<GateMatrixRow
|
||||
key={gate.gate_id}
|
||||
label={gate.display_name}
|
||||
value={gate.status}
|
||||
detail={t('lowMediumWhitelist.labels.gateDetail', {
|
||||
owner: gate.owner_agent,
|
||||
fields: gate.required_fields.length,
|
||||
checks: gate.acceptance_checks.length,
|
||||
})}
|
||||
tone={gate.status === 'owner_review_required' ? 'warn' : gate.status === 'blocked_by_runtime_gate' ? 'danger' : 'ok'}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: 12, border: '0.5px solid #d8e7e3', borderRadius: 7, background: '#fff', minWidth: 0 }}>
|
||||
<SmallLabel>{t('lowMediumWhitelist.sections.verifiers')}</SmallLabel>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
{lowMediumRiskWhitelist.dry_run_verifiers.slice(0, 3).map(verifier => (
|
||||
<GateMatrixRow
|
||||
key={verifier.verifier_id}
|
||||
label={verifier.display_name}
|
||||
value={verifier.verifier_status}
|
||||
detail={redactPublicText(verifier.pass_condition)}
|
||||
tone={verifier.verifier_status === 'owner_review_required' ? 'warn' : 'ok'}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: 12, border: '0.5px solid #d8e7e3', borderRadius: 7, background: '#fff', minWidth: 0 }}>
|
||||
<SmallLabel>{t('lowMediumWhitelist.sections.truth')}</SmallLabel>
|
||||
<span style={{ display: 'block', marginTop: 7, fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#5f5a4f', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
|
||||
{redactPublicText(lowMediumRiskWhitelist.whitelist_truth.truth_note)}
|
||||
</span>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 9 }}>
|
||||
<Chip value={t('lowMediumWhitelist.labels.generated', {
|
||||
generated: formatDateTime(lowMediumRiskWhitelist.generated_at),
|
||||
})} muted />
|
||||
<Chip value={t('lowMediumWhitelist.labels.env', { value: lowMediumRiskWhitelist.telegram_policy.canonical_room_env })} muted />
|
||||
<Chip value={t('lowMediumWhitelist.labels.redaction', { value: String(lowMediumRiskWhitelist.display_redaction_contract.redaction_required) })} muted />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
<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' }}>
|
||||
|
||||
@@ -349,6 +349,11 @@ export const apiClient = {
|
||||
return handleResponse<AiAgentReportNoWriteAnalysisRuntimeSnapshot>(res)
|
||||
},
|
||||
|
||||
async getAiAgentLowMediumRiskWhitelist() {
|
||||
const res = await fetch(`${API_BASE_URL}/agents/agent-low-medium-risk-whitelist`)
|
||||
return handleResponse<AiAgentLowMediumRiskWhitelistSnapshot>(res)
|
||||
},
|
||||
|
||||
async getHostRunawayAiopsLoopReadiness() {
|
||||
const res = await fetch(`${API_BASE_URL}/agents/agent-host-runaway-aiops-loop-readiness`)
|
||||
return handleResponse<HostRunawayAiopsLoopReadinessSnapshot>(res)
|
||||
@@ -3349,6 +3354,206 @@ export interface AiAgentReportNoWriteAnalysisRuntimeSnapshot {
|
||||
}>
|
||||
}
|
||||
|
||||
export interface AiAgentLowMediumRiskWhitelistSnapshot {
|
||||
schema_version: 'ai_agent_low_medium_risk_whitelist_v1'
|
||||
generated_at: string
|
||||
program_status: {
|
||||
overall_completion_percent: number
|
||||
current_priority: 'P0' | 'P1' | 'P2' | 'P3'
|
||||
current_task_id: 'P2-408'
|
||||
next_task_id: 'P2-409'
|
||||
read_only_mode: true
|
||||
runtime_authority: 'low_medium_risk_whitelist_no_live_execution_committed_snapshot'
|
||||
status_note: string
|
||||
}
|
||||
source_refs: string[]
|
||||
source_readbacks: Array<{
|
||||
readback_id: string
|
||||
source_schema_version: string
|
||||
source_ref: string
|
||||
endpoint: string
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre'
|
||||
status: string
|
||||
key_readback: string
|
||||
next_action: string
|
||||
}>
|
||||
whitelist_truth: {
|
||||
p2_407_no_write_analysis_loaded: true
|
||||
operation_permission_model_loaded: true
|
||||
candidate_dry_run_evidence_loaded: true
|
||||
report_policy_review_loaded: true
|
||||
dependency_drift_loaded: true
|
||||
low_risk_candidates_ready: true
|
||||
medium_risk_candidates_ready: true
|
||||
dry_run_verifier_required: true
|
||||
rollback_proof_required: true
|
||||
audit_reason_required: true
|
||||
high_risk_redirect_ready: true
|
||||
auto_worker_enabled: false
|
||||
low_risk_live_execution_enabled: false
|
||||
medium_risk_live_execution_enabled: false
|
||||
gateway_queue_write_enabled: false
|
||||
telegram_send_enabled: false
|
||||
bot_api_call_enabled: false
|
||||
receipt_production_write_enabled: false
|
||||
production_write_enabled: false
|
||||
secret_read_enabled: false
|
||||
paid_api_call_enabled: false
|
||||
host_write_enabled: false
|
||||
kubectl_action_enabled: false
|
||||
destructive_operation_enabled: false
|
||||
openclaw_replacement_allowed: false
|
||||
auto_worker_run_count_24h: number
|
||||
low_risk_execution_count_24h: number
|
||||
medium_risk_execution_count_24h: number
|
||||
gateway_queue_write_count_24h: number
|
||||
telegram_send_count_24h: number
|
||||
bot_api_call_count_24h: number
|
||||
receipt_production_write_count_24h: number
|
||||
production_write_count_24h: number
|
||||
secret_read_count_24h: number
|
||||
paid_api_call_count_24h: number
|
||||
host_write_count_24h: number
|
||||
kubectl_action_count_24h: number
|
||||
destructive_operation_count_24h: number
|
||||
truth_note: string
|
||||
}
|
||||
whitelist_candidates: Array<{
|
||||
candidate_id: string
|
||||
display_name: string
|
||||
risk_tier: 'low' | 'medium'
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre'
|
||||
whitelist_status: 'candidate_only_no_live_execution' | 'dry_run_ready_no_live_execution' | 'owner_review_required_no_live_execution'
|
||||
allowed_no_write_outputs: string[]
|
||||
required_evidence: string[]
|
||||
dry_run_verifier_id: string
|
||||
rollback_proof_id: string
|
||||
audit_reason_template_id: string
|
||||
owner_approval_required_for_live_execution: true
|
||||
live_execution_allowed: false
|
||||
production_write_allowed: false
|
||||
side_effect_count: number
|
||||
blocked_runtime_actions: string[]
|
||||
next_gate: string
|
||||
}>
|
||||
dry_run_verifiers: Array<{
|
||||
verifier_id: string
|
||||
display_name: string
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre'
|
||||
verifier_status: 'ready' | 'owner_review_required'
|
||||
required_inputs: string[]
|
||||
pass_condition: string
|
||||
live_readback_allowed: false
|
||||
production_write_allowed: false
|
||||
}>
|
||||
rollback_proofs: Array<{
|
||||
rollback_proof_id: string
|
||||
display_name: string
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre'
|
||||
rollback_scope: string
|
||||
proof_status: 'noop_ready' | 'draft_ready' | 'owner_review_required'
|
||||
rollback_command_allowed: false
|
||||
required_before_live_execution: true
|
||||
}>
|
||||
audit_reason_templates: Array<{
|
||||
template_id: string
|
||||
display_name: string
|
||||
risk_tier: 'low' | 'medium'
|
||||
required_fields: string[]
|
||||
example_reason: string
|
||||
sensitive_payload_allowed: false
|
||||
}>
|
||||
high_risk_redirects: Array<{
|
||||
redirect_id: string
|
||||
display_name: string
|
||||
risk_tier: 'high' | 'critical'
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre'
|
||||
redirect_to: 'P2-409 Owner Review Queue'
|
||||
blocked_runtime_actions: string[]
|
||||
reason: string
|
||||
}>
|
||||
owner_review_gates: Array<{
|
||||
gate_id: string
|
||||
display_name: string
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre'
|
||||
status: 'owner_review_required' | 'blocked_by_runtime_gate' | 'draft_ready'
|
||||
required_fields: string[]
|
||||
acceptance_checks: string[]
|
||||
blocked_runtime_actions: string[]
|
||||
}>
|
||||
activation_boundaries: {
|
||||
read_only_whitelist_allowed: true
|
||||
dry_run_verifier_preview_allowed: true
|
||||
rollback_proof_preview_allowed: true
|
||||
audit_reason_template_allowed: true
|
||||
auto_worker_enabled: false
|
||||
low_risk_live_execution_enabled: false
|
||||
medium_risk_live_execution_enabled: false
|
||||
gateway_queue_write_enabled: false
|
||||
telegram_send_enabled: false
|
||||
bot_api_call_enabled: false
|
||||
receipt_production_write_enabled: false
|
||||
production_write_enabled: false
|
||||
secret_read_enabled: false
|
||||
paid_api_call_enabled: false
|
||||
host_write_enabled: false
|
||||
kubectl_action_enabled: false
|
||||
destructive_operation_enabled: false
|
||||
openclaw_replacement_allowed: false
|
||||
}
|
||||
telegram_policy: {
|
||||
canonical_room: 'AwoooI SRE 戰情室'
|
||||
canonical_room_env: 'SRE_GROUP_CHAT_ID'
|
||||
gateway_queue_write_allowed: false
|
||||
direct_bot_api_allowed: false
|
||||
telegram_send_allowed: false
|
||||
receipt_write_allowed: false
|
||||
policy_note: string
|
||||
}
|
||||
display_redaction_contract: {
|
||||
redaction_required: true
|
||||
unsafe_payload_display_allowed: false
|
||||
private_reasoning_display_allowed: false
|
||||
secret_value_display_allowed: false
|
||||
work_window_transcript_display_allowed: false
|
||||
allowed_display_fields: string[]
|
||||
blocked_display_fields: string[]
|
||||
}
|
||||
rollups: {
|
||||
source_readback_count: number
|
||||
whitelist_candidate_count: number
|
||||
low_risk_candidate_count: number
|
||||
medium_risk_candidate_count: number
|
||||
candidate_only_count: number
|
||||
dry_run_verifier_count: number
|
||||
rollback_proof_count: number
|
||||
audit_reason_template_count: number
|
||||
high_risk_redirect_count: number
|
||||
owner_review_gate_count: number
|
||||
live_execution_approval_required_count: number
|
||||
blocked_runtime_action_count: number
|
||||
auto_worker_run_count: number
|
||||
low_risk_execution_count: number
|
||||
medium_risk_execution_count: number
|
||||
gateway_queue_write_count: number
|
||||
telegram_send_count: number
|
||||
bot_api_call_count: number
|
||||
receipt_production_write_count: number
|
||||
production_write_count: number
|
||||
secret_read_count: number
|
||||
paid_api_call_count: number
|
||||
host_write_count: number
|
||||
kubectl_action_count: number
|
||||
destructive_operation_count: number
|
||||
}
|
||||
next_actions: Array<{
|
||||
task_id: string
|
||||
priority: 'P0' | 'P1' | 'P2' | 'P3'
|
||||
summary: string
|
||||
gate: string
|
||||
}>
|
||||
}
|
||||
|
||||
export interface AiAgentReportAutomationReviewSnapshot {
|
||||
schema_version: 'ai_agent_report_automation_review_v1'
|
||||
generated_at: string
|
||||
|
||||
Reference in New Issue
Block a user