Skip to content

Commit abac81d

Browse files
trilltinoclaude
andcommitted
refactor: settle on x402-only + deterministic decisions, close CoralOS threads on shutdown
Drops the escrow-gated/LLM-tool-loop settlement path in favor of direct x402 payment and deterministic bid/award/verify logic (agent/{capability,loop,safety,tools}.ts, llm/*, bid-review.ts, bid-tools.ts, award-tools.ts, verify-tools.ts, and the per-agent escrow/arbiter clients are removed; escrow/arbiter Anchor programs remain deployed as an opt-in building block, not the default flow). Refreshes docs and code comments across the repo to match: README/CORAL.md/PAY.md/ API.md, the solana-agent-commerce skill references, package.json's description, and stale in-code comments that still described escrow-gated release or LLM-shaped output. Deletes LLM.md (described a fully-removed provider-config subsystem). Adds CoralMcpAgent.closeThread(), wired into startCoralAgent's shutdown handler so a buyer-agent's market thread is marked closed on SIGINT/SIGTERM instead of just going quiet - verified live (SIGTERM -> closeThread -> disconnect -> clean exit, no hang). Verified end-to-end against live TxLINE World Cup data: a full CoralOS round (WANT -> BID -> AWARD -> PAYMENT_PROOF -> PAYMENT_CONFIRMED -> DELIVERED -> VERIFIED -> SETTLED) settling a real devnet x402 transfer, rendered correctly in the web UI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 2fc77f9 commit abac81d

109 files changed

Lines changed: 1152 additions & 6162 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,22 @@
22

33
# ── Wallets (node scripts/setup.js generates these) ───────────────────────────
44

5-
# Buyer keypair (base58 secret) — signs the escrow deposit/release and the TxLINE subscribe.
5+
# Buyer keypair (base58 secret) — signs x402 payments and the TxLINE subscribe.
66
# Fund it at https://faucet.solana.com (GitHub sign-in; CLI/RPC airdrops are gated).
77
BUYER_KEYPAIR_B58=
88

99
# Seller keypair (base58 secret) — a distinct party so settlement is a real two-party transfer.
10-
# It only RECEIVES on release, so it needs no funding. (setup.js generates this.)
10+
# It only RECEIVES payment, so it needs no funding, UNLESS PROCURE_RAIL=x402 is set (then it also
11+
# spends on the seller's own upstream procurement leg — see PAY.md). (setup.js generates this.)
1112
SELLER_KEYPAIR_B58=
1213

13-
# Seller public key — the escrow's payout destination. setup.js sets this to SELLER_KEYPAIR_B58's
14-
# pubkey. If unset, settle() self-pays the buyer (one-wallet demo; not a real two-party transfer).
14+
# Seller public key — the x402 payment's payout destination. setup.js sets this to
15+
# SELLER_KEYPAIR_B58's pubkey. If unset, settle() self-pays the buyer (one-wallet demo; not a real
16+
# two-party transfer).
1517
WALLET=
1618

17-
# Arbiter keypair (base58 secret) — the neutral 3rd signer that gates release/refund (the trustless
18-
# wrapper, examples/txodds/escrow/programs/arbiter). When set, settle() routes through the arbiter so
19-
# the buyer can't take delivery and refund. Needs only tx-fee funds — the proxy tops it up from the
20-
# buyer automatically. If unset, settle() falls back to the direct buyer-released escrow. (setup.js generates this.)
21-
ARBITER_KEYPAIR_B58=
22-
2319
SOLANA_RPC_URL=https://api.devnet.solana.com
2420

25-
# ── LLM brain (the agent's reasoning + delivery) ──────────────────────────────
26-
27-
# Recommended: Groq — OpenAI-compatible, genuinely free (a renewing per-day/per-minute rate limit,
28-
# not a one-time credit grant that runs out). Create a key at https://console.groq.com/keys, no card
29-
# needed. Set both of these:
30-
# LLM_PROVIDER=groq
31-
GROQ_API_KEY=
32-
33-
# Alternative: Venice AI — OpenAI-compatible, free SIGNUP CREDITS (a one-time pool that can run out,
34-
# unlike Groq's renewing limit). Create a key at https://venice.ai/settings/api; new accounts get $50
35-
# free with redeem code IMPERIAL50 (bottom of that page).
36-
# LLM_PROVIDER=venice
37-
VENICE_API_KEY=
38-
39-
# Other alternatives (no code change) — set LLM_PROVIDER + the matching key:
40-
# LLM_PROVIDER=openai → OPENAI_API_KEY=...
41-
# LLM_PROVIDER=anthropic → ANTHROPIC_API_KEY=...
42-
OPENAI_API_KEY=
43-
ANTHROPIC_API_KEY=
44-
45-
LLM_PROVIDER=
46-
LLM_MODEL=
47-
4821
# ── TxLINE (optional) ─────────────────────────────────────────────────────────
4922
# The proxy subscribes the buyer wallet to the free World Cup tier on its own, so you don't need to
5023
# set these. `npm run mint` (examples/txodds) writes TXLINE_API_KEY here for the standalone TxLineClient.

API.md

Lines changed: 10 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,13 @@ if (msg.verb === 'BID') {
4242
| `WANT` | Buyer → thread | Request for a service at a budget. |
4343
| `BID` | Seller → thread | Price offer for the want. |
4444
| `AWARD` | Buyer → thread | Selects a seller and confirms price. |
45-
| `ESCROW_REQUIRED` | Buyer → thread | Requires escrow deposit before delivery. |
46-
| `DEPOSITED` | Buyer → thread | Confirms escrow deposit with tx signature. |
45+
| `PAYMENT_REQUIRED` | Seller → buyer | Payment terms for the award — the primary settlement leg, `rail=x402` by default. |
46+
| `PAYMENT_PROOF` | Buyer → seller | Buyer's signed-but-unsubmitted transfer, bound to the reference. |
47+
| `PAYMENT_CONFIRMED` | Seller → buyer | Seller submitted + verified the payment on-chain. |
4748
| `DELIVERED` | Seller → thread | Delivery payload with content hash. |
48-
| `VERIFY` | Buyer → verifier | Request delivery verification. |
49+
| `VERIFY` | Buyer → verifier | Request delivery verification (informational — payment already settled). |
4950
| `VERIFIED` | Verifier → thread | `pass` or `fail` with reason. |
50-
| `PAYMENT_REQUIRED` | Seller → buyer | Rail-based payment request. |
51-
| `PAYMENT_PROOF` | Buyer → seller | Payment proof (tx signature, reference). |
52-
| `PAYMENT_CONFIRMED` | Seller → thread | Payment verified on-chain. |
53-
| `SETTLED` | System → thread | Round complete. |
51+
| `SETTLED` | Buyer → thread | Round complete. |
5452

5553
## Payment Rails
5654

@@ -139,50 +137,6 @@ const receipt = toProofReceipt({
139137
// Write to run ledger
140138
```
141139

142-
## LLM Integration
143-
144-
### Basic Completion
145-
146-
```ts
147-
import { complete } from '@pay/agent-runtime'
148-
149-
const analysis = await complete({
150-
system: 'You are a concise analyst. Return structured JSON.',
151-
user: JSON.stringify(apiResponse),
152-
maxTokens: 512,
153-
})
154-
```
155-
156-
### With Model Override
157-
158-
```ts
159-
const result = await complete({
160-
system: 'Summarize this data in one sentence.',
161-
user: rawPayload,
162-
model: 'gpt-4o-mini', // overrides LLM_MODEL env var
163-
maxTokens: 128,
164-
})
165-
```
166-
167-
### Bounded Tool Loop
168-
169-
```ts
170-
import { runToolLoop, BudgetGuard, StepCounter } from '@pay/agent-runtime'
171-
172-
const outcome = await runToolLoop({
173-
system: 'You are a pricing agent. Use the tools to decide on a bid.',
174-
tools: [clampPriceTool, submitBidTool],
175-
finalTool: 'submit_bid_decision',
176-
budget: new BudgetGuard({ maxToolCalls: 8, maxSpendLamports: 0, maxDurationSecs: 30 }),
177-
steps: new StepCounter(4),
178-
llm: complete,
179-
})
180-
181-
if (outcome.finalInput) {
182-
console.log(outcome.finalInput.bid, outcome.finalInput.priceSol)
183-
}
184-
```
185-
186140
## Policy Enforcement
187141

188142
```ts
@@ -268,8 +222,8 @@ await coral.postMessage(mention.threadId, formatBid({
268222
```ts
269223
import { createHarness } from '@pay/harness-runtime'
270224

271-
// node-llm adapter: runs deliverService() in-process
272-
const harness = createHarness('node-llm', {
225+
// in-process adapter: runs deliverService() in this process
226+
const harness = createHarness('in-process', {
273227
deliverService: async (want, cfg) => {
274228
const data = await fetch('https://api.example.com/data')
275229
return { payload: await data.json(), contentHash: hash(data) }
@@ -328,7 +282,7 @@ AGENT_NAME = "seller-myservice"
328282
PERSONA = "myservice"
329283
SERVICES = "my-api-service"
330284
FLOOR_SOL = "0.01"
331-
HARNESS = "node-llm"
285+
HARNESS = "in-process"
332286
MY_API_KEY = "${MY_API_KEY}"
333287
```
334288

@@ -365,19 +319,11 @@ app.get('/api/premium-data', async (req, res) => {
365319
| `SOLANA_RPC_URL` | agent-runtime | Solana RPC endpoint. Devnet enforced unless `ALLOW_MAINNET=1`. |
366320
| `BUYER_KEYPAIR_B58` | agent-runtime | Buyer wallet (base58 secret key). |
367321
| `SELLER_WALLET` | agent-runtime | Seller's receive-only public key. |
368-
| `SELLER_KEYPAIR_B58` | payment-runtime | Seller's spend key (for x402 procurement). |
369-
| `ARBITER_KEYPAIR_B58` | payment-runtime | Arbiter authority for escrow release/refund. |
370-
| `LLM_PROVIDER` | agent-runtime | `groq`, `venice`, `openai`, or `anthropic`. Groq recommended — free, renewing rate limit. |
371-
| `GROQ_API_KEY` | agent-runtime | Groq API key. |
372-
| `VENICE_API_KEY` | agent-runtime | Venice API key. |
373-
| `OPENAI_API_KEY` | agent-runtime | OpenAI API key. |
374-
| `ANTHROPIC_API_KEY` | agent-runtime | Anthropic API key. |
375-
| `LLM_MODEL` | agent-runtime | Model override. |
322+
| `SELLER_KEYPAIR_B58` | payment-runtime | Seller's spend key (for x402 upstream procurement). |
376323
| `CORAL_CONNECTION_URL` | agent-runtime | CoralOS server URL (set by CoralOS at container launch). |
377324
| `PROCURE_RAIL` | payment-runtime | Set to `x402` to enable upstream procurement. |
378325
| `PROCURE_X402_URL` | payment-runtime | x402 endpoint URL. |
379326
| `ALLOW_MAINNET` | agent-runtime | Set to `1` to allow mainnet RPC URLs. |
380-
| `BID_REVIEW_ENABLED` | harness-runtime | Set to `1` to enable adversarial bid review. |
381327

382328
## Integration Patterns
383329

@@ -403,7 +349,7 @@ const proof = await rail.verifyPayment(request)
403349
Wire CoralOS agents to any backend:
404350

405351
```ts
406-
import { CoralClient, complete, PolicyEngine } from '@pay/agent-runtime'
352+
import { CoralClient, PolicyEngine } from '@pay/agent-runtime'
407353
import { createRailRouter } from '@pay/payment-runtime'
408354

409355
const coral = new CoralClient({ url: process.env.CORAL_CONNECTION_URL, agentName: 'seller-api' })

CLAUDE.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,21 @@ The repository implements a devnet agent-commerce reference system. Agents coord
99
Primary lifecycle:
1010

1111
```text
12-
WANT -> BID -> AWARD -> ESCROW_REQUIRED -> DEPOSITED -> DELIVERED -> VERIFIED -> RELEASED | ARBITER_RELEASED
12+
WANT -> BID -> AWARD -> PAYMENT_REQUIRED -> PAYMENT_PROOF -> PAYMENT_CONFIRMED -> DELIVERED -> VERIFIED -> SETTLED
1313
```
1414

15-
The Rust surface is limited to `examples/txodds/escrow`, which contains the escrow and arbiter programs. Most repository code is TypeScript.
15+
Settlement is x402: the buyer pays the seller directly and finally, before delivery. There is no
16+
escrow in the default flow — the escrow/arbiter Anchor programs still exist and are deployed
17+
(`examples/txodds/escrow`), available as an alternative building block, but unused by
18+
`coral-agents/buyer-agent`/`seller-agent`. The Rust surface is limited to that directory; most
19+
repository code is TypeScript.
1620

1721
## Layout
1822

1923
| Path | Purpose |
2024
|---|---|
21-
| `packages/agent-runtime/` | LLM shim, Coral MCP client, Solana guard/helpers, market protocol, run ledger, reputation, and policy. |
22-
| `packages/harness-runtime/` | Seller execution adapter SDK for `node-llm`, `claude-code`, and arbitrary CLI harnesses. |
25+
| `packages/agent-runtime/` | Coral MCP client, Solana guard/helpers, market protocol, run ledger, reputation, and policy. |
26+
| `packages/harness-runtime/` | Seller execution adapter SDK for `in-process`, `claude-code`, and arbitrary CLI harnesses. |
2327
| `packages/payment-runtime/` | Rail interface/router, working devnet Solana Pay and escrow rails, scaffold rails, and proof receipts. |
2428
| `packages/solana-agent-tools/` | Read-only Solana context tools and optional Solana Agent Kit adapter. |
2529
| `examples/txodds/` | TxODDS proxy, web UI, service implementation, feed server, and escrow workspace. |
@@ -77,18 +81,17 @@ The market protocol is owned by `packages/agent-runtime/src/market/protocol.ts`;
7781

7882
## Agent Orchestration Framework
7983

80-
`packages/agent-runtime/src/agent/` holds capability grants, process-level safety gates
81-
(`BudgetGuard`/`StepCounter`), a `Tool` contract with an audit-log shape, an evaluation/ranking
82-
helper, and a bounded provider-agnostic LLM tool-calling loop (`runToolLoop`), available for
83-
building new Coral-native agents.
84+
`packages/agent-runtime/src/agent/` holds small scoring/ranking helpers (`rank`/`best`/
85+
`evaluateDirectionalCall`) for picking the best of several options and grading past calls,
86+
available for building new Coral-native agents.
8487

8588
## Payment and Policy
8689

8790
Solana value movement is devnet by default. Runtime helpers reject mainnet RPC URLs unless `ALLOW_MAINNET=1` is set.
8891

89-
Policy checks are centralized in `packages/agent-runtime/src/policy` and cover spend caps, service allowlists, payout binding, award-price binding, rate limiting, and verifier gating.
92+
Policy checks are centralized in `packages/agent-runtime/src/policy` and cover spend caps, service allowlists, payout binding, award-price binding, and rate limiting — all checked before the buyer signs a payment, since x402 settlement is direct and final and there is no later release step to gate.
9093

91-
Harness processes should not receive signing keys. Agent processes hold wallet authority and call policy before deposits/releases. See `PAY.md` for how the three payment rails (Solana Pay, escrow, x402) are actually used, `CORAL.md` for how the coordination layer works, and `LLM.md` for how LLM-backed decisions are proposed and enforced.
94+
Harness processes should not receive signing keys. Agent processes hold wallet authority and call policy before every payment. See `PAY.md` for how the three payment rails (x402, Solana Pay, escrow) are actually used and `CORAL.md` for how the coordination layer works.
9295

9396
## Environment
9497

@@ -97,11 +100,8 @@ Common variables:
97100
| Variable | Purpose |
98101
|---|---|
99102
| `BUYER_KEYPAIR_B58` | Buyer funding keypair for devnet transactions. |
100-
| `ARBITER_KEYPAIR_B58` | Arbiter release/refund keypair. |
101103
| `WALLET` / `SELLER_WALLET` | Seller payout addresses. |
102104
| `SOLANA_RPC_URL` | Defaults to devnet if unset. |
103-
| `LLM_PROVIDER` | `groq`, `venice`, `openai`, or `anthropic`. Groq is the recommended free default — see `LLM.md`. |
104-
| `GROQ_API_KEY` / `VENICE_API_KEY` / `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` | Provider keys. |
105105
| `TXLINE_API_KEY` | TxLINE token for TxODDS examples. |
106106

107107
Never commit `.env`, private keys, provider keys, seed phrases, or generated wallet secrets.

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ Contributions are welcome. The `main` branch is the integration branch — targe
66

77
| Directory | Language | Typical changes |
88
|-----------|----------|-----------------|
9-
| `packages/agent-runtime/` | TypeScript | The runtime: LLM shim, Solana Pay + devnet guard, CoralOS MCP client, the market protocol (incl. VERIFY/VERIFIED), the run ledger + reputation, the policy choke point |
10-
| `packages/harness-runtime/` | TypeScript | The harness adapter SDK (`node-llm` / `claude-code` / any CLI as sellers) |
9+
| `packages/agent-runtime/` | TypeScript | The runtime: Solana Pay + devnet guard, CoralOS MCP client, the market protocol (incl. VERIFY/VERIFIED), the run ledger + reputation, the policy choke point |
10+
| `packages/harness-runtime/` | TypeScript | The harness adapter SDK (`in-process` / `claude-code` / any CLI as sellers) |
1111
| `examples/txodds/` | TypeScript | The World Cup Oracle — the edge transform, the proxy (+ run grading), the web app, the CoralOS round launcher, the feed server, the research watcher |
1212
| `examples/txodds/escrow/` | Rust (Anchor) | The escrow + arbiter settlement contracts |
1313
| `coral-agents/` | TypeScript | The per-session agents: buyer, seller (+ personas), verifier, echo |
1414

1515
## Prerequisites
1616

1717
- Node.js 20+
18-
- An LLM key + a funded devnet wallet to run the live demo (see the root README). **The default demo
18+
- A funded devnet wallet to run the live demo (see the root README). **The default demo
1919
needs no Docker**; the multi-agent CoralOS round needs Docker (coral-server).
2020

2121
## Development Commands

CORAL.md

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ await startCoralAgent({ agentName: 'my-agent' }, async (ctx) => {
4545

4646
| Agent | Role |
4747
|---|---|
48-
| `buyer-agent` | Creates market thread, sends `WANT`, collects bids, awards, deposits, verifies, and releases. |
49-
| `seller-agent` | Bids on supported services, verifies funded escrow, runs a harness adapter, and delivers payloads. |
48+
| `buyer-agent` | Creates market thread, sends `WANT`, collects bids, awards, pays directly via x402, and verifies. |
49+
| `seller-agent` | Bids on supported services, submits + verifies the buyer's x402 payment, runs a harness adapter, and delivers payloads. |
5050
| `verifier-agent` | Checks delivery hash/structure and replies `VERIFIED pass` or `fail`. |
5151

5252
### Adding Another Seller
@@ -128,17 +128,20 @@ buyer-agent -> createThread("market", sellers, verifier)
128128
buyer-agent -> WANT round=<n> service=<service> arg=<arg> budget=<sol>
129129
seller-* -> BID round=<n> price=<sol> by=<seller>
130130
buyer-agent -> AWARD round=<n> to=<seller>
131-
seller -> ESCROW_REQUIRED round=<n> reference=<hash> settlement=arbiter
132-
buyer-agent -> policy check, then escrow/arbiter deposit
133-
buyer-agent -> DEPOSITED round=<n> vault=<vault PDA>
134-
seller -> funded escrow check, harness run
131+
seller -> PAYMENT_REQUIRED round=<n> rail=x402 amount=<sol> reference=<ref> seller=<addr>
132+
buyer-agent -> policy check, then sign (not submit) a transfer
133+
buyer-agent -> PAYMENT_PROOF round=<n> rail=x402 reference=<ref> proof=<signed-tx> buyer=<addr>
134+
seller -> submit + verify on-chain, harness run
135+
seller -> PAYMENT_CONFIRMED round=<n> rail=x402 reference=<ref> paid=true sig=<sig>
135136
seller -> DELIVERED round=<n> payload=<json>
136137
buyer-agent -> VERIFY round=<n> sha=<hash> ...
137-
verifier -> VERIFIED round=<n> verdict=pass|fail
138-
buyer-agent -> policy check, then release or leave funds refundable
138+
verifier -> VERIFIED round=<n> verdict=pass|fail (informational - payment already settled)
139+
buyer-agent -> SETTLED round=<n> rail=x402 reference=<ref> sig=<sig>
139140
```
140141

141-
Solana deposit/release/refund calls happen outside CoralOS. CoralOS carries coordination messages only — see [PAY.md](PAY.md) for settlement details.
142+
Payment is direct and final: the buyer signs, the seller submits and verifies on-chain — there is no
143+
escrow, no release step, and no refund path. Solana calls happen outside CoralOS; CoralOS carries
144+
coordination messages only — see [PAY.md](PAY.md) for settlement details.
142145

143146
### Protocol Messages
144147

@@ -196,17 +199,6 @@ npm run dev # Probes automatically before launch
196199

197200
Set `CORAL_CONSOLE=0` to skip the probe. Set `CORAL_CONSOLE_REQUIRED=1` to make failures fatal.
198201

199-
## Not Adopted: Coral Cloud LLM Proxy
200-
201-
CoralOS agents can route every model call through **Coral Cloud** (`llm.coralcloud.ai`), a hosted
202-
OpenAI-compatible proxy, by declaring `[[llm.proxies]]` in `coral-agent.toml` and setting a
203-
`CORAL_API_KEY`. This repo doesn't use it — LLM calls go straight to Groq/Anthropic/OpenAI/Venice
204-
(`packages/agent-runtime/src/llm/complete.ts`). That's a deliberate choice, not an oversight: Coral
205-
Cloud trades this kit's bring-your-own-key flexibility (in particular Groq's free renewing-rate-limit
206-
path, which the whole kit is built around — see `README.md`) for a dependency on a separate hosted
207-
account. Worth knowing it exists if you're forking this for a context where a Coral Cloud account is
208-
already a given.
209-
210202
## Running
211203

212204
```sh
@@ -256,5 +248,4 @@ referencing it as an external plugin instead.
256248
## See Also
257249

258250
- [PAY.md](PAY.md) — payment rails and settlement.
259-
- [LLM.md](LLM.md) — provider config for bid/award/verify decisions.
260251
- [API.md](API.md) — using the market protocol with any API.

0 commit comments

Comments
 (0)