/** * fadpPayment — universal FADP payment skill. * * One-liner plug-in for LangChain, AutoGen, Vercel AI SDK, Claude Code, and raw LLM loops. * Handles the full 402-detect → sign → retry cycle automatically. * * Usage: * LangChain: tools: [toLangChainTool(fadpPaymentSkill(agent))] * AutoGen: register_function(fadpPaymentSkill(agent).autogen(), ...) * Vercel AI: tools: { fadp_pay: toVercelAITool(fadpPaymentSkill(agent)) } * Anthropic: tools: [fadpPaymentSkill(agent).anthropic()] * OpenAI: tools: [fadpPaymentSkill(agent).openai()] * Claude Code: install via fluid-wallet-skills/fadp-pay * Raw: await fadpPaymentSkill(agent).run({ url, method, body, maxPayUsd }) */ import type { FluidAgent } from "../FluidAgent"; // ─── Core skill definition ────────────────────────────────────────────────── export interface FADPPaymentSkillInput { /** URL to fetch — may be FADP-gated (will auto-pay the 402) */ url: string; /** HTTP method (default: GET) */ method?: string; /** Request body as JSON string for POST/PUT */ body?: string; /** Max USDC to auto-pay if the endpoint charges (default: 10) */ maxPayUsd?: number; /** What you expect to get — included in response for context */ description?: string; } export interface FADPPaymentSkillResult { status: number; ok: boolean; paid: boolean; data: unknown; description?: string; } export interface FADPPaymentSkill { name: string; description: string; parameters: object; /** Execute the skill directly */ run(input: FADPPaymentSkillInput): Promise; /** Anthropic Claude tool_use format */ anthropic(): AnthropicFADPTool; /** OpenAI function calling format */ openai(): OpenAIFADPTool; /** LangChain DynamicStructuredTool-compatible format */ langchain(): LangChainFADPTool; /** AutoGen / Python function signature + schema */ autogen(): AutoGenFADPDef; /** Vercel AI SDK tool format */ vercel(jsonSchemaHelper: (schema: object) => unknown): VercelFADPTool; } // ─── Framework-specific types ─────────────────────────────────────────────── export interface AnthropicFADPTool { name: string; description: string; input_schema: { type: "object"; properties: Record; required?: string[] }; } export interface OpenAIFADPTool { type: "function"; function: { name: string; description: string; parameters: object }; } export interface LangChainFADPTool { name: string; description: string; schema: object; func(input: FADPPaymentSkillInput): Promise; } export interface AutoGenFADPDef { name: string; description: string; parameters: object; handler(input: FADPPaymentSkillInput): Promise; } export interface VercelFADPTool { description: string; parameters: unknown; execute(input: FADPPaymentSkillInput): Promise; } // ─── Shared schema ────────────────────────────────────────────────────────── const SKILL_NAME = "fadp_pay"; const SKILL_DESCRIPTION = "Call any URL that may be protected by an FADP (Fluid Agentic DeFi Protocol) payment wall. " + "Automatically handles the full payment cycle: detects HTTP 402 → pays USDC on Base → retries with proof. " + "Use this whenever you need to access paid APIs, premium data, or AI services (LLM calls, GPU jobs, compute). " + "Returns the API response after any required payment. Requires the 'pay' scope."; const PARAMETERS_SCHEMA = { type: "object" as const, required: ["url"], properties: { url: { type: "string", description: "The API or service URL to call (may be FADP-gated)", }, method: { type: "string", description: "HTTP method: GET | POST | PUT | DELETE (default: GET)", }, body: { type: "string", description: "Request body as a JSON string (for POST/PUT requests)", }, maxPayUsd: { type: "number", description: "Maximum USDC to auto-pay if the endpoint charges a fee (default: 10)", }, description: { type: "string", description: "What you are trying to fetch — for logging and context", }, }, }; // ─── Core executor ────────────────────────────────────────────────────────── async function execute( agent: FluidAgent, input: FADPPaymentSkillInput ): Promise { const { url, method = "GET", body, maxPayUsd = 10, description } = input; const fadpFetch = agent.fadpFetch({ maxAutoPayUsd: Number(maxPayUsd) }); const init: RequestInit = { method, headers: { "Content-Type": "application/json" }, ...(body ? { body: typeof body === "string" ? body : JSON.stringify(body) } : {}), }; // First probe — detect if free or FADP-gated const probe = await fetch(url, init); const wasFree = probe.status !== 402 || !probe.headers.get("x-fadp-required"); let res: Response; let paid = false; if (wasFree) { res = probe; } else { res = await fadpFetch(url, init); paid = true; } const text = await res.text(); let data: unknown; try { data = JSON.parse(text); } catch { data = text; } return { status: res.status, ok: res.ok, paid, data, description }; } function resultToString(r: FADPPaymentSkillResult): string { return JSON.stringify(r); } // ─── Factory ───────────────────────────────────────────────────────────────── /** * Create a universal FADP payment skill bound to a FluidAgent instance. * * @example * import { FluidAgent } from 'fluid-wallet-agentkit'; * import { fadpPaymentSkill } from 'fluid-wallet-agentkit/skills/fadpPayment'; * * const agent = new FluidAgent({ agentKey: 'fwag_...', fadp: true }); * const skill = fadpPaymentSkill(agent); * * // LangChain * const { DynamicStructuredTool } = require('@langchain/core/tools'); * const tool = new DynamicStructuredTool(skill.langchain()); * * // AutoGen * register_function(skill.autogen().handler, name: skill.autogen().name, ...) * * // Anthropic Claude * const tool = skill.anthropic(); * * // OpenAI * const tool = skill.openai(); * * // Vercel AI SDK * import { jsonSchema } from 'ai'; * const tool = skill.vercel(jsonSchema); * * // Raw * const result = await skill.run({ url: 'https://api.example.com/data' }); */ export function fadpPaymentSkill(agent: FluidAgent): FADPPaymentSkill { return { name: SKILL_NAME, description: SKILL_DESCRIPTION, parameters: PARAMETERS_SCHEMA, async run(input) { return execute(agent, input); }, anthropic() { return { name: SKILL_NAME, description: SKILL_DESCRIPTION, input_schema: PARAMETERS_SCHEMA as AnthropicFADPTool["input_schema"], }; }, openai() { return { type: "function" as const, function: { name: SKILL_NAME, description: SKILL_DESCRIPTION, parameters: PARAMETERS_SCHEMA, }, }; }, langchain() { return { name: SKILL_NAME, description: SKILL_DESCRIPTION, schema: PARAMETERS_SCHEMA, async func(input) { return resultToString(await execute(agent, input)); }, }; }, autogen() { return { name: SKILL_NAME, description: SKILL_DESCRIPTION, parameters: PARAMETERS_SCHEMA, async handler(input) { return resultToString(await execute(agent, input)); }, }; }, vercel(jsonSchemaHelper) { return { description: SKILL_DESCRIPTION, parameters: jsonSchemaHelper(PARAMETERS_SCHEMA), async execute(input) { return resultToString(await execute(agent, input)); }, }; }, }; } /** * Handle an Anthropic tool_use block for fadp_pay. * Drop-in for runAnthropicToolCall when you only need FADP. */ export async function runFADPToolCall( agent: FluidAgent, block: { name: string; input: Record } ): Promise { if (block.name !== SKILL_NAME) throw new Error(`fadpPayment skill only handles '${SKILL_NAME}', got '${block.name}'`); return resultToString(await execute(agent, block.input as unknown as FADPPaymentSkillInput)); } /** * Handle an OpenAI function_call for fadp_pay. */ export async function runFADPOpenAIToolCall( agent: FluidAgent, toolCall: { function: { name: string; arguments: string } } ): Promise { if (toolCall.function.name !== SKILL_NAME) throw new Error(`fadpPayment skill only handles '${SKILL_NAME}'`); return resultToString(await execute(agent, JSON.parse(toolCall.function.arguments) as FADPPaymentSkillInput)); }