mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
66 lines
3.1 KiB
Python
66 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""agent_reports.py — save each Agent-tool subagent's FINAL message (its full prose report) to a file (P32 T3, 2026-09-05).
|
|
|
|
`agent_verdicts.py` extracts only the one-line JSON verdict. The agent's final message also carries the evidence a
|
|
successor needs to route a NEAR or bank a MATCH — the levers that closed it, the levers measured INERT, the residual's
|
|
mechanism, TU-plumbing warnings ("do NOT hoist this extern"). Those lived only inside the transcript JSONL (never cat
|
|
one into a session) until this tool: it writes one Markdown file per agent, named by the function and arm.
|
|
|
|
python3 tools/agent_reports.py <subagents-dir> --out <dir>
|
|
# -> <dir>/<fn>__<arm>__<agentId>.md (header: model, start/end, target; body: the final assistant message)
|
|
|
|
Agents whose prompt names no function are written as UNKNOWN__… (R32: nothing silently skipped).
|
|
"""
|
|
import argparse, glob, json, os, re
|
|
|
|
|
|
def parse(tp):
|
|
first = last = model = None; prompt = ""; final = ""
|
|
for line in open(tp, errors="replace"):
|
|
try:
|
|
o = json.loads(line)
|
|
except Exception:
|
|
continue
|
|
ts = o.get("timestamp")
|
|
if ts:
|
|
first = first or ts; last = ts
|
|
t = o.get("type"); m = o.get("message") or {}
|
|
if t == "user" and not prompt:
|
|
c = m.get("content")
|
|
if isinstance(c, str):
|
|
prompt = c
|
|
elif isinstance(c, list):
|
|
prompt = " ".join(x.get("text", "") for x in c if isinstance(x, dict) and x.get("type") == "text")
|
|
if t == "assistant":
|
|
model = model or m.get("model")
|
|
for x in m.get("content") or []:
|
|
if x.get("type") == "text" and x["text"].strip():
|
|
final = x["text"]
|
|
fn = re.findall(r"func_[0-9A-Fa-f]{8}", prompt)
|
|
arm = re.search(r"\bArm:\s*(\w+)", prompt)
|
|
binm = re.search(r"in binary (\S+)", prompt)
|
|
return dict(model=model, start=first, end=last, fn=fn[0] if fn else "UNKNOWN",
|
|
arm=(arm.group(1) if arm else "unknown"), binary=(binm.group(1) if binm else "?"),
|
|
prompt=prompt, final=final)
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("subagents_dir"); ap.add_argument("--out", required=True)
|
|
a = ap.parse_args(); os.makedirs(a.out, exist_ok=True); n = 0
|
|
for tp in sorted(glob.glob(os.path.join(a.subagents_dir, "agent-*.jsonl"))):
|
|
tag = os.path.basename(tp)[len("agent-"):-len(".jsonl")]
|
|
r = parse(tp)
|
|
outp = os.path.join(a.out, f"{r['fn']}__{r['arm']}__{tag}.md")
|
|
with open(outp, "w") as f:
|
|
f.write(f"# {r['binary']}:{r['fn']} — arm {r['arm']} — model {r['model']}\n")
|
|
f.write(f"agent {tag} · start {r['start']} · end {r['end']} · transcript {tp}\n\n")
|
|
f.write("## Task prompt (first 600 chars)\n\n" + r["prompt"][:600] + "\n\n## Final report (verbatim)\n\n" + r["final"] + "\n")
|
|
n += 1
|
|
print(f"{r['binary']:13} {r['fn']:14} {r['arm']:7} -> {outp}")
|
|
print(f"agents={n} -> {a.out}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|