@bnbagent/studio-cli 0.0.6-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DISCLAIMER.md +48 -0
- package/LICENSE +201 -0
- package/dist/_agentcoreName-DZDWEYD3.js +7 -0
- package/dist/_twak-5XQMOFUC.js +25 -0
- package/dist/bag.js +19358 -0
- package/dist/chunk-7RAKL4AS.js +172 -0
- package/dist/chunk-M3ODFCA7.js +1053 -0
- package/dist/chunk-U7IDQ3K5.js +14 -0
- package/dist/deployCli-N6TPN6XA.js +40 -0
- package/package.json +64 -0
- package/recipes/agent/code/{{PKG}}/signing.ts.tmpl +287 -0
- package/recipes/agent/recipe.toml +35 -0
- package/recipes/providers/pieverse-llm/recipe.toml +16 -0
- package/recipes/providers/pieverse-llm/skills/funding-pieverse-llm.md +203 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/.dockerignore.tmpl +8 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/Dockerfile.tmpl +50 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/agentCard.ts.tmpl +135 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/dualMain.ts.tmpl +402 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/executor.ts.tmpl +147 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/main.ts.tmpl +344 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/mcpMain.ts.tmpl +677 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/model.ts.tmpl +117 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/sellerCore.ts.tmpl +503 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/tools.ts.tmpl +157 -0
- package/recipes/runtimes/agentcore/recipe.toml +97 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/.dockerignore.tmpl +8 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/Dockerfile.tmpl +47 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/agentCard.ts.tmpl +131 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/executor.ts.tmpl +504 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/foundryMain.ts.tmpl +300 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/main.ts.tmpl +196 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/mcpMain.ts.tmpl +562 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/model.ts.tmpl +117 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/tools.ts.tmpl +157 -0
- package/recipes/runtimes/azure-foundry/recipe.toml +88 -0
- package/recipes/tools-chain/code/{{PKG}}/chainTools.ts.tmpl +166 -0
- package/recipes/tools-chain/recipe.toml +11 -0
- package/recipes/wallet/recipe.toml +20 -0
- package/recipes/x402-buyer/code/{{PKG}}/x402Buyer.ts.tmpl +175 -0
- package/recipes/x402-buyer/recipe.toml +15 -0
- package/skills/bnbagent-studio.md +107 -0
- package/skills/references/bnbagent-studio-adding-to-project.md +241 -0
- package/skills/references/bnbagent-studio-buying-from-bazaar.md +169 -0
- package/skills/references/bnbagent-studio-buying-via-8183.md +222 -0
- package/skills/references/bnbagent-studio-extending-signing.md +227 -0
- package/skills/references/bnbagent-studio-operating.md +211 -0
- package/skills/references/bnbagent-studio-scaffolding-agent.md +536 -0
- package/skills/references/bnbagent-studio-selling-via-8183.md +271 -0
- package/skills/references/bnbagent-studio-selling-via-b402.md +194 -0
- package/skills/references/bnbagent-studio-use-aws-agentcore.md +208 -0
- package/skills/references/bnbagent-studio-use-azure-foundry.md +164 -0
- package/skills/references/bnbagent-studio-use-bnb-trial.md +92 -0
- package/skills/references/bnbagent-studio-using-altana-wallet.md +68 -0
- package/skills/references/bnbagent-studio-using-twak-wallet.md +260 -0
- package/skills/references/bnbagent-studio-wiring-llm-tools.md +338 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bnbagent-studio-selling-via-8183
|
|
3
|
+
description: When the user is acting as an ERC-8183 seller on the single selected-protocol AgentCore runtime — implementing the value `notify_funded` produces, tuning the deterministic rule-based quote price (fixed list price, clamp + sign — no LLM in the negotiate path), understanding A2A async delivery vs MCP synchronous delivery, handling submitted jobs, defending against buyer disputes, and ensuring LLM credit continuity during long jobs. Owns the seller-side decision tree for the entire job lifecycle.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Reference file** of the `bnbagent-studio` router skill — installed at `bnbagent-studio/references/` and loaded on demand (not a standalone skill). Route here via the router's decision tree.
|
|
7
|
+
|
|
8
|
+
# bnbagent-studio-selling-via-8183
|
|
9
|
+
|
|
10
|
+
Procedure for the **single seller flow**: implement the value your Agent
|
|
11
|
+
produces, deploy it to AgentCore (where it serves A2A by default or MCP
|
|
12
|
+
optionally and signs in-process), and handle the job lifecycle (Agent quotes →
|
|
13
|
+
buyer funds → buyer calls `notify_funded` → Agent delivers → buyer reads the
|
|
14
|
+
result from the chain → buyer settles or disputes).
|
|
15
|
+
|
|
16
|
+
Audience: Claude Code in a working repo with a funded wallet (tBNB + U) and an
|
|
17
|
+
Agent that produces some valuable output (text, classification, image — whatever).
|
|
18
|
+
|
|
19
|
+
**Different from**:
|
|
20
|
+
- `bnbagent-studio-scaffolding-agent.md` (same directory) — creates the project (this playbook runs after)
|
|
21
|
+
- `bnbagent-studio-adding-to-project.md` (in this same references/ directory) — wires the seller runtime into an existing project (the static setup, not the runtime behavior)
|
|
22
|
+
- `bnbagent-studio-operating.md` (same directory) — generic ops (dev / doctor / balance); jumps here for seller-specific decisions
|
|
23
|
+
|
|
24
|
+
This skill owns: **implement the `notify_funded` value → deploy the agent → defend disputes**.
|
|
25
|
+
|
|
26
|
+
## The single seller runtime (one workspace sub-project)
|
|
27
|
+
|
|
28
|
+
A v1 seller ships as **one runtime**: a single valuable Agent on AWS Bedrock
|
|
29
|
+
AgentCore that **serves its selected protocol directly** (A2A:
|
|
30
|
+
`0.0.0.0:9000`; MCP: `0.0.0.0:8000/mcp`), holds the key, and signs in-process.
|
|
31
|
+
It exposes two fixed-code commerce operations — `negotiate` and `notify_funded`
|
|
32
|
+
— as A2A skills on `SellerAgentExecutor` or tools on the MCP server, behind a
|
|
33
|
+
mandatory Cognito OAuth2 authorizer. There is no separate forwarding service.
|
|
34
|
+
|
|
35
|
+
| Sub-project | Role |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `<workspace>/app/agent/` | The value (LLM/memory/tools/KB) AND the **sole signer**, deployed to AgentCore and serving the selected protocol. A2A uses `SellerAgentExecutor` in `<workspace>/app/agent/src/executor.ts`, which reads the inbound A2A message's data part and dispatches on its `"skill"` (`negotiate` / `notify_funded`); MCP uses `src/mcpMain.ts` tools with the same bounded operations. ALL signing is fixed `<workspace>/app/agent/src/signing.ts` code — never an LLM tool. Keystore lives at the WORKSPACE root `<workspace>/.studio/wallets/` (outside the AgentCore codeLocation, so no deploy packaging path can bundle it; the Agent is its sole reader via `[wallet].keystore_dir`). At deploy the keystore is injected via AWS Secrets Manager (`WALLET_KEYSTORE_JSON`) and reconstructed at cold start by the entrypoint's `loadRuntimeSecrets()`. Config in `<workspace>/app/agent/studio.toml`. |
|
|
38
|
+
|
|
39
|
+
> The earlier two-layer model (an invoke-only Agent plus a public keyless EC2
|
|
40
|
+
> service relaying via `InvokeAgentRuntime`) is **removed**. Once AgentCore could
|
|
41
|
+
> serve the protocol surface on a public HTTPS endpoint, the agent became its own
|
|
42
|
+
> public surface and the relay disappeared. See `docs/design/single-seller-agent.md`
|
|
43
|
+
> for the A2A decision history.
|
|
44
|
+
|
|
45
|
+
## Preconditions
|
|
46
|
+
|
|
47
|
+
- `bag doctor` is clean (or only warns on optional checks) — run from workspace root
|
|
48
|
+
- Wallet has ≥ 0.05 tBNB (gas to submit deliverables) and ≥ 0 U (sellers receive U, don't spend)
|
|
49
|
+
- The agent sub-project is emitted (`<workspace>/app/agent/src/main.ts` for A2A or `src/mcpMain.ts` for MCP, plus `src/signing.ts`). If not, run `bag init` or read the `bnbagent-studio-adding-to-project.md` reference first.
|
|
50
|
+
- For LLM-using sellers: `[llm].provider` configured in `app/agent/studio.toml` + (if Pieverse) `bag llm activate` has been run
|
|
51
|
+
|
|
52
|
+
## Stage 1 — Implement the `notify_funded` value (the `runWork` hook)
|
|
53
|
+
|
|
54
|
+
The valuable work lives in the `notify_funded` path. When a buyer calls
|
|
55
|
+
`notify_funded` for a `job_id`, the runtime first synchronously re-verifies the
|
|
56
|
+
job is genuinely FUNDED + ours on-chain (`signing.ts` `verifySignedJob`). In A2A
|
|
57
|
+
mode, the executor **acks `accepted`** at once and then runs the LLM work +
|
|
58
|
+
`signing.ts` `submitResult` (which SIGNS + broadcasts the deliverable) in a
|
|
59
|
+
**background task**. In MCP mode, the tool call verifies, runs the LLM work, and
|
|
60
|
+
submits synchronously, reporting progress while it runs. The block to specialise
|
|
61
|
+
is the **developer hook** — the work function that produces the deliverable text:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
async function runWork(prompt: string, opts: { sessionId: string }): Promise<string> {
|
|
65
|
+
// DEVELOPER HOOK — replace the generic LLM passthrough with your real work:
|
|
66
|
+
// call domain tools, hit your APIs, run a pipeline, use the KB.
|
|
67
|
+
// Return the deliverable TEXT — the executor handles submit + on-chain signing.
|
|
68
|
+
const { text } = await generateText({ model, tools: LLM_READ_TOOLS, prompt });
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Hard rules**:
|
|
74
|
+
- The `notify_funded` work must finish before the on-chain `submitDeadline` — keep it bounded (A2A also has to land within the session max-lifetime, ≤8h).
|
|
75
|
+
- Don't raise unhandled exceptions through `submitResult`. A permanently-bad job is rejected synchronously. In A2A, once accepted, a background delivery FAILURE leaves the job FUNDED (never reaching SUBMITTED) and is surfaced in CloudWatch — not the A2A reply. Let work errors surface so the job simply doesn't get a deliverable and the buyer can dispute cleanly; don't fake a deliverable.
|
|
76
|
+
- All chain WRITES go through `app/agent/src/signing.ts` (fixed code). In the `notify_funded` work the LLM only PRODUCES work text — it never signs, and it never sets the price (the quote price is rule-based; see Stage 2).
|
|
77
|
+
|
|
78
|
+
## Stage 2 — Rule-based pricing (fixed list price, clamp + sign — no LLM)
|
|
79
|
+
|
|
80
|
+
A `negotiate` skill message hits the executor, which dispatches to the quote path.
|
|
81
|
+
The quote path is **deterministic policy — no LLM, no tools**:
|
|
82
|
+
|
|
83
|
+
1. Fixed code reads the configured **list price** from `[payments.erc8183].price`
|
|
84
|
+
(`signing.ts` `listPrice()`). The LLM is never invoked in the negotiate path and never
|
|
85
|
+
proposes or touches the price.
|
|
86
|
+
2. **Fixed code CLAMPS** the list price to `[min_price, max_price]`
|
|
87
|
+
(`signing.ts` `clampPrice()`) — a misconfigured or hostile request can never sign
|
|
88
|
+
out of bounds. For per-task pricing, compute the price from the request in the
|
|
89
|
+
quote path *before* clamping; it stays deterministic code, not an LLM decision.
|
|
90
|
+
3. `signing.ts` `signQuote` does the **EIP-191 sign** with a short TTL (returns the
|
|
91
|
+
SDK `NegotiationResult` envelope verbatim — price, currency, negotiation_hash,
|
|
92
|
+
provider_sig), which the executor returns directly to the buyer over A2A.
|
|
93
|
+
`chain_id` + `verifying_contract` are bound into the signature, so the quote
|
|
94
|
+
cannot be replayed on another chain/contract. **Money is never in the LLM.**
|
|
95
|
+
|
|
96
|
+
Tune the clamp in `<workspace>/app/agent/studio.toml`:
|
|
97
|
+
|
|
98
|
+
```toml
|
|
99
|
+
# app/agent/studio.toml
|
|
100
|
+
[payments.erc8183]
|
|
101
|
+
currency = "0x..." # $U token — prefilled by `bag init` from [network].default; rarely changed
|
|
102
|
+
price = "0" # raw wei — the asking list price the quote signs
|
|
103
|
+
min_price = "0" # raw wei — clamp floor
|
|
104
|
+
max_price = "0" # raw wei — clamp ceiling (set > 0 to enforce)
|
|
105
|
+
quote_ttl_seconds = 300
|
|
106
|
+
default_estimated_completion_seconds = 600
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Stage 3 — LLM credit continuity (Pieverse projects only)
|
|
110
|
+
|
|
111
|
+
If `[llm].provider = "pieverse-llm"`, the Agent's emitted
|
|
112
|
+
`app/agent/src/model.ts` `buildModel()` factory returns an AI SDK model wrapped
|
|
113
|
+
with credit-ensure middleware (the stack-neutral credit logic lives in
|
|
114
|
+
`@bnbagent/studio-runtime/pieverse` `PieverseCreditEnsurer`; the AI-SDK shell
|
|
115
|
+
is the emitted file). Its **automatic, budget-gated auto-renew hook** tops up
|
|
116
|
+
the active Pieverse key before an LLM call when the cached balance is below
|
|
117
|
+
the floor. It fires **only in `notify_funded` work** (the `negotiate` path
|
|
118
|
+
runs no LLM, so it never triggers there). This is the **only automatic signing
|
|
119
|
+
path outside `signing.ts`**; it rides on the hardened x402 buyer kernel
|
|
120
|
+
(`@bnbagent/studio-runtime/x402`, the payment signer) but is **NOT an LLM
|
|
121
|
+
tool** — the Agent (the sole key-holder) does it transparently inside the
|
|
122
|
+
model wrapper.
|
|
123
|
+
|
|
124
|
+
| Layer | Mechanism | When it fires |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| **Per-call auto-renew hook** | The emitted managed-model wrapper checks key balance before each LLM call | Cache miss (60s default) or balance < `min_balance_usd` |
|
|
127
|
+
|
|
128
|
+
If `[budget].enabled = true` (opt-in), the hook auto-tops-up from the
|
|
129
|
+
wallet within the 6-gate budget; otherwise it raises
|
|
130
|
+
`PieverseAccountBalanceExhaustedError` and the executor's `notify_funded`
|
|
131
|
+
work should:
|
|
132
|
+
|
|
133
|
+
1. Let the error surface in the background task so the job stays FUNDED (never reaches SUBMITTED) and the buyer can dispute (don't silently fake a deliverable). The job was already acked `accepted`, so the failure is visible on-chain + in CloudWatch, not in the A2A reply.
|
|
134
|
+
2. **Don't** retry — the buyer's deadline keeps ticking
|
|
135
|
+
3. The owner sees the error in CloudWatch logs + should run `bag llm topup` to refill (or `bag budget enable`)
|
|
136
|
+
|
|
137
|
+
Full Pieverse credit decisions live in `funding-pieverse-llm` (project-scope skill in Pieverse projects).
|
|
138
|
+
|
|
139
|
+
## Stage 4 — Deploy + register (announce to buyers)
|
|
140
|
+
|
|
141
|
+
ERC-8183 / ERC-8004 registration is a **deploy-time** concern: the public
|
|
142
|
+
AgentCore endpoint must exist before you register, so register **last**. The
|
|
143
|
+
agent endpoint has no anonymous mode, so you must provision the Cognito OAuth2
|
|
144
|
+
authorizer first.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
bag deploy prepare # readiness sweep
|
|
148
|
+
bag deploy provision-cognito # emit the Cognito CDK app; run `cdk deploy`, wire discoveryUrl/clientId
|
|
149
|
+
bag deploy --provider aws # ship the agent to AgentCore (selected protocol)
|
|
150
|
+
bag deploy verify --provider aws # delegated status + reconcile ERC-8004 identity
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
ERC-8004 identity is registered with the **AgentCore endpoint**: A2A uses
|
|
154
|
+
`AgentEndpoint.a2a(baseUrl, "0.3.0")` (normalizing to
|
|
155
|
+
`/.well-known/agent-card.json`), while MCP records the `/mcp` endpoint plus access
|
|
156
|
+
metadata. Buyers reach the agent directly. `bag deploy verify --provider aws`
|
|
157
|
+
asks bnbagent-deploy for live status first, then performs the reconcile.
|
|
158
|
+
|
|
159
|
+
**Hard rules**:
|
|
160
|
+
- The endpoint must be **reachable** when registered. For A2A, smoke test the normalized card URL directly, for example `curl <agentcore-invocations-url>/.well-known/agent-card.json` (or `curl <already-registered-card-url>` if the endpoint already includes `/.well-known/agent-card.json`). For MCP, connect an MCP client to the deployed `/mcp` URL. Chain doesn't verify reachability, but buyers will see failures.
|
|
161
|
+
- To serve external buyers, configure the Cognito authorizer (`provision-cognito` → `cdk deploy --outputs-file` → `provision-cognito --wire`); `bag deploy prepare` warns (W9) if unset but does NOT block. With no authorizer the runtime is IAM/SigV4 owner-only (never anonymous) — buyers without AWS creds just can't reach it.
|
|
162
|
+
- Price bounds (`min_price`/`max_price`) live with the Agent — it clamps + signs the quote.
|
|
163
|
+
|
|
164
|
+
## Stage 5 — How a SUBMITTED job happens
|
|
165
|
+
|
|
166
|
+
There is **no background poll loop** in v1. Delivery is triggered by
|
|
167
|
+
`notify_funded`; the execution model depends on the selected protocol:
|
|
168
|
+
|
|
169
|
+
1. **A2A buyer push (the prompt path):** buyer negotiates over A2A (`negotiate`
|
|
170
|
+
skill) → buyer anchors the signed quote on-chain (`createJob` → `register` →
|
|
171
|
+
`setBudget` → `fund`, provider = the agent address) → buyer pushes a
|
|
172
|
+
`notify_funded` A2A message for the `job_id` → the executor synchronously
|
|
173
|
+
re-verifies (`verifySignedJob`) and **acks `accepted`** at once, then in a
|
|
174
|
+
**background task** does the LLM work and SIGNS + submits the deliverable
|
|
175
|
+
on-chain → job → SUBMITTED. The buyer **polls the chain** for the result (it
|
|
176
|
+
does not wait on the ack), then approves or disputes within `dispute_window`
|
|
177
|
+
(24h on testnet). While any background delivery is in flight the runtime
|
|
178
|
+
reports `/ping` `HEALTHY_BUSY` (AgentCore's long-running async pattern), so the
|
|
179
|
+
scale-to-zero runtime stays warm until the work lands — bounded by the session
|
|
180
|
+
max-lifetime (≤8h).
|
|
181
|
+
2. **A2A in-process sweep (the fallback):** on every accepted `notify_funded`, the
|
|
182
|
+
executor opportunistically sweeps (in the background)
|
|
183
|
+
`ERC8183JobOps.getPendingJobs()` for other FUNDED jobs assigned to this
|
|
184
|
+
provider and delivers them too. This catches jobs whose buyer funded on-chain
|
|
185
|
+
but never pushed `notify_funded`. The sweep is **deduped** (a job already being
|
|
186
|
+
delivered — by the notification or a concurrent sweep — is skipped via an
|
|
187
|
+
in-flight set, so notify + sweep never double-deliver the same job),
|
|
188
|
+
**idempotent** (`verifySignedJob` returns non-OK for an already-SUBMITTED
|
|
189
|
+
job, so there is no state file), and **best-effort** (one bad job never aborts
|
|
190
|
+
the sweep, and a sweep failure never affects the ack).
|
|
191
|
+
3. **MCP synchronous delivery:** buyer negotiates through the `negotiate` MCP
|
|
192
|
+
tool, funds on-chain, then calls `notify_funded`; the tool verifies, runs the
|
|
193
|
+
work, and signs + submits before returning, with progress heartbeats. There is
|
|
194
|
+
no background ack/sweep path in MCP.
|
|
195
|
+
|
|
196
|
+
Because the sweep runs only when *someone* invokes `notify_funded` (and only while
|
|
197
|
+
the runtime is warm), a totally idle, scaled-to-zero agent will not deliver during
|
|
198
|
+
the cold window until the next notify. A periodic Lambda poller is the v2
|
|
199
|
+
robustness path that closes it. The full line protocol is in
|
|
200
|
+
`docs/design/erc8183-buyer-push.md`.
|
|
201
|
+
|
|
202
|
+
Manual override (rare — only the agent holds the key):
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
bag erc8183 submit <job_id> "<deliverable text>" [--metadata-json '{...}']
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Use only if the Agent produced a deliverable out-of-band and you need to attach it
|
|
209
|
+
manually.
|
|
210
|
+
|
|
211
|
+
## Stage 6 — Defending against disputes
|
|
212
|
+
|
|
213
|
+
Buyer can `settle --action dispute` within `dispute_window`. Seller responses:
|
|
214
|
+
|
|
215
|
+
| Symptom | Diagnosis | Action |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| Buyer disputed within 24h | Their right; goes to quorum vote | Provide evidence via the off-chain governance flow; don't argue on-chain |
|
|
218
|
+
| `0x17be5b7b` revert when buyer tried to `approve` | Buyer tried to approve before 24h passed | This is buyer's mistake, not yours; they'll need to wait or use `dispute` |
|
|
219
|
+
| `Submission deadline has passed` when you tried to submit | Buyer set too-short `expiredAt` on the job | You can't submit late; politely tell buyer to re-buy with longer deadline |
|
|
220
|
+
| Job stuck `FUNDED` for >24h | Nobody pushed `notify_funded` (and the agent stayed idle, so the sweep never ran) or the background `runWork` crashed (the job was acked `accepted` but never reached SUBMITTED) | Inspect CloudWatch logs; push a `notify_funded` to wake the agent, or submit work manually if a deliverable exists but submit failed |
|
|
221
|
+
|
|
222
|
+
**Hard rules**:
|
|
223
|
+
- Never tamper with quorum vote signals (no fake validators / no spam-disputes).
|
|
224
|
+
- If you produced a defective deliverable, **don't dispute** — accept the buyer's reject; chain reputation is real.
|
|
225
|
+
- Keep the agent's logs (it prints redacted audit events to stdout → CloudWatch).
|
|
226
|
+
|
|
227
|
+
## Stage 7 — Settle + withdraw funds
|
|
228
|
+
|
|
229
|
+
`settle` is the **seller's manual step** — there is no in-runtime auto-settle in
|
|
230
|
+
v1 (no long-running process to run it). After the dispute window elapses:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
bag erc8183 settle <job_id> # default --action approve
|
|
234
|
+
bag erc8183 status <job_id> # confirm COMPLETED
|
|
235
|
+
# Funds auto-transfer on COMPLETED in the current contract; no manual withdraw needed.
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Older contracts may have required a manual `withdraw` — confirm against the
|
|
239
|
+
deployed commerce contract's behavior.
|
|
240
|
+
|
|
241
|
+
## How a buyer reads the deliverable
|
|
242
|
+
|
|
243
|
+
The agent serves **no** job-query endpoint. The buyer reads the deliverable back
|
|
244
|
+
from the on-chain submission (the `submit` tx carries the deterministic
|
|
245
|
+
`deliverable_url`) / the IPFS object. This is by design — the chain is the shared
|
|
246
|
+
source of truth, and the agent stays a thin A2A surface.
|
|
247
|
+
|
|
248
|
+
## Common errors + remediation
|
|
249
|
+
|
|
250
|
+
| Error | Cause | Fix |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| A2A job acked `accepted` but never reaches SUBMITTED | background `runWork` / `submitResult` raised (the failure is NOT in the A2A reply — the ack already went out) | Inspect CloudWatch logs; wrap LLM/domain calls; let the error surface so the buyer can dispute, don't fake a deliverable |
|
|
253
|
+
| `notify_funded` reply `{ "status": "rejected", "reason": ... }` | synchronous verify failed (not our signature, tampered terms, underfunded, expired) or malformed `job_id` | Confirm the buyer funded the exact signed quote against this provider; re-quote if terms changed |
|
|
254
|
+
| `submit_work reverts gas estimation` | Job already SUBMITTED (idempotency violation) | Check `bag erc8183 status <id>` before retry — the sweep already handles this idempotently |
|
|
255
|
+
| `PieverseAccountBalanceExhaustedError` mid-job | Agent's LLM credit ran out | Let it surface; run `bag llm topup`; consider `bag budget enable` |
|
|
256
|
+
| Agent unreachable (buyer reports) | AgentCore runtime is down, DNS wrong, or the OAuth2 bearer is missing/expired | `bag deploy status` / `bag deploy logs --provider aws` to inspect; redeploy via `bag deploy --provider aws` |
|
|
257
|
+
|
|
258
|
+
## Hard rules (security boundary)
|
|
259
|
+
|
|
260
|
+
- Don't store buyer-supplied data outside the request lifecycle (privacy + storage cost).
|
|
261
|
+
- The Agent signs only for **its own** quote / submit / settle actions; it never signs for the buyer.
|
|
262
|
+
- The key lives ONLY in the agent at the workspace root `.studio/wallets/` (outside every codeLocation, so no packaging path can bundle it) and is injected into the agent via AWS Secrets Manager at deploy — never inlined into the code artifact (mainnet refuses the envvars fallback). Never log API keys / PII into stdout (the agent's audit events are redacted before they hit CloudWatch).
|
|
263
|
+
- Inbound auth is mandatory Cognito OAuth2 — buyers send plain HTTPS + a Bearer (no AWS SigV4). An unauthorized request is rejected 401; there is no anonymous mode.
|
|
264
|
+
- LLM model name selection (paid vs free) is a per-project decision — load `funding-pieverse-llm` if user is unsure which model to use for paid services.
|
|
265
|
+
|
|
266
|
+
## Reference
|
|
267
|
+
|
|
268
|
+
- `docs/design/single-seller-agent.md` (the A2A deploy model and history)
|
|
269
|
+
- `docs/design/erc8183-buyer-push.md` (the negotiate → fund → notify_funded line protocol)
|
|
270
|
+
- `docs/design/architecture.md` §2.5 (the single seller runtime — as-built)
|
|
271
|
+
- the `bnbagent-studio-wiring-llm-tools.md` reference (in this same references/ directory) — wiring read-only chain tools into the Agent's LLM
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bnbagent-studio-selling-via-b402
|
|
3
|
+
description: When the user wants a bnbagent-studio agent to sell paid HTTP requests through the B402-backed x402 rail. Owns per-agent merchant onboarding, RSA key preparation, egress-IP allowlisting, sandbox/production separation, B402 environment setup, seller status checks, and activation by redeploy (managed platform or self-hosted AgentCore).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Reference file** of the `bnbagent-studio` router skill — installed at
|
|
7
|
+
> `bnbagent-studio/references/` and loaded on demand (not a standalone skill).
|
|
8
|
+
> Route here via the router's decision tree.
|
|
9
|
+
|
|
10
|
+
# Sell via B402
|
|
11
|
+
|
|
12
|
+
Use this playbook to activate the x402 seller rail for one agent. B402 merchant
|
|
13
|
+
credentials are per agent and per environment. Never reuse a merchant record
|
|
14
|
+
across agent wallets, or mix sandbox and production values.
|
|
15
|
+
|
|
16
|
+
## Preconditions
|
|
17
|
+
|
|
18
|
+
- The agent wallet already exists. Its address will receive the U payments.
|
|
19
|
+
- The project targets the managed platform or self-hosted AgentCore
|
|
20
|
+
(azure-foundry cannot activate the rail).
|
|
21
|
+
- Managed platform only: a platform bearer token is available for reading the
|
|
22
|
+
platform egress IPs — the GitHub-login access token from
|
|
23
|
+
`bag platform login`, or a `bnbk_…` API token minted once by
|
|
24
|
+
`bag platform token`.
|
|
25
|
+
- `[payments.x402_seller]` exists. If not, run
|
|
26
|
+
`bag x402 sell init`.
|
|
27
|
+
|
|
28
|
+
The application uses the **agent wallet address**, not a developer treasury,
|
|
29
|
+
buyer wallet, or platform wallet.
|
|
30
|
+
|
|
31
|
+
## Generate the agent's RSA material
|
|
32
|
+
|
|
33
|
+
This playbook is the canonical key-generation procedure; neither the CLI nor
|
|
34
|
+
the B402 SDK generates keys. The pair authenticates every facilitator API
|
|
35
|
+
call under the
|
|
36
|
+
[B402 request-signing scheme](https://developers.binance.com/en/docs/products/onchainpay-x402/basics/3.request-signing);
|
|
37
|
+
the runtime signs each request automatically once the credentials are stored.
|
|
38
|
+
|
|
39
|
+
Work from the workspace root. Keep private material under `.studio/`, which is
|
|
40
|
+
excluded from source and deploy artifacts:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
umask 077
|
|
44
|
+
mkdir -p .studio/b402
|
|
45
|
+
openssl genrsa -out .studio/b402/private.pem 2048
|
|
46
|
+
openssl pkey -in .studio/b402/private.pem -pubout \
|
|
47
|
+
-out .studio/b402/public.pem
|
|
48
|
+
openssl pkey -in .studio/b402/private.pem -pubout -outform DER |
|
|
49
|
+
openssl base64 -A > .studio/b402/public.der.b64
|
|
50
|
+
openssl pkcs8 -topk8 -nocrypt -in .studio/b402/private.pem -outform DER |
|
|
51
|
+
openssl base64 -A > .studio/b402/private.der.b64
|
|
52
|
+
chmod 600 .studio/b402/private.pem .studio/b402/private.der.b64
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Do not print the private key or its base64 form. Submit only the public key
|
|
56
|
+
material to B402.
|
|
57
|
+
|
|
58
|
+
Studio generates a 2048-bit RSA key. The current B402 application form text
|
|
59
|
+
mentions 1024 bits, while 2048-bit acceptance still requires confirmation from
|
|
60
|
+
the B402 operator. Confirm that field with the operator; do not silently
|
|
61
|
+
downgrade the key.
|
|
62
|
+
|
|
63
|
+
## Collect the IP allowlist
|
|
64
|
+
|
|
65
|
+
B402 allowlists the merchant's **outbound** (egress) IPs — the addresses the
|
|
66
|
+
agent's facilitator calls come FROM. Submit every part that applies to your
|
|
67
|
+
deployment target:
|
|
68
|
+
|
|
69
|
+
1. **Platform egress IPs (managed-platform deploys)** — the addresses
|
|
70
|
+
deployed-agent requests reach B402 from. Authenticated read (bearer from
|
|
71
|
+
the Preconditions):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
curl -H "Authorization: Bearer $BNBAGENT_API_TOKEN" \
|
|
75
|
+
"https://bnbagent-api.bnbchain.world/v1/b402/whitelist-ips"
|
|
76
|
+
# → {"whitelist_ips": ["13.115.15.190", …], "cache_ttl_seconds": 300}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Submit every address in `whitelist_ips`. The list is served with a short
|
|
80
|
+
cache TTL and can rotate — re-read it right before submitting the form.
|
|
81
|
+
Set `BAG_PLATFORM_API_BASE` to target a non-production platform
|
|
82
|
+
environment.
|
|
83
|
+
|
|
84
|
+
2. **Your local public IP** — required so a local `bag dev` run can reach
|
|
85
|
+
B402:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
curl ipinfo.io/ip
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
3. **Self-hosted AgentCore egress (self-deploys)** — AgentCore gives the
|
|
92
|
+
container no fixed egress IP, so there is no address to submit directly.
|
|
93
|
+
Route the facilitator egress through a fixed-IP forward proxy you control
|
|
94
|
+
and submit THAT IP: set `HTTPS_PROXY` on the runtime and enable proxy
|
|
95
|
+
support for Node's fetch, which ignores proxy env vars by default —
|
|
96
|
+
install an undici `EnvHttpProxyAgent` global dispatcher in the agent
|
|
97
|
+
entrypoint, or run the container on Node 24+ with `--use-env-proxy`.
|
|
98
|
+
Without this, every verify/settle call is rejected as an unallowlisted
|
|
99
|
+
IP even though the deploy summary reports the rail ACTIVE.
|
|
100
|
+
|
|
101
|
+
Do not add the public inbound gateway IP, a transient build-runner IP, or
|
|
102
|
+
guessed addresses. If the whitelist endpoint is unreachable, stop onboarding
|
|
103
|
+
and confirm the platform environment with the operator.
|
|
104
|
+
|
|
105
|
+
## Submit the B402 merchant application
|
|
106
|
+
|
|
107
|
+
Apply through the
|
|
108
|
+
[B402 developer account application](https://developers.binance.com/en/docs/products/onchainpay-x402/basics/6.apply-developer-account).
|
|
109
|
+
Complete one application for sandbox and a separate application for
|
|
110
|
+
production. Fill the form as follows:
|
|
111
|
+
|
|
112
|
+
| Field | Value |
|
|
113
|
+
|---|---|
|
|
114
|
+
| Business Name | Your agent or business display name |
|
|
115
|
+
| Website | Your project or service URL |
|
|
116
|
+
| Wallet | The agent wallet address |
|
|
117
|
+
| RSA Public Key | The contents of `.studio/b402/public.der.b64` in the format the operator confirms |
|
|
118
|
+
| IP allowlist | Every platform egress IP from `/v1/b402/whitelist-ips` plus your local public IP (see "Collect the IP allowlist") |
|
|
119
|
+
|
|
120
|
+
Keep the two environments isolated:
|
|
121
|
+
|
|
122
|
+
| Environment | Chain | Credentials | Wallet/IP registration |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| Sandbox | BSC testnet | sandbox-only | apply separately |
|
|
125
|
+
| Production | BSC mainnet | production-only | apply separately |
|
|
126
|
+
|
|
127
|
+
## Store the issued credentials
|
|
128
|
+
|
|
129
|
+
Open the workspace `.studio/.env.local` in an editor and fill exactly four
|
|
130
|
+
values:
|
|
131
|
+
|
|
132
|
+
```dotenv
|
|
133
|
+
B402_BASE_URL=
|
|
134
|
+
B402_CLIENT_ID=
|
|
135
|
+
B402_ACCESS_TOKEN=
|
|
136
|
+
B402_PRIVATE_KEY_B64=
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Copy the single-line DER value from
|
|
140
|
+
`.studio/b402/private.der.b64` into `B402_PRIVATE_KEY_B64`. Do not include the
|
|
141
|
+
value in shell history, terminal output, source files, TOML, screenshots, or
|
|
142
|
+
support tickets.
|
|
143
|
+
|
|
144
|
+
`B402_PRIVATE_KEY` accepts the PEM representation as an alternative. Keep
|
|
145
|
+
exactly one private-key form; do not set both.
|
|
146
|
+
|
|
147
|
+
## Verify and activate
|
|
148
|
+
|
|
149
|
+
Check names and presence without exposing values:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
bag x402 sell status --no-probe
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
When the credentials, IP allowlist, and facilitator environment are ready, run
|
|
156
|
+
the authenticated read-only capability check:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
bag x402 sell status
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
For a sandbox/trial agent, it must find exact/eip3009 U on `eip155:97`. For
|
|
163
|
+
production it must find the mainnet environment expected by the project. A
|
|
164
|
+
network mismatch is not safe to ignore.
|
|
165
|
+
|
|
166
|
+
Run the deployment gate, then redeploy to activate the paid route:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
bag deploy prepare
|
|
170
|
+
bag deploy --provider bnb # managed platform
|
|
171
|
+
bag deploy --provider aws # self-hosted AgentCore
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
On the managed platform the deploy summary must say `x402 rail is ACTIVE` and
|
|
175
|
+
print the anonymous `/x402` URL. On a self-hosted AgentCore deploy it says
|
|
176
|
+
`x402 rail is ACTIVE (self-hosted AgentCore)`: the rail runs in-process, but
|
|
177
|
+
there is no anonymous URL — operate your own HTTP front that relays
|
|
178
|
+
envelope-v1 JSON over SigV4-signed `InvokeAgentRuntime` calls (see
|
|
179
|
+
`docs/guides/x402-selling.md`, "Self-hosted AgentCore access"). A dormant or
|
|
180
|
+
forced-dormant summary means the rail was not activated; fix the named
|
|
181
|
+
credential, runtime, network, or tunnel condition and redeploy.
|
|
182
|
+
|
|
183
|
+
## Hard rules
|
|
184
|
+
|
|
185
|
+
- Never log or print any B402 value or private key.
|
|
186
|
+
- Never put a B402 value in `studio.toml` or a deploy descriptor.
|
|
187
|
+
- Never retry a settlement whose outcome is unknown. Follow
|
|
188
|
+
`docs/guides/x402-selling.md` and reconcile `(nonce, network, payer)` first.
|
|
189
|
+
- Settlement happens before work. A later work failure retains the payment and
|
|
190
|
+
does not trigger an automatic refund.
|
|
191
|
+
- The rail activates on AgentCore targets only (managed platform or
|
|
192
|
+
self-hosted); azure-foundry stays forced dormant.
|
|
193
|
+
- The payout wallet must be `wallet.kind` `evm-local` or `twak`; other kinds
|
|
194
|
+
(including Altana) are rejected at init, `sell init`, and readiness.
|