From da5039e50fb989de63df374ce1813a7afceb30a8 Mon Sep 17 00:00:00 2001 From: Gram Ricko Date: Mon, 15 Jun 2026 16:09:27 +0000 Subject: [PATCH] fix: clamp importance to 1-5 and surface Mnemon CLI errors in tool response --- mnemon/__init__.py | 12 ++++++++++-- tests/test_mnemon.py | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/mnemon/__init__.py b/mnemon/__init__.py index a500719..10be3f4 100644 --- a/mnemon/__init__.py +++ b/mnemon/__init__.py @@ -96,6 +96,7 @@ def __init__(self): self._recall_cache: list | None = None self._last_prefetch_at: float = 0.0 self._prefetch_ttl: int = 30 + self._last_remember_error: str | None = None INTENT_MAP = { "why": "WHY", "because": "WHY", @@ -251,15 +252,19 @@ def _remember(self, text: str, category: str = "general", entities: list[str] | None = None, tags: list[str] | None = None, source: str = "agent") -> str | None: + importance = max(1, min(5, importance)) args = ["remember", text, "--cat", category, "--imp", str(importance), "--source", source] if entities: args += ["--entities", ",".join(entities)] if tags: args += ["--tags", ",".join(tags)] - code, stdout, _ = _run_mnemon(args, timeout=15) + code, stdout, stderr = _run_mnemon(args, timeout=15) if code != 0: + logger.warning("mnemon remember failed (rc=%d): %s", code, stderr.strip()[:200]) + self._last_remember_error = stderr.strip() return None + self._last_remember_error = None return _json_output(stdout).get("id") def _remember_and_index(self, text: str, **kwargs) -> str | None: @@ -413,7 +418,10 @@ def handle_tool_call(self, tool_name: str, args: dict, **kwargs) -> str: tags=args.get("tags", []), source=args.get("source", "agent"), ) - return json.dumps({"success": bool(iid), "id": iid or "error"}) + if not iid: + err = getattr(self, "_last_remember_error", "") or "unknown error" + return json.dumps({"success": False, "id": "error", "error": err}) + return json.dumps({"success": True, "id": iid}) if tool_name == "mnemon_recall": q = args.get("query", "").strip() diff --git a/tests/test_mnemon.py b/tests/test_mnemon.py index b1a40d7..b76d775 100644 --- a/tests/test_mnemon.py +++ b/tests/test_mnemon.py @@ -246,6 +246,32 @@ def test_remember_empty_text(self): data = json.loads(result) self.assertIn("error", data) + @patch("mnemon._run_mnemon") + def test_remember_error_reports_stderr(self, mock_run): + mock_run.return_value = (1, "", "content too long (10000 chars, max 8000)") + idx_path = Path("/tmp/test_mnemon_index_%d.json" % id(self)) + with patch.object(self.p, "_index_path", idx_path): + result = self.p.handle_tool_call("mnemon_remember", {"text": "foo"}) + data = json.loads(result) + self.assertFalse(data["success"]) + self.assertEqual(data["id"], "error") + self.assertEqual(data["error"], "content too long (10000 chars, max 8000)") + + @patch("mnemon._run_mnemon") + def test_remember_clamping(self, mock_run): + mock_run.return_value = (0, MNEMON_OK, "") + idx_path = Path("/tmp/test_mnemon_index_%d.json" % id(self)) + with patch.object(self.p, "_index_path", idx_path): + if idx_path.exists(): + try: + idx_path.unlink() + except Exception: + pass + self.p.handle_tool_call("mnemon_remember", {"text": "foo", "importance": 8}) + called_args = mock_run.call_args[0][0] + self.assertIn("--imp", called_args) + self.assertEqual(called_args[called_args.index("--imp") + 1], "5") + @patch("mnemon._run_mnemon") def test_recall_returns_hits(self, mock_run): mock_run.return_value = (0, RECALL_OK, "")