@@ -7955,7 +7955,7 @@ def setup_hook(dry_run=False):
79557955
79567956# ========== Persistent Dashboard Daemon ==========
79577957
7958- TOKEN_OPTIMIZER_VERSION = "5.6.3 " # Keep in sync with plugin.json + marketplace.json
7958+ TOKEN_OPTIMIZER_VERSION = "5.6.4 " # Keep in sync with plugin.json + marketplace.json
79597959DAEMON_LABEL = "com.token-optimizer.dashboard"
79607960DAEMON_PORT = 24842 # Memorable: 2-4-8-4-2 (powers of 2 palindrome), avoids common ports
79617961LAUNCH_AGENTS_DIR = Path.home() / "Library" / "LaunchAgents"
@@ -10052,6 +10052,8 @@ def _parse_jsonl_for_quality(filepath):
1005210052 reads = [] # (index, path, timestamp)
1005310053 writes = [] # (index, path, timestamp)
1005410054 tool_results = [] # (index, tool_name, result_size_chars, referenced_later)
10055+ tool_result_meta = [] # richer metadata for live detectors
10056+ tool_name_by_id = {}
1005510057 system_reminders = [] # (index, content_hash, size_chars)
1005610058 messages = [] # (index, role, text_length, is_substantive)
1005710059 compactions = 0
@@ -10091,6 +10093,8 @@ def _parse_jsonl_for_quality(filepath):
1009110093 reads = []
1009210094 writes = []
1009310095 tool_results = []
10096+ tool_result_meta = []
10097+ tool_name_by_id = {}
1009410098 system_reminders = []
1009510099 messages = []
1009610100 agent_dispatches = []
@@ -10136,6 +10140,9 @@ def _parse_jsonl_for_quality(filepath):
1013610140 elif block.get("type") == "tool_use":
1013710141 is_substantive = True # tool invocations ARE decisions
1013810142 tool_name = block.get("name", "")
10143+ tool_id = block.get("id", "")
10144+ if tool_id:
10145+ tool_name_by_id[tool_id] = tool_name
1013910146 inp = block.get("input", {})
1014010147
1014110148 if tool_name == "Read":
@@ -10166,6 +10173,13 @@ def _parse_jsonl_for_quality(filepath):
1016610173 result_text = _extract_tool_result_text(block)
1016710174 tool_id = block.get("tool_use_id", "")
1016810175 tool_results.append((idx, tool_id, len(result_text), False))
10176+ tool_result_meta.append({
10177+ "index": idx,
10178+ "tool_id": tool_id,
10179+ "tool_name": tool_name_by_id.get(tool_id, ""),
10180+ "size": len(result_text),
10181+ "is_failure": _tool_result_looks_failed(block, result_text),
10182+ })
1016910183
1017010184 # Update agent dispatch result sizes
1017110185 if agent_dispatches and agent_dispatches[-1][2] == 0:
@@ -10184,6 +10198,7 @@ def _parse_jsonl_for_quality(filepath):
1018410198 "reads": reads,
1018510199 "writes": writes,
1018610200 "tool_results": tool_results,
10201+ "tool_result_meta": tool_result_meta,
1018710202 "system_reminders": system_reminders,
1018810203 "messages": messages,
1018910204 "compactions": compactions,
@@ -10746,6 +10761,55 @@ def _extract_tool_result_text(block):
1074610761 return str(rc)
1074710762
1074810763
10764+ _TOOL_FAILURE_RE = re.compile(
10765+ r"("
10766+ r"\btraceback\b|"
10767+ r"\bexception\b|"
10768+ r"\bfailed\b|"
10769+ r"\bfailure\b|"
10770+ r"\bfatal:|"
10771+ r"\berror:|"
10772+ r"\bpermission denied\b|"
10773+ r"\bno such file or directory\b|"
10774+ r"\bcommand not found\b|"
10775+ r"\bexit (?:code|status) [2-9]\d*\b|"
10776+ r"\bexited with code [2-9]\d*\b|"
10777+ r"\breturned non-zero\b|"
10778+ r"\breturncode [2-9]\d*\b|"
10779+ r"\btimed out\b|"
10780+ r"\bsyntaxerror\b|"
10781+ r"\btypeerror\b|"
10782+ r"\bvalueerror\b|"
10783+ r"\bassertionerror\b|"
10784+ r"\bnpm err!|"
10785+ r"\btests? failed\b"
10786+ r")",
10787+ re.IGNORECASE,
10788+ )
10789+
10790+
10791+ _TOOL_SUCCESS_COUNT_RE = re.compile(
10792+ r"\b(?:0 failed|0 failures|0 errors|no failures|no errors)\b",
10793+ re.IGNORECASE,
10794+ )
10795+ _TOOL_NONZERO_FAILURE_COUNT_RE = re.compile(
10796+ r"\b[1-9]\d*\s+(?:failed|failures|errors)\b",
10797+ re.IGNORECASE,
10798+ )
10799+
10800+
10801+ def _tool_result_looks_failed(block, result_text):
10802+ """Return True only for result blocks that carry a concrete failure signal."""
10803+ if block.get("is_error") is True:
10804+ return True
10805+ text = (result_text or "").strip()
10806+ if not text:
10807+ return False
10808+ if _TOOL_SUCCESS_COUNT_RE.search(text) and not _TOOL_NONZERO_FAILURE_COUNT_RE.search(text):
10809+ return False
10810+ return bool(_TOOL_FAILURE_RE.search(text[:2000]))
10811+
10812+
1074910813def _resolve_jsonl_path(arg=None):
1075010814 """Resolve a JSONL file path from a session ID, file path, or auto-detect.
1075110815
@@ -14418,20 +14482,28 @@ def _check_realtime_loops(quality_data):
1441814482 })
1441914483
1442014484 # --- Retry churn detection ---
14421- tool_results = quality_data.get("tool_results", [])
14422- if len(tool_results) >= 3:
14423- recent_tools = tool_results[-5:]
14424- # tool_results entries are: (index, tool_id, result_size_chars, referenced_later)
14425- # Check for repeated small results (errors tend to be short)
14426- small_results = [t for t in recent_tools if t[2] < 200] # short results = likely errors
14427- if len(small_results) >= 3:
14428- # If 3+ of the last 5 tool results are very short, might be error loop
14429- sizes = [t[2] for t in small_results]
14430- if all(abs(s - sizes[0]) < 50 for s in sizes):
14485+ # Short tool results are common for successful operations ("done",
14486+ # empty search results, concise shell output). Only warn when recent
14487+ # short results also carry concrete failure signals and come from
14488+ # the same tool family.
14489+ tool_result_meta = quality_data.get("tool_result_meta", [])
14490+ if len(tool_result_meta) >= 3:
14491+ recent_tools = tool_result_meta[-5:]
14492+ short_failures = [
14493+ t for t in recent_tools
14494+ if t.get("is_failure") and t.get("size", 0) < 400
14495+ ]
14496+ if len(short_failures) >= 3:
14497+ by_tool = {}
14498+ for item in short_failures:
14499+ tool_name = item.get("tool_name") or "unknown"
14500+ by_tool[tool_name] = by_tool.get(tool_name, 0) + 1
14501+ most_repeated = max(by_tool.values()) if by_tool else 0
14502+ if most_repeated >= 3:
1443114503 warnings.append({
1443214504 "type": "retry_churn",
14433- "confidence": 0.6 ,
14434- "count": len(small_results) ,
14505+ "confidence": 0.75 ,
14506+ "count": most_repeated ,
1443514507 })
1443614508
1443714509 except Exception:
0 commit comments