feat(bounty): add A2A create_sub_task mechanism
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 7s
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 7s
This commit is contained in:
@@ -29,6 +29,8 @@ model Task {
|
||||
is_priority Boolean @default(false)
|
||||
is_private Boolean @default(false)
|
||||
referred_by_agent String?
|
||||
parent_task_id String?
|
||||
created_by_agent String?
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
|
||||
@@ -71,3 +71,30 @@ paths:
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
/create_sub_task:
|
||||
post:
|
||||
operationId: createSubTask
|
||||
summary: Delegate task to another agent (A2A)
|
||||
description: Create a sub-task using your own bounty reward budget.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [parent_task_id, claim_token, title, description, reward_amount, acceptance_criteria]
|
||||
properties:
|
||||
parent_task_id:
|
||||
type: string
|
||||
claim_token:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
reward_amount:
|
||||
type: integer
|
||||
acceptance_criteria:
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
ListOpenTasksRequestSchema,
|
||||
ClaimTaskRequestSchema,
|
||||
SubmitSolutionRequestSchema,
|
||||
CreateSubTaskRequestSchema,
|
||||
TaskStatus,
|
||||
JudgeOverallResult
|
||||
} from "@agent-bounty/contracts";
|
||||
@@ -684,6 +685,70 @@ export async function POST(request: NextRequest, props: { params: Promise<{ tool
|
||||
});
|
||||
}
|
||||
|
||||
case "create_sub_task": {
|
||||
const parsed = CreateSubTaskRequestSchema.parse(body);
|
||||
|
||||
const subTask = await prisma.$transaction(async (tx) => {
|
||||
const claim = await tx.claim.findUnique({ where: { claim_token: parsed.claim_token } });
|
||||
if (!claim || claim.task_id !== parsed.parent_task_id || claim.status !== TaskStatus.EXECUTING) {
|
||||
throw new Error("Invalid claim token or parent task is not EXECUTING");
|
||||
}
|
||||
if (parsed.reward_amount >= claim.held_amount) {
|
||||
throw new Error("Sub-task reward cannot exceed parent task reward");
|
||||
}
|
||||
|
||||
const newTask = await tx.task.create({
|
||||
data: {
|
||||
title: `[Sub-Task] ${parsed.title}`,
|
||||
description: parsed.description,
|
||||
status: TaskStatus.OPEN,
|
||||
difficulty: "HELLO_WORLD",
|
||||
reward_amount: parsed.reward_amount,
|
||||
reward_currency: claim.held_currency,
|
||||
required_stack: ["A2A", "Agent Sub-Task"],
|
||||
scope_clarity_score: 1.0,
|
||||
parent_task_id: parsed.parent_task_id,
|
||||
created_by_agent: claim.agent_id,
|
||||
acceptance_criteria: parsed.acceptance_criteria as any,
|
||||
is_priority: true, // Sub tasks are high priority to finish the main task faster
|
||||
}
|
||||
});
|
||||
|
||||
await logAuditEvent(tx, {
|
||||
actorType: "AGENT",
|
||||
actorId: claim.agent_id,
|
||||
action: "CREATE_SUB_TASK",
|
||||
entityType: "TASK",
|
||||
entityId: newTask.id,
|
||||
beforeState: null,
|
||||
afterState: { status: TaskStatus.OPEN, parent: claim.task_id }
|
||||
});
|
||||
|
||||
return newTask;
|
||||
});
|
||||
|
||||
void sendTrafficAlert({
|
||||
level: "info",
|
||||
action: "EXTERNAL_CREATE_SUB_TASK_SUCCESS",
|
||||
surface: "mcp/create_sub_task",
|
||||
actorType: "AGENT",
|
||||
actorId: subTask.created_by_agent!,
|
||||
taskId: subTask.id,
|
||||
message: `A2A 內循環!Agent 發佈了子任務: ${subTask.id}`,
|
||||
metadata: {
|
||||
parent_task_id: parsed.parent_task_id,
|
||||
reward: parsed.reward_amount,
|
||||
payload_summary: summarizeRequestPayload(tool, body),
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
sub_task_id: subTask.id,
|
||||
status: subTask.status,
|
||||
request_id: requestContext.request_id,
|
||||
});
|
||||
}
|
||||
|
||||
case "check_payout_status": {
|
||||
const parsed = z.object({ task_id: z.string().uuid() }).parse(body);
|
||||
|
||||
|
||||
@@ -228,5 +228,6 @@ export const MCPToolName = {
|
||||
CLAIM_TASK: "claim_task",
|
||||
SUBMIT_SOLUTION: "submit_solution",
|
||||
CHECK_PAYOUT_STATUS: "check_payout_status",
|
||||
CREATE_SUB_TASK: "create_sub_task",
|
||||
} as const;
|
||||
export type MCPToolName = (typeof MCPToolName)[keyof typeof MCPToolName];
|
||||
|
||||
@@ -181,6 +181,27 @@ export const SubmitSolutionResponseSchema = z.object({
|
||||
estimated_judge_complete_at: z.string().datetime().optional(),
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// create_sub_task Request/Response
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
export const CreateSubTaskRequestSchema = z.object({
|
||||
parent_task_id: UUIDSchema,
|
||||
claim_token: z.string().uuid(),
|
||||
title: z.string().min(5).max(120),
|
||||
description: z.string().min(20).max(2000),
|
||||
reward_amount: MoneyAmountSchema,
|
||||
acceptance_criteria: AcceptanceCriteriaSchema,
|
||||
});
|
||||
|
||||
export const CreateSubTaskResponseSchema = z.object({
|
||||
sub_task_id: UUIDSchema,
|
||||
status: z.union([
|
||||
z.literal(TaskStatus.DRAFT),
|
||||
z.literal(TaskStatus.OPEN),
|
||||
]),
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Judge Result Schema(E2B 沙盒回傳)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
ListOpenTasksRequestSchema,
|
||||
ClaimTaskRequestSchema,
|
||||
SubmitSolutionRequestSchema,
|
||||
CreateSubTaskRequestSchema,
|
||||
} from "@agent-bounty/contracts";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -70,6 +71,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
}) as any
|
||||
),
|
||||
},
|
||||
{
|
||||
name: MCPToolName.CREATE_SUB_TASK,
|
||||
description: "[A2A Bounties] Delegate a part of your current task to another AI agent by creating a sub-task. The reward will be deducted from your final payout.",
|
||||
inputSchema: zodToJsonSchema(CreateSubTaskRequestSchema as any),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
@@ -117,6 +123,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
};
|
||||
}
|
||||
|
||||
case MCPToolName.CREATE_SUB_TASK: {
|
||||
const parsed = CreateSubTaskRequestSchema.parse(args);
|
||||
const data = await proxyToBackend("/api/mcp/create_sub_task", parsed, API_BASE_URL, API_KEY);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user