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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant