#!/usr/bin/env python3
"""ACE-owned Hermes bridge worker.

The worker reads one JSON request from stdin and writes newline-delimited
bridge events to stdout. All Hermes stdout chatter is redirected to stderr so
ACE never has to guess whether an unframed line is assistant text or authority.
"""

from __future__ import annotations

import contextlib
import json
import os
import sys
import traceback
from pathlib import Path
from typing import Any


ORIGINAL_STDOUT = sys.stdout


def emit(event: dict[str, Any]) -> None:
    ORIGINAL_STDOUT.write(json.dumps(event, ensure_ascii=False, separators=(",", ":")) + "\n")
    ORIGINAL_STDOUT.flush()


def load_request() -> dict[str, Any]:
    raw = sys.stdin.read()
    if not raw.strip():
        raise ValueError("empty bridge request")
    parsed = json.loads(raw)
    if not isinstance(parsed, dict):
        raise ValueError("bridge request must be a JSON object")
    return parsed


def provider_for_hermes(provider: str) -> str:
    if provider in {"llama.cpp", "ollama"}:
        return "openai"
    return provider


def base_url_for_hermes(provider: str, base_url: Any) -> str:
    raw = str(base_url or "").rstrip("/")
    if provider == "ollama" and raw and not raw.endswith("/v1"):
        return f"{raw}/v1"
    return raw


def main() -> int:
    try:
        request = load_request()
        hermes_root = Path(request["hermes_root"]).resolve()
        sys.path.insert(0, str(hermes_root))

        session_id = str(request["session_id"])
        turn_id = str(request.get("turn_id") or session_id)
        emit({"type": "session_open", "session_id": session_id, "turn_id": turn_id})

        with contextlib.redirect_stdout(sys.stderr):
            from run_agent import AIAgent  # type: ignore

            def status_callback(kind: str, message: str) -> None:
                emit({"type": "status" if kind == "lifecycle" else "status", "session_id": session_id, "turn_id": turn_id, "kind": kind, "message": str(message)})

            def delta_callback(text: str) -> None:
                emit({"type": "delta", "session_id": session_id, "turn_id": turn_id, "text": str(text)})

            def reasoning_callback(text: str) -> None:
                emit({"type": "reasoning", "session_id": session_id, "turn_id": turn_id, "text": str(text)})

            def interim_callback(text: str, **kwargs: Any) -> None:
                emit({"type": "assistant_interim", "session_id": session_id, "turn_id": turn_id, "text": str(text), **kwargs})

            def thinking_callback(text: str) -> None:
                emit({"type": "reasoning", "session_id": session_id, "turn_id": turn_id, "text": str(text)})

            def tool_start_callback(tool_name: str, *args: Any, **kwargs: Any) -> None:
                emit({"type": "tool_start", "session_id": session_id, "turn_id": turn_id, "tool": str(tool_name), "input": kwargs.get("args") or (args[0] if args else {})})

            def tool_progress_callback(tool_name: str, message: str = "", *args: Any, **kwargs: Any) -> None:
                emit({"type": "tool_progress", "session_id": session_id, "turn_id": turn_id, "tool": str(tool_name), "message": str(message), **kwargs})

            def tool_complete_callback(tool_name: str, result: Any = None, *args: Any, **kwargs: Any) -> None:
                emit({"type": "tool_complete", "session_id": session_id, "turn_id": turn_id, "tool": str(tool_name), "result": result, **kwargs})

            agent = AIAgent(
                base_url=base_url_for_hermes(str(request.get("provider") or ""), request.get("base_url")),
                api_key=str(request.get("api_key") or "no-key-required"),
                provider=provider_for_hermes(str(request.get("provider") or "")),
                model=str(request.get("model") or ""),
                max_iterations=int(request.get("max_turns") or 6),
                enabled_toolsets=["mcp-ace_shadow"],
                disabled_toolsets=["terminal", "filesystem", "web", "vision", "creative", "reasoning"],
                quiet_mode=True,
                verbose_logging=False,
                ephemeral_system_prompt=str(request.get("system_prompt") or ""),
                session_id=session_id,
                platform="ace",
                skip_context_files=True,
                skip_memory=True,
                status_callback=status_callback,
                stream_delta_callback=delta_callback,
                reasoning_callback=reasoning_callback,
                thinking_callback=thinking_callback,
                interim_assistant_callback=interim_callback,
                tool_start_callback=tool_start_callback,
                tool_progress_callback=tool_progress_callback,
                tool_complete_callback=tool_complete_callback,
            )
            emit({"type": "turn_run", "session_id": session_id, "turn_id": turn_id})
            result = agent.run_conversation(str(request.get("task") or ""), task_id=turn_id)

        final = result.get("final_response") if isinstance(result, dict) else str(result)
        emit({
            "type": "final",
            "session_id": session_id,
            "turn_id": turn_id,
            "text": final or "",
            "completed": bool(result.get("completed", True)) if isinstance(result, dict) else True,
            "turns": int(result.get("api_calls", 1) or 1) if isinstance(result, dict) else 1,
        })
        emit({"type": "session_close", "session_id": session_id, "turn_id": turn_id})
        return 0
    except Exception as exc:
        emit({
            "type": "error",
            "message": str(exc),
            "traceback": traceback.format_exc(limit=8),
        })
        return 1


if __name__ == "__main__":
    raise SystemExit(main())
