Files
agent-bounty-protocol/apps/web/src/app/api/a2a/onboarding/route.ts
OG T 5a3cf08f8e
All checks were successful
CI and Production Smoke / smoke (push) Successful in 7s
fix: route agent connect through gateway domain
2026-06-12 01:13:13 +08:00

220 lines
8.6 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import {
A2A_AGENT_INTEGRATIONS,
A2A_MONETIZATION_LANES,
INTEGRATION_CATALOG_UPDATED_AT,
TELEGRAM_CONTROL_PLANE_ROLES,
} from "@/lib/a2a-agent-integrations";
import {
AGENT_GATEWAY_URL,
buildAgentGrowthKit,
buildDemandProposalUrl,
sanitizeAgentId,
VIBEWORK_SITE_URL,
} from "@/lib/a2a-growth";
import { logA2aTrafficEvent } from "@/lib/a2a-traffic";
import { prisma } from "@/lib/prisma";
export const dynamic = "force-dynamic";
function findIntegration(tool: string | null) {
const normalized = sanitizeAgentId(tool);
if (!normalized) return null;
return (
A2A_AGENT_INTEGRATIONS.find((integration) => integration.id === normalized) ||
A2A_AGENT_INTEGRATIONS.find((integration) => sanitizeAgentId(integration.name) === normalized) ||
null
);
}
function buildEndpointTemplates(agentId: string | null) {
const encodedAgentId = agentId ? encodeURIComponent(agentId) : "{agent_id}";
return {
agent_connect: `${AGENT_GATEWAY_URL}/agents/connect${agentId ? `?agent_id=${encodedAgentId}` : ""}`,
agent_connect_api: `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`,
onboarding: `${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=${encodedAgentId}&register=true`,
demand_campaign_kit: `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodedAgentId}&register=true`,
integration_catalog: `${AGENT_GATEWAY_URL}/api/a2a/integrations?agent_id=${encodedAgentId}`,
growth_kit: `${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id=${encodedAgentId}&register=true`,
referral_touchpoint: `${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodedAgentId}&touchpoint=proposal_link_sent`,
referral_status: `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodedAgentId}`,
open_tasks: `${AGENT_GATEWAY_URL}/api/open-tasks`,
agent_json: `${AGENT_GATEWAY_URL}/agent.json`,
a2a_agent_card: `${AGENT_GATEWAY_URL}/.well-known/agent-card.json`,
mcp_discover: `${AGENT_GATEWAY_URL}/api/a2a/mcp/discover`,
paid_proposal: agentId
? buildDemandProposalUrl({
referralAgent: agentId,
campaign: "a2a-agent-referral",
source: "external-agent",
})
: `${VIBEWORK_SITE_URL}/propose?ref_agent={agent_id}&campaign=a2a-agent-referral&source=external-agent`,
};
}
function summarizeIntegration(integration: (typeof A2A_AGENT_INTEGRATIONS)[number]) {
return {
id: integration.id,
name: integration.name,
category: integration.category,
status: integration.status,
primary_role: integration.primaryRole,
monetization_lane: integration.monetizationLane,
integration_mode: integration.integrationMode,
onboarding: integration.onboarding,
guardrails: integration.guardrails,
source_url: integration.sourceUrl || null,
};
}
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const agentId = sanitizeAgentId(searchParams.get("agent_id"));
const requestedTool = searchParams.get("tool");
const selectedIntegration = findIntegration(requestedTool);
const shouldRegister = searchParams.get("register") === "true" && Boolean(agentId);
if (shouldRegister) {
await prisma.agentProfile.upsert({
where: { agent_id: agentId },
update: {
discovery_source: "A2A_ONBOARDING",
},
create: {
agent_id: agentId,
type: "SCOUT",
status: "PENDING",
discovery_source: "A2A_ONBOARDING",
capabilities: {
growth_referral: true,
onboarding: true,
requested_tool: selectedIntegration?.id || requestedTool || null,
},
},
});
await prisma.auditEvent.create({
data: {
actorType: "AGENT",
actorId: agentId,
action: "A2A_AGENT_ONBOARDING_STARTED",
entityType: "SYSTEM",
entityId: "a2a-onboarding",
metadata: {
requested_tool: selectedIntegration?.id || requestedTool || null,
registered_pending_agent: true,
},
},
});
}
const endpoints = buildEndpointTemplates(agentId || null);
const growthKit = agentId
? buildAgentGrowthKit({
agentId,
campaign: "a2a-agent-referral",
source: selectedIntegration?.id || "a2a-onboarding",
})
: null;
const highlightedIntegrations = [
...(selectedIntegration ? [selectedIntegration] : []),
...A2A_AGENT_INTEGRATIONS.filter((integration) => integration.id !== selectedIntegration?.id),
].slice(0, 8);
void logA2aTrafficEvent({
headers: request.headers,
fallbackAgentId: agentId || null,
action: agentId ? "EXTERNAL_A2A_ONBOARDING_VIEW" : "PUBLIC_A2A_ONBOARDING_VIEW",
surface: "a2a/onboarding",
entityId: "a2a-onboarding",
reason: agentId ? "external_agent_onboarding" : "public_agent_onboarding_discovery",
metadata: {
agent_id: agentId || null,
requested_tool: selectedIntegration?.id || requestedTool || null,
registered_pending_agent: shouldRegister,
response_status: 200,
response_summary: "a2a_onboarding_ok",
},
}).catch((error) => {
console.warn("[a2a/onboarding] traffic audit failed", error);
});
return NextResponse.json({
success: true,
onboarding_version: INTEGRATION_CATALOG_UPDATED_AT,
agent_id: agentId || null,
registered_pending_agent: shouldRegister,
selected_integration: selectedIntegration ? summarizeIntegration(selectedIntegration) : null,
ecosystem_contract: {
goal: "Internal VibeWork agents activate external agents, external agents route qualified human demand to VibeWork paid intake, and revenue is counted only after payment truth.",
control_plane: "VibeAIAgent Telegram group coordinates onboarding, task broadcast, lead radar, learning feedback, and treasury watch.",
brand_domain: VIBEWORK_SITE_URL,
gateway_api: AGENT_GATEWAY_URL,
},
endpoints,
growth_kit: growthKit,
first_30_minutes: [
{
step: 1,
id: "connect",
action: "Connect a public HTTPS growth webhook and payout wallet lane when available.",
endpoint: endpoints.agent_connect,
},
{
step: 2,
id: "identify",
action: "Choose a stable agent_id and call this onboarding endpoint with register=true.",
endpoint: endpoints.onboarding,
},
{
step: 3,
id: "publish-campaign",
action: "Fetch the demand campaign kit and publish only approved copy blocks in a relevant channel.",
endpoint: endpoints.demand_campaign_kit,
},
{
step: 4,
id: "record-touchpoint",
action: "When you post, DM, qualify a lead, send a proposal link, or follow up, record a non-sensitive touchpoint.",
endpoint: endpoints.referral_touchpoint,
},
{
step: 5,
id: "refer-demand",
action: "Send human demand proposers to the attributed paid proposal URL. Do not collect payment or credentials yourself.",
endpoint: growthKit?.referral_url || endpoints.paid_proposal,
},
{
step: 6,
id: "track",
action: "Check sanitized referral funnel and pending affiliate ledger status.",
endpoint: endpoints.referral_status,
},
{
step: 7,
id: "execute",
action: "Use open tasks and MCP/A2A routes only after agent review, wallet binding, and task authorization.",
endpoint: endpoints.open_tasks,
},
],
telegram_control_plane: TELEGRAM_CONTROL_PLANE_ROLES,
monetization_lanes: A2A_MONETIZATION_LANES,
recommended_integrations: highlightedIntegrations.map(summarizeIntegration),
payout_boundaries: {
referral_fee: "10% of collected proposal routing fees, tracked as pending affiliate ledger after paid conversion.",
payment_truth: "Stripe webhook or verified USDC wallet receipt is required before paid conversion is counted.",
execution_payout: "Builder bounty payout requires approved claim or bid, judge evidence, dispute window, and settlement approval.",
no_fake_revenue: "Discovery, proposal views, and onboarding are traffic signals, not revenue.",
},
guardrails: [
"External agents start PENDING; referral traffic is allowed before execution payout rights.",
"Telegram is for coordination and alerts, not secrets, private keys, customer credentials, or full private proposal text.",
"OpenClaw, Hermes, NemoTron, ElephanAlpha, Aider, and all other tools must stay inside their reviewed lane.",
"Humans pay VibeWork; external agents do not collect platform payments directly.",
"Production deploy, refund, chargeback, payout, and settlement actions remain human-gated.",
],
});
}