@miller-tech/uap 1.220.1 → 1.220.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.220.1",
3
+ "version": "1.220.2",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -122,13 +122,63 @@ def save_state(state: dict, root: Path | None = None) -> None:
122
122
  pass # evidence is best-effort; never wedge the hook that records it
123
123
 
124
124
 
125
+ _SIG_LINE_RE = re.compile(
126
+ r"(error\[E\d+\]|^error(?:\[|:)|panicked at|^\s*FAIL\b|test result: FAILED|AssertionError|Traceback|"
127
+ r"\bfailed\b.*\bassert|^E\s{2,}|error TS\d+|\bERROR\b)",
128
+ re.IGNORECASE | re.MULTILINE,
129
+ )
130
+
131
+
132
+ def failure_signature(detail: str) -> str:
133
+ """A stable fingerprint of WHAT failed, so two red runs can be told apart.
134
+
135
+ The first diagnostic line (rustc error code, panic site, failing test
136
+ name, tsc code, traceback) with volatile bits — thread ids, pids,
137
+ timestamps, hex addresses, durations — stripped. Falls back to the first
138
+ non-empty line. Same signature twice = the same wall; a different one =
139
+ the agent moved the problem, which is progress, not a streak."""
140
+ text = detail or ""
141
+ line = ""
142
+ for raw in text.splitlines():
143
+ if _SIG_LINE_RE.search(raw):
144
+ line = raw
145
+ break
146
+ if not line:
147
+ for raw in text.splitlines():
148
+ if raw.strip():
149
+ line = raw
150
+ break
151
+ line = re.sub(r"\(\d+\)", "", line) # thread/pid ids
152
+ line = re.sub(r"0x[0-9a-fA-F]+", "0x", line) # addresses
153
+ line = re.sub(r"\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}\S*", "", line)
154
+ line = re.sub(r"\b\d+(?:\.\d+)?\s*(?:ms|s|secs?|seconds?)\b", "", line)
155
+ line = re.sub(r"\s+", " ", line).strip()
156
+ return line[:200]
157
+
158
+
125
159
  def record_fail(source: str, detail: str, root: Path | None = None) -> dict:
160
+ """Count a red verification toward the escalation threshold.
161
+
162
+ CONSECUTIVE means "the same failure keeps coming back". A red run whose
163
+ signature differs from the previous one is the agent making progress on a
164
+ different problem (observed live: overflow at hash.rs:130 after fixing a
165
+ pgrx-gated test, both counted, a one-line fix escalated to a 75-minute
166
+ deliver loop that did not land). Such a failure restarts the streak at 1
167
+ instead of extending it."""
126
168
  st = load_state(root)
127
- st["failures"] = int(st.get("failures") or 0) + 1
169
+ sig = failure_signature(detail)
170
+ prev = st.get("last_failure") if isinstance(st.get("last_failure"), dict) else None
171
+ prev_sig = (prev or {}).get("sig")
172
+ if prev_sig and sig and sig != prev_sig:
173
+ st["failures"] = 1
174
+ st["streak_reset"] = {"ts": int(time.time()), "from": prev_sig[:120], "to": sig[:120]}
175
+ else:
176
+ st["failures"] = int(st.get("failures") or 0) + 1
128
177
  st["last_failure"] = {
129
178
  "ts": int(time.time()),
130
179
  "source": source or "unknown",
131
180
  "detail": (detail or "")[-DETAIL_MAX:],
181
+ "sig": sig,
132
182
  }
133
183
  save_state(st, root)
134
184
  return st