import argparse
import hashlib
import json
import os
import socket
import sys
import time
import textwrap
import uuid
import webbrowser
from datetime import datetime, timezone
from contextlib import contextmanager
from importlib import metadata as importlib_metadata
from pathlib import Path
from urllib.parse import quote_plus

from hiveos.intelligence.tracing import build_trace_event, emit_trace, read_trace_events
from hiveos.intelligence import tracing as tracing_module
from hiveos import config as hive_config
from hiveos.tei import validate_tei_batch, validate_tei_event
from hiveos_trace import fallback_ui as _fallback_ui
from hiveos_trace.cli_parser import build_parser as _build_parser_impl
from hiveos_trace.help_verbose import print_verbose_help
from hiveos_trace import proxy as _proxy
from hiveos_trace import insight_ops as _insight_ops
from hiveos_trace import ops_lifecycle as _ops_lifecycle
from hiveos_trace import replay_ops as _replay_ops
from hiveos_trace import run_exec as _run_exec
from hiveos_trace import run_store as _run_store
from hiveos_trace import terminal_style as _term
from hiveos_trace import tei_cli as _tei_cli
from hiveos_trace import trace_views as _trace_views
from hiveos_trace.tei_ingest import (
    ingest_tei_event as _ingest_tei_event_impl,
    ingest_tei_events as _ingest_tei_events_impl,
)

TRACE_BUNDLE_VERSION = 1


def _now_ms():
    return int(time.time() * 1000)


def _shorten(value, limit=800):
    text = str(value or "")
    if len(text) <= limit:
        return text
    return text[: max(0, limit - 3)] + "..."


def _build_run_id(prefix="observe"):
    return f"{prefix}:{uuid.uuid4().hex[:12]}"


def _add_help_verbose_flag(parser):
    parser.add_argument(
        "--help-verbose",
        action="store_true",
        help="Show verbose usage notes and examples for this command.",
    )


def _hive_version():
    for name in ("hiveos-trace", "hiveos"):
        try:
            return str(importlib_metadata.version(name))
        except importlib_metadata.PackageNotFoundError:
            continue
    return "dev"


def _active_trace_log_path():
    # Resolve to an absolute path so `trace show` works even when cwd changes.
    return str(Path(str(tracing_module.TRACE_LOG_PATH or "trace_events.log")).expanduser().resolve())


def _trace_home():
    configured = str(os.environ.get("HIVE_TRACE_HOME") or "").strip()
    if configured:
        return Path(configured)
    return Path.home() / ".hiveos-trace"


def _runs_dir():
    return _run_store.runs_dir(trace_home=_trace_home)


def _run_file(run_id):
    return _run_store.run_file(run_id, trace_home=_trace_home)


def _persist_run_record(record):
    return _run_store.persist_run_record(record, trace_home=_trace_home)


def _touch_run_record(run_id, **fields):
    return _run_store.touch_run_record(
        run_id,
        trace_home=_trace_home,
        now_ms=_now_ms,
        read_record=_read_run_record,
        persist_record=_persist_run_record,
        **fields,
    )


def _read_run_record(run_id):
    return _run_store.read_run_record(run_id, trace_home=_trace_home)


def _list_run_records(limit=100):
    return _run_store.list_run_records(trace_home=_trace_home, limit=limit)


def _list_all_run_records():
    return _run_store.list_all_run_records(trace_home=_trace_home)


def _record_is_archived(record):
    return bool(record.get("archived"))


def _record_effective_status(record):
    if _record_is_archived(record):
        return "archived"
    return str(record.get("status") or "unknown")


def _record_last_activity_ms(record, *, prefer_output=False):
    if prefer_output:
        return int(record.get("last_output_ms") or record.get("last_update_ms") or record.get("started_at_ms") or 0)
    return int(record.get("last_update_ms") or record.get("finished_at_ms") or record.get("started_at_ms") or 0)


def _record_runtime_state(record, *, now_ms=None, idle_after_ms=30000):
    status = _record_effective_status(record)
    if status != "running":
        return status
    now = int(now_ms or _now_ms())
    last_activity_ms = _record_last_activity_ms(record, prefer_output=True)
    if last_activity_ms <= 0:
        return "running_unknown"
    age_ms = max(0, now - last_activity_ms)
    return "running_idle" if age_ms >= max(1000, int(idle_after_ms or 30000)) else "running_active"


def _filter_records_by_status(records, status):
    wanted = str(status or "all").strip().lower()
    if wanted in {"", "all"}:
        return list(records)
    if wanted == "active":
        return [record for record in records if not _record_is_archived(record)]
    if wanted == "archived":
        return [record for record in records if _record_is_archived(record)]
    return [
        record
        for record in records
        if (not _record_is_archived(record)) and str(record.get("status") or "unknown").lower() == wanted
    ]


def _record_reference_time_ms(record):
    if _record_is_archived(record):
        return int(record.get("archived_at_ms") or record.get("finished_at_ms") or record.get("started_at_ms") or 0)
    return int(record.get("finished_at_ms") or record.get("started_at_ms") or 0)


def _parse_age_window_ms(value):
    text = str(value or "").strip().lower()
    if not text:
        raise ValueError("older-than must be provided")
    scale = 24 * 60 * 60 * 1000
    if text[-1] in {"s", "m", "h", "d"}:
        unit = text[-1]
        num = text[:-1].strip()
        if not num or not num.isdigit():
            raise ValueError("older-than must be numeric with optional suffix s|m|h|d")
        base = int(num)
        if unit == "s":
            scale = 1000
        elif unit == "m":
            scale = 60 * 1000
        elif unit == "h":
            scale = 60 * 60 * 1000
        else:
            scale = 24 * 60 * 60 * 1000
        return max(1000, base * scale)
    if text.isdigit():
        return max(1000, int(text) * scale)
    raise ValueError("older-than must be numeric with optional suffix s|m|h|d")


def _parse_duration_window_ms(value, *, default_unit="s"):
    text = str(value or "").strip().lower()
    if not text:
        raise ValueError("duration window must be provided")
    unit = default_unit if default_unit in {"s", "m", "h", "d"} else "s"
    number = text
    if text[-1] in {"s", "m", "h", "d"}:
        unit = text[-1]
        number = text[:-1].strip()
    if not number or not number.isdigit():
        raise ValueError("duration window must be numeric with optional suffix s|m|h|d")
    base = int(number)
    scale = 1000
    if unit == "m":
        scale = 60 * 1000
    elif unit == "h":
        scale = 60 * 60 * 1000
    elif unit == "d":
        scale = 24 * 60 * 60 * 1000
    return max(1000, base * scale)


def _reconcile_stale_running_runs(*, stale_after_ms, now_ms=None, emit_events=True):
    threshold = max(1000, int(stale_after_ms or 0))
    now = int(now_ms or _now_ms())
    checked = 0
    reconciled = []
    for record in _list_all_run_records():
        if str(record.get("status") or "").strip().lower() != "running":
            continue
        checked += 1
        run_id = str(record.get("run_id") or "").strip()
        if not run_id:
            continue
        heartbeat = int(record.get("last_update_ms") or record.get("started_at_ms") or 0)
        if heartbeat <= 0:
            continue
        if now - heartbeat < threshold:
            continue
        started = int(record.get("started_at_ms") or heartbeat)
        record["status"] = "failed"
        record["exit_code"] = int(record.get("exit_code") or 1)
        record["finished_at_ms"] = now
        record["duration_ms"] = max(0, now - started)
        record["last_update_ms"] = now
        record["stale_reconciled"] = True
        record["stale_reconciled_at_ms"] = now
        record["stale_reconcile_reason"] = "orphaned_run_reconciled"
        _persist_run_record(record)
        reconciled.append(run_id)
        if emit_events:
            emit_observe_event(
                run_id=run_id,
                event_type="observe_run_reconciled",
                outcome="failed",
                payload={
                    "step_name": "run.reconcile",
                    "reason": "orphaned_run_reconciled",
                    "stale_after_ms": threshold,
                    "source": "cli",
                },
                agent_id="observer.reconcile",
            )
    return {
        "checked_running": checked,
        "reconciled_runs": len(reconciled),
        "reconciled_run_ids": reconciled,
        "stale_after_ms": threshold,
    }


def _maybe_auto_reconcile():
    if not bool(getattr(hive_config, "TRACE_RECONCILE_ENABLED", True)):
        return None
    if not bool(getattr(hive_config, "TRACE_RECONCILE_AUTO_ON_READ", True)):
        return None
    stale_sec = int(getattr(hive_config, "TRACE_RECONCILE_STALE_SEC", 300) or 300)
    return _reconcile_stale_running_runs(stale_after_ms=max(1, stale_sec) * 1000, emit_events=True)


def _archive_run_record(run_id, *, archive=True, reason="", actor="operator"):
    run_key = str(run_id or "").strip()
    if not run_key:
        raise ValueError("run_id is required")
    record = _read_run_record(run_key)
    if not record:
        return None
    now = _now_ms()
    if archive:
        record["archived"] = True
        record["archived_at_ms"] = now
        record["archive_reason"] = str(reason or "").strip()
        record["archive_actor"] = str(actor or "operator").strip() or "operator"
        record["last_update_ms"] = now
    else:
        record["archived"] = False
        record["archived_at_ms"] = None
        record["archive_reason"] = None
        record["archive_actor"] = None
        record["last_update_ms"] = now
    _persist_run_record(record)
    return record


def _drop_events_for_runs(run_ids_by_log_path):
    dropped = 0
    touched_files = 0
    for base_path, run_ids in run_ids_by_log_path.items():
        wanted = {str(run_id or "").strip() for run_id in (run_ids or set()) if str(run_id or "").strip()}
        if not wanted:
            continue
        for file_path in _iter_trace_files_from_base_oldest_first(base_path):
            try:
                source = Path(file_path)
                tmp = source.with_suffix(source.suffix + ".tmp")
                changed = False
                removed_here = 0
                with source.open("r", encoding="utf-8") as src, tmp.open("w", encoding="utf-8") as dst:
                    for line in src:
                        keep = True
                        raw = line.strip()
                        if raw:
                            try:
                                event = json.loads(raw)
                                if str(event.get("run_id") or "").strip() in wanted:
                                    keep = False
                            except Exception:
                                keep = True
                        if keep:
                            dst.write(line)
                        else:
                            changed = True
                            removed_here += 1
                if changed:
                    os.replace(str(tmp), str(source))
                    dropped += removed_here
                    touched_files += 1
                else:
                    tmp.unlink(missing_ok=True)
            except Exception:
                continue
    return {"dropped_events": dropped, "touched_files": touched_files}


def _iter_trace_files_from_base_oldest_first(base_path):
    text = str(base_path or "").strip()
    if not text:
        return []
    path = Path(text)
    parent = path.parent
    name = path.name
    rotated = []
    try:
        for candidate in parent.glob(f"{name}.*"):
            suffix = candidate.name[len(name) + 1 :]
            if suffix.isdigit():
                rotated.append((int(suffix), candidate))
    except Exception:
        rotated = []
    rotated.sort(key=lambda item: item[0], reverse=True)
    files = [candidate for _, candidate in rotated]
    files.append(path)
    return [item for item in files if item.exists()]


def _read_trace_events_for_run(run_id, *, limit=200, trace_log_path=""):
    from collections import deque

    rid = str(run_id or "").strip()
    if not rid:
        return []
    out = deque(maxlen=max(1, int(limit or 200)))
    candidates = []
    preferred = str(trace_log_path or "").strip()
    if preferred:
        candidates.extend(_iter_trace_files_from_base_oldest_first(preferred))
    active = _active_trace_log_path()
    if active and active != preferred:
        candidates.extend(_iter_trace_files_from_base_oldest_first(active))
    seen_paths = set()
    for path in candidates:
        norm = str(path)
        if norm in seen_paths:
            continue
        seen_paths.add(norm)
        try:
            with path.open("r", encoding="utf-8") as handle:
                for line in handle:
                    line = line.strip()
                    if not line:
                        continue
                    try:
                        event = json.loads(line)
                    except Exception:
                        continue
                    if str(event.get("run_id") or "") == rid:
                        out.append(event)
        except OSError:
            continue
    return list(out)


def _read_all_trace_events_for_run(run_id, *, trace_log_path=""):
    rid = str(run_id or "").strip()
    if not rid:
        return []
    out = []
    candidates = []
    preferred = str(trace_log_path or "").strip()
    if preferred:
        candidates.extend(_iter_trace_files_from_base_oldest_first(preferred))
    active = _active_trace_log_path()
    if active and active != preferred:
        candidates.extend(_iter_trace_files_from_base_oldest_first(active))
    seen_paths = set()
    for path in candidates:
        norm = str(path)
        if norm in seen_paths:
            continue
        seen_paths.add(norm)
        try:
            with path.open("r", encoding="utf-8") as handle:
                for line in handle:
                    line = line.strip()
                    if not line:
                        continue
                    try:
                        event = json.loads(line)
                    except Exception:
                        continue
                    if str(event.get("run_id") or "") == rid:
                        out.append(event)
        except OSError:
            continue
    return out


def _run_has_step_id(run_id, step_id, *, event_limit=50000):
    return _replay_ops.run_has_step_id(
        run_id,
        step_id,
        event_limit=event_limit,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        read_trace_events=read_trace_events,
    )


def _run_has_checkpoint_id(run_id, checkpoint_id, *, event_limit=50000):
    return _replay_ops.run_has_checkpoint_id(
        run_id,
        checkpoint_id,
        event_limit=event_limit,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        read_trace_events=read_trace_events,
    )


def emit_observe_event(
    *,
    run_id,
    event_type,
    outcome="success",
    payload=None,
    agent_id="observer.sdk",
    task_id=None,
    chunk_id=None,
    zone_id=None,
    flow_id=None,
    step_id=None,
    parent_step_id=None,
    tool_call_id=None,
    attempt=None,
    step_type=None,
):
    _touch_run_record(run_id)
    event = build_trace_event(
        event_type=event_type,
        run_id=run_id,
        outcome=outcome if outcome in {"success", "failed", "denied"} else "success",
        payload=payload or {},
        agent_id=agent_id,
        task_id=task_id,
        chunk_id=chunk_id,
        zone_id=zone_id,
        flow_id=flow_id,
        step_id=step_id,
        parent_step_id=parent_step_id,
        tool_call_id=tool_call_id,
        attempt=attempt,
        step_type=step_type,
    )
    return emit_trace(event)


def ingest_tei_event(
    event,
    *,
    agent_id="observer.tei",
    persist_db=True,
    persist_file=True,
    strict_version=None,
):
    return _ingest_tei_event_impl(
        event,
        agent_id=agent_id,
        persist_db=persist_db,
        persist_file=persist_file,
        strict_version=strict_version,
        now_ms=_now_ms,
        read_run_record=_read_run_record,
        persist_run_record=_persist_run_record,
        touch_run_record=_touch_run_record,
        active_trace_log_path=_active_trace_log_path,
    )


def ingest_tei_events(
    events,
    *,
    agent_id="observer.tei",
    persist_db=True,
    persist_file=True,
    strict_version=None,
):
    return _ingest_tei_events_impl(
        events,
        agent_id=agent_id,
        persist_db=persist_db,
        persist_file=persist_file,
        strict_version=strict_version,
        now_ms=_now_ms,
        read_run_record=_read_run_record,
        persist_run_record=_persist_run_record,
        touch_run_record=_touch_run_record,
        active_trace_log_path=_active_trace_log_path,
    )


@contextmanager
def observe_run(
    run_name,
    *,
    run_id=None,
    metadata=None,
    agent_id="observer.sdk",
):
    rid = str(run_id or _build_run_id())
    started_at = _now_ms()
    emit_observe_event(
        run_id=rid,
        event_type="observe_run_started",
        outcome="success",
        payload={
            "run_name": str(run_name or "run"),
            "metadata": dict(metadata or {}),
            "source": "sdk",
        },
        agent_id=agent_id,
    )
    try:
        yield rid
        elapsed_ms = max(0, _now_ms() - started_at)
        emit_observe_event(
            run_id=rid,
            event_type="observe_run_finished",
            outcome="success",
            payload={"run_name": str(run_name or "run"), "duration_ms": elapsed_ms, "source": "sdk"},
            agent_id=agent_id,
        )
    except Exception as exc:
        elapsed_ms = max(0, _now_ms() - started_at)
        emit_observe_event(
            run_id=rid,
            event_type="observe_run_finished",
            outcome="failed",
            payload={
                "run_name": str(run_name or "run"),
                "duration_ms": elapsed_ms,
                "error": _shorten(str(exc), limit=400),
                "source": "sdk",
            },
            agent_id=agent_id,
        )
        raise


def observe_step(
    run_id,
    step_name,
    *,
    payload=None,
    outcome="success",
    agent_id="observer.sdk",
    flow_id="",
    step_id="",
    parent_step_id="",
    tool_call_id="",
    attempt=None,
    step_type="",
):
    lineage_kwargs = {}
    if str(flow_id or "").strip():
        lineage_kwargs["flow_id"] = str(flow_id or "").strip()
    if str(step_id or "").strip():
        lineage_kwargs["step_id"] = str(step_id or "").strip()
    if str(parent_step_id or "").strip():
        lineage_kwargs["parent_step_id"] = str(parent_step_id or "").strip()
    if str(tool_call_id or "").strip():
        lineage_kwargs["tool_call_id"] = str(tool_call_id or "").strip()
    if attempt is not None:
        lineage_kwargs["attempt"] = int(attempt)
    if str(step_type or "").strip():
        lineage_kwargs["step_type"] = str(step_type or "").strip()
    return emit_observe_event(
        run_id=run_id,
        event_type="observe_step",
        outcome=outcome,
        payload={"step_name": str(step_name or "step"), "source": "sdk", **dict(payload or {})},
        agent_id=agent_id,
        **lineage_kwargs,
    )


def observe_agent_step_start(
    run_id,
    step_id,
    *,
    flow_id="",
    step_name="",
    parent_step_id="",
    attempt=1,
    payload=None,
    agent_id="observer.sdk",
):
    body = {
        "phase": "start",
        "boundary_type": "step.generic",
        "idempotency_hint": "unknown",
        "side_effect": False,
        "external_write": False,
    }
    body.update(dict(payload or {}))
    return observe_step(
        run_id=run_id,
        step_name=str(step_name or step_id or "agent_step"),
        payload=body,
        outcome="success",
        agent_id=agent_id,
        flow_id=flow_id,
        step_id=step_id,
        parent_step_id=parent_step_id,
        attempt=attempt,
        step_type="agent_step",
    )


def observe_agent_step_end(
    run_id,
    step_id,
    *,
    flow_id="",
    step_name="",
    parent_step_id="",
    attempt=1,
    outcome="success",
    payload=None,
    agent_id="observer.sdk",
):
    body = {
        "phase": "end",
        "boundary_type": "step.generic",
        "idempotency_hint": "unknown",
        "side_effect": False,
        "external_write": False,
    }
    body.update(dict(payload or {}))
    return observe_step(
        run_id=run_id,
        step_name=str(step_name or step_id or "agent_step"),
        payload=body,
        outcome=outcome,
        agent_id=agent_id,
        flow_id=flow_id,
        step_id=step_id,
        parent_step_id=parent_step_id,
        attempt=attempt,
        step_type="agent_step",
    )


def observe_tool_call_start(
    run_id,
    tool_call_id,
    *,
    flow_id="",
    step_id="",
    parent_step_id="",
    tool_name="",
    attempt=1,
    payload=None,
    agent_id="observer.sdk",
):
    body = {
        "phase": "start",
        "canonical_anchor_type": "tool.request",
        "idempotency_hint": "high",
        "side_effect": True,
        "external_write": True,
    }
    if str(tool_name or "").strip():
        body["tool_name"] = str(tool_name or "").strip()
    body.update(dict(payload or {}))
    return observe_step(
        run_id=run_id,
        step_name=str(step_id or tool_call_id or "tool_call"),
        payload=body,
        outcome="success",
        agent_id=agent_id,
        flow_id=flow_id,
        step_id=step_id,
        parent_step_id=parent_step_id,
        tool_call_id=tool_call_id,
        attempt=attempt,
        step_type="tool_call",
    )


def observe_tool_call_end(
    run_id,
    tool_call_id,
    *,
    flow_id="",
    step_id="",
    parent_step_id="",
    tool_name="",
    attempt=1,
    outcome="success",
    payload=None,
    agent_id="observer.sdk",
):
    body = {
        "phase": "end",
        "canonical_anchor_type": "tool.result",
        "idempotency_hint": "medium",
        "side_effect": True,
        "external_write": True,
    }
    if str(tool_name or "").strip():
        body["tool_name"] = str(tool_name or "").strip()
    body.update(dict(payload or {}))
    return observe_step(
        run_id=run_id,
        step_name=str(step_id or tool_call_id or "tool_call"),
        payload=body,
        outcome=outcome,
        agent_id=agent_id,
        flow_id=flow_id,
        step_id=step_id,
        parent_step_id=parent_step_id,
        tool_call_id=tool_call_id,
        attempt=attempt,
        step_type="tool_call",
    )


def observe_step_retry(
    run_id,
    step_id,
    *,
    flow_id="",
    parent_step_id="",
    attempt=1,
    reason="",
    payload=None,
    agent_id="observer.sdk",
):
    body = {
        "phase": "retry",
        "reason": str(reason or "").strip(),
        "boundary_type": "step.generic",
        "idempotency_hint": "unknown",
        "side_effect": False,
        "external_write": False,
    }
    body.update(dict(payload or {}))
    return observe_step(
        run_id=run_id,
        step_name=str(step_id or "retry"),
        payload=body,
        outcome="success",
        agent_id=agent_id,
        flow_id=flow_id,
        step_id=step_id,
        parent_step_id=parent_step_id,
        attempt=attempt,
        step_type="agent_step",
    )


def observe_branch_decision(
    run_id,
    step_id,
    *,
    flow_id="",
    parent_step_id="",
    selected_branch="",
    branches=None,
    payload=None,
    agent_id="observer.sdk",
):
    body = {
        "phase": "branch",
        "selected_branch": str(selected_branch or "").strip(),
        "branches": list(branches or []),
        "canonical_anchor_type": "decision.branch",
        "idempotency_hint": "low",
        "side_effect": False,
        "external_write": False,
    }
    body.update(dict(payload or {}))
    return observe_step(
        run_id=run_id,
        step_name=str(step_id or "branch"),
        payload=body,
        outcome="success",
        agent_id=agent_id,
        flow_id=flow_id,
        step_id=step_id,
        parent_step_id=parent_step_id,
        step_type="branch",
    )


def observe_checkpoint(
    run_id,
    checkpoint_id,
    *,
    step_name="",
    state_ref="",
    payload=None,
    agent_id="observer.sdk",
):
    body = {
        "checkpoint_id": str(checkpoint_id or "").strip(),
        "step_name": str(step_name or "").strip(),
        "state_ref": str(state_ref or "").strip(),
        "canonical_anchor_type": "checkpoint.state",
        "idempotency_hint": "low",
        "side_effect": False,
        "external_write": False,
        "source": "sdk",
    }
    body.update(dict(payload or {}))
    if not body["checkpoint_id"]:
        raise ValueError("checkpoint_id is required")
    return emit_observe_event(
        run_id=run_id,
        event_type="observe_checkpoint",
        outcome="success",
        payload=body,
        agent_id=agent_id,
    )


def observe_rerun_request(
    run_id,
    *,
    from_checkpoint_id,
    reason="",
    overrides=None,
    agent_id="observer.sdk",
):
    checkpoint_id = str(from_checkpoint_id or "").strip()
    if not checkpoint_id:
        raise ValueError("from_checkpoint_id is required")
    return emit_observe_event(
        run_id=run_id,
        event_type="observe_rerun_requested",
        outcome="success",
        payload={
            "from_checkpoint_id": checkpoint_id,
            "reason": str(reason or "").strip(),
            "overrides": dict(overrides or {}),
            "source": "sdk",
        },
        agent_id=agent_id,
    )


def _safe_stream_write(target, text):
    return _run_exec.safe_stream_write(target, text)


def _stream_process_output(proc, run_id, command_text, agent_id, heartbeat_cb=None, timeout_sec=None, started_at_ms=None):
    return _run_exec.stream_process_output(
        proc,
        run_id,
        command_text,
        agent_id,
        now_ms=_now_ms,
        shorten=_shorten,
        emit_event=emit_observe_event,
        heartbeat_cb=heartbeat_cb,
        timeout_sec=timeout_sec,
        started_at_ms=started_at_ms,
    )


@contextmanager
def observe_proxy(*, run_id, upstream_base, bind_host="127.0.0.1", bind_port=0, agent_id="observer.proxy"):
    with _proxy.observe_proxy(
        run_id=run_id,
        upstream_base=upstream_base,
        bind_host=bind_host,
        bind_port=bind_port,
        agent_id=agent_id,
        emit_event=emit_observe_event,
        shorten=_shorten,
        now_ms=_now_ms,
    ) as server:
        yield server


def _build_proxy_env(proxy_url):
    return _proxy.build_proxy_env(proxy_url)


def run_command_observed(
    command,
    *,
    run_id=None,
    run_name=None,
    cwd=None,
    timeout_sec=None,
    agent_id="observer.cli",
    open_ui_url="",
    env_overrides=None,
    record_fields=None,
):
    return _run_exec.run_command_observed(
        command,
        run_id=run_id,
        run_name=run_name,
        cwd=cwd,
        timeout_sec=timeout_sec,
        agent_id=agent_id,
        open_ui_url=open_ui_url,
        env_overrides=env_overrides,
        record_fields=record_fields,
        build_run_id=_build_run_id,
        now_ms=_now_ms,
        active_trace_log_path=_active_trace_log_path,
        persist_run_record=_persist_run_record,
        touch_run_record=_touch_run_record,
        emit_event=emit_observe_event,
        open_run_in_ui=_open_run_in_ui,
        shorten=_shorten,
    )


def _build_parser():
    return _build_parser_impl(
        add_help_verbose_flag=_add_help_verbose_flag,
        hive_version_getter=_hive_version,
        parser_cls=_StyledArgumentParser,
    )


class _StyledArgumentParser(argparse.ArgumentParser):
    class _HiveHelpFormatter(argparse.HelpFormatter):
        def _format_action_invocation(self, action):
            if not action.option_strings:
                return super()._format_action_invocation(action)
            if isinstance(action, argparse._SubParsersAction):
                return super()._format_action_invocation(action)
            if getattr(action, "nargs", None) == 0:
                return ", ".join(action.option_strings)
            return ", ".join(action.option_strings)

    def __init__(self, *args, **kwargs):
        if "formatter_class" not in kwargs:
            kwargs["formatter_class"] = lambda prog: self._HiveHelpFormatter(
                prog,
                max_help_position=40,
                width=116,
            )
        super().__init__(*args, **kwargs)

    def print_help(self, file=None):
        target = file or sys.stdout
        text = self.format_help()
        try:
            styled = _term.style_help_text(text)
        except Exception:
            styled = text
        target.write(styled)


def _print_trace_rows(events):
    if not events:
        print("No trace events matched.")
        return
    for event in events:
        ts = str(event.get("timestamp") or "-")
        outcome = str(event.get("outcome") or "-")
        event_type = str(event.get("event_type") or "-")
        run_id = str(event.get("run_id") or "-")
        payload = event.get("payload") if isinstance(event.get("payload"), dict) else {}
        step_name = str(payload.get("step_name") or payload.get("run_name") or "").strip()
        suffix = f" | {step_name}" if step_name else ""
        print(f"{ts} | {outcome:<7} | {event_type:<22} | {run_id}{suffix}")


def _is_truthy_env(value):
    return str(value or "").strip().lower() in {"1", "true", "yes", "on"}


def _print_branding_stamp(*, as_json=False):
    if as_json:
        return
    if _is_truthy_env(os.environ.get("HIVE_TRACE_BRANDING_DISABLE")):
        return
    print("")
    print(_term.muted("-----"))
    print(_term.brand(f"- hiveos-trace v{_hive_version()}"))
    print("")


def _finalize_cli_output(code, args):
    exit_code = int(code or 0)
    if not bool(getattr(args, "json", False)):
        print("")
    return exit_code


def _handle_legacy_trace_events(args):
    limit = max(1, min(1000, int(args.limit or 50)))
    filters = {
        "run_id": str(args.run_id or "").strip() or None,
        "event_type": str(args.event_type or "").strip() or None,
        "outcome": str(args.outcome or "").strip() or None,
        "agent_id": str(args.agent_id or "").strip() or None,
        "task_id": str(args.task_id or "").strip() or None,
        "chunk_id": str(args.chunk_id or "").strip() or None,
        "zone_id": str(args.zone_id or "").strip() or None,
    }
    events = read_trace_events(limit=limit, filters=filters)
    if args.json:
        for event in events:
            print(json.dumps(event, separators=(",", ":")))
    else:
        _print_trace_rows(events)
    return 0


def _build_ui_url(base_url, run_id):
    base = str(base_url or "").strip() or "http://localhost:5173"
    joiner = "&" if "?" in base else "?"
    return f"{base}{joiner}run_id={quote_plus(str(run_id))}"


def _resolve_ui_url_for_command(args):
    explicit_open = bool(getattr(args, "open", False))
    explicit_no_open = bool(getattr(args, "no_open", False))
    if explicit_open and explicit_no_open:
        raise SystemExit("Choose only one of --open or --no-open")
    default_open = bool(getattr(hive_config, "TRACE_OPEN_ON_RUN", False))
    should_open = explicit_open or (default_open and not explicit_no_open)
    if not should_open:
        return ""
    return str(getattr(args, "ui_url", "") or os.environ.get("HIVE_TRACE_UI_URL") or "http://localhost:5173").strip()


def _open_run_in_ui(base_url, run_id):
    url = _build_ui_url(base_url, run_id)
    if str(os.environ.get("HIVE_TRACE_DISABLE_BROWSER") or "").strip() in {"1", "true", "yes"}:
        return url
    ui_ok, _ = _check_ui_reachable(base_url)
    if not ui_ok:
        return _open_run_status_fallback(url, run_id)
    try:
        webbrowser.open(url, new=2)
    except Exception:
        pass
    return url


def _build_status_page_html(*, run_id, run_record, target_url):
    return _fallback_ui.build_status_page_html(
        run_id=run_id,
        run_record=run_record,
        target_url=target_url,
        active_trace_log_path=_active_trace_log_path,
        run_file=_run_file,
        read_trace_events_for_run=_read_trace_events_for_run,
        list_run_records=_list_run_records,
        shorten=_shorten,
    )


def _open_run_status_fallback(target_url, run_id):
    return _fallback_ui.open_run_status_fallback(
        target_url=target_url,
        run_id=run_id,
        read_run_record=_read_run_record,
        build_status_page_html_fn=_build_status_page_html,
    )


def _handle_trace_run(args):
    raw = list(args.argv or [])
    if raw and raw[0] == "--":
        raw = raw[1:]
    if not raw:
        raise SystemExit("No command provided. Example: hive trace run -- python agent.py")
    run_id = _build_run_id("observe-run")
    ui_url = _resolve_ui_url_for_command(args)
    proxy_enabled = bool(args.proxy)
    proxy_upstream = str(
        args.proxy_upstream
        or os.environ.get("HIVE_TRACE_PROXY_UPSTREAM")
        or os.environ.get("OPENAI_BASE_URL")
        or "https://api.openai.com/v1"
    ).strip()
    env_overrides = {}
    proxy_context = None
    if proxy_enabled:
        proxy_context = observe_proxy(
            run_id=run_id,
            upstream_base=proxy_upstream,
            bind_host="127.0.0.1",
            bind_port=int(args.proxy_port or 0),
            agent_id="observer.proxy",
        )
    if proxy_context is None:
        result = run_command_observed(
            raw,
            run_id=run_id,
            run_name=args.run_name or None,
            cwd=args.cwd or None,
            timeout_sec=args.timeout_sec or None,
            agent_id="observer.cli",
            open_ui_url=ui_url,
            env_overrides=env_overrides,
        )
    else:
        with proxy_context as proxy_server:
            env_overrides = _build_proxy_env(proxy_server.url)
            result = run_command_observed(
                raw,
                run_id=run_id,
                run_name=args.run_name or None,
                cwd=args.cwd or None,
                timeout_sec=args.timeout_sec or None,
                agent_id="observer.cli",
                open_ui_url=ui_url,
                env_overrides=env_overrides,
            )
            result["proxy_url"] = proxy_server.url
    print(f"run_id={result['run_id']} status={result['status']} duration_ms={result['duration_ms']}")
    if result.get("proxy_url"):
        print(f"proxy={result['proxy_url']}")
    if ui_url:
        print(f"ui={_build_ui_url(ui_url, result['run_id'])}")
    return int(result["exit_code"])


def _handle_trace_replay(args):
    return _replay_ops.handle_trace_replay(
        args,
        resolve_replay_plan_fn=_resolve_replay_plan,
        execute_replay_from_plan_fn=_execute_replay_from_plan,
    )


def _resolve_replay_plan(args):
    return _replay_ops.resolve_replay_plan(
        args,
        read_run_record=_read_run_record,
        run_has_step_id=_run_has_step_id,
        run_has_checkpoint_id=_run_has_checkpoint_id,
        read_trace_events_for_run=_read_trace_events_for_run,
        read_trace_events=read_trace_events,
        resolve_ui_url_for_command=_resolve_ui_url_for_command,
        collect_run_anchors_fn=_collect_run_anchors,
    )


def _execute_replay_from_plan(plan, *, timeout_sec=None):
    return _replay_ops.execute_replay_from_plan(
        plan,
        timeout_sec=timeout_sec,
        run_command_observed=run_command_observed,
        build_ui_url=_build_ui_url,
    )


def _write_replay_plan_artifact(payload, *, artifact_path=""):
    target_text = str(artifact_path or "").strip()
    if target_text:
        target = Path(target_text).expanduser()
    else:
        stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
        run_safe = str(payload.get("replay_of_run_id") or "run").replace(":", "_")
        target = _trace_home() / "artifacts" / "replay_plans" / f"{run_safe}.{stamp}.json"
    target.parent.mkdir(parents=True, exist_ok=True)
    plan_payload = dict(payload or {})
    plan_text = json.dumps(plan_payload, separators=(",", ":"), sort_keys=True)
    digest = hashlib.sha256(plan_text.encode("utf-8")).hexdigest()
    envelope = {
        "artifact_version": 1,
        "artifact_type": "replay_plan",
        "artifact_id": f"replay-plan:{digest[:12]}",
        "created_at": datetime.now(timezone.utc).isoformat(),
        "integrity": {
            "plan_hash": f"sha256:{digest}",
            "hash_scope": "plan",
        },
        "plan": plan_payload,
    }
    target.write_text(json.dumps(envelope, indent=2), encoding="utf-8")
    return str(target.resolve())


def _artifacts_root():
    root = _trace_home() / "artifacts"
    root.mkdir(parents=True, exist_ok=True)
    return root


def _iter_artifact_files():
    root = _artifacts_root()
    for path in root.rglob("*.json"):
        if path.is_file():
            yield path


def _read_artifact_file(path):
    try:
        payload = json.loads(Path(path).read_text(encoding="utf-8"))
    except Exception:
        return None
    if not isinstance(payload, dict):
        return None
    return payload


def _artifact_row(path, payload):
    plan = payload.get("plan") if isinstance(payload.get("plan"), dict) else {}
    source_window = plan.get("source_window") if isinstance(plan.get("source_window"), dict) else {}
    anchor = source_window.get("anchor") if isinstance(source_window.get("anchor"), dict) else {}
    return {
        "artifact_id": str(payload.get("artifact_id") or "").strip() or None,
        "artifact_type": str(payload.get("artifact_type") or "").strip() or None,
        "artifact_version": int(payload.get("artifact_version") or 0),
        "created_at": str(payload.get("created_at") or "").strip() or None,
        "path": str(Path(path).resolve()),
        "source_run_id": str(source_window.get("source_run_id") or plan.get("replay_of_run_id") or "").strip() or None,
        "validation_mode": str(source_window.get("validation_mode") or plan.get("validation_mode") or "").strip() or None,
        "anchor_type": str(anchor.get("anchor_type") or "").strip() or None,
        "anchor_id": str(anchor.get("anchor_id") or "").strip() or None,
    }


def _handle_trace_artifacts_ls(args):
    wanted_type = str(getattr(args, "type", "") or "").strip().lower()
    rows = []
    for path in _iter_artifact_files():
        payload = _read_artifact_file(path)
        if not payload:
            continue
        row = _artifact_row(path, payload)
        artifact_type = str(row.get("artifact_type") or "").strip().lower()
        if wanted_type and artifact_type != wanted_type:
            continue
        rows.append(row)
    rows.sort(key=lambda item: (str(item.get("created_at") or ""), str(item.get("path") or "")), reverse=True)
    rows = rows[: max(1, min(1000, int(getattr(args, "limit", 30) or 30)))]
    if bool(getattr(args, "json", False)):
        for row in rows:
            print(json.dumps(row, separators=(",", ":")))
        return 0
    if not rows:
        print("No trace artifacts found.")
        return 0
    for row in rows:
        print(
            f"{row.get('artifact_id') or '-'} | {row.get('artifact_type') or '-'} | "
            f"{row.get('source_run_id') or '-'} | {row.get('created_at') or '-'} | {row.get('path') or '-'}"
        )
    return 0


def _resolve_artifact_ref(artifact_ref):
    ref = str(artifact_ref or "").strip()
    if not ref:
        return None, None
    as_path = Path(ref).expanduser()
    if as_path.exists() and as_path.is_file():
        payload = _read_artifact_file(as_path)
        return (as_path.resolve(), payload) if payload else (None, None)
    for path in _iter_artifact_files():
        payload = _read_artifact_file(path)
        if not payload:
            continue
        if str(payload.get("artifact_id") or "").strip() == ref:
            return path.resolve(), payload
    return None, None


def _handle_trace_artifacts_show(args):
    path, payload = _resolve_artifact_ref(getattr(args, "artifact_ref", ""))
    if (not path) or (not payload):
        raise SystemExit(f"artifact not found: {str(getattr(args, 'artifact_ref', '') or '').strip()}")
    if bool(getattr(args, "json", False)):
        print(json.dumps(payload, separators=(",", ":")))
        return 0
    row = _artifact_row(path, payload)
    print(
        f"artifact_id={row.get('artifact_id')} artifact_type={row.get('artifact_type')} "
        f"source_run_id={row.get('source_run_id')} validation_mode={row.get('validation_mode')}"
    )
    print(f"path={row.get('path')}")
    print(f"created_at={row.get('created_at')}")
    return 0


def _handle_trace_artifacts_replay(args):
    path, payload = _resolve_artifact_ref(getattr(args, "artifact_ref", ""))
    if (not path) or (not payload):
        raise SystemExit(f"artifact not found: {str(getattr(args, 'artifact_ref', '') or '').strip()}")
    artifact_type = str(payload.get("artifact_type") or "").strip()
    if artifact_type != "replay_plan":
        raise SystemExit(f"artifact_type not replayable: {artifact_type or '-'}")
    plan = payload.get("plan") if isinstance(payload.get("plan"), dict) else {}
    replay_command = plan.get("replay_command")
    if isinstance(replay_command, list):
        replay_command = [str(item) for item in replay_command if str(item or "").strip()]
    else:
        replay_command = str(replay_command or "").strip()
    if not replay_command:
        raise SystemExit("artifact replay command missing")
    branch_preview = plan.get("branch_preview") if isinstance(plan.get("branch_preview"), dict) else {}
    if bool(branch_preview.get("preview_only")):
        raise SystemExit("artifact contains preview-only branch selection and cannot be executed")
    validation_checks = plan.get("validation_checks") if isinstance(plan.get("validation_checks"), dict) else {}
    if str(validation_checks.get("status") or "").strip().lower() == "fail":
        raise SystemExit("artifact validation status is fail and cannot be executed")
    resume_envelope = plan.get("resume_envelope") if isinstance(plan.get("resume_envelope"), dict) else {}
    admission = resume_envelope.get("admission") if isinstance(resume_envelope.get("admission"), dict) else {}
    if resume_envelope and (not bool(admission.get("allowed"))):
        reasons = ", ".join([str(item) for item in (admission.get("reasons") or []) if str(item).strip()]) or "resume admission failed"
        raise SystemExit(f"artifact replay denied: {reasons}")
    replay_plan = {
        "base_run_id": str(plan.get("replay_of_run_id") or "").strip(),
        "replay_command": replay_command,
        "run_name": str(getattr(args, "run_name", "") or "").strip() or str(plan.get("run_name") or "").strip() or "replay:artifact",
        "replay_cwd": str(getattr(args, "cwd", "") or "").strip() or plan.get("replay_cwd") or None,
        "from_step_id": str(plan.get("from_step_id") or "").strip(),
        "from_checkpoint_id": str(plan.get("from_checkpoint_id") or "").strip(),
        "validation_mode": str(plan.get("validation_mode") or "tolerant").strip().lower(),
        "validation_checks": validation_checks,
        "anchor_category": str(plan.get("anchor_type") or "").strip() or None,
        "branch_preview": branch_preview,
        "resume_envelope": resume_envelope,
        "record_fields": dict(plan.get("record_fields") or {}),
        "ui_url": _resolve_ui_url_for_command(args),
    }
    if replay_plan["record_fields"]:
        replay_plan["record_fields"] = dict(replay_plan["record_fields"])
    replay_plan["record_fields"]["replay_plan_artifact_id"] = str(payload.get("artifact_id") or "").strip() or None
    replay_plan["record_fields"]["replay_plan_artifact_path"] = str(path)
    return _execute_replay_from_plan(replay_plan, timeout_sec=args.timeout_sec or None)


def _handle_trace_replay_plan(args):
    return _replay_ops.handle_trace_replay_plan(
        args,
        resolve_replay_plan_fn=_resolve_replay_plan,
        execute_replay_from_plan_fn=_execute_replay_from_plan,
        write_replay_plan_artifact_fn=_write_replay_plan_artifact,
    )


def _handle_trace_checkpoint(args):
    _maybe_auto_reconcile()
    run_id = str(getattr(args, "run_id", "") or "").strip()
    checkpoint_id = str(getattr(args, "checkpoint_id", "") or "").strip()
    auto_id = bool(getattr(args, "auto_id", False))
    from_step_id = str(getattr(args, "from_step_id", "") or "").strip()
    if not run_id:
        raise SystemExit("run_id is required")
    record = _read_run_record(run_id)
    if not record:
        raise SystemExit(f"run_id not found: {run_id}")
    if (not checkpoint_id) and (not auto_id):
        raise SystemExit("checkpoint_id is required (or use --auto-id)")
    step_name = str(getattr(args, "step_name", "") or "").strip()
    state_ref = str(getattr(args, "state_ref", "") or "").strip()
    checkpoint_payload = {}
    if from_step_id:
        preferred_log = str(record.get("trace_log_path") or "").strip()
        events = _read_all_trace_events_for_run(run_id, trace_log_path=preferred_log)
        step_event = next(
            (
                event
                for event in reversed(list(events or []))
                if str(event.get("step_id") or "").strip() == from_step_id
            ),
            None,
        )
        if not step_event:
            raise SystemExit(f"step_id not found on source run: {from_step_id}")
        payload = step_event.get("payload") if isinstance(step_event.get("payload"), dict) else {}
        checkpoint_payload["source_step_id"] = from_step_id
        flow_id = str(step_event.get("flow_id") or "").strip()
        if flow_id:
            checkpoint_payload["source_flow_id"] = flow_id
        if not step_name:
            step_name = str(payload.get("step_name") or "").strip() or from_step_id
        if not state_ref:
            state_ref = f"step://{from_step_id}"
    if auto_id and (not checkpoint_id):
        seed = from_step_id or run_id
        safe_seed = "".join(ch if ch.isalnum() else "-" for ch in seed).strip("-") or "run"
        checkpoint_id = f"ckpt:{safe_seed}:{_now_ms()}"
    event = observe_checkpoint(
        run_id,
        checkpoint_id,
        step_name=step_name,
        state_ref=state_ref,
        payload=checkpoint_payload,
        agent_id=str(getattr(args, "agent_id", "observer.sdk") or "observer.sdk").strip() or "observer.sdk",
    )
    payload = {
        "ok": True,
        "run_id": run_id,
        "checkpoint_id": checkpoint_id,
        "from_step_id": from_step_id or None,
        "step_name": step_name or None,
        "state_ref": state_ref or None,
        "event_type": str((event or {}).get("event_type") or "observe_checkpoint"),
    }
    if bool(getattr(args, "json", False)):
        print(json.dumps(payload, separators=(",", ":")))
        return 0
    print(
        f"{_term.key('checkpoint_emitted')}={_term.ok('true')} "
        f"{_term.key('run_id')}={run_id} "
        f"{_term.key('checkpoint_id')}={checkpoint_id} "
        f"{_term.key('step_name')}={payload['step_name']} "
        f"{_term.key('state_ref')}={payload['state_ref']}"
    )
    print(_term.hint(f"next=hive trace anchors {run_id} --json"))
    return 0


def _collect_run_anchors(run_id, *, event_limit=5000):
    return _replay_ops.collect_run_anchors(
        run_id,
        event_limit=event_limit,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        read_trace_events=read_trace_events,
    )


def _handle_trace_anchors(args):
    return _replay_ops.handle_trace_anchors(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_run_record=_read_run_record,
        collect_run_anchors_fn=_collect_run_anchors,
    )


def _format_age_ms(age_ms):
    sec = max(0, int(age_ms / 1000))
    if sec < 60:
        return f"{sec}s"
    mins = sec // 60
    if mins < 60:
        return f"{mins}m"
    hrs = mins // 60
    if hrs < 24:
        return f"{hrs}h"
    return f"{hrs // 24}d"


def _handle_trace_ls(args):
    _maybe_auto_reconcile()
    records = _list_run_records(limit=args.limit)
    records = _filter_records_by_status(records, args.status)
    now = _now_ms()
    if args.json:
        for record in records:
            row = dict(record)
            last_activity_ms = _record_last_activity_ms(record, prefer_output=True)
            heartbeat_ms = _record_last_activity_ms(record, prefer_output=False)
            row["last_activity_ms"] = last_activity_ms if last_activity_ms > 0 else None
            row["last_activity_age_ms"] = max(0, now - last_activity_ms) if last_activity_ms > 0 else None
            row["last_heartbeat_ms"] = heartbeat_ms if heartbeat_ms > 0 else None
            row["last_heartbeat_age_ms"] = max(0, now - heartbeat_ms) if heartbeat_ms > 0 else None
            row["runtime_state"] = _record_runtime_state(record, now_ms=now)
            print(json.dumps(row, separators=(",", ":")))
        return 0
    if not records:
        print(_term.warn("No traced runs found."))
        return 0
    run_width = 0
    for rec in records:
        run_id = str(rec.get("run_id") or "-")
        run_width = max(run_width, len(run_id))
    run_width = max(16, run_width)
    for rec in records:
        run_id = str(rec.get("run_id") or "-")
        status = _record_effective_status(rec)
        started = int(rec.get("started_at_ms") or 0)
        age = _format_age_ms(now - started) if started > 0 else "--"
        runtime_state = _record_runtime_state(rec, now_ms=now)
        live_age = "--"
        if status == "running":
            last_activity_ms = _record_last_activity_ms(rec, prefer_output=True)
            live_age = _format_age_ms(now - last_activity_ms) if last_activity_ms > 0 else "--"
        live_state = "--"
        if runtime_state == "running_active":
            live_state = "active"
        elif runtime_state == "running_idle":
            live_state = "idle"
        elif runtime_state == "running_unknown":
            live_state = "unknown"
        command = _shorten(rec.get("command"), limit=100)
        run_plain = str(run_id)
        status_plain = str(status)
        age_plain = str(age)
        live_state_plain = str(live_state)
        live_age_plain = str(live_age)
        run_cell = _term.pad(_term.key(run_id), run_width)
        status_cell = _term.pad(_term.status_text(status), 7)
        age_cell = _term.pad(age_plain, 4, align="right")
        live_state_cell = _term.pad(_term.status_text(live_state), 7)
        live_age_cell = _term.pad(live_age_plain, 4, align="right")
        run_plain_cell = run_plain.ljust(run_width)
        status_plain_cell = status_plain.ljust(7)
        age_plain_cell = age_plain.rjust(4)
        live_state_plain_cell = live_state_plain.ljust(7)
        live_age_plain_cell = live_age_plain.rjust(4)
        prefix_styled = f"{run_cell} | {status_cell} | {age_cell} | {live_state_cell} {live_age_cell} | "
        prefix_plain = f"{run_plain_cell} | {status_plain_cell} | {age_plain_cell} | {live_state_plain_cell} {live_age_plain_cell} | "
        available_width = max(20, _term.terminal_width() - len(prefix_plain))
        wrapped = textwrap.wrap(
            str(command or ""),
            width=available_width,
            break_long_words=False,
            break_on_hyphens=False,
        ) or [""]
        print(f"{prefix_styled}{wrapped[0]}")
        if len(wrapped) > 1:
            continuation_indent = " " * len(prefix_plain)
            for line in wrapped[1:]:
                print(f"{continuation_indent}{line}")
    _print_branding_stamp(as_json=False)
    return 0


def _handle_legacy_trace_events_argv(argv):
    parser = argparse.ArgumentParser(prog="hive trace-events")
    parser.add_argument("--run-id", default="")
    parser.add_argument("--event-type", default="")
    parser.add_argument("--outcome", default="")
    parser.add_argument("--agent-id", default="")
    parser.add_argument("--task-id", default="")
    parser.add_argument("--chunk-id", default="")
    parser.add_argument("--zone-id", default="")
    parser.add_argument("--limit", type=int, default=50)
    parser.add_argument("--json", action="store_true")
    args = parser.parse_args(argv)
    return _handle_legacy_trace_events(args)


def _handle_trace_show(args):
    return _trace_views.handle_trace_show(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        read_trace_events=read_trace_events,
        print_trace_rows=_print_trace_rows,
    )


def _event_identity(event):
    return _trace_views.event_identity(event)


def _read_tail_events(limit=50, run_id=""):
    return _trace_views.read_tail_events(read_trace_events=read_trace_events, limit=limit, run_id=run_id)


def _emit_events(events, *, as_json=False):
    return _trace_views.emit_events(events, as_json=as_json, print_trace_rows=_print_trace_rows)


def _handle_trace_tail(args):
    return _trace_views.handle_trace_tail(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_trace_events=read_trace_events,
        print_trace_rows=_print_trace_rows,
    )


def _handle_trace_export(args):
    _maybe_auto_reconcile()
    run_id = str(args.run_id or "").strip()
    if not run_id:
        raise SystemExit("run_id is required")
    record = _read_run_record(run_id)
    if not record:
        raise SystemExit(f"run_id not found: {run_id}")
    preferred_log = str(record.get("trace_log_path") or "").strip()
    events = _read_all_trace_events_for_run(run_id, trace_log_path=preferred_log)
    bundle_path = Path(str(args.bundle or "").strip())
    if not str(bundle_path):
        raise SystemExit("--bundle is required")
    bundle_path.parent.mkdir(parents=True, exist_ok=True)
    payload = {
        "hive_trace_bundle_version": TRACE_BUNDLE_VERSION,
        "exported_at": _now_ms(),
        "source": {
            "trace_log_path": preferred_log or _active_trace_log_path(),
            "trace_home": str(_trace_home()),
        },
        "run_record": record,
        "events": events,
    }
    bundle_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
    print(f"exported={run_id} events={len(events)} bundle={bundle_path}")
    return 0


def _load_and_validate_bundle(path):
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except Exception as exc:
        raise SystemExit(f"invalid bundle json: {_shorten(exc, limit=200)}")
    if not isinstance(payload, dict):
        raise SystemExit("invalid bundle: expected object")
    version = int(payload.get("hive_trace_bundle_version") or 0)
    if version != TRACE_BUNDLE_VERSION:
        raise SystemExit(
            f"unsupported bundle version: {version} (expected {TRACE_BUNDLE_VERSION})"
        )
    record = payload.get("run_record")
    events = payload.get("events")
    if not isinstance(record, dict):
        raise SystemExit("invalid bundle: run_record must be an object")
    if not isinstance(events, list):
        raise SystemExit("invalid bundle: events must be an array")
    run_id = str(record.get("run_id") or "").strip()
    if not run_id:
        raise SystemExit("invalid bundle: run_record.run_id is required")
    out_events = []
    for item in events:
        if not isinstance(item, dict):
            continue
        if str(item.get("run_id") or "").strip() != run_id:
            continue
        out_events.append(item)
    return {"record": record, "events": out_events, "run_id": run_id}


def _append_events_to_active_trace_log(events):
    if not events:
        return 0
    path = Path(_active_trace_log_path())
    path.parent.mkdir(parents=True, exist_ok=True)
    written = 0
    with path.open("a", encoding="utf-8") as handle:
        for event in events:
            handle.write(json.dumps(event, separators=(",", ":")) + "\n")
            written += 1
    return written


def _handle_trace_import(args):
    _maybe_auto_reconcile()
    bundle_path = Path(str(args.bundle or "").strip())
    if not str(bundle_path):
        raise SystemExit("--bundle is required")
    if not bundle_path.exists():
        raise SystemExit(f"bundle not found: {bundle_path}")
    loaded = _load_and_validate_bundle(bundle_path)
    source_run_id = loaded["run_id"]
    target_run_id = str(args.as_run_id or "").strip() or source_run_id
    if not target_run_id:
        raise SystemExit("target run_id is required")
    if _read_run_record(target_run_id):
        raise SystemExit(f"run_id already exists: {target_run_id}")

    record = dict(loaded["record"])
    events = [dict(item) for item in loaded["events"]]
    if target_run_id != source_run_id:
        record["run_id"] = target_run_id
        if str(record.get("replay_of_run_id") or "").strip() == source_run_id:
            record["replay_of_run_id"] = target_run_id
        for event in events:
            event["run_id"] = target_run_id

    now = _now_ms()
    record["imported_from_bundle"] = True
    record["imported_at_ms"] = now
    record["import_source_run_id"] = source_run_id
    record["trace_log_path"] = _active_trace_log_path()
    record["last_update_ms"] = now

    _persist_run_record(record)
    imported_events = _append_events_to_active_trace_log(events)
    print(f"imported={target_run_id} events={imported_events} bundle={bundle_path}")
    return 0


def _handle_trace_flow_ls(args):
    _maybe_auto_reconcile()
    rows = _collect_recent_flow_summaries(limit=args.limit)
    if args.json:
        for row in rows:
            print(json.dumps(row, separators=(",", ":")))
        return 0
    if not rows:
        print("No flows found.")
        return 0
    now = _now_ms()
    for row in rows:
        flow_id = str(row.get("flow_id") or "-")
        status = str(row.get("status") or "unknown")
        ts_ms = int(row.get("last_timestamp_ms") or 0)
        age = _format_age_ms(max(0, now - ts_ms)) if ts_ms > 0 else "--"
        step_count = int(row.get("step_count") or 0)
        retry_steps = int(row.get("retry_steps") or 0)
        print(f"{flow_id} | {status:<7} | {age:>4} | steps={step_count} retries={retry_steps}")
    return 0


def _handle_trace_flow_show(args):
    _maybe_auto_reconcile()
    flow_id = str(args.flow_id or "").strip()
    if not flow_id:
        raise SystemExit("flow_id is required")
    events = _read_flow_events(flow_id, event_limit=args.event_limit)
    if not events:
        raise SystemExit(f"flow_id not found: {flow_id}")
    summary = _build_flow_summary(flow_id, events)
    steps = _derive_flow_steps(events)
    lineage_integrity = _analyze_flow_lineage_integrity(events, steps)
    if args.json:
        print(
            json.dumps(
                {
                    "flow": summary,
                    "events": events,
                    "lineage_integrity": lineage_integrity,
                },
                separators=(",", ":"),
            )
        )
        return 0
    print("flow_summary:")
    print(
        f"  flow_id={summary['flow_id']} status={summary['status']} events={summary['event_count']} "
        f"steps={summary['step_count']} retries={summary['retry_steps']} runs={summary['run_count']} "
        f"lineage_warnings={int(lineage_integrity.get('warning_count') or 0)}"
    )
    for warning in (lineage_integrity.get("warnings") or []):
        print(f"  warning[{warning.get('code')}] {warning.get('message')}")
    print("flow_timeline:")
    for event in _sort_events_by_time(events):
        ts = str(event.get("timestamp") or "-")
        outcome = str(event.get("outcome") or "-")
        payload = event.get("payload") if isinstance(event.get("payload"), dict) else {}
        step_name = str(payload.get("step_name") or payload.get("run_name") or "").strip() or "-"
        step_id = str(event.get("step_id") or "-").strip() or "-"
        step_type = str(event.get("step_type") or "-").strip() or "-"
        attempt = int(event.get("attempt") or 1)
        if attempt < 1:
            attempt = 1
        parent_step_id = str(event.get("parent_step_id") or "").strip()
        tool_call_id = str(event.get("tool_call_id") or "").strip()
        extras = []
        if parent_step_id:
            extras.append(f"parent={parent_step_id}")
        if tool_call_id:
            extras.append(f"tool={tool_call_id}")
        anchor_type = _classify_anchor_type(event)
        if anchor_type:
            extras.append(f"anchor={anchor_type}")
        extra_text = f" | {' '.join(extras)}" if extras else ""
        print(
            f"  {ts} | {outcome:<7} | step_id={step_id} | type={step_type:<10} "
            f"| attempt={attempt} | name={step_name}{extra_text}"
        )
    print("next=hive trace flow steps <flow_id>")
    return 0


def _handle_trace_flow_steps(args):
    _maybe_auto_reconcile()
    flow_id = str(args.flow_id or "").strip()
    if not flow_id:
        raise SystemExit("flow_id is required")
    events = _read_flow_events(flow_id, event_limit=args.event_limit)
    if not events:
        raise SystemExit(f"flow_id not found: {flow_id}")
    steps = _derive_flow_steps(events)
    lineage_integrity = _analyze_flow_lineage_integrity(events, steps)
    if args.json:
        print(
            json.dumps(
                {
                    "flow_id": flow_id,
                    "steps": steps,
                    "lineage_integrity": lineage_integrity,
                },
                separators=(",", ":"),
            )
        )
        return 0
    if not steps:
        print("No steps found for flow.")
        return 0
    print(f"lineage_warnings={int(lineage_integrity.get('warning_count') or 0)}")
    for warning in (lineage_integrity.get("warnings") or []):
        print(f"  warning[{warning.get('code')}] {warning.get('message')}")
    print("flow_steps:")
    for step in steps:
        parent = str(step.get("parent_step_id") or "").strip() or "-"
        tools = ",".join([str(item) for item in (step.get("tool_call_ids") or []) if str(item).strip()]) or "-"
        anchors = ",".join([str(item) for item in (step.get("anchor_types") or []) if str(item).strip()]) or "-"
        duration_ms = step.get("duration_ms")
        duration_text = f"{int(duration_ms)}ms" if isinstance(duration_ms, int) else "-"
        print(
            f"  {step['step_id']} | type={step.get('step_type') or '-'} | attempts={int(step.get('attempts') or 1)} "
            f"| outcome={step.get('terminal_outcome') or '-':<7} | events={int(step.get('event_count') or 0)} "
            f"| parent={parent} | tools={tools} | anchors={anchors} | duration={duration_text}"
        )
    print("next=hive trace flow show <flow_id>")
    return 0


def _handle_trace_flow_diff(args):
    _maybe_auto_reconcile()
    flow_id_a = str(args.flow_id_a or "").strip()
    flow_id_b = str(args.flow_id_b or "").strip()
    if not flow_id_a or not flow_id_b:
        raise SystemExit("flow_id_a and flow_id_b are required")
    events_a = _read_flow_events(flow_id_a, event_limit=args.event_limit)
    events_b = _read_flow_events(flow_id_b, event_limit=args.event_limit)
    if not events_a:
        raise SystemExit(f"flow_id not found: {flow_id_a}")
    if not events_b:
        raise SystemExit(f"flow_id not found: {flow_id_b}")
    diff = _build_flow_diff(flow_id_a, flow_id_b, events_a, events_b)
    if args.json:
        print(json.dumps(diff, separators=(",", ":")))
        return 0
    print(
        f"flow_a={flow_id_a} steps={len(diff['steps_a'])} status={diff['flow_a']['status']} "
        f"flow_b={flow_id_b} steps={len(diff['steps_b'])} status={diff['flow_b']['status']}"
    )
    print(f"first_divergence_index={int(diff.get('first_divergence_index') or -1)}")
    if diff.get("retry_delta"):
        print("retry_delta:")
        for step_id, delta in diff["retry_delta"].items():
            print(f"  {step_id}: a={delta['a']} b={delta['b']}")
    else:
        print("retry_delta: none")
    return 0


def _handle_trace_summary(args):
    return _trace_views.handle_trace_summary(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        read_trace_events=read_trace_events,
        build_run_summary=_build_run_summary,
        print_branding_stamp=_print_branding_stamp,
    )


def _collect_output_lines(events):
    stdout_lines = []
    stderr_lines = []
    for event in events:
        if str(event.get("event_type") or "") != "observe_output":
            continue
        payload = event.get("payload") if isinstance(event.get("payload"), dict) else {}
        text = str(payload.get("text") or "")
        if not text:
            continue
        stream = str(payload.get("stream") or "stdout").strip().lower()
        if stream == "stderr":
            stderr_lines.append(text)
        else:
            stdout_lines.append(text)
    return {"stdout": stdout_lines, "stderr": stderr_lines}


def _build_run_summary(record, events, tail_lines=20):
    t = max(1, int(tail_lines or 20))
    run_id = str(record.get("run_id") or "")
    status = _record_effective_status(record)
    raw_exit_code = record.get("exit_code")
    exit_code = int(raw_exit_code) if raw_exit_code is not None else None
    now = _now_ms()
    last_activity_ms = _record_last_activity_ms(record, prefer_output=True)
    heartbeat_ms = _record_last_activity_ms(record, prefer_output=False)
    outputs = _collect_output_lines(events)
    suggestions = [
        f"hive trace show {run_id} --limit 120",
        f"hive trace diagnose {run_id}",
        f"hive trace replay {run_id} --no-open",
        "hive trace ls --limit 10",
    ]
    return {
        "run_id": run_id,
        "status": status,
        "exit_code": exit_code,
        "duration_ms": int(record.get("duration_ms") or 0),
        "event_count": len(events),
        "command": str(record.get("command") or ""),
        "cwd": str(record.get("cwd") or ""),
        "runtime_state": _record_runtime_state(record, now_ms=now),
        "last_activity_ms": last_activity_ms if last_activity_ms > 0 else None,
        "last_activity_age_ms": max(0, now - last_activity_ms) if last_activity_ms > 0 else None,
        "last_heartbeat_ms": heartbeat_ms if heartbeat_ms > 0 else None,
        "last_heartbeat_age_ms": max(0, now - heartbeat_ms) if heartbeat_ms > 0 else None,
        "stderr_tail": outputs["stderr"][-t:],
        "stdout_tail": outputs["stdout"][-t:],
        "suggestions": suggestions,
    }


def _event_timestamp_ms(event):
    raw = str(event.get("timestamp") or "").strip()
    if not raw:
        return 0
    try:
        dt = datetime.fromisoformat(raw.replace("Z", "+00:00"))
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        return int(dt.timestamp() * 1000)
    except Exception:
        return 0


def _sort_events_by_time(events):
    indexed = list(enumerate(events or []))
    indexed.sort(key=lambda item: (_event_timestamp_ms(item[1]), item[0]))
    return [event for _, event in indexed]


def _read_flow_events(flow_id, *, event_limit=2000):
    flow_key = str(flow_id or "").strip()
    if not flow_key:
        return []
    bounded = max(1, min(20000, int(event_limit or 2000)))
    events = read_trace_events(limit=bounded, filters={"flow_id": flow_key})
    return _sort_events_by_time(events)


def _read_recent_events_window(limit=6000):
    bounded = max(1, min(50000, int(limit or 6000)))
    events = read_trace_events(limit=bounded, filters={})
    return _sort_events_by_time(events)


def _classify_anchor_type(event):
    event_type = str(event.get("event_type") or "").strip()
    if event_type == "observe_checkpoint":
        return "checkpoint.state"
    if event_type == "observe_output":
        return "output.commit"
    if event_type != "observe_step":
        return None
    step_type = str(event.get("step_type") or "").strip().lower()
    payload = event.get("payload") if isinstance(event.get("payload"), dict) else {}
    phase = str(payload.get("phase") or "").strip().lower()
    if step_type == "branch":
        return "decision.branch"
    if step_type == "tool_call":
        if phase == "start":
            return "tool.request"
        if phase == "end":
            return "tool.result"
        return "tool.result"
    if step_type in {"model", "llm"}:
        if phase in {"start", "request"}:
            return "model.request"
        if phase in {"end", "response"}:
            return "model.response"
    return "step.generic"


def _derive_flow_steps(events):
    ordered = _sort_events_by_time(events)
    by_step = {}
    for event in ordered:
        step_id = str(event.get("step_id") or "").strip()
        if not step_id:
            continue
        item = by_step.get(step_id)
        if item is None:
            item = {
                "step_id": step_id,
                "parent_step_id": str(event.get("parent_step_id") or "").strip() or None,
                "step_type": str(event.get("step_type") or "").strip() or None,
                "attempts": 1,
                "event_count": 0,
                "first_timestamp": str(event.get("timestamp") or ""),
                "last_timestamp": str(event.get("timestamp") or ""),
                "first_timestamp_ms": _event_timestamp_ms(event),
                "last_timestamp_ms": _event_timestamp_ms(event),
                "terminal_outcome": str(event.get("outcome") or "success"),
                "tool_call_ids": [],
                "anchor_types": [],
            }
            by_step[step_id] = item
        attempt = int(event.get("attempt") or 1)
        if attempt < 1:
            attempt = 1
        item["attempts"] = max(item["attempts"], attempt)
        item["event_count"] += 1
        ts_ms = _event_timestamp_ms(event)
        if ts_ms and (item["first_timestamp_ms"] == 0 or ts_ms < item["first_timestamp_ms"]):
            item["first_timestamp_ms"] = ts_ms
            item["first_timestamp"] = str(event.get("timestamp") or "")
        if ts_ms >= item["last_timestamp_ms"]:
            item["last_timestamp_ms"] = ts_ms
            item["last_timestamp"] = str(event.get("timestamp") or "")
            item["terminal_outcome"] = str(event.get("outcome") or "success")
            step_type = str(event.get("step_type") or "").strip()
            if step_type:
                item["step_type"] = step_type
            parent = str(event.get("parent_step_id") or "").strip()
            if parent:
                item["parent_step_id"] = parent
        tool_call_id = str(event.get("tool_call_id") or "").strip()
        if tool_call_id and tool_call_id not in item["tool_call_ids"]:
            item["tool_call_ids"].append(tool_call_id)
        anchor_type = _classify_anchor_type(event)
        if anchor_type and anchor_type not in item["anchor_types"]:
            item["anchor_types"].append(anchor_type)
    steps = list(by_step.values())
    steps.sort(key=lambda item: (int(item.get("first_timestamp_ms") or 0), str(item.get("step_id") or "")))
    for step in steps:
        first_ms = int(step.get("first_timestamp_ms") or 0)
        last_ms = int(step.get("last_timestamp_ms") or 0)
        step["duration_ms"] = max(0, last_ms - first_ms) if first_ms and last_ms else None
    return steps


def _analyze_flow_lineage_integrity(events, steps):
    ordered = _sort_events_by_time(events)
    derived_steps = list(steps or _derive_flow_steps(ordered))
    known_step_ids = {str(item.get("step_id") or "").strip() for item in derived_steps if str(item.get("step_id") or "").strip()}
    warnings = []

    missing_step_id_events = 0
    unknown_parent_ids = set()
    self_parent_ids = set()
    parent_variants = {}

    for event in ordered:
        event_type = str(event.get("event_type") or "").strip()
        step_id = str(event.get("step_id") or "").strip()
        parent_step_id = str(event.get("parent_step_id") or "").strip()
        if event_type == "observe_step" and not step_id:
            missing_step_id_events += 1
        if step_id and parent_step_id:
            if step_id == parent_step_id:
                self_parent_ids.add(step_id)
            variants = parent_variants.setdefault(step_id, set())
            variants.add(parent_step_id)
            if parent_step_id not in known_step_ids:
                unknown_parent_ids.add(parent_step_id)

    if missing_step_id_events > 0:
        warnings.append(
            {
                "code": "missing_step_id_on_observe_step",
                "severity": "warning",
                "count": int(missing_step_id_events),
                "message": f"{missing_step_id_events} observe_step events are missing step_id.",
            }
        )
    if unknown_parent_ids:
        missing = sorted(unknown_parent_ids)
        warnings.append(
            {
                "code": "parent_step_missing",
                "severity": "warning",
                "count": len(missing),
                "step_ids": missing,
                "message": f"{len(missing)} parent_step_id values do not resolve to known step_id values.",
            }
        )
    if self_parent_ids:
        cycles = sorted(self_parent_ids)
        warnings.append(
            {
                "code": "self_parent_cycle",
                "severity": "warning",
                "count": len(cycles),
                "step_ids": cycles,
                "message": f"{len(cycles)} steps reference themselves as parent_step_id.",
            }
        )

    conflicting = []
    for step_id, variants in parent_variants.items():
        if len(variants) > 1:
            conflicting.append({"step_id": step_id, "parent_step_ids": sorted(variants)})
    if conflicting:
        warnings.append(
            {
                "code": "conflicting_parent_step_id",
                "severity": "warning",
                "count": len(conflicting),
                "items": conflicting,
                "message": f"{len(conflicting)} step_id values map to multiple parent_step_id values.",
            }
        )

    return {
        "status": "warning" if warnings else "ok",
        "warning_count": len(warnings),
        "warnings": warnings,
    }


def _build_flow_summary(flow_id, events):
    ordered = _sort_events_by_time(events)
    if not ordered:
        return {
            "flow_id": str(flow_id or ""),
            "status": "unknown",
            "event_count": 0,
            "step_count": 0,
            "retry_steps": 0,
            "failed_events": 0,
            "denied_events": 0,
            "run_count": 0,
            "runs": [],
            "last_timestamp": "",
            "last_timestamp_ms": 0,
        }
    steps = _derive_flow_steps(ordered)
    lineage_integrity = _analyze_flow_lineage_integrity(ordered, steps)
    runs = sorted({str(event.get("run_id") or "").strip() for event in ordered if str(event.get("run_id") or "").strip()})
    failed_events = sum(1 for event in ordered if str(event.get("outcome") or "") == "failed")
    denied_events = sum(1 for event in ordered if str(event.get("outcome") or "") == "denied")
    status = str(ordered[-1].get("outcome") or "success")
    return {
        "flow_id": str(flow_id or ""),
        "status": status,
        "event_count": len(ordered),
        "step_count": len(steps),
        "retry_steps": sum(1 for step in steps if int(step.get("attempts") or 1) > 1),
        "failed_events": failed_events,
        "denied_events": denied_events,
        "lineage_warning_count": int(lineage_integrity.get("warning_count") or 0),
        "run_count": len(runs),
        "runs": runs,
        "last_timestamp": str(ordered[-1].get("timestamp") or ""),
        "last_timestamp_ms": _event_timestamp_ms(ordered[-1]),
    }


def _collect_recent_flow_summaries(limit=30):
    wanted = max(1, min(1000, int(limit or 30)))
    window = max(1000, wanted * 250)
    events = _read_recent_events_window(limit=window)
    grouped = {}
    for event in events:
        flow_id = str(event.get("flow_id") or "").strip()
        if not flow_id:
            continue
        grouped.setdefault(flow_id, []).append(event)
    summaries = []
    for flow_id, flow_events in grouped.items():
        summaries.append(_build_flow_summary(flow_id, flow_events))
    summaries.sort(key=lambda item: int(item.get("last_timestamp_ms") or 0), reverse=True)
    return summaries[:wanted]


def _build_flow_diff(flow_id_a, flow_id_b, events_a, events_b):
    steps_a = _derive_flow_steps(events_a)
    steps_b = _derive_flow_steps(events_b)

    sig_a = [(item["step_id"], item.get("terminal_outcome"), int(item.get("attempts") or 1)) for item in steps_a]
    sig_b = [(item["step_id"], item.get("terminal_outcome"), int(item.get("attempts") or 1)) for item in steps_b]
    first_divergence_index = -1
    for idx in range(min(len(sig_a), len(sig_b))):
        if sig_a[idx] != sig_b[idx]:
            first_divergence_index = idx
            break
    if first_divergence_index < 0 and len(sig_a) != len(sig_b):
        first_divergence_index = min(len(sig_a), len(sig_b))

    retry_delta = {}
    steps_by_id_a = {item["step_id"]: item for item in steps_a}
    steps_by_id_b = {item["step_id"]: item for item in steps_b}
    for step_id in sorted(set(steps_by_id_a.keys()) | set(steps_by_id_b.keys())):
        aa = int((steps_by_id_a.get(step_id) or {}).get("attempts") or 0)
        bb = int((steps_by_id_b.get(step_id) or {}).get("attempts") or 0)
        if aa != bb:
            retry_delta[step_id] = {"a": aa, "b": bb}

    return {
        "flow_a": _build_flow_summary(flow_id_a, events_a),
        "flow_b": _build_flow_summary(flow_id_b, events_b),
        "steps_a": steps_a,
        "steps_b": steps_b,
        "first_divergence_index": first_divergence_index,
        "retry_delta": retry_delta,
    }


def _event_signature(event):
    event_type = str(event.get("event_type") or "")
    payload = event.get("payload") if isinstance(event.get("payload"), dict) else {}
    text_sig = ""
    if event_type == "observe_output":
        text_sig = str(payload.get("text") or "")
    return (
        event_type,
        str(event.get("outcome") or ""),
        str(payload.get("step_name") or ""),
        str(payload.get("stream") or ""),
        str(payload.get("exit_code") or ""),
        text_sig,
    )


def _first_divergence_index(events_a, events_b):
    limit = min(len(events_a), len(events_b))
    for idx in range(limit):
        if _event_signature(events_a[idx]) != _event_signature(events_b[idx]):
            return idx
    if len(events_a) != len(events_b):
        return limit
    return -1


def _build_run_diff(record_a, record_b, events_a, events_b, tail_lines=20):
    outputs_a = _collect_output_lines(events_a)
    outputs_b = _collect_output_lines(events_b)
    divergence = _first_divergence_index(events_a, events_b)
    ta = max(1, int(tail_lines or 20))
    summary_a = {
        "run_id": str(record_a.get("run_id") or ""),
        "status": _record_effective_status(record_a),
        "duration_ms": int(record_a.get("duration_ms") or 0),
        "started_at_ms": int(record_a.get("started_at_ms") or 0),
    }
    summary_b = {
        "run_id": str(record_b.get("run_id") or ""),
        "status": _record_effective_status(record_b),
        "duration_ms": int(record_b.get("duration_ms") or 0),
        "started_at_ms": int(record_b.get("started_at_ms") or 0),
    }
    config_deltas = {}
    for key in ("command", "cwd", "trace_log_path"):
        av = str(record_a.get(key) or "")
        bv = str(record_b.get(key) or "")
        if av != bv:
            config_deltas[key] = {"a": av, "b": bv}
    execution = {
        "events_a": len(events_a),
        "events_b": len(events_b),
        "first_divergence_index": int(divergence),
    }
    output = {
        "stdout_count_a": len(outputs_a["stdout"]),
        "stdout_count_b": len(outputs_b["stdout"]),
        "stderr_count_a": len(outputs_a["stderr"]),
        "stderr_count_b": len(outputs_b["stderr"]),
        "stdout_tail_a": outputs_a["stdout"][-ta:],
        "stdout_tail_b": outputs_b["stdout"][-ta:],
        "stderr_tail_a": outputs_a["stderr"][-ta:],
        "stderr_tail_b": outputs_b["stderr"][-ta:],
    }
    if summary_a["status"] != summary_b["status"]:
        hint = "Run outcomes differ; inspect finish step and error/output tails first."
    elif divergence >= 0:
        hint = "Execution diverged; inspect event stream near first_divergence_index."
    elif config_deltas:
        hint = "Config changed with stable execution shape."
    else:
        hint = "Runs are closely aligned at inspected granularity."
    return {
        "run_a": summary_a,
        "run_b": summary_b,
        "config_deltas": config_deltas,
        "execution": execution,
        "output": output,
        "hint": hint,
    }


def _handle_trace_diff(args):
    return _trace_views.handle_trace_diff(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        build_run_diff=_build_run_diff,
        shorten=_shorten,
        print_branding_stamp=_print_branding_stamp,
    )


def _find_recent_success_for_command(command_text, exclude_run_id):
    target = str(command_text or "").strip()
    if not target:
        return None
    exclude = str(exclude_run_id or "").strip()
    for record in _list_all_run_records():
        if str(record.get("run_id") or "").strip() == exclude:
            continue
        if _record_effective_status(record) != "success":
            continue
        if str(record.get("command") or "").strip() == target:
            return record
    return None


def _build_run_diagnosis(record, events, tail_lines=20):
    status = _record_effective_status(record)
    exit_code = int(record.get("exit_code") or 0)
    outputs = _collect_output_lines(events)
    stderr_lines = outputs["stderr"]
    stdout_lines = outputs["stdout"]
    t = max(1, int(tail_lines or 20))
    proxy_failed = any(
        str(event.get("event_type") or "") == "observe_proxy_response" and str(event.get("outcome") or "") == "failed"
        for event in events
    )
    timeout_seen = any(
        str(event.get("event_type") or "") == "observe_run_finished"
        and str((event.get("payload") or {}).get("error") or "") == "timeout_expired"
        for event in events
    )
    if status == "success":
        category = "success"
    elif timeout_seen or exit_code == 124:
        category = "timeout"
    elif proxy_failed:
        category = "proxy_error"
    elif exit_code != 0:
        category = "nonzero_exit"
    elif stderr_lines and len(stderr_lines) >= len(stdout_lines):
        category = "stderr_heavy"
    elif not stderr_lines and not stdout_lines:
        category = "no_output"
    else:
        category = "unknown"

    suggestions = []
    if category == "success":
        suggestions.append("Run is healthy; compare with alternate configurations using `hive trace diff`.")
    elif category == "timeout":
        suggestions.append("Replay with a higher timeout using `hive trace replay <run_id> --timeout-sec <n>`.")
    elif category == "proxy_error":
        suggestions.append("Verify upstream base URL and auth, then replay with `hive trace run --proxy --proxy-upstream ...`.")
    elif category == "nonzero_exit":
        suggestions.append("Inspect stderr tail and exit code, then replay after adjusting command/env.")
    elif category == "stderr_heavy":
        suggestions.append("Prioritize stderr output review and compare against a known-success run.")
    elif category == "no_output":
        suggestions.append("Confirm command emitted output and did not exit before logging.")
    else:
        suggestions.append("Inspect run with `hive trace show` and compare with `hive trace diff`.")

    baseline = _find_recent_success_for_command(record.get("command"), record.get("run_id"))
    baseline_run_id = str((baseline or {}).get("run_id") or "").strip()
    if baseline_run_id:
        suggestions.append(f"Compare against prior success: `hive trace diff {baseline_run_id} {record.get('run_id')}`.")

    return {
        "run_id": str(record.get("run_id") or ""),
        "status": status,
        "category": category,
        "exit_code": exit_code,
        "duration_ms": int(record.get("duration_ms") or 0),
        "event_count": len(events),
        "stderr_tail": stderr_lines[-t:],
        "stdout_tail": stdout_lines[-t:],
        "baseline_run_id": baseline_run_id or None,
        "recommendations": suggestions,
    }


def _handle_trace_diagnose(args):
    return _trace_views.handle_trace_diagnose(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        build_run_diagnosis=_build_run_diagnosis,
        print_branding_stamp=_print_branding_stamp,
    )


def _handle_trace_insight_explain(args):
    return _insight_ops.handle_trace_insight_explain(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        build_run_summary=_build_run_summary,
        build_run_diagnosis=_build_run_diagnosis,
    )


def _handle_trace_insight_drift(args):
    return _insight_ops.handle_trace_insight_drift(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_run_record=_read_run_record,
        read_trace_events_for_run=_read_trace_events_for_run,
        build_run_diff=_build_run_diff,
    )


def _handle_trace_insight_health(args):
    return _insight_ops.handle_trace_insight_health(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        parse_duration_window_ms=_parse_duration_window_ms,
        now_ms_fn=_now_ms,
        list_all_run_records=_list_all_run_records,
        record_effective_status=_record_effective_status,
        record_runtime_state=_record_runtime_state,
        read_trace_events_for_run=_read_trace_events_for_run,
        build_run_diagnosis=_build_run_diagnosis,
    )


def _handle_trace_open(args):
    return _trace_views.handle_trace_open(
        args,
        maybe_auto_reconcile=_maybe_auto_reconcile,
        read_run_record=_read_run_record,
        open_run_in_ui=_open_run_in_ui,
    )


def _handle_trace_archive(args):
    return _ops_lifecycle.handle_trace_archive(args, archive_run_record=_archive_run_record)


def _handle_trace_unarchive(args):
    return _ops_lifecycle.handle_trace_unarchive(args, archive_run_record=_archive_run_record)


def _handle_trace_prune(args):
    return _ops_lifecycle.handle_trace_prune(
        args,
        parse_age_window_ms=_parse_age_window_ms,
        now_ms_fn=_now_ms,
        list_all_run_records=_list_all_run_records,
        record_is_archived=_record_is_archived,
        record_reference_time_ms=_record_reference_time_ms,
        run_file=_run_file,
        drop_events_for_runs=_drop_events_for_runs,
    )


def _handle_trace_reconcile(args):
    return _ops_lifecycle.handle_trace_reconcile(
        args,
        config_module=hive_config,
        parse_duration_window_ms=_parse_duration_window_ms,
        reconcile_stale_running_runs=_reconcile_stale_running_runs,
    )


def _check_python_runtime():
    major, minor = sys.version_info[:2]
    ok = (major, minor) >= (3, 10)
    msg = f"Python {major}.{minor}"
    if not ok:
        msg += " (requires >= 3.10)"
    return ok, msg


def _check_trace_home():
    try:
        root = _trace_home()
        root.mkdir(parents=True, exist_ok=True)
        probe = root / ".write_probe"
        probe.write_text("ok", encoding="utf-8")
        probe.unlink(missing_ok=True)
        return True, f"trace home writable: {root}"
    except Exception as exc:
        return False, f"trace home not writable: {_shorten(exc, limit=200)}"


def _check_ui_reachable(ui_url):
    url = str(ui_url or "").strip() or "http://localhost:5173"
    host = "localhost"
    port = 5173
    try:
        stripped = url.split("://", 1)[-1]
        host_port = stripped.split("/", 1)[0]
        if ":" in host_port:
            host, raw_port = host_port.rsplit(":", 1)
            port = int(raw_port)
        else:
            host = host_port
            port = 80
    except Exception:
        host, port = "localhost", 5173
    try:
        with socket.create_connection((host, port), timeout=1.2):
            return True, f"ui reachable: {host}:{port}"
    except Exception:
        return False, f"ui unreachable: {host}:{port}"


def _handle_doctor(args):
    checks = [
        ("python", *_check_python_runtime()),
        ("trace_home", *_check_trace_home()),
        ("ui", *_check_ui_reachable(args.ui_url)),
    ]
    all_ok = True
    for name, ok, message in checks:
        status = "OK" if ok else "WARN"
        status_text = _term.ok(status) if ok else _term.warn(status)
        print(f"[{status_text}] {_term.key(name)}: {message}")
        if not ok and name in {"python", "trace_home"}:
            all_ok = False
    return 0 if all_ok else 2


def _handle_quickstart(args):
    ui_url = _resolve_ui_url_for_command(args)
    result = run_command_observed(
        ["python", "-c", "print('hive-trace quickstart ok')"],
        run_name="hive-quickstart",
        agent_id="observer.quickstart",
        open_ui_url=ui_url,
    )
    print(f"{_term.key('quickstart_complete')}={_term.ok('true')}")
    print(f"{_term.key('run_id')}={result['run_id']}")
    print(_term.hint("next=hive trace ls"))
    print(_term.hint(f"next=hive trace show {result['run_id']}"))
    if ui_url:
        print(_term.hint(f"next=hive trace open {result['run_id']} --ui-url {ui_url}"))
    return int(result["exit_code"])


def main(argv=None):
    raw_argv = list(argv if argv is not None else sys.argv[1:])
    color_mode = ""
    for idx, token in enumerate(raw_argv):
        t = str(token or "").strip()
        if t.startswith("--color="):
            color_mode = t.split("=", 1)[1].strip()
            break
        if t == "--color" and idx + 1 < len(raw_argv):
            color_mode = str(raw_argv[idx + 1] or "").strip()
            break
    _term.configure(color_mode=color_mode or "auto", stream=sys.stdout)
    if raw_argv:
        # Legacy compatibility: `hive run -- ...` => `hive trace run -- ...`
        if raw_argv[0] == "run":
            raw_argv = ["trace", "run", *raw_argv[1:]]
        # Legacy compatibility: `hive trace-events ...`
        if raw_argv and raw_argv[0] == "trace-events":
            return _handle_legacy_trace_events_argv(raw_argv[1:])
    if "--help-verbose" in raw_argv or ("--help" in raw_argv and "-v" in raw_argv):
        return print_verbose_help(raw_argv)

    parser = _build_parser()
    args = parser.parse_args(raw_argv)
    _term.configure(color_mode=str(getattr(args, "color", "auto") or "auto"), stream=sys.stdout)

    if args.command == "quickstart":
        return _finalize_cli_output(_handle_quickstart(args), args)
    if args.command == "doctor":
        return _finalize_cli_output(_handle_doctor(args), args)

    if args.command == "trace":
        if args.trace_command == "run":
            return _finalize_cli_output(_handle_trace_run(args), args)
        if args.trace_command == "replay":
            return _finalize_cli_output(_handle_trace_replay(args), args)
        if args.trace_command == "replay-plan":
            return _finalize_cli_output(_handle_trace_replay_plan(args), args)
        if args.trace_command == "checkpoint":
            return _finalize_cli_output(_handle_trace_checkpoint(args), args)
        if args.trace_command == "anchors":
            return _finalize_cli_output(_handle_trace_anchors(args), args)
        if args.trace_command == "ls":
            return _finalize_cli_output(_handle_trace_ls(args), args)
        if args.trace_command == "show":
            return _finalize_cli_output(_handle_trace_show(args), args)
        if args.trace_command == "tail":
            return _finalize_cli_output(_handle_trace_tail(args), args)
        if args.trace_command == "flow":
            if args.flow_command == "ls":
                return _finalize_cli_output(_handle_trace_flow_ls(args), args)
            if args.flow_command == "show":
                return _finalize_cli_output(_handle_trace_flow_show(args), args)
            if args.flow_command == "steps":
                return _finalize_cli_output(_handle_trace_flow_steps(args), args)
            if args.flow_command == "diff":
                return _finalize_cli_output(_handle_trace_flow_diff(args), args)
            parser.print_help()
            return _finalize_cli_output(1, args)
        if args.trace_command == "tei":
            if args.tei_command == "validate":
                return _finalize_cli_output(_tei_cli.handle_trace_tei_validate(
                    args,
                    validate_tei_event=validate_tei_event,
                    validate_tei_batch=validate_tei_batch,
                ), args)
            if args.tei_command == "ingest":
                return _finalize_cli_output(_tei_cli.handle_trace_tei_ingest(
                    args,
                    ingest_tei_event=ingest_tei_event,
                    ingest_tei_events=ingest_tei_events,
                ), args)
            parser.print_help()
            return _finalize_cli_output(1, args)
        if args.trace_command == "artifacts":
            if args.artifacts_command == "ls":
                return _finalize_cli_output(_handle_trace_artifacts_ls(args), args)
            if args.artifacts_command == "show":
                return _finalize_cli_output(_handle_trace_artifacts_show(args), args)
            if args.artifacts_command == "replay":
                return _finalize_cli_output(_handle_trace_artifacts_replay(args), args)
            parser.print_help()
            return _finalize_cli_output(1, args)
        if args.trace_command == "insight":
            if args.insight_command == "explain":
                return _finalize_cli_output(_handle_trace_insight_explain(args), args)
            if args.insight_command == "drift":
                return _finalize_cli_output(_handle_trace_insight_drift(args), args)
            if args.insight_command == "health":
                return _finalize_cli_output(_handle_trace_insight_health(args), args)
            parser.print_help()
            return _finalize_cli_output(1, args)
        if args.trace_command == "ops":
            if args.ops_command == "archive":
                return _finalize_cli_output(_handle_trace_archive(args), args)
            if args.ops_command == "unarchive":
                return _finalize_cli_output(_handle_trace_unarchive(args), args)
            if args.ops_command == "prune":
                return _finalize_cli_output(_handle_trace_prune(args), args)
            if args.ops_command == "reconcile":
                return _finalize_cli_output(_handle_trace_reconcile(args), args)
            if args.ops_command == "export":
                return _finalize_cli_output(_handle_trace_export(args), args)
            if args.ops_command == "import":
                return _finalize_cli_output(_handle_trace_import(args), args)
            parser.print_help()
            return _finalize_cli_output(1, args)
        if args.trace_command == "summary":
            return _finalize_cli_output(_handle_trace_summary(args), args)
        if args.trace_command == "diff":
            return _finalize_cli_output(_handle_trace_diff(args), args)
        if args.trace_command == "diagnose":
            return _finalize_cli_output(_handle_trace_diagnose(args), args)
        if args.trace_command == "open":
            return _finalize_cli_output(_handle_trace_open(args), args)
        parser.print_help()
        return _finalize_cli_output(1, args)

    parser.print_help()
    return 1


if __name__ == "__main__":
    raise SystemExit(main())
    run_id = _build_run_id("observe-run")

