diff --git a/README.md b/README.md index 89ae4be..a23e51e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@

- Version 0.2.17 + Version 0.2.18 A2A v0.3.0 MIT License Status: Beta @@ -235,7 +235,8 @@ Each agent has an A2A Agent Card describing its capabilities: ], "capabilities": { "streaming": false, "pushNotifications": false }, "defaultInputModes": ["text/plain"], - "defaultOutputModes": ["text/plain"] + "defaultOutputModes": ["text/plain"], + "fundingAddress": "0x742d...8f4a" } ``` @@ -449,7 +450,7 @@ The protocol version is defined in a single place: ```typescript // protocol/src/types.ts -export const REEF_VERSION = "0.2.17"; +export const REEF_VERSION = "0.2.18"; export const A2A_PROTOCOL_VERSION = "0.3.0"; ``` diff --git a/client/package.json b/client/package.json index bc2d0b9..fb8ee02 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@reef-protocol/client", - "version": "0.2.17", + "version": "0.2.18", "description": "Reef Protocol client — daemon, CLI, and identity management", "type": "module", "main": "dist/daemon.js", diff --git a/directory/package.json b/directory/package.json index 8ca046d..b9013bc 100644 --- a/directory/package.json +++ b/directory/package.json @@ -1,7 +1,7 @@ { "name": "@reef-protocol/directory", "private": true, - "version": "0.2.17", + "version": "0.2.18", "description": "Reef Protocol directory server — agent discovery and network stats", "type": "module", "main": "dist/index.js", diff --git a/directory/src/migrations/00011_add-funding-address.ts b/directory/src/migrations/00011_add-funding-address.ts new file mode 100644 index 0000000..2d45907 --- /dev/null +++ b/directory/src/migrations/00011_add-funding-address.ts @@ -0,0 +1,16 @@ +import { DataTypes, type Sequelize } from "sequelize"; + +export async function up({ context: sequelize }: { context: Sequelize }) { + const qi = sequelize.getQueryInterface(); + + await qi.addColumn("agents", "funding_address", { + type: DataTypes.STRING(42), + allowNull: true, + defaultValue: null, + }); +} + +export async function down({ context: sequelize }: { context: Sequelize }) { + const qi = sequelize.getQueryInterface(); + await qi.removeColumn("agents", "funding_address"); +} diff --git a/directory/src/models/Agent.ts b/directory/src/models/Agent.ts index 24dd4cd..6af9ec6 100644 --- a/directory/src/models/Agent.ts +++ b/directory/src/models/Agent.ts @@ -20,6 +20,7 @@ export interface AgentAttributes { reputation_updated_at: Date | null; country: string | null; icon_url: string | null; + funding_address: string | null; } export class Agent extends Model { @@ -41,6 +42,7 @@ export class Agent extends Model { declare reputation_updated_at: Date | null; declare country: string | null; declare icon_url: string | null; + declare funding_address: string | null; declare readonly created_at: Date; declare readonly updated_at: Date; } @@ -133,6 +135,11 @@ export function initAgentModel(sequelize: Sequelize): void { allowNull: true, defaultValue: null, }, + funding_address: { + type: DataTypes.STRING(42), + allowNull: true, + defaultValue: null, + }, }, { sequelize, diff --git a/directory/src/routes/agents.ts b/directory/src/routes/agents.ts index 29fced3..55e35d5 100644 --- a/directory/src/routes/agents.ts +++ b/directory/src/routes/agents.ts @@ -56,6 +56,8 @@ agentsRouter.post("/register", registrationLimiter, async (req, res, next) => { const skillTags = agentCard.skills.flatMap((s) => s.tags); const iconUrl = (agentCard as unknown as Record) .iconUrl as string | undefined; + const fundingAddress = (agentCard as unknown as Record) + .fundingAddress as string | undefined; let agent = await Agent.findByPk(addr); @@ -70,6 +72,7 @@ agentsRouter.post("/register", registrationLimiter, async (req, res, next) => { last_heartbeat: new Date(), agent_card: agentCard, icon_url: iconUrl || agent.icon_url, + funding_address: fundingAddress || agent.funding_address, }); } else { agent = await Agent.create({ @@ -90,6 +93,7 @@ agentsRouter.post("/register", registrationLimiter, async (req, res, next) => { app_interactions: {}, reputation_updated_at: null, icon_url: iconUrl || null, + funding_address: fundingAddress || null, }); } @@ -152,6 +156,7 @@ agentsRouter.get("/search", searchLimiter, async (req, res, next) => { availability: a.availability, agentCard: a.agent_card, iconUrl: a.icon_url ?? null, + fundingAddress: a.funding_address ?? null, registeredAt: a.created_at?.toISOString(), lastHeartbeat: a.last_heartbeat?.toISOString(), reputationScore: a.reputation_score, @@ -378,6 +383,7 @@ agentsRouter.get("/:address", readLimiter, async (req, res, next) => { tasksFailed: agent.tasks_failed, totalInteractions: agent.total_interactions, country: agent.country ?? null, + fundingAddress: agent.funding_address ?? null, }); } catch (err) { next(err); diff --git a/package.json b/package.json index d2612d3..30e9b55 100644 --- a/package.json +++ b/package.json @@ -32,5 +32,5 @@ "typescript-eslint": "^8.56.0", "vitest": "^3.0.0" }, - "version": "0.2.17" + "version": "0.2.18" } diff --git a/protocol/package.json b/protocol/package.json index de5d73d..789654c 100644 --- a/protocol/package.json +++ b/protocol/package.json @@ -1,6 +1,6 @@ { "name": "@reef-protocol/protocol", - "version": "0.2.17", + "version": "0.2.18", "description": "Shared message types, envelope codec, and validation for Reef Protocol", "type": "module", "main": "dist/index.js", diff --git a/protocol/src/builders.ts b/protocol/src/builders.ts index 8e3e42a..04f6c1d 100644 --- a/protocol/src/builders.ts +++ b/protocol/src/builders.ts @@ -84,7 +84,7 @@ export function buildReefAgentCard( name: string, description: string, skills: AgentSkill[], - options?: { iconUrl?: string }, + options?: { iconUrl?: string; fundingAddress?: string }, ): AgentCard { const card: AgentCard = { name, @@ -105,6 +105,10 @@ export function buildReefAgentCard( if (options?.iconUrl) { (card as unknown as Record).iconUrl = options.iconUrl; } + if (options?.fundingAddress) { + (card as unknown as Record).fundingAddress = + options.fundingAddress; + } return card; } diff --git a/protocol/src/types.ts b/protocol/src/types.ts index 020b4ca..a1fb0e6 100644 --- a/protocol/src/types.ts +++ b/protocol/src/types.ts @@ -2,7 +2,7 @@ import type { AgentCard } from "@a2a-js/sdk"; -export const REEF_VERSION = "0.2.17"; +export const REEF_VERSION = "0.2.18"; export const A2A_PROTOCOL_VERSION = "0.3.0"; export const DEFAULT_DIRECTORY_URL = "https://reef-protocol-production.up.railway.app"; @@ -104,6 +104,8 @@ export interface AgentSearchResult { agentCard: AgentCard | null; /** URL to an icon/avatar image for this agent */ iconUrl?: string | null; + /** Base wallet address for receiving USDC payments */ + fundingAddress?: string | null; registeredAt?: string; lastHeartbeat?: string; reputationScore?: number; diff --git a/protocol/src/validation.ts b/protocol/src/validation.ts index b03eab4..f4aeb0e 100644 --- a/protocol/src/validation.ts +++ b/protocol/src/validation.ts @@ -116,6 +116,7 @@ export const agentCardSchema = z.object({ defaultOutputModes: z.array(z.string()), preferredTransport: z.string().optional(), iconUrl: z.string().url().optional(), + fundingAddress: z.string().optional(), provider: z .object({ organization: z.string(),