"""The page's routes, as a plain function from a path to a response, so the tests drive it without a socket (X6). / the sources: agents and kept runs /s// a source's work items /s//item/ an item's walkthrough ("-": the agent-level log) /s//call/ one turn, read against its log /s//event/ one log event in full """ import json import sqlite3 from dataclasses import dataclass, field from importlib import resources from typing import Any from urllib.parse import quote, unquote, urlsplit from jinja2 import Environment, PackageLoader, StrictUndefined from nisse_core.models import calls from nisse_ui import walk from nisse_ui.sources import Source, StateReader, UnknownSchema, call_record AGENT_LEVEL = "-" # the item id that stands for the agent-level log # Everything shown is untrusted text (L11): escaped by the templates, and # the browser runs no script at all, loads nothing from elsewhere, and # sends no referrer. HEADERS = { "Content-Security-Policy": ( "default-src 'none'; style-src 'self'; img-src 'self'; base-uri 'none'; " "form-action 'none'; frame-ancestors 'none'" ), "X-Content-Type-Options": "nosniff", "Referrer-Policy": "no-referrer", "Cache-Control": "no-store", } @dataclass(frozen=True) class Response: status: int body: bytes content_type: str = "text/html; charset=utf-8" headers: dict[str, str] = field(default_factory=lambda: dict(HEADERS)) def _when(ts: Any) -> str: return str(ts)[:19].replace("T", " ") if ts else "—" def _num(n: Any) -> str: return f"{n:,}" if isinstance(n, int) and not isinstance(n, bool) else "—" def _pretty(text: Any) -> str: """JSON text indented, anything else as it is.""" if not isinstance(text, str): return json.dumps(text, indent=2, ensure_ascii=False) try: return json.dumps(json.loads(text), indent=2, ensure_ascii=False) except ValueError: return text def _clip(text: Any, limit: int = 300) -> str: text = str(text or "") return ( text if len(text) <= limit else text[:limit] + f" … [{len(text) - limit:,} more]" ) def summary(kind: str, payload: dict[str, Any]) -> str: """One event as a line of text: what it said or did.""" match kind: case "user_message" | "assistant_text" | "fact": return str(payload.get("text", "")) case "tool_call": args = json.dumps(payload.get("arguments", {}), ensure_ascii=False) return f"{payload.get('tool', '?')}({args})" case "tool_result": mark = "ok" if payload.get("success") else "FAILED" return f"{mark}: {payload.get('result', '')}" case "plan_submitted": return str(payload.get("plan", "")) case "gate_opened": return f"{payload.get('gate', '')}: {payload.get('reason', '')}" case "delegation_started": return f"({payload.get('model', '')}) {payload.get('brief', '')}" case "delegation_completed": return f"[{payload.get('exit_status', '')}] {payload.get('handback', '')}" case "compaction": first, last = (payload.get("replaces") or ["?", "?"])[:2] return f"replaces {first}–{last}: {payload.get('summary', '')}" case "action_verdict": return ( f"{payload.get('tool', '')}: {payload.get('effect', '')} → " f"{payload.get('decision', '')} ({payload.get('reason', '')})" ) case _: return json.dumps(payload, ensure_ascii=False) def _reply_line(reply: walk.Reply) -> str: calls_made = "; ".join(f"{tool}({args})" for tool, args in reply.calls) text = " ".join(reply.text.split()) return " · ".join(p for p in (text, calls_made) if p) or "(no reply)" class App: def __init__(self, sources: list[Source]) -> None: self.sources = list(sources) self.by_name = {s.name: s for s in sources} env = Environment( loader=PackageLoader("nisse_ui", "templates"), autoescape=True, # always: every page shows untrusted text undefined=StrictUndefined, trim_blocks=True, lstrip_blocks=True, ) env.filters.update( u=lambda s: quote(str(s), safe=""), when=_when, num=_num, pretty=_pretty, clip=_clip, reply_line=_reply_line, ) env.globals.update(summary=summary, AGENT_LEVEL=AGENT_LEVEL) self.env = env def page(self, template: str, status: int = 200, **values: Any) -> Response: html = self.env.get_template(template).render(**values) return Response(status, html.encode()) def problem(self, status: int, title: str, detail: str) -> Response: return self.page("problem.html", status, title=title, detail=detail) def handle(self, raw_path: str) -> Response: path = urlsplit(raw_path).path parts = [unquote(p) for p in path.split("/")[1:]] if parts in ([], [""]): return self.home() if parts == ["static", "style.css"]: css = resources.files("nisse_ui").joinpath("static/style.css").read_bytes() return Response(200, css, "text/css; charset=utf-8") if len(parts) >= 2 and parts[0] == "s": source = self.by_name.get(parts[1]) if source is None: return self.problem(404, "No such source", parts[1]) rest = parts[2:] try: if rest in ([], [""]): return self.items(source) if len(rest) == 2 and rest[0] == "item": item = None if rest[1] == AGENT_LEVEL else rest[1] return self.item(source, item) if ( len(rest) == 2 and rest[0] in ("call", "event") and rest[1].isdigit() ): if rest[0] == "call": return self.call(source, int(rest[1])) return self.event(source, int(rest[1])) except UnknownSchema as exc: return self.problem( 409, "A state store this page doesn't know", str(exc) ) except calls.UnknownLayout as exc: return self.problem( 409, "A call record this page doesn't know", str(exc) ) except sqlite3.Error as exc: return self.problem(503, "Couldn't read the store", str(exc)) return self.problem(404, "Not found", path) # --- pages --------------------------------------------------------------------- def home(self) -> Response: rows = [] for source in self.sources: row: dict[str, Any] = {"source": source, "count": None, "note": ""} try: with StateReader(source.state_db) as state: row["count"] = state.count_items() row["schema"] = state.schema_version except (UnknownSchema, sqlite3.Error) as exc: row["note"] = str(exc) row["record"] = source.calls_db.is_file() rows.append(row) return self.page("sources.html", rows=rows) def items(self, source: Source) -> Response: with StateReader(source.state_db) as state: return self.page( "items.html", source=source, items=state.items(), active=state.active_item(), ) def item(self, source: Source, item_id: str | None) -> Response: with StateReader(source.state_db) as state: if item_id is not None and state.item(item_id) is None: return self.problem(404, "No such work item", item_id) try: with call_record(source) as reader: view = walk.walkthrough(state, reader, item_id) except calls.UnknownLayout as exc: view = walk.walkthrough(state, None, item_id, record_note=str(exc)) return self.page("item.html", source=source, view=view) def call(self, source: Source, call_id: int) -> Response: with StateReader(source.state_db) as state, call_record(source) as reader: if reader is None: return self.problem(404, "No call record", str(source.calls_db)) view = walk.turn(state, reader, call_id) if view is None: return self.problem(404, "No such call", str(call_id)) return self.page("call.html", source=source, view=view) def event(self, source: Source, event_id: int) -> Response: with StateReader(source.state_db) as state: found = state.event(event_id) if found is None: return self.problem(404, "No such event", str(event_id)) return self.page("event.html", source=source, event=found)