feat(governance): surface AI agent execution runways
This commit is contained in:
@@ -3576,6 +3576,41 @@
|
||||
"pipelineBadge": "Sensor → Gate → Verifier",
|
||||
"boundaryTitle": "真實邊界",
|
||||
"boundaryBadge": "不誇大接管",
|
||||
"runwayTitle": "AI Agents 專業執行跑道",
|
||||
"runwayBadge": "只讀 / 乾跑 / 審核 / 回寫",
|
||||
"runwaySummary": "可無寫入推進 {noWrite} 類;正式執行 {runtime}、Telegram 發送 {sends}、production 寫入 {writes} 仍依 gate。",
|
||||
"runway": {
|
||||
"readOnlyInvestigation": {
|
||||
"label": "主動巡檢與證據蒐集",
|
||||
"detail": "只讀探針 {probes} 個,服務需處置訊號 {health} 個。",
|
||||
"next": "下一步:整理 owner packet;runtime blocker {blocked} 個。"
|
||||
},
|
||||
"dryRunCandidate": {
|
||||
"label": "乾跑候選與套用審查",
|
||||
"detail": "乾跑證據 {evidence} 筆,Verifier plan {verifier} 個。",
|
||||
"next": "下一步:{review} 個候選進 owner review,不直接執行。"
|
||||
},
|
||||
"shadowReplay": {
|
||||
"label": "Nemotron replay / shadow",
|
||||
"detail": "無寫入 replay {replays} 次,Verifier shadow case {verifier} 個。",
|
||||
"next": "下一步:{approvals} 個 checkpoint 需批准才可升級。"
|
||||
},
|
||||
"permissionModel": {
|
||||
"label": "操作權限模型",
|
||||
"detail": "Gate transition {gates} 條,blocked category {blocked} 個。",
|
||||
"next": "下一步:{approvals} 類仍需人工批准。"
|
||||
},
|
||||
"reportTelegram": {
|
||||
"label": "日週月報與 Telegram receipt",
|
||||
"detail": "Gateway queue write {queue},live report delivery {delivery}。",
|
||||
"next": "下一步:{approvals} 個報告/通知決策需批准。"
|
||||
},
|
||||
"learningWriteback": {
|
||||
"label": "學習回寫與 PlayBook 成長",
|
||||
"detail": "已匹配 approval {matched} 筆,KM draft {drafts} 筆。",
|
||||
"next": "下一步:{gates} 個 learning / trust gate 未打開。"
|
||||
}
|
||||
},
|
||||
"badges": {
|
||||
"readOnly": "全域只讀控管",
|
||||
"noTranscript": "只顯示脫敏治理狀態",
|
||||
|
||||
@@ -3576,6 +3576,41 @@
|
||||
"pipelineBadge": "Sensor → Gate → Verifier",
|
||||
"boundaryTitle": "真實邊界",
|
||||
"boundaryBadge": "不誇大接管",
|
||||
"runwayTitle": "AI Agents 專業執行跑道",
|
||||
"runwayBadge": "只讀 / 乾跑 / 審核 / 回寫",
|
||||
"runwaySummary": "可無寫入推進 {noWrite} 類;正式執行 {runtime}、Telegram 發送 {sends}、production 寫入 {writes} 仍依 gate。",
|
||||
"runway": {
|
||||
"readOnlyInvestigation": {
|
||||
"label": "主動巡檢與證據蒐集",
|
||||
"detail": "只讀探針 {probes} 個,服務需處置訊號 {health} 個。",
|
||||
"next": "下一步:整理 owner packet;runtime blocker {blocked} 個。"
|
||||
},
|
||||
"dryRunCandidate": {
|
||||
"label": "乾跑候選與套用審查",
|
||||
"detail": "乾跑證據 {evidence} 筆,Verifier plan {verifier} 個。",
|
||||
"next": "下一步:{review} 個候選進 owner review,不直接執行。"
|
||||
},
|
||||
"shadowReplay": {
|
||||
"label": "Nemotron replay / shadow",
|
||||
"detail": "無寫入 replay {replays} 次,Verifier shadow case {verifier} 個。",
|
||||
"next": "下一步:{approvals} 個 checkpoint 需批准才可升級。"
|
||||
},
|
||||
"permissionModel": {
|
||||
"label": "操作權限模型",
|
||||
"detail": "Gate transition {gates} 條,blocked category {blocked} 個。",
|
||||
"next": "下一步:{approvals} 類仍需人工批准。"
|
||||
},
|
||||
"reportTelegram": {
|
||||
"label": "日週月報與 Telegram receipt",
|
||||
"detail": "Gateway queue write {queue},live report delivery {delivery}。",
|
||||
"next": "下一步:{approvals} 個報告/通知決策需批准。"
|
||||
},
|
||||
"learningWriteback": {
|
||||
"label": "學習回寫與 PlayBook 成長",
|
||||
"detail": "已匹配 approval {matched} 筆,KM draft {drafts} 筆。",
|
||||
"next": "下一步:{gates} 個 learning / trust gate 未打開。"
|
||||
}
|
||||
},
|
||||
"badges": {
|
||||
"readOnly": "全域只讀控管",
|
||||
"noTranscript": "只顯示脫敏治理狀態",
|
||||
|
||||
@@ -5364,6 +5364,97 @@ export function AutomationInventoryTab() {
|
||||
tone: hostRedactionLocked && professionalTaskRedactionLocked && warRoomRedactionLocked && serviceHealthRedactionLocked ? 'ok' as const : 'warn' as const,
|
||||
},
|
||||
]
|
||||
const globalControlRunwayRows: Array<{
|
||||
key: string
|
||||
label: string
|
||||
owner: string
|
||||
value: string
|
||||
detail: string
|
||||
next: string
|
||||
tone: 'ok' | 'warn' | 'danger' | 'neutral'
|
||||
icon: ReactNode
|
||||
}> = [
|
||||
{
|
||||
key: 'readOnlyInvestigation',
|
||||
label: t('globalControl.runway.readOnlyInvestigation.label'),
|
||||
owner: 'Hermes / OpenClaw',
|
||||
value: String(runtimeActionRequired + serviceHealthActions + hostRunawayAlertLanes),
|
||||
detail: t('globalControl.runway.readOnlyInvestigation.detail', {
|
||||
probes: hostStatefulInventory.rollups.readonly_probe_step_count,
|
||||
health: serviceHealthActions,
|
||||
}),
|
||||
next: t('globalControl.runway.readOnlyInvestigation.next', { blocked: hostRunawayBlocked }),
|
||||
tone: hostRunawayBlocked > 0 ? 'warn' : 'ok',
|
||||
icon: <Fingerprint size={15} />,
|
||||
},
|
||||
{
|
||||
key: 'dryRunCandidate',
|
||||
label: t('globalControl.runway.dryRunCandidate.label'),
|
||||
owner: 'OpenClaw / SRE',
|
||||
value: `${candidateDryRunPassed}/${candidateDryRunCount}`,
|
||||
detail: t('globalControl.runway.dryRunCandidate.detail', {
|
||||
evidence: candidateDryRunEvidenceCount,
|
||||
verifier: candidateDryRunVerifierPlans,
|
||||
}),
|
||||
next: t('globalControl.runway.dryRunCandidate.next', { review: candidateDryRunNeedsReview }),
|
||||
tone: candidateDryRunNeedsReview > 0 ? 'warn' : 'ok',
|
||||
icon: <ClipboardCheck size={15} />,
|
||||
},
|
||||
{
|
||||
key: 'shadowReplay',
|
||||
label: t('globalControl.runway.shadowReplay.label'),
|
||||
owner: 'Nemotron / Critic',
|
||||
value: `${runtimeShadowPassed}/${runtimeShadowCandidates}`,
|
||||
detail: t('globalControl.runway.shadowReplay.detail', {
|
||||
replays: runtimeShadowReplays,
|
||||
verifier: runtimeShadowVerifierCases,
|
||||
}),
|
||||
next: t('globalControl.runway.shadowReplay.next', { approvals: runtimeShadowApprovals }),
|
||||
tone: runtimeShadowApprovals > 0 || runtimeShadowBlocked > 0 ? 'warn' : 'ok',
|
||||
icon: <RefreshCw size={15} />,
|
||||
},
|
||||
{
|
||||
key: 'permissionModel',
|
||||
label: t('globalControl.runway.permissionModel.label'),
|
||||
owner: 'Security / OpenClaw',
|
||||
value: `${operationPermissionObserveOnly + operationPermissionNoWrite + operationPermissionProposalOnly}/${operationPermissionCategories}`,
|
||||
detail: t('globalControl.runway.permissionModel.detail', {
|
||||
gates: operationPermissionGates,
|
||||
blocked: operationPermissionBlocked,
|
||||
}),
|
||||
next: t('globalControl.runway.permissionModel.next', { approvals: operationPermissionHumanApproval }),
|
||||
tone: operationPermissionBlocked > 0 || operationPermissionHumanApproval > 0 ? 'warn' : 'ok',
|
||||
icon: <ShieldAlert size={15} />,
|
||||
},
|
||||
{
|
||||
key: 'reportTelegram',
|
||||
label: t('globalControl.runway.reportTelegram.label'),
|
||||
owner: 'Hermes / Reporter',
|
||||
value: `${reportRuntimeReady}/${reportRuntimeLanes}`,
|
||||
detail: t('globalControl.runway.reportTelegram.detail', {
|
||||
queue: reportRuntimeQueueWrites,
|
||||
delivery: reportRuntimeLiveDelivery,
|
||||
}),
|
||||
next: t('globalControl.runway.reportTelegram.next', { approvals: reportRuntimeApprovals }),
|
||||
tone: reportRuntimeApprovals > 0 || reportRuntimeBlocked > 0 ? 'warn' : 'ok',
|
||||
icon: <BellRing size={15} />,
|
||||
},
|
||||
{
|
||||
key: 'learningWriteback',
|
||||
label: t('globalControl.runway.learningWriteback.label'),
|
||||
owner: 'Hermes / OpenClaw',
|
||||
value: String(matchedPlaybookCandidates + taskResultKmDrafts),
|
||||
detail: t('globalControl.runway.learningWriteback.detail', {
|
||||
matched: matchedPlaybookApprovalMatched,
|
||||
drafts: taskResultKmDrafts,
|
||||
}),
|
||||
next: t('globalControl.runway.learningWriteback.next', {
|
||||
gates: matchedPlaybookGates + learningWritebackApprovals,
|
||||
}),
|
||||
tone: matchedPlaybookGates + learningWritebackApprovals > 0 ? 'warn' : 'ok',
|
||||
icon: <BookOpenCheck size={15} />,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="automation-inventory-tab-root" style={{ padding: 20, display: 'flex', flexDirection: 'column', gap: 16, minWidth: 0 }}>
|
||||
@@ -5505,6 +5596,63 @@ export function AutomationInventoryTab() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: 12, border: '0.5px solid #d9e7f7', borderRadius: 7, background: '#f8fbff', display: 'flex', flexDirection: 'column', gap: 11, minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0 }}>
|
||||
<SmallLabel>{t('globalControl.runwayTitle')}</SmallLabel>
|
||||
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
|
||||
{t('globalControl.runwaySummary', {
|
||||
noWrite: operationPermissionObserveOnly + operationPermissionNoWrite + operationPermissionProposalOnly,
|
||||
runtime: operationPermissionRuntimeRuns,
|
||||
sends: operationPermissionSends,
|
||||
writes: operationPermissionProductionWrites,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<Chip value={t('globalControl.runwayBadge')} muted />
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 10 }} className="automation-inventory-global-control-runway-grid">
|
||||
{globalControlRunwayRows.map(row => {
|
||||
const color = toneColor(row.tone)
|
||||
return (
|
||||
<div key={row.key} style={{ padding: 10, border: `0.5px solid ${color}44`, borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'flex-start', minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', gap: 7, alignItems: 'center', minWidth: 0 }}>
|
||||
<div style={{
|
||||
width: 26,
|
||||
height: 26,
|
||||
borderRadius: 7,
|
||||
border: `0.5px solid ${color}55`,
|
||||
background: `${color}12`,
|
||||
color,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
{row.icon}
|
||||
</div>
|
||||
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 760, color: '#141413', lineHeight: 1.2, overflowWrap: 'anywhere' }}>
|
||||
{row.label}
|
||||
</span>
|
||||
</div>
|
||||
<Chip value={row.value} muted={row.tone === 'ok'} />
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
||||
<Chip value={row.owner} />
|
||||
</div>
|
||||
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
||||
{row.detail}
|
||||
</span>
|
||||
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color, fontWeight: 700, lineHeight: 1.45, overflowWrap: 'anywhere' }}>
|
||||
{row.next}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 10 }} className="automation-inventory-global-control-domain-grid">
|
||||
{globalControlDomainRows.map(row => {
|
||||
const color = toneColor(row.tone)
|
||||
@@ -18216,6 +18364,7 @@ export function AutomationInventoryTab() {
|
||||
.automation-inventory-global-control-kpi-grid,
|
||||
.automation-inventory-global-control-grid,
|
||||
.automation-inventory-global-control-pipeline-grid,
|
||||
.automation-inventory-global-control-runway-grid,
|
||||
.automation-inventory-global-control-domain-grid,
|
||||
.automation-inventory-kpi-grid,
|
||||
.automation-inventory-command-grid,
|
||||
|
||||
Reference in New Issue
Block a user