feat(governance): 新增 AI Agent 專業任務擴展
All checks were successful
Code Review / ai-code-review (push) Successful in 15s
CD Pipeline / tests (push) Successful in 1m38s
CD Pipeline / build-and-deploy (push) Successful in 6m8s
CD Pipeline / post-deploy-checks (push) Successful in 1m40s

This commit is contained in:
Your Name
2026-06-15 14:19:23 +08:00
parent a923e89017
commit e101931efb
15 changed files with 1453 additions and 3 deletions

View File

@@ -85,6 +85,9 @@ from src.services.ai_agent_live_read_model_gate import (
from src.services.ai_agent_12_agent_war_room import (
load_latest_ai_agent_12_agent_war_room,
)
from src.services.ai_agent_professional_task_expansion import (
load_latest_ai_agent_professional_task_expansion,
)
from src.services.ai_agent_matched_playbook_learning_gap import (
load_latest_ai_agent_matched_playbook_learning_gap,
)
@@ -760,6 +763,36 @@ async def get_agent_12_agent_war_room() -> dict[str, Any]:
) from exc
@router.get(
"/agent-professional-task-expansion",
response_model=dict[str, Any],
summary="取得 AI Agent 專業任務擴展與 Telegram Runtime Bridge 快照",
description=(
"讀取最新已提交的 P2-405A AI Agent 專業任務擴展與 Telegram Runtime Bridge 只讀快照;"
"此端點只呈現 OpenClaw、Hermes、NemoTron 與專責 Agent 可承接的專業任務、MCP/RAG、"
"風險分層、Telegram no-send preview 與後續 canary gate"
"不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production write、"
"不改主機、不執行 kubectl。"
),
)
async def get_agent_professional_task_expansion() -> dict[str, Any]:
"""回傳最新 AI Agent 專業任務擴展與 Telegram Runtime Bridge 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_professional_task_expansion)
return redact_public_lan_topology(payload)
except FileNotFoundError as exc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(exc),
) from exc
except (json.JSONDecodeError, ValueError) as exc:
logger.error("ai_agent_professional_task_expansion_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 專業任務擴展與 Telegram Runtime Bridge 快照無效",
) from exc
@router.get(
"/agent-communication-learning-contract",
response_model=dict[str, Any],

View File

@@ -0,0 +1,241 @@
"""
AI Agent professional task expansion and Telegram runtime bridge snapshot.
Loads the latest committed P2-405A read-only contract. The contract expands
professional AI Agent work and defines Telegram bridge stages, but it does not
write Telegram Gateway queues, send Telegram messages, call the Bot API, read
secrets, or execute production changes.
"""
from __future__ import annotations
import copy
import json
from pathlib import Path
from typing import Any
from src.services.snapshot_paths import default_evaluations_dir
_DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__))
_SNAPSHOT_PATTERN = "ai_agent_professional_task_expansion_*.json"
_SCHEMA_VERSION = "ai_agent_professional_task_expansion_v1"
_RUNTIME_AUTHORITY = "professional_task_expansion_and_telegram_bridge_read_only_no_send"
_EXPECTED_TASK_COUNT = 24
_EXPECTED_DOMAIN_COUNT = 8
_EXPECTED_STAGE_COUNT = 5
_EXPECTED_MESSAGE_TYPE_COUNT = 6
_ZERO_ROLLUP_FIELDS = {
"current_live_count",
"gateway_queue_write_count",
"telegram_send_count",
"bot_api_call_count",
"delivery_receipt_write_count",
"production_write_count",
"secret_read_count",
"paid_api_call_count",
"host_write_count",
"kubectl_action_count",
}
_FORBIDDEN_PUBLIC_TERMS = {
"work_window_transcript",
"raw prompt",
"private reasoning",
"chain-of-thought",
"telegram token",
"authorization header",
"secret value",
}
def load_latest_ai_agent_professional_task_expansion(
evaluations_dir: Path | None = None,
) -> dict[str, Any]:
"""Load the newest committed AI Agent professional task expansion snapshot."""
directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
if not candidates:
raise FileNotFoundError(
f"no AI Agent professional task expansion snapshots found in {directory}"
)
latest = candidates[-1]
with latest.open(encoding="utf-8") as handle:
payload = json.load(handle)
if not isinstance(payload, dict):
raise ValueError(f"{latest}: expected JSON object")
label = str(latest)
_require_schema(payload, label)
_require_telegram_bridge(payload, label)
_require_professional_tasks(payload, label)
_require_reporting_and_redaction(payload, label)
_require_rollups(payload, label)
_require_no_forbidden_public_terms(payload, label)
return payload
def _require_schema(payload: dict[str, Any], label: str) -> None:
if payload.get("schema_version") != _SCHEMA_VERSION:
raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}")
status = payload.get("program_status") or {}
expected = {
"current_priority": "P2",
"current_task_id": "P2-405A",
"next_task_id": "P2-405B",
"read_only_mode": True,
"runtime_authority": _RUNTIME_AUTHORITY,
"overall_completion_percent": 82,
}
mismatches = _mismatches(status, expected)
if mismatches:
raise ValueError(f"{label}: program_status mismatch: {mismatches}")
if not status.get("status_note"):
raise ValueError(f"{label}: program_status.status_note is required")
def _require_telegram_bridge(payload: dict[str, Any], label: str) -> None:
bridge = payload.get("telegram_runtime_bridge") or {}
expected = {
"canonical_room": "AwoooI SRE 戰情室",
"canonical_room_env": "SRE_GROUP_CHAT_ID",
"gateway_required": True,
"no_send_preview_ready": True,
"queue_preview_readback_ready": True,
"approved_canary_required": True,
"direct_bot_api_allowed": False,
"bot_api_call_enabled": False,
"gateway_queue_write_enabled": False,
"telegram_send_enabled": False,
"delivery_receipt_write_enabled": False,
}
mismatches = _mismatches(bridge, expected)
if mismatches:
raise ValueError(f"{label}: telegram_runtime_bridge mismatch: {mismatches}")
stages = bridge.get("stages") or []
if len(stages) != _EXPECTED_STAGE_COUNT:
raise ValueError(f"{label}: expected {_EXPECTED_STAGE_COUNT} Telegram stages")
if any(stage.get("live_send_enabled") is not False for stage in stages):
raise ValueError(f"{label}: Telegram stages must keep live_send_enabled false")
message_types = bridge.get("message_types") or []
if len(message_types) != _EXPECTED_MESSAGE_TYPE_COUNT:
raise ValueError(f"{label}: expected {_EXPECTED_MESSAGE_TYPE_COUNT} message types")
def _require_professional_tasks(payload: dict[str, Any], label: str) -> None:
domains = payload.get("professional_task_domains") or []
if len(domains) != _EXPECTED_DOMAIN_COUNT:
raise ValueError(f"{label}: expected {_EXPECTED_DOMAIN_COUNT} professional task domains")
domain_ids = {domain.get("domain_id") for domain in domains}
tasks = payload.get("professional_tasks") or []
if len(tasks) != _EXPECTED_TASK_COUNT:
raise ValueError(f"{label}: expected {_EXPECTED_TASK_COUNT} professional tasks")
task_ids = [task.get("task_id") for task in tasks]
if len(set(task_ids)) != len(task_ids):
raise ValueError(f"{label}: task_id values must be unique")
owners = {task.get("owner_agent") for task in tasks}
required_owners = {
"openclaw",
"hermes",
"nemotron",
"telegram_ops_liaison",
"security_sentinel",
"sre_sentinel",
"devops_commander",
}
if not required_owners.issubset(owners):
raise ValueError(f"{label}: professional tasks must include owners {sorted(required_owners)}")
for task in tasks:
task_id = task.get("task_id")
if task.get("domain_id") not in domain_ids:
raise ValueError(f"{label}: {task_id}.domain_id must reference a known domain")
if task.get("current_live_count_24h") != 0:
raise ValueError(f"{label}: {task_id}.current_live_count_24h must remain zero")
if not task.get("required_mcp"):
raise ValueError(f"{label}: {task_id}.required_mcp must not be empty")
if not task.get("required_rag"):
raise ValueError(f"{label}: {task_id}.required_rag must not be empty")
if not task.get("blocked_actions"):
raise ValueError(f"{label}: {task_id}.blocked_actions must not be empty")
risk = task.get("risk_tier")
if risk in {"high", "critical"} and task.get("approval_required") is not True:
raise ValueError(f"{label}: {task_id} high/critical tasks must require approval")
if risk == "critical" and task.get("automation_mode") not in {
"approval_required_before_execution",
"blocked_until_owner_response",
}:
raise ValueError(f"{label}: {task_id} critical tasks must stay approval/blocker gated")
def _require_reporting_and_redaction(payload: dict[str, Any], label: str) -> None:
reporting = payload.get("reporting_contract") or {}
for cadence in ("daily", "weekly", "monthly", "action_required"):
if (reporting.get(cadence) or {}).get("required") is not True:
raise ValueError(f"{label}: reporting_contract.{cadence}.required must be true")
redaction = payload.get("redaction_contract") or {}
expected = {
"redaction_required": True,
"conversation_transcript_display_allowed": False,
"raw_prompt_display_allowed": False,
"private_reasoning_display_allowed": False,
"secret_value_display_allowed": False,
"raw_runtime_payload_display_allowed": False,
"telegram_message_must_be_sanitized": True,
}
mismatches = _mismatches(redaction, expected)
if mismatches:
raise ValueError(f"{label}: redaction_contract mismatch: {mismatches}")
def _require_rollups(payload: dict[str, Any], label: str) -> None:
rollups = payload.get("rollups") or {}
tasks = payload.get("professional_tasks") or []
domains = payload.get("professional_task_domains") or []
bridge = payload.get("telegram_runtime_bridge") or {}
expected = {
"professional_task_count": len(tasks),
"domain_count": len(domains),
"telegram_stage_count": len(bridge.get("stages") or []),
"telegram_message_type_count": len(bridge.get("message_types") or []),
"approval_required_count": sum(1 for task in tasks if task.get("approval_required") is True),
"low_risk_task_count": sum(1 for task in tasks if task.get("risk_tier") == "low"),
"medium_risk_task_count": sum(1 for task in tasks if task.get("risk_tier") == "medium"),
"high_risk_task_count": sum(1 for task in tasks if task.get("risk_tier") == "high"),
"critical_risk_task_count": sum(1 for task in tasks if task.get("risk_tier") == "critical"),
}
mismatches = _mismatches(rollups, expected)
if mismatches:
raise ValueError(f"{label}: rollups mismatch: {mismatches}")
for field in _ZERO_ROLLUP_FIELDS:
if rollups.get(field) != 0:
raise ValueError(f"{label}: rollups.{field} must remain zero")
def _require_no_forbidden_public_terms(payload: dict[str, Any], label: str) -> None:
scrubbed = copy.deepcopy(payload)
redaction = scrubbed.get("redaction_contract")
if isinstance(redaction, dict):
redaction["forbidden_terms"] = []
public_text = json.dumps(scrubbed, ensure_ascii=False).lower()
leaked = sorted(term for term in _FORBIDDEN_PUBLIC_TERMS if term.lower() in public_text)
if leaked:
raise ValueError(f"{label}: forbidden public terms leaked: {leaked}")
def _mismatches(payload: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]:
return {
key: {"expected": expected_value, "actual": payload.get(key)}
for key, expected_value in expected.items()
if payload.get(key) != expected_value
}

View File

@@ -0,0 +1,126 @@
from __future__ import annotations
import copy
import json
import os
from pathlib import Path
import pytest
os.environ.setdefault("DATABASE_URL", "postgresql+asyncpg://test:test@localhost/test")
from src.services.ai_agent_professional_task_expansion import (
load_latest_ai_agent_professional_task_expansion,
)
def test_load_latest_ai_agent_professional_task_expansion_snapshot() -> None:
snapshot = load_latest_ai_agent_professional_task_expansion()
assert snapshot["schema_version"] == "ai_agent_professional_task_expansion_v1"
assert snapshot["program_status"]["current_task_id"] == "P2-405A"
assert snapshot["program_status"]["next_task_id"] == "P2-405B"
assert snapshot["program_status"]["overall_completion_percent"] == 82
assert snapshot["program_status"]["runtime_authority"] == (
"professional_task_expansion_and_telegram_bridge_read_only_no_send"
)
bridge = snapshot["telegram_runtime_bridge"]
assert bridge["canonical_room"] == "AwoooI SRE 戰情室"
assert bridge["canonical_room_env"] == "SRE_GROUP_CHAT_ID"
assert bridge["no_send_preview_ready"] is True
assert bridge["queue_preview_readback_ready"] is True
assert bridge["direct_bot_api_allowed"] is False
assert bridge["bot_api_call_enabled"] is False
assert bridge["gateway_queue_write_enabled"] is False
assert bridge["telegram_send_enabled"] is False
assert len(bridge["stages"]) == 5
assert len(bridge["message_types"]) == 6
rollups = snapshot["rollups"]
assert rollups["professional_task_count"] == 24
assert rollups["domain_count"] == 8
assert rollups["telegram_stage_count"] == 5
assert rollups["telegram_message_type_count"] == 6
assert rollups["approval_required_count"] == 19
assert rollups["low_risk_task_count"] == 3
assert rollups["medium_risk_task_count"] == 10
assert rollups["high_risk_task_count"] == 6
assert rollups["critical_risk_task_count"] == 5
assert rollups["current_live_count"] == 0
assert rollups["gateway_queue_write_count"] == 0
assert rollups["telegram_send_count"] == 0
assert rollups["bot_api_call_count"] == 0
assert rollups["delivery_receipt_write_count"] == 0
assert rollups["production_write_count"] == 0
assert rollups["secret_read_count"] == 0
assert rollups["paid_api_call_count"] == 0
assert rollups["host_write_count"] == 0
assert rollups["kubectl_action_count"] == 0
def test_professional_tasks_cover_required_agents_and_reporting() -> None:
snapshot = load_latest_ai_agent_professional_task_expansion()
owners = {task["owner_agent"] for task in snapshot["professional_tasks"]}
assert {
"openclaw",
"hermes",
"nemotron",
"telegram_ops_liaison",
"security_sentinel",
"sre_sentinel",
"devops_commander",
}.issubset(owners)
assert snapshot["reporting_contract"]["daily"]["required"] is True
assert snapshot["reporting_contract"]["weekly"]["required"] is True
assert snapshot["reporting_contract"]["monthly"]["required"] is True
assert snapshot["reporting_contract"]["action_required"]["required"] is True
assert snapshot["redaction_contract"]["conversation_transcript_display_allowed"] is False
assert snapshot["redaction_contract"]["raw_prompt_display_allowed"] is False
assert snapshot["redaction_contract"]["private_reasoning_display_allowed"] is False
assert snapshot["redaction_contract"]["secret_value_display_allowed"] is False
def test_rejects_telegram_send_enabled(tmp_path: Path) -> None:
snapshot = copy.deepcopy(load_latest_ai_agent_professional_task_expansion())
snapshot["telegram_runtime_bridge"]["telegram_send_enabled"] = True
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="telegram_runtime_bridge mismatch"):
load_latest_ai_agent_professional_task_expansion(tmp_path)
def test_rejects_gateway_queue_write_count(tmp_path: Path) -> None:
snapshot = copy.deepcopy(load_latest_ai_agent_professional_task_expansion())
snapshot["rollups"]["gateway_queue_write_count"] = 1
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="must remain zero"):
load_latest_ai_agent_professional_task_expansion(tmp_path)
def test_rejects_high_risk_without_approval(tmp_path: Path) -> None:
snapshot = copy.deepcopy(load_latest_ai_agent_professional_task_expansion())
high_task = next(task for task in snapshot["professional_tasks"] if task["risk_tier"] == "high")
high_task["approval_required"] = False
snapshot["rollups"]["approval_required_count"] -= 1
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="high/critical tasks must require approval"):
load_latest_ai_agent_professional_task_expansion(tmp_path)
def test_rejects_forbidden_public_terms_outside_policy_list(tmp_path: Path) -> None:
snapshot = copy.deepcopy(load_latest_ai_agent_professional_task_expansion())
snapshot["professional_tasks"][0]["title"] = "raw prompt leakage candidate"
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="forbidden public terms leaked"):
load_latest_ai_agent_professional_task_expansion(tmp_path)
def _write_snapshot(directory: Path, payload: dict) -> None:
path = directory / "ai_agent_professional_task_expansion_2099-01-01.json"
path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")

View File

@@ -0,0 +1,39 @@
from __future__ import annotations
import os
from fastapi.testclient import TestClient
os.environ.setdefault("DATABASE_URL", "postgresql+asyncpg://test:test@localhost/test")
from src.main import app
def test_ai_agent_professional_task_expansion_endpoint() -> None:
client = TestClient(app)
response = client.get("/api/v1/agents/agent-professional-task-expansion")
assert response.status_code == 200
payload = response.json()
assert payload["schema_version"] == "ai_agent_professional_task_expansion_v1"
assert payload["program_status"]["current_task_id"] == "P2-405A"
assert payload["program_status"]["next_task_id"] == "P2-405B"
assert payload["program_status"]["overall_completion_percent"] == 82
assert payload["program_status"]["runtime_authority"] == (
"professional_task_expansion_and_telegram_bridge_read_only_no_send"
)
assert payload["telegram_runtime_bridge"]["canonical_room"] == "AwoooI SRE 戰情室"
assert payload["telegram_runtime_bridge"]["direct_bot_api_allowed"] is False
assert payload["telegram_runtime_bridge"]["gateway_queue_write_enabled"] is False
assert payload["telegram_runtime_bridge"]["telegram_send_enabled"] is False
assert payload["telegram_runtime_bridge"]["bot_api_call_enabled"] is False
assert payload["rollups"]["professional_task_count"] == 24
assert payload["rollups"]["domain_count"] == 8
assert payload["rollups"]["approval_required_count"] == 19
assert payload["rollups"]["high_risk_task_count"] == 6
assert payload["rollups"]["critical_risk_task_count"] == 5
assert payload["rollups"]["gateway_queue_write_count"] == 0
assert payload["rollups"]["telegram_send_count"] == 0
assert payload["rollups"]["bot_api_call_count"] == 0
assert payload["rollups"]["production_write_count"] == 0

View File

@@ -6447,6 +6447,43 @@
"monthly": "月報"
}
},
"professionalTaskExpansion": {
"title": "P2-405A AI Agent 專業任務擴展",
"source": "產生 {generated};目前 {current};下一步 {next}",
"runtime": "runtime={value}",
"telegramTitle": "Telegram Runtime Bridge",
"redactionTitle": "脫敏與實發邊界",
"tasksTitle": "首批專業任務",
"metrics": {
"overall": "契約進度",
"tasks": "專業任務",
"domains": "任務領域",
"telegramStages": "TG bridge",
"messageTypes": "訊息類型",
"approvals": "需批准",
"highCritical": "高風險 / critical",
"liveWrites": "live / send / write"
},
"labels": {
"gateway": "Gateway required={value}",
"noSend": "no-send preview={value}",
"queuePreview": "queue preview={value}",
"directBot": "direct Bot API={value}",
"send": "Telegram send={value}",
"redaction": "脫敏鎖定={value}",
"gatewayWrites": "Gateway 寫入={value}",
"botCalls": "Bot API={value}",
"productionWrites": "正式寫入={value}",
"approval": "需批准={value}",
"liveCount": "live={value}"
},
"riskTiers": {
"low": "低風險",
"medium": "中風險",
"high": "高風險",
"critical": "Critical"
}
},
"resultCaptureReleaseVerifierOwnerReviewPacket": {
"title": "P2-137 釋出驗證器負責人審查包",
"source": "產生 {generated};目前 {current};下一步 {next}",

View File

@@ -6447,6 +6447,43 @@
"monthly": "月報"
}
},
"professionalTaskExpansion": {
"title": "P2-405A AI Agent 專業任務擴展",
"source": "產生 {generated};目前 {current};下一步 {next}",
"runtime": "runtime={value}",
"telegramTitle": "Telegram Runtime Bridge",
"redactionTitle": "脫敏與實發邊界",
"tasksTitle": "首批專業任務",
"metrics": {
"overall": "契約進度",
"tasks": "專業任務",
"domains": "任務領域",
"telegramStages": "TG bridge",
"messageTypes": "訊息類型",
"approvals": "需批准",
"highCritical": "高風險 / critical",
"liveWrites": "live / send / write"
},
"labels": {
"gateway": "Gateway required={value}",
"noSend": "no-send preview={value}",
"queuePreview": "queue preview={value}",
"directBot": "direct Bot API={value}",
"send": "Telegram send={value}",
"redaction": "脫敏鎖定={value}",
"gatewayWrites": "Gateway 寫入={value}",
"botCalls": "Bot API={value}",
"productionWrites": "正式寫入={value}",
"approval": "需批准={value}",
"liveCount": "live={value}"
},
"riskTiers": {
"low": "低風險",
"medium": "中風險",
"high": "高風險",
"critical": "Critical"
}
},
"resultCaptureReleaseVerifierOwnerReviewPacket": {
"title": "P2-137 釋出驗證器負責人審查包",
"source": "產生 {generated};目前 {current};下一步 {next}",

View File

@@ -41,6 +41,7 @@ import { redactPublicIdentifier } from '@/lib/public-security-redaction'
import {
apiClient,
type AiAgent12AgentWarRoomSnapshot,
type AiAgentProfessionalTaskExpansionSnapshot,
type AiAgentCandidateOperationDryRunEvidenceSnapshot,
type AiAgentCriticReviewerResultCaptureSnapshot,
type AiAgentDeploymentLayoutSnapshot,
@@ -463,6 +464,7 @@ export function AutomationInventoryTab() {
const [providerRouteMatrix, setProviderRouteMatrix] = useState<AiProviderRouteMatrixSnapshot | null>(null)
const [deploymentLayout, setDeploymentLayout] = useState<AiAgentDeploymentLayoutSnapshot | null>(null)
const [warRoom, setWarRoom] = useState<AiAgent12AgentWarRoomSnapshot | null>(null)
const [professionalTaskExpansion, setProfessionalTaskExpansion] = useState<AiAgentProfessionalTaskExpansionSnapshot | null>(null)
const [proactiveOperations, setProactiveOperations] = useState<AiAgentProactiveOperationsContractSnapshot | null>(null)
const [interactionLearningProof, setInteractionLearningProof] = useState<AiAgentInteractionLearningProofSnapshot | null>(null)
const [liveReadModelGate, setLiveReadModelGate] = useState<AiAgentLiveReadModelGateSnapshot | null>(null)
@@ -545,6 +547,7 @@ export function AutomationInventoryTab() {
apiClient.getAiProviderRouteMatrix(),
apiClient.getAiAgentDeploymentLayout(),
apiClient.getAiAgent12AgentWarRoom(),
apiClient.getAiAgentProfessionalTaskExpansion(),
apiClient.getAiAgentProactiveOperationsContract(),
apiClient.getAiAgentInteractionLearningProof(),
apiClient.getAiAgentLiveReadModelGate(),
@@ -626,6 +629,7 @@ export function AutomationInventoryTab() {
providerRouteMatrixResult,
deploymentLayoutResult,
warRoomResult,
professionalTaskExpansionResult,
proactiveOperationsResult,
interactionLearningProofResult,
liveReadModelGateResult,
@@ -704,6 +708,7 @@ export function AutomationInventoryTab() {
setProviderRouteMatrix(providerRouteMatrixResult.status === 'fulfilled' ? providerRouteMatrixResult.value : null)
setDeploymentLayout(deploymentLayoutResult.status === 'fulfilled' ? deploymentLayoutResult.value : null)
setWarRoom(warRoomResult.status === 'fulfilled' ? warRoomResult.value : null)
setProfessionalTaskExpansion(professionalTaskExpansionResult.status === 'fulfilled' ? professionalTaskExpansionResult.value : null)
setProactiveOperations(proactiveOperationsResult.status === 'fulfilled' ? proactiveOperationsResult.value : null)
setInteractionLearningProof(interactionLearningProofResult.status === 'fulfilled' ? interactionLearningProofResult.value : null)
setLiveReadModelGate(liveReadModelGateResult.status === 'fulfilled' ? liveReadModelGateResult.value : null)
@@ -780,6 +785,7 @@ export function AutomationInventoryTab() {
providerRouteMatrixResult,
deploymentLayoutResult,
warRoomResult,
professionalTaskExpansionResult,
proactiveOperationsResult,
interactionLearningProofResult,
liveReadModelGateResult,
@@ -2094,7 +2100,7 @@ export function AutomationInventoryTab() {
)
}
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !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 || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !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 || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
return (
<div style={{ padding: 20 }}>
<GlassCard variant="subtle" padding="lg">
@@ -3666,6 +3672,34 @@ export function AutomationInventoryTab() {
warRoom.display_redaction_contract.conversation_transcript_display_allowed === false
&& warRoom.display_redaction_contract.redaction_required === true
)
const professionalTaskOverall = professionalTaskExpansion.program_status.overall_completion_percent
const professionalTaskTotal = professionalTaskExpansion.rollups.professional_task_count
const professionalTaskDomains = professionalTaskExpansion.rollups.domain_count
const professionalTaskTelegramStages = professionalTaskExpansion.rollups.telegram_stage_count
const professionalTaskMessageTypes = professionalTaskExpansion.rollups.telegram_message_type_count
const professionalTaskApprovals = professionalTaskExpansion.rollups.approval_required_count
const professionalTaskHighCritical = (
professionalTaskExpansion.rollups.high_risk_task_count
+ professionalTaskExpansion.rollups.critical_risk_task_count
)
const professionalTaskLiveWrites = (
professionalTaskExpansion.rollups.current_live_count
+ professionalTaskExpansion.rollups.gateway_queue_write_count
+ professionalTaskExpansion.rollups.telegram_send_count
+ professionalTaskExpansion.rollups.bot_api_call_count
+ professionalTaskExpansion.rollups.delivery_receipt_write_count
+ professionalTaskExpansion.rollups.production_write_count
+ professionalTaskExpansion.rollups.secret_read_count
+ professionalTaskExpansion.rollups.paid_api_call_count
+ professionalTaskExpansion.rollups.host_write_count
+ professionalTaskExpansion.rollups.kubectl_action_count
)
const professionalTaskRedactionLocked = (
professionalTaskExpansion.redaction_contract.conversation_transcript_display_allowed === false
&& professionalTaskExpansion.redaction_contract.telegram_message_must_be_sanitized === true
&& professionalTaskExpansion.redaction_contract.redaction_required === true
)
const visibleProfessionalTasks = professionalTaskExpansion.professional_tasks.slice(0, 8)
const backlogProgressPercent = backlog.progress_summary.overall_percent
const explicitApprovalItemCount = backlog.item_approval_boundary_rollup.items_requiring_explicit_approval.length
const taskBoundaryCount = snapshot.task_approval_boundary_rollup.total_tasks
@@ -4052,6 +4086,98 @@ export function AutomationInventoryTab() {
</div>
</GlassCard>
<GlassCard variant="subtle" padding="md">
<div style={{ display: 'flex', flexDirection: 'column', gap: 13, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
<Boxes size={15} style={{ color: '#176d8a' }} />
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 14, fontWeight: 700, color: '#141413' }}>
{t('professionalTaskExpansion.title')}
</span>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, justifyContent: 'flex-end' }}>
<Chip
value={t('professionalTaskExpansion.source', {
generated: formatDateTime(professionalTaskExpansion.generated_at),
current: professionalTaskExpansion.program_status.current_task_id,
next: professionalTaskExpansion.program_status.next_task_id,
})}
muted
/>
<Chip value={t('professionalTaskExpansion.runtime', { value: professionalTaskExpansion.program_status.runtime_authority })} muted />
</div>
</div>
<p style={{ margin: 0, fontFamily: "'DM Mono', monospace", fontSize: 11, lineHeight: 1.55, color: '#5c5a55', overflowWrap: 'anywhere' }}>
{professionalTaskExpansion.program_status.status_note}
</p>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(128px, 1fr))', gap: 10 }} className="automation-inventory-live-read-kpi-grid">
<MetricCard label={t('professionalTaskExpansion.metrics.overall')} value={`${professionalTaskOverall}%`} tone="warn" icon={<Gauge size={16} />} />
<MetricCard label={t('professionalTaskExpansion.metrics.tasks')} value={professionalTaskTotal} tone="neutral" icon={<Boxes size={16} />} />
<MetricCard label={t('professionalTaskExpansion.metrics.domains')} value={professionalTaskDomains} tone="neutral" icon={<Layers3 size={16} />} />
<MetricCard label={t('professionalTaskExpansion.metrics.telegramStages')} value={professionalTaskTelegramStages} tone="warn" icon={<BellRing size={16} />} />
<MetricCard label={t('professionalTaskExpansion.metrics.messageTypes')} value={professionalTaskMessageTypes} tone="neutral" icon={<FileText size={16} />} />
<MetricCard label={t('professionalTaskExpansion.metrics.approvals')} value={professionalTaskApprovals} tone="warn" icon={<ShieldAlert size={16} />} />
<MetricCard label={t('professionalTaskExpansion.metrics.highCritical')} value={professionalTaskHighCritical} tone="danger" icon={<AlertTriangle size={16} />} />
<MetricCard label={t('professionalTaskExpansion.metrics.liveWrites')} value={professionalTaskLiveWrites} tone={professionalTaskLiveWrites === 0 ? 'ok' : 'danger'} icon={<BellOff size={16} />} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: 10 }}>
<div style={{ padding: 10, border: '0.5px solid #c8d2e3', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('professionalTaskExpansion.telegramTitle')}</SmallLabel>
<p style={{ margin: '6px 0 8px', fontFamily: "'DM Mono', monospace", fontSize: 10, lineHeight: 1.5, color: '#5c5a55', overflowWrap: 'anywhere' }}>
{professionalTaskExpansion.telegram_runtime_bridge.canonical_room}
</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('professionalTaskExpansion.labels.gateway', { value: String(professionalTaskExpansion.telegram_runtime_bridge.gateway_required) })} />
<Chip value={t('professionalTaskExpansion.labels.noSend', { value: String(professionalTaskExpansion.telegram_runtime_bridge.no_send_preview_ready) })} muted />
<Chip value={t('professionalTaskExpansion.labels.queuePreview', { value: String(professionalTaskExpansion.telegram_runtime_bridge.queue_preview_readback_ready) })} muted />
<Chip value={t('professionalTaskExpansion.labels.directBot', { value: String(professionalTaskExpansion.telegram_runtime_bridge.direct_bot_api_allowed) })} muted />
<Chip value={t('professionalTaskExpansion.labels.send', { value: String(professionalTaskExpansion.telegram_runtime_bridge.telegram_send_enabled) })} muted />
</div>
</div>
<div style={{ padding: 10, border: '0.5px solid #c8d2e3', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('professionalTaskExpansion.redactionTitle')}</SmallLabel>
<p style={{ margin: '6px 0 8px', fontFamily: "'DM Mono', monospace", fontSize: 10, lineHeight: 1.5, color: '#5c5a55', overflowWrap: 'anywhere' }}>
{redactPublicText(professionalTaskExpansion.redaction_contract.frontend_display_policy)}
</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('professionalTaskExpansion.labels.redaction', { value: String(professionalTaskRedactionLocked) })} />
<Chip value={t('professionalTaskExpansion.labels.gatewayWrites', { value: String(professionalTaskExpansion.rollups.gateway_queue_write_count) })} muted />
<Chip value={t('professionalTaskExpansion.labels.botCalls', { value: String(professionalTaskExpansion.rollups.bot_api_call_count) })} muted />
<Chip value={t('professionalTaskExpansion.labels.productionWrites', { value: String(professionalTaskExpansion.rollups.production_write_count) })} muted />
</div>
</div>
</div>
<SmallLabel>{t('professionalTaskExpansion.tasksTitle')}</SmallLabel>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{visibleProfessionalTasks.map(task => {
const taskTone = task.risk_tier === 'critical' ? 'danger' : task.risk_tier === 'high' ? 'warn' : 'neutral'
const taskColor = toneColor(taskTone)
return (
<div key={task.task_id} style={{ padding: 10, border: `0.5px solid ${taskColor}55`, 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' }}>
{task.title}
</span>
<Chip value={t(`professionalTaskExpansion.riskTiers.${task.risk_tier}` as never)} muted={task.risk_tier !== 'critical'} />
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={task.owner_agent} muted />
<Chip value={t('professionalTaskExpansion.labels.approval', { value: String(task.approval_required) })} muted />
<Chip value={t('professionalTaskExpansion.labels.liveCount', { value: String(task.current_live_count_24h) })} muted />
<Chip value={task.telegram_policy} muted />
</div>
</div>
)
})}
</div>
</div>
</GlassCard>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.05fr) minmax(0, 0.95fr)', gap: 12 }} className="automation-inventory-command-grid">
<GlassCard variant="subtle" padding="md">
<div style={{ display: 'flex', flexDirection: 'column', gap: 13, minWidth: 0 }}>

View File

@@ -334,6 +334,11 @@ export const apiClient = {
return handleResponse<AiAgent12AgentWarRoomSnapshot>(res)
},
async getAiAgentProfessionalTaskExpansion() {
const res = await fetch(`${API_BASE_URL}/agents/agent-professional-task-expansion`)
return handleResponse<AiAgentProfessionalTaskExpansionSnapshot>(res)
},
async getAiAgentProactiveOperationsContract() {
const res = await fetch(`${API_BASE_URL}/agents/agent-proactive-operations-contract`)
return handleResponse<AiAgentProactiveOperationsContractSnapshot>(res)
@@ -1418,6 +1423,110 @@ export interface AiAgent12AgentWarRoomSnapshot {
}
}
export interface AiAgentProfessionalTaskExpansionSnapshot {
schema_version: 'ai_agent_professional_task_expansion_v1'
generated_at: string
program_status: {
overall_completion_percent: number
current_priority: 'P0' | 'P1' | 'P2' | 'P3'
current_task_id: 'P2-405A'
next_task_id: 'P2-405B'
read_only_mode: true
runtime_authority: 'professional_task_expansion_and_telegram_bridge_read_only_no_send'
status_note: string
}
source_refs: string[]
telegram_runtime_bridge: {
canonical_room: string
canonical_room_env: string
gateway_required: boolean
gateway_owner: string
arbiter: string
receipt_owner: string
replay_owner: string
no_send_preview_ready: boolean
queue_preview_readback_ready: boolean
approved_canary_required: boolean
direct_bot_api_allowed: boolean
bot_api_call_enabled: boolean
gateway_queue_write_enabled: boolean
telegram_send_enabled: boolean
delivery_receipt_write_enabled: boolean
stages: Array<{
stage_id: string
title: string
status: string
allowed_output: string
live_send_enabled: boolean
exit_condition: string
}>
message_types: Array<{
message_type_id: string
title: string
cadence: string
risk_scope: string
delivery_gate: string
}>
}
professional_task_domains: Array<{
domain_id: string
title: string
summary: string
primary_owner: string
task_count: number
}>
professional_tasks: Array<{
task_id: string
title: string
domain_id: string
owner_agent: string
risk_tier: 'low' | 'medium' | 'high' | 'critical'
automation_mode: string
telegram_policy: string
required_mcp: string[]
required_rag: string[]
approval_required: boolean
current_live_count_24h: number
blocked_actions: string[]
}>
reporting_contract: Record<'daily' | 'weekly' | 'monthly' | 'action_required', {
required: boolean
delivery_mode: string
sections: string[]
}>
redaction_contract: {
redaction_required: boolean
conversation_transcript_display_allowed: boolean
raw_prompt_display_allowed: boolean
private_reasoning_display_allowed: boolean
secret_value_display_allowed: boolean
raw_runtime_payload_display_allowed: boolean
telegram_message_must_be_sanitized: boolean
frontend_display_policy: string
}
rollups: {
professional_task_count: number
domain_count: number
telegram_stage_count: number
telegram_message_type_count: number
approval_required_count: number
low_risk_task_count: number
medium_risk_task_count: number
high_risk_task_count: number
critical_risk_task_count: number
current_live_count: number
gateway_queue_write_count: number
telegram_send_count: number
bot_api_call_count: number
delivery_receipt_write_count: number
production_write_count: number
secret_read_count: number
paid_api_call_count: number
host_write_count: number
kubectl_action_count: number
}
}
export interface AiAgentProactiveOperationsContractSnapshot {
schema_version: 'ai_agent_proactive_operations_contract_v1'
generated_at: string