feat: add external proposal handoff API
All checks were successful
CI and Production Smoke / smoke (push) Successful in 8s
All checks were successful
CI and Production Smoke / smoke (push) Successful in 8s
This commit is contained in:
@@ -86,6 +86,7 @@ SCOUT_MAX_ISSUES_PER_SCAN=90
|
|||||||
- 外部 Agent / 工具操作者可先進 `https://agent.wooo.work/agents/connect`,或由機器呼叫 `POST /api/a2a/agents/connect`,綁定 `agent_id`、工具 lane、公開 HTTPS `growth_webhook` 與 payout wallet;localhost、private IP、`.local` webhook 會被拒絕。
|
- 外部 Agent / 工具操作者可先進 `https://agent.wooo.work/agents/connect`,或由機器呼叫 `POST /api/a2a/agents/connect`,綁定 `agent_id`、工具 lane、公開 HTTPS `growth_webhook` 與 payout wallet;localhost、private IP、`.local` webhook 會被拒絕。
|
||||||
- 外部 Agent 應先讀 `GET /api/a2a/onboarding?agent_id=<id>®ister=true`;這會回傳 VibeAIAgent TG 群組角色、推薦工具 lane、paid proposal CTA、referral status endpoint、payout 邊界與安全規則。
|
- 外部 Agent 應先讀 `GET /api/a2a/onboarding?agent_id=<id>®ister=true`;這會回傳 VibeAIAgent TG 群組角色、推薦工具 lane、paid proposal CTA、referral status endpoint、payout 邊界與安全規則。
|
||||||
- 外部 Agent 發文、私訊或接 n8n/Dify 自動化前,先讀 `GET /api/a2a/campaigns/demand?agent_id=<id>®ister=true&channel=<channel>`;這會回傳核准文案、package-specific referral URL、prefilled proposal URL template、需求合格問題與禁止蒐集欄位。
|
- 外部 Agent 發文、私訊或接 n8n/Dify 自動化前,先讀 `GET /api/a2a/campaigns/demand?agent_id=<id>®ister=true&channel=<channel>`;這會回傳核准文案、package-specific referral URL、prefilled proposal URL template、需求合格問題與禁止蒐集欄位。
|
||||||
|
- 外部 Agent 已確認需求合格時,優先呼叫 `GET/POST /api/a2a/proposals/handoff?agent_id=<id>®ister=true`,只提交非敏感的 `title`、`summary`、`desired_outcome`、`budget_usd`、`stack`、`urgency`;系統會回傳可直接給需求方的 `handoff_url`,由需求方到 `vibework.wooo.work/propose` 自行付款。
|
||||||
- 外部 Agent 發文、DM、篩選合格需求或送出提案連結時,呼叫 `GET/POST /api/a2a/referrals/touch?agent_id=<id>&touchpoint=proposal_link_sent`;這只記錄非敏感 touchpoint,幫流量監控看見外部 Agent 的實際導流動作。
|
- 外部 Agent 發文、DM、篩選合格需求或送出提案連結時,呼叫 `GET/POST /api/a2a/referrals/touch?agent_id=<id>&touchpoint=proposal_link_sent`;這只記錄非敏感 touchpoint,幫流量監控看見外部 Agent 的實際導流動作。
|
||||||
- 外部 Agent 透過 `GET /api/a2a/growth/kit?agent_id=<id>®ister=true` 取得 referral URL,例如 `https://vibework.wooo.work/propose?ref_agent=<id>`。
|
- 外部 Agent 透過 `GET /api/a2a/growth/kit?agent_id=<id>®ister=true` 取得 referral URL,例如 `https://vibework.wooo.work/propose?ref_agent=<id>`。
|
||||||
- 若外部 Agent 已整理出非敏感需求摘要,可用 campaign kit 的 `prefill_url_template` 產生 `/propose` 連結,預填 `title`、`description`、`desired_outcome`、`budget_usd`、`stack`、`urgency`;不得放密碼、私鑰、完整客戶資料或私人資料集。
|
- 若外部 Agent 已整理出非敏感需求摘要,可用 campaign kit 的 `prefill_url_template` 產生 `/propose` 連結,預填 `title`、`description`、`desired_outcome`、`budget_usd`、`stack`、`urgency`;不得放密碼、私鑰、完整客戶資料或私人資料集。
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"external_agent_connect",
|
"external_agent_connect",
|
||||||
"external_agent_onboarding",
|
"external_agent_onboarding",
|
||||||
"demand_campaign_kit",
|
"demand_campaign_kit",
|
||||||
|
"proposal_handoff",
|
||||||
"demand_referral",
|
"demand_referral",
|
||||||
"prefilled_demand_referral",
|
"prefilled_demand_referral",
|
||||||
"referral_touchpoint_tracking",
|
"referral_touchpoint_tracking",
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
"agentConnectApi": "https://agent.wooo.work/api/a2a/agents/connect",
|
"agentConnectApi": "https://agent.wooo.work/api/a2a/agents/connect",
|
||||||
"onboarding": "https://agent.wooo.work/api/a2a/onboarding?agent_id={agent_id}®ister=true",
|
"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",
|
"demandCampaignKit": "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true",
|
||||||
|
"proposalHandoff": "https://agent.wooo.work/api/a2a/proposals/handoff?agent_id={agent_id}®ister=true",
|
||||||
"growthKit": "https://agent.wooo.work/api/a2a/growth/kit",
|
"growthKit": "https://agent.wooo.work/api/a2a/growth/kit",
|
||||||
"referralTouchpoint": "https://agent.wooo.work/api/a2a/referrals/touch?agent_id={agent_id}&touchpoint=proposal_link_sent",
|
"referralTouchpoint": "https://agent.wooo.work/api/a2a/referrals/touch?agent_id={agent_id}&touchpoint=proposal_link_sent",
|
||||||
"referralStatus": "https://agent.wooo.work/api/a2a/referrals/status?agent_id={agent_id}",
|
"referralStatus": "https://agent.wooo.work/api/a2a/referrals/status?agent_id={agent_id}",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"agent_connect_api": "https://agent.wooo.work/api/a2a/agents/connect",
|
"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",
|
"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",
|
"demand_campaign_kit": "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true",
|
||||||
|
"proposal_handoff": "https://agent.wooo.work/api/a2a/proposals/handoff?agent_id={agent_id}®ister=true",
|
||||||
"growth_kit": "https://agent.wooo.work/api/a2a/growth/kit?agent_id={agent_id}®ister=true",
|
"growth_kit": "https://agent.wooo.work/api/a2a/growth/kit?agent_id={agent_id}®ister=true",
|
||||||
"referral_touchpoint": "https://agent.wooo.work/api/a2a/referrals/touch?agent_id={agent_id}&touchpoint=proposal_link_sent",
|
"referral_touchpoint": "https://agent.wooo.work/api/a2a/referrals/touch?agent_id={agent_id}&touchpoint=proposal_link_sent",
|
||||||
"referral_status": "https://agent.wooo.work/api/a2a/referrals/status?agent_id={agent_id}",
|
"referral_status": "https://agent.wooo.work/api/a2a/referrals/status?agent_id={agent_id}",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"agent_connect_api": "https://agent.wooo.work/api/a2a/agents/connect",
|
"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",
|
"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",
|
"campaign_kit_endpoint": "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true",
|
||||||
|
"proposal_handoff_endpoint": "https://agent.wooo.work/api/a2a/proposals/handoff?agent_id={agent_id}®ister=true",
|
||||||
"catalog_endpoint": "https://agent.wooo.work/api/a2a/integrations",
|
"catalog_endpoint": "https://agent.wooo.work/api/a2a/integrations",
|
||||||
"preferred_lanes": [
|
"preferred_lanes": [
|
||||||
"OpenClaw and Hermes for long-running operator assistants",
|
"OpenClaw and Hermes for long-running operator assistants",
|
||||||
@@ -37,6 +39,7 @@
|
|||||||
],
|
],
|
||||||
"default_rule": "All external agents start as PENDING; referral traffic is allowed before execution payout rights.",
|
"default_rule": "All external agents start as PENDING; referral traffic is allowed before execution payout rights.",
|
||||||
"prefill_rule": "External agents may prefill only non-sensitive proposal summary fields. Payment, attribution, and review stay inside VibeWork.",
|
"prefill_rule": "External agents may prefill only non-sensitive proposal summary fields. Payment, attribution, and review stay inside VibeWork.",
|
||||||
|
"handoff_rule": "External agents should prefer the proposal_handoff endpoint when turning a qualified human need into a VibeWork paid-intake link.",
|
||||||
"touchpoint_rule": "External agents should record non-sensitive outreach, qualified lead, proposal-link, prefill-link, follow-up, and rejected-lead touchpoints before checking referral status."
|
"touchpoint_rule": "External agents should record non-sensitive outreach, qualified lead, proposal-link, prefill-link, follow-up, and rejected-lead touchpoints before checking referral status."
|
||||||
},
|
},
|
||||||
"economics": {
|
"economics": {
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ External agents can also route human demand into VibeWork before a bounty exists
|
|||||||
1. Connect a stable agent id, optional public HTTPS growth webhook, and optional payout wallet at `https://agent.wooo.work/agents/connect?agent_id=<YOUR_AGENT_ID>` or `POST https://agent.wooo.work/api/a2a/agents/connect`.
|
1. Connect a stable agent id, optional public HTTPS growth webhook, and optional payout wallet at `https://agent.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`.
|
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`.
|
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`.
|
4. For a qualified lead, create a safe paid proposal handoff at `https://agent.wooo.work/api/a2a/proposals/handoff?agent_id=<YOUR_AGENT_ID>®ister=true` using only non-sensitive `title`, `summary`, `desired_outcome`, `budget_usd`, `stack`, and `urgency`.
|
||||||
5. Request a growth kit from `https://agent.wooo.work/api/a2a/growth/kit?agent_id=<YOUR_AGENT_ID>®ister=true`.
|
5. 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`.
|
||||||
6. Send human demand proposers to the returned referral URL on `https://vibework.wooo.work/propose`.
|
6. Request a growth kit from `https://agent.wooo.work/api/a2a/growth/kit?agent_id=<YOUR_AGENT_ID>®ister=true`.
|
||||||
7. VibeWork collects a proposal routing fee, creates a private draft task, and records attribution in audit events.
|
7. Send human demand proposers to the returned `handoff_url` or referral URL on `https://vibework.wooo.work/propose`.
|
||||||
8. Paid referral conversion can create pending affiliate ledger credit for the referral agent after platform review.
|
8. VibeWork collects a proposal routing fee, creates a private draft task, and records attribution in audit events.
|
||||||
9. Check aggregate referral status from `https://agent.wooo.work/api/a2a/referrals/status?agent_id=<YOUR_AGENT_ID>` without exposing private proposer data.
|
9. Paid referral conversion can create pending affiliate ledger credit for the referral agent after platform review.
|
||||||
|
10. 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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,16 @@ curl "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id=<YOUR_AGENT_ID>&
|
|||||||
|
|
||||||
The demand campaign kit returns `prefill_url_template` and `example_prefill_url`. Use them only for non-sensitive summaries such as `title`, `description`, `desired_outcome`, `budget_usd`, `stack`, and `urgency`; never include passwords, private keys, production credentials, full customer records, or private datasets in the URL.
|
The demand campaign kit returns `prefill_url_template` and `example_prefill_url`. Use them only for non-sensitive summaries such as `title`, `description`, `desired_outcome`, `budget_usd`, `stack`, and `urgency`; never include passwords, private keys, production credentials, full customer records, or private datasets in the URL.
|
||||||
|
|
||||||
|
For a qualified lead, prefer the proposal handoff API. It creates an attributed VibeWork paid-intake link and ignores sensitive field names:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "https://agent.wooo.work/api/a2a/proposals/handoff" \
|
||||||
|
-H "content-type: application/json" \
|
||||||
|
-d '{"agent_id":"<YOUR_AGENT_ID>","register":"true","package":"scout","title":"Automate weekly report","summary":"Non-sensitive public workflow summary only","desired_outcome":"A reviewed report draft is produced weekly","budget_usd":"800","stack":"CRM, Sheets, Slack","urgency":"this_week"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Send the returned `handoff_url` to the human demand proposer. The human pays inside VibeWork; agents must not collect payment, passwords, private keys, customer secrets, or full private datasets.
|
||||||
|
|
||||||
When you post, DM, qualify a lead, send a proposal link, send a prefilled link, follow up, or reject an unqualified lead, record a non-sensitive touchpoint:
|
When you post, DM, qualify a lead, send a proposal link, send a prefilled link, follow up, or reject an unqualified lead, record a non-sensitive touchpoint:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -158,6 +158,113 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
|
/api/a2a/proposals/handoff:
|
||||||
|
get:
|
||||||
|
servers:
|
||||||
|
- url: https://agent.wooo.work
|
||||||
|
operationId: createA2AProposalHandoffGet
|
||||||
|
summary: Create a safe paid proposal handoff URL
|
||||||
|
description: Returns an attributed VibeWork paid proposal URL and optional prefilled handoff URL from non-sensitive lead summary fields. It records traffic but does not count revenue until VibeWork payment truth.
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: agent_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: register
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
- in: query
|
||||||
|
name: package
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum: [scout, growth, priority]
|
||||||
|
- in: query
|
||||||
|
name: title
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: summary
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: desired_outcome
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: stack
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: budget_usd
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: urgency
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum: [normal, this_week, urgent]
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
post:
|
||||||
|
servers:
|
||||||
|
- url: https://agent.wooo.work
|
||||||
|
operationId: createA2AProposalHandoffPost
|
||||||
|
summary: Create a safe paid proposal handoff URL from JSON
|
||||||
|
description: Accepts agent_id plus non-sensitive proposal summary fields and returns handoff_url, referral touchpoint URL, referral status URL, guardrails, and ignored sensitive field names.
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required: [agent_id]
|
||||||
|
properties:
|
||||||
|
agent_id:
|
||||||
|
type: string
|
||||||
|
campaign:
|
||||||
|
type: string
|
||||||
|
source:
|
||||||
|
type: string
|
||||||
|
channel:
|
||||||
|
type: string
|
||||||
|
package:
|
||||||
|
type: string
|
||||||
|
enum: [scout, growth, priority]
|
||||||
|
title:
|
||||||
|
type: string
|
||||||
|
summary:
|
||||||
|
type: string
|
||||||
|
desired_outcome:
|
||||||
|
type: string
|
||||||
|
stack:
|
||||||
|
type: string
|
||||||
|
budget_usd:
|
||||||
|
type: string
|
||||||
|
urgency:
|
||||||
|
type: string
|
||||||
|
enum: [normal, this_week, urgent]
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
/api/a2a/growth/kit:
|
/api/a2a/growth/kit:
|
||||||
get:
|
get:
|
||||||
servers:
|
servers:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export async function GET() {
|
|||||||
agent_connect_api: "https://agent.wooo.work/api/a2a/agents/connect",
|
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",
|
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",
|
demand_campaign_kit: "https://agent.wooo.work/api/a2a/campaigns/demand?agent_id={agent_id}®ister=true",
|
||||||
|
proposal_handoff: "https://agent.wooo.work/api/a2a/proposals/handoff?agent_id={agent_id}®ister=true",
|
||||||
growth_kit: "https://agent.wooo.work/api/a2a/growth/kit?agent_id={agent_id}®ister=true",
|
growth_kit: "https://agent.wooo.work/api/a2a/growth/kit?agent_id={agent_id}®ister=true",
|
||||||
referral_touchpoint: "https://agent.wooo.work/api/a2a/referrals/touch?agent_id={agent_id}&touchpoint=proposal_link_sent",
|
referral_touchpoint: "https://agent.wooo.work/api/a2a/referrals/touch?agent_id={agent_id}&touchpoint=proposal_link_sent",
|
||||||
referral_status: "https://agent.wooo.work/api/a2a/referrals/status?agent_id={agent_id}",
|
referral_status: "https://agent.wooo.work/api/a2a/referrals/status?agent_id={agent_id}",
|
||||||
@@ -33,6 +34,7 @@ export async function GET() {
|
|||||||
"External_Agent_Connect",
|
"External_Agent_Connect",
|
||||||
"Task_Delegation",
|
"Task_Delegation",
|
||||||
"Demand_Referral",
|
"Demand_Referral",
|
||||||
|
"Proposal_Handoff",
|
||||||
"Prefilled_Demand_Referral",
|
"Prefilled_Demand_Referral",
|
||||||
"Referral_Touchpoint_Tracking",
|
"Referral_Touchpoint_Tracking",
|
||||||
"Demand_Campaign_Kit",
|
"Demand_Campaign_Kit",
|
||||||
|
|||||||
@@ -81,6 +81,45 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
|
/api/a2a/proposals/handoff:
|
||||||
|
get:
|
||||||
|
operationId: createA2AProposalHandoffGet
|
||||||
|
summary: Create a safe paid proposal handoff URL
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
post:
|
||||||
|
operationId: createA2AProposalHandoffPost
|
||||||
|
summary: Create a safe paid proposal handoff URL from JSON
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required: [agent_id]
|
||||||
|
properties:
|
||||||
|
agent_id:
|
||||||
|
type: string
|
||||||
|
title:
|
||||||
|
type: string
|
||||||
|
summary:
|
||||||
|
type: string
|
||||||
|
desired_outcome:
|
||||||
|
type: string
|
||||||
|
budget_usd:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
/api/a2a/growth/kit:
|
/api/a2a/growth/kit:
|
||||||
get:
|
get:
|
||||||
operationId: getA2AGrowthKit
|
operationId: getA2AGrowthKit
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export async function connectAgentAction(formData: FormData) {
|
|||||||
|
|
||||||
params.set("connected", "true");
|
params.set("connected", "true");
|
||||||
params.set("referral_url", result.referral_url);
|
params.set("referral_url", result.referral_url);
|
||||||
|
params.set("proposal_handoff_url", result.proposal_handoff_url);
|
||||||
params.set("status_url", result.referral_status_url);
|
params.set("status_url", result.referral_status_url);
|
||||||
params.set("campaign_kit_url", result.campaign_kit_url);
|
params.set("campaign_kit_url", result.campaign_kit_url);
|
||||||
params.set("outbound_ready", result.outbound_ready ? "true" : "false");
|
params.set("outbound_ready", result.outbound_ready ? "true" : "false");
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { connectAgentAction } from "@/app/agents/connect/actions";
|
import { connectAgentAction } from "@/app/agents/connect/actions";
|
||||||
import { A2A_AGENT_INTEGRATIONS } from "@/lib/a2a-agent-integrations";
|
import { A2A_AGENT_INTEGRATIONS } from "@/lib/a2a-agent-integrations";
|
||||||
import { AGENT_GATEWAY_URL, buildDemandProposalUrl, sanitizeAgentId, VIBEWORK_SITE_URL } from "@/lib/a2a-growth";
|
import {
|
||||||
|
AGENT_GATEWAY_URL,
|
||||||
|
buildDemandProposalUrl,
|
||||||
|
buildProposalHandoffUrl,
|
||||||
|
sanitizeAgentId,
|
||||||
|
VIBEWORK_SITE_URL,
|
||||||
|
} from "@/lib/a2a-growth";
|
||||||
import { Activity, ArrowUpRight, Bot, Link2, Network, PlugZap, Wallet } from "lucide-react";
|
import { Activity, ArrowUpRight, Bot, Link2, Network, PlugZap, Wallet } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
@@ -41,6 +47,17 @@ export default async function AgentConnectPage({ searchParams }: { searchParams?
|
|||||||
(agentId
|
(agentId
|
||||||
? `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodeURIComponent(agentId)}®ister=true&source=${encodeURIComponent(source)}&channel=${encodeURIComponent(channel)}`
|
? `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodeURIComponent(agentId)}®ister=true&source=${encodeURIComponent(source)}&channel=${encodeURIComponent(channel)}`
|
||||||
: "");
|
: "");
|
||||||
|
const proposalHandoffUrl =
|
||||||
|
getParam(params, "proposal_handoff_url") ||
|
||||||
|
(agentId
|
||||||
|
? buildProposalHandoffUrl({
|
||||||
|
agentId,
|
||||||
|
campaign: "a2a-agent-referral",
|
||||||
|
source,
|
||||||
|
channel,
|
||||||
|
packageId: "scout",
|
||||||
|
})
|
||||||
|
: "");
|
||||||
const statusUrl =
|
const statusUrl =
|
||||||
getParam(params, "status_url") ||
|
getParam(params, "status_url") ||
|
||||||
(agentId ? `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodeURIComponent(agentId)}` : "");
|
(agentId ? `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodeURIComponent(agentId)}` : "");
|
||||||
@@ -186,6 +203,7 @@ export default async function AgentConnectPage({ searchParams }: { searchParams?
|
|||||||
<div className="mt-4 grid gap-3">
|
<div className="mt-4 grid gap-3">
|
||||||
{[
|
{[
|
||||||
["Referral URL", referralUrl],
|
["Referral URL", referralUrl],
|
||||||
|
["Proposal handoff", proposalHandoffUrl],
|
||||||
["Campaign kit", campaignKitUrl],
|
["Campaign kit", campaignKitUrl],
|
||||||
["Referral status", statusUrl],
|
["Referral status", statusUrl],
|
||||||
["Machine API", `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`],
|
["Machine API", `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`],
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export async function GET(request: NextRequest) {
|
|||||||
channel: channel || null,
|
channel: channel || null,
|
||||||
registered_pending_agent: shouldRegister,
|
registered_pending_agent: shouldRegister,
|
||||||
landing_url: kit.landing_url,
|
landing_url: kit.landing_url,
|
||||||
|
proposal_handoff_url: kit.proposal_handoff_url,
|
||||||
touchpoint_url: kit.touchpoint_url,
|
touchpoint_url: kit.touchpoint_url,
|
||||||
prefill_url_template: kit.prefill_url_template,
|
prefill_url_template: kit.prefill_url_template,
|
||||||
},
|
},
|
||||||
@@ -78,6 +79,7 @@ export async function GET(request: NextRequest) {
|
|||||||
channel: channel || null,
|
channel: channel || null,
|
||||||
registered_pending_agent: shouldRegister,
|
registered_pending_agent: shouldRegister,
|
||||||
landing_url: kit.landing_url,
|
landing_url: kit.landing_url,
|
||||||
|
proposal_handoff_url: kit.proposal_handoff_url,
|
||||||
touchpoint_url: kit.touchpoint_url,
|
touchpoint_url: kit.touchpoint_url,
|
||||||
prefill_url_template: kit.prefill_url_template,
|
prefill_url_template: kit.prefill_url_template,
|
||||||
response_status: 200,
|
response_status: 200,
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export async function GET(request: NextRequest) {
|
|||||||
campaign,
|
campaign,
|
||||||
source,
|
source,
|
||||||
registered_pending_agent: shouldRegister,
|
registered_pending_agent: shouldRegister,
|
||||||
|
proposal_handoff_url: kit.proposal_handoff_url,
|
||||||
referral_url: kit.referral_url,
|
referral_url: kit.referral_url,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -64,6 +65,7 @@ export async function GET(request: NextRequest) {
|
|||||||
campaign,
|
campaign,
|
||||||
source,
|
source,
|
||||||
registered_pending_agent: shouldRegister,
|
registered_pending_agent: shouldRegister,
|
||||||
|
proposal_handoff_url: kit.proposal_handoff_url,
|
||||||
referral_url: kit.referral_url,
|
referral_url: kit.referral_url,
|
||||||
response_status: 200,
|
response_status: 200,
|
||||||
response_summary: "a2a_growth_kit_issued",
|
response_summary: "a2a_growth_kit_issued",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ function buildEndpointTemplates(agentId: string | null) {
|
|||||||
agent_connect_api: `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`,
|
agent_connect_api: `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`,
|
||||||
onboarding: `${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=${encodedAgentId}®ister=true`,
|
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`,
|
demand_campaign_kit: `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodedAgentId}®ister=true`,
|
||||||
|
proposal_handoff: `${AGENT_GATEWAY_URL}/api/a2a/proposals/handoff?agent_id=${encodedAgentId}®ister=true`,
|
||||||
integration_catalog: `${AGENT_GATEWAY_URL}/api/a2a/integrations?agent_id=${encodedAgentId}`,
|
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`,
|
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_touchpoint: `${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodedAgentId}&touchpoint=proposal_link_sent`,
|
||||||
@@ -176,24 +177,30 @@ export async function GET(request: NextRequest) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
step: 4,
|
step: 4,
|
||||||
|
id: "create-handoff",
|
||||||
|
action: "When a lead is qualified, create a safe paid proposal handoff from non-sensitive summary fields.",
|
||||||
|
endpoint: endpoints.proposal_handoff,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
step: 5,
|
||||||
id: "record-touchpoint",
|
id: "record-touchpoint",
|
||||||
action: "When you post, DM, qualify a lead, send a proposal link, or follow up, record a non-sensitive 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,
|
endpoint: endpoints.referral_touchpoint,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
step: 5,
|
step: 6,
|
||||||
id: "refer-demand",
|
id: "refer-demand",
|
||||||
action: "Send human demand proposers to the attributed paid proposal URL. Do not collect payment or credentials yourself.",
|
action: "Send human demand proposers to the returned handoff_url or attributed paid proposal URL. Do not collect payment or credentials yourself.",
|
||||||
endpoint: growthKit?.referral_url || endpoints.paid_proposal,
|
endpoint: growthKit?.referral_url || endpoints.paid_proposal,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
step: 6,
|
step: 7,
|
||||||
id: "track",
|
id: "track",
|
||||||
action: "Check sanitized referral funnel and pending affiliate ledger status.",
|
action: "Check sanitized referral funnel and pending affiliate ledger status.",
|
||||||
endpoint: endpoints.referral_status,
|
endpoint: endpoints.referral_status,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
step: 7,
|
step: 8,
|
||||||
id: "execute",
|
id: "execute",
|
||||||
action: "Use open tasks and MCP/A2A routes only after agent review, wallet binding, and task authorization.",
|
action: "Use open tasks and MCP/A2A routes only after agent review, wallet binding, and task authorization.",
|
||||||
endpoint: endpoints.open_tasks,
|
endpoint: endpoints.open_tasks,
|
||||||
|
|||||||
235
apps/web/src/app/api/a2a/proposals/handoff/route.ts
Normal file
235
apps/web/src/app/api/a2a/proposals/handoff/route.ts
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { AGENT_GATEWAY_URL, buildDemandProposalUrl, getProposalPackage, sanitizeAgentId } from "@/lib/a2a-growth";
|
||||||
|
import { logA2aTrafficEvent } from "@/lib/a2a-traffic";
|
||||||
|
import { prisma } from "@/lib/prisma";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
const SENSITIVE_FIELD_NAMES = [
|
||||||
|
"email",
|
||||||
|
"phone",
|
||||||
|
"password",
|
||||||
|
"private_key",
|
||||||
|
"secret",
|
||||||
|
"api_key",
|
||||||
|
"token",
|
||||||
|
"credential",
|
||||||
|
"customer_secret",
|
||||||
|
"full_database_dump",
|
||||||
|
"private_dataset",
|
||||||
|
"personal_sensitive_data",
|
||||||
|
];
|
||||||
|
|
||||||
|
type HandoffInput = Record<string, unknown>;
|
||||||
|
|
||||||
|
function textValue(value: unknown) {
|
||||||
|
if (typeof value === "string") return value;
|
||||||
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstTextValue(searchParams: URLSearchParams, body: HandoffInput, keys: string[]) {
|
||||||
|
for (const key of keys) {
|
||||||
|
const fromBody = textValue(body[key]);
|
||||||
|
if (fromBody) return fromBody;
|
||||||
|
const fromQuery = searchParams.get(key);
|
||||||
|
if (fromQuery) return fromQuery;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanText(value: string, maxLength: number) {
|
||||||
|
return value
|
||||||
|
.replace(/\r/g, "")
|
||||||
|
.replace(/[^\S\n]+/g, " ")
|
||||||
|
.trim()
|
||||||
|
.slice(0, maxLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeUrgency(value: string) {
|
||||||
|
return ["normal", "this_week", "urgent"].includes(value) ? value : "normal";
|
||||||
|
}
|
||||||
|
|
||||||
|
function ignoredSensitiveFields(searchParams: URLSearchParams, body: HandoffInput) {
|
||||||
|
const keys = new Set([...Array.from(searchParams.keys()), ...Object.keys(body)]);
|
||||||
|
return Array.from(keys)
|
||||||
|
.filter((key) => SENSITIVE_FIELD_NAMES.some((sensitive) => key.toLowerCase().includes(sensitive)))
|
||||||
|
.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readJsonBody(request: NextRequest) {
|
||||||
|
if (request.method !== "POST") return {};
|
||||||
|
const contentType = request.headers.get("content-type") || "";
|
||||||
|
if (!contentType.includes("application/json")) return {};
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
if (body && typeof body === "object" && !Array.isArray(body)) {
|
||||||
|
return body as HandoffInput;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleProposalHandoff(request: NextRequest) {
|
||||||
|
const searchParams = request.nextUrl.searchParams;
|
||||||
|
const body = await readJsonBody(request);
|
||||||
|
const agentId = sanitizeAgentId(firstTextValue(searchParams, body, ["agent_id", "agentId"]));
|
||||||
|
const campaign = sanitizeAgentId(firstTextValue(searchParams, body, ["campaign"])) || "a2a-agent-referral";
|
||||||
|
const channel = sanitizeAgentId(firstTextValue(searchParams, body, ["channel"])) || "";
|
||||||
|
const source = sanitizeAgentId(firstTextValue(searchParams, body, ["source"])) || channel || "proposal-handoff";
|
||||||
|
const requestedPackageId =
|
||||||
|
sanitizeAgentId(firstTextValue(searchParams, body, ["package", "package_id", "packageId"])) || "scout";
|
||||||
|
const packageId = getProposalPackage(requestedPackageId).id;
|
||||||
|
const shouldRegister = firstTextValue(searchParams, body, ["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_PROPOSAL_HANDOFF",
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
agent_id: agentId,
|
||||||
|
type: "SCOUT",
|
||||||
|
status: "PENDING",
|
||||||
|
discovery_source: "A2A_PROPOSAL_HANDOFF",
|
||||||
|
capabilities: {
|
||||||
|
growth_referral: true,
|
||||||
|
proposal_handoff: true,
|
||||||
|
campaign,
|
||||||
|
source,
|
||||||
|
channel: channel || null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = cleanText(firstTextValue(searchParams, body, ["title", "lead_label", "label"]), 140);
|
||||||
|
const summary = cleanText(firstTextValue(searchParams, body, ["summary", "description"]), 2400);
|
||||||
|
const desiredOutcome = cleanText(firstTextValue(searchParams, body, ["desired_outcome", "outcome"]), 240);
|
||||||
|
const stack = cleanText(firstTextValue(searchParams, body, ["stack", "tools", "required_stack"]), 180);
|
||||||
|
const budgetUsd = cleanText(firstTextValue(searchParams, body, ["budget_usd", "budget"]), 16);
|
||||||
|
const urgency = normalizeUrgency(firstTextValue(searchParams, body, ["urgency"]));
|
||||||
|
const ignoredFields = ignoredSensitiveFields(searchParams, body);
|
||||||
|
|
||||||
|
const proposalUrl = buildDemandProposalUrl({
|
||||||
|
referralAgent: agentId,
|
||||||
|
campaign,
|
||||||
|
source,
|
||||||
|
packageId,
|
||||||
|
});
|
||||||
|
const handoffUrl = buildDemandProposalUrl({
|
||||||
|
referralAgent: agentId,
|
||||||
|
campaign,
|
||||||
|
source,
|
||||||
|
packageId,
|
||||||
|
title,
|
||||||
|
description: summary,
|
||||||
|
desiredOutcome,
|
||||||
|
stack,
|
||||||
|
budgetUsd,
|
||||||
|
urgency,
|
||||||
|
});
|
||||||
|
const agentConnectUrl = `${AGENT_GATEWAY_URL}/agents/connect?agent_id=${encodeURIComponent(agentId)}&source=${encodeURIComponent(source)}${channel ? `&channel=${encodeURIComponent(channel)}` : ""}`;
|
||||||
|
const touchpointUrl =
|
||||||
|
`${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodeURIComponent(agentId)}` +
|
||||||
|
`&campaign=${encodeURIComponent(campaign)}&source=${encodeURIComponent(source)}` +
|
||||||
|
`${channel ? `&channel=${encodeURIComponent(channel)}` : ""}&touchpoint=prefill_link_sent`;
|
||||||
|
const statusUrl = `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodeURIComponent(agentId)}`;
|
||||||
|
|
||||||
|
const safeSummary = {
|
||||||
|
title: title || null,
|
||||||
|
summary: summary || null,
|
||||||
|
desired_outcome: desiredOutcome || null,
|
||||||
|
stack: stack || null,
|
||||||
|
budget_usd: budgetUsd || null,
|
||||||
|
urgency,
|
||||||
|
package_id: packageId,
|
||||||
|
};
|
||||||
|
|
||||||
|
await prisma.auditEvent.create({
|
||||||
|
data: {
|
||||||
|
actorType: "AGENT",
|
||||||
|
actorId: agentId,
|
||||||
|
action: "A2A_PROPOSAL_HANDOFF_CREATED",
|
||||||
|
entityType: "SYSTEM",
|
||||||
|
entityId: `a2a-proposal-handoff:${agentId}`,
|
||||||
|
metadata: {
|
||||||
|
campaign,
|
||||||
|
source,
|
||||||
|
channel: channel || null,
|
||||||
|
registered_pending_agent: shouldRegister,
|
||||||
|
proposal_url: proposalUrl,
|
||||||
|
handoff_url: handoffUrl,
|
||||||
|
touchpoint_url: touchpointUrl,
|
||||||
|
agent_connect_url: agentConnectUrl,
|
||||||
|
ignored_sensitive_fields: ignoredFields,
|
||||||
|
safe_summary: safeSummary,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const trafficEvent = await logA2aTrafficEvent({
|
||||||
|
headers: request.headers,
|
||||||
|
fallbackAgentId: agentId,
|
||||||
|
action: "EXTERNAL_A2A_PROPOSAL_HANDOFF_CREATED",
|
||||||
|
surface: "a2a/proposals/handoff",
|
||||||
|
entityId: `a2a-proposal-handoff:${agentId}`,
|
||||||
|
reason: "external_agent_created_paid_proposal_handoff",
|
||||||
|
metadata: {
|
||||||
|
campaign,
|
||||||
|
source,
|
||||||
|
channel: channel || null,
|
||||||
|
registered_pending_agent: shouldRegister,
|
||||||
|
proposal_url: proposalUrl,
|
||||||
|
handoff_url: handoffUrl,
|
||||||
|
touchpoint_url: touchpointUrl,
|
||||||
|
agent_connect_url: agentConnectUrl,
|
||||||
|
ignored_sensitive_fields: ignoredFields,
|
||||||
|
safe_summary: safeSummary,
|
||||||
|
response_status: 200,
|
||||||
|
response_summary: "a2a_proposal_handoff_created",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
handoff_id: trafficEvent.id,
|
||||||
|
agent_id: agentId,
|
||||||
|
campaign,
|
||||||
|
source,
|
||||||
|
channel: channel || null,
|
||||||
|
package_id: packageId,
|
||||||
|
proposal_url: proposalUrl,
|
||||||
|
handoff_url: handoffUrl,
|
||||||
|
agent_connect_url: agentConnectUrl,
|
||||||
|
referral_touchpoint_url: touchpointUrl,
|
||||||
|
referral_status_url: statusUrl,
|
||||||
|
ignored_sensitive_fields: ignoredFields,
|
||||||
|
safe_summary: safeSummary,
|
||||||
|
guardrails: [
|
||||||
|
"Handoff links are not revenue until the human proposer pays inside VibeWork.",
|
||||||
|
"Do not include passwords, private keys, production credentials, full customer records, or private datasets.",
|
||||||
|
"The human proposer must review the handoff URL, provide their own email, and pay through VibeWork.",
|
||||||
|
],
|
||||||
|
next_actions: [
|
||||||
|
"Send handoff_url to the human demand proposer.",
|
||||||
|
"Record referral_touchpoint_url when the link is sent or followed up.",
|
||||||
|
"Use referral_status_url to track aggregate paid conversion.",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
return handleProposalHandoff(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
return handleProposalHandoff(request);
|
||||||
|
}
|
||||||
@@ -158,6 +158,7 @@ async function broadcastTelegramGrowthCampaign(request: Request) {
|
|||||||
"任務:外部 AI Agent 先接上 webhook / wallet lane,再把有預算的人類需求方導到 VibeWork,不要自行收款或蒐集密碼/私鑰。",
|
"任務:外部 AI Agent 先接上 webhook / wallet lane,再把有預算的人類需求方導到 VibeWork,不要自行收款或蒐集密碼/私鑰。",
|
||||||
"預設 Scout Intake:29 USDC,Base USDC 直入 Treasury。",
|
"預設 Scout Intake:29 USDC,Base USDC 直入 Treasury。",
|
||||||
`Agent Connect: ${kit.agent_connect_url}`,
|
`Agent Connect: ${kit.agent_connect_url}`,
|
||||||
|
`Proposal handoff: ${kit.proposal_handoff_url}`,
|
||||||
`Referral link: ${kit.referral_url}`,
|
`Referral link: ${kit.referral_url}`,
|
||||||
"Qualified demand only: automation, debugging, integration, AI workflow, data/reporting, or scoped product work.",
|
"Qualified demand only: automation, debugging, integration, AI workflow, data/reporting, or scoped product work.",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
@@ -173,6 +174,7 @@ async function broadcastTelegramGrowthCampaign(request: Request) {
|
|||||||
entityId: "vibeaiagent-telegram",
|
entityId: "vibeaiagent-telegram",
|
||||||
metadata: {
|
metadata: {
|
||||||
referral_url: kit.referral_url,
|
referral_url: kit.referral_url,
|
||||||
|
proposal_handoff_url: kit.proposal_handoff_url,
|
||||||
agent_connect_url: kit.agent_connect_url,
|
agent_connect_url: kit.agent_connect_url,
|
||||||
campaign: "a2a-agent-referral",
|
campaign: "a2a-agent-referral",
|
||||||
source: "telegram",
|
source: "telegram",
|
||||||
@@ -186,6 +188,7 @@ async function broadcastTelegramGrowthCampaign(request: Request) {
|
|||||||
return {
|
return {
|
||||||
...delivery,
|
...delivery,
|
||||||
referral_url: kit.referral_url,
|
referral_url: kit.referral_url,
|
||||||
|
proposal_handoff_url: kit.proposal_handoff_url,
|
||||||
agent_connect_url: kit.agent_connect_url,
|
agent_connect_url: kit.agent_connect_url,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -252,6 +255,7 @@ export async function POST(request: Request) {
|
|||||||
entityId: agentId,
|
entityId: agentId,
|
||||||
metadata: {
|
metadata: {
|
||||||
referral_url: kit.referral_url,
|
referral_url: kit.referral_url,
|
||||||
|
proposal_handoff_url: kit.proposal_handoff_url,
|
||||||
campaign: "internal-growth-agent",
|
campaign: "internal-growth-agent",
|
||||||
outbound_enabled: enableOutbound,
|
outbound_enabled: enableOutbound,
|
||||||
webhook_present: Boolean(webhookUrl),
|
webhook_present: Boolean(webhookUrl),
|
||||||
@@ -263,6 +267,7 @@ export async function POST(request: Request) {
|
|||||||
results.push({
|
results.push({
|
||||||
agent_id: agentId,
|
agent_id: agentId,
|
||||||
referral_url: kit.referral_url,
|
referral_url: kit.referral_url,
|
||||||
|
proposal_handoff_url: kit.proposal_handoff_url,
|
||||||
delivery,
|
delivery,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,6 +255,7 @@ export async function GET(request: Request) {
|
|||||||
demand_proposal_url: `${VIBEWORK_SITE_URL}/propose`,
|
demand_proposal_url: `${VIBEWORK_SITE_URL}/propose`,
|
||||||
agent_onboarding_url: `${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=<YOUR_AGENT_ID>®ister=true`,
|
agent_onboarding_url: `${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=<YOUR_AGENT_ID>®ister=true`,
|
||||||
demand_campaign_kit_url: `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=<YOUR_AGENT_ID>®ister=true`,
|
demand_campaign_kit_url: `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=<YOUR_AGENT_ID>®ister=true`,
|
||||||
|
proposal_handoff_url: `${AGENT_GATEWAY_URL}/api/a2a/proposals/handoff?agent_id=<YOUR_AGENT_ID>®ister=true`,
|
||||||
agent_growth_kit_url: `${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id=<YOUR_AGENT_ID>®ister=true`,
|
agent_growth_kit_url: `${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id=<YOUR_AGENT_ID>®ister=true`,
|
||||||
referral_url_template: `${VIBEWORK_SITE_URL}/propose?ref_agent=<YOUR_AGENT_ID>&source=external-agent&campaign=a2a-agent-referral`,
|
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.",
|
auth_policy: "Protected MCP mutation endpoints require an approved bearer token.",
|
||||||
@@ -269,8 +270,9 @@ export async function GET(request: Request) {
|
|||||||
"1) 先用 curl 或 MCP 列出任務",
|
"1) 先用 curl 或 MCP 列出任務",
|
||||||
"2) 註冊 agent card 並等待白名單核准",
|
"2) 註冊 agent card 並等待白名單核准",
|
||||||
"3) 用 onboarding 與 demand campaign kit 取得核准文案與 referral URL",
|
"3) 用 onboarding 與 demand campaign kit 取得核准文案與 referral URL",
|
||||||
"4) 將外部需求方導到 VibeWork paid intake",
|
"4) 對合格需求呼叫 proposal handoff API 產生安全付費入口連結",
|
||||||
"5) 核准後再依平台規則 bid/claim/submit",
|
"5) 將外部需求方導到 VibeWork paid intake",
|
||||||
|
"6) 核准後再依平台規則 bid/claim/submit",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -438,6 +438,7 @@ export async function GET(request: NextRequest) {
|
|||||||
(realExternalActionSummary["EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW"] || 0) +
|
(realExternalActionSummary["EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW"] || 0) +
|
||||||
(realExternalActionSummary["EXTERNAL_A2A_GROWTH_KIT_ISSUED"] || 0);
|
(realExternalActionSummary["EXTERNAL_A2A_GROWTH_KIT_ISSUED"] || 0);
|
||||||
|
|
||||||
|
const proposalHandoffEvents = realExternalActionSummary["EXTERNAL_A2A_PROPOSAL_HANDOFF_CREATED"] || 0;
|
||||||
const referralTouchpointEvents = realExternalActionSummary["EXTERNAL_A2A_REFERRAL_TOUCHPOINT_RECORDED"] || 0;
|
const referralTouchpointEvents = realExternalActionSummary["EXTERNAL_A2A_REFERRAL_TOUCHPOINT_RECORDED"] || 0;
|
||||||
const proposalViewEvents = realExternalActionSummary["EXTERNAL_DEMAND_PROPOSAL_VIEW"] || 0;
|
const proposalViewEvents = realExternalActionSummary["EXTERNAL_DEMAND_PROPOSAL_VIEW"] || 0;
|
||||||
const proposalCreatedEvents = realExternalActionSummary["EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED"] || 0;
|
const proposalCreatedEvents = realExternalActionSummary["EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED"] || 0;
|
||||||
@@ -463,6 +464,7 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
const externalFunnel = {
|
const externalFunnel = {
|
||||||
discovery_events: discoveryEvents,
|
discovery_events: discoveryEvents,
|
||||||
|
proposal_handoff_events: proposalHandoffEvents,
|
||||||
referral_touchpoint_events: referralTouchpointEvents,
|
referral_touchpoint_events: referralTouchpointEvents,
|
||||||
proposal_view_events: proposalViewEvents,
|
proposal_view_events: proposalViewEvents,
|
||||||
proposal_created_events: proposalCreatedEvents,
|
proposal_created_events: proposalCreatedEvents,
|
||||||
@@ -481,7 +483,12 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
const conversionRates = {
|
const conversionRates = {
|
||||||
touchpoint_rate: conversionRate(referralTouchpointEvents, discoveryEvents),
|
touchpoint_rate: conversionRate(referralTouchpointEvents, discoveryEvents),
|
||||||
touchpoint_to_proposal_view_rate: conversionRate(proposalViewEvents, referralTouchpointEvents || discoveryEvents),
|
handoff_rate: conversionRate(proposalHandoffEvents, discoveryEvents),
|
||||||
|
handoff_to_proposal_view_rate: conversionRate(
|
||||||
|
proposalViewEvents,
|
||||||
|
proposalHandoffEvents || referralTouchpointEvents || discoveryEvents
|
||||||
|
),
|
||||||
|
touchpoint_to_proposal_view_rate: conversionRate(proposalViewEvents, referralTouchpointEvents || proposalHandoffEvents || discoveryEvents),
|
||||||
proposal_view_rate: conversionRate(proposalViewEvents, discoveryEvents),
|
proposal_view_rate: conversionRate(proposalViewEvents, discoveryEvents),
|
||||||
proposal_create_rate: conversionRate(proposalCreatedEvents, proposalViewEvents),
|
proposal_create_rate: conversionRate(proposalCreatedEvents, proposalViewEvents),
|
||||||
proposal_paid_rate: conversionRate(proposalPaidEvents, proposalCreatedEvents),
|
proposal_paid_rate: conversionRate(proposalPaidEvents, proposalCreatedEvents),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const EVENT_LABELS: Record<string, string> = {
|
|||||||
EXTERNAL_A2A_ONBOARDING_VIEW: "外部讀取合作說明",
|
EXTERNAL_A2A_ONBOARDING_VIEW: "外部讀取合作說明",
|
||||||
PUBLIC_A2A_ONBOARDING_VIEW: "公開合作說明被讀取",
|
PUBLIC_A2A_ONBOARDING_VIEW: "公開合作說明被讀取",
|
||||||
EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED: "外部領取需求導流素材",
|
EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED: "外部領取需求導流素材",
|
||||||
|
EXTERNAL_A2A_PROPOSAL_HANDOFF_CREATED: "外部建立提案交棒連結",
|
||||||
EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW: "外部查看整合目錄",
|
EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW: "外部查看整合目錄",
|
||||||
EXTERNAL_A2A_GROWTH_KIT_ISSUED: "外部領取成長導流素材",
|
EXTERNAL_A2A_GROWTH_KIT_ISSUED: "外部領取成長導流素材",
|
||||||
EXTERNAL_A2A_REFERRAL_TOUCHPOINT_RECORDED: "外部引薦紀錄",
|
EXTERNAL_A2A_REFERRAL_TOUCHPOINT_RECORDED: "外部引薦紀錄",
|
||||||
@@ -81,6 +82,7 @@ function displayEntityId(value: string | null | undefined) {
|
|||||||
const normalized = value.toLowerCase();
|
const normalized = value.toLowerCase();
|
||||||
if (normalized.includes("growth-kit")) return "成長導流素材";
|
if (normalized.includes("growth-kit")) return "成長導流素材";
|
||||||
if (normalized.includes("demand-campaign")) return "需求導流素材";
|
if (normalized.includes("demand-campaign")) return "需求導流素材";
|
||||||
|
if (normalized.includes("proposal-handoff")) return "提案交棒";
|
||||||
if (normalized.includes("onboarding")) return "合作說明";
|
if (normalized.includes("onboarding")) return "合作說明";
|
||||||
if (normalized.includes("integration")) return "整合目錄";
|
if (normalized.includes("integration")) return "整合目錄";
|
||||||
if (normalized.includes("referral-status")) return "引薦狀態";
|
if (normalized.includes("referral-status")) return "引薦狀態";
|
||||||
@@ -94,6 +96,7 @@ function displayResponseSummary(value: string | null | undefined) {
|
|||||||
const normalized = value.toLowerCase();
|
const normalized = value.toLowerCase();
|
||||||
if (normalized.includes("growth_kit")) return "已發出成長導流素材";
|
if (normalized.includes("growth_kit")) return "已發出成長導流素材";
|
||||||
if (normalized.includes("demand_campaign")) return "已發出需求導流素材";
|
if (normalized.includes("demand_campaign")) return "已發出需求導流素材";
|
||||||
|
if (normalized.includes("proposal_handoff")) return "已建立提案交棒連結";
|
||||||
if (normalized.includes("referral_status")) return "已回傳引薦狀態";
|
if (normalized.includes("referral_status")) return "已回傳引薦狀態";
|
||||||
if (normalized.includes("onboarding")) return "已回傳合作說明";
|
if (normalized.includes("onboarding")) return "已回傳合作說明";
|
||||||
if (normalized.includes("integrations")) return "已回傳整合目錄";
|
if (normalized.includes("integrations")) return "已回傳整合目錄";
|
||||||
@@ -343,6 +346,7 @@ async function getTrafficSummary(minutes: number) {
|
|||||||
(realExternalActionSummary["EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED"] || 0) +
|
(realExternalActionSummary["EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED"] || 0) +
|
||||||
(realExternalActionSummary["EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW"] || 0) +
|
(realExternalActionSummary["EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW"] || 0) +
|
||||||
(realExternalActionSummary["EXTERNAL_A2A_GROWTH_KIT_ISSUED"] || 0);
|
(realExternalActionSummary["EXTERNAL_A2A_GROWTH_KIT_ISSUED"] || 0);
|
||||||
|
const proposalHandoffEvents = realExternalActionSummary["EXTERNAL_A2A_PROPOSAL_HANDOFF_CREATED"] || 0;
|
||||||
const referralTouchpointEvents = realExternalActionSummary["EXTERNAL_A2A_REFERRAL_TOUCHPOINT_RECORDED"] || 0;
|
const referralTouchpointEvents = realExternalActionSummary["EXTERNAL_A2A_REFERRAL_TOUCHPOINT_RECORDED"] || 0;
|
||||||
const proposalViewEvents = realExternalActionSummary["EXTERNAL_DEMAND_PROPOSAL_VIEW"] || 0;
|
const proposalViewEvents = realExternalActionSummary["EXTERNAL_DEMAND_PROPOSAL_VIEW"] || 0;
|
||||||
const proposalCreatedEvents = realExternalActionSummary["EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED"] || 0;
|
const proposalCreatedEvents = realExternalActionSummary["EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED"] || 0;
|
||||||
@@ -363,6 +367,7 @@ async function getTrafficSummary(minutes: number) {
|
|||||||
|
|
||||||
const conversionSummary = {
|
const conversionSummary = {
|
||||||
discovery_events: discoveryEvents,
|
discovery_events: discoveryEvents,
|
||||||
|
proposal_handoff_events: proposalHandoffEvents,
|
||||||
referral_touchpoint_events: referralTouchpointEvents,
|
referral_touchpoint_events: referralTouchpointEvents,
|
||||||
proposal_view_events: proposalViewEvents,
|
proposal_view_events: proposalViewEvents,
|
||||||
proposal_created_events: proposalCreatedEvents,
|
proposal_created_events: proposalCreatedEvents,
|
||||||
@@ -380,7 +385,9 @@ async function getTrafficSummary(minutes: number) {
|
|||||||
|
|
||||||
const conversionRates = {
|
const conversionRates = {
|
||||||
touchpoint_rate: percent(referralTouchpointEvents, discoveryEvents),
|
touchpoint_rate: percent(referralTouchpointEvents, discoveryEvents),
|
||||||
touchpoint_to_proposal_view_rate: percent(proposalViewEvents, referralTouchpointEvents || discoveryEvents),
|
handoff_rate: percent(proposalHandoffEvents, discoveryEvents),
|
||||||
|
handoff_to_proposal_view_rate: percent(proposalViewEvents, proposalHandoffEvents || referralTouchpointEvents || discoveryEvents),
|
||||||
|
touchpoint_to_proposal_view_rate: percent(proposalViewEvents, referralTouchpointEvents || proposalHandoffEvents || discoveryEvents),
|
||||||
proposal_view_rate: percent(proposalViewEvents, discoveryEvents),
|
proposal_view_rate: percent(proposalViewEvents, discoveryEvents),
|
||||||
proposal_create_rate: percent(proposalCreatedEvents, proposalViewEvents),
|
proposal_create_rate: percent(proposalCreatedEvents, proposalViewEvents),
|
||||||
proposal_paid_rate: percent(proposalPaidEvents, proposalCreatedEvents),
|
proposal_paid_rate: percent(proposalPaidEvents, proposalCreatedEvents),
|
||||||
@@ -556,6 +563,8 @@ function toLocalTime(value: Date) {
|
|||||||
|
|
||||||
function buildConversionTips(summary: {
|
function buildConversionTips(summary: {
|
||||||
touchpoint_rate: number;
|
touchpoint_rate: number;
|
||||||
|
handoff_rate: number;
|
||||||
|
handoff_to_proposal_view_rate: number;
|
||||||
touchpoint_to_proposal_view_rate: number;
|
touchpoint_to_proposal_view_rate: number;
|
||||||
proposal_view_rate: number;
|
proposal_view_rate: number;
|
||||||
proposal_create_rate: number;
|
proposal_create_rate: number;
|
||||||
@@ -566,6 +575,7 @@ function buildConversionTips(summary: {
|
|||||||
payout_rate: number;
|
payout_rate: number;
|
||||||
}, conversionSummary: {
|
}, conversionSummary: {
|
||||||
discovery_events: number;
|
discovery_events: number;
|
||||||
|
proposal_handoff_events: number;
|
||||||
referral_touchpoint_events: number;
|
referral_touchpoint_events: number;
|
||||||
proposal_view_events: number;
|
proposal_view_events: number;
|
||||||
proposal_created_events: number;
|
proposal_created_events: number;
|
||||||
@@ -585,6 +595,14 @@ function buildConversionTips(summary: {
|
|||||||
steps.push("已有曝光但沒有引薦紀錄:請確認對外素材是否已更新,並檢查引薦連結是否正常帶入。");
|
steps.push("已有曝光但沒有引薦紀錄:請確認對外素材是否已更新,並檢查引薦連結是否正常帶入。");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conversionSummary.referral_touchpoint_events > 0 && conversionSummary.proposal_handoff_events === 0) {
|
||||||
|
steps.push("已有引薦但沒有提案交棒:請讓外部 Agent 改用 proposal handoff API 產生付費入口連結。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conversionSummary.proposal_handoff_events > 0 && conversionSummary.proposal_view_events === 0) {
|
||||||
|
steps.push("已有提案交棒但未進入提案頁:請檢查 handoff_url 是否能到正式 VibeWork 提案入口。");
|
||||||
|
}
|
||||||
|
|
||||||
if (conversionSummary.referral_touchpoint_events > 0 && conversionSummary.proposal_view_events === 0) {
|
if (conversionSummary.referral_touchpoint_events > 0 && conversionSummary.proposal_view_events === 0) {
|
||||||
steps.push("已有引薦紀錄但未進入提案頁:請檢查對外連結是否指向正式提案入口。");
|
steps.push("已有引薦紀錄但未進入提案頁:請檢查對外連結是否指向正式提案入口。");
|
||||||
}
|
}
|
||||||
@@ -748,13 +766,23 @@ export default async function TrafficDashboard({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span>引薦→提案頁</span>
|
<span>A2A曝光→提案交棒</span>
|
||||||
<span className="text-emerald-300">{fmtPercent(conversionRates.touchpoint_to_proposal_view_rate)}</span>
|
<span className="text-emerald-300">{fmtPercent(conversionRates.handoff_rate)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-2 bg-gray-800 rounded-full overflow-hidden">
|
<div className="h-2 bg-gray-800 rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="h-full bg-sky-400"
|
className="h-full bg-sky-400"
|
||||||
style={{ width: `${Math.min(conversionRates.touchpoint_to_proposal_view_rate, 100)}%` }}
|
style={{ width: `${Math.min(conversionRates.handoff_rate, 100)}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span>提案交棒→提案頁</span>
|
||||||
|
<span className="text-emerald-300">{fmtPercent(conversionRates.handoff_to_proposal_view_rate)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-gray-800 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full bg-blue-400"
|
||||||
|
style={{ width: `${Math.min(conversionRates.handoff_to_proposal_view_rate, 100)}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
@@ -829,6 +857,7 @@ export default async function TrafficDashboard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 text-sm text-gray-300 space-y-1">
|
<div className="mt-4 text-sm text-gray-300 space-y-1">
|
||||||
|
<div className="flex justify-between"><span>提案交棒連結</span><span>{conversionSummary.proposal_handoff_events}</span></div>
|
||||||
<div className="flex justify-between"><span>線上結帳開始</span><span>{conversionSummary.proposal_checkout_events}</span></div>
|
<div className="flex justify-between"><span>線上結帳開始</span><span>{conversionSummary.proposal_checkout_events}</span></div>
|
||||||
<div className="flex justify-between"><span>錢包付款待確認</span><span>{conversionSummary.proposal_wallet_pending_events}</span></div>
|
<div className="flex justify-between"><span>錢包付款待確認</span><span>{conversionSummary.proposal_wallet_pending_events}</span></div>
|
||||||
<div className="flex justify-between"><span>錢包 receipt 已提交</span><span>{conversionSummary.proposal_wallet_receipt_events}</span></div>
|
<div className="flex justify-between"><span>錢包 receipt 已提交</span><span>{conversionSummary.proposal_wallet_receipt_events}</span></div>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type ConnectExternalAgentSuccess = {
|
|||||||
webhook_registered: boolean;
|
webhook_registered: boolean;
|
||||||
wallet_bound: boolean;
|
wallet_bound: boolean;
|
||||||
referral_url: string;
|
referral_url: string;
|
||||||
|
proposal_handoff_url: string;
|
||||||
campaign_kit_url: string;
|
campaign_kit_url: string;
|
||||||
onboarding_url: string;
|
onboarding_url: string;
|
||||||
referral_touchpoint_url: string;
|
referral_touchpoint_url: string;
|
||||||
@@ -222,6 +223,7 @@ export async function connectExternalAgent(input: ConnectExternalAgentInput): Pr
|
|||||||
webhook_registered: Boolean(existingWebhook(contactEndpoints)),
|
webhook_registered: Boolean(existingWebhook(contactEndpoints)),
|
||||||
wallet_bound: Boolean(agent.wallet_address),
|
wallet_bound: Boolean(agent.wallet_address),
|
||||||
referral_url: growthKit.referral_url,
|
referral_url: growthKit.referral_url,
|
||||||
|
proposal_handoff_url: growthKit.proposal_handoff_url,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -242,6 +244,7 @@ export async function connectExternalAgent(input: ConnectExternalAgentInput): Pr
|
|||||||
webhook_registered: Boolean(existingWebhook(contactEndpoints)),
|
webhook_registered: Boolean(existingWebhook(contactEndpoints)),
|
||||||
wallet_bound: Boolean(agent.wallet_address),
|
wallet_bound: Boolean(agent.wallet_address),
|
||||||
referral_url: growthKit.referral_url,
|
referral_url: growthKit.referral_url,
|
||||||
|
proposal_handoff_url: growthKit.proposal_handoff_url,
|
||||||
campaign_kit_url: campaignKitUrl,
|
campaign_kit_url: campaignKitUrl,
|
||||||
onboarding_url: onboardingUrl,
|
onboarding_url: onboardingUrl,
|
||||||
referral_touchpoint_url: referralTouchpointUrl,
|
referral_touchpoint_url: referralTouchpointUrl,
|
||||||
@@ -252,6 +255,7 @@ export async function connectExternalAgent(input: ConnectExternalAgentInput): Pr
|
|||||||
demand_campaign_kit: demandCampaignKit,
|
demand_campaign_kit: demandCampaignKit,
|
||||||
next_actions: [
|
next_actions: [
|
||||||
"Use referral_url when sending human demand proposers to VibeWork.",
|
"Use referral_url when sending human demand proposers to VibeWork.",
|
||||||
|
"Use proposal_handoff_url to turn a qualified non-sensitive lead summary into a paid-intake handoff link.",
|
||||||
"Record non-sensitive outreach with referral_touchpoint_url.",
|
"Record non-sensitive outreach with referral_touchpoint_url.",
|
||||||
"Use campaign_kit_url for approved copy blocks and prefilled proposal links.",
|
"Use campaign_kit_url for approved copy blocks and prefilled proposal links.",
|
||||||
"Check referral_status_url for paid conversion and pending affiliate ledger.",
|
"Check referral_status_url for paid conversion and pending affiliate ledger.",
|
||||||
@@ -265,6 +269,7 @@ export function buildAgentConnectDescription() {
|
|||||||
success: true,
|
success: true,
|
||||||
endpoint: `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`,
|
endpoint: `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
next_step_after_connect: "Use the returned proposal_handoff_url for qualified leads before sending humans to VibeWork paid intake.",
|
||||||
accepts: {
|
accepts: {
|
||||||
agent_id: "stable external agent id",
|
agent_id: "stable external agent id",
|
||||||
tool: "optional integration id such as aider, openclaw, openhands, n8n, dify",
|
tool: "optional integration id such as aider, openclaw, openhands, n8n, dify",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export type TelegramControlPlaneRole = {
|
|||||||
successSignal: string;
|
successSignal: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const INTEGRATION_CATALOG_UPDATED_AT = "2026-06-11";
|
export const INTEGRATION_CATALOG_UPDATED_AT = "2026-06-12";
|
||||||
|
|
||||||
const VIBEWORK_SITE_URL = (
|
const VIBEWORK_SITE_URL = (
|
||||||
process.env.VIBEWORK_SITE_URL ||
|
process.env.VIBEWORK_SITE_URL ||
|
||||||
@@ -111,7 +111,7 @@ export const A2A_AGENT_INTEGRATIONS: A2AAgentIntegration[] = [
|
|||||||
bestFor: ["operator assistant", "lead triage", "cross-app workflow execution"],
|
bestFor: ["operator assistant", "lead triage", "cross-app workflow execution"],
|
||||||
integrationMode: ["growth kit referral", "agent card registration", "Telegram control-plane relay", "MCP tools behind operator approval"],
|
integrationMode: ["growth kit referral", "agent card registration", "Telegram control-plane relay", "MCP tools behind operator approval"],
|
||||||
monetizationLane: "demand-scout",
|
monetizationLane: "demand-scout",
|
||||||
onboarding: ["Request growth kit", "Register agent card", "Post only sanitized lead summary to Telegram", "Send humans to /propose"],
|
onboarding: ["Request growth kit", "Register agent card", "Create proposal handoff links from sanitized summaries", "Send humans to /propose"],
|
||||||
guardrails: ["No direct credential collection", "No production write access by default", "Patch and isolate self-hosted runtimes"],
|
guardrails: ["No direct credential collection", "No production write access by default", "Patch and isolate self-hosted runtimes"],
|
||||||
sourceUrl: "https://github.com/openclaw/openclaw",
|
sourceUrl: "https://github.com/openclaw/openclaw",
|
||||||
},
|
},
|
||||||
@@ -189,7 +189,7 @@ export const A2A_AGENT_INTEGRATIONS: A2AAgentIntegration[] = [
|
|||||||
bestFor: ["specialized crews", "lead research", "proposal scoping", "content operations"],
|
bestFor: ["specialized crews", "lead research", "proposal scoping", "content operations"],
|
||||||
integrationMode: ["growth kit campaign agent", "proposal scoping crew", "MCP client tools"],
|
integrationMode: ["growth kit campaign agent", "proposal scoping crew", "MCP client tools"],
|
||||||
monetizationLane: "demand-scout",
|
monetizationLane: "demand-scout",
|
||||||
onboarding: ["Create scout/researcher/reviewer roles", "Route all human payment to /propose"],
|
onboarding: ["Create scout/researcher/reviewer roles", "Use proposal handoff for qualified leads", "Route all human payment to /propose"],
|
||||||
guardrails: ["No spam outreach", "No scraped personal data beyond policy", "No payout before paid conversion"],
|
guardrails: ["No spam outreach", "No scraped personal data beyond policy", "No payout before paid conversion"],
|
||||||
sourceUrl: "https://docs.crewai.com/",
|
sourceUrl: "https://docs.crewai.com/",
|
||||||
},
|
},
|
||||||
@@ -228,7 +228,7 @@ export const A2A_AGENT_INTEGRATIONS: A2AAgentIntegration[] = [
|
|||||||
bestFor: ["lead capture", "CRM sync", "payment follow-up", "operator notifications"],
|
bestFor: ["lead capture", "CRM sync", "payment follow-up", "operator notifications"],
|
||||||
integrationMode: ["webhook to growth kit", "proposal event receiver", "Telegram notification workflow"],
|
integrationMode: ["webhook to growth kit", "proposal event receiver", "Telegram notification workflow"],
|
||||||
monetizationLane: "demand-scout",
|
monetizationLane: "demand-scout",
|
||||||
onboarding: ["Use webhook allowlist", "Write only sanitized proposal metadata", "Send all payments to VibeWork"],
|
onboarding: ["Use webhook allowlist", "Write only sanitized proposal metadata", "Call proposal handoff API", "Send all payments to VibeWork"],
|
||||||
guardrails: ["Restrict public webhooks", "Rotate credentials", "No secret values in workflow logs"],
|
guardrails: ["Restrict public webhooks", "Rotate credentials", "No secret values in workflow logs"],
|
||||||
sourceUrl: "https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/",
|
sourceUrl: "https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/",
|
||||||
},
|
},
|
||||||
@@ -241,7 +241,7 @@ export const A2A_AGENT_INTEGRATIONS: A2AAgentIntegration[] = [
|
|||||||
bestFor: ["proposal chatbot", "RAG intake", "workflow apps", "team handoff"],
|
bestFor: ["proposal chatbot", "RAG intake", "workflow apps", "team handoff"],
|
||||||
integrationMode: ["intake bot with referral URL", "MCP/API connector", "operator review queue"],
|
integrationMode: ["intake bot with referral URL", "MCP/API connector", "operator review queue"],
|
||||||
monetizationLane: "demand-scout",
|
monetizationLane: "demand-scout",
|
||||||
onboarding: ["Embed VibeWork growth kit link", "Send structured proposal payload", "Keep payment on /propose"],
|
onboarding: ["Embed VibeWork growth kit link", "Send structured proposal payload to handoff API", "Keep payment on /propose"],
|
||||||
guardrails: ["No direct bounty promise before paid review", "Do not store credentials from demand proposers"],
|
guardrails: ["No direct bounty promise before paid review", "Do not store credentials from demand proposers"],
|
||||||
sourceUrl: "https://docs.dify.ai/en/use-dify/nodes/agent",
|
sourceUrl: "https://docs.dify.ai/en/use-dify/nodes/agent",
|
||||||
},
|
},
|
||||||
@@ -280,7 +280,7 @@ export const A2A_AGENT_INTEGRATIONS: A2AAgentIntegration[] = [
|
|||||||
bestFor: ["agent discovery", "partner scouting", "marketplace listings"],
|
bestFor: ["agent discovery", "partner scouting", "marketplace listings"],
|
||||||
integrationMode: ["growth campaign", "agent card invitation", "external listing"],
|
integrationMode: ["growth campaign", "agent card invitation", "external listing"],
|
||||||
monetizationLane: "demand-scout",
|
monetizationLane: "demand-scout",
|
||||||
onboarding: ["Recruit agents into PENDING status", "Send demand to attributed proposal URL"],
|
onboarding: ["Recruit agents into PENDING status", "Send demand through attributed proposal handoff URL"],
|
||||||
guardrails: ["Do not trust marketplace identity alone", "Require VibeWork agent review before payout"],
|
guardrails: ["Do not trust marketplace identity alone", "Require VibeWork agent review before payout"],
|
||||||
sourceUrl: "https://agent.ai/",
|
sourceUrl: "https://agent.ai/",
|
||||||
},
|
},
|
||||||
@@ -304,6 +304,7 @@ export function buildA2aIntegrationCatalog(agentId?: string | null) {
|
|||||||
const agentConnectUrl = `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`;
|
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 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 growthKitUrl = `${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id={agent_id}®ister=true`;
|
||||||
|
const proposalHandoffUrl = `${AGENT_GATEWAY_URL}/api/a2a/proposals/handoff?agent_id={agent_id}®ister=true`;
|
||||||
const integrationsUrl = `${AGENT_GATEWAY_URL}/api/a2a/integrations`;
|
const integrationsUrl = `${AGENT_GATEWAY_URL}/api/a2a/integrations`;
|
||||||
const referralTouchpointUrl = `${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id={agent_id}&touchpoint=proposal_link_sent`;
|
const referralTouchpointUrl = `${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id={agent_id}&touchpoint=proposal_link_sent`;
|
||||||
const referralStatusUrl = `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id={agent_id}`;
|
const referralStatusUrl = `${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id={agent_id}`;
|
||||||
@@ -318,6 +319,7 @@ export function buildA2aIntegrationCatalog(agentId?: string | null) {
|
|||||||
agent_connect: agentConnectUrl,
|
agent_connect: agentConnectUrl,
|
||||||
agent_connect_page: `${AGENT_GATEWAY_URL}/agents/connect`,
|
agent_connect_page: `${AGENT_GATEWAY_URL}/agents/connect`,
|
||||||
demand_campaign_kit: campaignKitUrl,
|
demand_campaign_kit: campaignKitUrl,
|
||||||
|
proposal_handoff: proposalHandoffUrl,
|
||||||
integration_catalog: integrationsUrl,
|
integration_catalog: integrationsUrl,
|
||||||
growth_kit: growthKitUrl,
|
growth_kit: growthKitUrl,
|
||||||
referral_touchpoint: referralTouchpointUrl,
|
referral_touchpoint: referralTouchpointUrl,
|
||||||
@@ -336,6 +338,7 @@ export function buildA2aIntegrationCatalog(agentId?: string | null) {
|
|||||||
`Connect webhook and wallet lane: ${AGENT_GATEWAY_URL}/agents/connect?agent_id=${encodeURIComponent(sanitizedAgentId)}`,
|
`Connect webhook and wallet lane: ${AGENT_GATEWAY_URL}/agents/connect?agent_id=${encodeURIComponent(sanitizedAgentId)}`,
|
||||||
`Start onboarding contract: ${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=${encodeURIComponent(sanitizedAgentId)}®ister=true`,
|
`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`,
|
`Fetch demand campaign kit: ${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodeURIComponent(sanitizedAgentId)}®ister=true`,
|
||||||
|
`Create a safe paid proposal handoff: ${AGENT_GATEWAY_URL}/api/a2a/proposals/handoff?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`,
|
`Record non-sensitive referral touchpoint: ${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodeURIComponent(sanitizedAgentId)}&touchpoint=proposal_link_sent`,
|
||||||
`Fetch your growth kit: ${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id=${encodeURIComponent(sanitizedAgentId)}®ister=true`,
|
`Fetch your growth kit: ${AGENT_GATEWAY_URL}/api/a2a/growth/kit?agent_id=${encodeURIComponent(sanitizedAgentId)}®ister=true`,
|
||||||
`Check referral status: ${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodeURIComponent(sanitizedAgentId)}`,
|
`Check referral status: ${AGENT_GATEWAY_URL}/api/a2a/referrals/status?agent_id=${encodeURIComponent(sanitizedAgentId)}`,
|
||||||
@@ -348,6 +351,7 @@ export function buildA2aIntegrationCatalog(agentId?: string | null) {
|
|||||||
"Connect through /agents/connect or POST /api/a2a/agents/connect.",
|
"Connect through /agents/connect or POST /api/a2a/agents/connect.",
|
||||||
"Call /api/a2a/onboarding with register=true.",
|
"Call /api/a2a/onboarding with register=true.",
|
||||||
"Fetch /api/a2a/campaigns/demand before posting or DMing demand proposers.",
|
"Fetch /api/a2a/campaigns/demand before posting or DMing demand proposers.",
|
||||||
|
"Use /api/a2a/proposals/handoff to create safe prefilled paid-intake links from non-sensitive summaries.",
|
||||||
"Record /api/a2a/referrals/touch when sending or qualifying proposal leads.",
|
"Record /api/a2a/referrals/touch when sending or qualifying proposal leads.",
|
||||||
"Fetch a growth kit with register=true.",
|
"Fetch a growth kit with register=true.",
|
||||||
"Register an Agent Card for execution privileges.",
|
"Register an Agent Card for execution privileges.",
|
||||||
|
|||||||
@@ -134,6 +134,23 @@ function buildChannelProposalUrl(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildProposalHandoffUrl(params: {
|
||||||
|
agentId: string;
|
||||||
|
campaign?: string | null;
|
||||||
|
source?: string | null;
|
||||||
|
channel?: string | null;
|
||||||
|
packageId?: string | null;
|
||||||
|
}) {
|
||||||
|
const url = new URL("/api/a2a/proposals/handoff", AGENT_GATEWAY_URL);
|
||||||
|
url.searchParams.set("agent_id", sanitizeAgentId(params.agentId));
|
||||||
|
url.searchParams.set("register", "true");
|
||||||
|
if (params.campaign) url.searchParams.set("campaign", sanitizeAgentId(params.campaign));
|
||||||
|
if (params.source) url.searchParams.set("source", sanitizeAgentId(params.source));
|
||||||
|
if (params.channel && params.channel !== "all") url.searchParams.set("channel", sanitizeAgentId(params.channel));
|
||||||
|
if (params.packageId) url.searchParams.set("package", getProposalPackage(params.packageId).id);
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
export function buildAgentDemandCampaignKit(params: {
|
export function buildAgentDemandCampaignKit(params: {
|
||||||
agentId: string;
|
agentId: string;
|
||||||
campaign?: string | null;
|
campaign?: string | null;
|
||||||
@@ -146,6 +163,13 @@ export function buildAgentDemandCampaignKit(params: {
|
|||||||
const selectedChannel = sanitizeAgentId(params.channel) || "all";
|
const selectedChannel = sanitizeAgentId(params.channel) || "all";
|
||||||
const defaultUrl = buildChannelProposalUrl({ agentId, campaign, source });
|
const defaultUrl = buildChannelProposalUrl({ agentId, campaign, source });
|
||||||
const priorityUrl = buildChannelProposalUrl({ agentId, campaign, source, packageId: "priority" });
|
const priorityUrl = buildChannelProposalUrl({ agentId, campaign, source, packageId: "priority" });
|
||||||
|
const proposalHandoffUrl = buildProposalHandoffUrl({
|
||||||
|
agentId,
|
||||||
|
campaign,
|
||||||
|
source,
|
||||||
|
channel: selectedChannel,
|
||||||
|
packageId: "scout",
|
||||||
|
});
|
||||||
const touchpointUrl =
|
const touchpointUrl =
|
||||||
`${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodeURIComponent(agentId)}` +
|
`${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodeURIComponent(agentId)}` +
|
||||||
`&campaign=${encodeURIComponent(campaign)}&source=${encodeURIComponent(source)}` +
|
`&campaign=${encodeURIComponent(campaign)}&source=${encodeURIComponent(source)}` +
|
||||||
@@ -185,6 +209,7 @@ export function buildAgentDemandCampaignKit(params: {
|
|||||||
objective:
|
objective:
|
||||||
"Route qualified humans or teams with software, automation, data, or AI workflow demand into VibeWork paid proposal intake.",
|
"Route qualified humans or teams with software, automation, data, or AI workflow demand into VibeWork paid proposal intake.",
|
||||||
agent_connect_url: agentConnectUrl,
|
agent_connect_url: agentConnectUrl,
|
||||||
|
proposal_handoff_url: proposalHandoffUrl,
|
||||||
landing_url: defaultUrl,
|
landing_url: defaultUrl,
|
||||||
touchpoint_url: touchpointUrl,
|
touchpoint_url: touchpointUrl,
|
||||||
prefill_url_template: prefillUrlTemplate,
|
prefill_url_template: prefillUrlTemplate,
|
||||||
@@ -211,7 +236,7 @@ export function buildAgentDemandCampaignKit(params: {
|
|||||||
urgent_cta:
|
urgent_cta:
|
||||||
`For urgent or cross-system work, use Priority Bounty Launch so VibeWork can fast-track review and agent broadcast prep: ${priorityUrl}`,
|
`For urgent or cross-system work, use Priority Bounty Launch so VibeWork can fast-track review and agent broadcast prep: ${priorityUrl}`,
|
||||||
agent_instruction:
|
agent_instruction:
|
||||||
`Use this campaign only for qualified demand. Connect webhook/wallet at ${agentConnectUrl}, record outreach or handoff with touchpoint_url, then send humans to ${defaultUrl}, or generate a prefilled URL from prefill_url_template after removing secrets. Do not collect payment, passwords, private keys, or production credentials yourself.`,
|
`Use this campaign only for qualified demand. Connect webhook/wallet at ${agentConnectUrl}, create a safe paid proposal handoff through ${proposalHandoffUrl}, record outreach with touchpoint_url, then send humans to VibeWork. Do not collect payment, passwords, private keys, or production credentials yourself.`,
|
||||||
},
|
},
|
||||||
target_segments: [
|
target_segments: [
|
||||||
"Teams with manual spreadsheet, CRM, reporting, or back-office workflows.",
|
"Teams with manual spreadsheet, CRM, reporting, or back-office workflows.",
|
||||||
@@ -231,6 +256,8 @@ export function buildAgentDemandCampaignKit(params: {
|
|||||||
campaign,
|
campaign,
|
||||||
source,
|
source,
|
||||||
agent_connect_url: agentConnectUrl,
|
agent_connect_url: agentConnectUrl,
|
||||||
|
proposal_handoff_endpoint: `${AGENT_GATEWAY_URL}/api/a2a/proposals/handoff`,
|
||||||
|
proposal_handoff_url: proposalHandoffUrl,
|
||||||
referral_url: defaultUrl,
|
referral_url: defaultUrl,
|
||||||
touchpoint_endpoint: `${AGENT_GATEWAY_URL}/api/a2a/referrals/touch`,
|
touchpoint_endpoint: `${AGENT_GATEWAY_URL}/api/a2a/referrals/touch`,
|
||||||
touchpoint_url: touchpointUrl,
|
touchpoint_url: touchpointUrl,
|
||||||
@@ -253,10 +280,24 @@ export function buildAgentDemandCampaignKit(params: {
|
|||||||
stack: "comma-separated public tools",
|
stack: "comma-separated public tools",
|
||||||
urgency: "normal | this_week | urgent",
|
urgency: "normal | this_week | urgent",
|
||||||
},
|
},
|
||||||
|
handoff_json_example: {
|
||||||
|
agent_id: agentId,
|
||||||
|
campaign,
|
||||||
|
source,
|
||||||
|
channel: selectedChannel === "all" ? null : selectedChannel,
|
||||||
|
package: "scout",
|
||||||
|
title: "short public task title",
|
||||||
|
summary: "non-sensitive summary only",
|
||||||
|
desired_outcome: "acceptance-oriented outcome",
|
||||||
|
budget_usd: "rough budget number",
|
||||||
|
stack: "comma-separated public tools",
|
||||||
|
urgency: "normal",
|
||||||
|
},
|
||||||
prefill_url_template: prefillUrlTemplate,
|
prefill_url_template: prefillUrlTemplate,
|
||||||
},
|
},
|
||||||
success_metrics: [
|
success_metrics: [
|
||||||
"EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED",
|
"EXTERNAL_A2A_DEMAND_CAMPAIGN_KIT_ISSUED",
|
||||||
|
"EXTERNAL_A2A_PROPOSAL_HANDOFF_CREATED",
|
||||||
"EXTERNAL_A2A_REFERRAL_TOUCHPOINT_RECORDED",
|
"EXTERNAL_A2A_REFERRAL_TOUCHPOINT_RECORDED",
|
||||||
"EXTERNAL_DEMAND_PROPOSAL_VIEW",
|
"EXTERNAL_DEMAND_PROPOSAL_VIEW",
|
||||||
"EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED",
|
"EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED",
|
||||||
@@ -292,6 +333,12 @@ export function buildAgentGrowthKit(params: {
|
|||||||
const touchpointUrl =
|
const touchpointUrl =
|
||||||
`${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodeURIComponent(agentId)}&campaign=${encodeURIComponent(params.campaign || "a2a-agent-referral")}&source=${encodeURIComponent(params.source || "external-agent")}`;
|
`${AGENT_GATEWAY_URL}/api/a2a/referrals/touch?agent_id=${encodeURIComponent(agentId)}&campaign=${encodeURIComponent(params.campaign || "a2a-agent-referral")}&source=${encodeURIComponent(params.source || "external-agent")}`;
|
||||||
const agentConnectUrl = `${AGENT_GATEWAY_URL}/agents/connect?agent_id=${encodeURIComponent(agentId)}&source=${encodeURIComponent(params.source || "external-agent")}`;
|
const agentConnectUrl = `${AGENT_GATEWAY_URL}/agents/connect?agent_id=${encodeURIComponent(agentId)}&source=${encodeURIComponent(params.source || "external-agent")}`;
|
||||||
|
const proposalHandoffUrl = buildProposalHandoffUrl({
|
||||||
|
agentId,
|
||||||
|
campaign: params.campaign || "a2a-agent-referral",
|
||||||
|
source: params.source || "external-agent",
|
||||||
|
packageId: "scout",
|
||||||
|
});
|
||||||
const campaignKit = buildAgentDemandCampaignKit({
|
const campaignKit = buildAgentDemandCampaignKit({
|
||||||
agentId,
|
agentId,
|
||||||
campaign: params.campaign || "a2a-agent-referral",
|
campaign: params.campaign || "a2a-agent-referral",
|
||||||
@@ -301,6 +348,7 @@ export function buildAgentGrowthKit(params: {
|
|||||||
return {
|
return {
|
||||||
agent_id: agentId,
|
agent_id: agentId,
|
||||||
agent_connect_url: agentConnectUrl,
|
agent_connect_url: agentConnectUrl,
|
||||||
|
proposal_handoff_url: proposalHandoffUrl,
|
||||||
referral_url: proposalUrl,
|
referral_url: proposalUrl,
|
||||||
landing_domain: VIBEWORK_SITE_URL,
|
landing_domain: VIBEWORK_SITE_URL,
|
||||||
gateway_api: AGENT_GATEWAY_URL,
|
gateway_api: AGENT_GATEWAY_URL,
|
||||||
@@ -311,6 +359,7 @@ export function buildAgentGrowthKit(params: {
|
|||||||
external_agent_pitch: [
|
external_agent_pitch: [
|
||||||
`Connect webhook and payout lane at ${agentConnectUrl}`,
|
`Connect webhook and payout lane at ${agentConnectUrl}`,
|
||||||
"Find humans or teams with software, automation, data, or AI workflow needs.",
|
"Find humans or teams with software, automation, data, or AI workflow needs.",
|
||||||
|
`Create safe paid proposal handoff links at ${proposalHandoffUrl}`,
|
||||||
`Record outreach or proposal-link handoff at ${touchpointUrl}`,
|
`Record outreach or proposal-link handoff at ${touchpointUrl}`,
|
||||||
`Send them to ${proposalUrl}`,
|
`Send them to ${proposalUrl}`,
|
||||||
"Ask them to describe the outcome, budget, stack, and acceptance criteria.",
|
"Ask them to describe the outcome, budget, stack, and acceptance criteria.",
|
||||||
@@ -324,12 +373,13 @@ export function buildAgentGrowthKit(params: {
|
|||||||
dm:
|
dm:
|
||||||
`I can route your software/automation request into VibeWork's AI-agent bounty network. Start with a paid proposal intake: ${proposalUrl}`,
|
`I can route your software/automation request into VibeWork's AI-agent bounty network. Start with a paid proposal intake: ${proposalUrl}`,
|
||||||
agent_instruction:
|
agent_instruction:
|
||||||
`Connect webhook/wallet at ${agentConnectUrl}. When you meet a human demand proposer, do not collect sensitive credentials. Send them to ${proposalUrl} and let VibeWork handle paid intake, scoping, and routing.`,
|
`Connect webhook/wallet at ${agentConnectUrl}. When you meet a human demand proposer, create a safe handoff at ${proposalHandoffUrl}, do not collect sensitive credentials, and let VibeWork handle paid intake, scoping, and routing.`,
|
||||||
},
|
},
|
||||||
campaign_kit: campaignKit,
|
campaign_kit: campaignKit,
|
||||||
api_next_steps: {
|
api_next_steps: {
|
||||||
agent_connect: agentConnectUrl,
|
agent_connect: agentConnectUrl,
|
||||||
connect_agent_api: `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`,
|
connect_agent_api: `${AGENT_GATEWAY_URL}/api/a2a/agents/connect`,
|
||||||
|
proposal_handoff: proposalHandoffUrl,
|
||||||
onboarding: `${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=${encodeURIComponent(agentId)}®ister=true`,
|
onboarding: `${AGENT_GATEWAY_URL}/api/a2a/onboarding?agent_id=${encodeURIComponent(agentId)}®ister=true`,
|
||||||
demand_campaign_kit: `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodeURIComponent(agentId)}®ister=true`,
|
demand_campaign_kit: `${AGENT_GATEWAY_URL}/api/a2a/campaigns/demand?agent_id=${encodeURIComponent(agentId)}®ister=true`,
|
||||||
register_agent_card: `${AGENT_GATEWAY_URL}/api/mcp/agent_card`,
|
register_agent_card: `${AGENT_GATEWAY_URL}/api/mcp/agent_card`,
|
||||||
|
|||||||
Reference in New Issue
Block a user