fix(bridge): approval flow robustness — pending cleanup, MERGE dedup, false positive filter, auto_resolve, 30min timeout
This commit is contained in:
49
bot.py
49
bot.py
@@ -35,7 +35,7 @@ class ApprovalView(discord.ui.View):
|
||||
"""Discord buttons for approving/rejecting Antigravity actions."""
|
||||
|
||||
def __init__(self, bridge: BridgeProtocol, request: ApprovalRequest):
|
||||
super().__init__(timeout=300)
|
||||
super().__init__(timeout=1800) # 30 minutes
|
||||
self.bridge = bridge
|
||||
self.request = request
|
||||
self.responded = False
|
||||
@@ -100,6 +100,7 @@ class GravityBot(commands.Bot):
|
||||
self.channel_to_project: dict[int, str] = {} # channel.id → project
|
||||
self.session_status_messages: dict[str, int] = {} # conv_id → msg_id
|
||||
self._sent_approval_ids: set[str] = set()
|
||||
self._deferred_ids: dict[str, int] = {} # request_id → defer count
|
||||
self._ready_event = asyncio.Event()
|
||||
self._channel_lock = asyncio.Lock()
|
||||
self.bridge = BridgeProtocol()
|
||||
@@ -450,6 +451,25 @@ class GravityBot(commands.Bot):
|
||||
if req.discord_message_id != 0:
|
||||
continue
|
||||
|
||||
# Defer short-command pendings (e.g. "Run") by 4 cycles (~12s)
|
||||
# to give step_probe time to merge detailed command info
|
||||
# (step_probe MERGE happens ~10s after pending creation)
|
||||
if len(req.command) <= 15:
|
||||
if req.request_id not in self._deferred_ids:
|
||||
self._deferred_ids[req.request_id] = 1
|
||||
continue # skip this cycle
|
||||
elif self._deferred_ids[req.request_id] < 4:
|
||||
self._deferred_ids[req.request_id] += 1
|
||||
# Re-read from file (step_probe may have merged)
|
||||
fresh = self.bridge.read_pending_request(req.request_id)
|
||||
if fresh and len(fresh.command) > 15:
|
||||
req = fresh # use merged version — send now!
|
||||
else:
|
||||
continue # wait one more cycle
|
||||
|
||||
# Clean up defer tracking
|
||||
self._deferred_ids.pop(req.request_id, None)
|
||||
|
||||
# Learn project mapping from pending approval
|
||||
project = req.project_name or Config.PROJECT_NAME
|
||||
if req.conversation_id and req.conversation_id != '__global__':
|
||||
@@ -459,6 +479,33 @@ class GravityBot(commands.Bot):
|
||||
if channel:
|
||||
self._sent_approval_ids.add(req.request_id)
|
||||
await self._send_approval_request(channel, req)
|
||||
|
||||
# ── Check for auto_resolved pendings (approved directly in AG) ──
|
||||
for f in self.bridge.pending_dir.glob("*.json"):
|
||||
try:
|
||||
data = json.loads(f.read_text(encoding="utf-8-sig"))
|
||||
if data.get("status") == "auto_resolved":
|
||||
msg_id = data.get("discord_message_id", 0)
|
||||
project = data.get("project_name", Config.PROJECT_NAME)
|
||||
if msg_id:
|
||||
channel = await self._get_channel(project)
|
||||
if channel:
|
||||
try:
|
||||
msg = await channel.fetch_message(msg_id)
|
||||
embed = discord.Embed(
|
||||
title="✅ AG에서 직접 승인됨",
|
||||
description=f"```\n{data.get('command', '')[:500]}\n```",
|
||||
color=discord.Color.green(),
|
||||
)
|
||||
embed.set_footer(text=f"ID: {data.get('request_id', '')}")
|
||||
await msg.edit(embed=embed, view=None)
|
||||
except discord.NotFound:
|
||||
pass
|
||||
f.unlink()
|
||||
self._deferred_ids.pop(data.get("request_id", ""), None)
|
||||
except (json.JSONDecodeError, OSError):
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error scanning approvals: {e}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user