diff --git a/apps/api/src/services/approval_execution.py b/apps/api/src/services/approval_execution.py index 3de07695..871469b0 100644 --- a/apps/api/src/services/approval_execution.py +++ b/apps/api/src/services/approval_execution.py @@ -496,11 +496,53 @@ class ApprovalExecutionService: settings = get_settings() gateway = get_telegram_gateway() + # 2026-04-19 ogt + Claude Opus 4.7 修 AP-2: 除了 reply 外, + # 也 edit 原卡片移除按鈕 + 更新狀態戳記(避免卡片永遠停在「執行中」) + try: + await gateway._send_request("editMessageReplyMarkup", { + "chat_id": settings.OPENCLAW_TG_CHAT_ID, + "message_id": orig_msg_id, + "reply_markup": {"inline_keyboard": []}, + }) + except Exception as _edit_e: + logger.debug("push_execution_edit_buttons_failed", + approval_id=str(approval.id), error=str(_edit_e)) + + # 附加 KM/Playbook 增量(查最近該 incident 的 KM + playbook 使用) + km_info = "" + try: + from sqlalchemy import text as _sql + from src.db.base import get_db_context + async with get_db_context() as _db: + _km_row = await _db.execute( + _sql("""SELECT COUNT(*) FROM knowledge_entries + WHERE created_at > NOW() - interval '2 minutes'"""), + ) + _km_count = _km_row.scalar() or 0 + _pb_row = await _db.execute( + _sql("""SELECT COUNT(*) FROM playbooks + WHERE updated_at > NOW() - interval '2 minutes'"""), + ) + _pb_count = _pb_row.scalar() or 0 + if _km_count or _pb_count: + km_info = f"\n📚 KM +{_km_count} 🎯 Playbook 更新×{_pb_count}" + except Exception: + pass + if success: - text = f"✅ 執行成功\n{(approval.action or '')[:180]}" + text = ( + f"✅ 執行成功\n" + f"{(approval.action or '')[:180]}" + f"{km_info}" + ) else: err_short = (error or "未知錯誤")[:150] - text = f"❌ 執行失敗\n{(approval.action or '')[:180]}\n原因: {err_short}" + text = ( + f"❌ 執行失敗\n" + f"{(approval.action or '')[:180]}\n" + f"原因: {err_short}" + f"{km_info}" + ) await gateway._http_client.post( f"https://api.telegram.org/bot{settings.OPENCLAW_TG_BOT_TOKEN}/sendMessage", diff --git a/apps/api/src/services/openclaw.py b/apps/api/src/services/openclaw.py index 6495452d..c81730a8 100644 --- a/apps/api/src/services/openclaw.py +++ b/apps/api/src/services/openclaw.py @@ -1269,7 +1269,12 @@ class OpenClawService: data["confidence"] = 0.0 # 截斷/缺失 → 0.0,不可偽造 if "risk_level" not in data: data["risk_level"] = "low" - if "primary_responsibility" not in data: + # 2026-04-19 ogt + Claude Opus 4.7 修 AP-3: + # primary_responsibility 有時 LLM 填空字串/None → resp_display 顯示「❓ 未知」 + # 強制正規化: 空/None/不在白名單 → 用 kubectl 有無推 INFRA 或 BE (非「未知」) + _valid_resp = {"FE", "BE", "INFRA", "DB", "COLLAB"} + _cur_resp = str(data.get("primary_responsibility") or "").strip().upper() + if _cur_resp not in _valid_resp: data["primary_responsibility"] = "INFRA" if "kubectl" in str(data) else "BE" if "suggested_action" not in data: data["suggested_action"] = "RESTART_DEPLOYMENT" if "restart" in str(data).lower() else "NO_ACTION" diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index 26bed6fa..5aafdd0f 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -1717,6 +1717,35 @@ class TelegramGateway: except Exception as _db_e: logger.warning("notification_outcomes_db_write_failed", error=str(_db_e)) + # 2026-04-19 ogt + Claude Opus 4.7: 修 AP-1 — message_id 同時存進 + # approval_records.telegram_message_id,不只 Redis(重啟會丟) + if _msg_id: + try: + from src.services.approval_db import get_approval_service + _svc = get_approval_service() + if hasattr(_svc, "update_telegram_message"): + # 若有 update_telegram_message 方法(通常用 incident_id) + # 先用 incident_id 更新,再 fallback 直接 UPDATE approval_records + from sqlalchemy import text as _sql2 + from src.db.base import get_db_context as _gdc + async with _gdc() as _db2: + await _db2.execute( + _sql2(""" + UPDATE approval_records + SET telegram_message_id = :mid, + telegram_chat_id = :cid + WHERE id = :aid + """), + { + "mid": int(_msg_id), + "cid": int(settings.OPENCLAW_TG_CHAT_ID), + "aid": str(approval_id), + }, + ) + except Exception as _db_e2: + logger.warning("approval_tg_msg_id_db_persist_failed", + approval_id=str(approval_id), error=str(_db_e2)) + # 2026-04-10 Claude Sonnet 4.6 Asia/Taipei: 儲存 message_id 供自動修復後更新卡片 # key: tg_approval:{approval_id},TTL 24h if _msg_id: