Skip to content

feat(market): /market command + agent_talent tool to hire marketplace skills#83

Open
Zambala108 wants to merge 8 commits into
BlockRunAI:mainfrom
Zambala108:feat/agent-talent-marketplace
Open

feat(market): /market command + agent_talent tool to hire marketplace skills#83
Zambala108 wants to merge 8 commits into
BlockRunAI:mainfrom
Zambala108:feat/agent-talent-marketplace

Conversation

@Zambala108

Copy link
Copy Markdown

What

A buyer surface for the BlockRun agent marketplace (business.blockrun.ai) — a catalog of paid AI skills published by other creators, each runnable for ONE standard single-leg exact x402 USDC payment on Base. Two surfaces over one shared payment path (src/market/client.ts):

Surface For Actions
/market command a human at the terminal /market (browse top), /market <keyword> (search), /market info <slug> (detail card), /market run <slug> <input> (hire)
agent_talent tool the agent, autonomously mid-task {action:"list", query?} (free discovery), {action:"run", slug, input} (hire, paid)

Browsing/search/info are free GETs against the public catalog; run answers a 402 with one wallet-signed payment.

Why

The marketplace speaks standard x402, so Franklin pays it with the same EVM wallet and the same @blockrun/llm signer it already uses for the gateway. The only deltas from the existing BlockRun gateway primitive (src/tools/blockrun.ts) are the x-payment header name and the base URL (BLOCKRUN_MARKET_URL, default https://business.blockrun.ai). This makes Franklin a first-class buyer of marketplace talent — the agent can hire a specialized skill (live data, a domain analysis) for a sub-task it can't do well itself, charged only on success.

Note on naming: Franklin's skillRegistry already owns the word "skill" (local SKILL.md slash commands), so the marketplace gets its own namespace (/market, agent_talent) to avoid collision.

How it pays

src/market/client.ts is the single payment path shared by both surfaces:

  1. POST /api/v1/skills/<slug>/run → 402 with the standard challenge body { accepts:[{amount, payTo, network, …}] }.
  2. extractPaymentDetails + createPaymentPayload (@blockrun/llm) sign one EIP-3009 exact authorization for exactly the advertised amount.
  3. Retry with the x-payment header → result. Fails closed (the route settles only on success, so any non-2xx = no charge).

Receipts

Regression test (test/market.local.mjs, in npm test) drives the built client against a mock marketplace and asserts Franklin signs the EXACT advertised price — the invariant the live route enforces with signedValueMicro === totalMicro — plus the discovery parse, keyword filter, and fail-closed-no-charge path. It caught a real bug: a Run — <skill> payment description with an em-dash made @blockrun/llm's btoa-based payload encoding throw Invalid character on any non-Latin1 skill name; the description is now ASCII-stripped before signing.

Live E2E vs. real Coinbase CDP (Base mainnet). Franklin's built dist client (both runMarketSkill and the agent_talent tool) ran against a shim wired to business.blockrun.ai's real verifyPayment + real CDP /verify:

[shim] 402 challenge issued (network: eip155:8453 price: 0.01)
[shim] verifyPayment: {"valid":true,"payer":"0x1f44…"}
[shim] signedValueMicro: 10000 expected: 10000
[shim] PASS — payer 0x1f44…
Franklin runMarketSkill: ok:true status:200 paidUsd:0.01

This closes the one untested real-world link: CDP accepts the signature @blockrun/llm produces, and it clears the route's strict exact-price gate. The remaining legs (on-chain settle + instant creator payout) are proven on Base mainnet in the business repo: settle 0x1cea9dcdd2eff4e4474f1d504acf217d7c6ac85bca27432ce3dc476a6565e4da, payout 0xc0d971c3501cf4624e5c2601ec4bc77f0d2380e76fc0ee5091dcd61f8f18a066.

Tests / gates

  • npx tsc --noEmit ✓ · npm run build ✓ · node dist/index.js --help
  • npm test453/453 pass (incl. the new 5 market tests and the English-only source gate)

Notes

  • Base-only by design: a hire always pays from the EVM wallet regardless of the session chain.
  • No new deps. agent_talent is registered in allCapabilities; /market is registered in /help and command discovery.

🤖 Generated with Claude Code

krishna and others added 8 commits June 13, 2026 15:20
… skills

Add a buyer surface for the BlockRun agent marketplace
(business.blockrun.ai): a catalog of paid AI skills, each runnable for ONE
standard single-leg `exact` x402 USDC payment on Base. Two surfaces over one
payment path (src/market/client.ts):

- `/market` slash command — a human browses (`/market`, `/market <keyword>`),
  inspects (`/market info <slug>`), and hires (`/market run <slug> <input>`).
  Browsing is a free GET; run pays one x402 from the wallet.
- `agent_talent` tool — the agent discovers (`action:"list"`) and hires
  (`action:"run"`) talent autonomously mid-task, charged only on success.

The marketplace speaks standard x402, so Franklin pays it with the same EVM
wallet and the same `@blockrun/llm` signer it already uses for the gateway —
the only deltas from the BlockRun gateway primitive are the `x-payment`
header name and the base URL (env BLOCKRUN_MARKET_URL, default
business.blockrun.ai). Base-only: a hire always pays from the EVM wallet.

Regression test (test/market.local.mjs) drives the client against a mock
marketplace and asserts Franklin signs the EXACT advertised price — the
invariant the live route enforces with `signedValueMicro === totalMicro`.
This caught a real bug: a `Run — <skill>` description with an em-dash made
@blockrun/llm's btoa-based payload encoding throw "Invalid character" on any
non-Latin1 skill name; the description is now ASCII-stripped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extend the regression suite from the client-only checks to the two buyer
surfaces: agent_talent (list / list+query / empty / run / missing slug+input
/ failed-hire-no-charge / unknown action / concurrency safety) and the
/market command dispatch (browse / search / info / info-usage / run / run-usage
/ run-failure-no-charge), plus limit clamping and formatSkillCard. 22 market
tests, 470/470 in the full suite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rename the user-facing catalog heading from "Agent marketplace" to
"Agent talents" (consistent with the agent_talent tool + the talent panel),
in both the /market command and the tool's list output. Default browse shows
the top 12 by popularity; when more exist the heading now reads
"top 12 of <total>" instead of silently capping.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…search

Broaden the tool description so the model reliably routes a plain-language
"find / search / recommend an agent for <domain>" request to
agent_talent{action:"list", query}, not only the agent's own sub-task needs.
Verified live (Anthropic Haiku 4.5) among Franklin's real toolset: NL requests
in English and Chinese route to agent_talent list with a sensible query, the
model picks it over WebSearch for "find me an agent", and an unrelated question
triggers no marketplace call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
agent_talent run spends USDC from the wallet with no refund, so it now goes
through the permission prompt (behavior "ask") like the other paid,
irreversible tools (VoiceCall, BuyPhoneNumber) — previously it fell through to
the generic default that dumped raw args. The prompt now reads:

  Hire 'yield-radar' from the agent marketplace — pays from your wallet
  (USDC on Base), charged only on a successful run.

action="list" (browse/search) is a free read, so it auto-allows with no prompt.
In --trust mode both still auto-allow, matching the existing policy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lugs whole

The list rows hard-sliced the description to 40 chars, cutting mid-word
("Live stablecoin yields ranked across cha"). Truncate at the last word
boundary with an ellipsis instead ("...across…"), and stop slicing the slug —
it is the identifier the user types into `/market run <slug>`, so a cut slug
was unusable. Same bug class, fixed in both spots; short descriptions and short
slugs are unaffected and columns still align.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove the "N runs" stat from the /market list rows, the /market info card,
and the agent_talent list output. The catalog is still ordered by popularity
(the discovery API sorts by run_count server-side) — the count is just no
longer shown.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The catalog's ordering contract on every surface (the /market list, the
agent_talent tool, the future web panel) is most-called-first. The discovery
API already sorts by run_count desc, but the CLI takes a top-N slice, so make
the ordering explicit and self-contained: fetchCatalog stable-sorts by
run_count desc before slicing/filtering. Regression test locks it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant