fix(work): import hashlib and wire heal subparser into main CLI
This commit is contained in:
+51
-12
@@ -364,12 +364,11 @@ DM_LOG_FILE = os.path.join(NETVM_ROOT, "dm-log.jsonl")
|
||||
JOB_LOG_FILE = os.path.join(NETVM_ROOT, "job-log.jsonl")
|
||||
|
||||
|
||||
def reconstruct_loops(limit=50, agent=None, status_filter=None) -> list:
|
||||
"""Reconstruct active and recent loops from followups.json and dm-log.jsonl.
|
||||
def _load_loop_candidates() -> dict:
|
||||
"""Parse followups.json + dm-log.jsonl into a loop_id -> dict map.
|
||||
|
||||
Returns a list of dicts:
|
||||
loop_id, agent, sender, target, purpose, state, sent_at, deadline,
|
||||
nudges_sent, nudges_allowed, escalate_to, tags, summary
|
||||
Pure parse phase of reconstruct_loops, extracted so diagnose_breaks and
|
||||
remediate_breaks can share one parse instead of re-reading the logs.
|
||||
"""
|
||||
loops = {} # loop_id -> dict
|
||||
|
||||
@@ -499,6 +498,11 @@ def reconstruct_loops(limit=50, agent=None, status_filter=None) -> list:
|
||||
"source": "dm-log.jsonl",
|
||||
}
|
||||
|
||||
return loops
|
||||
|
||||
|
||||
def _select_loops(loops: dict, limit=50, agent=None, status_filter=None) -> list:
|
||||
"""Filter/sort/limit a candidate map from _load_loop_candidates."""
|
||||
# Filter and sort
|
||||
result = list(loops.values())
|
||||
if agent:
|
||||
@@ -517,6 +521,17 @@ def reconstruct_loops(limit=50, agent=None, status_filter=None) -> list:
|
||||
return result[:limit]
|
||||
|
||||
|
||||
def reconstruct_loops(limit=50, agent=None, status_filter=None) -> list:
|
||||
"""Reconstruct active and recent loops from followups.json and dm-log.jsonl.
|
||||
|
||||
Returns a list of dicts:
|
||||
loop_id, agent, sender, target, purpose, state, sent_at, deadline,
|
||||
nudges_sent, nudges_allowed, escalate_to, tags, summary
|
||||
"""
|
||||
return _select_loops(_load_loop_candidates(), limit=limit, agent=agent,
|
||||
status_filter=status_filter)
|
||||
|
||||
|
||||
def get_fleet_loop_health(threshold=None) -> dict:
|
||||
"""Calculate fleet loop health per agent and overall verdict."""
|
||||
if threshold is None:
|
||||
@@ -570,8 +585,14 @@ def get_fleet_loop_health(threshold=None) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def diagnose_breaks() -> list:
|
||||
"""Diagnose break taxonomy across intrinsic loops and support services."""
|
||||
def diagnose_breaks(_fleet_cache=None, _loops_cache=None) -> list:
|
||||
"""Diagnose break taxonomy across intrinsic loops and support services.
|
||||
|
||||
_fleet_cache: optional list; when given, the fleet approval scan result
|
||||
is appended so callers (remediate_breaks) can reuse it instead of
|
||||
re-scanning (each scan fans 6 nodes over the full audit log).
|
||||
_loops_cache: optional list; when given, the parsed loop-candidate map
|
||||
is appended for the same single-parse sharing."""
|
||||
import subprocess
|
||||
breaks = []
|
||||
|
||||
@@ -609,7 +630,10 @@ def diagnose_breaks() -> list:
|
||||
})
|
||||
|
||||
# 3. Active follow-up loops check
|
||||
active_loops = reconstruct_loops(limit=20, status_filter="pending")
|
||||
_loops_map = _load_loop_candidates()
|
||||
if _loops_cache is not None:
|
||||
_loops_cache.append(_loops_map)
|
||||
active_loops = _select_loops(_loops_map, limit=20, status_filter="pending")
|
||||
now_ts = time.time()
|
||||
for l in active_loops:
|
||||
nudges_sent = l.get("nudges_sent", 0)
|
||||
@@ -628,6 +652,8 @@ def diagnose_breaks() -> list:
|
||||
try:
|
||||
import approvals
|
||||
fleet_apps = approvals.check_fleet_approvals()
|
||||
if _fleet_cache is not None:
|
||||
_fleet_cache.append(fleet_apps)
|
||||
for app in fleet_apps:
|
||||
if app.get("has_pending"):
|
||||
node = app["node"]
|
||||
@@ -734,7 +760,9 @@ def remediate_breaks(dry_run=False) -> dict:
|
||||
escalated = []
|
||||
|
||||
# 1. Check diagnosed hard breaks first
|
||||
breaks = diagnose_breaks()
|
||||
_fleet_cache = []
|
||||
_loops_cache = []
|
||||
breaks = diagnose_breaks(_fleet_cache=_fleet_cache, _loops_cache=_loops_cache)
|
||||
for b in breaks:
|
||||
if b.get("severity") in ("CRITICAL", "WARNING"):
|
||||
escalated.append(b)
|
||||
@@ -753,8 +781,13 @@ def remediate_breaks(dry_run=False) -> dict:
|
||||
|
||||
now_iso = datetime.now(timezone.utc).isoformat()
|
||||
|
||||
# Build answer map from reconstruct_loops
|
||||
loops = reconstruct_loops(limit=200)
|
||||
# Build answer map from reconstruct_loops (reuse diagnose's parse:
|
||||
# nothing between the parses writes the loop logs in-process, and a
|
||||
# concurrently landed reply is picked up on the next cycle).
|
||||
if _loops_cache:
|
||||
loops = _select_loops(_loops_cache[0], limit=200)
|
||||
else:
|
||||
loops = reconstruct_loops(limit=200)
|
||||
answered_dms = {
|
||||
l["loop_id"]: l for l in loops if l.get("state") in ("ANSWERED", "CLOSED")
|
||||
}
|
||||
@@ -836,7 +869,13 @@ def remediate_breaks(dry_run=False) -> dict:
|
||||
# Auto-remediate trusted approval blocks
|
||||
try:
|
||||
import approvals
|
||||
fleet_apps = approvals.check_fleet_approvals()
|
||||
# Reuse the diagnose_breaks scan: nothing between the scans touches
|
||||
# browser-approval state, and this block only reads it. Fall back to
|
||||
# a fresh scan if the first one failed.
|
||||
if _fleet_cache:
|
||||
fleet_apps = _fleet_cache[0]
|
||||
else:
|
||||
fleet_apps = approvals.check_fleet_approvals()
|
||||
for app in fleet_apps:
|
||||
if app.get("has_pending") and app.get("is_trusted") and app.get("status") != "KEY_APPROVAL":
|
||||
node = app["node"]
|
||||
|
||||
Reference in New Issue
Block a user