Files
awoooi/apps/api/src/api/v1/agents.py
2026-06-27 22:59:31 +08:00

3948 lines
177 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Agent Teams API - Phase 9.5 多專家協作系統
==========================================
Endpoints:
- POST /api/v1/agents/analyze - 觸發 Agent Teams 分析
- GET /api/v1/agents/status/{task_id} - 查詢分析狀態
- GET /api/v1/agents/result/{task_id} - 取得分析結果
- GET /api/v1/agents/stream/{task_id} - SSE 串流進度
Phase 9.4-9.5 核心功能:
1. ConsensusEngine 整合多專家意見
2. BackgroundTasks 執行長時間分析
3. Redis Working Memory 儲存結果
4. SSE 推送即時進度
Phase 17 技術債修復 (2026-03-26):
- Router 層不再直接存取 Redis
- 所有業務邏輯封裝至 AgentService
- 符合 leWOOOgo 積木化原則
統帥鐵律:
- 所有分析任務必須可追蹤 (task_id)
- 超過 60 秒的分析必須用 BackgroundTasks
- 結果必須存入 Redis (7 天 TTL)
"""
import asyncio
import json
from typing import Any
from fastapi import APIRouter, BackgroundTasks, HTTPException, status
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from src.core.logging import get_logger
from src.core.sse import get_publisher
from src.services.agent_market_governance_snapshot import (
load_latest_agent_market_governance_snapshot,
)
from src.services.agent_service import (
AgentService,
TaskState,
get_agent_service,
)
from src.services.ai_agent_12_agent_war_room import (
load_latest_ai_agent_12_agent_war_room,
)
from src.services.ai_agent_action_audit_ledger import (
load_latest_ai_agent_action_audit_ledger,
)
from src.services.ai_agent_action_owner_acceptance_event_bus import (
load_latest_ai_agent_action_owner_acceptance_event_bus,
)
from src.services.ai_agent_automation_backlog_snapshot import (
load_latest_ai_agent_automation_backlog_snapshot,
)
from src.services.ai_agent_automation_inventory_snapshot import (
load_latest_ai_agent_automation_inventory_snapshot,
)
from src.services.ai_agent_autonomous_runtime_control import (
build_ai_agent_autonomous_runtime_control,
)
from src.services.ai_agent_candidate_operation_dry_run_evidence import (
load_latest_ai_agent_candidate_operation_dry_run_evidence,
)
from src.services.ai_agent_canonical_runtime_readback_owner_acceptance import (
load_latest_ai_agent_canonical_runtime_readback_owner_acceptance,
)
from src.services.ai_agent_communication_learning_contract import (
load_latest_ai_agent_communication_learning_contract,
)
from src.services.ai_agent_controlled_executor_handoff import (
load_latest_ai_agent_controlled_executor_handoff,
)
from src.services.ai_agent_critic_reviewer_result_capture import (
load_latest_ai_agent_critic_reviewer_result_capture,
)
from src.services.ai_agent_deployment_layout import (
load_latest_ai_agent_deployment_layout,
)
from src.services.ai_agent_failure_receipt_no_send_replay import (
load_latest_ai_agent_failure_receipt_no_send_replay,
)
from src.services.ai_agent_gitea_pr_draft_lane import (
load_latest_ai_agent_gitea_pr_draft_lane,
)
from src.services.ai_agent_high_risk_owner_review_queue import (
load_latest_ai_agent_high_risk_owner_review_queue,
)
from src.services.ai_agent_host_stateful_version_inventory import (
load_latest_ai_agent_host_stateful_version_inventory,
)
from src.services.ai_agent_interaction_learning_proof import (
load_latest_ai_agent_interaction_learning_proof,
)
from src.services.ai_agent_learning_writeback_approval_package import (
load_latest_ai_agent_learning_writeback_approval_package,
)
from src.services.ai_agent_live_read_model_gate import (
load_latest_ai_agent_live_read_model_gate,
)
from src.services.ai_agent_low_medium_risk_whitelist import (
load_latest_ai_agent_low_medium_risk_whitelist,
)
from src.services.ai_agent_market_radar_readback import (
load_latest_ai_agent_market_radar_readback,
)
from src.services.ai_agent_matched_playbook_learning_gap import (
load_latest_ai_agent_matched_playbook_learning_gap,
)
from src.services.ai_agent_operation_permission_model import (
load_latest_ai_agent_operation_permission_model,
)
from src.services.ai_agent_owner_approved_fixture_dry_run import (
load_latest_ai_agent_owner_approved_fixture_dry_run,
)
from src.services.ai_agent_owner_approved_fixture_promotion_gate import (
load_latest_ai_agent_owner_approved_fixture_promotion_gate,
)
from src.services.ai_agent_owner_approved_learning_dry_run import (
load_latest_ai_agent_owner_approved_learning_dry_run,
)
from src.services.ai_agent_owner_approved_result_capture_dry_run import (
load_latest_ai_agent_owner_approved_result_capture_dry_run,
)
from src.services.ai_agent_owner_approved_result_capture_promotion_dry_run import (
load_latest_ai_agent_owner_approved_result_capture_promotion_dry_run,
)
from src.services.ai_agent_owner_approved_result_capture_readback import (
load_latest_ai_agent_owner_approved_result_capture_readback,
)
from src.services.ai_agent_post_write_verifier_package import (
load_latest_ai_agent_post_write_verifier_package,
)
from src.services.ai_agent_proactive_operations_contract import (
load_latest_ai_agent_proactive_operations_contract,
)
from src.services.ai_agent_professional_task_expansion import (
load_latest_ai_agent_professional_task_expansion,
)
from src.services.ai_agent_receipt_readback_owner_review import (
load_latest_ai_agent_receipt_readback_owner_review,
)
from src.services.ai_agent_redis_dry_run_gate import (
load_latest_ai_agent_redis_dry_run_gate,
)
from src.services.ai_agent_report_automation_review import (
load_latest_ai_agent_report_automation_review,
)
from src.services.ai_agent_report_live_delivery_approval_package import (
load_latest_ai_agent_report_live_delivery_approval_package,
)
from src.services.ai_agent_report_no_write_analysis_runtime import (
load_latest_ai_agent_report_no_write_analysis_runtime,
)
from src.services.ai_agent_report_runtime_dry_run import (
load_latest_ai_agent_report_runtime_dry_run,
)
from src.services.ai_agent_report_runtime_fixture_readback import (
load_latest_ai_agent_report_runtime_fixture_readback,
)
from src.services.ai_agent_report_runtime_readiness import (
load_latest_ai_agent_report_runtime_readiness,
)
from src.services.ai_agent_report_source_health import (
build_ai_agent_report_source_health,
)
from src.services.ai_agent_report_status_board import (
load_latest_ai_agent_report_status_board,
)
from src.services.ai_agent_report_truth_actionability_review import (
load_latest_ai_agent_report_truth_actionability_review,
)
from src.services.ai_agent_result_capture_final_release_candidate_readback import (
load_latest_ai_agent_result_capture_final_release_candidate_readback,
)
from src.services.ai_agent_result_capture_no_write_readback import (
load_latest_ai_agent_result_capture_no_write_readback,
)
from src.services.ai_agent_result_capture_owner_acceptance_maintenance_gate import (
load_latest_ai_agent_result_capture_owner_acceptance_maintenance_gate,
)
from src.services.ai_agent_result_capture_owner_acceptance_readback_preflight_hold import (
load_latest_ai_agent_result_capture_owner_acceptance_readback_preflight_hold,
)
from src.services.ai_agent_result_capture_owner_approved_execution_rehearsal import (
load_latest_ai_agent_result_capture_owner_approved_execution_rehearsal,
)
from src.services.ai_agent_result_capture_owner_approved_preflight_release_package import (
load_latest_ai_agent_result_capture_owner_approved_preflight_release_package,
)
from src.services.ai_agent_result_capture_owner_approved_release_readiness_readback import (
load_latest_ai_agent_result_capture_owner_approved_release_readiness_readback,
)
from src.services.ai_agent_result_capture_owner_promotion_review import (
load_latest_ai_agent_result_capture_owner_promotion_review,
)
from src.services.ai_agent_result_capture_owner_release_approval_gate import (
load_latest_ai_agent_result_capture_owner_release_approval_gate,
)
from src.services.ai_agent_result_capture_post_release_verifier_rollback_gate import (
load_latest_ai_agent_result_capture_post_release_verifier_rollback_gate,
)
from src.services.ai_agent_result_capture_promotion_approval_gate import (
load_latest_ai_agent_result_capture_promotion_approval_gate,
)
from src.services.ai_agent_result_capture_release_authorization_hold import (
load_latest_ai_agent_result_capture_release_authorization_hold,
)
from src.services.ai_agent_result_capture_release_authorization_readback_gate import (
load_latest_ai_agent_result_capture_release_authorization_readback_gate,
)
from src.services.ai_agent_result_capture_release_decision_hold import (
load_latest_ai_agent_result_capture_release_decision_hold,
)
from src.services.ai_agent_result_capture_release_decision_input_prep import (
load_latest_ai_agent_result_capture_release_decision_input_prep,
)
from src.services.ai_agent_result_capture_release_decision_next_handoff import (
load_latest_ai_agent_result_capture_release_decision_next_handoff,
)
from src.services.ai_agent_result_capture_release_decision_owner_response_acceptance_gate import (
load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate,
)
from src.services.ai_agent_result_capture_release_decision_owner_response_preflight import (
load_latest_ai_agent_result_capture_release_decision_owner_response_preflight,
)
from src.services.ai_agent_result_capture_release_decision_owner_response_readback import (
load_latest_ai_agent_result_capture_release_decision_owner_response_readback,
)
from src.services.ai_agent_result_capture_release_decision_readback import (
load_latest_ai_agent_result_capture_release_decision_readback,
)
from src.services.ai_agent_result_capture_release_verifier_owner_review_packet import (
load_latest_ai_agent_result_capture_release_verifier_owner_review_packet,
)
from src.services.ai_agent_result_capture_release_verifier_preflight_gate import (
load_latest_ai_agent_result_capture_release_verifier_preflight_gate,
)
from src.services.ai_agent_result_capture_write_gate_review import (
load_latest_ai_agent_result_capture_write_gate_review,
)
from src.services.ai_agent_result_capture_writer_dry_run_fixture import (
load_latest_ai_agent_result_capture_writer_dry_run_fixture,
)
from src.services.ai_agent_result_capture_writer_dry_run_readback import (
load_latest_ai_agent_result_capture_writer_dry_run_readback,
)
from src.services.ai_agent_result_capture_writer_implementation_review import (
load_latest_ai_agent_result_capture_writer_implementation_review,
)
from src.services.ai_agent_reviewer_queue_no_write_readback import (
load_latest_ai_agent_reviewer_queue_no_write_readback,
)
from src.services.ai_agent_runtime_readback_approval_package import (
load_latest_ai_agent_runtime_readback_approval_package,
)
from src.services.ai_agent_runtime_readback_fixture_approval import (
load_latest_ai_agent_runtime_readback_fixture_approval,
)
from src.services.ai_agent_runtime_readback_implementation_review import (
load_latest_ai_agent_runtime_readback_implementation_review,
)
from src.services.ai_agent_runtime_readback_promotion_gate import (
load_latest_ai_agent_runtime_readback_promotion_gate,
)
from src.services.ai_agent_runtime_verifier_evidence_review import (
load_latest_ai_agent_runtime_verifier_evidence_review,
)
from src.services.ai_agent_runtime_worker_shadow_gate import (
load_latest_ai_agent_runtime_worker_shadow_gate,
)
from src.services.ai_agent_runtime_write_gate_review import (
load_latest_ai_agent_runtime_write_gate_review,
)
from src.services.ai_agent_task_result_audit_trail import (
load_latest_ai_agent_task_result_audit_trail,
)
from src.services.ai_agent_telegram_action_required_digest_policy import (
load_latest_ai_agent_telegram_action_required_digest_policy,
)
from src.services.ai_agent_telegram_receipt_approval_package import (
load_latest_ai_agent_telegram_receipt_approval_package,
)
from src.services.ai_agent_tool_adoption_approval_package import (
load_latest_ai_agent_tool_adoption_approval_package,
)
from src.services.ai_agent_version_freshness_snapshot import (
load_latest_ai_agent_version_freshness_snapshot,
)
from src.services.ai_agent_version_lifecycle_update_proposal import (
load_latest_ai_agent_version_lifecycle_update_proposal,
)
from src.services.ai_provider_route_matrix import (
load_latest_ai_provider_route_matrix,
)
from src.services.ai_technology_radar_readback import (
load_latest_ai_technology_radar_readback,
)
from src.services.ai_technology_report_cadence_readback import (
load_latest_ai_technology_report_cadence_readback,
)
from src.services.awoooi_status_cleanup_dashboard import (
load_latest_awoooi_status_cleanup_dashboard,
)
from src.services.backup_dr_readiness_matrix import (
load_latest_backup_dr_readiness_matrix,
)
from src.services.backup_dr_target_inventory import (
load_latest_backup_dr_target_inventory,
)
from src.services.backup_notification_policy import (
load_latest_backup_notification_policy,
)
from src.services.backup_restore_drill_approval_package_template import (
load_latest_backup_restore_drill_approval_package_template,
)
from src.services.delivery_closure_workbench import (
load_delivery_closure_workbench,
)
from src.services.dependency_drift_check_plan import (
load_latest_dependency_drift_check_plan,
)
from src.services.dependency_risk_policy import (
load_latest_dependency_risk_policy,
)
from src.services.dependency_supply_chain_drift_monitor import (
load_latest_dependency_supply_chain_drift_monitor,
)
from src.services.dependency_upgrade_approval_package_template import (
load_latest_dependency_upgrade_approval_package_template,
)
from src.services.docker_build_surface_inventory import (
load_latest_docker_build_surface_inventory,
)
from src.services.gitea_workflow_runner_health import (
load_latest_gitea_workflow_runner_health,
)
from src.services.github_target_private_backup_evidence_gate import (
load_latest_github_target_private_backup_evidence_gate,
preflight_github_target_owner_response_submission,
validate_github_target_safe_credential_evidence_refs,
)
from src.services.host_runaway_aiops_loop_readiness import (
load_latest_host_runaway_aiops_loop_readiness,
)
from src.services.javascript_package_inventory import (
load_latest_javascript_package_inventory,
)
from src.services.observability_contract_matrix import (
load_latest_observability_contract_matrix,
)
from src.services.offsite_escrow_readiness_status import (
load_latest_offsite_escrow_readiness_status,
)
from src.services.package_supply_chain_inventory import (
load_latest_package_supply_chain_inventory,
)
from src.services.product_code_review_gate import (
load_latest_product_code_review_gate,
)
from src.services.public_redaction import redact_public_lan_topology
from src.services.runtime_surface_inventory import (
load_latest_runtime_surface_inventory,
)
from src.services.service_health_failure_notification_policy import (
load_latest_service_health_failure_notification_policy,
)
from src.services.service_health_gap_matrix import (
load_latest_service_health_gap_matrix,
)
router = APIRouter(prefix="/agents", tags=["Agent Teams"])
logger = get_logger("awoooi.agents")
# =============================================================================
# Request/Response Models
# =============================================================================
class AnalyzeRequest(BaseModel):
"""分析請求"""
incident_id: str | None = Field(
None,
description="現有 Incident ID (二選一)"
)
# 或直接提供 Incident 資訊
severity: str | None = Field(
None,
description="事件嚴重度 (P0/P1/P2/P3)"
)
affected_services: list[str] | None = Field(
None,
description="受影響服務列表"
)
alert_names: list[str] | None = Field(
None,
description="告警名稱列表"
)
context: dict[str, Any] | None = Field(
None,
description="額外上下文"
)
class AnalyzeResponse(BaseModel):
"""分析回應"""
task_id: str
status: str
message: str
estimated_seconds: int = 30
class TaskStatusResponse(BaseModel):
"""任務狀態回應"""
task_id: str
state: str
progress: int # 0-100
current_step: str | None = None
agents_completed: int = 0
total_agents: int = 4
started_at: str | None = None
completed_at: str | None = None
error: str | None = None
class TaskResultResponse(BaseModel):
"""任務結果回應"""
task_id: str
state: str
consensus_id: str | None = None
incident_id: str | None = None
consensus_score: float | None = None
recommended_action: str | None = None
recommended_kubectl: str | None = None
risk_level: str | None = None
final_reasoning: str | None = None
opinions: list[dict[str, Any]] | None = None
dissenting_opinions: list[str] | None = None
created_at: str | None = None
# =============================================================================
# Background Task Wrapper
# =============================================================================
async def _run_analysis_task(
service: AgentService,
task_id: str,
incident_id: str,
) -> None:
"""
背景任務包裝器
從 AgentService 取得 Incident 並執行分析
"""
incident = await service.get_incident(incident_id)
if incident is None:
logger.error("background_task_incident_not_found", incident_id=incident_id)
return
await service.run_analysis(task_id, incident)
# =============================================================================
# API Endpoints
# =============================================================================
@router.post(
"/analyze",
response_model=AnalyzeResponse,
summary="觸發 Agent Teams 分析",
description="""
觸發多專家協作分析。
可提供:
- 現有 Incident ID (從 Redis 讀取)
- 或直接提供事件資訊 (severity, affected_services, alert_names)
分析在背景執行,使用 task_id 追蹤進度。
專家團隊:
- SRE Agent: 系統穩定性分析
- Security Agent: 資安風險評估
- Cost Agent: 成本效益分析
- Performance Agent: 效能優化建議
""",
)
async def analyze(
request: AnalyzeRequest,
background_tasks: BackgroundTasks,
) -> AnalyzeResponse:
"""
觸發 Agent Teams 分析
返回 task_id 用於追蹤進度
"""
service = get_agent_service()
# 取得或建立 Incident
if request.incident_id:
# 從 Redis 讀取現有 Incident
incident = await service.get_incident(request.incident_id)
if incident is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Incident not found: {request.incident_id}",
)
elif request.severity and request.affected_services:
# 建立臨時 Incident
incident = service.create_adhoc_incident(
severity=request.severity,
affected_services=request.affected_services,
alert_names=request.alert_names,
)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Must provide either incident_id or (severity + affected_services)",
)
# 建立任務
task_id = await service.create_analysis_task(incident, trigger="manual")
# 加入背景任務
background_tasks.add_task(
service.run_analysis,
task_id,
incident,
)
logger.info(
"agent_analysis_started",
task_id=task_id,
incident_id=incident.incident_id,
severity=incident.severity.value,
)
return AnalyzeResponse(
task_id=task_id,
status="pending",
message="Agent Teams 分析已啟動",
estimated_seconds=30,
)
@router.get(
"/status/{task_id}",
response_model=TaskStatusResponse,
summary="查詢分析狀態",
description="查詢 Agent Teams 分析任務的目前狀態與進度。",
)
async def get_status(task_id: str) -> TaskStatusResponse:
"""
查詢任務狀態
返回進度百分比與目前步驟
"""
service = get_agent_service()
task_data = await service.get_task_status(task_id)
if task_data is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Task not found: {task_id}",
)
return TaskStatusResponse(
task_id=task_id,
state=task_data.get("state", "unknown"),
progress=task_data.get("progress", 0),
current_step=task_data.get("current_step"),
agents_completed=task_data.get("agents_completed", 0),
total_agents=task_data.get("total_agents", 4),
started_at=task_data.get("started_at"),
completed_at=task_data.get("completed_at"),
error=task_data.get("error"),
)
@router.get(
"/result/{task_id}",
response_model=TaskResultResponse,
summary="取得分析結果",
description="取得 Agent Teams 分析的完整結果,包含所有專家意見與共識決策。",
)
async def get_result(task_id: str) -> TaskResultResponse:
"""
取得分析結果
只有 COMPLETED 狀態才有完整結果
"""
service = get_agent_service()
task_data = await service.get_task_result(task_id)
if task_data is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Task not found: {task_id}",
)
return TaskResultResponse(
task_id=task_id,
state=task_data.get("state", "unknown"),
consensus_id=task_data.get("consensus_id"),
incident_id=task_data.get("incident_id"),
consensus_score=task_data.get("consensus_score"),
recommended_action=task_data.get("recommended_action"),
recommended_kubectl=task_data.get("recommended_kubectl"),
risk_level=task_data.get("risk_level"),
final_reasoning=task_data.get("final_reasoning"),
opinions=task_data.get("opinions"),
dissenting_opinions=task_data.get("dissenting_opinions"),
created_at=task_data.get("completed_at"),
)
@router.get(
"/stream/{task_id}",
summary="SSE 串流進度",
description="透過 Server-Sent Events 即時接收分析進度更新。",
)
async def stream_progress(task_id: str) -> StreamingResponse:
"""
SSE 串流分析進度
客戶端可訂閱此端點接收即時更新
"""
service = get_agent_service()
# 驗證任務存在
task_data = await service.get_task_status(task_id)
if task_data is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Task not found: {task_id}",
)
async def generate():
"""SSE 串流生成器"""
publisher = await get_publisher()
client = await publisher.subscribe(
topics=[f"agent_task:{task_id}"],
metadata={"task_id": task_id},
)
try:
# 發送初始狀態
current_data = await service.get_task_status(task_id)
if current_data:
payload = json.dumps(
{"type": "status", **current_data},
ensure_ascii=False,
)
yield f"data: {payload}\n\n"
# 串流後續更新
async for event_str in publisher.stream(client):
yield event_str
# 檢查是否完成或失敗
current_data = await service.get_task_status(task_id)
if current_data:
final_states = [TaskState.COMPLETED.value, TaskState.FAILED.value]
if current_data.get("state") in final_states:
break
except asyncio.CancelledError:
logger.info("agent_stream_cancelled", task_id=task_id)
raise
finally:
await publisher.unsubscribe(client.id)
return StreamingResponse(
generate(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
},
)
@router.get(
"/market-governance-snapshot",
response_model=dict[str, Any],
summary="取得 AI Agent 市場治理快照",
description=(
"讀取最新已提交的 Agent market governance snapshot"
"此 endpoint 不呼叫外部來源、不批准 SDK/API/replay/shadow/canary/production change。"
),
)
async def get_market_governance_snapshot() -> dict[str, Any]:
"""Return the latest read-only Agent market governance snapshot."""
try:
return await asyncio.to_thread(load_latest_agent_market_governance_snapshot)
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("agent_market_governance_snapshot_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Agent market governance snapshot is invalid",
) from exc
@router.get(
"/ai-agent-market-radar-readback",
response_model=dict[str, Any],
summary="取得 AI Agent 市場雷達與近期變更盤點",
description=(
"讀取最新已提交的 AI Agent 市場雷達 readback"
"此端點只呈現近期治理變更、市場主流 Agent 技術來源、候選角色、優先工作清單與封鎖 gate。"
"它不呼叫外部來源、不安裝 SDK、不呼叫付費 API、不跑 replay、不進 shadow/canary、"
"不送 Telegram、不改主機、不修改 workflow、不替換 OpenClaw。"
),
)
async def get_ai_agent_market_radar_readback() -> dict[str, Any]:
"""回傳最新 AI Agent 市場雷達與近期變更盤點只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_market_radar_readback)
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_market_radar_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 市場雷達 readback 無效",
) from exc
@router.get(
"/ai-technology-radar-readback",
response_model=dict[str, Any],
summary="取得 AI 技術雷達與滾動更新讀回",
description=(
"讀取最新已提交的 AI 技術雷達 readback"
"此端點只呈現 AI 技術 primary sources、技術領域、審核佇列、Agent 分工與滾動更新 Gate。"
"它不呼叫外部來源、不安裝 SDK、不呼叫付費 API、不切換模型、不送 Telegram、"
"不改主機、不修改 production routing、不替換 OpenClaw。"
),
)
async def get_ai_technology_radar_readback() -> dict[str, Any]:
"""回傳最新 AI 技術雷達與滾動更新只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_technology_radar_readback)
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_technology_radar_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI 技術雷達 readback 無效",
) from exc
@router.get(
"/ai-technology-report-cadence-readback",
response_model=dict[str, Any],
summary="取得 AI 技術雷達日週月報與報告後分析讀回",
description=(
"讀取最新已提交的 AI 技術雷達日報、週報、月報 readback"
"此端點只呈現報告節奏、Agent 工作狀態、圖表化摘要、報告後 AI 分析包、"
"低中高風險處理邊界與 Telegram no-send 審核包。"
"它不送 Telegram、不寫 receipt、不呼叫 Bot API、不執行低中風險 runtime write、"
"不安裝 SDK、不呼叫付費 API、不切換模型、不改主機、不修改 production routing、不替換 OpenClaw。"
),
)
async def get_ai_technology_report_cadence_readback() -> dict[str, Any]:
"""回傳 AI 技術雷達日週月報與報告後分析只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_technology_report_cadence_readback)
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_technology_report_cadence_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI 技術雷達日週月報 readback 無效",
) from exc
@router.get(
"/automation-inventory-snapshot",
response_model=dict[str, Any],
summary="取得 AI Agent 自動化盤點快照",
description=(
"讀取最新已提交的 AI Agent 自動化盤點快照;"
"此端點不呼叫外部來源、不碰 DB/Redis、不批准 SDK/API/shadow/canary/生產變更。"
),
)
async def get_automation_inventory_snapshot() -> dict[str, Any]:
"""Return the latest read-only AI Agent automation inventory snapshot."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_automation_inventory_snapshot)
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_automation_inventory_snapshot_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent automation inventory snapshot is invalid",
) from exc
@router.get(
"/agent-autonomous-runtime-control",
response_model=dict[str, Any],
summary="取得 AI Agent 目前有效自主化控制層",
description=(
"回傳目前有效的 AI Agent 自主化控制層;此端點明確覆寫舊 no-send / no-live "
"歷史快照,宣告 low / medium / high 風險可在 allowlist、Ansible check-mode、"
"controlled apply、post-apply verifier、KM 與 Telegram Gateway receipt 下受控自動處理。"
"它不讀 secret、不呼叫 Bot API、不暴露 chat id、不執行 runtime 動作runtime 動作由既有 worker / Gateway 接手。"
),
)
async def get_agent_autonomous_runtime_control() -> dict[str, Any]:
"""回傳目前有效 AI Agent 自主化控制層。"""
try:
return await asyncio.to_thread(build_ai_agent_autonomous_runtime_control)
except ValueError as exc:
logger.error("ai_agent_autonomous_runtime_control_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 目前有效自主化控制層無效",
) from exc
@router.get(
"/automation-backlog-snapshot",
response_model=dict[str, Any],
summary="取得 AI Agent 自動化待辦快照",
description=(
"讀取最新已提交的 AI Agent 自動化待辦快照;"
"此端點不呼叫外部來源、不碰 DB/Redis、不批准 SDK/API/shadow/canary/生產變更。"
),
)
async def get_automation_backlog_snapshot() -> dict[str, Any]:
"""Return the latest read-only AI Agent automation backlog snapshot."""
try:
return await asyncio.to_thread(load_latest_ai_agent_automation_backlog_snapshot)
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_automation_backlog_snapshot_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent automation backlog snapshot is invalid",
) from exc
@router.get(
"/agent-deployment-layout",
response_model=dict[str, Any],
summary="取得 AI Agent 佈建布局快照",
description=(
"讀取最新已提交的 OpenClaw / Hermes / NemoTron 佈建布局快照;"
"此端點不部署 Agent、不呼叫外部模型、不送 Telegram、不碰 DB/Redis、不讀 Secret payload、"
"不批准 SDK/API/shadow/canary/生產路由或主機變更。"
),
)
async def get_agent_deployment_layout() -> dict[str, Any]:
"""Return the latest read-only AI Agent deployment layout snapshot."""
try:
return await asyncio.to_thread(load_latest_ai_agent_deployment_layout)
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_deployment_layout_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 佈建布局快照無效",
) from exc
@router.get(
"/awoooi-status-cleanup-dashboard",
response_model=dict[str, Any],
summary="取得 AWOOOI 狀態清理儀表板",
description=(
"讀取最新已提交的 AWOOOI Status Cleanup Dashboard 只讀快照;"
"此端點只呈現狀態清理完成度、owner gate、apply gate、artifact sync 與 Wazuh handoff 邊界。"
"它不更新 project_current_status / memory、不同步 raw Codex App DB / auth / conversations / sessions、"
"不呼叫 Wazuh live API、不執行 active response、不改主機、不修改 workflow / repo refs、"
"不執行 backup / restore / migration。"
),
)
async def get_awoooi_status_cleanup_dashboard() -> dict[str, Any]:
"""回傳最新 AWOOOI 狀態清理儀表板只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_awoooi_status_cleanup_dashboard)
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("awoooi_status_cleanup_dashboard_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AWOOOI 狀態清理儀表板快照無效",
) from exc
@router.get(
"/delivery-closure-workbench",
response_model=dict[str, Any],
summary="取得交付閉環工作台彙總",
description=(
"彙總 AWOOOI 狀態清理、GitHub 私有備援、Gitea / CI-CD、Runtime surface "
"與 Backup / DR 的既有只讀快照,回傳前端工作台可直接使用的交付主線、"
"完成度、阻擋數與下一步。此端點不呼叫 GitHub / Gitea / Wazuh / K8s live API、"
"不建立 repo、不改 visibility、不同步 refs、不觸發 workflow、不執行備份或還原、"
"不讀 secret、不做 runtime write。"
),
)
async def get_delivery_closure_workbench() -> dict[str, Any]:
"""回傳 AWOOOI 交付閉環工作台只讀彙總。"""
try:
payload = await asyncio.to_thread(load_delivery_closure_workbench)
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("delivery_closure_workbench_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AWOOOI 交付閉環工作台彙總無效",
) from exc
@router.get(
"/github-target-private-backup-evidence-gate",
response_model=dict[str, Any],
summary="取得 GitHub 私有備援證據閘門",
description=(
"彙整既有 GitHub target decision、owner response、approval package 與 probe snapshot"
"用只讀方式判定 GitHub 備援是否具備 private visibility、safe credential 與 owner evidence。"
"此端點不呼叫 GitHub live API、不建立 repo、不改 visibility、不同步 refs、不觸發 workflow、"
"不收 private clone URL credential 或任何 secret value。"
),
)
async def get_github_target_private_backup_evidence_gate() -> dict[str, Any]:
"""回傳 GitHub 私有備援 evidence gate 只讀彙總。"""
try:
payload = await asyncio.to_thread(load_latest_github_target_private_backup_evidence_gate)
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("github_target_private_backup_evidence_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="GitHub 私有備援證據閘門無效",
) from exc
@router.post(
"/github-target-owner-response-intake-preflight",
response_model=dict[str, Any],
summary="預檢 GitHub target owner response candidate",
description=(
"只驗證一份 GitHub target owner response candidate 是否符合 read-only intake 規則;"
"此端點不持久化 submission、不呼叫 GitHub live API、不建立 repo、不改 visibility、不同步 refs、"
"不觸發 workflow、不收 private clone URL credential 或任何 secret value。"
),
)
async def preflight_github_target_owner_response_intake(
submission: dict[str, Any],
) -> dict[str, Any]:
"""Validate a GitHub target owner response candidate without persisting it."""
try:
payload = await asyncio.to_thread(
preflight_github_target_owner_response_submission,
submission,
)
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(
"github_target_owner_response_intake_preflight_invalid",
error=str(exc),
)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="GitHub target owner response intake preflight 無效",
) from exc
@router.post(
"/github-target-safe-credential-evidence-reviewer-validation/validate-redacted-refs",
response_model=dict[str, Any],
summary="驗證 GitHub target safe credential 脫敏 evidence refs",
description=(
"針對單次 owner-provided redacted safe credential evidence refs 進行 no-persist "
"reviewer validation回傳 accepted / needs supplement / quarantined / rejected runtime "
"action 分流。此端點不保存 payload、不呼叫 GitHub live API、不建立 repo、不改 visibility、"
"不同步 refs、不觸發 workflow、不收 private clone URL credential 或任何 secret value也不更新 "
"safe credential accepted evidence 總帳。"
),
)
async def validate_github_target_safe_credential_evidence_review(
submission: dict[str, Any],
) -> dict[str, Any]:
"""回傳單次 GitHub safe credential 脫敏 evidence refs 公開安全驗證結果。"""
try:
payload = await asyncio.to_thread(
validate_github_target_safe_credential_evidence_refs,
submission,
)
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(
"github_target_safe_credential_evidence_review_invalid",
error=str(exc),
)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="GitHub target safe credential evidence review 無效",
) from exc
@router.get(
"/agent-12-agent-war-room",
response_model=dict[str, Any],
summary="取得 AI Agent 12-Agent War Room 快照",
description=(
"讀取最新已提交的 12-Agent War Room 只讀快照;"
"此端點只呈現 OpenClaw、Hermes、NemoTron、SRE、Security、DevOps、Data/DR、"
"Supply Chain、Product/UI、QA、Market Scout、Telegram Ops 的分工、工作量、阻擋項與批准邊界,"
"不開 runtime writer、不送 Telegram、不呼叫 Bot API、不安裝 SDK、不呼叫付費 API、"
"不讀 secret、不執行 production write。"
),
)
async def get_agent_12_agent_war_room() -> dict[str, Any]:
"""回傳最新 12-Agent War Room 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_12_agent_war_room)
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_12_agent_war_room_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 12-Agent War Room 快照無效",
) from exc
@router.get(
"/agent-professional-task-expansion",
response_model=dict[str, Any],
summary="取得 AI Agent 專業任務擴展與 Telegram Runtime Bridge 快照",
description=(
"讀取最新已提交的 P2-405F 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-receipt-readback-owner-review",
response_model=dict[str, Any],
summary="取得 P2-406B AI Agent receipt readback owner review",
description=(
"讀取最新已提交的 P2-406B receipt readback owner review 只讀快照;"
"此端點只呈現日報 / 週報 / 月報、Telegram receipt、P2-004 供應鏈漂移與報表真相 gate 的 owner review"
"不啟用排程、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 receipt production target、"
"不啟動 AI analysis worker、不執行中低風險自動處理、不寫 production、不讀 secret、"
"不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。"
),
)
async def get_agent_receipt_readback_owner_review() -> dict[str, Any]:
"""回傳最新 P2-406B receipt readback owner review 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_receipt_readback_owner_review)
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_receipt_readback_owner_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-406B AI Agent receipt readback owner review 快照無效",
) from exc
@router.get(
"/agent-report-no-write-analysis-runtime",
response_model=dict[str, Any],
summary="取得 P2-407 AI Agent 報表 no-write 分析 runtime",
description=(
"讀取最新已提交的 P2-407 AI Agent 報表 no-write 分析快照;此端點只呈現 "
"OpenClaw、Hermes、NemoTron 讀取日報 / 週報 / 月報、P2-406B receipt owner review、"
"P2-004 供應鏈漂移與 P2-403J 報表真相後產生的分析草稿與風險分級。"
"它不啟動 live AI worker、不排程實發、不寫 Gateway queue、不送 Telegram、"
"不呼叫 Bot API、不寫 receipt production target、不寫 production、不讀 secret、"
"不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。"
),
)
async def get_agent_report_no_write_analysis_runtime() -> dict[str, Any]:
"""回傳最新 P2-407 report no-write analysis runtime 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_report_no_write_analysis_runtime)
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_report_no_write_analysis_runtime_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-407 AI Agent 報表 no-write 分析 runtime 快照無效",
) from exc
@router.get(
"/agent-low-medium-risk-whitelist",
response_model=dict[str, Any],
summary="取得 P2-408 AI Agent 中低風險自動處理白名單",
description=(
"讀取最新已提交的 P2-408 AI Agent 中 / 低風險候選白名單快照;此端點只呈現 "
"low / medium action policy、dry-run verifier、rollback proof、audit reason 與高風險分流。"
"它不啟動 auto worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫 receipt production target、不寫 production、不讀 secret、不呼叫付費 API、"
"不改主機、不執行 kubectl 或不可逆操作。"
),
)
async def get_agent_low_medium_risk_whitelist() -> dict[str, Any]:
"""回傳最新 P2-408 low / medium risk whitelist 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_low_medium_risk_whitelist)
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_low_medium_risk_whitelist_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-408 AI Agent 中低風險自動處理白名單快照無效",
) from exc
@router.get(
"/agent-high-risk-owner-review-queue",
response_model=dict[str, Any],
summary="取得 P2-409 AI Agent 高風險受控執行 / Break-glass 佇列",
description=(
"讀取最新已提交的 P2-409 AI Agent 高風險受控執行 / critical break-glass 只讀快照;"
"此端點呈現 high controlled apply queue、critical break-glass queue、packet、"
"rejection guard、reviewer checklist、Telegram policy 與執行邊界。"
"它不自行啟動 auto worker、不執行 live action、不寫 Gateway queue、不送 Telegram、"
"不呼叫 Bot API、不寫 receipt production target、不寫 production、不讀 secret、"
"不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。"
),
)
async def get_agent_high_risk_owner_review_queue() -> dict[str, Any]:
"""回傳最新 P2-409 high-risk controlled apply queue 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_high_risk_owner_review_queue)
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_high_risk_owner_review_queue_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-409 AI Agent 高風險受控執行 / Break-glass 佇列快照無效",
) from exc
@router.get(
"/agent-controlled-executor-handoff",
response_model=dict[str, Any],
summary="取得 P2-415 AI Agent 受控 Executor 交接跑道",
description=(
"讀取最新已提交的 P2-415 AI Agent controlled executor handoff 只讀快照;"
"此端點呈現 high risk packet 是否具備 allowlist、Ansible check-mode、rollback、"
"post-action verifier、Telegram evidence、KM / PlayBook trust writeback 條件,"
"以及 critical break-glass 邊界。它不 dispatch executor、不執行 live apply、"
"不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 KM、不更新 PlayBook trust、"
"不寫 production、不讀 secret、不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。"
),
)
async def get_agent_controlled_executor_handoff() -> dict[str, Any]:
"""回傳最新 P2-415 controlled executor handoff 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_controlled_executor_handoff)
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_controlled_executor_handoff_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-415 AI Agent 受控 Executor 交接跑道快照無效",
) from exc
@router.get(
"/agent-action-audit-ledger",
response_model=dict[str, Any],
summary="取得 P2-410 AI Agent 行動審計帳本",
description=(
"讀取最新已提交的 P2-410 AI Agent 行動審計帳本快照;此端點只呈現 "
"immutable audit event template、source readback、verifier receipt gate、redaction contract "
"與 no-write activation boundary。它不寫 audit DB、不寫 timeline、不寫 KM、不更新 PlayBook trust、"
"不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production、不讀 secret、"
"不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。"
),
)
async def get_agent_action_audit_ledger() -> dict[str, Any]:
"""回傳最新 P2-410 action audit ledger 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_action_audit_ledger)
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_action_audit_ledger_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-410 AI Agent 行動審計帳本快照無效",
) from exc
@router.get(
"/agent-action-owner-acceptance-event-bus",
response_model=dict[str, Any],
summary="取得 P2-411 AI Agent Owner Acceptance / Handoff Event Bus",
description=(
"讀取最新已提交的 P2-411 AI Agent owner acceptance / handoff event bus "
"no-write 快照;此端點只呈現 owner acceptance lane、handoff event template、"
"RAG memory proposal、verifier gate 與 no-write activation boundary。它不 publish event bus、"
"不寫 audit DB、不寫 timeline、不寫 KM、不更新 PlayBook trust、不寫 Gateway queue、"
"不送 Telegram、不呼叫 Bot API、不 dispatch worker、不寫 production、不讀 secret、"
"不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。"
),
)
async def get_agent_action_owner_acceptance_event_bus() -> dict[str, Any]:
"""回傳最新 P2-411 owner acceptance / handoff event bus 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_action_owner_acceptance_event_bus)
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_action_owner_acceptance_event_bus_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-411 AI Agent Owner Acceptance / Handoff Event Bus 快照無效",
) from exc
@router.get(
"/agent-host-runaway-aiops-loop-readiness",
response_model=dict[str, Any],
summary="取得 P3-009 Host runaway AIOps loop readiness",
description=(
"讀取最新已提交的 P3-009 Host runaway AIOps loop readiness 只讀快照;"
"此端點只呈現 110 runaway process 的監控、告警、AI event packet、PlayBook、KM / Verifier "
"與 gated remediation 合約,不送 Telegram、不寫 Gateway queue、不呼叫 Bot API、不讀 secret、"
"不 kill process、不重啟 Docker / systemd / Nginx、不改 firewall、不執行 kubectl、不寫 production。"
),
)
async def get_agent_host_runaway_aiops_loop_readiness() -> dict[str, Any]:
"""回傳最新 P3-009 Host runaway AIOps loop readiness 只讀快照。"""
try:
payload = await asyncio.to_thread(load_latest_host_runaway_aiops_loop_readiness)
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("host_runaway_aiops_loop_readiness_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P3-009 Host runaway AIOps loop readiness 快照無效",
) from exc
@router.get(
"/agent-communication-learning-contract",
response_model=dict[str, Any],
summary="取得 AI Agent 主動溝通與學習契約",
description=(
"讀取最新已提交的 OpenClaw / Hermes / NemoTron 主動溝通、學習、記錄、MCP 與 RAG 契約;"
"此端點不啟動 worker、不建立 DB migration、不送 Telegram、不安裝 SDK、不呼叫付費服務、"
"不修改生產路由或主機。"
),
)
async def get_agent_communication_learning_contract() -> dict[str, Any]:
"""Return the latest read-only AI Agent communication learning contract."""
try:
return await asyncio.to_thread(load_latest_ai_agent_communication_learning_contract)
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_communication_learning_contract_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 主動溝通與學習契約無效",
) from exc
@router.get(
"/agent-interaction-learning-proof",
response_model=dict[str, Any],
summary="取得 AI Agent 互動與學習證據面",
description=(
"讀取最新已提交的 OpenClaw / Hermes / NemoTron 互動、接手、學習、成長與 Telegram 收據證據面;"
"此端點不啟動 worker、不讀寫 Redis consumer group、不建立 DB migration、不送 Telegram、"
"不回傳內部協作逐字稿、提示詞、私有推理或機密值。"
),
)
async def get_agent_interaction_learning_proof() -> dict[str, Any]:
"""Return the latest read-only AI Agent interaction and learning proof surface."""
try:
return await asyncio.to_thread(load_latest_ai_agent_interaction_learning_proof)
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_interaction_learning_proof_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 互動與學習證據面無效",
) from exc
@router.get(
"/agent-live-read-model-gate",
response_model=dict[str, Any],
summary="取得 AI Agent live read model gate",
description=(
"讀取最新已提交的 AgentSession / Redis Streams live read model gate"
"此端點不連 DB、不讀寫 Redis、不啟動 worker、不建立 DB migration、不送 Telegram、"
"不回傳內部協作逐字稿、Agent 原始輸出、提示詞、私有推理或機密值。"
),
)
async def get_agent_live_read_model_gate() -> dict[str, Any]:
"""Return the latest read-only AI Agent live read model gate snapshot."""
try:
return await asyncio.to_thread(load_latest_ai_agent_live_read_model_gate)
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_live_read_model_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent live read model gate 無效",
) from exc
@router.get(
"/agent-redis-dry-run-gate",
response_model=dict[str, Any],
summary="取得 AI Agent Redis dry-run gate",
description=(
"讀取最新已提交的 Redis Streams consumer group dry-run、handoff envelope、"
"ack / dead-letter / replay gate此端點不連 Redis、不建立 consumer group、不 XADD、"
"不 XREADGROUP、不 ACK、不寫 dead-letter、不 replay、不送 Telegram、不做 learning writeback、"
"不回傳未核准內部細節。"
),
)
async def get_agent_redis_dry_run_gate() -> dict[str, Any]:
"""Return the latest read-only AI Agent Redis dry-run gate snapshot."""
try:
return await asyncio.to_thread(load_latest_ai_agent_redis_dry_run_gate)
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_redis_dry_run_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent Redis dry-run gate 無效",
) from exc
@router.get(
"/agent-learning-writeback-approval-package",
response_model=dict[str, Any],
summary="取得 AI Agent learning writeback approval package",
description=(
"讀取最新已提交的 KM / PlayBook trust / timeline learning / replay score 回寫批准包;"
"此端點不寫 KM、不更新 PlayBook trust、不寫 timeline、不寫 replay score、不送 Telegram、"
"不啟動 runtime worker、不回傳未核准內部細節。"
),
)
async def get_agent_learning_writeback_approval_package() -> dict[str, Any]:
"""Return the latest read-only AI Agent learning writeback approval package."""
try:
return await asyncio.to_thread(load_latest_ai_agent_learning_writeback_approval_package)
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_learning_writeback_approval_package_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent learning writeback approval package 無效",
) from exc
@router.get(
"/agent-telegram-receipt-approval-package",
response_model=dict[str, Any],
summary="取得 AI Agent Telegram receipt approval package",
description=(
"讀取最新已提交的 Telegram receipt / queue / delivery / ack / failure / retry 批准包;"
"此端點不寫 Gateway queue、不呼叫 Telegram Bot API、不改 receiver route、不發送通知、"
"不啟動 runtime worker、不回傳 Telegram token、raw chat id 或未脫敏 payload。"
),
)
async def get_agent_telegram_receipt_approval_package() -> dict[str, Any]:
"""Return the latest read-only AI Agent Telegram receipt approval package."""
try:
return await asyncio.to_thread(load_latest_ai_agent_telegram_receipt_approval_package)
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_telegram_receipt_approval_package_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent Telegram receipt approval package 無效",
) from exc
@router.get(
"/agent-owner-approved-learning-dry-run",
response_model=dict[str, Any],
summary="取得 AI Agent owner-approved learning dry-run contract",
description=(
"讀取最新已提交的 owner-approved learning writeback dry-run 契約;"
"此端點只回傳 dry-run preview、人工操作選項、驗證與 rollback 契約,"
"不寫 KM、不更新 PlayBook trust、不寫 timeline、不寫 replay score、不發 Telegram、"
"不啟動 runtime worker、不回傳未脫敏 payload。"
),
)
async def get_agent_owner_approved_learning_dry_run() -> dict[str, Any]:
"""Return the latest read-only AI Agent owner-approved learning dry-run contract."""
try:
return await asyncio.to_thread(load_latest_ai_agent_owner_approved_learning_dry_run)
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_owner_approved_learning_dry_run_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent owner-approved learning dry-run 無效",
) from exc
@router.get(
"/agent-runtime-write-gate-review",
response_model=dict[str, Any],
summary="取得 AI Agent runtime write gate review",
description=(
"讀取最新已提交的 runtime write gate review 契約;此端點只回傳雙重批准、"
"dry-run hash、post-write verifier 與 redaction 欄位檢查,"
"不寫 KM、不更新 PlayBook trust、不寫 timeline、不寫 replay score、不發 Telegram、"
"不啟動 runtime worker、不回傳未脫敏 payload。"
),
)
async def get_agent_runtime_write_gate_review() -> dict[str, Any]:
"""Return the latest read-only AI Agent runtime write gate review."""
try:
return await asyncio.to_thread(load_latest_ai_agent_runtime_write_gate_review)
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_runtime_write_gate_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent runtime write gate review 無效",
) from exc
@router.get(
"/agent-post-write-verifier-package",
response_model=dict[str, Any],
summary="取得 AI Agent post-write verifier package",
description=(
"讀取最新已提交的 post-write verifier implementation package此端點只回傳 verifier package、"
"rollback lane、failure lane 與人工操作選項,"
"不寫 KM、不更新 PlayBook trust、不寫 timeline、不寫 replay score、不發 Telegram、"
"不啟動 runtime worker、不讀 canonical target、不回傳未脫敏 payload。"
),
)
async def get_agent_post_write_verifier_package() -> dict[str, Any]:
"""Return the latest read-only AI Agent post-write verifier package."""
try:
return await asyncio.to_thread(load_latest_ai_agent_post_write_verifier_package)
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_post_write_verifier_package_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent post-write verifier package 無效",
) from exc
@router.get(
"/agent-runtime-verifier-evidence-review",
response_model=dict[str, Any],
summary="取得 AI Agent runtime verifier evidence review",
description=(
"讀取最新已提交的 runtime verifier evidence implementation review此端點只回傳 "
"evidence checks、implementation review lanes、redaction policy 與人工操作選項,"
"不實作或執行 verifier、不讀 canonical target、不寫 rollback work item、不發 Telegram、"
"不寫 KM / PlayBook trust / timeline / replay score、不啟動 runtime worker。"
),
)
async def get_agent_runtime_verifier_evidence_review() -> dict[str, Any]:
"""Return the latest read-only AI Agent runtime verifier evidence review."""
try:
return await asyncio.to_thread(load_latest_ai_agent_runtime_verifier_evidence_review)
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_runtime_verifier_evidence_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent runtime verifier evidence review 無效",
) from exc
@router.get(
"/agent-report-truth-actionability-review",
response_model=dict[str, Any],
summary="取得 AI Agent 報表真相與告警可處置性審查",
description=(
"讀取最新已提交的日報 / 週報 / 月報真相與告警可處置性審查;此端點只回傳 "
"zero-signal findings、cadence contracts、actionability lanes 與人工操作選項,"
"不發 Telegram、不修改 CronJob、不改 Prometheus / Alertmanager、不建立 work item、"
"不寫 KM / PlayBook trust、不啟動 runtime worker。"
),
)
async def get_agent_report_truth_actionability_review() -> dict[str, Any]:
"""Return the latest read-only AI Agent report truth actionability review."""
try:
return await asyncio.to_thread(load_latest_ai_agent_report_truth_actionability_review)
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_report_truth_actionability_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 報表真相與告警可處置性審查無效",
) from exc
@router.get(
"/agent-report-automation-review",
response_model=dict[str, Any],
summary="取得 AI Agent 日週月報與風險自動化 review",
description=(
"讀取最新已提交的 AI Agent 日報、週報、月報、Agent 工作量、圖表化報告、"
"AI 分析建議與高/中/低風險自動化政策;此端點不排程實發、不送 Telegram、"
"不啟動中低風險自動執行器、不執行生產優化、不讀 secret、不回傳內部工作視窗對話。"
),
)
async def get_agent_report_automation_review() -> dict[str, Any]:
"""Return the latest read-only AI Agent report automation review."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_report_automation_review)
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_report_automation_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 日週月報與風險自動化 review 無效",
) from exc
@router.get(
"/agent-report-status-board",
response_model=dict[str, Any],
summary="取得 AI Agent 日週月報與工作狀態總覽",
description=(
"讀取最新已提交的 P2-108 AI Agent 日報、週報、月報完成狀態、"
"OpenClaw / Hermes / NemoTron 工作量、圖表化狀態、Telegram 草案與自動優化邊界;"
"此端點不排程實發、不送 Telegram、不寫 Gateway queue、不寫讀報回執、"
"不啟動 AI 分析 worker、不執行生產優化、不讀 secret、不回傳內部協作內容。"
),
)
async def get_agent_report_status_board() -> dict[str, Any]:
"""Return the latest read-only AI Agent report status board."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_report_status_board)
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_report_status_board_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 日週月報與工作狀態總覽無效",
) from exc
@router.get(
"/agent-report-source-health",
response_model=dict[str, Any],
summary="取得 AI Agent 報表資料源健康與 no-send preview",
description=(
"回傳日報 / 週報 / 月報資料源健康、全 0 判讀、no-send preview、"
"KM / PlayBook / 腳本 / 排程 / Verifier 沉澱與 report-source-gap 工作項;"
"此端點只做 redacted readback不送 Telegram、不寫 Gateway queue、不改排程、"
"不啟動 AI runtime、不執行中低風險自動修復、不讀 secret。"
),
)
async def get_agent_report_source_health() -> dict[str, Any]:
"""Return the read-only AI Agent report source health model."""
try:
payload = await build_ai_agent_report_source_health()
return redact_public_lan_topology(payload)
except Exception as exc:
logger.error("ai_agent_report_source_health_failed", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 報表資料源健康讀取失敗",
) from exc
@router.get(
"/agent-report-runtime-readiness",
response_model=dict[str, Any],
summary="取得 AI Agent 報表 runtime 啟動前閘門",
description=(
"讀取最新已提交的 P2-403L 日週月報派送、Telegram Gateway queue、讀報回執、"
"AI 讀報後分析、中低風險自動處理、高風險審核與 post-action verifier 啟動前閘門;"
"此端點不排程實發、不送 Telegram、不寫 Gateway queue、不啟動 AI runtime worker、"
"不啟動中低風險 auto worker、不執行生產優化、不讀 secret、不回傳內部對話內容。"
),
)
async def get_agent_report_runtime_readiness() -> dict[str, Any]:
"""Return the latest read-only AI Agent report runtime readiness gate."""
try:
return await asyncio.to_thread(load_latest_ai_agent_report_runtime_readiness)
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_report_runtime_readiness_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 報表 runtime 啟動前閘門無效",
) from exc
@router.get(
"/agent-report-runtime-dry-run",
response_model=dict[str, Any],
summary="取得 AI Agent 報表 runtime no-write dry-run 證據包",
description=(
"讀取最新已提交的 P2-403M 報表 runtime no-write dry-run、Telegram Gateway queue 草案、"
"讀報回執 redaction 與 readback verifier 草案;此端點不排程實發、不送 Telegram、"
"不寫 Gateway queue、不寫讀報回執、不啟動 AI runtime worker、不啟動中低風險 auto worker、"
"不執行 verifier live readback、不讀 secret、不回傳內部對話內容。"
),
)
async def get_agent_report_runtime_dry_run() -> dict[str, Any]:
"""Return the latest read-only AI Agent report runtime dry-run package."""
try:
return await asyncio.to_thread(load_latest_ai_agent_report_runtime_dry_run)
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_report_runtime_dry_run_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 報表 runtime no-write dry-run 證據包無效",
) from exc
@router.get(
"/agent-report-runtime-fixture-readback",
response_model=dict[str, Any],
summary="取得 AI Agent 報表 runtime fixture readback 證據包",
description=(
"讀取最新已提交的 P2-403N fixture smoke、Telegram Gateway queue preview readback "
"與 verifier dry-run 證據包;此端點不排程實發、不送 Telegram、不呼叫 Bot API、"
"不寫 Gateway queue、不寫讀報回執、不啟動 AI runtime worker、不啟動中低風險 auto worker、"
"不執行 verifier live readback、不寫 production target、不讀 secret、不回傳內部對話內容。"
),
)
async def get_agent_report_runtime_fixture_readback() -> dict[str, Any]:
"""Return the latest read-only AI Agent fixture readback package."""
try:
return await asyncio.to_thread(load_latest_ai_agent_report_runtime_fixture_readback)
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_report_runtime_fixture_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 報表 runtime fixture readback 證據包無效",
) from exc
@router.get(
"/agent-runtime-worker-shadow-gate",
response_model=dict[str, Any],
summary="取得 AI Agent runtime worker shadow/no-write 證據閘門",
description=(
"讀取最新已提交的 P2-404 runtime worker shadow / no-write execution evidence gate"
"此端點只回傳 shadow candidate、no-write replay、verifier shadow case 與 operator checkpoint"
"不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫讀報回執、不啟動 live AI runtime worker、"
"不啟動中低風險 auto worker、不執行 verifier live readback、不寫 production target、不讀 secret。"
),
)
async def get_agent_runtime_worker_shadow_gate() -> dict[str, Any]:
"""Return the latest read-only AI Agent runtime worker shadow gate."""
try:
return await asyncio.to_thread(load_latest_ai_agent_runtime_worker_shadow_gate)
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_runtime_worker_shadow_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent runtime worker shadow/no-write 證據閘門無效",
) from exc
@router.get(
"/agent-operation-permission-model",
response_model=dict[str, Any],
summary="取得 AI Agent 操作類別權限模型",
description=(
"讀取最新已提交的 P2-101 操作類別權限模型;此端點只回傳 permission lane、"
"operation category、Agent responsibility、gate transition 與 operator template"
"不啟動 runtime worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫讀報回執、不執行 verifier live readback、不寫 production target、不讀 secret。"
),
)
async def get_agent_operation_permission_model() -> dict[str, Any]:
"""Return the latest read-only AI Agent operation permission model."""
try:
return await asyncio.to_thread(load_latest_ai_agent_operation_permission_model)
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_operation_permission_model_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 操作類別權限模型無效",
) from exc
@router.get(
"/agent-candidate-operation-dry-run-evidence",
response_model=dict[str, Any],
summary="取得 AI Agent 候選操作 dry-run 證據",
description=(
"讀取最新已提交的 P2-102 候選操作 dry-run 證據;此端點只回傳 candidate operation、"
"dry-run evidence hash、side-effect count、verifier plan、gate requirement 與 operator handoff"
"不啟動 runtime worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫讀報回執、不執行 verifier live readback、不寫 production target、不讀 secret。"
),
)
async def get_agent_candidate_operation_dry_run_evidence() -> dict[str, Any]:
"""Return the latest read-only AI Agent candidate operation dry-run evidence."""
try:
return await asyncio.to_thread(load_latest_ai_agent_candidate_operation_dry_run_evidence)
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_candidate_operation_dry_run_evidence_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 候選操作 dry-run 證據無效",
) from exc
@router.get(
"/agent-task-result-audit-trail",
response_model=dict[str, Any],
summary="取得 AI Agent 任務結果稽核軌跡",
description=(
"讀取最新已提交的 P2-103 任務結果稽核軌跡;此端點只回傳 result route、"
"KM / LOGBOOK / audit / timeline writeback contract、operator handoff 與 redaction boundary"
"不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不更新 PlayBook trust、"
"不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret。"
),
)
async def get_agent_task_result_audit_trail() -> dict[str, Any]:
"""Return the latest read-only AI Agent task result audit trail contract."""
try:
return await asyncio.to_thread(load_latest_ai_agent_task_result_audit_trail)
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_task_result_audit_trail_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 任務結果稽核軌跡無效",
) from exc
@router.get(
"/agent-matched-playbook-learning-gap",
response_model=dict[str, Any],
summary="取得 AI Agent matched PlayBook 學習缺口",
description=(
"讀取最新已提交的 P2-104 matched PlayBook 學習缺口;此端點只回傳 production DB 只讀回查摘要、"
"learning gap、gate、writeback candidate 與 redaction boundary"
"不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、"
"不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret。"
),
)
async def get_agent_matched_playbook_learning_gap() -> dict[str, Any]:
"""Return the latest read-only AI Agent matched PlayBook learning gap contract."""
try:
return await asyncio.to_thread(load_latest_ai_agent_matched_playbook_learning_gap)
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_matched_playbook_learning_gap_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent matched PlayBook 學習缺口無效",
) from exc
@router.get(
"/agent-critic-reviewer-result-capture",
response_model=dict[str, Any],
summary="取得 AI Agent critic / reviewer result capture 契約",
description=(
"讀取最新已提交的 P2-105 critic / reviewer 評分與 result capture 契約;"
"此端點只回傳 scorecard、result capture contract、promotion gate、candidate route 與 redaction boundary"
"不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、"
"不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret。"
),
)
async def get_agent_critic_reviewer_result_capture() -> dict[str, Any]:
"""Return the latest read-only critic / reviewer result capture contract."""
try:
return await asyncio.to_thread(load_latest_ai_agent_critic_reviewer_result_capture)
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_critic_reviewer_result_capture_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent critic / reviewer result capture 契約無效",
) from exc
@router.get(
"/agent-owner-approved-result-capture-dry-run",
response_model=dict[str, Any],
summary="取得 AI Agent owner-approved result capture dry-run",
description=(
"讀取最新已提交的 P2-106 owner-approved result capture dry-run"
"此端點只回傳 owner approval packet、no-write dry-run template、score fixture、"
"dry-run gate 與 operator action"
"不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、"
"不寫 audit DB、不寫 timeline、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫 production target、不讀 secret。"
),
)
async def get_agent_owner_approved_result_capture_dry_run() -> dict[str, Any]:
"""Return the latest read-only owner-approved result capture dry-run contract."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_owner_approved_result_capture_dry_run)
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_owner_approved_result_capture_dry_run_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent owner-approved result capture dry-run 無效",
) from exc
@router.get(
"/agent-owner-approved-result-capture-readback",
response_model=dict[str, Any],
summary="取得 AI Agent owner-approved result capture readback",
description=(
"讀取最新已提交的 P2-107 owner-approved result capture readback"
"此端點只回傳 result capture readback digest、promotion readiness review、failure lane、"
"reviewer queue preview 與 operator action"
"不讀 canonical runtime target、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、"
"不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret。"
),
)
async def get_agent_owner_approved_result_capture_readback() -> dict[str, Any]:
"""Return the latest read-only owner-approved result capture readback contract."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_owner_approved_result_capture_readback)
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_owner_approved_result_capture_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent owner-approved result capture readback 無效",
) from exc
@router.get(
"/agent-runtime-readback-approval-package",
response_model=dict[str, Any],
summary="取得 AI Agent runtime readback 批准包",
description=(
"讀取最新已提交的 P2-109 runtime readback approval package"
"此端點只回傳 approval packet、canonical readback plan、rollback drill、"
"Telegram 失敗收據 gate 與 operator action"
"不讀 canonical runtime target、不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、"
"不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 rollback work item、"
"不寫 production target、不讀 secret。"
),
)
async def get_agent_runtime_readback_approval_package() -> dict[str, Any]:
"""Return the latest read-only runtime readback approval package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_runtime_readback_approval_package)
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_runtime_readback_approval_package_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent runtime readback approval package 無效",
) from exc
@router.get(
"/agent-runtime-readback-implementation-review",
response_model=dict[str, Any],
summary="取得 AI Agent runtime readback 實作審查",
description=(
"讀取最新已提交的 P2-110 runtime readback implementation review"
"此端點只回傳 implementation review card、no-write verifier、阻塞原因與 operator action"
"不讀 canonical runtime target、不做 live query、不寫 score、不寫 result capture、不寫 learning、"
"不更新 PlayBook trust、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫 rollback work item、不寫 production target、不讀 secret。"
),
)
async def get_agent_runtime_readback_implementation_review() -> dict[str, Any]:
"""Return the latest read-only runtime readback implementation review."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_runtime_readback_implementation_review)
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_runtime_readback_implementation_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent runtime readback implementation review 無效",
) from exc
@router.get(
"/agent-report-live-delivery-approval-package",
response_model=dict[str, Any],
summary="取得 AI Agent report live delivery 批准包",
description=(
"讀取最新已提交的 P2-111 report live delivery approval package"
"此端點只回傳日報、週報、月報、失敗限定摘要與讀報回執的實發批准包、"
"route lock、payload redaction、no-send receipt 與 operator action"
"不排程、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 report receipt、"
"不啟動 AI analysis、不做中低風險自動優化、不寫 production target、不讀 secret。"
),
)
async def get_agent_report_live_delivery_approval_package() -> dict[str, Any]:
"""Return the latest read-only report live delivery approval package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_report_live_delivery_approval_package)
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_report_live_delivery_approval_package_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent report live delivery approval package 無效",
) from exc
@router.get(
"/agent-runtime-readback-fixture-approval",
response_model=dict[str, Any],
summary="取得 AI Agent runtime readback fixture 批准包",
description=(
"讀取最新已提交的 P2-112 runtime readback fixture approval package"
"此端點只回傳 fixture approval card、adapter contract、verifier fixture、"
"blocker mapping 與 operator action不讀 canonical runtime target、不做 live query、"
"不執行 runtime readback、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫 report receipt、不寫 result capture、不寫 production target、不讀 secret。"
),
)
async def get_agent_runtime_readback_fixture_approval() -> dict[str, Any]:
"""Return the latest read-only runtime readback fixture approval package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_runtime_readback_fixture_approval)
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_runtime_readback_fixture_approval_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent runtime readback fixture approval 無效",
) from exc
@router.get(
"/agent-runtime-readback-promotion-gate",
response_model=dict[str, Any],
summary="取得 AI Agent runtime readback promotion gate",
description=(
"讀取最新已提交的 P2-113 runtime readback promotion gate"
"此端點只回傳 failure receipt、reviewer queue、result capture 的 no-write promotion "
"lane、preview、verifier 與 blocker不讀 canonical runtime target、不做 live query、"
"不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫 report receipt、不寫 result capture、不寫 learning / PlayBook trust、不讀 secret。"
),
)
async def get_agent_runtime_readback_promotion_gate() -> dict[str, Any]:
"""Return the latest read-only runtime readback promotion gate."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_runtime_readback_promotion_gate)
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_runtime_readback_promotion_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent runtime readback promotion gate 無效",
) from exc
@router.get(
"/agent-owner-approved-fixture-promotion-gate",
response_model=dict[str, Any],
summary="取得 AI Agent owner-approved fixture promotion gate",
description=(
"讀取最新已提交的 P2-114 owner-approved fixture promotion gate"
"此端點只回傳 owner approval packet、acceptance template、fixture review、"
"no-write verifier 與 blocked promotion不讀 canonical runtime target、不做 live query、"
"不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫 report receipt、不寫 result capture、不寫 learning / PlayBook trust、不讀 secret。"
),
)
async def get_agent_owner_approved_fixture_promotion_gate() -> dict[str, Any]:
"""Return the latest read-only owner-approved fixture promotion gate."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_owner_approved_fixture_promotion_gate)
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_owner_approved_fixture_promotion_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent owner-approved fixture promotion gate 無效",
) from exc
@router.get(
"/agent-canonical-runtime-readback-owner-acceptance",
response_model=dict[str, Any],
summary="取得 AI Agent canonical runtime readback owner acceptance",
description=(
"讀取最新已提交的 P2-115 canonical runtime readback owner acceptance"
"此端點只回傳 owner acceptance package、canonical target scope、no-live verifier、"
"blocked acceptance 與 operator handoff不讀 canonical runtime target、不做 live query、"
"不寫 acceptance record、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、"
"不呼叫 Bot API、不寫 result capture、不寫 learning / PlayBook trust、不讀 secret。"
),
)
async def get_agent_canonical_runtime_readback_owner_acceptance() -> dict[str, Any]:
"""Return the latest read-only canonical runtime readback owner acceptance."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_canonical_runtime_readback_owner_acceptance)
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_canonical_runtime_readback_owner_acceptance_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent canonical runtime readback owner acceptance 無效",
) from exc
@router.get(
"/agent-failure-receipt-no-send-replay",
response_model=dict[str, Any],
summary="取得 AI Agent failure receipt no-send replay",
description=(
"讀取最新已提交的 P2-116 failure receipt no-send replay"
"此端點只回傳 no-send replay fixture、SRE 戰情室 route lock、redaction verifier、"
"blocked send 與 operator handoff不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、"
"不寫 reviewer queue、不寫 report receipt、不寫 result capture、不寫 learning / PlayBook trust、"
"不讀 canonical runtime target、不讀 secret。"
),
)
async def get_agent_failure_receipt_no_send_replay() -> dict[str, Any]:
"""Return the latest read-only failure receipt no-send replay package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_failure_receipt_no_send_replay)
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_failure_receipt_no_send_replay_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent failure receipt no-send replay 無效",
) from exc
@router.get(
"/agent-reviewer-queue-no-write-readback",
response_model=dict[str, Any],
summary="取得 AI Agent reviewer queue no-write readback",
description=(
"讀取最新已提交的 P2-117 reviewer queue no-write readback"
"此端點只回傳 reviewer queue preview fixture、queue item mapping、no-write verifier、"
"blocked queue write 與 operator handoff不寫 reviewer queue、不寫 Gateway queue、"
"不送 Telegram、不呼叫 Bot API、不寫 report receipt / result capture / learning / PlayBook trust、"
"不讀 canonical runtime target、不讀 secret。"
),
)
async def get_agent_reviewer_queue_no_write_readback() -> dict[str, Any]:
"""Return the latest read-only reviewer queue no-write readback package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_reviewer_queue_no_write_readback)
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_reviewer_queue_no_write_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent reviewer queue no-write readback 無效",
) from exc
@router.get(
"/agent-result-capture-no-write-readback",
response_model=dict[str, Any],
summary="取得 AI Agent result capture no-write readback",
description=(
"讀取最新已提交的 P2-118 result capture no-write readback"
"此端點只回傳 result capture preview fixture、capture field mapping、no-write verifier、"
"blocked result capture write 與 operator handoff不寫 result capture、learning、"
"PlayBook trust、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 canonical runtime target、"
"不讀 secret。"
),
)
async def get_agent_result_capture_no_write_readback() -> dict[str, Any]:
"""Return the latest read-only result capture no-write readback package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_no_write_readback)
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_result_capture_no_write_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture no-write readback 無效",
) from exc
@router.get(
"/agent-result-capture-promotion-approval-gate",
response_model=dict[str, Any],
summary="取得 AI Agent result capture promotion approval gate",
description=(
"讀取最新已提交的 P2-119 result capture promotion approval gate"
"此端點只回傳 promotion approval packet、acceptance template、verifier、"
"blocked promotion write 與 operator handoff不寫 result capture、learning、"
"PlayBook trust、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 canonical runtime target、"
"不讀 secret。"
),
)
async def get_agent_result_capture_promotion_approval_gate() -> dict[str, Any]:
"""Return the latest read-only result capture promotion approval gate package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_promotion_approval_gate)
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_result_capture_promotion_approval_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture promotion approval gate 無效",
) from exc
@router.get(
"/agent-owner-approved-result-capture-promotion-dry-run",
response_model=dict[str, Any],
summary="取得 AI Agent owner-approved result capture promotion dry-run",
description=(
"讀取最新已提交的 P2-120 owner-approved result capture promotion dry-run"
"此端點只回傳 dry-run template、owner acceptance fixture、verifier、blocked runtime promotion "
"與 operator handoff不寫 result capture、learning、PlayBook trust、Gateway queue"
"不送 Telegram、不呼叫 Bot API、不讀 canonical runtime target、不讀 secret。"
),
)
async def get_agent_owner_approved_result_capture_promotion_dry_run() -> dict[str, Any]:
"""Return the latest read-only owner-approved result capture promotion dry-run package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_owner_approved_result_capture_promotion_dry_run)
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_owner_approved_result_capture_promotion_dry_run_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent owner-approved result capture promotion dry-run 無效",
) from exc
@router.get(
"/agent-result-capture-write-gate-review",
response_model=dict[str, Any],
summary="取得 AI Agent result capture write gate review",
description=(
"讀取最新已提交的 P2-121 result capture write gate review"
"此端點只回傳 writer gate、approval gate、post-write verifier、blocked live write "
"與 operator handoff不寫 result capture、learning、PlayBook trust、reviewer queue、"
"Gateway queue不送 Telegram、不呼叫 Bot API、不讀 canonical runtime target、不讀 secret。"
),
)
async def get_agent_result_capture_write_gate_review() -> dict[str, Any]:
"""Return the latest read-only result capture write gate review package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_write_gate_review)
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_result_capture_write_gate_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture write gate review 無效",
) from exc
@router.get(
"/agent-result-capture-writer-implementation-review",
response_model=dict[str, Any],
summary="取得 AI Agent result capture writer implementation review",
description=(
"讀取最新已提交的 P2-122 result capture writer implementation review"
"此端點只回傳 writer implementation、implementation gate、post-implementation verifier、"
"blocked runtime change 與 operator handoff不套用 writer、不寫 result capture、learning、"
"PlayBook trust、reviewer queue、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_writer_implementation_review() -> dict[str, Any]:
"""Return the latest read-only result capture writer implementation review package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_writer_implementation_review)
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_result_capture_writer_implementation_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture writer implementation review 無效",
) from exc
@router.get(
"/agent-result-capture-writer-dry-run-fixture",
response_model=dict[str, Any],
summary="取得 AI Agent result capture writer dry-run fixture",
description=(
"讀取最新已提交的 P2-123 result capture writer dry-run fixture"
"此端點只回傳 writer dry-run fixture、receipt preview、idempotency replay、"
"rollback rehearsal、blocked runtime write 與 operator handoff不套用 writer、"
"不寫 result capture、learning、PlayBook trust、reviewer queue、Gateway queue"
"不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_writer_dry_run_fixture() -> dict[str, Any]:
"""Return the latest read-only result capture writer dry-run fixture package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_writer_dry_run_fixture)
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_result_capture_writer_dry_run_fixture_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture writer dry-run fixture 無效",
) from exc
@router.get(
"/agent-result-capture-writer-dry-run-readback",
response_model=dict[str, Any],
summary="取得 AI Agent result capture writer dry-run readback",
description=(
"讀取最新已提交的 P2-124 result capture writer dry-run readback"
"此端點只回傳 dry-run readback、receipt verifier、promotion readiness、"
"blocked promotion write 與 operator handoff不套用 writer、不執行 dry-run、"
"不寫 receipt、不寫 result capture、learning、PlayBook trust、reviewer queue、"
"Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_writer_dry_run_readback() -> dict[str, Any]:
"""Return the latest read-only result capture writer dry-run readback package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_writer_dry_run_readback)
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_result_capture_writer_dry_run_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture writer dry-run readback 無效",
) from exc
@router.get(
"/agent-result-capture-owner-promotion-review",
response_model=dict[str, Any],
summary="取得 AI Agent result capture owner promotion review",
description=(
"讀取最新已提交的 P2-125 result capture owner promotion review"
"此端點只回傳 owner promotion packet、execution gate、rollback owner review、"
"blocked execution write 與 operator handoff不套用 writer、不執行 live apply、"
"不寫 receipt、不寫 result capture、learning、PlayBook trust、reviewer queue、"
"Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_owner_promotion_review() -> dict[str, Any]:
"""Return the latest read-only result capture owner promotion review package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_owner_promotion_review)
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_result_capture_owner_promotion_review_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture owner promotion review 無效",
) from exc
@router.get(
"/agent-result-capture-owner-approved-execution-rehearsal",
response_model=dict[str, Any],
summary="取得 AI Agent result capture owner-approved execution rehearsal",
description=(
"讀取最新已提交的 P2-126 owner-approved execution rehearsal"
"此端點只回傳 no-write execution rehearsal、apply gate、verifier rehearsal、"
"rollback drill、blocked live apply 與 operator handoff不套用 writer、不執行 live apply、"
"不寫 receipt、不寫 result capture、learning、PlayBook trust、reviewer queue、"
"Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_owner_approved_execution_rehearsal() -> dict[str, Any]:
"""Return the latest read-only owner-approved execution rehearsal package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_owner_approved_execution_rehearsal)
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_result_capture_owner_approved_execution_rehearsal_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture owner-approved execution rehearsal 無效",
) from exc
@router.get(
"/agent-result-capture-owner-acceptance-maintenance-gate",
response_model=dict[str, Any],
summary="取得 AI Agent result capture owner acceptance maintenance gate",
description=(
"讀取最新已提交的 P2-127 owner acceptance / maintenance gate"
"此端點只回傳 owner acceptance packet、maintenance window、rollback owner、"
"post-apply verifier gate、blocked live write 與 operator handoff不套用 writer、"
"不執行 live apply、不寫 receipt、不寫 result capture、learning、PlayBook trust、"
"reviewer queue、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_owner_acceptance_maintenance_gate() -> dict[str, Any]:
"""Return the latest read-only owner acceptance / maintenance gate package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_owner_acceptance_maintenance_gate)
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_result_capture_owner_acceptance_maintenance_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture owner acceptance maintenance gate 無效",
) from exc
@router.get(
"/agent-result-capture-owner-acceptance-readback-preflight-hold",
response_model=dict[str, Any],
summary="取得 AI Agent result capture owner acceptance readback preflight hold",
description=(
"讀取最新已提交的 P2-128 owner acceptance readback / preflight hold"
"此端點只回傳 owner acceptance readback、live-apply preflight hold、"
"live apply hold gate、rollback preflight、blocked apply transition 與 operator handoff"
"不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、"
"reviewer queue、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_owner_acceptance_readback_preflight_hold() -> dict[str, Any]:
"""Return the latest read-only owner acceptance readback / preflight hold package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_owner_acceptance_readback_preflight_hold)
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_result_capture_owner_acceptance_readback_preflight_hold_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture owner acceptance readback preflight hold 無效",
) from exc
@router.get(
"/agent-result-capture-owner-approved-preflight-release-package",
response_model=dict[str, Any],
summary="取得 AI Agent result capture owner-approved preflight release package",
description=(
"讀取最新已提交的 P2-129 owner-approved preflight release package"
"此端點只回傳 owner-approved release package、release preflight check、"
"live apply release gate、rollback release check、blocked release transition 與 operator handoff"
"不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、"
"reviewer queue、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_owner_approved_preflight_release_package() -> dict[str, Any]:
"""Return the latest read-only owner-approved preflight release package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_owner_approved_preflight_release_package)
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_result_capture_owner_approved_preflight_release_package_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture owner-approved preflight release package 無效",
) from exc
@router.get(
"/agent-result-capture-owner-approved-release-readiness-readback",
response_model=dict[str, Any],
summary="取得 AI Agent result capture owner-approved release readiness readback",
description=(
"讀取最新已提交的 P2-130 owner-approved release readiness readback"
"此端點只回傳 release readiness readback、owner release readiness check、"
"live apply readiness gate、rollback readiness check、blocked readiness transition 與 operator handoff"
"不批准 owner release、不批准 maintenance window、不釋放 live apply、不套用 writer、不寫 receipt、"
"不寫 result capture、learning、PlayBook trust、reviewer queue、Gateway queue不送 Telegram、"
"不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_owner_approved_release_readiness_readback() -> dict[str, Any]:
"""Return the latest read-only owner-approved release readiness readback."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_owner_approved_release_readiness_readback)
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_result_capture_owner_approved_release_readiness_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture owner-approved release readiness readback 無效",
) from exc
@router.get(
"/agent-result-capture-owner-release-approval-gate",
response_model=dict[str, Any],
summary="取得 AI Agent result capture owner release approval gate",
description=(
"讀取最新已提交的 P2-131 owner release approval gate"
"此端點只回傳 owner release approval packet、maintenance window gate、"
"live apply approval gate、rollback owner approval check、blocked approval transition 與 operator handoff"
"不批准 owner release、不批准 maintenance window、不釋放 live apply、不套用 writer、不寫 receipt、"
"不寫 result capture、learning、PlayBook trust、reviewer queue、Gateway queue不送 Telegram、"
"不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_owner_release_approval_gate() -> dict[str, Any]:
"""Return the latest read-only owner release approval gate."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_owner_release_approval_gate)
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_result_capture_owner_release_approval_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture owner release approval gate 無效",
) from exc
@router.get(
"/agent-result-capture-post-release-verifier-rollback-gate",
response_model=dict[str, Any],
summary="取得 AI Agent result capture post-release verifier rollback gate",
description=(
"讀取最新已提交的 P2-132 post-release verifier / rollback gate"
"此端點只回傳 post-release verifier gate、rollback release gate、release verification hold、"
"live apply post-release gate、blocked post-release transition 與 operator handoff"
"不批准 owner release、不批准 maintenance window、不確認 rollback owner、不釋放 live apply、"
"不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、reviewer queue、"
"Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_post_release_verifier_rollback_gate() -> dict[str, Any]:
"""Return the latest read-only post-release verifier / rollback gate."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_post_release_verifier_rollback_gate)
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_result_capture_post_release_verifier_rollback_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture post-release verifier rollback gate 無效",
) from exc
@router.get(
"/agent-result-capture-final-release-candidate-readback",
response_model=dict[str, Any],
summary="取得 AI Agent result capture final release candidate readback",
description=(
"讀取最新已提交的 P2-133 final release candidate readback"
"此端點只回傳 final release candidate readback、rollback candidate readback、candidate acceptance hold、"
"live apply candidate hold、blocked final candidate transition 與 operator handoff"
"不批准 owner release、不批准 maintenance window、不確認 rollback owner、不通過 final candidate、"
"不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、"
"reviewer queue、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_final_release_candidate_readback() -> dict[str, Any]:
"""Return the latest read-only final release candidate readback."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_final_release_candidate_readback)
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_result_capture_final_release_candidate_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture final release candidate readback 無效",
) from exc
@router.get(
"/agent-result-capture-release-authorization-hold",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release authorization hold",
description=(
"讀取最新已提交的 P2-134 release authorization hold"
"此端點只回傳 release authorization hold、rollback authorization hold、release window hold、"
"live apply authorization hold、blocked authorization transition 與 operator handoff"
"不授權 owner release、不批准 maintenance window、不確認 rollback owner、不通過 release authorization、"
"不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、"
"reviewer queue、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_release_authorization_hold() -> dict[str, Any]:
"""Return the latest read-only release authorization hold."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_authorization_hold)
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_result_capture_release_authorization_hold_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release authorization hold 無效",
) from exc
@router.get(
"/agent-result-capture-release-authorization-readback-gate",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release authorization readback gate",
description=(
"讀取最新已提交的 P2-135 release authorization readback gate"
"此端點只回傳 release authorization readback、rollback release readback、maintenance window readback hold、"
"live apply release readback hold、blocked release readback transition 與 operator handoff"
"不授權 owner release、不批准 maintenance window、不確認 rollback owner、不通過 release authorization、"
"不釋放 rollback release 或 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、"
"reviewer queue、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_release_authorization_readback_gate() -> dict[str, Any]:
"""Return the latest read-only release authorization readback gate."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_authorization_readback_gate)
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_result_capture_release_authorization_readback_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release authorization readback gate 無效",
) from exc
@router.get(
"/agent-result-capture-release-verifier-preflight-gate",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release verifier preflight gate",
description=(
"讀取最新已提交的 P2-136 release verifier preflight gate"
"此端點只回傳 release verifier preflight、rollback verifier preflight、maintenance window verifier hold、"
"live apply verifier hold、blocked verifier preflight transition 與 operator handoff"
"不授權 owner release、不批准 maintenance window、不確認 rollback owner、不通過 release authorization、"
"不釋放 rollback release 或 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、"
"reviewer queue、Gateway queue不送 Telegram、不呼叫 Bot API、不讀 secret。"
),
)
async def get_agent_result_capture_release_verifier_preflight_gate() -> dict[str, Any]:
"""Return the latest read-only release verifier preflight gate."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_verifier_preflight_gate)
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_result_capture_release_verifier_preflight_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release verifier preflight gate 無效",
) from exc
@router.get(
"/agent-result-capture-release-verifier-owner-review-packet",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release verifier owner review packet",
description=(
"讀取最新已提交的 P2-137 release verifier owner review packet"
"此端點只回傳 owner review packet、verifier review packet、rollback owner review、maintenance window review hold、"
"live apply owner review hold、blocked owner review transition 與 operator action"
"不授權 owner release、不批准維護窗口、不確認 rollback owner、不通過 post-release verifier、"
"不核發或通過 release authorization、不釋放 rollback release 或 live apply、不套用 writer、"
"不寫 receipt、result capture、learning、PlayBook trust、reviewer queue 或 Gateway queue"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_verifier_owner_review_packet() -> dict[str, Any]:
"""Return the latest read-only release verifier owner review packet."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_verifier_owner_review_packet)
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_result_capture_release_verifier_owner_review_packet_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release verifier owner review packet 無效",
) from exc
@router.get(
"/agent-result-capture-release-decision-hold",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release decision hold",
description=(
"讀取最新已提交的 P2-138 release decision hold"
"此端點只回傳 release decision hold、owner / verifier / rollback / live apply decision hold、"
"blocked decision transition 與 operator action"
"不把 owner review packet 視為決策通過、不授權 owner release、不通過 release decision、"
"不核發或通過 release authorization、不釋放 rollback release 或 live apply、不套用 writer、"
"不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_decision_hold() -> dict[str, Any]:
"""Return the latest read-only release decision hold."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_decision_hold)
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_result_capture_release_decision_hold_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release decision hold 無效",
) from exc
@router.get(
"/agent-result-capture-release-decision-readback",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release decision readback",
description=(
"讀取最新已提交的 P2-139 release decision readback"
"此端點只回讀 P2-138 release decision hold 後的釋出、負責人、驗證器、回滾、"
"維護窗口與正式套用決策狀態,"
"不把 hold 視為決策通過、不授權 owner release、不核發 release authorization、"
"不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_decision_readback() -> dict[str, Any]:
"""Return the latest read-only release decision readback."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_decision_readback)
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_result_capture_release_decision_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release decision readback 無效",
) from exc
@router.get(
"/agent-result-capture-release-decision-next-handoff",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release decision next handoff",
description=(
"讀取最新已提交的 P2-140 release decision next handoff"
"此端點只回讀 P2-139 release decision readback 的下一關交接,隔離 P2-139 自我迴圈提示,"
"不把 handoff 視為 owner approval、不核發 release authorization、"
"不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_decision_next_handoff() -> dict[str, Any]:
"""Return the latest read-only release decision next-handoff readback."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_decision_next_handoff)
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_result_capture_release_decision_next_handoff_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release decision next handoff 無效",
) from exc
@router.get(
"/agent-result-capture-release-decision-input-prep",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release decision input prep",
description=(
"讀取最新已提交的 P2-141 release decision input prep"
"此端點只把 P2-140 next handoff 轉成 owner / verifier 決策輸入準備包,"
"不把準備包視為 owner approval、不核發 release authorization、"
"不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_decision_input_prep() -> dict[str, Any]:
"""Return the latest read-only release decision input-prep package."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_decision_input_prep)
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_result_capture_release_decision_input_prep_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release decision input prep 無效",
) from exc
@router.get(
"/agent-result-capture-release-decision-owner-response-preflight",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release decision owner response preflight",
description=(
"讀取最新已提交的 P2-143 release decision owner response preflight"
"此端點只把 P2-141 決策輸入準備包轉成 owner response 預檢與拒收邊界,"
"不把預檢視為正式收件或批准、不核發 release authorization、"
"不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_decision_owner_response_preflight() -> dict[str, Any]:
"""Return the latest read-only release decision owner-response preflight."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_decision_owner_response_preflight)
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_result_capture_release_decision_owner_response_preflight_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release decision owner response preflight 無效",
) from exc
@router.get(
"/agent-result-capture-release-decision-owner-response-readback",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release decision owner response readback",
description=(
"讀取最新已提交的 P2-144 release decision owner response readback"
"此端點只讀回 P2-143 預檢後的外部 owner response 狀態,"
"不把未收到回覆視為正式收件或批准、不核發 release authorization、"
"不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_decision_owner_response_readback() -> dict[str, Any]:
"""Return the latest read-only release decision owner-response readback."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_decision_owner_response_readback)
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_result_capture_release_decision_owner_response_readback_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release decision owner response readback 無效",
) from exc
@router.get(
"/agent-result-capture-release-decision-owner-response-acceptance-gate",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release decision owner response acceptance gate",
description=(
"讀取最新已提交的 P2-145 release decision owner response acceptance gate"
"此端點只建立 P2-144 readback 後的驗收門檻,"
"不把未收到或未遮罩回覆視為正式收件、接受、拒絕或 release authorization"
"不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_decision_owner_response_acceptance_gate() -> dict[str, Any]:
"""Return the latest read-only release decision owner-response acceptance gate."""
try:
payload = await asyncio.to_thread(
load_latest_ai_agent_result_capture_release_decision_owner_response_acceptance_gate
)
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_result_capture_release_decision_owner_response_acceptance_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release decision owner response acceptance gate 無效",
) from exc
@router.get(
"/agent-owner-approved-fixture-dry-run",
response_model=dict[str, Any],
summary="取得 AI Agent owner-approved fixture dry-run 批准包",
description=(
"讀取最新已提交的 owner-approved fixture dry-run 批准包;此端點只回傳 fixture-only dry-run 證據,"
"不寫 KM、不更新 PlayBook trust、不寫 timeline / replay score、不寫 Gateway queue、不呼叫 Telegram Bot API、"
"不啟動 runtime worker、不開 Redis consumer group、不執行 DB migration、不觸發 workflow、"
"不執行主機或 cluster 指令、不使用 secrets 或付費 API、不回傳未核准內部細節。"
),
)
async def get_agent_owner_approved_fixture_dry_run() -> dict[str, Any]:
"""Return the latest read-only AI Agent owner-approved fixture dry-run package."""
try:
return await asyncio.to_thread(load_latest_ai_agent_owner_approved_fixture_dry_run)
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_owner_approved_fixture_dry_run_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent owner-approved fixture dry-run 批准包無效",
) from exc
@router.get(
"/agent-proactive-operations-contract",
response_model=dict[str, Any],
summary="取得 AI Agent 主動營運委派與版本生命週期契約",
description=(
"讀取最新已提交的 AI Agent 主動營運、版本生命週期、可委派能力、MCP、RAG 與 Telegram 邊界契約;"
"此端點不啟用排程、不升級套件、不更新主機、不 pull image、不 auto merge、不送 Telegram、"
"不呼叫付費服務、不修改生產路由。"
),
)
async def get_agent_proactive_operations_contract() -> dict[str, Any]:
"""Return the latest read-only AI Agent proactive operations contract."""
try:
return await asyncio.to_thread(load_latest_ai_agent_proactive_operations_contract)
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_proactive_operations_contract_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 主動營運委派與版本生命週期契約無效",
) from exc
@router.get(
"/agent-version-lifecycle-update-proposal",
response_model=dict[str, Any],
summary="取得 AI Agent 版本生命週期更新提案佇列",
description=(
"讀取最新已提交的 AI Agent / 套件 / 工具 / 服務 / 主機版本生命週期更新提案;"
"此端點只回傳已脫敏治理資料與批准 gate不啟用排程、不外查 registry/CVE/市場來源、"
"不升級套件、不寫 lockfile、不 pull/build/push image、不操作 host/K3s/stateful、"
"不建立 PR、不 auto merge、不送 Telegram、不讀取機密、不切換 provider、"
"不替換 OpenClaw、不修改生產路由。"
),
)
async def get_agent_version_lifecycle_update_proposal() -> dict[str, Any]:
"""Return the latest read-only AI Agent version lifecycle update proposal."""
try:
return await asyncio.to_thread(load_latest_ai_agent_version_lifecycle_update_proposal)
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_version_lifecycle_update_proposal_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 版本生命週期更新提案無效",
) from exc
@router.get(
"/agent-version-freshness-snapshot",
response_model=dict[str, Any],
summary="取得 AI Agent repo-only 版本新鮮度快照",
description=(
"讀取最新已提交的 AI Agent repo-only 版本新鮮度快照;此端點不啟用每日排程、"
"不查外部 registry/CVE、不安裝或升級套件、不寫 lockfile、不 build/pull image、"
"不 probe 主機、不建立 PR、不發 Telegram、不呼叫付費服務、不修改生產路由。"
),
)
async def get_agent_version_freshness_snapshot() -> dict[str, Any]:
"""Return the latest read-only AI Agent version freshness snapshot."""
try:
return await asyncio.to_thread(load_latest_ai_agent_version_freshness_snapshot)
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_version_freshness_snapshot_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 版本新鮮度快照無效",
) from exc
@router.get(
"/agent-tool-adoption-approval-package",
response_model=dict[str, Any],
summary="取得 AI Agent 工具採用批准包",
description=(
"讀取最新已提交的 Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包;"
"此端點不安裝工具、不寫 CI workflow、不下載漏洞資料庫、不查外部 registry、不升級套件、"
"不寫 lockfile、不 build/pull image、不建立 Gitea PR、不 auto merge、不發 Telegram、"
"不呼叫付費服務、不修改生產路由。"
),
)
async def get_agent_tool_adoption_approval_package() -> dict[str, Any]:
"""Return the latest read-only AI Agent tool adoption approval package."""
try:
return await asyncio.to_thread(load_latest_ai_agent_tool_adoption_approval_package)
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_tool_adoption_approval_package_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent 工具採用批准包無效",
) from exc
@router.get(
"/agent-telegram-action-required-digest-policy",
response_model=dict[str, Any],
summary="取得 AI Agent Telegram action-required digest policy",
description=(
"讀取最新已提交的 AI Agent Telegram action-required digest policy"
"此端點只回傳 critical / action-required / failure-only digest 規則與 redaction 邊界,"
"不送 Telegram、不寫 Telegram Gateway queue、不改 Alertmanager route / receiver、"
"不寫 AwoooP event、不觸發 workflow、不查外部掃描、不執行 runtime、不讀取 secret、"
"不回傳內部協作逐字稿。"
),
)
async def get_agent_telegram_action_required_digest_policy() -> dict[str, Any]:
"""Return the latest read-only AI Agent Telegram action-required digest policy."""
try:
return await asyncio.to_thread(load_latest_ai_agent_telegram_action_required_digest_policy)
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_telegram_action_required_digest_policy_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent Telegram action-required digest policy 無效",
) from exc
@router.get(
"/agent-gitea-pr-draft-lane",
response_model=dict[str, Any],
summary="取得 AI Agent Gitea PR 草案 lane",
description=(
"讀取最新已提交的 AI Agent Gitea PR 草案 lane"
"此端點只回傳 grouping、automerge=false、測試證據、rollback、owner response 與 redaction 邊界,"
"不 push branch、不建立或更新 Gitea PR、不留言、不 auto merge、不觸發 workflow、不改 CI、"
"不寫 lockfile、不升級套件、不 build/pull image、不改 production route、不發 Telegram、"
"不讀取 secret、不回傳內部協作逐字稿。"
),
)
async def get_agent_gitea_pr_draft_lane() -> dict[str, Any]:
"""Return the latest read-only AI Agent Gitea PR draft lane policy."""
try:
return await asyncio.to_thread(load_latest_ai_agent_gitea_pr_draft_lane)
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_gitea_pr_draft_lane_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent Gitea PR 草案 lane 無效",
) from exc
@router.get(
"/agent-host-stateful-version-inventory",
response_model=dict[str, Any],
summary="取得 AI Agent host / K3s / stateful 版本只讀盤點",
description=(
"讀取最新已提交的 AI Agent host OS / K3s / stateful services 版本只讀盤點與 "
"maintenance window 批准包;此端點不 SSH、不執行 host command、不執行 kubectl、"
"不 apt upgrade、不升級 kernel/K3s、不 drain node、不 reboot、不 restart stateful service、"
"不做 DB migration、不刪備份、不 restore、不 pull image、不安裝套件、不查外部版本來源、"
"不 active scan、不發 Telegram、不讀取 secret、不回傳內部協作逐字稿。"
),
)
async def get_agent_host_stateful_version_inventory() -> dict[str, Any]:
"""Return the latest read-only host / K3s / stateful version inventory."""
try:
return await asyncio.to_thread(load_latest_ai_agent_host_stateful_version_inventory)
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_host_stateful_version_inventory_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent host / K3s / stateful 版本只讀盤點無效",
) from exc
@router.get(
"/runtime-surface-inventory",
response_model=dict[str, Any],
summary="取得 Runtime surface 只讀盤點",
description=(
"讀取最新已提交的 API / Web / Worker / K8s runtime surface 盤點;"
"此端點不呼叫 live cluster、不碰 DB/Redis、不讀 Secret payload、"
"不執行 rollout/restart/scale/delete、不 patch K8s、不改 workflow、不改生產路由。"
),
)
async def get_runtime_surface_inventory() -> dict[str, Any]:
"""Return the latest read-only runtime surface inventory."""
try:
payload = await asyncio.to_thread(load_latest_runtime_surface_inventory)
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("runtime_surface_inventory_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Runtime surface 盤點快照無效",
) from exc
@router.get(
"/gitea-workflow-runner-health",
response_model=dict[str, Any],
summary="取得 Gitea 工作流程與 runner 健康合約",
description=(
"讀取最新已提交的 Gitea workflow / runner health contract"
"此端點不呼叫 Gitea API、不修改 workflow、不重啟 runner、不停止 container、"
"不讀 Secret payload、不送 Telegram 測試通知、不觸發 deploy 或 migration。"
),
)
async def get_gitea_workflow_runner_health() -> dict[str, Any]:
"""Return the latest read-only Gitea workflow / runner health contract."""
try:
return await asyncio.to_thread(load_latest_gitea_workflow_runner_health)
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("gitea_workflow_runner_health_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Gitea 工作流程與 runner 健康合約快照無效",
) from exc
@router.get(
"/observability-contract-matrix",
response_model=dict[str, Any],
summary="取得監控合約與降噪機會矩陣",
description=(
"讀取最新已提交的 Prometheus / Alertmanager / Grafana / SigNoz / ClickHouse / Sentry "
"只讀 observability matrix此端點不修改 alert rules、不呼叫 silence API、"
"不建立 Grafana dashboard、不改 SigNoz / Sentry 設定、不讀 Secret payload、"
"不送 Telegram 測試通知、不觸發 monitoring deploy。"
),
)
async def get_observability_contract_matrix() -> dict[str, Any]:
"""Return the latest read-only observability contract matrix."""
try:
return await asyncio.to_thread(load_latest_observability_contract_matrix)
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("observability_contract_matrix_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="監控合約與降噪機會矩陣快照無效",
) from exc
@router.get(
"/ai-provider-route-matrix",
response_model=dict[str, Any],
summary="取得 AI Provider 路由只讀矩陣",
description=(
"讀取最新已提交的 AI Router / Ollama / OpenClaw / Nemotron / Gemini provider route matrix"
"此端點不切換 provider、不呼叫 Gemini / NVIDIA / Claude、不改 USE_AI_ROUTER、"
"不修改 fallback order、不讀 Secret payload、不進 shadow / canary、"
"不觸發 workflow / deploy / reload / runtime execution。"
),
)
async def get_ai_provider_route_matrix() -> dict[str, Any]:
"""Return the latest read-only AI provider route matrix."""
try:
return await asyncio.to_thread(load_latest_ai_provider_route_matrix)
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_provider_route_matrix_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Provider 路由只讀矩陣快照無效",
) from exc
@router.get(
"/service-health-gap-matrix",
response_model=dict[str, Any],
summary="取得服務健康缺口與過期端點矩陣",
description=(
"讀取最新已提交的 service health gap matrix此端點不做 live probe、"
"不重啟服務、不修改 endpoint / ConfigMap、不讀 Secret/Redis/DB payload、"
"不發通知、不觸發 workflow/deploy/reload/runtime execution。"
),
)
async def get_service_health_gap_matrix() -> dict[str, Any]:
"""Return the latest read-only service health gap matrix."""
try:
payload = await asyncio.to_thread(load_latest_service_health_gap_matrix)
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("service_health_gap_matrix_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="服務健康缺口矩陣快照無效",
) from exc
@router.get(
"/backup-dr-target-inventory",
response_model=dict[str, Any],
summary="取得 Backup / DR 目標盤點",
description=(
"讀取最新已提交的 Backup / DR 目標盤點;"
"此端點不呼叫外部來源、不執行備份/restore/offsite sync、"
"不寫 credential marker、不改排程、不批准任何破壞性操作。"
),
)
async def get_backup_dr_target_inventory() -> dict[str, Any]:
"""Return the latest read-only Backup / DR target inventory."""
try:
return await asyncio.to_thread(load_latest_backup_dr_target_inventory)
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("backup_dr_target_inventory_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Backup / DR target inventory is invalid",
) from exc
@router.get(
"/backup-dr-readiness-matrix",
response_model=dict[str, Any],
summary="取得 Backup / DR 準備度矩陣",
description=(
"讀取最新已提交的 Backup / DR 準備度矩陣;"
"此端點不呼叫外部來源、不執行備份/restore/offsite sync、"
"不寫 credential marker、不改排程、不批准任何破壞性操作。"
),
)
async def get_backup_dr_readiness_matrix() -> dict[str, Any]:
"""Return the latest read-only Backup / DR readiness matrix."""
try:
return await asyncio.to_thread(load_latest_backup_dr_readiness_matrix)
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("backup_dr_readiness_matrix_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Backup / DR readiness matrix is invalid",
) from exc
@router.get(
"/backup-notification-policy",
response_model=dict[str, Any],
summary="取得備份通知政策",
description=(
"讀取最新已提交的備份通知政策;此端點只回傳 success-noise suppression、"
"failure/action-required 升級與每日摘要合約,不送通知、不執行備份/restore/offsite sync、"
"不寫 credential marker、不改排程、不寫 workflow、不發 Telegram 測試訊息。"
),
)
async def get_backup_notification_policy() -> dict[str, Any]:
"""Return the latest read-only backup notification policy."""
try:
return await asyncio.to_thread(load_latest_backup_notification_policy)
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("backup_notification_policy_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="備份通知政策快照無效",
) from exc
@router.get(
"/service-health-failure-notification-policy",
response_model=dict[str, Any],
summary="取得服務健康失敗限定通知合約",
description=(
"讀取最新已提交的 service health failure-only Telegram / AwoooP 通知合約;"
"此端點只回傳成功降噪、action-required 與 failure escalation 規則,"
"不送通知、不做 live probe、不重啟服務、不改 endpoint、不觸發 workflow / runtime execution、"
"不讀取 secret payload、不回傳內部協作逐字稿或提示詞。"
),
)
async def get_service_health_failure_notification_policy() -> dict[str, Any]:
"""Return the latest read-only service health failure-only notification policy."""
try:
return await asyncio.to_thread(load_latest_service_health_failure_notification_policy)
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("service_health_failure_notification_policy_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="服務健康失敗限定通知合約快照無效",
) from exc
@router.get(
"/backup-restore-drill-approval-package-template",
response_model=dict[str, Any],
summary="取得 Backup / DR 復原演練批准包模板",
description=(
"讀取最新已提交的 Backup / DR restore drill、credential escrow review、"
"K8s resource recovery、observability recovery 與 route reconstruction 批准包模板;"
"此端點只回傳 read-only template不執行 backup、restore、offsite sync、"
"不寫 credential marker、不改排程、不寫 workflow、不送 Telegram 測試通知、"
"不輸出 secret 明文、不做破壞性 prune、不呼叫付費 API、不建立 shadow/canary、不改生產路由。"
),
)
async def get_backup_restore_drill_approval_package_template() -> dict[str, Any]:
"""Return the latest read-only Backup / DR restore drill approval package template."""
try:
return await asyncio.to_thread(load_latest_backup_restore_drill_approval_package_template)
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("backup_restore_drill_approval_package_template_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Backup / DR 復原演練批准包模板快照無效",
) from exc
@router.get(
"/offsite-escrow-readiness-status",
response_model=dict[str, Any],
summary="取得異地 / Escrow 準備度狀態",
description=(
"讀取最新已提交的異地備份、credential escrow 與 K8s resource offsite readiness 狀態;"
"此端點只回傳 read-only status不執行 backup、restore、offsite sync、"
"不寫 credential marker、不讀 credential、不輸出 secret 明文、不改排程、不寫 workflow、"
"不送 Telegram 測試通知、不做破壞性 prune、不改生產路由。"
),
)
async def get_offsite_escrow_readiness_status() -> dict[str, Any]:
"""Return the latest read-only offsite / escrow readiness status."""
try:
return await asyncio.to_thread(load_latest_offsite_escrow_readiness_status)
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("offsite_escrow_readiness_status_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="異地 / Escrow 準備度狀態快照無效",
) from exc
@router.get(
"/package-supply-chain-inventory",
response_model=dict[str, Any],
summary="取得套件 / 供應鏈盤點",
description=(
"讀取最新已提交的套件 / 供應鏈盤點;"
"此端點不呼叫外部來源、不安裝依賴、不升級套件、"
"不寫 lockfile、不查外部 CVE、不重建 image、不改生產路由。"
),
)
async def get_package_supply_chain_inventory() -> dict[str, Any]:
"""Return the latest read-only package supply-chain inventory."""
try:
return await asyncio.to_thread(load_latest_package_supply_chain_inventory)
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("package_supply_chain_inventory_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="套件 / 供應鏈盤點快照無效",
) from exc
@router.get(
"/javascript-package-inventory",
response_model=dict[str, Any],
summary="取得 JavaScript 套件盤點",
description=(
"讀取最新已提交的 JavaScript / pnpm 套件盤點;"
"此端點不呼叫外部來源、不安裝套件、不升級套件、"
"不寫 lockfile、不執行 npm audit、不改生產路由。"
),
)
async def get_javascript_package_inventory() -> dict[str, Any]:
"""Return the latest read-only JavaScript package inventory."""
try:
return await asyncio.to_thread(load_latest_javascript_package_inventory)
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("javascript_package_inventory_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="JavaScript 套件盤點快照無效",
) from exc
@router.get(
"/docker-build-surface-inventory",
response_model=dict[str, Any],
summary="取得 Docker build surface 盤點",
description=(
"讀取最新已提交的 Docker base image 與 build surface 盤點;"
"此端點不執行 docker build、不 pull image、不推 registry、"
"不查外部 CVE、不安裝套件、不改生產路由。"
),
)
async def get_docker_build_surface_inventory() -> dict[str, Any]:
"""Return the latest read-only Docker build surface inventory."""
try:
return await asyncio.to_thread(load_latest_docker_build_surface_inventory)
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("docker_build_surface_inventory_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Docker build surface 盤點快照無效",
) from exc
@router.get(
"/dependency-risk-policy",
response_model=dict[str, Any],
summary="取得依賴風險政策",
description=(
"讀取最新已提交的 CVE / license / drift 嚴重度政策;"
"此端點不呼叫外部 CVE 或 license 來源、不安裝套件、不升級套件、"
"不寫 lockfile、不執行 docker build、不 pull image、不推 registry、"
"不呼叫付費 API、不建立 shadow/canary、不改生產路由。"
),
)
async def get_dependency_risk_policy() -> dict[str, Any]:
"""Return the latest read-only dependency risk policy."""
try:
return await asyncio.to_thread(load_latest_dependency_risk_policy)
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("dependency_risk_policy_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="依賴風險政策快照無效",
) from exc
@router.get(
"/dependency-drift-check-plan",
response_model=dict[str, Any],
summary="取得依賴漂移檢查設計",
description=(
"讀取最新已提交的定期依賴漂移、外部資料來源與 AI Agent 市場觀察設計;"
"此端點只回傳 read-only plan不啟用排程、不寫 workflow、不呼叫外部 CVE / license / registry / 市場來源、"
"不安裝 SDK、不呼叫付費 API、不安裝或升級套件、不寫 lockfile、"
"不執行 docker build、不 pull image、不推 registry、不建立 shadow/canary、不改生產路由。"
),
)
async def get_dependency_drift_check_plan() -> dict[str, Any]:
"""Return the latest read-only dependency drift check plan."""
try:
return await asyncio.to_thread(load_latest_dependency_drift_check_plan)
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("dependency_drift_check_plan_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="依賴漂移檢查設計快照無效",
) from exc
@router.get(
"/dependency-supply-chain-drift-monitor",
response_model=dict[str, Any],
summary="取得 P2-004 依賴 / 供應鏈漂移監控",
description=(
"讀取最新已提交的 P2-004 依賴 / 供應鏈漂移監控;"
"此端點只回傳 repo-only committed snapshot不啟用排程、不寫 workflow、"
"不呼叫外部 CVE / license / registry / Agent market 來源、不安裝或升級套件、"
"不寫 lockfile、不執行 npm audit、不執行 docker build、不 pull image、不推 registry、"
"不建立 PR、不送 Telegram、不讀 secret、不做 host probe、不做 production write。"
),
)
async def get_dependency_supply_chain_drift_monitor() -> dict[str, Any]:
"""Return the latest read-only P2-004 dependency / supply-chain drift monitor."""
try:
return await asyncio.to_thread(load_latest_dependency_supply_chain_drift_monitor)
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("dependency_supply_chain_drift_monitor_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-004 依賴 / 供應鏈漂移監控快照無效",
) from exc
@router.get(
"/product-code-review-gate",
response_model=dict[str, Any],
summary="取得 P2-111 全產品 Code Review 防木馬 Gate",
description=(
"讀取最新已提交的 P2-111 全產品推版前後 Code Review / 防木馬 Gate"
"此端點會把 AwoooP tenants 資產台帳、Gitea code-review、供應鏈漂移、Aider 事件與 AI reviewer "
"分工收斂成只讀 read model。它不啟用外部 scanner、不寫 workflow、不 auto-merge、"
"不部署、不讀 secret、不推 registry、不簽 artifact、不送 Telegram、不寫 Gateway queue、不做 host probe。"
),
)
async def get_product_code_review_gate() -> dict[str, Any]:
"""Return the latest read-only all-product code review gate."""
try:
return await asyncio.to_thread(load_latest_product_code_review_gate)
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("product_code_review_gate_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="P2-111 全產品 Code Review Gate 快照無效",
) from exc
@router.get(
"/dependency-upgrade-approval-package-template",
response_model=dict[str, Any],
summary="取得依賴升級批准包模板",
description=(
"讀取最新已提交的依賴升級、digest pin、publish boundary 與外部來源啟用批准包模板;"
"此端點只回傳 read-only template不安裝或升級套件、不寫 manifest 或 lockfile、"
"不修改 Dockerfile、不執行 docker build、不 pull image、不推 registry、不 publish package、"
"不安裝 SDK、不呼叫付費 API、不建立 shadow/canary、不改生產路由。"
),
)
async def get_dependency_upgrade_approval_package_template() -> dict[str, Any]:
"""Return the latest read-only dependency upgrade approval package template."""
try:
return await asyncio.to_thread(load_latest_dependency_upgrade_approval_package_template)
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("dependency_upgrade_approval_package_template_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="依賴升級批准包模板快照無效",
) from exc
# =============================================================================
# Integration with Incident Flow
# =============================================================================
async def trigger_agent_analysis_for_incident(
incident_id: str,
background_tasks: BackgroundTasks,
) -> str | None:
"""
整合點: 當 Incident 需要複雜決策時自動觸發 Agent Teams
這個函數可被 incident_engine 或 webhooks 調用
Returns:
task_id if triggered, None if skipped
"""
service = get_agent_service()
task_id, incident = await service.trigger_for_incident(incident_id)
if task_id is None:
return None
if incident is None:
return None
# 加入背景任務
background_tasks.add_task(
service.run_analysis,
task_id,
incident,
)
return task_id