Skip to content

About

Autonomous dispute resolution for the agent economy. Built on GenLayer, AgentCourt investigates machine-readable disputes using verifiable evidence, independent AI reasoning, adversarial review, and consensus before triggering deterministic on-chain settlement.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

AgentCourt

Autonomous dispute resolution for the agent economy — powered by GenLayer Intelligent Contracts.

When agents disagree, let the evidence speak.

Live app (GenLayer Studionet, chain 61999): https://agent-court-genlayer.vercel.app/

AgentCourt is a decentralized arbitration protocol that turns a machine-readable dispute into an executable investigation:

Claim → Evidence → Investigation → Independent Reasoning → Adversarial Review → Consensus → Verdict → Settlement

Instead of a single LLM answer or a centralized moderator, four independent evaluator roles reason over the same evidence, an adversarial reviewer challenges the emerging majority, GenLayer validators re-derive the consensus exactly and independently re-check decisive verdicts, and the final verdict is derived on-chain — never supplied by a caller.


The problem

Autonomous agents and protocols transact with each other constantly — datasets delivered, APIs purchased, escrows funded, SLAs promised. When they disagree, today's options are human support queues, private arbitrators, or a single opaque AI call. None of that scales to machine-to-machine commerce, and none of it is auditable.

AgentCourt provides recourse as a programmable, transparent primitive:

  • Evidence is explicit and referenced by ID.
  • Reasoning is independent, multi-role, and adversarially reviewed.
  • Uncertainty is represented (UNVERIFIABLE, REVIEW) instead of forced into a binary.
  • Settlement is deterministic and reads only the on-chain verdict — fail-closed when review is required.

What it does (the problem it solves)

Pain today AgentCourt answer
"The AI said so" with no process Full evidence → evaluators → adversarial → consensus trail on-chain
One LLM call is trivially gamed 4 roles + adversarial + sandboxed validator check (run_nondet)
Caller can submit a favorable verdict Caller input ≠ final verdict — finalize_verdict(dispute_id) takes no verdict
Uncertain cases auto-settle unsafely Fail-closed states: EVALUATION_FAILED / INCONCLUSIVE / DISPUTED freeze settlement
Escrow can't consume AI opinions Machine-readable verdict: {verdict, confidence, resolution, reviewRequired}

Screenshots

All shots are from the live deployment on GenLayer Studionet (chain 61999), walking one dispute from creation to a fail-closed UNVERIFIABLE verdict.

Dashboard

Registry of on-chain disputes with totals, search/filter, and per-dispute lifecycle badges.

AgentCourt dashboard: dispute registry with totals and status badges

Dispute detail

Workflow actions gated by lifecycle state, claim metadata, the verdict derived from evaluation, and the event timeline.

Dispute detail with workflow actions, claim metadata, derived verdict, and event timeline

Evidence explorer

Submitted evidence with type, source, and verification status, plus provenance stats.

Evidence explorer showing verified evidence rows and source statistics

Evaluator results

Four independent evaluator roles, each with its verdict, confidence, and reasoning hash.

Evaluator results with four independent role cards and a majority REVIEW verdict

Adversarial review

The reviewer challenges the emerging majority and itemizes the disputed assumptions.

Adversarial review challenging consensus with three finding cards

Consensus

Vote breakdown, agreement ratio, and material-disagreement detection with the consensus reasoning.

Consensus breakdown with disagreement detected

Verdict explorer

The machine-readable verdict that escrow and settlement contracts consume.

Verdict explorer showing an UNVERIFIABLE verdict with freeze resolution

Deployed app

Live frontend https://agent-court-genlayer.vercel.app/
Network GenLayer Studionet — Chain ID 61999
Explorer https://explorer-studio.genlayer.com

The live frontend is bound to the two contracts below via VITE_AGENTCOURT_CORE / VITE_RESOLUTION_MANAGER (see .env.example).

Deployed contracts (GenLayer Studionet, Chain ID 61999)

Contract Address
AgentCourtCore (AgentCourt.py) 0x2cC0B2285E67cA33db09bDC1Eec9f64F8ea72F3a — import in Studio
ResolutionManager (ResolutionManager.py) 0xC7225962aB73576087Ff157798Ffb3967E36Ec11 — import in Studio

set_core is wired for this deployment (manager → core verified via get_core; manager/core owner: 0x5B36…4c89). There is no Solidity path — both contracts are Python GenLayer Intelligent Contracts.

Verification evidence

To confirm the deployed contracts respond and are wired correctly:

CORE=0x2cC0B2285E67cA33db09bDC1Eec9f64F8ea72F3a \
MANAGER=0xC7225962aB73576087Ff157798Ffb3967E36Ec11 \
./scripts/verification/verify.sh
# → checks get_dispute_count, get_core wiring, get_owner, is_paused; prints "Verification PASSED"

Staking escrow

Each party to a dispute can bond its stake on-chain before the window closes:

deposit_stake(dispute_id)   ← @gl.public.write.payable, sender must be claimant/respondent,
                              value ≥ dispute.stake, one deposit per party
get_escrow(dispute_id)      ← deposited flag + amounts per party, released/payouts record
execute_settlement(id)      ← winner refunded its bond and receives the slashed loser bond;
                              reviewRequired/UNVERIFIABLE/REVIEW keep funds escrowed (fail-closed);
                              settlement is idempotent — no double release

Live verification scripts (run on Studionet):

  • scripts/verification/smoke_staking.mjs — deposit, get_escrow hold, settlement blocked pre-verdict
  • scripts/verification/e2e_staking.mjs — both parties stake → live evaluation → finalize → settle (or fail-closed INCONCLUSIVE retention)
  • tests/unit/test_escrow_staking.py — 13 unit cases incl. release math, double-release guard, review-freeze, unauthorized/insufficient/double deposits, case-insensitive party matching

How to use it

1. Install

git clone https://github.com/habte-selassie27/AgentCourt.git
cd AgentCourt
npm install
cp .env.example .env   # set VITE_AGENTCOURT_CORE / VITE_RESOLUTION_MANAGER if you redeploy

2. Run the frontend

npm run dev

Connect a wallet on chain 61999 (the UI offers to switch/add the network), then walk the full lifecycle from the dashboard:

  1. Create dispute (parties, agreement hash, claim type, stake, deadline + evidence)
  2. Start investigation
  3. Request evaluation — live web fetch of evidence URLs + 4 LLM evaluators + adversarial review under validator consensus (takes minutes)
  4. Finalize verdict — no inputs; derived from the stored evaluation
  5. Execute settlement — only when reviewRequired is false and no appeal is open
  6. Appeal (optional, only from a live VERDICT) — Open appeal moves the dispute to APPEALED; re-run Request evaluation (allowed while the appeal is open), Finalize verdict to write a superseding, higher-version verdict, then the ResolutionManager owner calls Resolve appeal (compares core verdict versions); settlement is refused until that call closes the appeal. A settled (CLOSED) dispute is final and cannot be appealed

3. Run the checks

pytest tests/unit/ -v                 # 73 Python acceptance tests (IC workflow, caller isolation, appeal flow, escrow/staking)
npm test                              # vitest domain tests
npm run typecheck && npm run build    # TypeScript + Vite build
GENVM_VERSION=v0.3.0-rc7 genvm-lint check intelligent-contracts/core/agentcourt_core.py
GENVM_VERSION=v0.3.0-rc7 genvm-lint check intelligent-contracts/registry/resolution_manager.py

4. Integrate from TypeScript

import { getCourt } from './sdk';

const court = getCourt();
const address = await court.connectWallet((window as any).ethereum);

const disputeId = await court.createDispute({ /* respondent, agreementHash, claimType, description, stake, deadline, evidence[] */ });
await court.startInvestigation(disputeId);
await court.requestEvaluation(disputeId);            // minutes: LLM + validators
const verdict = await court.waitForVerdict(disputeId);
if (!verdict.reviewRequired) await court.executeSettlement(disputeId);

Full method tables, contract API, and error handling: docs/DEVELOPER_GUIDE.md.

Architecture

AgentCourtCore (Python IC)
  ├── disputes, evidence, evaluations, verdicts (TreeMap state)
  ├── request_evaluation → gl.vm.run_nondet
  │      ├── gl.nondet.web.get       (live HTTP evidence fetch)
  │      ├── gl.nondet.exec_prompt × 4 (neutral / claimant / respondent / auditor)
  │      └── gl.nondet.exec_prompt     (adversarial review)
  │            validators re-derive the consensus exactly
  │            and independently re-check decisive verdicts
  ├── finalize_verdict(dispute_id)     # NO verdict parameter
  └── execute_settlement(dispute_id)   # emits to ResolutionManager

ResolutionManager (Python IC)
  ├── set_core (one-time wiring)
  ├── open_appeal (core only) / resolve_appeal (owner only — compares core verdict versions)
  └── execute_settlement(dispute_id)   # reads core.get_verdict() via view()

Appeal path: openAppeal → requestEvaluation (re-run while APPEALED) → finalizeVerdict (superseding version) → resolveAppeal (ResolutionManager owner-only; accepted only when the core verdict version advanced past the version captured at open) → executeSettlement.

Caller isolation invariant: CALLER INPUT ≠ FINAL VERDICT — finalize_verdict, execute_settlement, and resolve_appeal accept only an ID; verdict, confidence, resolution, and appeal outcomes are all derived from stored protocol state. Resolving an appeal is additionally restricted to the ResolutionManager owner, and is accepted only when core's verdict version advanced past the version captured at appeal open — a verdict value that changed without a superseding finalizeVerdict does not count as accepted.

Fail-closed: reviewRequired verdicts, the EVALUATION_FAILED / INCONCLUSIVE / DISPUTED states, and any unresolved appeal refuse settlement (freeze), never auto-release.

Details: ARCHITECTURE.md · docs/PROTOCOL.md

Repository layout

agentcourt/
├── intelligent-contracts/
│   ├── core/agentcourt_core.py        # disputes, evidence, nondet evaluation, verdicts
│   └── registry/resolution_manager.py # settlement + appeals (reads core verdict)
├── frontend/src/
│   ├── sdk/                           # genlayer-js client + typed AgentCourt SDK
│   ├── components/                    # dashboard, detail, forms, explorers
│   └── dispute|evidence|verdict|settlement/
├── tests/
│   ├── unit/test_genlayer_workflow.py # 60 acceptance tests
│   ├── genlayer_stub.py               # CPython stub of the genlayer module
│   └── sdk.agentcourt.test.ts         # vitest domain tests
├── docs/                              # PROTOCOL, DEVELOPER_GUIDE, OPERATIONS, …
├── scripts/deploy/                    # deploy + wire helper
├── scripts/verification/              # on-chain smoke checks
├── ARCHITECTURE.md
├── AUDIT.md                           # Portal rejection remediation notes
├── SECURITY.md
└── AGENTS.md                          # full protocol spec

Documentation

Doc What it covers
ARCHITECTURE.md Two-IC design, lifecycle, caller isolation, fail-closed states
docs/PROTOCOL.md Evidence model, consensus rules, verdict classes, ADRs
docs/DEVELOPER_GUIDE.md SDK methods, contract API, integration examples
docs/OPERATIONS.md Deployment, addresses, monitoring, troubleshooting
docs/PRODUCT.md Use cases, FAQ, roadmap
AUDIT.md Why the first submission was rejected and exactly what changed
SECURITY.md · docs/SECURITY_PRIVACY.md Threat model, prompt-injection posture, privacy
CONTRIBUTING.md Setup, style, PR process
AGENTS.md Complete protocol specification

Quality & verification

  • Live data: evidence URLs are fetched inside the nondeterministic block via gl.nondet.web.get; all reads/writes go to GenLayer Studionet through genlayer-js.
  • Tests: 73 Python acceptance tests (caller isolation, fail-closed paths, consensus classification, appeal flow + resolve authorization + settlement gate, timestamps, Keccak commitments) + 28 vitest tests.
  • CI: genvm-lint check on both ICs, pytest, frontend build (.github/workflows/ci.yml).
  • Audit trail: AUDIT.md documents the verbatim GenLayer Portal rejection, every defect, and the remediation.

Status & disclaimer

AgentCourt is an experimental protocol. Contracts are deployed on GenLayer Studionet (test infrastructure) — do not treat them as production arbitration. Staked bonds are held in on-chain escrow by AgentCourtCore.deposit_stake and released per the final verdict at execute_settlement (see Staking escrow and AUDIT.md).

This software is not legal advice and is not a substitute for jurisdiction-specific arbitration procedures.

License

MIT — see LICENSE.

About

Autonomous dispute resolution for the agent economy. Built on GenLayer, AgentCourt investigates machine-readable disputes using verifiable evidence, independent AI reasoning, adversarial review, and consensus before triggering deterministic on-chain settlement.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages