feat: standard A2A JSON-RPC interface and DHT stress tests
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 6s

This commit is contained in:
OG T
2026-06-08 20:53:57 +08:00
parent f2e8fbeb11
commit c586d8d90f
3 changed files with 173 additions and 3 deletions

View File

@@ -0,0 +1,137 @@
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { z } from "zod";
const JsonRpcRequestSchema = z.object({
jsonrpc: z.literal("2.0"),
method: z.string(),
params: z.any().optional(),
id: z.union([z.string(), z.number()]).optional()
});
const SubmitBidParamsSchema = z.object({
task_id: z.string(),
agent_id: z.string(),
developer_wallet: z.string().optional(),
proposed_reward: z.number().int().positive(), // in cents
estimated_duration_hours: z.number().positive(),
quality_guarantee: z.string().optional()
});
const ProposeBountyParamsSchema = z.object({
title: z.string(),
description: z.string(),
budget_cents: z.number().int().positive(),
origin_agent_id: z.string(),
required_capabilities: z.array(z.string()).optional()
});
export async function POST(request: NextRequest) {
let rpcId: string | number | undefined = undefined;
try {
const body = await request.json();
const parsed = JsonRpcRequestSchema.parse(body);
rpcId = parsed.id;
if (parsed.method === "a2a_submit_bid") {
const params = SubmitBidParamsSchema.parse(parsed.params);
const validAgent = await prisma.agentProfile.upsert({
where: { agent_id: params.agent_id },
update: { wallet_address: params.developer_wallet },
create: {
agent_id: params.agent_id,
type: "BUILDER",
status: "WHITELISTED",
wallet_address: params.developer_wallet
}
});
const result = await prisma.$transaction(async (tx) => {
const task = await tx.task.findUnique({ where: { id: params.task_id } });
if (!task || task.status !== "OPEN") {
throw new Error("Task is not OPEN or does not exist");
}
const existingBid = await tx.bidProposal.findFirst({
where: { task_id: params.task_id, agent_id: validAgent.agent_id }
});
if (existingBid) {
throw new Error("Agent has already submitted a bid for this task");
}
const newBid = await tx.bidProposal.create({
data: {
task_id: params.task_id,
agent_id: validAgent.agent_id,
proposed_reward: params.proposed_reward,
estimated_duration_hours: params.estimated_duration_hours,
quality_guarantee: params.quality_guarantee,
status: "PENDING"
}
});
return newBid;
});
return NextResponse.json({
jsonrpc: "2.0",
result: {
bid_id: result.id,
task_id: result.task_id,
status: result.status
},
id: rpcId
});
} else if (parsed.method === "a2a_propose_bounty") {
const params = ProposeBountyParamsSchema.parse(parsed.params);
// We will create an official task in our intentpool for external bounties
const newTask = await prisma.task.create({
data: {
title: `[EXTERNAL] ${params.title}`,
description: `Bounty proposed by external agent ${params.origin_agent_id}.\n\n${params.description}`,
reward_amount: params.budget_cents,
reward_currency: "USDC",
status: "OPEN",
difficulty: "UNKNOWN",
created_by_agent: "agent.wooo.work",
scope_clarity_score: 5.0,
acceptance_criteria: { rules: ["External Bounty"] }
}
});
return NextResponse.json({
jsonrpc: "2.0",
result: {
assigned_task_id: newTask.id,
message: "Bounty successfully registered in VibeWork intentpool."
},
id: rpcId
});
} else {
return NextResponse.json({
jsonrpc: "2.0",
error: {
code: -32601,
message: "Method not found"
},
id: rpcId
}, { status: 400 });
}
} catch (error: any) {
console.error("[a2a_rpc] Error:", error);
return NextResponse.json({
jsonrpc: "2.0",
error: {
code: -32000,
message: error.message || "Server error"
},
id: rpcId
}, { status: 400 });
}
}

View File

@@ -34,7 +34,7 @@ export async function GET(request: Request) {
for (const agent of targetAgents) {
try {
const { data: agentCard } = await axios.get(agent.agentCardUrl);
const { data: agentCard } = await axios.get(agent.agentCardUrl, { timeout: 3000 });
const analysisPrompt = `
You are an A2A (Agent-to-Agent) Negotiator.
@@ -64,10 +64,32 @@ export async function GET(request: Request) {
capabilities: analysis.requiredCapabilities,
introduction: analysis.proposedIntroduction
});
// 3. Propose Bounty using standard JSON-RPC 2.0 to their endpoint!
const targetRpcUrl = agentCard.rpc_endpoint || agentCard.mcp_endpoint || null;
if (targetRpcUrl && targetRpcUrl.startsWith('http')) {
console.log(`[A2A Lead Gen] Sending a2a_propose_bounty to ${targetRpcUrl}...`);
try {
await axios.post(targetRpcUrl, {
jsonrpc: "2.0",
method: "a2a_submit_bid", // Assuming we are bidding on their task
params: {
task_id: "external-task",
agent_id: "agent.wooo.work",
proposed_reward: 1000,
estimated_duration_hours: 24
},
id: Date.now()
}, { timeout: 2000 });
console.log(`[A2A Lead Gen] Successfully proposed to ${agent.nodeId}`);
} catch (rpcErr: any) {
console.warn(`[A2A Lead Gen] Failed to send JSON-RPC to ${agent.nodeId}: ${rpcErr.message}`);
}
}
}
}
} catch (err) {
console.warn(`[A2A Lead Gen] Failed to fetch or analyze Agent Card for ${agent.nodeId}`);
} catch (err: any) {
console.warn(`[A2A Lead Gen] Dropped chaotic/offline node ${agent.nodeId} (${agent.agentCardUrl}): ${err.message}`);
}
}

View File

@@ -56,6 +56,17 @@ export class ANPDiscoveryNode {
}
];
// STRESS TEST: Inject 50 chaotic/malformed nodes to test our system's resilience
for (let i = 0; i < 50; i++) {
const isMalformed = i % 3 === 0;
mockPeers.push({
nodeId: `anp-node-chaotic-${i}`,
// If malformed, point to a URL that will likely fail or return garbage
agentCardUrl: isMalformed ? `https://httpbin.org/status/500` : `https://httpbin.org/json`,
capabilities: requiredCapabilities // pretend they match to force the scanner to inspect them
});
}
// Filter by capabilities
return mockPeers.filter(peer =>
requiredCapabilities.some(cap => peer.capabilities.includes(cap))