Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
## x402

- Public client methods send grant and policy metadata to a configured gateway; the gateway owns signing and reconciliation.
- Paid MCP client calls are restricted to explicit tool-name unions and use the gateway's native
MCP adapter; never expose an arbitrary tool proxy or accept payer material.
- Bind exact HTTPS resource, method, network, asset, amount, recipient, purpose, category, grant, and idempotency.
- Preserve the distinction between read-only challenge inspection and funded execution.
- CDP Bazaar search/merchant helpers are public and keyless. Do not attach ACM bearer tokens, payer keys, or CDP seller credentials to discovery calls, and do not claim a seller is indexed until the catalog returns it after CDP settlement.
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Success means the report contains:
}
```

The check reads Coinbase's public x402 Bazaar catalog, confirms all seven canonical Omni routes, and validates the `0.003` Base Sepolia USDC quote. It creates no signature or payment.
The check reads Coinbase's public x402 Bazaar catalog, confirms all nine canonical Omni routes, and validates the `0.003` Base Sepolia USDC quote. It creates no signature or payment.

## Five-minute local demo

Expand Down Expand Up @@ -92,16 +92,18 @@ The acceptance flow buys one current BTC market-risk result, requires a fresh sc
| Goal | Command or guide | Status |
|---|---|---|
| Inspect live Bazaar routes without paying | `npx github:InTheta/agent-capability-middleware#main inspect` | Implemented |
| Build exact calls across all seven Omni x402 products | `npx github:InTheta/agent-capability-middleware#main recipes` | Implemented |
| Build exact calls across all nine Omni x402 products | `npx github:InTheta/agent-capability-middleware#main recipes` | Implemented |
| Buy through a protected, policy-bound payer | [Getting started](docs/getting-started.md) | Implemented on Base Sepolia |
| Buy a bounded Omni MCP tool through the protected payer | [`consumeX402McpTestnet`](docs/sdk-api.md#consume-a-paid-mcp-tool) | Implemented on Base Sepolia |
| Charge agents for a developer API | `acm demo developer-seller` | Experimental local offer helper |
| Offer a user-confirmed minimum-disclosure capability | `acm demo user-seller` | Experimental local offer helper |
| Compare both offer types | `acm demo exchange` | Experimental local directory |

The seven canonical Omni templates cover targeted and market-wide AI news, exact time windows,
The nine canonical Omni templates cover targeted and market-wide AI news, exact time windows,
all bounded liquidation views, all seven trader ranks, public trader-profile projections,
15/60-minute composite market risk, and candles with optional liquidation overlay. `acm recipes`
prints 23 concrete no-spend request plans over those templates; it does not claim 23 Bazaar
15/60-minute composite market risk, candles with optional liquidation overlay, current market
entity resolution, and current funding/carry. `acm recipes`
prints 25 concrete no-spend request plans over those templates; it does not claim 25 Bazaar
listings.

## Architecture
Expand All @@ -115,7 +117,7 @@ flowchart LR
G --> L["Receipt and audit"]
```

MCP can carry tool calls, OAuth/OIDC can identify workloads and users, verifiable credentials can carry attestations, and x402 carries payment requirements and proofs. ACM composes existing standards rather than replacing them.
MCP can carry tool calls, OAuth/OIDC can identify workloads and users, verifiable credentials can carry attestations, and x402 carries payment requirements and proofs. ACM composes existing standards rather than replacing them. The protected gateway now applies the same exact grant and payment binding to four bounded Omni MCP tools.

## Experimental seller previews

Expand Down
8 changes: 4 additions & 4 deletions dist/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ else if (command === "recipes") {
console.log(JSON.stringify({
ok: true,
action: "bounded_omni_agent_recipes",
canonicalRouteTemplates: 7,
catalogedRouteTemplates: 7,
canonicalRouteTemplates: 9,
catalogedRouteTemplates: 9,
recipes: listOmniAgentRecipes().map(({ label, kind, resourceUrl, schema, priceUsdc, purpose, note }) => ({
label, kind, resourceUrl, schema, priceUsdc, purpose, ...(note ? { note } : {}),
})),
spent: false,
privateKeyUsed: false,
note: "Recipes reuse seven funded Base Sepolia seller route templates, all verified in CDP Bazaar; query variants do not create extra listings.",
note: "Recipes reuse nine funded Base Sepolia seller route templates, all verified in CDP Bazaar; query variants do not create extra listings.",
}, null, 2));
}
else if (command === "demo") {
Expand Down Expand Up @@ -109,7 +109,7 @@ function printHelp() {
Usage:
acm doctor Check the local SDK runtime. No network request or payment.
acm inspect Inspect one live x402 resource through public CDP Bazaar. No payment.
acm recipes Print bounded calls across all seven real Omni x402 products. No payment.
acm recipes Print bounded calls across all nine real Omni x402 products. No payment.
acm demo buyer|developer-seller|user-seller|exchange
Run a local, keyless product flow. No payment.
acm partner-check
Expand Down
18 changes: 18 additions & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,23 @@ export interface PayQuotedX402TestnetRequest {
export interface PayQuotedX402Request extends PayQuotedX402TestnetRequest {
approvalId?: string;
}
export type OmniX402McpToolName = "get_market_moving_events" | "get_market_risk_context" | "resolve_market_entities" | "get_market_carry";
export interface PayQuotedX402McpTestnetRequest {
grantId: string;
serverUrl: string;
toolName: OmniX402McpToolName;
arguments: Record<string, unknown>;
category: string;
purpose: string;
idempotencyKey: string;
currency?: string;
expectedPayment?: {
amount: number;
network: string;
asset: string;
payTo: string;
};
}
export interface X402ConsumptionResult<T = unknown> {
decision: "paid" | "allow" | "deny" | "requires_approval" | "needs_user_approval";
reason?: string;
Expand Down Expand Up @@ -392,6 +409,7 @@ export declare class AgentCapabilityClient {
requestSpend(request: SpendRequest): Promise<Record<string, unknown>>;
payQuotedX402Testnet(request: PayQuotedX402TestnetRequest): Promise<X402ConsumptionResult>;
consumeX402Testnet<T = unknown>(request: PayQuotedX402TestnetRequest): Promise<X402ConsumptionResult<T>>;
consumeX402McpTestnet<T = unknown>(request: PayQuotedX402McpTestnetRequest): Promise<X402ConsumptionResult<T>>;
payQuotedX402<T = unknown>(request: PayQuotedX402Request): Promise<X402ConsumptionResult<T>>;
consumeX402<T = unknown>(request: PayQuotedX402Request): Promise<X402ConsumptionResult<T>>;
approveApproval(approvalId: string): Promise<Record<string, unknown>>;
Expand Down
2 changes: 1 addition & 1 deletion dist/index.d.ts.map

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ export class AgentCapabilityClient {
consumeX402Testnet(request) {
return this.post("/v1/pay/x402/testnet/quoted", request);
}
consumeX402McpTestnet(request) {
return this.post("/v1/pay/x402/mcp/testnet/quoted", request);
}
payQuotedX402(request) {
return this.post("/v1/pay/x402/quoted", request);
}
Expand Down
61 changes: 59 additions & 2 deletions dist/omni.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,54 @@ export interface OmniMarketRiskResponse {
liquidations: OmniLiquidationMapResponse;
news: OmniNewsPulseResponse;
}
export type OmniX402Response = OmniNewsPulseResponse | OmniTraderLeaderboardResponse | OmniLiquidationMapResponse | OmniTraderProfileResponse | OmniMarketRiskResponse | OmniMarketSnapshotResponse;
export interface OmniEntityResolutionResponse {
service: "omni.market_entity_resolution";
product_version: "v1";
schema: "market_entity_resolution.v1";
generated_at: string;
data_as_of: string | null;
freshness: OmniFreshness;
venue: "hyperliquid";
results: Array<{
input: string;
resolution_status: "resolved" | "unsupported";
canonical_symbol: string | null;
confidence: number;
product_supported: boolean;
[key: string]: unknown;
}>;
usage: {
mention_count: number;
resolved_count: number;
mention_limit: 20;
};
}
export interface OmniMarketCarryResponse {
service: "omni.hyperliquid_market_carry";
product_version: "v1";
schema: "hyperliquid_market_carry.v1";
symbol: string;
canonical_symbol: string;
venue: "hyperliquid";
market_type: "perp";
generated_at: string;
data_as_of: string | null;
freshness: OmniFreshness;
carry: {
funding_rate_per_hour: number;
funding_annualized_pct: number;
annualization_method: "simple_current_hourly_rate_x_8760";
funding_direction: "longs_pay_shorts" | "shorts_pay_longs" | "balanced";
premium: number | null;
};
positioning: Record<string, number | null>;
prices: Record<string, number | null>;
usage: {
current_snapshot_only: true;
historical_series_included: false;
};
}
export type OmniX402Response = OmniNewsPulseResponse | OmniTraderLeaderboardResponse | OmniLiquidationMapResponse | OmniTraderProfileResponse | OmniMarketRiskResponse | OmniMarketSnapshotResponse | OmniEntityResolutionResponse | OmniMarketCarryResponse;
type NewsFilters = {
limit?: number;
sentiment?: OmniNewsSentiment;
Expand Down Expand Up @@ -202,6 +249,13 @@ export type OmniRecipeInput = ({
limit?: number;
scope?: OmniAnalyticsScope;
includeLiquidations?: boolean;
} | {
kind: "entity_resolution";
mentions: string[];
venue?: "hyperliquid";
} | {
kind: "market_carry";
symbol: string;
};
export interface OmniX402Recipe {
kind: OmniRecipeInput["kind"];
Expand All @@ -211,6 +265,9 @@ export interface OmniX402Recipe {
priceUsdc: 0.001 | 0.002 | 0.003;
category: "market_intelligence";
purpose: string;
method?: "GET" | "POST";
headers?: Record<string, string>;
body?: string;
expectedPayment: {
amount: 0.001 | 0.002 | 0.003;
network: typeof OMNI_BASE_SEPOLIA_NETWORK;
Expand All @@ -220,7 +277,7 @@ export interface OmniX402Recipe {
note?: string;
}
/**
* Build one deterministic, bounded Omni x402 request. These are recipes over seven seller route
* Build one deterministic, bounded Omni x402 request. These are recipes over nine seller route
* templates—not additional per-query routes or a generic query proxy. Bazaar catalog status is
* verified separately because a new route requires a successful CDP settlement before indexing.
*/
Expand Down
2 changes: 1 addition & 1 deletion dist/omni.d.ts.map

Large diffs are not rendered by default.

68 changes: 67 additions & 1 deletion dist/omni.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export const OMNI_X402_RECEIVER = "0x733f40A4FA0cd13d59aBADE04b9eD2e9acAc6457";
export const OMNI_BASE_SEPOLIA_NETWORK = "eip155:84532";
export const OMNI_BASE_SEPOLIA_USDC = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
/**
* Build one deterministic, bounded Omni x402 request. These are recipes over seven seller route
* Build one deterministic, bounded Omni x402 request. These are recipes over nine seller route
* templates—not additional per-query routes or a generic query proxy. Bazaar catalog status is
* verified separately because a new route requires a successful CDP settlement before indexing.
*/
Expand Down Expand Up @@ -145,6 +145,47 @@ export function createOmniX402Recipe(input) {
purpose = `evaluate_${symbol.toLowerCase()}_price_and_liquidation_structure`;
break;
}
case "entity_resolution": {
const mentions = normalizedMentions(input.mentions);
const venue = input.venue ?? "hyperliquid";
url.pathname += "symbols/resolve";
label = "Current market entity resolution";
schema = "market_entity_resolution.v1";
priceUsdc = 0.001;
purpose = "resolve_current_market_entities";
note =
"Current-only resolution for 1-20 mentions; it does not expose the full alias registry.";
return {
kind: input.kind,
label,
resourceUrl: url.toString(),
schema,
priceUsdc,
category: "market_intelligence",
purpose,
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ mentions, venue }),
expectedPayment: {
amount: priceUsdc,
network: OMNI_BASE_SEPOLIA_NETWORK,
asset: OMNI_BASE_SEPOLIA_USDC,
payTo: OMNI_X402_RECEIVER,
},
note,
};
}
case "market_carry": {
const symbol = normalizedSymbol(input.symbol);
url.pathname += `market-carry/${symbol}`;
label = `${symbol} current market carry`;
schema = "hyperliquid_market_carry.v1";
priceUsdc = 0.001;
purpose = `evaluate_current_${symbol.toLowerCase()}_market_carry`;
note =
"Current funding annualization is mechanical and is not a forecast or historical series.";
break;
}
}
return {
kind: input.kind,
Expand Down Expand Up @@ -210,6 +251,9 @@ export function createOmniPaymentRequest(grantId, recipe, idempotencyKey) {
purpose: recipe.purpose,
idempotencyKey,
expectedPayment: recipe.expectedPayment,
...(recipe.method ? { method: recipe.method } : {}),
...(recipe.headers ? { headers: recipe.headers } : {}),
...(recipe.body ? { body: recipe.body } : {}),
};
}
export function listOmniAgentRecipes(now = Date.now()) {
Expand Down Expand Up @@ -237,6 +281,11 @@ export function listOmniAgentRecipes(now = Date.now()) {
createOmniX402Recipe({ kind: "market_risk", symbol: "BTC", eventWindowMinutes: 60, limit: 5 }),
createOmniX402Recipe({ kind: "market_snapshot", symbol: "BTC", interval: "1h", limit: 120 }),
createOmniX402Recipe({ kind: "market_snapshot", symbol: "BTC", interval: "15m", limit: 96, includeLiquidations: false }),
createOmniX402Recipe({
kind: "entity_resolution",
mentions: ["bitcoin", "BTC-PERP"],
}),
createOmniX402Recipe({ kind: "market_carry", symbol: "BTC" }),
];
}
function addNewsQuery(url, input, mode, eventWindowMinutes) {
Expand Down Expand Up @@ -280,6 +329,23 @@ function normalizedAddress(value) {
throw new TypeError("address must be a 20-byte EVM address");
return address;
}
function normalizedMentions(values) {
if (!Array.isArray(values) || values.length < 1 || values.length > 20) {
throw new RangeError("mentions must contain from 1 to 20 items");
}
return values.map((value) => {
if (typeof value !== "string") {
throw new TypeError("each mention must be a string");
}
const mention = value.trim();
if (mention.length < 1 ||
mention.length > 100 ||
/[\u0000-\u001f\u007f]/.test(mention)) {
throw new RangeError("each mention must contain from 1 to 100 printable characters");
}
return mention;
});
}
function boundedInteger(value, minimum, maximum, name) {
if (!Number.isInteger(value) || value < minimum || value > maximum) {
throw new RangeError(`${name} must be an integer from ${minimum} to ${maximum}`);
Expand Down
2 changes: 1 addition & 1 deletion dist/partner-check.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export declare const CANONICAL_OMNI_RECEIVER = "0x733f40A4FA0cd13d59aBADE04b9eD2
export declare const CANONICAL_OMNI_MARKET_RISK_ROUTE = "https://omniterminal.app/api/x402/v1/market-risk/:symbol";
export declare const CANONICAL_OMNI_MARKET_RISK_RESOURCE = "https://omniterminal.app/api/x402/v1/market-risk/BTC?scope=current";
export declare const BASE_SEPOLIA_USDC = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
export declare const CANONICAL_OMNI_ROUTES: readonly ["https://omniterminal.app/api/x402/v1/news/:symbol", "https://omniterminal.app/api/x402/v1/news", "https://omniterminal.app/api/x402/v1/trader-profile/:address", "https://omniterminal.app/api/x402/v1/liquidations/:symbol", "https://omniterminal.app/api/x402/v1/traders/:symbol", "https://omniterminal.app/api/x402/v1/market-risk/:symbol", "https://omniterminal.app/api/x402/v1/market-snapshot/:symbol"];
export declare const CANONICAL_OMNI_ROUTES: readonly ["https://omniterminal.app/api/x402/v1/news/:symbol", "https://omniterminal.app/api/x402/v1/news", "https://omniterminal.app/api/x402/v1/trader-profile/:address", "https://omniterminal.app/api/x402/v1/liquidations/:symbol", "https://omniterminal.app/api/x402/v1/traders/:symbol", "https://omniterminal.app/api/x402/v1/market-risk/:symbol", "https://omniterminal.app/api/x402/v1/market-snapshot/:symbol", "https://omniterminal.app/api/x402/v1/symbols/resolve", "https://omniterminal.app/api/x402/v1/market-carry/:symbol"];
export interface DesignPartnerCheckOptions {
gatewayUrl?: string;
apiKey?: string;
Expand Down
2 changes: 1 addition & 1 deletion dist/partner-check.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading