@misterhuydo/sentinel 1.4.88 → 1.4.89

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/.cairn/.hint-lock CHANGED
@@ -1 +1 @@
1
- 2026-03-27T05:10:33.256Z
1
+ 2026-03-27T05:41:33.700Z
@@ -1,6 +1,6 @@
1
1
  {
2
- "message": "Auto-checkpoint at 2026-03-27T05:16:34.970Z",
3
- "checkpoint_at": "2026-03-27T05:16:34.971Z",
2
+ "message": "Auto-checkpoint at 2026-03-27T05:38:58.308Z",
3
+ "checkpoint_at": "2026-03-27T05:38:58.309Z",
4
4
  "active_files": [],
5
5
  "notes": [],
6
6
  "mtime_snapshot": {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@misterhuydo/sentinel",
3
- "version": "1.4.88",
3
+ "version": "1.4.89",
4
4
  "description": "Sentinel — Autonomous DevOps Agent installer and manager",
5
5
  "bin": {
6
6
  "sentinel": "./bin/sentinel.js"
@@ -52,7 +52,9 @@ class IssueEvent:
52
52
 
53
53
  def __post_init__(self):
54
54
  if not self.fingerprint:
55
- raw = f"issue:{self.source}:{self.message[:200]}"
55
+ # Use target_repo + message (not filename) so retries of the same
56
+ # issue share the same fingerprint — enables cooldown dedup.
57
+ raw = f"issue:{self.target_repo}:{self.message[:200]}"
56
58
  self.fingerprint = hashlib.sha1(raw.encode()).hexdigest()[:16]
57
59
  if not self.submitter_user_id:
58
60
  import re as _re
@@ -1790,7 +1790,63 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
1790
1790
  source_file = candidates[0]
1791
1791
  content = source_file.read_text(encoding="utf-8", errors="replace")
1792
1792
 
1793
- # Re-submit as a fresh issue file (new name = new fingerprint = no cooldown block)
1793
+ # ── Guard: refuse if state_store shows this issue already fixed/pending ──
1794
+ # Compute the content-based fingerprint (same formula as IssueEvent.__post_init__)
1795
+ import hashlib as _hashlib
1796
+ _target_repo = ""
1797
+ _message = ""
1798
+ _body_lines = []
1799
+ _in_body = False
1800
+ _META_UPPER = ("TARGET_REPO:", "SUBMITTED_BY:", "SUBMITTED_AT:", "SUPPORT_URL:")
1801
+ for _line in content.splitlines():
1802
+ _s = _line.strip()
1803
+ if not _in_body:
1804
+ if _s.upper().startswith("TARGET_REPO"):
1805
+ _target_repo = _s.split(":", 1)[-1].split("=", 1)[-1].strip()
1806
+ elif any(_s.upper().startswith(_p) for _p in _META_UPPER) or not _s:
1807
+ pass
1808
+ else:
1809
+ _in_body = True
1810
+ _body_lines.append(_line)
1811
+ else:
1812
+ _body_lines.append(_line)
1813
+ _message = next((l.strip() for l in _body_lines if l.strip()), source_file.name)
1814
+ _fp_raw = f"issue:{_target_repo}:{_message[:200]}"
1815
+ _fp = _hashlib.sha1(_fp_raw.encode()).hexdigest()[:16]
1816
+
1817
+ if store:
1818
+ try:
1819
+ with store._conn() as _c:
1820
+ _row = _c.execute(
1821
+ "SELECT status, pr_url, commit_hash FROM fixes "
1822
+ "WHERE fingerprint=? ORDER BY timestamp DESC LIMIT 1",
1823
+ (_fp,),
1824
+ ).fetchone()
1825
+ if _row:
1826
+ _status = _row["status"]
1827
+ if _status in ("merged", "applied"):
1828
+ _commit = _row["commit_hash"] or ""
1829
+ return json.dumps({
1830
+ "error": (
1831
+ f"Already fixed — this issue was resolved "
1832
+ + (f"in commit `{_commit[:8]}`" if _commit else "successfully")
1833
+ + f". Status: `{_status}`. "
1834
+ f"If the problem recurred, describe it as a new issue."
1835
+ )
1836
+ })
1837
+ if _status == "pending":
1838
+ _pr = _row["pr_url"] or ""
1839
+ return json.dumps({
1840
+ "error": (
1841
+ f"There is already an open PR for this issue"
1842
+ + (f": {_pr}" if _pr else "")
1843
+ + ". Merge or close it before retrying."
1844
+ )
1845
+ })
1846
+ except Exception as _e:
1847
+ logger.debug("retry_issue: state_store guard failed (non-fatal): %s", _e)
1848
+
1849
+ # Re-submit as a fresh issue file
1794
1850
  issues_dir = project_dir / "issues"
1795
1851
  issues_dir.mkdir(exist_ok=True)
1796
1852
  fname = f"retry-{source_file.stem[-8:]}-{uuid.uuid4().hex[:6]}.txt"