@miller-tech/uap 1.220.1 → 1.220.3

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.3",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -386,6 +386,7 @@ def _record_escalation_edit(root: Path, rel: str) -> None:
386
386
  edits = st.get("edits_since_green") if isinstance(st.get("edits_since_green"), dict) else {}
387
387
  edits[rel] = int(edits.get(rel, 0)) + 1
388
388
  st["edits_since_green"] = edits
389
+ st["edits_total"] = int(st.get("edits_total") or 0) + 1 # attempt counter (tracker reads it)
389
390
  st.setdefault("failures", 0)
390
391
  st["updated"] = int(_time.time())
391
392
  try:
@@ -122,13 +122,74 @@ 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
+ edits_total = int(st.get("edits_total") or 0)
173
+ _ep = (prev or {}).get("edits_total")
174
+ edits_at_prev = int(_ep) if isinstance(_ep, (int, float)) else -1 # 0 is a real value
175
+ if prev_sig and sig and sig != prev_sig:
176
+ st["failures"] = 1
177
+ st["streak_reset"] = {"ts": int(time.time()), "from": prev_sig[:120], "to": sig[:120]}
178
+ elif prev_sig and sig == prev_sig and edits_total == edits_at_prev:
179
+ # The SAME failure observed again with no edit in between (idle/stop
180
+ # verify hooks re-run the gates every time the session pauses). That is
181
+ # a re-observation, not another failed attempt — the streak must not
182
+ # grow while the agent has not tried anything. Observed live: 4 "failures"
183
+ # from one red user-path test across four idle pauses.
184
+ st["reobserved"] = int(st.get("reobserved") or 0) + 1
185
+ else:
186
+ st["failures"] = int(st.get("failures") or 0) + 1
128
187
  st["last_failure"] = {
129
188
  "ts": int(time.time()),
130
189
  "source": source or "unknown",
131
190
  "detail": (detail or "")[-DETAIL_MAX:],
191
+ "sig": sig,
192
+ "edits_total": edits_total,
132
193
  }
133
194
  save_state(st, root)
134
195
  return st
@@ -149,6 +210,7 @@ def record_edit(rel: str, root: Path | None = None) -> dict:
149
210
  edits = st.get("edits_since_green") or {}
150
211
  edits[rel] = int(edits.get(rel, 0)) + 1
151
212
  st["edits_since_green"] = edits
213
+ st["edits_total"] = int(st.get("edits_total") or 0) + 1 # monotonic: "an attempt happened"
152
214
  save_state(st, root)
153
215
  return st
154
216