hermes: bound publication titles by UTF-8 bytes
This commit is contained in:
parent
3231e1af07
commit
cac4c206cd
@ -93,6 +93,13 @@ def payload_digest(payload: Any) -> str:
|
||||
return hashlib.sha256(canonical_json(payload)).hexdigest()
|
||||
|
||||
|
||||
def truncate_utf8(value: str, maximum: int) -> str:
|
||||
"""Bound display text by UTF-8 bytes without splitting a character."""
|
||||
if maximum < 0:
|
||||
raise ProtocolError("text byte limit is invalid")
|
||||
return value.encode("utf-8", "replace")[:maximum].decode("utf-8", "ignore")
|
||||
|
||||
|
||||
def derive_ordinal_key(master: bytes, ordinal: int) -> bytes:
|
||||
"""Derive one cryptographically isolated worker authority from the pool root."""
|
||||
if ordinal not in range(3) or not 32 <= len(master) <= 4096:
|
||||
|
||||
@ -19,6 +19,7 @@ from execution_pool_protocol import (
|
||||
ProtocolError,
|
||||
atomic_json,
|
||||
canonical_json,
|
||||
truncate_utf8,
|
||||
)
|
||||
ROOT = Path(os.environ.get("HERMES_WORKER_ROOT", "/workspace"))
|
||||
ORDINAL = int(os.environ.get("HERMES_WORKER_ORDINAL", "-1"))
|
||||
@ -380,7 +381,7 @@ def execute(assignment: dict[str, Any]) -> None:
|
||||
"finish",
|
||||
binding=binding,
|
||||
payload=result_payload,
|
||||
title=str(structured.get("summary") or f"Hermes task {binding['task_id']}")[:240],
|
||||
title=truncate_utf8(str(structured.get("summary") or f"Hermes task {binding['task_id']}"), 512),
|
||||
body=json.dumps(structured, indent=2, sort_keys=True)[:12000],
|
||||
)
|
||||
if not response.get("ack", {}).get("accepted"):
|
||||
|
||||
@ -12,7 +12,7 @@ import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from execution_pool_protocol import ProtocolError, canonical_json, read_key
|
||||
from execution_pool_protocol import ProtocolError, canonical_json, read_key, truncate_utf8
|
||||
from execution_pool_scm import Boundary
|
||||
|
||||
ROOT = Path(os.environ.get("HERMES_WORKER_ROOT", "/workspace"))
|
||||
@ -88,7 +88,7 @@ def bootstrap(key: bytes, raw_assignment: Any) -> dict[str, Any]:
|
||||
boundary = Boundary(key)
|
||||
assignment = boundary.verify(raw_assignment)
|
||||
structured = _completed_result(assignment)
|
||||
title = structured["summary"].strip()[:512]
|
||||
title = truncate_utf8(structured["summary"].strip(), 512)
|
||||
body = json.dumps(structured, indent=2, sort_keys=True)
|
||||
return boundary.resume_artifact(assignment, {"title": title, "body": body}, structured)
|
||||
|
||||
|
||||
@ -459,6 +459,30 @@ def test_mediator_bootstrap_recovers_one_unique_nested_session_result(tmp_path,
|
||||
assert resume_bootstrap._completed_result(exact) == completed
|
||||
|
||||
|
||||
def test_mediator_bootstrap_truncates_multibyte_title_at_the_byte_limit(tmp_path, monkeypatch):
|
||||
"""Receipt title limits count UTF-8 bytes while preserving full evidence."""
|
||||
exact = protocol.sign_envelope(
|
||||
KEY, "assignment", binding(), payload(root_task_id="t_deadbeef", continuation_kind="repair")
|
||||
)
|
||||
log = tmp_path / "session-state" / "metis" / "t_deadbeef" / "42.log"
|
||||
log.parent.mkdir(parents=True)
|
||||
completed = {**RESULT, "summary": "界" * 200}
|
||||
log.write_bytes(protocol.canonical_json({"structured_output": completed}) + b"\n")
|
||||
monkeypatch.setattr(resume_bootstrap, "ROOT", tmp_path)
|
||||
seen = {}
|
||||
monkeypatch.setattr(
|
||||
scm.Boundary, "resume_artifact",
|
||||
lambda _self, _assignment, request, structured: seen.update(
|
||||
request=request, structured=structured
|
||||
) or {},
|
||||
)
|
||||
|
||||
resume_bootstrap.bootstrap(KEY, exact)
|
||||
|
||||
assert len(seen["request"]["title"].encode()) <= 512
|
||||
assert seen["structured"]["summary"] == completed["summary"]
|
||||
|
||||
|
||||
def test_mediator_bootstrap_rejects_a_symlinked_log_ancestor(tmp_path, monkeypatch):
|
||||
exact = protocol.sign_envelope(KEY, "assignment", binding(), payload())
|
||||
outside = tmp_path / "outside"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user