From f3cbe4560507b46657bc2858b8d7d8c64c343bbd Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 16 Jul 2026 06:40:36 -0700 Subject: [PATCH] refactor(kanban): unify attachment size cap on KANBAN_ATTACHMENT_MAX_BYTES The salvaged attachment-toolset commit predated main centralizing the 25 MB cap as kanban_db.KANBAN_ATTACHMENT_MAX_BYTES and re-introduced a private _MAX_ATTACHMENT_BYTES alias. Drop the duplicate: kanban_db's store_attachment_bytes(), the dashboard upload endpoint, and the kanban_attach_url tool all reference the one shared constant now, and the tests monkeypatch that same name. --- hermes_cli/kanban_db.py | 11 +++++------ plugins/kanban/dashboard/plugin_api.py | 6 +++--- tests/tools/test_kanban_tools.py | 4 ++-- tools/kanban_tools.py | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/hermes_cli/kanban_db.py b/hermes_cli/kanban_db.py index 979f6fdc0a1..de332f36ee4 100644 --- a/hermes_cli/kanban_db.py +++ b/hermes_cli/kanban_db.py @@ -2963,11 +2963,10 @@ def list_comments(conn: sqlite3.Connection, task_id: str) -> list[Comment]: # Attachments # --------------------------------------------------------------------------- -# Cap a single attachment so a runaway upload can't fill the disk. 25 MB -# comfortably covers PDFs, images, and source docs — the kanban use case. -# Shared by the dashboard HTTP endpoint, the agent toolset, and the CLI so -# the limit cannot drift between surfaces. -_MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024 +# The attachment size cap is the module-level ``KANBAN_ATTACHMENT_MAX_BYTES`` +# (defined near the top of this file) — one constant shared by the dashboard +# HTTP endpoint, the agent toolset, and the CLI so the limit cannot drift +# between surfaces. class AttachmentTooLarge(ValueError): @@ -3045,7 +3044,7 @@ def store_attachment_bytes( is removed before re-raising. """ if max_bytes is None: - max_bytes = _MAX_ATTACHMENT_BYTES + max_bytes = KANBAN_ATTACHMENT_MAX_BYTES if len(data) > max_bytes: raise AttachmentTooLarge( f"attachment exceeds {max_bytes // (1024 * 1024)} MB limit" diff --git a/plugins/kanban/dashboard/plugin_api.py b/plugins/kanban/dashboard/plugin_api.py index a66567556e9..1b85b6fea47 100644 --- a/plugins/kanban/dashboard/plugin_api.py +++ b/plugins/kanban/dashboard/plugin_api.py @@ -666,7 +666,7 @@ def create_task(payload: CreateTaskBody, board: Optional[str] = Query(None)): # ``ValueError`` there; the upload handler's ``except ValueError`` below maps # it to a 400, preserving the previous response. from hermes_cli.kanban_db import ( # noqa: E402 - _MAX_ATTACHMENT_BYTES, + KANBAN_ATTACHMENT_MAX_BYTES, _collision_free_path, _safe_attachment_name, ) @@ -726,13 +726,13 @@ async def upload_task_attachment( if not chunk: break total += len(chunk) - if total > _MAX_ATTACHMENT_BYTES: + if total > KANBAN_ATTACHMENT_MAX_BYTES: out.close() dest_path.unlink(missing_ok=True) raise HTTPException( status_code=413, detail=( - f"attachment exceeds {_MAX_ATTACHMENT_BYTES // (1024 * 1024)} MB limit" + f"attachment exceeds {KANBAN_ATTACHMENT_MAX_BYTES // (1024 * 1024)} MB limit" ), ) out.write(chunk) diff --git a/tests/tools/test_kanban_tools.py b/tests/tools/test_kanban_tools.py index 42bfc5e5bd0..0ebce7af640 100644 --- a/tests/tools/test_kanban_tools.py +++ b/tests/tools/test_kanban_tools.py @@ -2302,7 +2302,7 @@ def test_attach_rejects_oversize(worker_env, monkeypatch): from tools import kanban_tools as kt # Shrink the cap so we don't have to build a 25 MB payload. - monkeypatch.setattr(kb, "_MAX_ATTACHMENT_BYTES", 8) + monkeypatch.setattr(kb, "KANBAN_ATTACHMENT_MAX_BYTES", 8) out = kt._handle_attach({ "filename": "big.bin", "content_base64": base64.b64encode(b"0123456789").decode(), @@ -2451,7 +2451,7 @@ def test_attach_url_rejects_oversize_stream(worker_env, monkeypatch): def log_message(self, *a): pass - monkeypatch.setattr(kb, "_MAX_ATTACHMENT_BYTES", 1024) + monkeypatch.setattr(kb, "KANBAN_ATTACHMENT_MAX_BYTES", 1024) srv = http.server.HTTPServer(("127.0.0.1", 0), _Handler) threading.Thread(target=srv.serve_forever, daemon=True).start() try: diff --git a/tools/kanban_tools.py b/tools/kanban_tools.py index 373748f55be..d79691e33eb 100644 --- a/tools/kanban_tools.py +++ b/tools/kanban_tools.py @@ -956,7 +956,7 @@ def _handle_attach_url(args: dict, **kw) -> str: content_type = args.get("content_type") board = args.get("board") try: - data, fetched_ct = _download_url_with_cap(url, kb._MAX_ATTACHMENT_BYTES) + data, fetched_ct = _download_url_with_cap(url, kb.KANBAN_ATTACHMENT_MAX_BYTES) except ValueError as e: return tool_error(f"kanban_attach_url: {e}") except Exception as e: