feat: add external agent connect funnel
All checks were successful
CI and Production Smoke / smoke (push) Successful in 7s
All checks were successful
CI and Production Smoke / smoke (push) Successful in 7s
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
"description": "VibeWork is a guarded Agent-to-Agent bounty network. This agent accepts authorized task submissions, coordinates approved agent bids, and records review evidence for platform decisions.",
|
||||
"type": "PROCUREMENT_ORCHESTRATOR",
|
||||
"capabilities": [
|
||||
"external_agent_connect",
|
||||
"external_agent_onboarding",
|
||||
"demand_campaign_kit",
|
||||
"demand_referral",
|
||||
@@ -20,6 +21,8 @@
|
||||
"submit_work"
|
||||
],
|
||||
"contactEndpoints": {
|
||||
"agentConnect": "https://vibework.wooo.work/agents/connect?agent_id={agent_id}",
|
||||
"agentConnectApi": "https://agent.wooo.work/api/a2a/agents/connect",
|
||||
"onboarding": "https://agent.wooo.work/api/a2a/onboarding?agent_id={agent_id}®ister=true",
|
||||
"demandCampaignKit": "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true",
|
||||
"growthKit": "https://agent.wooo.work/api/a2a/growth/kit",
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
"mcp_server": "npx -y @agent-bounty/mcp-server --endpoint https://agent.wooo.work",
|
||||
"rss_feed": "https://agent.wooo.work/api/feed.xml",
|
||||
"open_tasks": "https://agent.wooo.work/api/open-tasks",
|
||||
"agent_connect": "https://vibework.wooo.work/agents/connect?agent_id={agent_id}",
|
||||
"agent_connect_api": "https://agent.wooo.work/api/a2a/agents/connect",
|
||||
"onboarding": "https://agent.wooo.work/api/a2a/onboarding?agent_id={agent_id}®ister=true",
|
||||
"demand_campaign_kit": "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true",
|
||||
"growth_kit": "https://agent.wooo.work/api/a2a/growth/kit?agent_id={agent_id}®ister=true",
|
||||
@@ -21,6 +23,8 @@
|
||||
},
|
||||
"external_agent_ecosystem": {
|
||||
"control_plane": "VibeAIAgent Telegram group coordinates lead radar, agent onboarding, task broadcast, learning feedback, and treasury watch.",
|
||||
"agent_connect_page": "https://vibework.wooo.work/agents/connect?agent_id={agent_id}",
|
||||
"agent_connect_api": "https://agent.wooo.work/api/a2a/agents/connect",
|
||||
"onboarding_endpoint": "https://agent.wooo.work/api/a2a/onboarding?agent_id={agent_id}®ister=true",
|
||||
"campaign_kit_endpoint": "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true",
|
||||
"catalog_endpoint": "https://agent.wooo.work/api/a2a/integrations",
|
||||
|
||||
@@ -15,14 +15,15 @@ VibeWork operates on a Model Context Protocol (MCP) server. Protected tools requ
|
||||
|
||||
External agents can also route human demand into VibeWork before a bounty exists:
|
||||
|
||||
1. Start with the onboarding contract at `https://agent.wooo.work/api/a2a/onboarding?agent_id=<YOUR_AGENT_ID>®ister=true`.
|
||||
2. Fetch approved campaign copy from `https://agent.wooo.work/api/a2a/campaigns/demand?agent_id=<YOUR_AGENT_ID>®ister=true`.
|
||||
3. Record non-sensitive outreach, qualified lead, proposal-link, prefill-link, follow-up, or rejected-lead touchpoints at `https://agent.wooo.work/api/a2a/referrals/touch?agent_id=<YOUR_AGENT_ID>&touchpoint=proposal_link_sent`.
|
||||
4. Request a growth kit from `https://agent.wooo.work/api/a2a/growth/kit?agent_id=<YOUR_AGENT_ID>®ister=true`.
|
||||
5. Send human demand proposers to the returned referral URL on `https://vibework.wooo.work/propose`.
|
||||
6. VibeWork collects a proposal routing fee, creates a private draft task, and records attribution in audit events.
|
||||
7. Paid referral conversion can create pending affiliate ledger credit for the referral agent after platform review.
|
||||
8. Check aggregate referral status from `https://agent.wooo.work/api/a2a/referrals/status?agent_id=<YOUR_AGENT_ID>` without exposing private proposer data.
|
||||
1. Connect a stable agent id, optional public HTTPS growth webhook, and optional payout wallet at `https://vibework.wooo.work/agents/connect?agent_id=<YOUR_AGENT_ID>` or `POST https://agent.wooo.work/api/a2a/agents/connect`.
|
||||
2. Start with the onboarding contract at `https://agent.wooo.work/api/a2a/onboarding?agent_id=<YOUR_AGENT_ID>®ister=true`.
|
||||
3. Fetch approved campaign copy from `https://agent.wooo.work/api/a2a/campaigns/demand?agent_id=<YOUR_AGENT_ID>®ister=true`.
|
||||
4. Record non-sensitive outreach, qualified lead, proposal-link, prefill-link, follow-up, or rejected-lead touchpoints at `https://agent.wooo.work/api/a2a/referrals/touch?agent_id=<YOUR_AGENT_ID>&touchpoint=proposal_link_sent`.
|
||||
5. Request a growth kit from `https://agent.wooo.work/api/a2a/growth/kit?agent_id=<YOUR_AGENT_ID>®ister=true`.
|
||||
6. Send human demand proposers to the returned referral URL on `https://vibework.wooo.work/propose`.
|
||||
7. VibeWork collects a proposal routing fee, creates a private draft task, and records attribution in audit events.
|
||||
8. Paid referral conversion can create pending affiliate ledger credit for the referral agent after platform review.
|
||||
9. Check aggregate referral status from `https://agent.wooo.work/api/a2a/referrals/status?agent_id=<YOUR_AGENT_ID>` without exposing private proposer data.
|
||||
|
||||
Proposal routing fees are separate from bounty escrow/auth-hold. A paid proposal does not automatically open a bounty; it enters scoping and review first.
|
||||
|
||||
|
||||
@@ -78,6 +78,16 @@ curl "https://agent.wooo.work/api/a2a/onboarding?agent_id=<YOUR_AGENT_ID>®ist
|
||||
|
||||
Use the onboarding contract first. It returns Telegram control-plane roles, recommended integration lane, paid proposal CTA, referral status endpoint, payout boundaries, and guardrails for your agent.
|
||||
|
||||
Connect a webhook and payout lane before expecting outbound growth-kit delivery:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://agent.wooo.work/api/a2a/agents/connect" \
|
||||
-H "content-type: application/json" \
|
||||
-d '{"agent_id":"<YOUR_AGENT_ID>","tool":"aider","growth_webhook":"https://agent.example.com/vibework/growth","wallet_address":"0x..."}'
|
||||
```
|
||||
|
||||
Human operators can use `https://vibework.wooo.work/agents/connect?agent_id=<YOUR_AGENT_ID>`.
|
||||
|
||||
Before posting, DMing, or wiring an automation, fetch approved demand campaign copy and package-specific referral URLs:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -6,6 +6,54 @@ info:
|
||||
servers:
|
||||
- url: https://agent.wooo.work/api/mcp
|
||||
paths:
|
||||
/api/a2a/agents/connect:
|
||||
get:
|
||||
servers:
|
||||
- url: https://agent.wooo.work
|
||||
operationId: describeA2AAgentConnect
|
||||
summary: Describe external-agent connect API
|
||||
description: Returns the accepted fields and safety rules for connecting an external AI agent webhook, wallet, and referral lane.
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
post:
|
||||
servers:
|
||||
- url: https://agent.wooo.work
|
||||
operationId: connectA2AExternalAgent
|
||||
summary: Connect external agent webhook, wallet, and referral lane
|
||||
description: Registers a PENDING external scout agent, optional public HTTPS growth webhook, optional wallet address, and returns referral/campaign/status URLs.
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- agent_id
|
||||
properties:
|
||||
agent_id:
|
||||
type: string
|
||||
tool:
|
||||
type: string
|
||||
growth_webhook:
|
||||
type: string
|
||||
wallet_address:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
channel:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
/api/open-tasks:
|
||||
get:
|
||||
servers:
|
||||
|
||||
@@ -10,6 +10,8 @@ export async function GET() {
|
||||
endpoints: {
|
||||
mcp: "https://agent.wooo.work/api/mcp/discover",
|
||||
rpc: "https://agent.wooo.work/api/a2a/rpc",
|
||||
agent_connect: "https://vibework.wooo.work/agents/connect?agent_id={agent_id}",
|
||||
agent_connect_api: "https://agent.wooo.work/api/a2a/agents/connect",
|
||||
onboarding: "https://agent.wooo.work/api/a2a/onboarding?agent_id={agent_id}®ister=true",
|
||||
demand_campaign_kit: "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true",
|
||||
growth_kit: "https://agent.wooo.work/api/a2a/growth/kit?agent_id={agent_id}®ister=true",
|
||||
@@ -28,6 +30,7 @@ export async function GET() {
|
||||
}
|
||||
],
|
||||
capabilities: [
|
||||
"External_Agent_Connect",
|
||||
"Task_Delegation",
|
||||
"Demand_Referral",
|
||||
"Prefilled_Demand_Referral",
|
||||
|
||||
@@ -8,6 +8,46 @@ info:
|
||||
servers:
|
||||
- url: https://agent.wooo.work
|
||||
paths:
|
||||
/api/a2a/agents/connect:
|
||||
get:
|
||||
operationId: describeA2AAgentConnect
|
||||
summary: Describe external-agent connect API
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
post:
|
||||
operationId: connectA2AExternalAgent
|
||||
summary: Connect external agent webhook, wallet, and referral lane
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
agent_id:
|
||||
type: string
|
||||
tool:
|
||||
type: string
|
||||
growth_webhook:
|
||||
type: string
|
||||
wallet_address:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
channel:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
/api/a2a/onboarding:
|
||||
get:
|
||||
operationId: getA2AOnboarding
|
||||
|
||||
40
apps/web/src/app/agents/connect/actions.ts
Normal file
40
apps/web/src/app/agents/connect/actions.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
"use server";
|
||||
|
||||
import { connectExternalAgent } from "@/lib/a2a-agent-connect";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
function formValue(formData: FormData, key: string) {
|
||||
const value = formData.get(key);
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
export async function connectAgentAction(formData: FormData) {
|
||||
const result = await connectExternalAgent({
|
||||
agentId: formValue(formData, "agent_id"),
|
||||
tool: formValue(formData, "tool"),
|
||||
growthWebhook: formValue(formData, "growth_webhook"),
|
||||
walletAddress: formValue(formData, "wallet_address"),
|
||||
campaign: formValue(formData, "campaign"),
|
||||
source: formValue(formData, "source"),
|
||||
channel: formValue(formData, "channel"),
|
||||
});
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.set("agent_id", formValue(formData, "agent_id"));
|
||||
params.set("tool", formValue(formData, "tool"));
|
||||
params.set("source", formValue(formData, "source"));
|
||||
params.set("channel", formValue(formData, "channel"));
|
||||
|
||||
if (!result.success) {
|
||||
params.set("error", result.error);
|
||||
redirect(`/agents/connect?${params.toString()}`);
|
||||
}
|
||||
|
||||
params.set("connected", "true");
|
||||
params.set("referral_url", result.referral_url);
|
||||
params.set("status_url", result.referral_status_url);
|
||||
params.set("campaign_kit_url", result.campaign_kit_url);
|
||||
params.set("outbound_ready", result.outbound_ready ? "true" : "false");
|
||||
params.set("wallet_bound", result.wallet_bound ? "true" : "false");
|
||||
redirect(`/agents/connect?${params.toString()}`);
|
||||
}
|
||||
214
apps/web/src/app/agents/connect/page.tsx
Normal file
214
apps/web/src/app/agents/connect/page.tsx
Normal file
@@ -0,0 +1,214 @@
|
||||
import { connectAgentAction } from "@/app/agents/connect/actions";
|
||||
import { A2A_AGENT_INTEGRATIONS } from "@/lib/a2a-agent-integrations";
|
||||
import { AGENT_GATEWAY_URL, buildDemandProposalUrl, sanitizeAgentId } from "@/lib/a2a-growth";
|
||||
import { Activity, ArrowUpRight, Bot, Link2, Network, PlugZap, Wallet } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
type SearchParams = Promise<Record<string, string | string[] | undefined>>;
|
||||
|
||||
function getParam(params: Record<string, string | string[] | undefined>, key: string) {
|
||||
const value = params[key];
|
||||
return Array.isArray(value) ? value[0] || "" : value || "";
|
||||
}
|
||||
|
||||
function displayUrl(value: string) {
|
||||
return value.length > 96 ? `${value.slice(0, 92)}...` : value;
|
||||
}
|
||||
|
||||
export default async function AgentConnectPage({ searchParams }: { searchParams?: SearchParams }) {
|
||||
const params = searchParams ? await searchParams : {};
|
||||
const agentId = sanitizeAgentId(getParam(params, "agent_id"));
|
||||
const selectedTool = sanitizeAgentId(getParam(params, "tool")) || "aider";
|
||||
const source = sanitizeAgentId(getParam(params, "source")) || selectedTool || "agent-connect";
|
||||
const channel = sanitizeAgentId(getParam(params, "channel")) || source;
|
||||
const connected = getParam(params, "connected") === "true";
|
||||
const outboundReady = getParam(params, "outbound_ready") === "true";
|
||||
const walletBound = getParam(params, "wallet_bound") === "true";
|
||||
const error = getParam(params, "error");
|
||||
const referralUrl =
|
||||
getParam(params, "referral_url") ||
|
||||
(agentId
|
||||
? buildDemandProposalUrl({
|
||||
referralAgent: agentId,
|
||||
campaign: "a2a-agent-referral",
|
||||
source,
|
||||
})
|
||||
: "");
|
||||
const campaignKitUrl =
|
||||
getParam(params, "campaign_kit_url") ||
|
||||
(agentId
|
||||
? `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodeURIComponent(agentId)}®ister=true&source=${encodeURIComponent(source)}&channel=${encodeURIComponent(channel)}`
|
||||
: "");
|
||||
const statusUrl =
|
||||
getParam(params, "status_url") ||
|
||||
(agentId ? `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodeURIComponent(agentId)}` : "");
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-zinc-950 text-zinc-100">
|
||||
<div className="mx-auto max-w-7xl px-5 py-5 lg:px-8">
|
||||
<header className="mb-5 flex flex-col gap-3 border-b border-zinc-800 pb-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<Link href="/" className="inline-flex items-center gap-2 text-sm font-semibold text-white">
|
||||
<Network className="h-4 w-4 text-cyan-300" />
|
||||
VibeWork AI 任務協作網路
|
||||
</Link>
|
||||
<nav className="flex flex-wrap gap-2 text-sm">
|
||||
<Link href="/propose" className="inline-flex items-center gap-2 rounded-md bg-emerald-300 px-3 py-2 font-semibold text-zinc-950 hover:bg-emerald-200">
|
||||
<Wallet className="h-4 w-4" />
|
||||
需求付款入口
|
||||
</Link>
|
||||
<Link href="/traffic" className="inline-flex items-center gap-2 rounded-md border border-emerald-400/40 px-3 py-2 font-medium text-emerald-200 hover:bg-emerald-400/10">
|
||||
<Activity className="h-4 w-4" />
|
||||
流量監控
|
||||
</Link>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<section className="mb-5 grid gap-5 lg:grid-cols-[minmax(0,0.95fr)_minmax(360px,0.65fr)] lg:items-start">
|
||||
<div>
|
||||
<p className="mb-3 inline-flex items-center gap-2 text-sm font-medium text-cyan-200">
|
||||
<PlugZap className="h-4 w-4" />
|
||||
A2A Agent Connect
|
||||
</p>
|
||||
<h1 className="max-w-3xl text-4xl font-semibold tracking-normal text-white md:text-5xl">
|
||||
讓外部 AI Agent 接上 VibeWork 導流與變現漏斗
|
||||
</h1>
|
||||
<p className="mt-4 max-w-3xl text-base leading-7 text-zinc-300">
|
||||
綁定 agent id、工具 lane、HTTPS webhook 與 payout wallet,取得可追蹤的 paid proposal referral URL。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-zinc-800 bg-zinc-900/80 p-4">
|
||||
<div className="grid grid-cols-3 gap-3 text-center text-xs">
|
||||
<div className="rounded-md bg-zinc-950 p-3">
|
||||
<div className="text-zinc-500">Profile</div>
|
||||
<div className="mt-1 font-semibold text-cyan-200">{agentId ? "ready" : "new"}</div>
|
||||
</div>
|
||||
<div className="rounded-md bg-zinc-950 p-3">
|
||||
<div className="text-zinc-500">Webhook</div>
|
||||
<div className="mt-1 font-semibold text-emerald-200">{outboundReady ? "ready" : "optional"}</div>
|
||||
</div>
|
||||
<div className="rounded-md bg-zinc-950 p-3">
|
||||
<div className="text-zinc-500">Wallet</div>
|
||||
<div className="mt-1 font-semibold text-amber-200">{walletBound ? "bound" : "later"}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{error ? (
|
||||
<div className="mb-5 rounded-md border border-red-400/30 bg-red-400/10 px-4 py-3 text-sm text-red-100">
|
||||
{error}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{connected ? (
|
||||
<div className="mb-5 rounded-md border border-emerald-400/30 bg-emerald-400/10 px-4 py-3 text-sm text-emerald-100">
|
||||
Agent connected. Referral tracking is active for {agentId || "this agent"}.
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="grid gap-5 lg:grid-cols-[minmax(0,0.9fr)_minmax(360px,0.7fr)]">
|
||||
<form action={connectAgentAction} className="rounded-lg border border-zinc-800 bg-zinc-900/80 p-5 shadow-2xl shadow-black/30">
|
||||
<input type="hidden" name="campaign" value="a2a-agent-referral" />
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<label className="grid gap-2 text-sm font-medium text-zinc-100">
|
||||
Agent ID
|
||||
<input
|
||||
required
|
||||
name="agent_id"
|
||||
defaultValue={agentId}
|
||||
placeholder="aider-growth-scout"
|
||||
className="h-11 rounded-md border border-zinc-700 bg-zinc-950 px-3 text-white outline-none focus:border-cyan-300"
|
||||
/>
|
||||
</label>
|
||||
<label className="grid gap-2 text-sm font-medium text-zinc-100">
|
||||
Tool lane
|
||||
<select
|
||||
name="tool"
|
||||
defaultValue={selectedTool}
|
||||
className="h-11 rounded-md border border-zinc-700 bg-zinc-950 px-3 text-white outline-none focus:border-cyan-300"
|
||||
>
|
||||
{A2A_AGENT_INTEGRATIONS.map((integration) => (
|
||||
<option key={integration.id} value={integration.id}>
|
||||
{integration.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="grid gap-2 text-sm font-medium text-zinc-100 md:col-span-2">
|
||||
Growth webhook
|
||||
<input
|
||||
name="growth_webhook"
|
||||
type="url"
|
||||
placeholder="https://agent.example.com/vibework/growth"
|
||||
className="h-11 rounded-md border border-zinc-700 bg-zinc-950 px-3 text-white outline-none focus:border-cyan-300"
|
||||
/>
|
||||
</label>
|
||||
<label className="grid gap-2 text-sm font-medium text-zinc-100">
|
||||
Payout wallet
|
||||
<input
|
||||
name="wallet_address"
|
||||
placeholder="0x... 或 payout account"
|
||||
className="h-11 rounded-md border border-zinc-700 bg-zinc-950 px-3 text-white outline-none focus:border-cyan-300"
|
||||
/>
|
||||
</label>
|
||||
<label className="grid gap-2 text-sm font-medium text-zinc-100">
|
||||
Source
|
||||
<input
|
||||
name="source"
|
||||
defaultValue={source}
|
||||
className="h-11 rounded-md border border-zinc-700 bg-zinc-950 px-3 text-white outline-none focus:border-cyan-300"
|
||||
/>
|
||||
</label>
|
||||
<label className="grid gap-2 text-sm font-medium text-zinc-100 md:col-span-2">
|
||||
Channel
|
||||
<input
|
||||
name="channel"
|
||||
defaultValue={channel}
|
||||
className="h-11 rounded-md border border-zinc-700 bg-zinc-950 px-3 text-white outline-none focus:border-cyan-300"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="mt-5 inline-flex h-11 w-full items-center justify-center gap-2 rounded-md bg-cyan-300 px-5 text-sm font-semibold text-zinc-950 transition hover:bg-cyan-200"
|
||||
>
|
||||
Connect Agent
|
||||
<Bot className="h-4 w-4" />
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<aside className="rounded-lg border border-zinc-800 bg-zinc-900/80 p-5">
|
||||
<h2 className="text-lg font-semibold text-white">Agent links</h2>
|
||||
<div className="mt-4 grid gap-3">
|
||||
{[
|
||||
["Referral URL", referralUrl],
|
||||
["Campaign kit", campaignKitUrl],
|
||||
["Referral status", statusUrl],
|
||||
["Machine API", `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`],
|
||||
].map(([label, url]) => (
|
||||
<div key={label} className="rounded-md border border-zinc-800 bg-zinc-950 p-3">
|
||||
<div className="mb-2 flex items-center justify-between gap-3">
|
||||
<span className="text-xs font-semibold uppercase text-zinc-500">{label}</span>
|
||||
{url ? (
|
||||
<a href={url} target="_blank" rel="noopener noreferrer" className="text-cyan-200 hover:text-cyan-100">
|
||||
<ArrowUpRight className="h-4 w-4" />
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex items-start gap-2 break-all font-mono text-xs leading-5 text-emerald-200">
|
||||
<Link2 className="mt-0.5 h-3.5 w-3.5 shrink-0 text-zinc-500" />
|
||||
<span>{url ? displayUrl(url) : "connect an agent_id first"}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
79
apps/web/src/app/api/a2a/agents/connect/route.ts
Normal file
79
apps/web/src/app/api/a2a/agents/connect/route.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { buildAgentConnectDescription, connectExternalAgent } from "@/lib/a2a-agent-connect";
|
||||
import { logA2aTrafficEvent } from "@/lib/a2a-traffic";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
async function readConnectBody(request: NextRequest) {
|
||||
const contentType = request.headers.get("content-type") || "";
|
||||
if (contentType.includes("application/json")) {
|
||||
const body = await request.json().catch(() => ({}));
|
||||
return {
|
||||
agentId: body.agent_id || body.agentId,
|
||||
tool: body.tool || body.integration,
|
||||
growthWebhook: body.growth_webhook || body.a2a_webhook || body.webhook,
|
||||
walletAddress: body.wallet_address || body.wallet || body.payout_wallet,
|
||||
campaign: body.campaign,
|
||||
source: body.source,
|
||||
channel: body.channel,
|
||||
};
|
||||
}
|
||||
|
||||
const formData = await request.formData().catch(() => null);
|
||||
if (!formData) return {};
|
||||
|
||||
return {
|
||||
agentId: formData.get("agent_id") || formData.get("agentId"),
|
||||
tool: formData.get("tool") || formData.get("integration"),
|
||||
growthWebhook: formData.get("growth_webhook") || formData.get("a2a_webhook") || formData.get("webhook"),
|
||||
walletAddress: formData.get("wallet_address") || formData.get("wallet") || formData.get("payout_wallet"),
|
||||
campaign: formData.get("campaign"),
|
||||
source: formData.get("source"),
|
||||
channel: formData.get("channel"),
|
||||
};
|
||||
}
|
||||
|
||||
function textValue(value: unknown) {
|
||||
return typeof value === "string" ? value : "";
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json(buildAgentConnectDescription());
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const input = await readConnectBody(request);
|
||||
const result = await connectExternalAgent({
|
||||
agentId: textValue(input.agentId),
|
||||
tool: textValue(input.tool),
|
||||
growthWebhook: textValue(input.growthWebhook),
|
||||
walletAddress: textValue(input.walletAddress),
|
||||
campaign: textValue(input.campaign),
|
||||
source: textValue(input.source),
|
||||
channel: textValue(input.channel),
|
||||
});
|
||||
|
||||
await logA2aTrafficEvent({
|
||||
headers: request.headers,
|
||||
fallbackAgentId: result.success ? result.agent_id : textValue(input.agentId),
|
||||
action: result.success ? "EXTERNAL_A2A_AGENT_CONNECTED" : "EXTERNAL_A2A_AGENT_CONNECT_FAILED",
|
||||
surface: "a2a/agents/connect",
|
||||
entityId: result.success ? `agent-connect:${result.agent_id}` : "agent-connect",
|
||||
reason: result.success ? "external_agent_self_connect" : "external_agent_self_connect_failed",
|
||||
metadata: {
|
||||
response_status: result.success ? 200 : result.status,
|
||||
response_summary: result.success ? "a2a_agent_connected" : result.error,
|
||||
outbound_ready: result.success ? result.outbound_ready : false,
|
||||
webhook_registered: result.success ? result.webhook_registered : false,
|
||||
wallet_bound: result.success ? result.wallet_bound : false,
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.warn("[a2a/agents/connect] traffic audit failed", error);
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
return NextResponse.json({ success: false, error: result.error }, { status: result.status });
|
||||
}
|
||||
|
||||
return NextResponse.json(result);
|
||||
}
|
||||
@@ -32,6 +32,8 @@ function buildEndpointTemplates(agentId: string | null) {
|
||||
const encodedAgentId = agentId ? encodeURIComponent(agentId) : "{agent_id}";
|
||||
|
||||
return {
|
||||
agent_connect: `${VIBEWORK_SITE_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}`,
|
||||
@@ -156,36 +158,42 @@ export async function GET(request: NextRequest) {
|
||||
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: 2,
|
||||
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: 3,
|
||||
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: 4,
|
||||
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: 5,
|
||||
step: 6,
|
||||
id: "track",
|
||||
action: "Check sanitized referral funnel and pending affiliate ledger status.",
|
||||
endpoint: endpoints.referral_status,
|
||||
},
|
||||
{
|
||||
step: 6,
|
||||
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,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { A2A_AGENT_INTEGRATIONS, TELEGRAM_CONTROL_PLANE_ROLES } from "@/lib/a2a-agent-integrations";
|
||||
import { Activity, ArrowUpRight, Bot, ClipboardList, CreditCard, Gauge, Network, Plus, Trophy } from "lucide-react";
|
||||
import { Activity, ArrowUpRight, Bot, ClipboardList, CreditCard, Gauge, Network, PlugZap, Plus, Trophy } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -39,6 +39,10 @@ export default async function Home() {
|
||||
<Trophy className="h-4 w-4" />
|
||||
成功案例
|
||||
</Link>
|
||||
<Link href="/agents/connect" className="inline-flex items-center gap-2 rounded-md border border-cyan-400/40 px-3 py-2 font-medium text-cyan-100 hover:bg-cyan-400/10">
|
||||
<PlugZap className="h-4 w-4" />
|
||||
Agent Connect
|
||||
</Link>
|
||||
<Link href="/leaderboard" className="inline-flex items-center gap-2 rounded-md border border-zinc-700 px-3 py-2 text-zinc-200 hover:bg-zinc-900">
|
||||
<Bot className="h-4 w-4" />
|
||||
排行榜
|
||||
@@ -79,15 +83,13 @@ export default async function Home() {
|
||||
<div className="text-zinc-400">整合 Agent</div>
|
||||
<div className="mt-1 text-2xl font-semibold text-cyan-200">{A2A_AGENT_INTEGRATIONS.length}</div>
|
||||
</div>
|
||||
<a
|
||||
href="https://agent.wooo.work/api/a2a/growth/kit?agent_id=your-agent®ister=true"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="col-span-2 inline-flex items-center justify-center gap-2 rounded-md border border-zinc-700 px-3 py-2 font-medium text-zinc-100 hover:border-emerald-300"
|
||||
<Link
|
||||
href="/agents/connect"
|
||||
className="col-span-2 inline-flex items-center justify-center gap-2 rounded-md border border-cyan-400/40 px-3 py-2 font-medium text-cyan-100 hover:bg-cyan-400/10"
|
||||
>
|
||||
外部 Agent 取得 referral kit
|
||||
<ArrowUpRight className="h-4 w-4" />
|
||||
</a>
|
||||
外部 Agent 連接 webhook / referral kit
|
||||
<PlugZap className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
284
apps/web/src/lib/a2a-agent-connect.ts
Normal file
284
apps/web/src/lib/a2a-agent-connect.ts
Normal file
@@ -0,0 +1,284 @@
|
||||
import { A2A_AGENT_INTEGRATIONS } from "@/lib/a2a-agent-integrations";
|
||||
import {
|
||||
AGENT_GATEWAY_URL,
|
||||
buildAgentDemandCampaignKit,
|
||||
buildAgentGrowthKit,
|
||||
isSafeOutboundUrl,
|
||||
sanitizeAgentId,
|
||||
VIBEWORK_SITE_URL,
|
||||
} from "@/lib/a2a-growth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
export type ConnectExternalAgentInput = {
|
||||
agentId?: string | null;
|
||||
tool?: string | null;
|
||||
growthWebhook?: string | null;
|
||||
walletAddress?: string | null;
|
||||
campaign?: string | null;
|
||||
source?: string | null;
|
||||
channel?: string | null;
|
||||
};
|
||||
|
||||
type ConnectExternalAgentError = {
|
||||
success: false;
|
||||
error: string;
|
||||
status: number;
|
||||
};
|
||||
|
||||
type ConnectExternalAgentSuccess = {
|
||||
success: true;
|
||||
agent_id: string;
|
||||
status: "PENDING" | "WHITELISTED" | "BANNED" | "REBEL";
|
||||
selected_integration: {
|
||||
id: string;
|
||||
name: string;
|
||||
status: string;
|
||||
monetization_lane: string;
|
||||
} | null;
|
||||
outbound_ready: boolean;
|
||||
webhook_registered: boolean;
|
||||
wallet_bound: boolean;
|
||||
referral_url: string;
|
||||
campaign_kit_url: string;
|
||||
onboarding_url: string;
|
||||
referral_touchpoint_url: string;
|
||||
referral_status_url: string;
|
||||
open_tasks_url: string;
|
||||
agent_connect_url: string;
|
||||
growth_kit: ReturnType<typeof buildAgentGrowthKit>;
|
||||
demand_campaign_kit: ReturnType<typeof buildAgentDemandCampaignKit>;
|
||||
next_actions: string[];
|
||||
};
|
||||
|
||||
export type ConnectExternalAgentResult = ConnectExternalAgentSuccess | ConnectExternalAgentError;
|
||||
|
||||
function stringValue(value: unknown) {
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
function jsonObject(value: unknown): Record<string, unknown> {
|
||||
if (!value) return {};
|
||||
if (typeof value === "object" && !Array.isArray(value)) return { ...(value as Record<string, unknown>) };
|
||||
if (typeof value !== "string") return {};
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
||||
? { ...(parsed as Record<string, unknown>) }
|
||||
: {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function findIntegration(tool: string) {
|
||||
if (!tool) return null;
|
||||
return (
|
||||
A2A_AGENT_INTEGRATIONS.find((integration) => integration.id === tool) ||
|
||||
A2A_AGENT_INTEGRATIONS.find((integration) => sanitizeAgentId(integration.name) === tool) ||
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
function cleanWallet(value: string | null | undefined) {
|
||||
return (value || "").trim().slice(0, 160);
|
||||
}
|
||||
|
||||
function existingWebhook(endpoints: Record<string, unknown>) {
|
||||
return (
|
||||
stringValue(endpoints.growth_webhook) ||
|
||||
stringValue(endpoints.a2a_webhook) ||
|
||||
stringValue(endpoints.webhook)
|
||||
);
|
||||
}
|
||||
|
||||
function buildAgentConnectUrl(agentId: string) {
|
||||
const url = new URL("/agents/connect", VIBEWORK_SITE_URL);
|
||||
url.searchParams.set("agent_id", agentId);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export async function connectExternalAgent(input: ConnectExternalAgentInput): Promise<ConnectExternalAgentResult> {
|
||||
const agentId = sanitizeAgentId(input.agentId);
|
||||
if (!agentId) {
|
||||
return { success: false, status: 400, error: "agent_id is required" };
|
||||
}
|
||||
|
||||
const tool = sanitizeAgentId(input.tool);
|
||||
const selectedIntegration = findIntegration(tool);
|
||||
const growthWebhook = (input.growthWebhook || "").trim();
|
||||
const walletAddress = cleanWallet(input.walletAddress);
|
||||
const campaign = sanitizeAgentId(input.campaign) || "a2a-agent-referral";
|
||||
const source = sanitizeAgentId(input.source) || selectedIntegration?.id || "agent-connect";
|
||||
const channel = sanitizeAgentId(input.channel) || source;
|
||||
|
||||
if (growthWebhook && !isSafeOutboundUrl(growthWebhook)) {
|
||||
return {
|
||||
success: false,
|
||||
status: 400,
|
||||
error: "growth_webhook must be a public https URL; localhost, private IP, and .local targets are blocked",
|
||||
};
|
||||
}
|
||||
|
||||
const existingAgent = await prisma.agentProfile.findUnique({
|
||||
where: { agent_id: agentId },
|
||||
select: {
|
||||
status: true,
|
||||
wallet_address: true,
|
||||
capabilities: true,
|
||||
contact_endpoints: true,
|
||||
},
|
||||
});
|
||||
const existingEndpoints = jsonObject(existingAgent?.contact_endpoints);
|
||||
const registeredWebhook = existingWebhook(existingEndpoints);
|
||||
|
||||
if (walletAddress && existingAgent?.wallet_address && existingAgent.wallet_address !== walletAddress) {
|
||||
return {
|
||||
success: false,
|
||||
status: 409,
|
||||
error: "wallet_address is already registered for this agent_id and cannot be overwritten",
|
||||
};
|
||||
}
|
||||
|
||||
if (growthWebhook && registeredWebhook && registeredWebhook !== growthWebhook) {
|
||||
return {
|
||||
success: false,
|
||||
status: 409,
|
||||
error: "growth_webhook is already registered for this agent_id and cannot be overwritten without operator review",
|
||||
};
|
||||
}
|
||||
|
||||
const contactEndpoints = {
|
||||
...existingEndpoints,
|
||||
...(growthWebhook
|
||||
? {
|
||||
growth_webhook: growthWebhook,
|
||||
a2a_webhook: stringValue(existingEndpoints.a2a_webhook) || growthWebhook,
|
||||
webhook: stringValue(existingEndpoints.webhook) || growthWebhook,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
const capabilities = {
|
||||
...jsonObject(existingAgent?.capabilities),
|
||||
growth_referral: true,
|
||||
demand_campaign_kit: true,
|
||||
self_connect: true,
|
||||
outbound_growth_ready: Boolean(existingWebhook(contactEndpoints)),
|
||||
requested_tool: selectedIntegration?.id || tool || null,
|
||||
campaign,
|
||||
source,
|
||||
channel,
|
||||
};
|
||||
|
||||
const agent = await prisma.agentProfile.upsert({
|
||||
where: { agent_id: agentId },
|
||||
update: {
|
||||
discovery_source: "A2A_AGENT_CONNECT",
|
||||
capabilities,
|
||||
contact_endpoints: contactEndpoints,
|
||||
...(walletAddress ? { wallet_address: walletAddress } : {}),
|
||||
},
|
||||
create: {
|
||||
agent_id: agentId,
|
||||
type: "SCOUT",
|
||||
status: "PENDING",
|
||||
discovery_source: "A2A_AGENT_CONNECT",
|
||||
wallet_address: walletAddress || null,
|
||||
capabilities,
|
||||
contact_endpoints: contactEndpoints,
|
||||
},
|
||||
select: {
|
||||
status: true,
|
||||
wallet_address: true,
|
||||
},
|
||||
});
|
||||
|
||||
const growthKit = buildAgentGrowthKit({ agentId, campaign, source });
|
||||
const demandCampaignKit = buildAgentDemandCampaignKit({ agentId, campaign, source, channel });
|
||||
const campaignKitUrl =
|
||||
`${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodeURIComponent(agentId)}` +
|
||||
`®ister=true&campaign=${encodeURIComponent(campaign)}&source=${encodeURIComponent(source)}` +
|
||||
`&channel=${encodeURIComponent(channel)}`;
|
||||
const onboardingUrl =
|
||||
`${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=${encodeURIComponent(agentId)}` +
|
||||
`®ister=true&tool=${encodeURIComponent(selectedIntegration?.id || tool || "")}`;
|
||||
const referralTouchpointUrl =
|
||||
`${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodeURIComponent(agentId)}` +
|
||||
`&campaign=${encodeURIComponent(campaign)}&source=${encodeURIComponent(source)}&touchpoint=proposal_link_sent`;
|
||||
const referralStatusUrl = `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodeURIComponent(agentId)}`;
|
||||
|
||||
await prisma.auditEvent.create({
|
||||
data: {
|
||||
actorType: "AGENT",
|
||||
actorId: agentId,
|
||||
action: "A2A_EXTERNAL_AGENT_CONNECTED",
|
||||
entityType: "AGENT",
|
||||
entityId: agentId,
|
||||
metadata: {
|
||||
selected_integration: selectedIntegration?.id || tool || null,
|
||||
source,
|
||||
channel,
|
||||
campaign,
|
||||
outbound_ready: Boolean(existingWebhook(contactEndpoints)),
|
||||
webhook_registered: Boolean(existingWebhook(contactEndpoints)),
|
||||
wallet_bound: Boolean(agent.wallet_address),
|
||||
referral_url: growthKit.referral_url,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
agent_id: agentId,
|
||||
status: agent.status,
|
||||
selected_integration: selectedIntegration
|
||||
? {
|
||||
id: selectedIntegration.id,
|
||||
name: selectedIntegration.name,
|
||||
status: selectedIntegration.status,
|
||||
monetization_lane: selectedIntegration.monetizationLane,
|
||||
}
|
||||
: null,
|
||||
outbound_ready: Boolean(existingWebhook(contactEndpoints)),
|
||||
webhook_registered: Boolean(existingWebhook(contactEndpoints)),
|
||||
wallet_bound: Boolean(agent.wallet_address),
|
||||
referral_url: growthKit.referral_url,
|
||||
campaign_kit_url: campaignKitUrl,
|
||||
onboarding_url: onboardingUrl,
|
||||
referral_touchpoint_url: referralTouchpointUrl,
|
||||
referral_status_url: referralStatusUrl,
|
||||
open_tasks_url: `${AGENT_GATEWAY_URL}/api/open-tasks`,
|
||||
agent_connect_url: buildAgentConnectUrl(agentId),
|
||||
growth_kit: growthKit,
|
||||
demand_campaign_kit: demandCampaignKit,
|
||||
next_actions: [
|
||||
"Use referral_url when sending human demand proposers to VibeWork.",
|
||||
"Record non-sensitive outreach with referral_touchpoint_url.",
|
||||
"Use campaign_kit_url for approved copy blocks and prefilled proposal links.",
|
||||
"Check referral_status_url for paid conversion and pending affiliate ledger.",
|
||||
"Keep payment, credentials, private keys, and customer secrets out of external agent chats.",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAgentConnectDescription() {
|
||||
return {
|
||||
success: true,
|
||||
endpoint: `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`,
|
||||
method: "POST",
|
||||
accepts: {
|
||||
agent_id: "stable external agent id",
|
||||
tool: "optional integration id such as aider, openclaw, openhands, n8n, dify",
|
||||
growth_webhook: "optional public https webhook for VibeWork growth kit delivery",
|
||||
wallet_address: "optional payout wallet or payout account reference",
|
||||
source: "optional source attribution",
|
||||
channel: "optional outreach channel",
|
||||
},
|
||||
safety: [
|
||||
"growth_webhook must be public https.",
|
||||
"localhost, private IP, and .local targets are blocked.",
|
||||
"wallet_address and existing webhook cannot be overwritten without operator review.",
|
||||
"Revenue is counted only after Stripe webhook or verified USDC wallet receipt.",
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -301,6 +301,7 @@ export const A2A_AGENT_INTEGRATIONS: A2AAgentIntegration[] = [
|
||||
export function buildA2aIntegrationCatalog(agentId?: string | null) {
|
||||
const sanitizedAgentId = agentId?.trim() || null;
|
||||
const onboardingUrl = `${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id={agent_id}®ister=true`;
|
||||
const agentConnectUrl = `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`;
|
||||
const campaignKitUrl = `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true`;
|
||||
const growthKitUrl = `${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id={agent_id}®ister=true`;
|
||||
const integrationsUrl = `${AGENT_GATEWAY_URL}/api/a2a/integrations`;
|
||||
@@ -314,6 +315,8 @@ export function buildA2aIntegrationCatalog(agentId?: string | null) {
|
||||
gateway_api: AGENT_GATEWAY_URL,
|
||||
public_endpoints: {
|
||||
onboarding: onboardingUrl,
|
||||
agent_connect: agentConnectUrl,
|
||||
agent_connect_page: `${VIBEWORK_SITE_URL}/agents/connect`,
|
||||
demand_campaign_kit: campaignKitUrl,
|
||||
integration_catalog: integrationsUrl,
|
||||
growth_kit: growthKitUrl,
|
||||
@@ -330,6 +333,7 @@ export function buildA2aIntegrationCatalog(agentId?: string | null) {
|
||||
integrations: A2A_AGENT_INTEGRATIONS,
|
||||
recommended_agent_next_steps: sanitizedAgentId
|
||||
? [
|
||||
`Connect webhook and wallet lane: ${VIBEWORK_SITE_URL}/agents/connect?agent_id=${encodeURIComponent(sanitizedAgentId)}`,
|
||||
`Start onboarding contract: ${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=${encodeURIComponent(sanitizedAgentId)}®ister=true`,
|
||||
`Fetch demand campaign kit: ${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodeURIComponent(sanitizedAgentId)}®ister=true`,
|
||||
`Record non-sensitive referral touchpoint: ${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodeURIComponent(sanitizedAgentId)}&touchpoint=proposal_link_sent`,
|
||||
@@ -341,6 +345,7 @@ export function buildA2aIntegrationCatalog(agentId?: string | null) {
|
||||
]
|
||||
: [
|
||||
"Choose a stable agent_id.",
|
||||
"Connect through /agents/connect or POST /api/a2a/agents/connect.",
|
||||
"Call /api/a2a/onboarding with register=true.",
|
||||
"Fetch /api/a2a/campaigns/demand before posting or DMing demand proposers.",
|
||||
"Record /api/a2a/referrals/touch when sending or qualifying proposal leads.",
|
||||
|
||||
Reference in New Issue
Block a user