diff --git a/docs/developer-kit/bnbchain-studio/architecture.md b/docs/developer-kit/bnbchain-studio/architecture.md index 4369ca9e72..43af525fb4 100644 --- a/docs/developer-kit/bnbchain-studio/architecture.md +++ b/docs/developer-kit/bnbchain-studio/architecture.md @@ -4,159 +4,202 @@ title: BNB Agent Studio Architecture # Architecture -BNB Agent Studio wraps the [BNB Agent SDK](../bnbagent-sdk/index.md) with scaffolding, safety controls, and IDE integration. A v1 seller project is **two deployable artifacts** on top of a **six-layer stack**. +BNB Agent Studio wraps the [BNB Agent SDK](../bnbagent-sdk/index.md) with scaffolding, +safety boundaries, and IDE integration. A project is **one deployable runtime** composed +from orthogonal recipes. -## Six-layer stack +## One runtime, one signer -| Layer | What | -|-------|------| -| **L1 — user code** | `app/agent/*`, `app/service/*` — emitted by recipes, you own and edit | -| **L2 — IDE** | Claude Code / Cursor — reads skills, calls CLI/MCP, edits your files | -| **L3 — surfaces** | `bag` CLI, MCP server, recipes, skills | -| **L4 — `bnbagent_studio_core`** | Wallet factory, `Policy`, ERC-8004/8183 workflows, x402, audit log | -| **L5 — `bnbagent-sdk`** | Protocol clients (`ERC8004Agent`, `ERC8183Client`, `EVMWalletProvider`) | -| **L6 — chain** | BSC testnet/mainnet — ERC-8004 registry, [APEX / ERC-8183](https://github.com/bnb-chain/apex-contracts#deployments), x402 | +A single deployed process serves every public face you selected and holds the only key. +There is no separate keyless service tier and no cross-service invoke hop. -```mermaid -graph TB - subgraph L1["L1 · User code"] - Agent["app/agent/"] - Service["app/service/"] - end +| Entrypoint | Face | Local surface | Runtimes | +| --- | --- | --- | --- | +| `src/unifiedMain.ts` | A2A (+ X402) | agent card and JSON-RPC on `:9000` | AgentCore, Azure Foundry | +| `src/mcpMain.ts` | MCP | Streamable HTTP at `:8000/mcp` | AgentCore, Azure Foundry (local only) | +| `src/dualMain.ts` | A2A-native with tunneled `/mcp` | `:9000` | AgentCore only | - subgraph L2["L2 · IDE"] - IDE["Claude Code / Cursor"] - end +Faces are composable (`--protocols`); one seller core and one wallet serve all of them. - subgraph L3["L3 · Studio surfaces"] - CLI["bag CLI"] - MCP["MCP server"] - Skills["Skills"] - Recipes["Recipes"] +```mermaid +graph TB + Buyer["Buyer"] + subgraph RT["One deployed runtime · sole key-holder"] + Face["unifiedMain.ts / mcpMain.ts / dualMain.ts"] + Seller["sellerCore.ts · runWork()"] + Sign["signing.ts · fixed code"] + LLM["LLM + read-only chain tools"] end + SDKL["@bnbagent/studio-runtime"] + SDK["@bnbagent/sdk"] + Chain["BSC · ERC-8004 · ERC-8183 · B402"] + + Buyer -->|"negotiate / notify_funded / x402"| Face + Face --> Seller + Seller --> LLM + Face --> Sign + Sign --> SDKL + Seller --> SDKL + SDKL --> SDK + SDK --> Chain +``` - subgraph L4["L4 · bnbagent_studio_core"] - Core["config · policy · workflows"] - end +## The bounded operations - subgraph L5["L5 · bnbagent-sdk"] - SDK["ERC8004 · ERC8183 · wallet"] - end +The ERC-8183 rail exposes exactly two operations — deliberately narrow, so neither the +LLM nor a buyer can widen them. - subgraph L6["L6 · BSC"] - Chain["8004 + 8183 + x402"] - end +**`negotiate`** — a rule-based price clamp against `studio.toml`, then an EIP-191 +signature. **No LLM touches money**: pricing is deterministic code. - IDE --> Skills - IDE --> CLI - IDE --> MCP - IDE --> Agent - CLI --> Recipes - CLI --> Core - MCP --> Core - Agent --> Core - Service --> SDK - Core --> SDK - SDK --> Chain -``` +**`notify_funded`** — verify the funded job on-chain (signed terms, assigned provider, +status, budget, funded state) **before** doing paid work, produce the deliverable, then +submit its reference on-chain. A2A acknowledges first and delivers in the background; +MCP delivers synchronously inside the tool call. -## Two-layer deploy model +The optional x402 rail adds an anonymous HTTP request at `/x402`. A positive price +settles through B402 before work starts; an explicit zero is FREE passthrough and +bypasses the facilitator entirely. -The split exists because AgentCore is invoke-only — no public HTTP routes, no background poll loop. +`settle` is **manual** — `bag erc8183 settle `. Studio never silently +auto-settles a buyer's job. -| Layer | Path | Deploy target | Holds key? | Runs LLM? | -|-------|------|---------------|------------|-----------| -| **A — Agent** | `app/agent/` | AWS Bedrock AgentCore | Yes (sole signer) | Yes | -| **B — Service** | `app/service/` | EC2 / Fargate | No (keyless) | No | +## Signing boundary -**Dependency is one-way:** Service → Agent. The public Service can only *ask* the Agent to sign; the Agent re-validates on chain before signing. +All signing is fixed entrypoint code in `app/agent/src/signing.ts`, or the runtime's +bounded x402 payment handler. It is **never** an LLM-callable tool, and no general +signing tool is exposed. The LLM's chain tools are read-only. -### Action envelopes +The encrypted keystore lives at the workspace root in `.studio/wallets/` — outside the +deploy `codeLocation`, so no packaging path can bundle it into an artifact. It reaches a +deployed runtime only through the selected provider's delegated secret channel. -The Agent is invoked on fixed action envelopes — never via LLM tools: +## Layer stack -| Action | Purpose | -|--------|---------| -| `quote` | Sign a price offer for a negotiate request | -| `fulfill` | Execute work and submit deliverable | -| `settle` | Apply policy verdict after dispute window | +| Layer | What | +|-------|------| +| **L1 — your code** | `app/agent/src/*` — emitted by recipes, you own and edit | +| **L2 — IDE** | Claude Code / Cursor — reads the skill, drives the CLI, edits your files | +| **L3 — Studio surfaces** | `bag` CLI, `bag mcp serve` (15 read-only tools), skills, recipes | +| **L4 — `@bnbagent/studio-runtime`** | Seller runtime, wallet loader, signing policy, x402 payment handler, audit log | +| **L5 — `@bnbagent/sdk`** | Protocol clients — `ERC8004Agent`, `ERC8183Client`, `EVMWalletProvider` | +| **L6 — chain** | BSC testnet/mainnet — ERC-8004 registry, ERC-8183, B402 | -All signing lives in `app/agent/signing.py` — fixed entrypoint code, never an LLM-callable tool. +Cloud lifecycle mutations are delegated to the pinned +[`@bnbagent/deploy-cli`](https://www.npmjs.com/package/@bnbagent/deploy-cli); the +generated project depends on `@bnbagent/studio-runtime`, **not** on the CLI. ## Recipe composition -`bag init` composes your project from orthogonal recipe axes: - -``` -bag/recipes/ -├── frameworks// e.g. frameworks/adk/ -├── runtimes// e.g. runtimes/agentcore/ -└── providers//code// - e.g. providers/pieverse-llm/code/adk/ +`bag init` composes the project from orthogonal recipe axes: + +```text +recipes/ +├── agent/ signing.ts +├── wallet/ wallet selection (evm-local · twak · altana) +├── runtimes/agentcore/ AWS Bedrock AgentCore entrypoints + Dockerfile +├── runtimes/azure-foundry/ Azure AI Foundry entrypoints + Dockerfile +├── providers/pieverse-llm/ Pieverse LLM wiring + funding playbook +├── tools-chain/ chainTools.ts — read-only chain tools +└── x402-buyer/ x402Buyer.ts — optional buyer rail ``` -| Axis | v1 default | Emitted into | -|------|------------|--------------| -| Framework | Google ADK | `app/agent/main.py`, `tools.py`, `managed_model.py` | -| Runtime | AgentCore | `app/agent/main.py`, `agentcore/agentcore.json` | -| LLM provider | Pieverse | `app/agent/managed_model.py` | -| Service | `8183-service` | `app/service/service.py` (framework-neutral, keyless) | +| Axis | Default | Emits | +|------|---------|-------| +| Agent | always | `signing.ts` | +| Runtime | `agentcore` | `unifiedMain.ts`, `mcpMain.ts`, `dualMain.ts`*, `executor.ts`, `sellerCore.ts`, `tools.ts`, `model.ts`, `agentCard.ts`, `Dockerfile` | +| LLM provider | `pieverse-llm` | provider wiring in `model.ts` | +| Wallet | `evm-local` | keystore selection and config | +| Chain tools | always | `chainTools.ts` | +| x402 buyer | opt-in | `x402Buyer.ts` | -### Emitted vs library +\* `dualMain.ts` is AgentCore-only. Azure Foundry ships no `dualMain` template, which is +why Foundry deploys **A2A scaffolds only** — an MCP entrypoint is rejected before deploy. -**Emitted (user-owned — edit freely):** +The A2A entrypoint is byte-identical across both runtimes (pinned by a parity test), so +the same image deploys to either cloud. -- `app/agent/main.py` — runtime entrypoint -- `app/agent/managed_model.py` — provider × framework glue -- `app/agent/tools.py` — read-only chain tools for the LLM -- `app/agent/signing.py` — protocol-neutral signing entrypoints -- `app/service/service.py` — keyless Layer B - -**Library (`bnbagent_studio_core` — shipped with `bnbagent-studio`):** - -- `studio.toml` schema, audit log protocol -- `SigningPolicy`, `BudgetGate`, `expected_recipient` -- Wallet loader, ERC-8183 workflows, x402 buyer, `chain_readonly` tools -- Pieverse credit ensurer, SIWE retry +### Emitted vs library -## Workspace isolation +**Emitted — you own it, edit freely:** + +- `sellerCore.ts` — the work your agent sells (`runWork`); the file you normally edit +- `signing.ts` — fixed quote / verify / submit path +- `tools.ts`, `chainTools.ts` — read-only chain tools for the LLM +- `model.ts` — LLM provider glue +- `unifiedMain.ts`, `mcpMain.ts`, `dualMain.ts` — face entrypoints +- `executor.ts`, `agentCard.ts` — shared seller executor and A2A card +- `x402Buyer.ts` — optional buyer rail + +**Library — shipped in `@bnbagent/studio-runtime`:** + +- `studio.toml` schema and the audit-log protocol +- signing policy and budget gating +- wallet loader, ERC-8183 workflows, the bounded x402 payment handler + +## Workspace layout + +```text +weatheragent/ +├── package.json · pnpm-workspace.yaml workspace markers +├── AGENTS.md safety rules for coding agents +├── agentcore/ deployment descriptor + AWS targets +├── .studio/ +│ ├── .env.local gitignored secrets, mode 0600 +│ └── wallets/ encrypted keystore, outside deployable code +└── app/agent/ + ├── studio.toml network · wallet · LLM · policy · rails · faces + └── src/ the emitted TypeScript above +``` -`bag init` emits two **independent sub-projects**, each with its own `pyproject.toml`, `studio.toml`, and `.env.local`: +Two boundaries do the security work: -1. **Keystore outside deploy codeLocation** — `.studio/wallets/` at workspace root, outside `app/agent/`, so no packaging path bundles it. -2. **Keyless Service boundary** — Service sub-project has no `bnbagent-studio` dependency and no `[wallet]` config. -3. **Lean Service image** — ~200 MB smaller without studio CLI deps. -4. **No shared mutable config** — only `[network].default` and provider address sync across layers. +1. **Keystore outside the deploy `codeLocation`** — `.studio/wallets/` sits at the + workspace root, so no packaging path bundles it. +2. **Signing outside the LLM's reach** — fixed code only; the model gets read-only tools. ## Commerce flow (seller) +```text +Buyer Runtime (sole signer) BNB Chain + │ negotiate │ │ + │ ─────────────────────────────────►│ clamp price, EIP-191 sign │ + │ ◄─────────────────────────────────│ signed quote │ + │ │ │ + │ createJob · setBudget · fund ────────────────────────────────────►│ + │ │ │ + │ notify_funded │ │ + │ ─────────────────────────────────►│ verify funded job on-chain │ + │ │ ──────────────────────────────►│ + │ │ run work, store deliverable │ + │ │ submit reference ─────────────►│ + │ ◄─────────────────────────────────│ deliverable │ + │ │ │ + │ approve / reject / dispute ──────────────────────────────────────►│ ``` -Buyer Layer B (Service) Layer A (Agent) BNB Chain - │ POST /apex/negotiate │ │ │ - │ ────────────────────────────►│ InvokeAgentRuntime(quote) │ │ - │ │ ──────────────────────────►│ sign offer │ - │ ◄────────────────────────────│ ◄──────────────────────────│ │ - │ (funded job on-chain) │ │ │ - │ │ poll funded jobs │ │ - │ │ InvokeAgentRuntime(fulfill) │ │ - │ │ ──────────────────────────►│ execute + submit │ - │ │ │ ──────────────────────►│ - │ │ InvokeAgentRuntime(settle) │ │ - │ │ ──────────────────────────►│ settle │ - │ │ │ ──────────────────────►│ -``` + +Settlement stays with the buyer. Operator-side settle is a manual +`bag erc8183 settle `. ## Core design principles -1. **Studio is not intelligent** — Claude Code / Cursor is the intelligence layer; studio provides recipes, CLI, MCP, and skills. -2. **Zero wallet abstraction** — studio uses `bnbagent-sdk`'s `WalletProvider` ABC via `get_wallet()`; no custom wrappers. -3. **Parallel payment protocols** — ERC-8183 and x402 each get independent CLI groups, recipes, and config sections. -4. **Payments are runtime services** — `@tool` functions in emitted code are the main battleground; CLI payment commands are operator diagnostics. +1. **Studio is not the intelligence** — Claude Code / Cursor is; Studio provides + recipes, the CLI, read-only MCP, and skills. +2. **No wallet abstraction of its own** — Studio selects a `WalletProvider` from the + SDK; it wraps nothing. +3. **Money is deterministic** — pricing clamps and signing are fixed code, never LLM + tool calls. +4. **Rails and faces are independent choices** — ERC-8183 and B402 are rails; A2A, MCP + and X402 are faces. One runtime serves any combination. +5. **The generated project is yours** — ordinary TypeScript, no closed-source runtime + and no lock-in. ## Further reading -- [BNB Agent SDK architecture](../bnbagent-sdk/architecture.md) — protocol module system -- [GitHub — architecture.md](https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/design/architecture.md) — full design document -- [GitHub — decisions.md](https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/design/decisions.md) — decision records +- [Quickstart](quickstart.md) — install, scaffold, run, deploy +- [Configuration](configuration.md) — `studio.toml` and `.env.local` +- [Deployment](deployment.md) — the three deploy targets +- [Security](security.md) — keystore posture, signing policy, MCP read-only guarantee +- [BNB Agent SDK architecture](../bnbagent-sdk/architecture.md) — the protocol layer underneath [← BNB Agent Studio overview](index.md)