@bnbagent/studio-cli 0.0.10 → 0.0.11-alpha.2
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/LICENSE +201 -0
- package/README.md +2 -2
- package/dist/_agentcoreName-DZDWEYD3.js +0 -0
- package/dist/_twak-4XF4H5PL.js +0 -0
- package/dist/bag.js +322 -125
- package/dist/chunk-RO726HJG.js +0 -0
- package/dist/{chunk-YFEM4564.js → chunk-TTPOH453.js} +79 -37
- package/dist/chunk-U7IDQ3K5.js +0 -0
- package/dist/{deployCli-NJFCWBSF.js → deployCli-K55GXDVO.js} +1 -1
- package/package.json +11 -12
- package/recipes/runtimes/agentcore/code/{{PKG}}/Dockerfile.tmpl +20 -21
- package/recipes/runtimes/agentcore/code/{{PKG}}/executor.ts.tmpl +36 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/unifiedMain.ts.tmpl +620 -0
- package/recipes/runtimes/agentcore/recipe.toml +3 -3
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/Dockerfile.tmpl +25 -23
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/agentCard.ts.tmpl +16 -12
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/executor.ts.tmpl +72 -393
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/mcpMain.ts.tmpl +160 -43
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/sellerCore.ts.tmpl +504 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/unifiedMain.ts.tmpl +620 -0
- package/recipes/runtimes/azure-foundry/recipe.toml +19 -11
- package/recipes/x402-buyer/code/{{PKG}}/x402Buyer.ts.tmpl +6 -4
- package/skills/bnbagent-studio.md +2 -2
- package/skills/references/bnbagent-studio-adding-to-project.md +1 -1
- package/skills/references/bnbagent-studio-buying-from-bazaar.md +1 -1
- package/skills/references/bnbagent-studio-operating.md +4 -4
- package/skills/references/bnbagent-studio-scaffolding-agent.md +4 -4
- package/skills/references/bnbagent-studio-selling-via-8183.md +3 -3
- package/skills/references/bnbagent-studio-selling-via-b402.md +2 -2
- package/skills/references/bnbagent-studio-use-aws-agentcore.md +1 -1
- package/skills/references/bnbagent-studio-use-azure-foundry.md +3 -3
- package/skills/references/bnbagent-studio-use-bnb-trial.md +1 -1
- package/skills/references/bnbagent-studio-wiring-llm-tools.md +3 -3
- package/recipes/runtimes/agentcore/code/{{PKG}}/main.ts.tmpl +0 -347
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/foundryMain.ts.tmpl +0 -422
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/main.ts.tmpl +0 -196
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
* trusted via `bag x402 trust` (`[payments.x402.merchants.*]` — e.g. the
|
|
7
7
|
* Binance Bazaar shelf: CMC market data).
|
|
8
8
|
*
|
|
9
|
-
* Wire into the agent (main.ts / mcpMain.ts
|
|
10
|
-
*
|
|
9
|
+
* Wire into the agent (main.ts / mcpMain.ts on agentcore; foundryMain.ts on
|
|
10
|
+
* azure-foundry — each carries this hint on its `tools:` line) — signing
|
|
11
|
+
* stays fixed code, these tools only *fetch with payment* inside the config
|
|
12
|
+
* fences:
|
|
11
13
|
*
|
|
12
14
|
* import { X402_BUYER_TOOLS } from "./x402Buyer.js";
|
|
13
15
|
* ...
|
|
@@ -150,8 +152,8 @@ export const X402_BUYER_TOOLS: ToolSet = {
|
|
|
150
152
|
// only produces text.
|
|
151
153
|
//
|
|
152
154
|
// In `sellerCore.ts` `doWorkAndSubmit` (you own that file) — or the
|
|
153
|
-
// azure-foundry `
|
|
154
|
-
// pre-fetch:
|
|
155
|
+
// azure-foundry equivalent, `foundryMain.ts` `buildRunWork` — replace the
|
|
156
|
+
// generic prompt with a pre-fetch:
|
|
155
157
|
//
|
|
156
158
|
// import { buyWithX402 } from "./x402Buyer.js";
|
|
157
159
|
//
|
|
@@ -11,7 +11,7 @@ Invoked as `/bnbagent-studio <ask>`? Treat `<ask>` as the user's intent and rout
|
|
|
11
11
|
|
|
12
12
|
## The single seller runtime model (the invariants)
|
|
13
13
|
|
|
14
|
-
One deployed runtime, one signer: a single valuable Agent serves the selected faces (A2A `src/
|
|
14
|
+
One deployed runtime, one signer: a single valuable Agent serves the selected faces (A2A `src/unifiedMain.ts` on `:9000`, MCP `src/mcpMain.ts` on `:8000/mcp`, or A2A-native `src/dualMain.ts` on `:9000` with tunneled `/mcp`), holds the key, and signs in-process. The ERC-8183 rail exposes exactly two bounded operations - **`negotiate`** (rule-based price clamp + EIP-191 sign; **no LLM touches money**) and **`notify_funded`** (verify the funded job → produce the deliverable → submit on-chain; A2A acks then delivers in the background, MCP delivers synchronously in the tool call). The optional x402 rail adds an anonymous HTTP request at `/x402`; positive prices settle through B402 before work, while explicit zero is FREE passthrough and bypasses the facilitator. It does not expose a general signing tool. Read-only chain tools remain available. ALL signing is fixed entrypoint code in `app/agent/src/signing.ts` or the runtime's bounded x402 payment handler, never an LLM-callable tool. The encrypted keystore lives at the workspace root `.studio/wallets/`, outside the deploy codeLocation, and is injected only via the selected provider's delegated secret channel. `settle` is manual (`bag erc8183 settle`). Full layout and lifecycle details live in the references below - read them before acting.
|
|
15
15
|
|
|
16
16
|
## Decision tree - which reference to read next
|
|
17
17
|
|
|
@@ -53,7 +53,7 @@ Treat ERC-8183 amounts as decimal strings at CLI/config boundaries and `bigint`
|
|
|
53
53
|
|
|
54
54
|
## CLI groups at a glance
|
|
55
55
|
|
|
56
|
-
`init`, `scan`, `recipe`, `skills`, `wallet`, `erc8004`, `erc8183`, `x402`, `agents`, `config`, `env`, `dev`, `doctor`, `audit`, `deploy`, `platform`, `llm`, `bundle`, `budget` - see `bag --help` for details. `bag deploy [--provider bnb\|aws] [--backend aws\|azure]` is the primary deploy command; `--backend` is valid only for provider `bnb` and confirms the recipe-derived managed backend. `prepare`, `verify`, `status`, `info`, `destroy`, `logs`, and `fix-gitignore` remain lifecycle subcommands (`deploy agent` is a deprecated compatibility alias). Provider deploy/status/logs/destroy and deploy-time credential validation are delegated to pinned `@bnbagent/deploy-cli@0.5.
|
|
56
|
+
`init`, `scan`, `recipe`, `skills`, `wallet`, `erc8004`, `erc8183`, `x402`, `agents`, `config`, `env`, `dev`, `doctor`, `audit`, `deploy`, `platform`, `llm`, `bundle`, `budget` - see `bag --help` for details. `bag deploy [--provider bnb\|aws] [--backend aws\|azure]` is the primary deploy command; `--backend` is valid only for provider `bnb` and confirms the recipe-derived managed backend. `prepare`, `verify`, `status`, `info`, `destroy`, `logs`, and `fix-gitignore` remain lifecycle subcommands (`deploy agent` is a deprecated compatibility alias). Provider deploy/status/logs/destroy and deploy-time credential validation are delegated to pinned `@bnbagent/deploy-cli@0.5.10`.
|
|
57
57
|
|
|
58
58
|
## Tool surface
|
|
59
59
|
|
|
@@ -55,7 +55,7 @@ bag scan
|
|
|
55
55
|
|
|
56
56
|
## Step 3 - Emit the agent
|
|
57
57
|
|
|
58
|
-
A seller is the single Agent serving the selected protocol. `bag init` already composes it from recipes - `agent` (the fixed-code `src/signing.ts`) and `runtimes/agentcore` (A2A: `src/
|
|
58
|
+
A seller is the single Agent serving the selected protocol. `bag init` already composes it from recipes - `agent` (the fixed-code `src/signing.ts`) and `runtimes/agentcore` (A2A: `src/unifiedMain.ts` (the express + A2A entrypoint, one code set for both deploy clouds) + `src/sellerCore.ts` (the protocol-neutral core; executor inherits it) + `src/executor.ts` + `src/agentCard.ts`; MCP: `src/mcpMain.ts`; shared `src/tools.ts` + `src/model.ts`; a `Dockerfile` is added only for a container deployment path such as TWAK). Use `bag recipe code` to inspect or re-emit a piece:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
bag recipe code agent > /dev/null # inspect; bag init writes app/agent/src/signing.ts for you
|
|
@@ -57,7 +57,7 @@ Hard-stop cases: a reviewed merchant whose live payTo drifts from the studio pin
|
|
|
57
57
|
bag recipe code x402-buyer # emits src/x402Buyer.ts (next to src/tools.ts)
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
Then wire the set into the agent's LLM in `src/
|
|
60
|
+
Then wire the set into the agent's LLM in `src/unifiedMain.ts` (A2A — one entrypoint deployed to both clouds) or `src/mcpMain.ts` (MCP) - the scaffold's `tools:` line carries this exact hint in a comment:
|
|
61
61
|
|
|
62
62
|
```ts
|
|
63
63
|
import { X402_BUYER_TOOLS } from "./x402Buyer.js";
|
|
@@ -7,7 +7,7 @@ description: When the user wants to run, debug, diagnose, or operate a bnbagent-
|
|
|
7
7
|
|
|
8
8
|
# bnbagent-studio-operating
|
|
9
9
|
|
|
10
|
-
Procedure for **day-to-day operation** of an existing bnbagent-studio project. Audience: Claude Code in a current TypeScript workspace (thin root with the `agentcore/` config dir + the workspace-root `.studio/wallets/` + funded wallet, plus the single sub-project `app/agent/` holding `studio.toml` + `src/
|
|
10
|
+
Procedure for **day-to-day operation** of an existing bnbagent-studio project. Audience: Claude Code in a current TypeScript workspace (thin root with the `agentcore/` config dir + the workspace-root `.studio/wallets/` + funded wallet, plus the single sub-project `app/agent/` holding `studio.toml` + `src/unifiedMain.ts` / `src/sellerCore.ts` / `src/executor.ts` / `src/agentCard.ts` / `src/signing.ts`). Most commands run from the workspace root; the current project-root fallback locates the `app/agent/` sub-project automatically for wallet/LLM/budget ops.
|
|
11
11
|
|
|
12
12
|
**Different from**:
|
|
13
13
|
|
|
@@ -63,7 +63,7 @@ bag dev # A2A agent on :9000, MCP on :8000/mcp (same for any dest
|
|
|
63
63
|
bag dev --port 9100 # override the port
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
`bag dev` sets `STORAGE_LOCAL_PATH=~/.bag/deliverables/<workspace-name>/` for the agent subprocess and runs it **without** Cognito env, so the local endpoint is reachable without a token (Cognito is mandatory only on the deployed AgentCore runtime). For A2A projects, the agent (`app/agent/src/
|
|
66
|
+
`bag dev` sets `STORAGE_LOCAL_PATH=~/.bag/deliverables/<workspace-name>/` for the agent subprocess and runs it **without** Cognito env, so the local endpoint is reachable without a token (Cognito is mandatory only on the deployed AgentCore runtime). For A2A projects, the agent (`app/agent/src/unifiedMain.ts`) exposes `/.well-known/agent-card.json` + JSON-RPC `message/send` + `GET /ping` on `:9000`. Smoke-test the card with curl:
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
curl http://localhost:9000/.well-known/agent-card.json
|
|
@@ -106,12 +106,12 @@ Returns a rich table of checks across the `app/agent/` sub-project:
|
|
|
106
106
|
| Check | What FAIL means | Fix |
|
|
107
107
|
| --- | --- | --- |
|
|
108
108
|
| app/agent/studio.toml parseable | Missing or syntax error | `bag init` to regenerate, or hand-fix TOML |
|
|
109
|
-
| Agent entrypoint imports | Protocol entrypoint raises on load (`src/
|
|
109
|
+
| Agent entrypoint imports | Protocol entrypoint raises on load (`src/unifiedMain.ts` for A2A, `src/mcpMain.ts` for MCP) | Fix the error printed by the check (often a missing env var or a broken import) |
|
|
110
110
|
| wallet keystore | No `<workspace>/.studio/wallets/*.json` | `bag wallet new` (writes to the workspace root keystore dir) |
|
|
111
111
|
| WALLET_PASSWORD env | Not in `.studio/.env.local` / not exported | Edit the owner-only `.studio/.env.local`; never pass the password on argv |
|
|
112
112
|
| LLM provider key | API key env not set | Edit `.studio/.env.local` or export the right `*_API_KEY` |
|
|
113
113
|
| Network reachable | RPC down/wrong URL | Override via `STUDIO_BSC_TESTNET_RPC=...` (testnet) / `STUDIO_BSC_RPC=...` (mainnet) - per-network env vars read by `@bnbagent/studio-runtime/networks` `getNetwork` |
|
|
114
|
-
| Wallet tBNB balance | 0 tBNB |
|
|
114
|
+
| Wallet tBNB balance | 0 tBNB | Message https://t.me/bnbchain_official_bot: `I would like to get tBNB to my wallet <address>` (more options: docs.bnbchain.org/bnb-smart-chain/developers/faucet/) |
|
|
115
115
|
| Wallet U balance | 0 U | Transfer from holder, or ask for sponsor U |
|
|
116
116
|
| 8004 registered | Not registered | Normally registered automatically at `bag deploy verify`. Manual: `bag erc8004 register --endpoint <url>` (only if you need an on-chain identity before deploy). WARN-only in `bag doctor` - it doesn't block local dev. |
|
|
117
117
|
|
|
@@ -36,7 +36,7 @@ Procedure for **greenfield** seller creation. Audience: Claude Code (or another
|
|
|
36
36
|
├── tsconfig.json builds src/ → dist/ (the deployed entrypoint is dist/*.js)
|
|
37
37
|
├── studio.toml wallet / llm / budget / payments.erc8183 bounds / payments.x402 / storage
|
|
38
38
|
├── Dockerfile container deploy path only (for example, twak)
|
|
39
|
-
└── src/
|
|
39
|
+
└── src/ unifiedMain.ts / mcpMain.ts / dualMain.ts / sellerCore.ts / executor.ts / agentCard.ts / signing.ts / tools.ts / model.ts
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
- **The Agent (`<name>/app/agent/`, → AWS Bedrock AgentCore).** ONE valuable agent (memory / tools / skills / KB / LLM) that **serves the selected faces** (A2A: `0.0.0.0:9000`; MCP: `0.0.0.0:8000/mcp`; A2A+MCP: A2A-native `dualMain.ts` on `:9000` with `/mcp` tunneled), holds the key, and signs **in-process**. Its outward surface is two fixed-code commerce operations (A2A skills on `SellerAgentExecutor`, or MCP tools on the MCP server):
|
|
@@ -112,7 +112,7 @@ Will create (single seller agent):
|
|
|
112
112
|
(twak is fully supported too - opt in with --wallet-kind twak)
|
|
113
113
|
storage: local (offline dev; switch to ipfs - needs a pinning endpoint + key - before deploy)
|
|
114
114
|
fixed: stack=AI SDK (model factory + tools), runtime=agentcore (AWS Bedrock AgentCore)
|
|
115
|
-
protocol: A2A (src/
|
|
115
|
+
protocol: A2A (src/unifiedMain.ts on 0.0.0.0:9000; local 127.0.0.1:9000)
|
|
116
116
|
or MCP (src/mcpMain.ts on 0.0.0.0:8000/mcp; local localhost:8000/mcp)
|
|
117
117
|
destination: platform (campaign default while the trial runs - 48h testnet on the operator's AWS; key leaves your machine, use a throwaway wallet)
|
|
118
118
|
or self (prepare for your own AWS Bedrock AgentCore; runtime material stays in your account - pass --destination self)
|
|
@@ -138,7 +138,7 @@ Build a TodoWrite list. The shape depends on the `wallet kind`. The canonical 8-
|
|
|
138
138
|
|
|
139
139
|
> **Onboarding note.** On a human TTY, `bag init` runs steps 3, 4 and 6 automatically (it prompts once for the wallet password, runs `bag wallet new`, zero-deposit-activates Pieverse, and prints faucet URLs). **You (Claude Code) drive `bag init` non-interactively**, so that auto-flow does NOT fire - keep steps 3/4/6 below. Pass `--no-onboard` to `bag init` to make this explicit and deterministic regardless of how the shell wires stdin.
|
|
140
140
|
|
|
141
|
-
1. `bag init <name> --llm-provider <p> --network <n> --storage-provider <s> --wallet-kind <k> --rails <8183|b402|both> [--erc8183-price <base-units>] [--b402-price <usd>] --no-onboard` - scaffold the current workspace. **`<name>` must start with a letter, use ASCII letters and digits only, and be at most 23 characters.** `bag init` rejects `-`, `_`, `.`, and overlong names instead of renaming them. Pass `--wallet-kind evm-local` (default) or `--wallet-kind twak` (twak is fully supported - pass the flag to opt in), and `--storage-provider local` (default) or `ipfs`, per the Stage-1 choices; for twak, add `--twak-home <path>` ONLY if the user wants to reuse an existing wallet (otherwise omit - a project-dedicated `.studio/twak` is the safe default). add `--protocols <comma-list>` when the user chose non-default or multiple faces (omit for A2A default; `--protocol <one>` is only a legacy alias), add `--model <m>` only if the user overrode the provider default, and `--enable-auto-topup` / `--no-auto-topup` only if they made an explicit choice (otherwise omit - consent stays deferred). Pass `--erc8183-price 0` only when the user explicitly chose FREE; omitting the flag preserves the paid 0.1 U default. The canonical stack supports FREE; if a custom deployment is selected, set all three `ERC8183_COMMERCE_ADDRESS`, `ERC8183_ROUTER_ADDRESS`, and `ERC8183_POLICY_ADDRESS` values from that same stack. For B402, pass `--b402-price 0` only after the user explicitly accepts an unrestricted anonymous FREE `/x402` endpoint. FREE bypasses B402 verify/settle and needs no merchant credentials; a positive price keeps the `$0.01` default and requires the paid onboarding playbook. **Destination:** while the trial campaign runs, bare `bag init` (no `--destination`) defaults to `platform` - so pass `--destination self` **explicitly** whenever the user chose their own AWS, otherwise studio.toml silently records `platform` and the confirmation block you echoed no longer matches what was written. Omit `--destination` only when the user actually wants the `platform` 48h testnet trial (the campaign default) - do NOT treat that default as a mistake or re-confirm it; it is the intended behavior while the campaign is open. (Bare init also resolves to `self` once the campaign ends, or when `--network bsc-mainnet` / a non-agentcore `--runtime` is passed.) On the `platform` path `bag init` hard-forces `bsc-testnet`, pins `--runtime agentcore` + packages an artifact (a zip for the default evm-local and altana wallets, a container for twak). For evm-local a wallet key will later leave your machine, so pair it with a throwaway `bag wallet new`; for altana only the bounded session ships - do NOT create a new wallet (full flow: `docs/guides/platform-deploy.md`). Defaults `--runtime agentcore` (the only advertised runtime; the Preview `azure-foundry` runtime remains explicitly selectable but is outside this playbook; there is no `--framework` flag because the AI SDK model/tools story is part of the runtime templates). Creates `<name>/` workspace root + `<name>/app/agent/` (the single sub-project: A2A emits `src/
|
|
141
|
+
1. `bag init <name> --llm-provider <p> --network <n> --storage-provider <s> --wallet-kind <k> --rails <8183|b402|both> [--erc8183-price <base-units>] [--b402-price <usd>] --no-onboard` - scaffold the current workspace. **`<name>` must start with a letter, use ASCII letters and digits only, and be at most 23 characters.** `bag init` rejects `-`, `_`, `.`, and overlong names instead of renaming them. Pass `--wallet-kind evm-local` (default) or `--wallet-kind twak` (twak is fully supported - pass the flag to opt in), and `--storage-provider local` (default) or `ipfs`, per the Stage-1 choices; for twak, add `--twak-home <path>` ONLY if the user wants to reuse an existing wallet (otherwise omit - a project-dedicated `.studio/twak` is the safe default). add `--protocols <comma-list>` when the user chose non-default or multiple faces (omit for A2A default; `--protocol <one>` is only a legacy alias), add `--model <m>` only if the user overrode the provider default, and `--enable-auto-topup` / `--no-auto-topup` only if they made an explicit choice (otherwise omit - consent stays deferred). Pass `--erc8183-price 0` only when the user explicitly chose FREE; omitting the flag preserves the paid 0.1 U default. The canonical stack supports FREE; if a custom deployment is selected, set all three `ERC8183_COMMERCE_ADDRESS`, `ERC8183_ROUTER_ADDRESS`, and `ERC8183_POLICY_ADDRESS` values from that same stack. For B402, pass `--b402-price 0` only after the user explicitly accepts an unrestricted anonymous FREE `/x402` endpoint. FREE bypasses B402 verify/settle and needs no merchant credentials; a positive price keeps the `$0.01` default and requires the paid onboarding playbook. **Destination:** while the trial campaign runs, bare `bag init` (no `--destination`) defaults to `platform` - so pass `--destination self` **explicitly** whenever the user chose their own AWS, otherwise studio.toml silently records `platform` and the confirmation block you echoed no longer matches what was written. Omit `--destination` only when the user actually wants the `platform` 48h testnet trial (the campaign default) - do NOT treat that default as a mistake or re-confirm it; it is the intended behavior while the campaign is open. (Bare init also resolves to `self` once the campaign ends, or when `--network bsc-mainnet` / a non-agentcore `--runtime` is passed.) On the `platform` path `bag init` hard-forces `bsc-testnet`, pins `--runtime agentcore` + packages an artifact (a zip for the default evm-local and altana wallets, a container for twak). For evm-local a wallet key will later leave your machine, so pair it with a throwaway `bag wallet new`; for altana only the bounded session ships - do NOT create a new wallet (full flow: `docs/guides/platform-deploy.md`). Defaults `--runtime agentcore` (the only advertised runtime; the Preview `azure-foundry` runtime remains explicitly selectable but is outside this playbook; there is no `--framework` flag because the AI SDK model/tools story is part of the runtime templates). Creates `<name>/` workspace root + `<name>/app/agent/` (the single sub-project: A2A emits `src/unifiedMain.ts` (the express + A2A entrypoint, one code set for both deploy clouds) + `src/sellerCore.ts` (the protocol-neutral core; executor inherits it) + `src/executor.ts` + `src/agentCard.ts`; MCP emits `src/mcpMain.ts`; both include `src/signing.ts` + `src/tools.ts` + `src/model.ts` + their own `studio.toml` + `package.json` + `tsconfig.json`) + `<name>/agentcore/` (`agentcore.json` + `aws-targets.json`, self-rendered - no agentcore CLI needed at init). The workspace root holds the `agentcore/` deploy descriptor, the `.studio/wallets/` keystore, a thin `package.json` + `pnpm-workspace.yaml`, README, and `.gitignore`. (v1 is seller-only - no `--role`.)
|
|
142
142
|
> **Altana + custom contracts:** Altana sessions remain bound to the canonical ERC-8183 targets. Use `evm-local` for a custom Commerce/Router/Policy stack; doctor and deploy readiness reject this unsupported combination when the ERC-8183 rail is active.
|
|
143
143
|
|
|
144
144
|
2. `cd <name>`, then make sure the dependencies are installed. `bag init` already runs the install by default (skip only if it was scaffolded with `--no-install`); the manual equivalent from the workspace root is:
|
|
@@ -186,7 +186,7 @@ Build a TodoWrite list. The shape depends on the `wallet kind`. The canonical 8-
|
|
|
186
186
|
- **evm-local** (default): `bag wallet new` creates the encrypted keystore. To import an existing key, the user pastes it and you immediately run `bag wallet new --private-key <pk>` (the key is written only into the keystore, nowhere else on disk).
|
|
187
187
|
|
|
188
188
|
5. **Fund the wallet - OPTIONAL; do NOT block on it.** The default `auto/free` LLM model runs at $0 and AgentCore deploy consumes no wallet balance, so a brand-new seller can scaffold, run `bag dev`, and deploy with an empty wallet. `bag doctor` and `bag deploy` only **WARN** (never block) on zero balance. Funding is needed later only for: a paid LLM model, on-chain settle, paying positive-price ERC-8183 job buys, or buying/smoking a PAID B402 request. A FREE ERC-8183 buy needs no U escrow or ERC-20 approval, but still needs the ERC-8183 state-changing calls and their gas/paymaster path. A FREE B402/x402 request needs neither token funding nor a facilitator call. When funding is needed, the wallet uses **TWO distinct U balances on TWO chains** (same wallet address, same private key, different chains):
|
|
189
|
-
- **tBNB (gas)** on BSC testnet: https://
|
|
189
|
+
- **tBNB (gas)** on BSC testnet: message the official Telegram bot https://t.me/bnbchain_official_bot with `I would like to get tBNB to my wallet <address>` (up to 0.3 tBNB/day; replies with the tx hash). More options: https://docs.bnbchain.org/bnb-smart-chain/developers/faucet/
|
|
190
190
|
- **BSC mainnet U** (`0xcE24439F2D9C6a2289F741120FE202248B666666`) - for Pieverse LLM topup. Minimum **0.2 U** recommended (0.1 for activate + slack). Pieverse runs **only on mainnet chainId=56**; testnet U cannot pay for LLM credits. Acquire via PancakeSwap. Wallet-funded renewal stays off unless the operator explicitly enables it with `bag budget enable`; otherwise refill Account Balance with `bag llm topup` as needed.
|
|
191
191
|
- **BSC testnet U** (`0xc70B8741B8B07A6d61E54fd4B20f22Fa648E5565`) - for ERC-8183 job payments if `[network].default = bsc-testnet`. Faucet: https://united-coin-u.github.io/u-faucet/ (paste the agent wallet address; see also `docs/guides/U-token-testnet.md`).
|
|
192
192
|
|
|
@@ -33,7 +33,7 @@ A v1 seller ships as **one runtime**: a single valuable Agent on AWS Bedrock Age
|
|
|
33
33
|
|
|
34
34
|
- `bag doctor` is clean (or only warns on optional checks) - run from workspace root
|
|
35
35
|
- Wallet has ≥ 0.05 tBNB (gas to submit deliverables) and ≥ 0 U (sellers receive U, don't spend)
|
|
36
|
-
- The agent sub-project is emitted (`<workspace>/app/agent/src/
|
|
36
|
+
- The agent sub-project is emitted (`<workspace>/app/agent/src/unifiedMain.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.
|
|
37
37
|
- For LLM-using sellers: `[llm].provider` configured in `app/agent/studio.toml` + (if Pieverse) `bag llm activate` has been run
|
|
38
38
|
|
|
39
39
|
## Stage 1 - Implement the `notify_funded` value (the `runWork` hook)
|
|
@@ -73,9 +73,9 @@ Tune the clamp in `<workspace>/app/agent/studio.toml`:
|
|
|
73
73
|
# app/agent/studio.toml
|
|
74
74
|
[payments.erc8183]
|
|
75
75
|
currency = "0x..." # $U token - prefilled by `bag init` from [network].default; rarely changed
|
|
76
|
-
price = "
|
|
76
|
+
price = "100000000000000000" # raw wei - the asking list price the quote signs (scaffold default 0.1 U; U has 18 decimals)
|
|
77
77
|
min_price = "0" # raw wei - clamp floor
|
|
78
|
-
max_price = "
|
|
78
|
+
max_price = "" # raw wei - clamp ceiling; empty = unbounded (a "0" ceiling clamps every paid quote to 0)
|
|
79
79
|
quote_ttl_seconds = 300
|
|
80
80
|
default_estimated_completion_seconds = 600
|
|
81
81
|
```
|
|
@@ -152,7 +152,7 @@ bag deploy --provider bnb # managed platform
|
|
|
152
152
|
bag deploy --provider aws # self-hosted AgentCore
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
On the managed platform the deploy summary must say `x402 rail is ACTIVE` (or `ACTIVE in FREE mode`) and print the anonymous `/x402` URL. On a self-hosted AgentCore deploy it says `x402 rail is ACTIVE (self-hosted AgentCore)` or `ACTIVE in FREE mode (self-hosted AgentCore)
|
|
155
|
+
On the managed platform the deploy summary must say `x402 rail is ACTIVE` (or `ACTIVE in FREE mode`) and print the anonymous `/x402` URL. On a self-hosted AgentCore deploy it says `x402 rail is ACTIVE (self-hosted AgentCore)` or `ACTIVE in FREE mode (self-hosted AgentCore)` (self-hosted Azure Foundry prints the same summary with its own label): the rail runs in-process, but there is no anonymous URL. Operate your own HTTP front that relays envelope-v1 JSON through an authenticated AgentCore invocation. The default Bag self-deploy uses Cognito OAuth over raw HTTPS; AWS SDK/SigV4 is only for a runtime deliberately configured with IAM authorization. PAID also needs your own fixed-egress B402 Relay or equivalent network path. The complete gateway wrapper, response parser, Relay example, and direct-invocation fallback are in the [self-hosted x402 gateway guide](https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway.md). A dormant or forced-dormant summary means the rail was not activated; fix the named credential, runtime, network, or tunnel condition and redeploy.
|
|
156
156
|
|
|
157
157
|
## Hard rules
|
|
158
158
|
|
|
@@ -161,6 +161,6 @@ On the managed platform the deploy summary must say `x402 rail is ACTIVE` (or `A
|
|
|
161
161
|
- Never replay a paid HTTP request whose outcome is unknown. Follow `docs/guides/x402-selling.md` and reconcile `(nonce, network, payer)` first.
|
|
162
162
|
- Binance `/settle` is asynchronous. A parseable `success: false` response with a transaction is pending and requires an idempotent poll with the same settlement payload. Studio v0.0.6 and the latest `@bnb-chain/b402@0.1.0` do not yet perform that poll; they classify pending as unknown. Do not claim current mainnet readiness until this is updated.
|
|
163
163
|
- Settlement happens before work. A later work failure retains the payment and does not trigger an automatic refund.
|
|
164
|
-
- The rail activates on AgentCore targets
|
|
164
|
+
- The rail activates on AgentCore and Azure Foundry targets (managed platform or self-hosted); self-hosted targets have no anonymous URL and need an operator-run envelope-v1 front.
|
|
165
165
|
- PAID B402 payout wallets must use `wallet.kind` `evm-local` (private-key wallet stored as an encrypted keystore) or `twak`. Altana is unsupported. FREE x402 bypasses B402, has no payout, and must not be described as Altana B402 support.
|
|
166
166
|
- Never describe FREE as a zero-value B402 settlement. It bypasses B402.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bnbagent-studio-use-aws-agentcore
|
|
3
|
-
description: When the user wants to deploy or operate a bnbagent-studio project on AWS Bedrock AgentCore - deploy with `bag deploy --provider aws` (all cloud lifecycle mutations are delegated to pinned `@bnbagent/deploy-cli@0.5.
|
|
3
|
+
description: When the user wants to deploy or operate a bnbagent-studio project on AWS Bedrock AgentCore - deploy with `bag deploy --provider aws` (all cloud lifecycle mutations are delegated to pinned `@bnbagent/deploy-cli@0.5.10`), inspect with `bag deploy status` / `logs --provider aws` / `verify --provider aws`, and tear down with `bag deploy destroy --provider aws --execute [--purge]`. Also covers AWS credential prerequisites, the optional read-only quota probe, and the runtime-secret channel.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bnbagent-studio-use-azure-foundry
|
|
3
|
-
description: When the user wants to deploy or operate a bnbagent-studio project on Azure AI Foundry Hosted Agents - scaffold with `bag init --runtime azure-foundry`, deploy either to the managed platform with `bag deploy --provider bnb --backend azure` or directly with `bag deploy --provider azure`; all cloud lifecycle execution is delegated to pinned `@bnbagent/deploy-cli@0.5.
|
|
3
|
+
description: When the user wants to deploy or operate a bnbagent-studio project on Azure AI Foundry Hosted Agents - scaffold with `bag init --runtime azure-foundry`, deploy either to the managed platform with `bag deploy --provider bnb --backend azure` or directly with `bag deploy --provider azure`; all cloud lifecycle execution is delegated to pinned `@bnbagent/deploy-cli@0.5.10`. Native MCP is not supported on Azure; use AgentCore for MCP.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
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.
|
|
@@ -13,7 +13,7 @@ Procedure for deploying and operating the seller Agent on **Azure AI Foundry Hos
|
|
|
13
13
|
|
|
14
14
|
```
|
|
15
15
|
<workspace>/
|
|
16
|
-
├── app/agent/ # the deployed code (src/
|
|
16
|
+
├── app/agent/ # the deployed code (src/unifiedMain.ts host + Dockerfile here)
|
|
17
17
|
│ ├── studio.toml # [azure] block: location / account_name / subdomain / project_name / agent_endpoint
|
|
18
18
|
│ └── Dockerfile # the container image bnbagent-deploy builds + pushes
|
|
19
19
|
└── .studio/ # secrets + wallets (workspace root - never in the image)
|
|
@@ -47,7 +47,7 @@ Procedure for deploying and operating the seller Agent on **Azure AI Foundry Hos
|
|
|
47
47
|
|
|
48
48
|
> The encrypted keystore (`.studio/wallets/`) stays at the workspace root and rides only that secret channel - never baked into the image.
|
|
49
49
|
|
|
50
|
-
Provider-native overrides go in the optional `studio.toml [deploy.foundry]` table (verbatim deploy-spec keys; deploy-cli 0.5.
|
|
50
|
+
Provider-native overrides go in the optional `studio.toml [deploy.foundry]` table (verbatim deploy-spec keys; deploy-cli 0.5.10 consumes `account`, `cpu`, `location`, `memory`, `project`, `projectEndpoint`, `protocol`, `registry` and warns about anything else). The `[azure]` block's `account_name` / `project_name` / `project_endpoint` / `location` win over conflicting `[deploy.foundry]` keys.
|
|
51
51
|
|
|
52
52
|
## Typical workflow
|
|
53
53
|
|
|
@@ -9,7 +9,7 @@ description: Use when deploying or operating a bnbagent-studio seller on the BNB
|
|
|
9
9
|
|
|
10
10
|
Treat this provider as a temporary testnet sandbox. Require a throwaway wallet, keep `bsc-testnet`, and explain that the runtime signing material is transmitted to the operator's managed secret store for the trial. Never use a mainnet key. Exception: `wallet.kind='altana'` ships only the bounded, budget-limited, revocable session - the throwaway-wallet advice does not apply; tighten the session instead (`bag wallet session grant --force --budget-u <small> --expiry-days <short>`) and never run `bag wallet new` on an altana project (it breaks the session's `[wallet].address` anchor).
|
|
11
11
|
|
|
12
|
-
All auth and cloud lifecycle work must cross the pinned `@bnbagent/deploy-cli@0.5.
|
|
12
|
+
All auth and cloud lifecycle work must cross the pinned `@bnbagent/deploy-cli@0.5.10` boundary. Do not call a cloud CLI or platform REST routes directly. The managed backend is recipe-derived: `agentcore` uses AWS; `azure-foundry` uses Azure. For headless managed Azure, confirm with `bag deploy --provider bnb --backend azure --yes`; never treat `--backend` as a cross-cloud recipe converter.
|
|
13
13
|
|
|
14
14
|
## Select and authenticate
|
|
15
15
|
|
|
@@ -81,7 +81,7 @@ Note: `pieverseUsage` does a SIWE EIP-191 personal_sign (no on-chain effect, dom
|
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
83
|
# from workspace root:
|
|
84
|
-
ls app/agent/studio.toml && (ls app/agent/src/
|
|
84
|
+
ls app/agent/studio.toml && (ls app/agent/src/unifiedMain.ts 2>/dev/null || ls app/agent/src/mcpMain.ts)
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
If `app/agent/src/tools.ts` already exists, the user has the AI SDK form already. Skip to Step 4 if they want to customize.
|
|
@@ -90,7 +90,7 @@ If `app/agent/src/tools.ts` already exists, the user has the AI SDK form already
|
|
|
90
90
|
|
|
91
91
|
## Step 2 - Identify the stack
|
|
92
92
|
|
|
93
|
-
Look at `app/agent/src/
|
|
93
|
+
Look at `app/agent/src/unifiedMain.ts` or `app/agent/src/mcpMain.ts` imports / `app/agent/package.json`:
|
|
94
94
|
|
|
95
95
|
| Sign in code | Stack |
|
|
96
96
|
| --- | --- |
|
|
@@ -105,7 +105,7 @@ If AI SDK: emit the stock recipe and stop.
|
|
|
105
105
|
bag recipe code tools-chain > app/agent/src/chainTools.ts # full inventory + CHAIN_READ_TOOLS
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
Then in the protocol entrypoint (`app/agent/src/
|
|
108
|
+
Then in the protocol entrypoint (`app/agent/src/unifiedMain.ts` for A2A, `app/agent/src/mcpMain.ts` for MCP), pass the tool set into the generate call:
|
|
109
109
|
|
|
110
110
|
```ts
|
|
111
111
|
import { LLM_READ_TOOLS } from "./tools.js";
|
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single A2A seller agent entrypoint. Generated by `bag recipe code agent`.
|
|
3
|
-
*
|
|
4
|
-
* This is the VALUABLE agent AND the SOLE key-holder/signer. It serves its
|
|
5
|
-
* two ERC-8183 seller skills DIRECTLY over the A2A protocol on AWS Bedrock
|
|
6
|
-
* AgentCore (`--protocol A2A`): an `@a2a-js/sdk` express app exposes the
|
|
7
|
-
* agent card at `/.well-known/agent-card.json` + JSON-RPC `message/send` on
|
|
8
|
-
* `0.0.0.0:9000` (`AGENT_PORT` overrides locally), plus `GET /ping` for the
|
|
9
|
-
* AgentCore liveness contract. There is no separate forwarding service — the
|
|
10
|
-
* agent IS the seller (see executor.ts + agentCard.ts).
|
|
11
|
-
*
|
|
12
|
-
* A2A skills (executor.ts):
|
|
13
|
-
*
|
|
14
|
-
* negotiate → read the FIXED list price → CLAMP to [min,max] → EIP-191 SIGN
|
|
15
|
-
* the offer (no LLM, no tools) → return the signed offer (or reject)
|
|
16
|
-
* notify_funded → re-verify the funded job on-chain (fast) → ACK accepted at once,
|
|
17
|
-
* then in the BACKGROUND: LLM work → manifest → storage →
|
|
18
|
-
* submitResult (SIGN + broadcast). The buyer polls the chain for
|
|
19
|
-
* the deliverable. Each notify also sweeps other FUNDED jobs
|
|
20
|
-
* (buyer-push fallback). While background work is in flight the
|
|
21
|
-
* `/ping` handler reports HEALTHY_BUSY so AgentCore keeps the
|
|
22
|
-
* scale-to-zero runtime warm until it lands.
|
|
23
|
-
*
|
|
24
|
-
* Buyers reach this endpoint with an OAuth2 (Cognito) bearer — AgentCore A2A
|
|
25
|
-
* mandates inbound auth (see agentCard.ts + `bag deploy provision-cognito`).
|
|
26
|
-
*
|
|
27
|
-
* ## Boundaries (do NOT cross — they are the whole point)
|
|
28
|
-
*
|
|
29
|
-
* - The agent does ALL deterministic SIGNING (quote-sign + submit + settle +
|
|
30
|
-
* automatic Pieverse LLM-credit auto-renew). ALL signing is FIXED code in
|
|
31
|
-
* `signing.ts` — NEVER an LLM-callable tool (money never in the LLM).
|
|
32
|
-
* - The price is a FIXED list price from studio.toml (clamped before
|
|
33
|
-
* signing) — the LLM never prices; it only PRODUCES the work text in the
|
|
34
|
-
* delivery step.
|
|
35
|
-
* - Chain access for the LLM is READ-ONLY tools only (`tools.ts`).
|
|
36
|
-
* - `settle` (claim payment after the dispute window) is operator-driven —
|
|
37
|
-
* run `bag erc8183 settle <job_id>`; it is deliberately NOT an A2A skill.
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
import { createHash } from "node:crypto";
|
|
41
|
-
import { pathToFileURL } from "node:url";
|
|
42
|
-
import {
|
|
43
|
-
GetSecretValueCommand,
|
|
44
|
-
SecretsManagerClient,
|
|
45
|
-
} from "@aws-sdk/client-secrets-manager";
|
|
46
|
-
import { DefaultRequestHandler, InMemoryTaskStore } from "@a2a-js/sdk/server";
|
|
47
|
-
import {
|
|
48
|
-
agentCardHandler,
|
|
49
|
-
jsonRpcHandler,
|
|
50
|
-
UserBuilder,
|
|
51
|
-
} from "@a2a-js/sdk/server/express";
|
|
52
|
-
import {
|
|
53
|
-
loadStudioToml,
|
|
54
|
-
type TomlTable,
|
|
55
|
-
} from "@bnbagent/studio-runtime/config";
|
|
56
|
-
import {
|
|
57
|
-
ensureAltanaSessionLoaded,
|
|
58
|
-
ensureKeystoreMaterialized,
|
|
59
|
-
ensureTwakMaterialized,
|
|
60
|
-
getWallet,
|
|
61
|
-
} from "@bnbagent/studio-runtime/wallet";
|
|
62
|
-
import {
|
|
63
|
-
createEnvelopeMiddleware,
|
|
64
|
-
X402_SELL_PATH,
|
|
65
|
-
type X402HttpRequest,
|
|
66
|
-
type X402RunWork,
|
|
67
|
-
X402Seller,
|
|
68
|
-
} from "@bnbagent/studio-runtime/x402";
|
|
69
|
-
import { generateText, stepCountIs } from "ai";
|
|
70
|
-
import express from "express";
|
|
71
|
-
import { buildAgentCard } from "./agentCard.js";
|
|
72
|
-
import { SellerAgentExecutor } from "./executor.js";
|
|
73
|
-
import { buildModel } from "./model.js";
|
|
74
|
-
import type { RunWork } from "./sellerCore.js";
|
|
75
|
-
import { LLM_READ_TOOLS } from "./tools.js";
|
|
76
|
-
|
|
77
|
-
const APP_NAME = "agent";
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Deliverable `generator` label: this seller's own name, read from
|
|
81
|
-
* studio.toml `[project].name` (minus the `-agent` suffix) so each delivered
|
|
82
|
-
* manifest is self-identifying. Best-effort — falls back to `APP_NAME` if
|
|
83
|
-
* the config can't be read.
|
|
84
|
-
*/
|
|
85
|
-
function generatorTag(): string {
|
|
86
|
-
let name = "";
|
|
87
|
-
try {
|
|
88
|
-
const cfg = loadStudioToml();
|
|
89
|
-
name = String(((cfg.project ?? {}) as Record<string, unknown>).name ?? "");
|
|
90
|
-
} catch {
|
|
91
|
-
// a metadata label must never break delivery
|
|
92
|
-
return APP_NAME;
|
|
93
|
-
}
|
|
94
|
-
return name.endsWith("-agent")
|
|
95
|
-
? name.slice(0, -"-agent".length)
|
|
96
|
-
: name || APP_NAME;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ── Runtime secrets ───────────────────────────────────────────────────────────
|
|
100
|
-
// Keep plaintext secrets OUT of agentcore.json. When BNBAGENT_RUNTIME_SECRET_ID
|
|
101
|
-
// is set (deployed runtime), pull a JSON {ENV_NAME: value} blob from AWS
|
|
102
|
-
// Secrets Manager into the process env BEFORE anything reads it (keystore
|
|
103
|
-
// unlock, provider key, buildModel, Cognito OAuth env). No-op locally, where
|
|
104
|
-
// .env.local already populated the environment. In a deployed runtime the
|
|
105
|
-
// managed secret bundle is authoritative and replaces any stale spec-level
|
|
106
|
-
// value left by an earlier runtime revision.
|
|
107
|
-
async function loadRuntimeSecrets(): Promise<void> {
|
|
108
|
-
const secretId = process.env.BNBAGENT_RUNTIME_SECRET_ID;
|
|
109
|
-
if (!secretId) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const resp = await new SecretsManagerClient({}).send(
|
|
113
|
-
new GetSecretValueCommand({ SecretId: secretId }),
|
|
114
|
-
);
|
|
115
|
-
const bundle = JSON.parse(resp.SecretString ?? "{}") as Record<
|
|
116
|
-
string,
|
|
117
|
-
unknown
|
|
118
|
-
>;
|
|
119
|
-
for (const [key, value] of Object.entries(bundle)) {
|
|
120
|
-
process.env[key] = String(value);
|
|
121
|
-
}
|
|
122
|
-
const pieverseKey = process.env.PIEVERSE_LLM_API_KEY;
|
|
123
|
-
if (pieverseKey) {
|
|
124
|
-
const fingerprint = createHash("sha256")
|
|
125
|
-
.update(pieverseKey, "utf-8")
|
|
126
|
-
.digest("hex")
|
|
127
|
-
.slice(0, 12);
|
|
128
|
-
console.info(
|
|
129
|
-
`[runtime-secrets] PIEVERSE_LLM_API_KEY source=secretsmanager sha256=${fingerprint}…`,
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** studio.toml `[network].default` (best-effort; used by the funded sweep). */
|
|
135
|
-
function defaultNetwork(): string {
|
|
136
|
-
try {
|
|
137
|
-
const cfg = loadStudioToml();
|
|
138
|
-
return String(
|
|
139
|
-
((cfg.network ?? {}) as Record<string, unknown>).default ?? "bsc-testnet",
|
|
140
|
-
);
|
|
141
|
-
} catch {
|
|
142
|
-
return "bsc-testnet";
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// ── One-shot LLM helper (the executor's delivery work hook) ──────────────────
|
|
147
|
-
// LLM credit auto-renew (Pieverse path): `buildModel()` (in model.ts) returns
|
|
148
|
-
// a model wrapped with a middleware that auto-tops up the active Pieverse key
|
|
149
|
-
// before each generate call when [llm.auto_renew] is enabled. That top-up is
|
|
150
|
-
// the ONLY automatic signing path outside signing.ts — it is budget-gated and
|
|
151
|
-
// is NOT an LLM tool. It rides transparently into the delivery step.
|
|
152
|
-
//
|
|
153
|
-
// The LLM runs only in an authorized value step: verified ERC-8183 delivery
|
|
154
|
-
// or x402 work after its payment/free gate. `negotiate` is rule-based and
|
|
155
|
-
// never touches the LLM. The read-only chain tools are
|
|
156
|
-
// attached so the work can read on-chain context if it needs to — drop them
|
|
157
|
-
// from `tools.ts` if your work doesn't read chain. Signing / settle are NEVER
|
|
158
|
-
// tools — they are fixed code in signing.ts, triggered by the A2A skills,
|
|
159
|
-
// never callable by the LLM. (The one deliberate exception: the x402-buyer
|
|
160
|
-
// recipe's PAID fetch tools — see the `tools:` note below — the LLM picks the
|
|
161
|
-
// URL, but who gets paid and the per-call/daily caps stay locked in
|
|
162
|
-
// studio.toml.)
|
|
163
|
-
export function buildRunWork(): RunWork {
|
|
164
|
-
// The model is resolved LAZILY on first delivery, not at boot: a seller
|
|
165
|
-
// with no provider key yet must still serve negotiate (which never calls
|
|
166
|
-
// the LLM) — missing-key errors surface at notify_funded delivery time.
|
|
167
|
-
let model: ReturnType<typeof buildModel> | undefined;
|
|
168
|
-
return async (prompt, { abortSignal }) => {
|
|
169
|
-
model ??= buildModel(); // managed model with the auto-renew hook (delivery only)
|
|
170
|
-
const result = await generateText({
|
|
171
|
-
model,
|
|
172
|
-
system:
|
|
173
|
-
"You are a seller agent. The runtime has already authorized this task " +
|
|
174
|
-
"through its configured commerce rail. Complete the user's task now; " +
|
|
175
|
-
"do not ask for a job ID or additional payment. " +
|
|
176
|
-
"Be concrete and concise. Use the read-only chain tools when on-chain " +
|
|
177
|
-
"context helps. If a paid-data tool such as `buy_with_x402` is available " +
|
|
178
|
-
"to you, USE IT to fetch the data a task needs — those merchants (e.g. " +
|
|
179
|
-
"CoinMarketCap) charge via on-chain wallet payment, NOT an API key; never " +
|
|
180
|
-
"reply that you cannot complete the task for lack of an API key.",
|
|
181
|
-
prompt,
|
|
182
|
-
// LLM_READ_TOOLS = read-only chain tools (wallet, balances,
|
|
183
|
-
// ERC-8004/8183 queries). Edit `tools.ts` to add/remove. These are
|
|
184
|
-
// READ-ONLY — the agent never signs via a tool; all signing is in
|
|
185
|
-
// signing.ts (fixed code).
|
|
186
|
-
// To let the agent BUY paid data at work time (e.g. CMC market data
|
|
187
|
-
// after `bag x402 trust cmc` + `bag recipe code x402-buyer`), spread
|
|
188
|
-
// the emitted tool set — payee + per-call/daily caps stay locked in
|
|
189
|
-
// studio.toml:
|
|
190
|
-
// import { X402_BUYER_TOOLS } from "./x402Buyer.js";
|
|
191
|
-
// tools: { ...LLM_READ_TOOLS, ...X402_BUYER_TOOLS },
|
|
192
|
-
tools: LLM_READ_TOOLS,
|
|
193
|
-
stopWhen: stepCountIs(8), // bounded tool-call loop, then final text
|
|
194
|
-
abortSignal,
|
|
195
|
-
});
|
|
196
|
-
return result.text.trim();
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function hasErc8183Rail(cfg: TomlTable): boolean {
|
|
201
|
-
const payments = asTable(cfg.payments);
|
|
202
|
-
return asTable(payments?.erc8183) !== null;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function asTable(value: unknown): TomlTable | null {
|
|
206
|
-
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
207
|
-
? (value as TomlTable)
|
|
208
|
-
: null;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function flatHeaders(
|
|
212
|
-
headers: Record<string, string | string[] | undefined>,
|
|
213
|
-
): Record<string, string> {
|
|
214
|
-
const out: Record<string, string> = {};
|
|
215
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
216
|
-
if (typeof value === "string") out[name] = value;
|
|
217
|
-
else if (value !== undefined) out[name] = value[0] ?? "";
|
|
218
|
-
}
|
|
219
|
-
return out;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function flatQuery(query: Record<string, unknown>): Record<string, string> {
|
|
223
|
-
const out: Record<string, string> = {};
|
|
224
|
-
for (const [name, value] of Object.entries(query)) {
|
|
225
|
-
if (typeof value === "string") out[name] = value;
|
|
226
|
-
}
|
|
227
|
-
return out;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function x402Work(runWork: RunWork): X402RunWork {
|
|
231
|
-
return ({ prompt }) => runWork(prompt, { sessionId: "x402" });
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// ── serving ───────────────────────────────────────────────────────────────────
|
|
235
|
-
|
|
236
|
-
async function main(): Promise<void> {
|
|
237
|
-
await loadRuntimeSecrets();
|
|
238
|
-
|
|
239
|
-
// Wallet material is NEVER bundled into the deploy artifact. `bag deploy`
|
|
240
|
-
// injects it via Secrets Manager and these calls (run once at cold start,
|
|
241
|
-
// before any signing) materialize it on disk. Each is a no-op for the
|
|
242
|
-
// other wallet kind and locally, where the wallet already lives on disk:
|
|
243
|
-
// - evm-local: WALLET_KEYSTORE_JSON → keystore file (unlocked with WALLET_PASSWORD)
|
|
244
|
-
// - twak: TWAK_WALLET_JSON / TWAK_CREDENTIALS_JSON → $TMPDIR/twak-home/.twak
|
|
245
|
-
// (exported as TWAK_HOME_DIR; twak reads TWAK_WALLET_PASSWORD itself)
|
|
246
|
-
ensureKeystoreMaterialized();
|
|
247
|
-
ensureTwakMaterialized();
|
|
248
|
-
await ensureAltanaSessionLoaded();
|
|
249
|
-
|
|
250
|
-
const cfg = loadStudioToml();
|
|
251
|
-
const rails = { erc8183: hasErc8183Rail(cfg) };
|
|
252
|
-
const host = process.env.AGENT_BIND_HOST || "0.0.0.0";
|
|
253
|
-
const port = Number(process.env.AGENT_PORT || "9000");
|
|
254
|
-
const runWork = buildRunWork();
|
|
255
|
-
|
|
256
|
-
// The executor backs the seller skills with signing.ts fixed code (NEVER an
|
|
257
|
-
// LLM tool). The express app hosts the agent card + JSON-RPC message/send
|
|
258
|
-
// on 0.0.0.0:9000 and GET /ping for AgentCore's liveness probe.
|
|
259
|
-
const executor = new SellerAgentExecutor({
|
|
260
|
-
runWork,
|
|
261
|
-
generator: generatorTag(),
|
|
262
|
-
network: defaultNetwork(),
|
|
263
|
-
commerceSkills: rails.erc8183,
|
|
264
|
-
});
|
|
265
|
-
const agentCard = buildAgentCard({ commerceSkills: rails.erc8183 });
|
|
266
|
-
const seller = await X402Seller.create({
|
|
267
|
-
cfg,
|
|
268
|
-
runWork: x402Work(runWork),
|
|
269
|
-
walletAddress: getWallet().address,
|
|
270
|
-
resourceUrl: `${
|
|
271
|
-
process.env.AGENTCORE_RUNTIME_URL ?? `http://localhost:${port}`
|
|
272
|
-
}${X402_SELL_PATH}`,
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
const handler = new DefaultRequestHandler(
|
|
276
|
-
agentCard,
|
|
277
|
-
new InMemoryTaskStore(),
|
|
278
|
-
executor,
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
const app = express();
|
|
282
|
-
|
|
283
|
-
// GET /ping status fed to AgentCore: HEALTHY_BUSY while a background
|
|
284
|
-
// delivery is in flight, else HEALTHY.
|
|
285
|
-
//
|
|
286
|
-
// notify_funded acks immediately and runs the slow work (LLM + on-chain
|
|
287
|
-
// submit) in the background. Reporting HEALTHY_BUSY tells AgentCore the
|
|
288
|
-
// runtime is still working, so the scale-to-zero runtime is NOT reaped on
|
|
289
|
-
// idle before delivery lands (bounded by the session max-lifetime; ≤8h).
|
|
290
|
-
app.get("/ping", (_req, res) => {
|
|
291
|
-
res.json({ status: executor.isBusy() ? "HEALTHY_BUSY" : "HEALTHY" });
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
if (seller.state !== "disabled") {
|
|
295
|
-
app.all(
|
|
296
|
-
X402_SELL_PATH,
|
|
297
|
-
express.text({ type: "*/*", limit: "1mb" }),
|
|
298
|
-
async (req, res) => {
|
|
299
|
-
const request: X402HttpRequest = {
|
|
300
|
-
method: req.method,
|
|
301
|
-
path: req.path,
|
|
302
|
-
query: flatQuery(req.query),
|
|
303
|
-
headers: flatHeaders(req.headers),
|
|
304
|
-
body:
|
|
305
|
-
typeof req.body === "string"
|
|
306
|
-
? req.body
|
|
307
|
-
: JSON.stringify(req.body ?? ""),
|
|
308
|
-
};
|
|
309
|
-
const out = await seller.handle(request);
|
|
310
|
-
res.status(out.status).set(out.headers).send(out.body);
|
|
311
|
-
},
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
app.use(express.json({ limit: "8mb" }));
|
|
316
|
-
app.use(createEnvelopeMiddleware({ port }));
|
|
317
|
-
|
|
318
|
-
app.use(
|
|
319
|
-
"/.well-known/agent-card.json",
|
|
320
|
-
agentCardHandler({ agentCardProvider: handler }),
|
|
321
|
-
);
|
|
322
|
-
app.use(
|
|
323
|
-
jsonRpcHandler({
|
|
324
|
-
requestHandler: handler,
|
|
325
|
-
userBuilder: UserBuilder.noAuthentication,
|
|
326
|
-
}),
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
// AgentCore's A2A contract is 0.0.0.0:9000. Do not honor the HTTP
|
|
330
|
-
// protocol's $PORT=8080 convention here; AGENT_PORT is the local-dev /
|
|
331
|
-
// rendered-container override.
|
|
332
|
-
app.listen(port, host, () => {
|
|
333
|
-
console.log(`[seller-agent] A2A serving on ${host}:${port}`);
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Run only as an entrypoint (`node main.js` / the AgentCore runtime), never
|
|
338
|
-
// on import — tests import the builders above without starting a server.
|
|
339
|
-
const isMain =
|
|
340
|
-
process.argv[1] !== undefined &&
|
|
341
|
-
import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
342
|
-
if (isMain) {
|
|
343
|
-
main().catch((e) => {
|
|
344
|
-
console.error("[seller-agent] fatal:", e);
|
|
345
|
-
process.exit(1);
|
|
346
|
-
});
|
|
347
|
-
}
|