feat(governance): 新增 AI Agent 專業任務擴展
This commit is contained in:
@@ -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],
|
||||
|
||||
241
apps/api/src/services/ai_agent_professional_task_expansion.py
Normal file
241
apps/api/src/services/ai_agent_professional_task_expansion.py
Normal 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
|
||||
}
|
||||
126
apps/api/tests/test_ai_agent_professional_task_expansion.py
Normal file
126
apps/api/tests/test_ai_agent_professional_task_expansion.py
Normal 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")
|
||||
@@ -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
|
||||
@@ -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}",
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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 }}>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user