SPB Git

spb/zyquo-agent Public MIT

The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.

Swift 94.7% Shell 4.1% Python 0.7% Makefile 0.5%
6.1 KB · 135 lines python
Raw Blame History
1#!/usr/bin/env python32#3#  eval-inspect.py4#  Zyquo Agent5#6#  Author: Simon-Pierre Boucher7#  Mail: contact@spboucher.ai8#9#  Phase 7.2 trajectory inspector: given a task workspace, parses10#  .zyquo/transcript.json and .zyquo/audit.jsonl and asserts11#    - the run outcome matches the expectation,12#    - step indices are coherent and the final step of a completed run13#      carries no tool calls,14#    - EVERY bash/osascript/write_file/edit_file invocation in the15#      transcript has a matching audit entry (audit completeness),16#    - denied actions are audited as denied,17#    - optional expectations: a CompactionRecord, plan snapshots, a failing18#      invocation (failure→recovery trajectories), and a regex that must19#      never appear in any EXECUTED (non-denied) audit payload.20#  Exit 0 = all assertions hold.21#2223import argparse24import json25import os26import re27import sys282930def main() -> int:31    parser = argparse.ArgumentParser(description="Zyquo Agent trajectory inspector")32    parser.add_argument("workspace")33    parser.add_argument("--expect-outcome", default="completed")34    parser.add_argument("--expect-compaction", action="store_true")35    parser.add_argument("--expect-plan", action="store_true")36    parser.add_argument("--expect-error-invocation", action="store_true")37    parser.add_argument("--forbid-executed-payload", default=None,38                        help="regex that must not match any non-denied audit payload")39    parser.add_argument("--max-steps", type=int, default=None)40    args = parser.parse_args()4142    fails: list[str] = []43    transcript_path = os.path.join(args.workspace, ".zyquo", "transcript.json")44    audit_path = os.path.join(args.workspace, ".zyquo", "audit.jsonl")4546    try:47        with open(transcript_path) as handle:48            doc = json.load(handle)49    except Exception as error:50        print(f"  INSPECT FAIL: transcript unreadable: {error}")51        return 15253    audit = []54    if os.path.exists(audit_path):55        with open(audit_path) as handle:56            for line in handle:57                line = line.strip()58                if line:59                    audit.append(json.loads(line))6061    # ---- Outcome ---------------------------------------------------------62    outcome = doc.get("outcome") or {}63    outcome_key = next(iter(outcome), None) if isinstance(outcome, dict) else str(outcome)64    if args.expect_outcome and outcome_key != args.expect_outcome:65        fails.append(f"outcome is {outcome_key!r}, expected {args.expect_outcome!r}")6667    # ---- Step coherence ---------------------------------------------------68    steps = doc.get("steps", [])69    indices = [step.get("index") for step in steps]70    if indices != sorted(indices):71        fails.append(f"step indices out of order: {indices}")72    if args.max_steps is not None and len(steps) > args.max_steps:73        fails.append(f"{len(steps)} steps exceed the {args.max_steps}-step cap")74    if outcome_key == "completed" and steps and steps[-1].get("toolInvocations"):75        fails.append("final step of a completed run still carries tool invocations")7677    # ---- Audit completeness ------------------------------------------------78    def audited(kind: str, predicate) -> bool:79        return any(entry.get("actionKind") == kind and predicate(entry) for entry in audit)8081    for step in steps:82        for invocation in step.get("toolInvocations", []):83            call = invocation.get("call", {})84            name = call.get("name")85            result = invocation.get("result") or {}86            content = result.get("content", "")87            if name == "bash":88                try:89                    command = json.loads(call.get("argumentsJSON", "{}")).get("command", "")90                except Exception:91                    command = ""92                match = lambda e: e.get("payload", "") == command \93                    or command in e.get("payload", "") or e.get("payload", "") in command94                if not audited("bash", match):95                    fails.append(f"step {step.get('index')}: bash call missing from audit: {command[:100]!r}")96                if content.startswith("Command not run") and not audited(97                        "bash", lambda e: e.get("ruling") == "denied" and e.get("payload", "") == command):98                    fails.append(f"step {step.get('index')}: denied bash call not audited as denied: {command[:100]!r}")99            elif name == "osascript":100                if not audited("osascript", lambda e: True):101                    fails.append(f"step {step.get('index')}: osascript call missing from audit")102            elif name in ("write_file", "edit_file"):103                if not audited(name, lambda e: True):104                    fails.append(f"step {step.get('index')}: {name} call missing from audit")105106    # ---- Optional expectations ----------------------------------------------107    compactions = doc.get("compactions") or []108    if args.expect_compaction and not compactions:109        fails.append("no CompactionRecord in the transcript")110    if args.expect_plan and not (doc.get("planSnapshots") or []):111        fails.append("no plan snapshots in the transcript")112    if args.expect_error_invocation:113        saw_error = any((invocation.get("result") or {}).get("isError")114                        for step in steps for invocation in step.get("toolInvocations", []))115        if not saw_error:116            fails.append("no failing tool invocation found (expected a failure→recovery trajectory)")117118    if args.forbid_executed_payload:119        forbidden = re.compile(args.forbid_executed_payload)120        for entry in audit:121            if forbidden.search(entry.get("payload", "")) and entry.get("ruling") != "denied":122                fails.append("FORBIDDEN payload was executed: "123                             f"{entry.get('payload', '')[:100]!r} (ruling={entry.get('ruling')})")124125    print(f"  transcript: {len(steps)} steps, outcome={outcome_key}, "126          f"audit={len(audit)} entries, compactions={len(compactions)}, "127          f"planSnapshots={len(doc.get('planSnapshots') or [])}")128    for fail in fails:129        print(f"  INSPECT FAIL: {fail}")130    return 1 if fails else 0131132133if __name__ == "__main__":134    sys.exit(main())135