@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.
Files changed (55) hide show
  1. package/DISCLAIMER.md +48 -0
  2. package/LICENSE +201 -0
  3. package/dist/_agentcoreName-DZDWEYD3.js +7 -0
  4. package/dist/_twak-5XQMOFUC.js +25 -0
  5. package/dist/bag.js +19358 -0
  6. package/dist/chunk-7RAKL4AS.js +172 -0
  7. package/dist/chunk-M3ODFCA7.js +1053 -0
  8. package/dist/chunk-U7IDQ3K5.js +14 -0
  9. package/dist/deployCli-N6TPN6XA.js +40 -0
  10. package/package.json +64 -0
  11. package/recipes/agent/code/{{PKG}}/signing.ts.tmpl +287 -0
  12. package/recipes/agent/recipe.toml +35 -0
  13. package/recipes/providers/pieverse-llm/recipe.toml +16 -0
  14. package/recipes/providers/pieverse-llm/skills/funding-pieverse-llm.md +203 -0
  15. package/recipes/runtimes/agentcore/code/{{PKG}}/.dockerignore.tmpl +8 -0
  16. package/recipes/runtimes/agentcore/code/{{PKG}}/Dockerfile.tmpl +50 -0
  17. package/recipes/runtimes/agentcore/code/{{PKG}}/agentCard.ts.tmpl +135 -0
  18. package/recipes/runtimes/agentcore/code/{{PKG}}/dualMain.ts.tmpl +402 -0
  19. package/recipes/runtimes/agentcore/code/{{PKG}}/executor.ts.tmpl +147 -0
  20. package/recipes/runtimes/agentcore/code/{{PKG}}/main.ts.tmpl +344 -0
  21. package/recipes/runtimes/agentcore/code/{{PKG}}/mcpMain.ts.tmpl +677 -0
  22. package/recipes/runtimes/agentcore/code/{{PKG}}/model.ts.tmpl +117 -0
  23. package/recipes/runtimes/agentcore/code/{{PKG}}/sellerCore.ts.tmpl +503 -0
  24. package/recipes/runtimes/agentcore/code/{{PKG}}/tools.ts.tmpl +157 -0
  25. package/recipes/runtimes/agentcore/recipe.toml +97 -0
  26. package/recipes/runtimes/azure-foundry/code/{{PKG}}/.dockerignore.tmpl +8 -0
  27. package/recipes/runtimes/azure-foundry/code/{{PKG}}/Dockerfile.tmpl +47 -0
  28. package/recipes/runtimes/azure-foundry/code/{{PKG}}/agentCard.ts.tmpl +131 -0
  29. package/recipes/runtimes/azure-foundry/code/{{PKG}}/executor.ts.tmpl +504 -0
  30. package/recipes/runtimes/azure-foundry/code/{{PKG}}/foundryMain.ts.tmpl +300 -0
  31. package/recipes/runtimes/azure-foundry/code/{{PKG}}/main.ts.tmpl +196 -0
  32. package/recipes/runtimes/azure-foundry/code/{{PKG}}/mcpMain.ts.tmpl +562 -0
  33. package/recipes/runtimes/azure-foundry/code/{{PKG}}/model.ts.tmpl +117 -0
  34. package/recipes/runtimes/azure-foundry/code/{{PKG}}/tools.ts.tmpl +157 -0
  35. package/recipes/runtimes/azure-foundry/recipe.toml +88 -0
  36. package/recipes/tools-chain/code/{{PKG}}/chainTools.ts.tmpl +166 -0
  37. package/recipes/tools-chain/recipe.toml +11 -0
  38. package/recipes/wallet/recipe.toml +20 -0
  39. package/recipes/x402-buyer/code/{{PKG}}/x402Buyer.ts.tmpl +175 -0
  40. package/recipes/x402-buyer/recipe.toml +15 -0
  41. package/skills/bnbagent-studio.md +107 -0
  42. package/skills/references/bnbagent-studio-adding-to-project.md +241 -0
  43. package/skills/references/bnbagent-studio-buying-from-bazaar.md +169 -0
  44. package/skills/references/bnbagent-studio-buying-via-8183.md +222 -0
  45. package/skills/references/bnbagent-studio-extending-signing.md +227 -0
  46. package/skills/references/bnbagent-studio-operating.md +211 -0
  47. package/skills/references/bnbagent-studio-scaffolding-agent.md +536 -0
  48. package/skills/references/bnbagent-studio-selling-via-8183.md +271 -0
  49. package/skills/references/bnbagent-studio-selling-via-b402.md +194 -0
  50. package/skills/references/bnbagent-studio-use-aws-agentcore.md +208 -0
  51. package/skills/references/bnbagent-studio-use-azure-foundry.md +164 -0
  52. package/skills/references/bnbagent-studio-use-bnb-trial.md +92 -0
  53. package/skills/references/bnbagent-studio-using-altana-wallet.md +68 -0
  54. package/skills/references/bnbagent-studio-using-twak-wallet.md +260 -0
  55. 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.