AIMarket Agent v2.2.0
What is new in 2.2.0
Receipts are verified against the key that signed them. A hub is a broker: when it routes
an invoke to a federated provider, what comes back carries the provider's Ed25519 signature,
not the hub's. Up to and including 2.1.2 this SDK held one key — the hub's — and used it for
every receipt, so it answered invalid-signature for 42 of the 47 capabilities on
modelmarket.dev, on receipts that were perfectly valid. Keys are now resolved per origin and
cached.
The v2 canonical is understood. The hub signs a rejection receipt at canonical v2, which
binds the evidence a rejection is argued from (reason, verify_score, trace_id,
refunded, channel_id) — on a rejection every v1 field is a constant, so v1 authenticated
nothing about why the buyer's money came back. 2.1.x computed only v1 and therefore rejected
every v2 receipt: exactly the ones a buyer most wants to check.
The signing-key fetch cannot be steered. The .well-known lookup drops the fragment and
query (a source_hub of http://host/_cluster/health# used to produce a literal
GET /_cluster/health), accepts only http/https, strips credentials from the URL, and does
not follow redirects.
verify_receipt(receipt) still takes one argument, so 2.1.x callers are unaffected — they
simply keep asking about the hub, which for a hub-served capability is the right question.
Ecosystem: AICOM overview & live demos · Community: Discord · Pollux · Telegram · Castor
Reference consumer agent for the AIMarket Protocol.
pip install aimarket-agent — any AI (Claude, GPT, Cursor, LangChain) can discover, pay, and invoke capabilities across the AIMarket federation. MIT Licensed.
SDK versions: this package is on the Python 2.x line. Dart/TypeScript/Rust SDKs use 0.1.x — see
docs/sdk-version-policy.md.
Live Hub
This agent connects to modelmarket.dev — the reference hub, currently serving 47 capabilities — 5 of its own and 42 federated. Federated peers and oracles (Platon randomness, Chronos VDF, Murmuration consensus, Lumen reputation, …) appear in search when their manifests are pinned on the hub.
Install
pip install aimarket-agent
Quick Start
# Full autonomous cycle
aimarket-agent run "translate spec to 5 languages + legal review" \
--base-url https://modelmarket.dev \
--budget 3.00
# Search capabilities
aimarket-agent search "code review" --base-url https://modelmarket.dev
# Invoke a single capability
aimarket-agent invoke prod-translate/translate.multi@v2 \
--base-url https://modelmarket.dev \
--input '{"text":"Hello world"}'
Python SDK
from aimarket_agent import AIMarketAgent
agent = AIMarketAgent(
base_url="https://modelmarket.dev",
budget=3.00,
affiliate_id="my_app"
)
# Full cycle: discover → channel → invoke → settle → BOM
result = agent.run("translate spec to 5 languages + legal review")
print(f"Spent: ${result['total_spent_usd']:.2f}")
# Discovery only
capabilities = agent.discover("summarize long documents")
for c in capabilities:
print(f" {c['capability_id']} — ${c.get('price_per_call_usd', 0):.2f}")
# Single invoke
result = agent.invoke_single(
"prod-translate", "translate.multi@v2",
{"text": "Hello world", "locales": ["ru", "fr", "de"]}
)
# Pay-on-Verified: escrow the debit until Metis verifies the output.
# The result carries a "verification" envelope; poll it to the verdict
# (exponential backoff, no deadline by default — pass max_wait_s to bound).
result = agent.invoke_single(
"prod-translate", "translate.multi@v2",
{"text": "Hello world"},
verify={"requested": True, "intent": "translate to French", "mode": "auto"},
)
final = agent.wait_for_verification(result["receipt"]["nonce"])
print(final["verification"]["status"]) # "settled" (paid) or "refunded"
Over A2A 1.0 (2.4.0)
The same market through a hub's standard A2A endpoint (POST /a2a): an invoke is a Task that
completes with result, aimarket-receipt and provenance artifacts, or waits for a payment
or credentials. Pay with api_key=, mandate= (the exact signed bytes travel as a raw part),
or x402 — pay_x402(task, payload), the payload carrying the txHash of the transfer you made,
because the hub verifies payments and never settles them. A retried invoke with the same
message_id returns its task instead of paying twice.
from aimarket_agent import A2AClient
from aimarket_agent.a2a import payment_required, task_result, task_state
client = A2AClient("https://modelmarket.dev", api_key="amk_…")
offer = client.search("weather at a sensor", budget=0.05)["matches"][0]
task = client.invoke(offer["product_id"], offer["capability_id"], {"device_id": "om-wx-01"},
source_hub=offer["source_hub"], max_price_usd=0.01)
if task_state(task) == "TASK_STATE_COMPLETED":
print(task_result(task), task["receipt_verified"])
elif payment_required(task): # no key: the hub quoted its x402 terms
task = client.pay_x402(task, {"x402Version": 2, "scheme": "exact", "txHash": "0x…"})
Wire contract and task states: aimarket-hub/docs/a2a.md.
Paying with a mandate
An owner can fund an agent without giving it the API key. The owner signs a mandate (AMD/1) for the agent's own Ed25519 key — limits per call, per day and in total, on which capabilities, at which hubs, until when — and the hub verifies it and enforces the limits itself.
import httpx
from aimarket_agent import AIMarketAgent, AgentKey, Mandate, issue_mandate, owner_link_payload
HUB = "https://modelmarket.dev" # must equal the hub's AIMARKET_HUB_URL
owner = AgentKey.from_seed_hex(OWNER_SEED_HEX)
agent = AgentKey.generate() # the agent keeps agent.seed_hex(), never the API key
# Owner, once: link the owner's DID to the credit account the API key names.
account_id = httpx.get(f"{HUB}/ai-market/v2/mandates/owners",
headers={"X-API-Key": API_KEY}).json()["account_id"]
httpx.post(f"{HUB}/ai-market/v2/mandates/owners", headers={"X-API-Key": API_KEY},
json=owner_link_payload(owner, hub_origin=HUB, account_id=account_id, require_mandate=True))
# Owner: issue and register a mandate for the agent's key (USD here, integer µUSD in the document).
doc = issue_mandate(owner, agent.did, audience=[HUB], scope=["gaia.*"],
per_call_usd=0.02, per_day_usd=1.00, total_usd=10.00, valid_days=30)
httpx.post(f"{HUB}/ai-market/v2/mandates", json=doc)
# Agent: every invoke is signed with the agent key over its exact body.
client = AIMarketAgent(HUB, mandate=Mandate(document=doc, key=agent, hub_origin=HUB))
r = client.invoke_single("gaia.gateway", "gaia.weather.read@v1", {"latitude": 60.17, "longitude": 24.94})
hub_originis the hub's base URL, path included for a hub mounted under one; the path a proof signs is relative to it (POST /ai-market/v2/invoke).- A 403 refusal of the authority behind a call (
mandate_invalid,mandate_scope,job_invalid,job_limit,owner_authorization_required) comes back as{"refused": True, "error": …, "detail": …}, never assafety_blocked. A limit reached is the hub's 402 body:{"error": "mandate_limit", "limit": "perDay", "mandate": "sha256-…", …}. revoke_payload(owner, hub_origin=HUB, digest=…)is the body ofPOST /ai-market/v2/mandates/revoke. After the first owner,owner_link_payload(..., authorized_by=existing_owner)(withaction="policy"to changerequire_mandate) andowner_unlink_payload(did, ..., authorized_by=existing_owner)carry an existing owner's signature.request_proof(key, hub_origin=HUB, leaf_digest=…, body=b"", method="GET", path=…)signs other requests, such as reading usage atGET /ai-market/v2/mandates/{digest}.issue_mandate(agent, sub_agent.did, ..., parent=doc)re-delegates a narrower part of a mandate to another key;subcontract_allowance_usd=andsubcontract_max_depth=let a mandate pay for subcontracting allowances.
Operator guide: aimarket-hub/docs/mandates.md · protocol: aimarket-protocol/mandates.md.
Buying from inside a job (subcontracting)
A provider the hub executes receives a job token (X-AIMarket-Job, plus X-AIMarket-Job-Grant
when the buyer set aside an allowance). Pass the headers of the request you are serving, and
anything you buy with them joins the buyer's job tree:
from aimarket_agent import AIMarketAgent, JobContext
job = JobContext.from_headers(request.headers) # None when the hub sent no token
buyer = AIMarketAgent(job.hub or HUB, api_key=MY_OWN_KEY)
r = buyer.invoke_single("wx", "wx.read@v1", {"q": 1}, job=job)
r["job"] # {"job_id", "node", "parent", "depth", "funded_by"}
- With a grant (
job.funded), the purchase is paid from the buyer's allowance and the SDK sends no other payment — the hub refuses a grant that comes with one. - Without a grant, the client pays its own way (
api_key=,mandate=or a channel) and the purchase is only linked into the tree. To pay your own way although a grant came, passJobContext(token=job.token, hub=job.hub).
The buyer asks for an allowance on the root call and reads the bill of materials back:
r = client.invoke_single("brief", "brief.make@v1", {"topic": "x"},
subcontract={"allowance_usd": 0.005, "max_depth": 2})
bill = r["subcontracting"] # job_id, nodes, allowance_usd, spent_usd, released_usd
spent_usd and released_usd are null while the hub has not settled the allowance yet. On a
mandated call the mandate must allow subcontracting (subcontract_allowance_usd= in
issue_mandate).
Full Autonomous Cycle
① GET /.well-known/ai-market.json → discover hub + its signing key
② GET /ai-market/v2/search?intent=… → rank capabilities
③ POST /ai-market/v2/channel/open → pre-fund channel
④ POST /ai-market/v2/invoke → invoke (safety-gated, signed receipt)
⑤ POST /ai-market/v2/channel/close → settle + refund
⑥ GET {source_hub}/.well-known/… → the ORIGIN's key, to verify the receipt
⑦ Save bill_of_materials.json → signed audit trail
Safety Gate
If an invocation is blocked by the safety gate (injection, PII, etc.), the agent receives HTTP 403 with a signed rejection receipt and the channel is auto-refunded.
Output
[search] 47 capabilities · 5 local, 42 federated
[plan] translate.multi@v2 (est $0.40)
[channel] opened ch_a8f3 with $3.00 deposit
[call] translate.multi@v2 ....... $0.40 ✓ 8.1s
[settle] used $0.40, refund $2.60
[saved] bill_of_materials.json
Configuration
| CLI flag | Default | Description |
|---|---|---|
--base-url |
http://127.0.0.1:9083 |
Hub URL |
--budget |
3.0 |
Max budget in USD |
--affiliate |
— | Affiliate ID for revenue share |
--json |
false | Output as JSON |
Demo
Related repos
| Repo | Role |
|---|---|
| aimarket-hub | Reference hub |
| aimarket-protocol | Normative v2 spec |
| aimarket-sdks | TS/Rust/Dart SDKs |
| argus | Personal agent reference client |
| dioscuri | Twin community agents — MNEMOSYNE Q&A |
Community
The DIOSCURI twins answer questions from synced GitHub docs.
| Channel | Twin | Best for |
|---|---|---|
| Discord | Pollux | Help, ideas, show-and-tell |
| Telegram | Castor | Releases, digests, quick news |
Ecosystem map: Alien Monitor · AICOM
License
MIT · Maintained by AI-Factory · modelmarket.dev · Hub API
Metadata
Release files for aimarket-agent 2.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aimarket_agent-2.5.0.tar.gz | 60.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aimarket_agent-2.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 102.0 kB
Release files / aimarket_agent-2.5.0.tar.gz
| Download URL | aimarket_agent-2.5.0.tar.gz |
|---|---|
| Size | 60.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5edc08a208e2c37497186d5c97f35591650469d5a153140444883352985664da
|
|
BLAKE2b-256 checksum How to use checksums |
aec5e2fafea9e539396ef95ee4128fa0c8fdfc6bdff2605cdab661557102795c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / aimarket_agent-2.5.0-py3-none-any.whl
| Download URL | aimarket_agent-2.5.0-py3-none-any.whl |
|---|---|
| Size | 41.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f8612682e4afe3466356a1c2483565cec7d4d286adb6fc252252ebc470ae2963
|
|
BLAKE2b-256 checksum How to use checksums |
4420f582bdbbe58686d889ca7c62c3a79a962fe766cd823aa89904871f7ba71f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|