#!/usr/bin/env python3
"""Ticket commitment for tech.daimon.llm@1 (kind 0). Stdlib only.

commitment = sha256("daimon.llm.ticket.v1" || 0x00 || agent_id(32) || u32le len(model) || model
                    || u32le len(body) || body)
body = RFC 8785 canonical JSON of {"messages": [...]}

Usage:
  python3 daimon_llm_commitment.py <identity_id> <model> --prompt "text"
  python3 daimon_llm_commitment.py <identity_id> <model> --messages @messages.json
  add --ptb to print [b0,b1,...] for `sui client ptb`
"""
import argparse, hashlib, json, struct, sys

def jcs(v):
    if v is None: return "null"
    if v is True: return "true"
    if v is False: return "false"
    if isinstance(v, str): return json.dumps(v, ensure_ascii=False)
    if isinstance(v, int): return str(v)
    if isinstance(v, list): return "[" + ",".join(jcs(x) for x in v) + "]"
    if isinstance(v, dict):
        keys = sorted(v, key=lambda k: k.encode("utf-16-be"))
        return "{" + ",".join(json.dumps(k, ensure_ascii=False) + ":" + jcs(v[k]) for k in keys) + "}"
    raise TypeError("only strings, ints, lists and objects are allowed")

def commitment(agent_hex, model, messages):
    h = agent_hex[2:] if agent_hex.startswith("0x") else sys.exit("identity_id must start with 0x")
    agent = bytes.fromhex(h.rjust(64, "0"))
    m = model.encode("utf-8")
    body = jcs({"messages": messages}).encode("utf-8")
    return hashlib.sha256(b"daimon.llm.ticket.v1" + b"\x00" + agent
                          + struct.pack("<I", len(m)) + m + struct.pack("<I", len(body)) + body).digest()

if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("identity_id"); ap.add_argument("model")
    g = ap.add_mutually_exclusive_group(required=True)
    g.add_argument("--prompt"); g.add_argument("--messages")
    ap.add_argument("--ptb", action="store_true")
    a = ap.parse_args()
    if a.prompt is not None:
        msgs = [{"role": "user", "content": a.prompt}]
    else:
        raw = a.messages
        msgs = json.loads(open(raw[1:], encoding="utf-8").read() if raw.startswith("@") else raw)
    d = commitment(a.identity_id, a.model, msgs)
    print("[" + ",".join(str(b) for b in d) + "]" if a.ptb else d.hex())
