"""Fail-open post-tool emitters: artifacts from real writes, sources from real web output. HUX-04 requires agent-created files to appear in the artifact workspace automatically; HUX-08 requires real web tool output to leave source records behind. Both emitters run strictly after a successful tool execution, never raise into the tool path, never send secrets beyond the written content itself (the service applies its own secret scrubbing and forces ``restricted`` sensitivity on hits), and stay bounded. """ from __future__ import annotations import base64 import hashlib import re from pathlib import Path from typing import Any MAX_ARTIFACT_BYTES = 1024 * 1024 MAX_RESULT_SCAN_BYTES = 64 * 1024 MAX_SOURCES_PER_CALL = 3 PATH_KEYS = ("path", "file_path", "filename", "target_path", "output_path") URL = re.compile(r"""https?://[^\s"'<>)\]]{8,500}""") TYPE_BY_SUFFIX = { ".md": "markdown", ".markdown": "markdown", ".html": "html", ".htm": "html", ".svg": "svg", ".json": "json", ".csv": "csv", ".png": "image", ".jpg": "image", ".jpeg": "image", ".gif": "image", ".webp": "image", ".pdf": "document", ".txt": "document", ".rst": "document", ".wav": "audio", ".mp3": "audio", } MIME_BY_TYPE = { "markdown": "text/markdown", "code": "text/plain", "html": "text/html", "svg": "image/svg+xml", "image": "application/octet-stream", "json": "application/json", "csv": "text/csv", "document": "text/plain", "audio": "application/octet-stream", } def _written_path(args: Any) -> Path | None: """The one plausible filesystem target of a write tool, or None.""" if not isinstance(args, dict): return None for key in PATH_KEYS: value = args.get(key) if isinstance(value, str) and value and len(value) < 4096: path = Path(value) try: if path.is_file() and not path.is_symlink(): return path except OSError: return None return None def _artifact_body(path: Path, data: bytes, conversation_id: str, project_id: str) -> dict[str, Any]: artifact_type = TYPE_BY_SUFFIX.get(path.suffix.lower(), "code") body: dict[str, Any] = { "type": artifact_type, "title": path.name[-200:], "mime": MIME_BY_TYPE[artifact_type], "conversation_id": conversation_id, "project_id": project_id, } try: body["content"] = data.decode("utf-8", errors="strict") except UnicodeDecodeError: body["content_base64"] = base64.b64encode(data).decode("ascii") return body def record_artifact(client: Any, call: Any, args: Any) -> str | None: """Create or version one artifact for a freshly written file; never raises.""" try: path = _written_path(args) if path is None or path.stat().st_size > MAX_ARTIFACT_BYTES: return None data = path.read_bytes() digest = hashlib.sha256(data).hexdigest()[:32] body = _artifact_body(path, data, call.conversation_id, call.project_id) listing = client.get("/hux/v1/artifacts", {"conversation_id": call.conversation_id, "cursor": "0"}) items = listing.body.get("items", []) if isinstance(listing.body, dict) else [] existing = next( (item for item in items if isinstance(item, dict) and item.get("title") == body["title"]), None, ) if existing is None: created = client.post( "/hux/v1/artifacts", body, idempotency_key=f"artifact-sync:new:{digest}"[:120], ) return created.body.get("id") if isinstance(created.body, dict) else None version = {key: body[key] for key in ("mime",) if key in body} version.update({k: v for k, v in body.items() if k.startswith("content")}) version["diff_from"] = existing.get("current_version") client.request( "POST", f"/hux/v1/artifacts/{existing['id']}/versions", version, idempotency_key=f"artifact-sync:{existing['id']}:{digest}"[:120], if_match=existing.get("revision"), ) return existing.get("id") except Exception: return None def record_sources(client: Any, result: Any) -> int: """Record up to three deduplicated web sources found in real tool output.""" try: if isinstance(result, bytes): text = result[:MAX_RESULT_SCAN_BYTES].decode("utf-8", errors="replace") elif isinstance(result, str): text = result[:MAX_RESULT_SCAN_BYTES] else: return 0 recorded = 0 seen: set[str] = set() for match in URL.finditer(text): url = match.group(0).rstrip(".,;") if url in seen: continue seen.add(url) digest = hashlib.sha256(url.encode()).hexdigest()[:32] client.post( "/hux/v1/sources", {"kind": "web", "title": url[:300], "uri": url[:2000], "classification": "unknown"}, idempotency_key=f"source-sync:{digest}"[:120], ) recorded += 1 if recorded >= MAX_SOURCES_PER_CALL: break return recorded except Exception: return 0 def sync_after_tool(client: Any, call: Any, capability: str, args: Any, result: Any) -> None: """Route one successful tool execution to its emitters; always returns.""" try: if capability == "write_files": record_artifact(client, call, args) elif capability in {"network", "web_search"}: record_sources(client, result) except Exception: return