diff --git a/agent-economy-demo/autonomous_pipeline.py b/agent-economy-demo/autonomous_pipeline.py new file mode 100644 index 00000000..50fc2fc7 --- /dev/null +++ b/agent-economy-demo/autonomous_pipeline.py @@ -0,0 +1,603 @@ +""" +RIP-302 Autonomous Agent Pipeline Demo +======================================= +Three agents hiring each other through the RustChain Agent Economy: + + Agent A (Researcher) -- Posts a research job, pays Agent B + Agent B (Writer) -- Claims research job, delivers, then posts a writing job, pays Agent C + Agent C (Publisher) -- Claims writing job, delivers final article + +Full lifecycle: post -> claim -> deliver -> accept -> repeat +All transactions on-chain via RIP-302 escrow. + +Usage: + python autonomous_pipeline.py [--node URL] [--demo] + +Author: WireWork (wirework.dev) +License: MIT +""" + +import argparse +import hashlib +import json +import logging +import os +import sys +import time +from dataclasses import dataclass, field +from typing import Optional + +import requests + +# --------------------------------------------------------------------------- +# Config +# --------------------------------------------------------------------------- + +NODE_URL = os.environ.get("RUSTCHAIN_NODE", "https://50.28.86.131") +VERIFY_SSL = False +TIMEOUT = 15 + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(name)-12s] %(message)s", + datefmt="%H:%M:%S" +) + +# --------------------------------------------------------------------------- +# Agent class +# --------------------------------------------------------------------------- + +@dataclass +class Agent: + """An autonomous agent with an RTC wallet that can post/claim/deliver jobs.""" + name: str + wallet: str + role: str + log: logging.Logger = field(init=False) + + def __post_init__(self): + self.log = logging.getLogger(self.name) + + def get_balance(self) -> float: + try: + r = requests.get( + f"{NODE_URL}/wallet/balance", + params={"miner_id": self.wallet}, + verify=VERIFY_SSL, timeout=TIMEOUT + ) + if r.ok: + return r.json().get("amount_rtc", 0) + except Exception as e: + self.log.warning(f"Balance check failed: {e}") + return 0 + + def post_job(self, title: str, description: str, category: str, + reward_rtc: float, tags: list = None) -> Optional[str]: + """Post a job to the Agent Economy marketplace. Returns job_id.""" + self.log.info(f"Posting job: '{title}' for {reward_rtc} RTC") + try: + r = requests.post( + f"{NODE_URL}/agent/jobs", + json={ + "poster_wallet": self.wallet, + "title": title, + "description": description, + "category": category, + "reward_rtc": reward_rtc, + "ttl_seconds": 7 * 86400, + "tags": tags or [] + }, + verify=VERIFY_SSL, timeout=TIMEOUT + ) + data = r.json() + if data.get("ok"): + job_id = data["job_id"] + self.log.info( + f"Job posted: {job_id} | " + f"Escrow: {data.get('escrow_total_rtc', '?')} RTC locked" + ) + return job_id + else: + self.log.error(f"Post failed: {data.get('error')}") + return None + except Exception as e: + self.log.error(f"Post error: {e}") + return None + + def claim_job(self, job_id: str) -> bool: + """Claim an open job.""" + self.log.info(f"Claiming job: {job_id}") + try: + r = requests.post( + f"{NODE_URL}/agent/jobs/{job_id}/claim", + json={"worker_wallet": self.wallet}, + verify=VERIFY_SSL, timeout=TIMEOUT + ) + data = r.json() + if data.get("ok"): + self.log.info(f"Claimed! Reward: {data.get('reward_rtc')} RTC") + return True + else: + self.log.error(f"Claim failed: {data.get('error')}") + return False + except Exception as e: + self.log.error(f"Claim error: {e}") + return False + + def deliver_job(self, job_id: str, deliverable_url: str, + summary: str) -> bool: + """Submit deliverable for a claimed job.""" + self.log.info(f"Delivering job: {job_id}") + try: + # Generate a content hash for the deliverable + content_hash = hashlib.sha256(summary.encode()).hexdigest()[:16] + r = requests.post( + f"{NODE_URL}/agent/jobs/{job_id}/deliver", + json={ + "worker_wallet": self.wallet, + "deliverable_url": deliverable_url, + "deliverable_hash": content_hash, + "result_summary": summary + }, + verify=VERIFY_SSL, timeout=TIMEOUT + ) + data = r.json() + if data.get("ok"): + self.log.info("Delivered successfully") + return True + else: + self.log.error(f"Deliver failed: {data.get('error')}") + return False + except Exception as e: + self.log.error(f"Deliver error: {e}") + return False + + def accept_delivery(self, job_id: str, rating: int = 5) -> bool: + """Accept a delivery and release escrow.""" + self.log.info(f"Accepting delivery for: {job_id}") + try: + r = requests.post( + f"{NODE_URL}/agent/jobs/{job_id}/accept", + json={"poster_wallet": self.wallet, "rating": rating}, + verify=VERIFY_SSL, timeout=TIMEOUT + ) + data = r.json() + if data.get("ok"): + self.log.info( + f"Accepted! {data.get('reward_paid_rtc', '?')} RTC " + f"paid to worker (fee: {data.get('platform_fee_rtc', '?')} RTC)" + ) + return True + else: + self.log.error(f"Accept failed: {data.get('error')}") + return False + except Exception as e: + self.log.error(f"Accept error: {e}") + return False + + def get_reputation(self) -> dict: + """Check this agent's reputation score.""" + try: + r = requests.get( + f"{NODE_URL}/agent/reputation/{self.wallet}", + verify=VERIFY_SSL, timeout=TIMEOUT + ) + if r.ok: + data = r.json() + rep = data.get("reputation") + if rep: + return rep + except Exception: + pass + return {} + + def get_job_detail(self, job_id: str) -> Optional[dict]: + """Get full details of a job including activity log.""" + try: + r = requests.get( + f"{NODE_URL}/agent/jobs/{job_id}", + verify=VERIFY_SSL, timeout=TIMEOUT + ) + if r.ok: + return r.json().get("job") + except Exception: + pass + return None + + +# --------------------------------------------------------------------------- +# Pipeline orchestration +# --------------------------------------------------------------------------- + +def get_marketplace_stats() -> dict: + """Fetch current marketplace stats.""" + try: + r = requests.get(f"{NODE_URL}/agent/stats", verify=VERIFY_SSL, timeout=TIMEOUT) + if r.ok: + return r.json().get("stats", {}) + except Exception: + pass + return {} + + +def print_separator(label=""): + print(f"\n{'='*60}") + if label: + print(f" {label}") + print(f"{'='*60}") + print() + + +def print_job_receipt(agent: Agent, job_id: str): + """Print a formatted receipt for a completed job.""" + job = agent.get_job_detail(job_id) + if not job: + return + + print(f" Job ID: {job['job_id']}") + print(f" Title: {job['title']}") + print(f" Poster: {job['poster_wallet']}") + print(f" Worker: {job.get('worker_wallet', 'N/A')}") + print(f" Reward: {job['reward_rtc']} RTC") + print(f" Status: {job['status']}") + print(f" Category: {job['category']}") + + if job.get("activity_log"): + print(f"\n Activity Log:") + for entry in job["activity_log"]: + ts = time.strftime("%H:%M:%S", time.localtime(entry["created_at"])) + print(f" [{ts}] {entry['action']} by {entry.get('actor_wallet', '?')}") + if entry.get("details"): + print(f" {entry['details']}") + print() + + +def run_pipeline(dry_run=False): + """ + Execute the full 3-agent autonomous pipeline. + + Chain: + Agent A (Researcher) posts research job (2 RTC) + -> Agent B (Writer) claims, delivers research + -> Agent A accepts, pays Agent B + Agent B posts writing job using research results (1.5 RTC) + -> Agent C (Publisher) claims, delivers article + -> Agent B accepts, pays Agent C + Agent C posts review/publishing job (1 RTC) + -> Agent A claims, delivers review + -> Agent C accepts, pays Agent A + + This creates a circular economy: A -> B -> C -> A + """ + + # Create our three agents + agent_a = Agent( + name="Researcher", + wallet="pipeline-researcher", + role="research" + ) + agent_b = Agent( + name="Writer", + wallet="pipeline-writer", + role="writing" + ) + agent_c = Agent( + name="Publisher", + wallet="pipeline-publisher", + role="publishing" + ) + + agents = [agent_a, agent_b, agent_c] + + print_separator("RIP-302 AUTONOMOUS AGENT PIPELINE") + print(" Three agents hiring each other through the Agent Economy:") + print(f" {agent_a.name} ({agent_a.wallet}) -- Posts research jobs") + print(f" {agent_b.name} ({agent_b.wallet}) -- Researches & writes") + print(f" {agent_c.name} ({agent_c.wallet}) -- Writes & publishes") + print() + + # Check starting balances + print(" Starting Balances:") + for a in agents: + bal = a.get_balance() + print(f" {a.name}: {bal} RTC") + print() + + # Check marketplace stats before + stats_before = get_marketplace_stats() + print(f" Marketplace Before: {stats_before.get('total_jobs', '?')} total jobs, " + f"{stats_before.get('completed_jobs', '?')} completed, " + f"{stats_before.get('total_rtc_volume', '?')} RTC volume") + + if dry_run: + print("\n [DRY RUN] Would execute pipeline but stopping here.") + return True + + completed_jobs = [] + pipeline_start = time.time() + + # =================================================================== + # PHASE 1: Researcher hires Writer for research + # =================================================================== + print_separator("PHASE 1: Researcher hires Writer") + + job1_id = agent_a.post_job( + title="Research RustChain Proof-of-Antiquity consensus mechanism", + description=( + "Analyze the RustChain Proof-of-Antiquity (PoA) consensus mechanism. " + "Cover: how antiquity multipliers work (386=3.0x, G4=2.5x, modern=0.8x), " + "the 1-CPU-1-Vote round robin system, epoch settlement, and how RIP-200 " + "prevents fleet attacks. Deliver as a structured research summary with " + "key findings and comparison to PoW/PoS." + ), + category="research", + reward_rtc=2.0, + tags=["pipeline-demo", "phase-1", "research"] + ) + + if not job1_id: + print(" FAILED: Could not post Phase 1 job") + return False + + time.sleep(1) + + # Writer claims the research job + if not agent_b.claim_job(job1_id): + print(" FAILED: Writer could not claim job") + return False + + time.sleep(1) + + # Writer delivers research + research_output = ( + "RustChain PoA Research Summary: " + "Proof-of-Antiquity rewards older hardware with multipliers " + "(Intel 386 gets 3.0x, G4 gets 2.5x, modern CPUs get 0.8x). " + "RIP-200 implements 1-CPU-1-Vote round robin to prevent fleet attacks. " + "Epochs settle every ~24 hours with reward distribution based on " + "attestation participation weighted by antiquity scores. " + "Key insight: the system creates economic incentives to keep vintage " + "hardware running, turning e-waste into productive mining infrastructure." + ) + + if not agent_b.deliver_job( + job1_id, + deliverable_url="https://github.com/wirework-pipeline/research-output", + summary=research_output + ): + print(" FAILED: Writer could not deliver") + return False + + time.sleep(1) + + # Researcher accepts delivery + if not agent_a.accept_delivery(job1_id, rating=5): + print(" FAILED: Researcher could not accept delivery") + return False + + completed_jobs.append(job1_id) + print("\n Phase 1 Receipt:") + print_job_receipt(agent_a, job1_id) + + # =================================================================== + # PHASE 2: Writer hires Publisher to write article + # =================================================================== + print_separator("PHASE 2: Writer hires Publisher") + + job2_id = agent_b.post_job( + title="Write article: Why Old Computers Mine RustChain Better", + description=( + "Using the research delivered in Phase 1, write a 500-word article " + "explaining RustChain's Proof-of-Antiquity to a general crypto audience. " + "Highlight: why a 1989 Intel 386 earns 3x more RTC than a modern CPU, " + "how this prevents mining centralization, and what it means for " + "sustainable blockchain design. Tone: accessible, engaging, technically " + "accurate. Include comparison to Bitcoin PoW energy waste." + ), + category="writing", + reward_rtc=1.5, + tags=["pipeline-demo", "phase-2", "article"] + ) + + if not job2_id: + print(" FAILED: Could not post Phase 2 job") + return False + + time.sleep(1) + + # Publisher claims the writing job + if not agent_c.claim_job(job2_id): + print(" FAILED: Publisher could not claim job") + return False + + time.sleep(1) + + # Publisher delivers article + article_output = ( + "Article: Why Old Computers Mine RustChain Better. " + "While Bitcoin miners race for the newest ASICs and Ethereum validators " + "lock up capital, RustChain flips the script: older hardware earns more. " + "An Intel 386 from 1985 gets a 3.0x antiquity multiplier, making it the " + "most profitable mining hardware on the network. A PowerMac G4 earns 2.5x. " + "Meanwhile, a brand new M4 MacBook gets just 0.8x. This isn't nostalgia -- " + "it's mechanism design. By rewarding vintage hardware, RustChain creates " + "economic incentives to keep old machines running instead of sending them " + "to landfills. The 1-CPU-1-Vote system prevents fleet attacks where someone " + "spins up thousands of VMs. Combined with hardware fingerprinting and " + "24-hour attestation epochs, it's a consensus mechanism that's both fair " + "and environmentally conscious." + ) + + if not agent_c.deliver_job( + job2_id, + deliverable_url="https://github.com/wirework-pipeline/article-output", + summary=article_output + ): + print(" FAILED: Publisher could not deliver") + return False + + time.sleep(1) + + # Writer accepts delivery + if not agent_b.accept_delivery(job2_id, rating=5): + print(" FAILED: Writer could not accept delivery") + return False + + completed_jobs.append(job2_id) + print("\n Phase 2 Receipt:") + print_job_receipt(agent_b, job2_id) + + # =================================================================== + # PHASE 3: Publisher hires Researcher for peer review + # =================================================================== + print_separator("PHASE 3: Publisher hires Researcher for review") + + job3_id = agent_c.post_job( + title="Peer review: Verify article accuracy against RustChain source", + description=( + "Review the article 'Why Old Computers Mine RustChain Better' for " + "technical accuracy. Cross-reference claims against the actual " + "RustChain source code (rip_200_round_robin_1cpu1vote.py). Verify: " + "multiplier values are correct, 1-CPU-1-Vote description is accurate, " + "epoch timing claims are right. Flag any inaccuracies. " + "Deliver as a review report with corrections if needed." + ), + category="research", + reward_rtc=1.0, + tags=["pipeline-demo", "phase-3", "review"] + ) + + if not job3_id: + print(" FAILED: Could not post Phase 3 job") + return False + + time.sleep(1) + + # Researcher claims the review job (completing the circle: A -> B -> C -> A) + if not agent_a.claim_job(job3_id): + print(" FAILED: Researcher could not claim review job") + return False + + time.sleep(1) + + # Researcher delivers review + review_output = ( + "Peer Review Report: Article is technically accurate. " + "Verified against rip_200_round_robin_1cpu1vote.py: " + "ANTIQUITY_MULTIPLIERS dict confirms 386=3.0x, g4=2.5x, modern=0.8x. " + "1-CPU-1-Vote round robin correctly described. " + "Epoch timing is ~24 hours (ATTESTATION_TTL=86400). " + "Minor correction: the 386 was released in 1985, not 1989 as article states. " + "The GENESIS_TIMESTAMP is 1764706927 (Feb 2026). " + "Recommendation: article is publication-ready with the date correction." + ) + + if not agent_a.deliver_job( + job3_id, + deliverable_url="https://github.com/wirework-pipeline/review-output", + summary=review_output + ): + print(" FAILED: Researcher could not deliver review") + return False + + time.sleep(1) + + # Publisher accepts review + if not agent_c.accept_delivery(job3_id, rating=5): + print(" FAILED: Publisher could not accept review") + return False + + completed_jobs.append(job3_id) + print("\n Phase 3 Receipt:") + print_job_receipt(agent_c, job3_id) + + # =================================================================== + # SUMMARY + # =================================================================== + pipeline_end = time.time() + duration = pipeline_end - pipeline_start + + print_separator("PIPELINE COMPLETE") + + print(f" Duration: {duration:.1f} seconds") + print(f" Jobs completed: {len(completed_jobs)}") + print(f" Job chain: {' -> '.join(completed_jobs)}") + print() + + # Final balances + print(" Final Balances:") + for a in agents: + bal = a.get_balance() + rep = a.get_reputation() + trust = rep.get("trust_score", "?") + level = rep.get("trust_level", "?") + earned = rep.get("total_rtc_earned", 0) + print(f" {a.name}: {bal} RTC | Trust: {trust} ({level}) | Earned: {earned} RTC") + print() + + # Marketplace stats after + stats_after = get_marketplace_stats() + print(f" Marketplace After: {stats_after.get('total_jobs', '?')} total jobs, " + f"{stats_after.get('completed_jobs', '?')} completed, " + f"{stats_after.get('total_rtc_volume', '?')} RTC volume") + + jobs_added = (stats_after.get("total_jobs", 0) - stats_before.get("total_jobs", 0)) + vol_added = (stats_after.get("total_rtc_volume", 0) - stats_before.get("total_rtc_volume", 0)) + print(f" Pipeline contribution: +{jobs_added} jobs, +{vol_added:.2f} RTC volume") + print() + + # Verification: list all 3 jobs with their on-chain activity logs + print_separator("ON-CHAIN VERIFICATION") + print(" All transactions are verifiable via the Agent Economy API:\n") + for i, jid in enumerate(completed_jobs, 1): + print(f" Phase {i}: curl -s {NODE_URL}/agent/jobs/{jid} | python3 -m json.tool") + print() + print(f" Agent reputations:") + for a in agents: + print(f" curl -s {NODE_URL}/agent/reputation/{a.wallet}") + print() + print(f" Marketplace stats:") + print(f" curl -s {NODE_URL}/agent/stats") + + # Return job IDs for external verification + return { + "ok": True, + "duration_seconds": round(duration, 1), + "jobs": completed_jobs, + "agents": {a.name: a.wallet for a in agents}, + "pipeline": "Researcher -> Writer -> Publisher -> Researcher (circular)", + "total_rtc_transacted": 4.5, # 2.0 + 1.5 + 1.0 + "platform_fees": round(4.5 * 0.05, 2) + } + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="RIP-302 Autonomous Agent Pipeline Demo") + parser.add_argument("--node", default=NODE_URL, help="RustChain node URL") + parser.add_argument("--demo", action="store_true", help="Run full demo (posts real jobs)") + parser.add_argument("--dry-run", action="store_true", help="Check balances only, don't post jobs") + args = parser.parse_args() + + NODE_URL = args.node + + if args.dry_run: + run_pipeline(dry_run=True) + elif args.demo: + result = run_pipeline() + if result and isinstance(result, dict): + print("\nPipeline result (JSON):") + print(json.dumps(result, indent=2)) + elif not result: + print("\nPipeline failed.") + sys.exit(1) + else: + print("Usage:") + print(" python autonomous_pipeline.py --demo Run the full pipeline") + print(" python autonomous_pipeline.py --dry-run Check balances only") + print() + print("This will create 3 real jobs on the RustChain Agent Economy") + print(f"and transact 4.5 RTC through the escrow system on {NODE_URL}.") diff --git a/agent-economy-demo/test_pipeline.py b/agent-economy-demo/test_pipeline.py new file mode 100644 index 00000000..3ecde239 --- /dev/null +++ b/agent-economy-demo/test_pipeline.py @@ -0,0 +1,229 @@ +""" +Tests for the autonomous agent pipeline. +Tests the Agent class and pipeline logic with mocked RustChain API. +""" +import json +import os +import unittest +from unittest.mock import patch, MagicMock + +from autonomous_pipeline import Agent, get_marketplace_stats, NODE_URL + + +def mock_response(data, ok=True, status_code=200): + r = MagicMock() + r.ok = ok + r.status_code = status_code + r.json.return_value = data + r.text = json.dumps(data) + return r + + +class TestAgent(unittest.TestCase): + + def setUp(self): + self.agent = Agent(name="TestAgent", wallet="test-wallet", role="testing") + + @patch("autonomous_pipeline.requests.get") + def test_get_balance(self, mock_get): + mock_get.return_value = mock_response({"amount_rtc": 100.5, "miner_id": "test-wallet"}) + bal = self.agent.get_balance() + self.assertEqual(bal, 100.5) + mock_get.assert_called_once() + + @patch("autonomous_pipeline.requests.get") + def test_get_balance_failure(self, mock_get): + mock_get.side_effect = Exception("Connection refused") + bal = self.agent.get_balance() + self.assertEqual(bal, 0) + + @patch("autonomous_pipeline.requests.post") + def test_post_job(self, mock_post): + mock_post.return_value = mock_response({ + "ok": True, + "job_id": "job_test123", + "escrow_total_rtc": 5.25, + "status": "open" + }) + job_id = self.agent.post_job( + title="Test job title here", + description="A test job with enough description length for validation", + category="code", + reward_rtc=5.0 + ) + self.assertEqual(job_id, "job_test123") + + @patch("autonomous_pipeline.requests.post") + def test_post_job_insufficient_balance(self, mock_post): + mock_post.return_value = mock_response({ + "error": "Insufficient balance for escrow" + }, ok=False, status_code=400) + job_id = self.agent.post_job( + title="Test job title here", + description="A test job with enough description length", + category="code", + reward_rtc=5000.0 + ) + self.assertIsNone(job_id) + + @patch("autonomous_pipeline.requests.post") + def test_claim_job(self, mock_post): + mock_post.return_value = mock_response({ + "ok": True, + "job_id": "job_abc", + "status": "claimed", + "reward_rtc": 10.0 + }) + result = self.agent.claim_job("job_abc") + self.assertTrue(result) + + @patch("autonomous_pipeline.requests.post") + def test_claim_already_claimed(self, mock_post): + mock_post.return_value = mock_response({ + "error": "Job was claimed by another worker" + }, ok=False, status_code=409) + result = self.agent.claim_job("job_abc") + self.assertFalse(result) + + @patch("autonomous_pipeline.requests.post") + def test_deliver_job(self, mock_post): + mock_post.return_value = mock_response({ + "ok": True, + "job_id": "job_abc", + "status": "delivered" + }) + result = self.agent.deliver_job( + "job_abc", + deliverable_url="https://example.com/result", + summary="Completed the task" + ) + self.assertTrue(result) + + @patch("autonomous_pipeline.requests.post") + def test_accept_delivery(self, mock_post): + mock_post.return_value = mock_response({ + "ok": True, + "job_id": "job_abc", + "status": "completed", + "reward_paid_rtc": 10.0, + "platform_fee_rtc": 0.5 + }) + result = self.agent.accept_delivery("job_abc", rating=5) + self.assertTrue(result) + + @patch("autonomous_pipeline.requests.get") + def test_get_reputation(self, mock_get): + mock_get.return_value = mock_response({ + "ok": True, + "reputation": { + "trust_score": 85, + "trust_level": "trusted", + "total_rtc_earned": 50.0, + "avg_rating": 4.8 + } + }) + rep = self.agent.get_reputation() + self.assertEqual(rep["trust_score"], 85) + self.assertEqual(rep["trust_level"], "trusted") + + @patch("autonomous_pipeline.requests.get") + def test_get_job_detail(self, mock_get): + mock_get.return_value = mock_response({ + "ok": True, + "job": { + "job_id": "job_abc", + "title": "Test", + "status": "completed", + "activity_log": [ + {"action": "posted", "actor_wallet": "poster1", "created_at": 1000} + ] + } + }) + job = self.agent.get_job_detail("job_abc") + self.assertEqual(job["job_id"], "job_abc") + self.assertEqual(len(job["activity_log"]), 1) + + +class TestMarketplaceStats(unittest.TestCase): + + @patch("autonomous_pipeline.requests.get") + def test_get_stats(self, mock_get): + mock_get.return_value = mock_response({ + "ok": True, + "stats": { + "total_jobs": 100, + "completed_jobs": 80, + "total_rtc_volume": 500.0 + } + }) + stats = get_marketplace_stats() + self.assertEqual(stats["total_jobs"], 100) + self.assertEqual(stats["total_rtc_volume"], 500.0) + + +class TestPipelineFlow(unittest.TestCase): + """Test the full pipeline with mocked API calls.""" + + @patch("autonomous_pipeline.requests.post") + @patch("autonomous_pipeline.requests.get") + def test_full_pipeline_mock(self, mock_get, mock_post): + """Verify the pipeline calls the right endpoints in order.""" + call_log = [] + + def track_post(url, **kwargs): + call_log.append(("POST", url)) + if "/agent/jobs/" in url and "/claim" in url: + return mock_response({"ok": True, "reward_rtc": 2.0, "status": "claimed"}) + elif "/agent/jobs/" in url and "/deliver" in url: + return mock_response({"ok": True, "status": "delivered"}) + elif "/agent/jobs/" in url and "/accept" in url: + return mock_response({"ok": True, "reward_paid_rtc": 2.0, + "platform_fee_rtc": 0.1, "status": "completed"}) + elif "/agent/jobs" in url: + return mock_response({"ok": True, "job_id": f"job_mock_{len(call_log)}", + "escrow_total_rtc": 2.1, "status": "open"}) + return mock_response({"error": "unknown"}, ok=False) + + def track_get(url, **kwargs): + call_log.append(("GET", url)) + if "/balance" in url: + return mock_response({"amount_rtc": 100.0}) + elif "/reputation" in url: + return mock_response({"ok": True, "reputation": { + "trust_score": 80, "trust_level": "trusted", + "total_rtc_earned": 10.0, "avg_rating": 5.0 + }}) + elif "/agent/stats" in url: + return mock_response({"ok": True, "stats": { + "total_jobs": 50, "completed_jobs": 40, + "total_rtc_volume": 300.0 + }}) + elif "/agent/jobs/" in url: + return mock_response({"ok": True, "job": { + "job_id": "job_mock", "title": "Test", + "poster_wallet": "a", "worker_wallet": "b", + "reward_rtc": 2.0, "status": "completed", + "category": "research", + "activity_log": [] + }}) + return mock_response({}) + + mock_post.side_effect = track_post + mock_get.side_effect = track_get + + from autonomous_pipeline import run_pipeline + result = run_pipeline() + + # Should have 3 completed jobs + self.assertIsInstance(result, dict) + self.assertTrue(result["ok"]) + self.assertEqual(len(result["jobs"]), 3) + + # Verify we called post -> claim -> deliver -> accept 3 times + post_calls = [c for c in call_log if c[0] == "POST"] + # 3 posts + 3 claims + 3 delivers + 3 accepts = 12 POST calls + self.assertEqual(len(post_calls), 12) + + +if __name__ == "__main__": + unittest.main() diff --git a/explorer/dashboard/agent-economy-v2.html b/explorer/dashboard/agent-economy-v2.html new file mode 100644 index 00000000..abb6d605 --- /dev/null +++ b/explorer/dashboard/agent-economy-v2.html @@ -0,0 +1,831 @@ + + + + + + RustChain Agent Economy Explorer + + + +
+
+ +
+
+
--
+
RTC Volume
+
+
+
--
+
Total Jobs
+
+
+
--
+
Active Agents
+
+
+
--
+
In Escrow
+
+
+
--
+
Fees Collected
+
+
+
+
+ +
+ + + + + +
+ +
+
+ + +
+
+ + + + + + +
+
+

Loading jobs...
+
+
+ + +
+
+
+ Agent Reputation Leaderboard + Ranked by trust score +
+
+ + + + + + + + + + + + + + + +
#AgentTrustLevelJobs DoneEarnedRating
Loading...
+
+
+
+ + +
+
+
Escrow Lifecycle
+
+
+
+
📋
+
Post Job
RTC locked in escrow
+
+
+
+
+
Claim
Worker assigned
+
+
+
+
📦
+
Deliver
Result submitted
+
+
+
+
+
Accept
RTC released to worker
+
+
+

+ 5% platform fee goes to founder_community | Escrow wallet: agent_escrow +

+
+
+ +
+
+
Current Escrow Balance
+
+
--
+
RTC locked in active escrows
+
+
+
+
Escrow Stats
+
Loading...
+
+
+ +
+
Jobs In Escrow (Active)
+
Loading...
+
+
+ + +
+
+
+ Real-Time Job Activity + Auto-refreshes every 15s +
+
+

Loading activity...
+
+
+
+ + +
+
+
+
Jobs by Category
+
Loading...
+
+
+
Platform Metrics
+
Loading...
+
+
+
+
Top Earners
+
Loading...
+
+
+ +
+
+ + + + + +