feat(agents): expose controlled executor handoff runway
Some checks failed
Code Review / ai-code-review (push) Successful in 22s
CD Pipeline / tests (push) Successful in 1m47s
CD Pipeline / build-and-deploy (push) Successful in 6m20s
CD Pipeline / post-deploy-checks (push) Successful in 2m18s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
Some checks failed
Code Review / ai-code-review (push) Successful in 22s
CD Pipeline / tests (push) Successful in 1m47s
CD Pipeline / build-and-deploy (push) Successful in 6m20s
CD Pipeline / post-deploy-checks (push) Successful in 2m18s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
This commit is contained in:
@@ -50,6 +50,7 @@ import {
|
||||
type AiAgentReportNoWriteAnalysisRuntimeSnapshot,
|
||||
type AiAgentLowMediumRiskWhitelistSnapshot,
|
||||
type AiAgentHighRiskOwnerReviewQueueSnapshot,
|
||||
type AiAgentControlledExecutorHandoffSnapshot,
|
||||
type AiAgentActionAuditLedgerSnapshot,
|
||||
type AiAgentActionOwnerAcceptanceEventBusSnapshot,
|
||||
type HostRunawayAiopsLoopReadinessSnapshot,
|
||||
@@ -864,6 +865,7 @@ export function AutomationInventoryTab() {
|
||||
const [reportNoWriteAnalysisRuntime, setReportNoWriteAnalysisRuntime] = useState<AiAgentReportNoWriteAnalysisRuntimeSnapshot | null>(null)
|
||||
const [lowMediumRiskWhitelist, setLowMediumRiskWhitelist] = useState<AiAgentLowMediumRiskWhitelistSnapshot | null>(null)
|
||||
const [highRiskOwnerReviewQueue, setHighRiskOwnerReviewQueue] = useState<AiAgentHighRiskOwnerReviewQueueSnapshot | null>(null)
|
||||
const [controlledExecutorHandoff, setControlledExecutorHandoff] = useState<AiAgentControlledExecutorHandoffSnapshot | null>(null)
|
||||
const [actionAuditLedger, setActionAuditLedger] = useState<AiAgentActionAuditLedgerSnapshot | null>(null)
|
||||
const [actionOwnerAcceptanceEventBus, setActionOwnerAcceptanceEventBus] = useState<AiAgentActionOwnerAcceptanceEventBusSnapshot | null>(null)
|
||||
const [hostRunawayAiops, setHostRunawayAiops] = useState<HostRunawayAiopsLoopReadinessSnapshot | null>(null)
|
||||
@@ -962,6 +964,7 @@ export function AutomationInventoryTab() {
|
||||
apiClient.getAiAgentReportNoWriteAnalysisRuntime(),
|
||||
apiClient.getAiAgentLowMediumRiskWhitelist(),
|
||||
apiClient.getAiAgentHighRiskOwnerReviewQueue(),
|
||||
apiClient.getAiAgentControlledExecutorHandoff(),
|
||||
apiClient.getAiAgentActionAuditLedger(),
|
||||
apiClient.getAiAgentActionOwnerAcceptanceEventBus(),
|
||||
apiClient.getHostRunawayAiopsLoopReadiness(),
|
||||
@@ -1053,6 +1056,7 @@ export function AutomationInventoryTab() {
|
||||
reportNoWriteAnalysisRuntimeResult,
|
||||
lowMediumRiskWhitelistResult,
|
||||
highRiskOwnerReviewQueueResult,
|
||||
controlledExecutorHandoffResult,
|
||||
actionAuditLedgerResult,
|
||||
actionOwnerAcceptanceEventBusResult,
|
||||
hostRunawayAiopsResult,
|
||||
@@ -1141,6 +1145,7 @@ export function AutomationInventoryTab() {
|
||||
setReportNoWriteAnalysisRuntime(settledPublicValue(reportNoWriteAnalysisRuntimeResult))
|
||||
setLowMediumRiskWhitelist(settledPublicValue(lowMediumRiskWhitelistResult))
|
||||
setHighRiskOwnerReviewQueue(settledPublicValue(highRiskOwnerReviewQueueResult))
|
||||
setControlledExecutorHandoff(settledPublicValue(controlledExecutorHandoffResult))
|
||||
setActionAuditLedger(settledPublicValue(actionAuditLedgerResult))
|
||||
setActionOwnerAcceptanceEventBus(settledPublicValue(actionOwnerAcceptanceEventBusResult))
|
||||
setHostRunawayAiops(settledPublicValue(hostRunawayAiopsResult))
|
||||
@@ -1231,6 +1236,9 @@ export function AutomationInventoryTab() {
|
||||
reportNoWriteAnalysisRuntimeResult,
|
||||
lowMediumRiskWhitelistResult,
|
||||
highRiskOwnerReviewQueueResult,
|
||||
controlledExecutorHandoffResult,
|
||||
actionAuditLedgerResult,
|
||||
actionOwnerAcceptanceEventBusResult,
|
||||
hostRunawayAiopsResult,
|
||||
proactiveOperationsResult,
|
||||
versionLifecycleProposalResult,
|
||||
@@ -1818,6 +1826,52 @@ export function AutomationInventoryTab() {
|
||||
.slice(0, 5)
|
||||
}, [highRiskOwnerReviewQueue])
|
||||
|
||||
const visibleControlledExecutorPackets = useMemo(() => {
|
||||
if (!controlledExecutorHandoff) return []
|
||||
const riskPriority = { critical: 0, high: 1 } as Record<string, number>
|
||||
const statusPriority = {
|
||||
critical_break_glass_only: 0,
|
||||
blocked_missing_check_mode: 1,
|
||||
blocked_missing_verifier: 2,
|
||||
blocked_missing_learning_writeback: 3,
|
||||
ready_for_controlled_executor: 4,
|
||||
} as Record<string, number>
|
||||
return [...controlledExecutorHandoff.executor_handoff_packets]
|
||||
.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.handoff_status] ?? 5
|
||||
const rightStatus = statusPriority[b.handoff_status] ?? 5
|
||||
if (leftStatus !== rightStatus) return leftStatus - rightStatus
|
||||
return a.packet_id.localeCompare(b.packet_id)
|
||||
})
|
||||
.slice(0, 7)
|
||||
}, [controlledExecutorHandoff])
|
||||
|
||||
const visibleControlledExecutorRoutes = useMemo(() => {
|
||||
if (!controlledExecutorHandoff) return []
|
||||
const statusPriority = { blocked_by_policy: 0, ready_for_handoff: 1 } as Record<string, number>
|
||||
return [...controlledExecutorHandoff.executor_routes]
|
||||
.sort((a, b) => {
|
||||
const leftStatus = statusPriority[a.route_status] ?? 2
|
||||
const rightStatus = statusPriority[b.route_status] ?? 2
|
||||
if (leftStatus !== rightStatus) return leftStatus - rightStatus
|
||||
return a.route_id.localeCompare(b.route_id)
|
||||
})
|
||||
.slice(0, 5)
|
||||
}, [controlledExecutorHandoff])
|
||||
|
||||
const visibleControlledExecutorVerifierBindings = useMemo(() => {
|
||||
if (!controlledExecutorHandoff) return []
|
||||
return [...controlledExecutorHandoff.verifier_bindings]
|
||||
.sort((a, b) => {
|
||||
if (a.blocked_count !== b.blocked_count) return b.blocked_count - a.blocked_count
|
||||
return a.binding_id.localeCompare(b.binding_id)
|
||||
})
|
||||
.slice(0, 5)
|
||||
}, [controlledExecutorHandoff])
|
||||
|
||||
const visibleActionAuditEvents = useMemo(() => {
|
||||
if (!actionAuditLedger) return []
|
||||
const riskPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record<string, number>
|
||||
@@ -2859,7 +2913,7 @@ export function AutomationInventoryTab() {
|
||||
)
|
||||
}
|
||||
|
||||
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !lowMediumRiskWhitelist || !highRiskOwnerReviewQueue || !actionAuditLedger || !actionOwnerAcceptanceEventBus || !hostRunawayAiops || !proactiveOperations || !versionLifecycleProposal || !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 || !highRiskOwnerReviewQueue || !controlledExecutorHandoff || !actionAuditLedger || !actionOwnerAcceptanceEventBus || !hostRunawayAiops || !proactiveOperations || !versionLifecycleProposal || !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">
|
||||
@@ -3209,6 +3263,30 @@ export function AutomationInventoryTab() {
|
||||
+ highRiskOwnerReviewQueue.rollups.kubectl_action_count
|
||||
+ highRiskOwnerReviewQueue.rollups.destructive_operation_count
|
||||
)
|
||||
const controlledExecutorOverall = controlledExecutorHandoff.program_status.overall_completion_percent
|
||||
const controlledExecutorPackets = controlledExecutorHandoff.rollups.handoff_packet_count
|
||||
const controlledExecutorReady = controlledExecutorHandoff.rollups.ready_for_controlled_executor_count
|
||||
const controlledExecutorCritical = controlledExecutorHandoff.rollups.critical_break_glass_count
|
||||
const controlledExecutorAnsible = controlledExecutorHandoff.rollups.ansible_check_mode_packet_count
|
||||
const controlledExecutorMcp = controlledExecutorHandoff.rollups.mcp_tool_route_count
|
||||
const controlledExecutorVerifiers = controlledExecutorHandoff.rollups.verifier_binding_count
|
||||
const controlledExecutorLearning = controlledExecutorHandoff.rollups.learning_writeback_contract_count
|
||||
const controlledExecutorOwnerRequired = controlledExecutorHandoff.rollups.owner_response_required_count
|
||||
const controlledExecutorDispatches = controlledExecutorHandoff.rollups.controlled_executor_dispatch_count
|
||||
const controlledExecutorLiveApply = controlledExecutorHandoff.rollups.live_apply_count
|
||||
const controlledExecutorLiveWrites = (
|
||||
controlledExecutorHandoff.rollups.gateway_queue_write_count
|
||||
+ controlledExecutorHandoff.rollups.telegram_send_count
|
||||
+ controlledExecutorHandoff.rollups.bot_api_call_count
|
||||
+ controlledExecutorHandoff.rollups.km_write_count
|
||||
+ controlledExecutorHandoff.rollups.playbook_trust_write_count
|
||||
+ controlledExecutorHandoff.rollups.production_write_count
|
||||
+ controlledExecutorHandoff.rollups.secret_read_count
|
||||
+ controlledExecutorHandoff.rollups.paid_api_call_count
|
||||
+ controlledExecutorHandoff.rollups.host_write_count
|
||||
+ controlledExecutorHandoff.rollups.kubectl_action_count
|
||||
+ controlledExecutorHandoff.rollups.destructive_operation_count
|
||||
)
|
||||
const actionAuditOverall = actionAuditLedger.program_status.overall_completion_percent
|
||||
const actionAuditEvents = actionAuditLedger.rollups.audit_event_template_count
|
||||
const actionAuditLowMedium = actionAuditLedger.rollups.low_medium_event_count
|
||||
@@ -6742,6 +6820,144 @@ 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 #0f766e35',
|
||||
background: 'rgba(15,118,110,0.08)',
|
||||
color: '#0f766e',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<Route 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('controlledExecutorHandoff.title')}
|
||||
</span>
|
||||
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, color: '#5c5a55', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
|
||||
{t('controlledExecutorHandoff.subtitle', {
|
||||
current: controlledExecutorHandoff.program_status.current_task_id,
|
||||
next: controlledExecutorHandoff.program_status.next_task_id,
|
||||
ready: controlledExecutorReady,
|
||||
packets: controlledExecutorPackets,
|
||||
critical: controlledExecutorCritical,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6, minWidth: 0 }}>
|
||||
<Chip value={t('controlledExecutorHandoff.badges.mode')} />
|
||||
<Chip value={t('controlledExecutorHandoff.badges.dispatch', { count: controlledExecutorDispatches })} muted={controlledExecutorDispatches === 0} />
|
||||
<Chip value={t('controlledExecutorHandoff.badges.live', { count: controlledExecutorLiveApply })} muted={controlledExecutorLiveApply === 0} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(112px, 1fr))', gap: 10 }} className="automation-inventory-kpi-grid">
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.overall')} value={`${controlledExecutorOverall}%`} tone="ok" icon={<Gauge size={16} />} />
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.packets')} value={controlledExecutorPackets} tone="ok" icon={<FileText size={16} />} />
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.ready')} value={controlledExecutorReady} tone="ok" icon={<ShieldCheck size={16} />} />
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.critical')} value={controlledExecutorCritical} tone="danger" icon={<Lock size={16} />} />
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.ansible')} value={controlledExecutorAnsible} tone="ok" icon={<Server size={16} />} />
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.mcp')} value={controlledExecutorMcp} tone="ok" icon={<Route size={16} />} />
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.verifiers')} value={controlledExecutorVerifiers} tone="ok" icon={<ClipboardCheck size={16} />} />
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.learning')} value={controlledExecutorLearning} tone="ok" icon={<BookOpenCheck size={16} />} />
|
||||
<MetricCard label={t('controlledExecutorHandoff.metrics.liveWrites')} value={controlledExecutorLiveWrites} tone={controlledExecutorLiveWrites === 0 ? 'ok' : 'danger'} icon={<BellOff size={16} />} />
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.2fr) minmax(260px, 0.8fr)', gap: 12 }} className="automation-inventory-visual-grid">
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
|
||||
<SmallLabel>{t('controlledExecutorHandoff.sections.packets')}</SmallLabel>
|
||||
{visibleControlledExecutorPackets.map(packet => {
|
||||
const tone = packet.risk_tier === 'critical' ? 'danger' : 'ok'
|
||||
return (
|
||||
<div key={packet.packet_id} style={{ padding: 11, border: '0.5px solid #cfe6df', borderRadius: 7, background: packet.risk_tier === 'critical' ? '#fffafa' : '#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(packet.display_name)}
|
||||
</span>
|
||||
<Chip value={t(`controlledExecutorHandoff.riskTiers.${packet.risk_tier}` as never)} muted={tone !== 'danger'} />
|
||||
</div>
|
||||
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#5f5a4f', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
|
||||
{redactPublicText(packet.next_gate)}
|
||||
</span>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
||||
<Chip value={t(`controlledExecutorHandoff.agents.${packet.owner_agent}` as never)} muted />
|
||||
<Chip value={t('controlledExecutorHandoff.labels.executor', { value: t(`controlledExecutorHandoff.agents.${packet.executor_agent}` as never) })} muted />
|
||||
<Chip value={t(`controlledExecutorHandoff.executorTypes.${packet.executor_type}` as never)} />
|
||||
<Chip value={t(`controlledExecutorHandoff.statuses.${packet.handoff_status}` as never)} muted={packet.handoff_status === 'ready_for_controlled_executor'} />
|
||||
<Chip value={t('controlledExecutorHandoff.labels.check', { value: packet.check_mode_passed ? 'ready' : 'blocked' })} muted={packet.check_mode_passed} />
|
||||
<Chip value={t('controlledExecutorHandoff.labels.verifier', { value: packet.post_action_verifier_ready ? 'ready' : 'blocked' })} muted={packet.post_action_verifier_ready} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
|
||||
<div style={{ padding: 12, border: '0.5px solid #cfe6df', borderRadius: 7, background: '#f7fcfa', minWidth: 0 }}>
|
||||
<SmallLabel>{t('controlledExecutorHandoff.sections.routes')}</SmallLabel>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
{visibleControlledExecutorRoutes.map(route => (
|
||||
<GateMatrixRow
|
||||
key={route.route_id}
|
||||
label={route.display_name}
|
||||
value={t(`controlledExecutorHandoff.routeStatuses.${route.route_status}` as never)}
|
||||
detail={t('controlledExecutorHandoff.labels.routeDetail', {
|
||||
agent: t(`controlledExecutorHandoff.agents.${route.executor_agent}` as never),
|
||||
inputs: route.required_inputs.length,
|
||||
blocked: route.blocked_actions.length,
|
||||
})}
|
||||
tone={route.route_status === 'ready_for_handoff' ? 'ok' : 'danger'}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: 12, border: '0.5px solid #cfe6df', borderRadius: 7, background: '#fff', minWidth: 0 }}>
|
||||
<SmallLabel>{t('controlledExecutorHandoff.sections.verifiers')}</SmallLabel>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
{visibleControlledExecutorVerifierBindings.map(binding => (
|
||||
<GateMatrixRow
|
||||
key={binding.binding_id}
|
||||
label={binding.display_name}
|
||||
value={t('controlledExecutorHandoff.labels.learning', { value: t(`controlledExecutorHandoff.agents.${binding.owner_agent}` as never) })}
|
||||
detail={t('controlledExecutorHandoff.labels.bindingDetail', {
|
||||
ready: binding.ready_count,
|
||||
blocked: binding.blocked_count,
|
||||
})}
|
||||
tone={binding.blocked_count === 0 ? 'ok' : 'danger'}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: 12, border: '0.5px solid #cfe6df', borderRadius: 7, background: '#fff', minWidth: 0 }}>
|
||||
<SmallLabel>{t('controlledExecutorHandoff.sections.truth')}</SmallLabel>
|
||||
<span style={{ display: 'block', marginTop: 7, fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#5f5a4f', lineHeight: 1.55, overflowWrap: 'anywhere' }}>
|
||||
{redactPublicText(controlledExecutorHandoff.handoff_truth.truth_note)}
|
||||
</span>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 9 }}>
|
||||
<Chip value={t('controlledExecutorHandoff.labels.generated', {
|
||||
generated: formatDateTime(controlledExecutorHandoff.generated_at),
|
||||
})} muted />
|
||||
<Chip value={t('controlledExecutorHandoff.labels.ownerRequired', { count: controlledExecutorOwnerRequired })} muted />
|
||||
<Chip value={t('controlledExecutorHandoff.labels.liveWrites', { count: controlledExecutorLiveWrites })} muted={controlledExecutorLiveWrites === 0} />
|
||||
<Chip value={t('controlledExecutorHandoff.labels.redaction', { value: controlledExecutorHandoff.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' }}>
|
||||
|
||||
@@ -647,6 +647,11 @@ export const apiClient = {
|
||||
return handleResponse<AiAgentHighRiskOwnerReviewQueueSnapshot>(res)
|
||||
},
|
||||
|
||||
async getAiAgentControlledExecutorHandoff() {
|
||||
const res = await fetch(`${API_BASE_URL}/agents/agent-controlled-executor-handoff`)
|
||||
return handleResponse<AiAgentControlledExecutorHandoffSnapshot>(res)
|
||||
},
|
||||
|
||||
async getAiAgentActionAuditLedger() {
|
||||
const res = await fetch(`${API_BASE_URL}/agents/agent-action-audit-ledger`)
|
||||
return handleResponse<AiAgentActionAuditLedgerSnapshot>(res)
|
||||
@@ -4487,6 +4492,226 @@ export interface AiAgentHighRiskOwnerReviewQueueSnapshot {
|
||||
}>
|
||||
}
|
||||
|
||||
export interface AiAgentControlledExecutorHandoffSnapshot {
|
||||
schema_version: 'ai_agent_controlled_executor_handoff_v1'
|
||||
generated_at: string
|
||||
program_status: {
|
||||
overall_completion_percent: number
|
||||
current_priority: 'P0'
|
||||
current_task_id: 'P2-415'
|
||||
next_task_id: 'P2-416'
|
||||
read_only_mode: true
|
||||
runtime_authority: 'controlled_executor_handoff_readback_no_live_apply'
|
||||
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' | 'security' | 'devops'
|
||||
status: string
|
||||
key_readback: string
|
||||
next_action: string
|
||||
}>
|
||||
handoff_truth: {
|
||||
p2_409_controlled_apply_queue_loaded: true
|
||||
p2_410_audit_ledger_loaded: true
|
||||
p2_411_handoff_event_bus_loaded: true
|
||||
runtime_readiness_loaded: true
|
||||
runtime_write_gate_loaded: true
|
||||
post_write_verifier_loaded: true
|
||||
learning_writeback_loaded: true
|
||||
telegram_receipt_loaded: true
|
||||
high_risk_controlled_executor_handoff_ready: true
|
||||
high_risk_owner_review_required: false
|
||||
critical_break_glass_required: true
|
||||
allowlist_route_required: true
|
||||
ansible_check_mode_required: true
|
||||
rollback_plan_required: true
|
||||
post_action_verifier_required: true
|
||||
telegram_evidence_required: true
|
||||
km_writeback_required: true
|
||||
playbook_trust_writeback_required: true
|
||||
controlled_executor_dispatch_enabled: false
|
||||
live_apply_enabled: false
|
||||
critical_auto_bypass_allowed: false
|
||||
gateway_queue_write_enabled: false
|
||||
telegram_send_enabled: false
|
||||
bot_api_call_enabled: false
|
||||
km_write_enabled: false
|
||||
playbook_trust_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
|
||||
controlled_executor_dispatch_count_24h: number
|
||||
live_apply_count_24h: number
|
||||
gateway_queue_write_count_24h: number
|
||||
telegram_send_count_24h: number
|
||||
bot_api_call_count_24h: number
|
||||
km_write_count_24h: number
|
||||
playbook_trust_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
|
||||
}
|
||||
executor_handoff_packets: Array<{
|
||||
packet_id: string
|
||||
source_queue_item_id: string
|
||||
display_name: string
|
||||
risk_tier: 'high' | 'critical'
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops'
|
||||
executor_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops'
|
||||
executor_type:
|
||||
| 'ansible_playbook'
|
||||
| 'mcp_tool_route'
|
||||
| 'telegram_gateway_queue'
|
||||
| 'km_playbook_writer'
|
||||
| 'readback_verifier'
|
||||
| 'break_glass_only'
|
||||
handoff_status:
|
||||
| 'ready_for_controlled_executor'
|
||||
| 'critical_break_glass_only'
|
||||
| 'blocked_missing_check_mode'
|
||||
| 'blocked_missing_verifier'
|
||||
| 'blocked_missing_learning_writeback'
|
||||
controlled_route_id: string
|
||||
playbook_ref: string
|
||||
mcp_tool_ref: string
|
||||
check_mode_ref: string
|
||||
verifier_ref: string
|
||||
rollback_ref: string
|
||||
telegram_evidence_ref: string
|
||||
km_writeback_ref: string
|
||||
playbook_trust_ref: string
|
||||
allowlist_match: boolean
|
||||
check_mode_passed: boolean
|
||||
rollback_plan_ready: boolean
|
||||
post_action_verifier_ready: boolean
|
||||
telegram_evidence_ready: boolean
|
||||
km_writeback_ready: boolean
|
||||
playbook_trust_writeback_ready: boolean
|
||||
owner_response_required: boolean
|
||||
break_glass_required: boolean
|
||||
controlled_executor_handoff_allowed: boolean
|
||||
live_apply_performed: false
|
||||
side_effect_count: number
|
||||
blocked_runtime_actions: string[]
|
||||
next_gate: string
|
||||
}>
|
||||
executor_routes: Array<{
|
||||
route_id: string
|
||||
display_name: string
|
||||
executor_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops'
|
||||
route_status: 'ready_for_handoff' | 'blocked_by_policy'
|
||||
required_inputs: string[]
|
||||
blocked_actions: string[]
|
||||
live_apply_allowed_by_this_readback: false
|
||||
}>
|
||||
verifier_bindings: Array<{
|
||||
binding_id: string
|
||||
display_name: string
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops'
|
||||
required_before_dispatch: true
|
||||
ready_count: number
|
||||
blocked_count: number
|
||||
failure_if_missing: string
|
||||
}>
|
||||
learning_writeback_contracts: Array<{
|
||||
contract_id: string
|
||||
display_name: string
|
||||
owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops'
|
||||
target_store: string
|
||||
writeback_status: 'ready_for_executor_receipt' | 'blocked_by_policy'
|
||||
required_fields: string[]
|
||||
runtime_write_performed: false
|
||||
}>
|
||||
activation_boundaries: {
|
||||
committed_snapshot_read_allowed: true
|
||||
controlled_executor_handoff_preview_allowed: true
|
||||
ansible_check_mode_receipt_preview_allowed: true
|
||||
mcp_tool_registry_route_preview_allowed: true
|
||||
post_action_verifier_binding_preview_allowed: true
|
||||
telegram_evidence_preview_allowed: true
|
||||
km_playbook_trust_writeback_preview_allowed: true
|
||||
controlled_executor_dispatch_enabled: false
|
||||
live_apply_enabled: false
|
||||
gateway_queue_write_enabled: false
|
||||
telegram_send_enabled: false
|
||||
bot_api_call_enabled: false
|
||||
km_write_enabled: false
|
||||
playbook_trust_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
|
||||
}
|
||||
display_redaction_contract: {
|
||||
redaction_required: true
|
||||
raw_tool_output_display_allowed: false
|
||||
raw_runtime_payload_display_allowed: false
|
||||
raw_telegram_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
|
||||
handoff_packet_count: number
|
||||
ready_for_controlled_executor_count: number
|
||||
critical_break_glass_count: number
|
||||
high_risk_packet_count: number
|
||||
critical_packet_count: number
|
||||
ansible_check_mode_packet_count: number
|
||||
mcp_tool_route_count: number
|
||||
post_action_verifier_binding_count: number
|
||||
telegram_evidence_binding_count: number
|
||||
km_writeback_binding_count: number
|
||||
playbook_trust_writeback_binding_count: number
|
||||
owner_response_required_count: number
|
||||
blocked_by_critical_boundary_count: number
|
||||
missing_check_mode_count: number
|
||||
missing_rollback_count: number
|
||||
missing_verifier_count: number
|
||||
missing_telegram_evidence_count: number
|
||||
missing_learning_writeback_count: number
|
||||
executor_route_count: number
|
||||
verifier_binding_count: number
|
||||
learning_writeback_contract_count: number
|
||||
controlled_executor_dispatch_count: number
|
||||
live_apply_count: number
|
||||
gateway_queue_write_count: number
|
||||
telegram_send_count: number
|
||||
bot_api_call_count: number
|
||||
km_write_count: number
|
||||
playbook_trust_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 AiAgentActionAuditLedgerSnapshot {
|
||||
schema_version: 'ai_agent_action_audit_ledger_v1'
|
||||
generated_at: string
|
||||
|
||||
Reference in New Issue
Block a user