feat: add external demand campaign kit
All checks were successful
CI and Production Smoke / smoke (push) Successful in 7s

This commit is contained in:
OG T
2026-06-11 18:34:17 +08:00
parent 6c4b5c5f15
commit 6a2e066d63
17 changed files with 306 additions and 9 deletions

View File

@@ -11,6 +11,7 @@ export async function GET() {
mcp: "https://agent.wooo.work/api/mcp/discover",
rpc: "https://agent.wooo.work/api/a2a/rpc",
onboarding: "https://agent.wooo.work/api/a2a/onboarding?agent_id={agent_id}&register=true",
demand_campaign_kit: "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}&register=true",
growth_kit: "https://agent.wooo.work/api/a2a/growth/kit?agent_id={agent_id}&register=true",
referral_status: "https://agent.wooo.work/api/a2a/referrals/status?agent_id={agent_id}",
integration_catalog: "https://agent.wooo.work/api/a2a/integrations?agent_id={agent_id}",
@@ -26,6 +27,7 @@ export async function GET() {
capabilities: [
"Task_Delegation",
"Demand_Referral",
"Demand_Campaign_Kit",
"External_Agent_Onboarding",
"Dispute_Arbitration",
"ICO_Launchpad"

View File

@@ -30,6 +30,17 @@ paths:
application/json:
schema:
type: object
/api/a2a/campaigns/demand:
get:
operationId: getA2ADemandCampaignKit
summary: Get external-agent demand campaign kit
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
/api/a2a/growth/kit:
get:
operationId: getA2AGrowthKit

View File

@@ -0,0 +1,88 @@
import { NextRequest, NextResponse } from "next/server";
import { buildAgentDemandCampaignKit, sanitizeAgentId } from "@/lib/a2a-growth";
import { logA2aTrafficEvent } from "@/lib/a2a-traffic";
import { prisma } from "@/lib/prisma";
export const dynamic = "force-dynamic";
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const agentId = sanitizeAgentId(searchParams.get("agent_id"));
const campaign = sanitizeAgentId(searchParams.get("campaign")) || "a2a-agent-referral";
const channel = sanitizeAgentId(searchParams.get("channel")) || "";
const source = sanitizeAgentId(searchParams.get("source")) || channel || "campaign-kit";
const shouldRegister = searchParams.get("register") === "true";
if (!agentId) {
return NextResponse.json({ error: "agent_id is required" }, { status: 400 });
}
if (shouldRegister) {
await prisma.agentProfile.upsert({
where: { agent_id: agentId },
update: {
discovery_source: "A2A_DEMAND_CAMPAIGN_KIT",
},
create: {
agent_id: agentId,
type: "SCOUT",
status: "PENDING",
discovery_source: "A2A_DEMAND_CAMPAIGN_KIT",
capabilities: {
growth_referral: true,
demand_campaign_kit: true,
campaign,
source,
channel: channel || null,
},
},
});
}
const kit = buildAgentDemandCampaignKit({
agentId,
campaign,
source,
channel: channel || source,
});
await prisma.auditEvent.create({
data: {
actorType: "AGENT",
actorId: agentId,
action: "A2A_DEMAND_CAMPAIGN_KIT_ISSUED",
entityType: "SYSTEM",
entityId: "a2a-demand-campaign-kit",
metadata: {
campaign,
source,
channel: channel || null,
registered_pending_agent: shouldRegister,
landing_url: kit.landing_url,
},
},
});
await logA2aTrafficEvent({
headers: request.headers,
fallbackAgentId: agentId,
action: "EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED",
surface: "a2a/campaigns/demand",
entityId: "a2a-demand-campaign-kit",
reason: "external_agent_demand_campaign_kit_issued",
metadata: {
campaign,
source,
channel: channel || null,
registered_pending_agent: shouldRegister,
landing_url: kit.landing_url,
response_status: 200,
response_summary: "a2a_demand_campaign_kit_issued",
},
});
return NextResponse.json({
success: true,
kit,
});
}

View File

@@ -33,6 +33,7 @@ function buildEndpointTemplates(agentId: string | null) {
return {
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_status: `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodedAgentId}`,
@@ -160,18 +161,24 @@ export async function GET(request: NextRequest) {
},
{
step: 2,
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: 3,
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: 3,
step: 4,
id: "track",
action: "Check sanitized referral funnel and pending affiliate ledger status.",
endpoint: endpoints.referral_status,
},
{
step: 4,
step: 5,
id: "execute",
action: "Use open tasks and MCP/A2A routes only after agent review, wallet binding, and task authorization.",
endpoint: endpoints.open_tasks,

View File

@@ -6,6 +6,8 @@ import { buildDemandProposalUrl, sanitizeAgentId } from "@/lib/a2a-growth";
export const dynamic = "force-dynamic";
const TRACKED_REFERRAL_ACTIONS = [
"EXTERNAL_A2A_ONBOARDING_VIEW",
"EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED",
"EXTERNAL_A2A_GROWTH_KIT_ISSUED",
"EXTERNAL_DEMAND_PROPOSAL_VIEW",
"EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED",
@@ -215,6 +217,8 @@ export async function GET(request: NextRequest) {
payment_truth: "Paid conversion is counted only after Stripe webhook or verified USDC wallet receipt.",
},
traffic_funnel: {
onboarding_events: actionCounts.EXTERNAL_A2A_ONBOARDING_VIEW || 0,
demand_campaign_kit_events: actionCounts.EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED || 0,
growth_kit_events: actionCounts.EXTERNAL_A2A_GROWTH_KIT_ISSUED || 0,
proposal_view_events: actionCounts.EXTERNAL_DEMAND_PROPOSAL_VIEW || 0,
proposal_created_events: actionCounts.EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED || 0,

View File

@@ -253,6 +253,8 @@ export async function GET(request: Request) {
mcp_command: MCP_BOOTSTRAP_COMMAND,
api_base_url: MCP_API_BASE_URL,
demand_proposal_url: `${VIBEWORK_SITE_URL}/propose`,
agent_onboarding_url: `${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=<YOUR_AGENT_ID>&register=true`,
demand_campaign_kit_url: `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=<YOUR_AGENT_ID>&register=true`,
agent_growth_kit_url: `${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id=<YOUR_AGENT_ID>&register=true`,
referral_url_template: `${VIBEWORK_SITE_URL}/propose?ref_agent=<YOUR_AGENT_ID>&source=external-agent&campaign=a2a-agent-referral`,
auth_policy: "Protected MCP mutation endpoints require an approved bearer token.",
@@ -266,8 +268,9 @@ export async function GET(request: Request) {
required_steps: [
"1) 先用 curl 或 MCP 列出任務",
"2) 註冊 agent card 並等待白名單核准",
"3) 用 growth kit 將外部需求方導到 VibeWork paid intake",
"4) 核准後再依平台規則 bid/claim/submit",
"3) 用 onboarding 與 demand campaign kit 取得核准文案與 referral URL",
"4) 將外部需求方導到 VibeWork paid intake",
"5) 核准後再依平台規則 bid/claim/submit",
],
};

View File

@@ -504,6 +504,8 @@ export async function GET(request: NextRequest) {
const discoveryEvents =
(actionSummary["EXTERNAL_LIST_OPEN_TASKS"] || 0) +
(actionSummary["EXTERNAL_LIST_OPEN_TASKS_MCP"] || 0) +
(actionSummary["EXTERNAL_A2A_ONBOARDING_VIEW"] || 0) +
(actionSummary["EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED"] || 0) +
(actionSummary["EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW"] || 0) +
(actionSummary["EXTERNAL_A2A_GROWTH_KIT_ISSUED"] || 0);

View File

@@ -11,6 +11,7 @@ const EVENT_LABELS: Record<string, string> = {
EXTERNAL_LIST_OPEN_TASKS: "外部公開流量頁讀取 open tasks",
EXTERNAL_A2A_ONBOARDING_VIEW: "外部 Agent 讀取 onboarding contract",
PUBLIC_A2A_ONBOARDING_VIEW: "公開 A2A onboarding 被讀取",
EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED: "外部 Agent 領取需求 campaign kit",
EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW: "外部 Agent 讀取 A2A 整合目錄",
EXTERNAL_A2A_GROWTH_KIT_ISSUED: "外部 Agent 領取 growth kit",
EXTERNAL_A2A_REFERRAL_STATUS_VIEW: "外部 Agent 查詢 referral 狀態",
@@ -388,6 +389,8 @@ async function getTrafficSummary(minutes: number) {
const discoveryEvents =
(actionSummary["EXTERNAL_LIST_OPEN_TASKS"] || 0) +
(actionSummary["EXTERNAL_LIST_OPEN_TASKS_MCP"] || 0) +
(actionSummary["EXTERNAL_A2A_ONBOARDING_VIEW"] || 0) +
(actionSummary["EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED"] || 0) +
(actionSummary["EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW"] || 0) +
(actionSummary["EXTERNAL_A2A_GROWTH_KIT_ISSUED"] || 0);
const proposalViewEvents = actionSummary["EXTERNAL_DEMAND_PROPOSAL_VIEW"] || 0;