Skip to content
Open
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
5 changes: 5 additions & 0 deletions typescript/.changeset/vdm-nexus-action-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@coinbase/agentkit": patch
---

Added vdm-nexus action provider — signed inference via x402 on Solana with verifiable receipts.
3 changes: 2 additions & 1 deletion typescript/agentkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
"sushi": "6.2.1",
"twitter-api-v2": "^1.18.2",
"viem": "2.47.4",
"zod": "^4.3.6"
"zod": "^4.3.6",
"@vdm-nexus/x402": "^0.4.0"
},
"devDependencies": {
"@types/jest": "^29.5.14",
Expand Down
1 change: 1 addition & 0 deletions typescript/agentkit/src/action-providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export * from "./superfluid";
export * from "./sushi";
export * from "./truemarkets";
export * from "./twitter";
export * from "./vdm-nexus";
export * from "./wallet";
export * from "./weth";
export * from "./wow";
Expand Down
101 changes: 101 additions & 0 deletions typescript/agentkit/src/action-providers/vdm-nexus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# VDM Nexus Action Provider

This directory contains the **VdmNexusActionProvider** implementation, which exposes [VDM Nexus](https://vdmnexus.com) — a signed-inference rail — as a tool surface inside any AgentKit-built agent.

Every paid `nexus_chat` call settles a USDC payment inline via [x402](https://github.com/coinbase/x402) and returns the OpenAI chat completion alongside a Signed Inference Receipt ([SIR v2](https://docs.vdmnexus.com/docs/spec/sir-v2)) anchored to the on-chain settlement transaction. Receipts are independently verifiable — a third party can confirm what the model returned without trusting the caller or the service operator.

First mainnet receipt: <https://vdmnexus.com/r/c9710ea7-9e1f-46ee-aaa9-903a536ae12e>

## Directory Structure

```
vdm-nexus/
├── vdmNexusActionProvider.ts # Main provider with the three actions
├── vdmNexusActionProvider.test.ts # Jest tests
├── schemas.ts # Zod input schemas
├── index.ts # Public exports
└── README.md # This file
```

## Usage

```typescript
import { AgentKit, SolanaKeypairWalletProvider } from "@coinbase/agentkit";
import { vdmNexusActionProvider } from "@coinbase/agentkit";

const walletProvider = await SolanaKeypairWalletProvider.fromNetwork(
"solana-mainnet",
process.env.SOLANA_PRIVATE_KEY!,
);

const agentKit = await AgentKit.from({
walletProvider,
actionProviders: [vdmNexusActionProvider()],
});
```

The wallet attached to AgentKit IS the agent identity — its keypair signs the SPL USDC transfer carried in the x402 `X-Payment` header. No separate agent secret env var is required.

## Configuration

```typescript
vdmNexusActionProvider({
// Point at a different deployment (e.g. self-hosted)
endpoint: "https://nexus.example.com/api/v1",

// Pin the operator pubkey for offline verification. When omitted, the
// verifier fetches it from `${endpoint}/operator-key` on first use.
operatorKey: "<base58 Ed25519 pubkey>",
});
```

## Actions

### `nexus_chat`

Pay-per-call signed inference. Performs the x402 two-roundtrip handshake (probe → sign → paid), runs the inference upstream, and returns the OpenAI chat completion plus the SIR v2 receipt and the settlement record.

**Inputs:** `model`, `messages`, optional `network` override.

**Returns:** `{ ok, openai, receipt, payment }` on success; `{ ok: false, error, ... }` on failure.

### `nexus_verify_receipt`

Run the five-check SIR v2 verifier against a receipt, prompt, and response. Confirms:

1. `prompt_hash_ok` — sha256(prompt) matches `receipt.prompt_hash`
2. `response_hash_ok` — sha256(response) matches `receipt.response_hash`
3. `nexus_signature_ok` — operator Ed25519 signature is valid
4. `payment_on_chain_ok` — settlement tx landed at the recipient
5. `payer_matches` — tx payer equals `receipt.agent_pubkey`

**Returns:** `{ ok, checks }` with the full breakdown so the agent can act on partial failures.

### `nexus_get_deposit_address`

Fetch the on-chain USDC deposit address for prepaid credit top-ups. Per-call x402 settlement via `nexus_chat` does **not** require this — it settles inline. Use this only when batching deposits is cheaper than per-call settlement (high-volume agents).

**Returns:** `{ ok, address, mint, network }`.

## Network Support

| Network | CAIP-2 | Status |
|---|---|---|
| Solana mainnet | `solana:mainnet` | Live |
| Solana devnet | `solana:devnet` | Live |

`supportsNetwork` returns true for any SVM (`network.protocolFamily === "svm"`) network. An EVM-bound sibling provider for Base mainnet/Sepolia settlement is on the roadmap.

## Why signed inference?

- **Audit trails.** Every inference call produces a tamper-evident receipt the caller can verify cryptographically. Useful for compliance regimes that require evidence of what a model returned (EU AI Act Article 12, NIST AI agent standards, OWASP LLM Top 10 logging).
- **Trustless multi-agent flows.** Agent A can hand Agent B a receipt; Agent B can verify it without trusting Agent A — the operator's Ed25519 signature plus the on-chain settlement record are the trust anchor.
- **Pay-per-call autonomy.** No prepaid balance to babysit. The wallet pays inline.

## Specification

The Signed Inference Receipt v2 wire format and verification rules are documented at <https://docs.vdmnexus.com/docs/spec/sir-v2>.

## Relationship to the existing `x402` action provider

The upstream [`x402` action provider](../x402/README.md) is a general-purpose client for calling any x402-protected API. `vdm-nexus` is a higher-level adapter targeting one specific x402 rail (Nexus) and adding the verifiable-receipt semantics on top — the wire-level handshake is the same, but the receipt verification, model selection, and OpenAI response shape are specific to the Nexus deployment. Use `x402` when you want to call arbitrary paid APIs; use `vdm-nexus` when you specifically want paid LLM inference with cryptographic receipts.
2 changes: 2 additions & 0 deletions typescript/agentkit/src/action-providers/vdm-nexus/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./vdmNexusActionProvider";
export { NexusChatSchema, NexusVerifyReceiptSchema, NexusGetDepositAddressSchema } from "./schemas";
104 changes: 104 additions & 0 deletions typescript/agentkit/src/action-providers/vdm-nexus/schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { z } from "zod";

/**
* OpenAI-shape chat message.
*/
const ChatMessageSchema = z.object({
role: z.enum(["system", "user", "assistant"]),
content: z.string(),
});

/**
* Input schema for the `nexus_chat` action.
*
* Mirrors the OpenAI chat completions request shape. The optional `network`
* field selects the settlement chain — must match a chain the configured
* wallet can sign for. For an `SvmWalletProvider`-bound provider, valid
* values are `solana:mainnet` and `solana:devnet`.
*/
export const NexusChatSchema = z
.object({
model: z
.string()
.describe(
'OpenRouter-style model slug, e.g. "openai/gpt-4o-mini", "anthropic/claude-3-haiku".',
),
messages: z.array(ChatMessageSchema).min(1).describe("OpenAI-shape chat messages array."),
network: z
.string()
.optional()
.describe(
'Optional CAIP-2 settlement network override. Must match the wallet\'s chain — e.g. "solana:mainnet" or "solana:devnet". Omit to use the server default.',
),
})
.describe(
"Pay-per-call signed inference against a VDM Nexus endpoint. " +
"Returns the OpenAI chat completion plus a Signed Inference Receipt (SIR v2) " +
"anchored to an on-chain USDC settlement transaction.",
);

/**
* Input schema for the `nexus_verify_receipt` action.
*
* Takes the original receipt + prompt + response and runs the five-check
* SIR v2 verification: hash recompute (prompt + response), operator Ed25519
* signature, on-chain settlement tx, payer identity.
*/
export const NexusVerifyReceiptSchema = z
.object({
receipt: z
.unknown()
.describe(
"The Signed Inference Receipt (SIR v2) to verify, as returned by `nexus_chat` in the `receipt` field.",
),
prompt: z
.union([z.string(), z.array(ChatMessageSchema)])
.describe(
"The prompt the receipt covers. For x402 chat-completion receipts, pass the `messages` array you sent. For prepaid `/inference` receipts, pass the raw prompt string.",
),
response: z
.union([z.string(), z.unknown()])
.describe(
"The response the receipt covers. For x402 chat-completion receipts, pass the OpenAI response body. For prepaid receipts, pass the raw response string.",
),
endpoint: z
.string()
.optional()
.describe(
"Optional Nexus base URL (e.g. https://nexus.vdmnexus.com). Used to fetch the operator public key when `operatorKey` is not supplied.",
),
operatorKey: z
.string()
.optional()
.describe(
"Optional base58 Ed25519 operator public key. If omitted, fetched from the endpoint's `/api/v1/operator-key`.",
),
rpc: z
.string()
.optional()
.describe(
"Optional RPC URL override for the on-chain payment check. Defaults are derived from `receipt.payment.network`.",
),
})
.describe(
"Run the five-check SIR v2 verification: prompt_hash_ok, response_hash_ok, " +
"nexus_signature_ok, payment_on_chain_ok, payer_matches. Returns the full " +
"check breakdown so the agent can act on partial failures.",
);

/**
* Input schema for the `nexus_get_deposit_address` action.
*/
export const NexusGetDepositAddressSchema = z
.object({
network: z
.string()
.optional()
.describe(
'Optional CAIP-2 network override, e.g. "solana:mainnet". Omit to use the endpoint default.',
),
})
.describe(
"Returns the USDC deposit address agents should send funds to in order to top up their prepaid credit balance on a Nexus deployment. " +
"Per-call x402 settlement via `nexus_chat` does NOT require this — it settles inline.",
);
Loading
Loading