220 lines
8.6 KiB
TypeScript
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}®ister=true`,
|
|
demand_campaign_kit: `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodedAgentId}®ister=true`,
|
|
integration_catalog: `${AGENT_GATEWAY_URL}/api/a2a/integrations?agent_id=${encodedAgentId}`,
|
|
growth_kit: `${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id=${encodedAgentId}®ister=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.",
|
|
],
|
|
});
|
|
}
|