@bnbagent/studio-cli 0.0.13 → 0.0.14-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.
Files changed (36) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +8 -0
  3. package/dist/_agentcoreName-DZDWEYD3.js +0 -0
  4. package/dist/bag.js +3741 -1612
  5. package/dist/{chunk-H4X2OOLA.js → chunk-CFMETPKS.js} +263 -73
  6. package/dist/chunk-U7IDQ3K5.js +0 -0
  7. package/dist/{deployCli-22NMZ4G7.js → deployCli-AVWKY3GN.js} +5 -2
  8. package/package.json +13 -14
  9. package/recipes/agent/code/{{PKG}}/deliveryPolicy.ts.tmpl +43 -0
  10. package/recipes/agent/code/{{PKG}}/readToolCatalog.ts.tmpl +102 -0
  11. package/recipes/agent/code/{{PKG}}/signing.ts.tmpl +7 -2
  12. package/recipes/agent/recipe.toml +5 -4
  13. package/recipes/mpp-buyer/recipe.toml +1 -1
  14. package/recipes/runtimes/agentcore/code/{{PKG}}/dualMain.ts.tmpl +6 -2
  15. package/recipes/runtimes/agentcore/code/{{PKG}}/mcpMain.ts.tmpl +173 -134
  16. package/recipes/runtimes/agentcore/code/{{PKG}}/model.ts.tmpl +7 -0
  17. package/recipes/runtimes/agentcore/code/{{PKG}}/sellerCore.ts.tmpl +31 -46
  18. package/recipes/runtimes/agentcore/code/{{PKG}}/tools.ts.tmpl +19 -48
  19. package/recipes/runtimes/agentcore/code/{{PKG}}/unifiedMain.ts.tmpl +2 -1
  20. package/recipes/runtimes/agentcore/recipe.toml +1 -1
  21. package/recipes/runtimes/azure-foundry/code/{{PKG}}/mcpMain.ts.tmpl +173 -134
  22. package/recipes/runtimes/azure-foundry/code/{{PKG}}/model.ts.tmpl +7 -0
  23. package/recipes/runtimes/azure-foundry/code/{{PKG}}/sellerCore.ts.tmpl +31 -46
  24. package/recipes/runtimes/azure-foundry/code/{{PKG}}/tools.ts.tmpl +19 -48
  25. package/recipes/runtimes/azure-foundry/code/{{PKG}}/unifiedMain.ts.tmpl +2 -1
  26. package/recipes/runtimes/azure-foundry/recipe.toml +1 -1
  27. package/recipes/wallet/recipe.toml +3 -2
  28. package/recipes/x402-buyer/recipe.toml +1 -1
  29. package/skills/bnbagent-studio.md +12 -1
  30. package/skills/references/bnbagent-studio-operating.md +1 -0
  31. package/skills/references/bnbagent-studio-scaffolding-agent.md +11 -34
  32. package/skills/references/bnbagent-studio-selling-via-b402.md +3 -3
  33. package/skills/references/bnbagent-studio-using-altana-wallet.md +7 -2
  34. package/skills/references/bnbagent-studio-using-twak-wallet.md +24 -76
  35. package/dist/_twak-4XF4H5PL.js +0 -25
  36. package/dist/chunk-RO726HJG.js +0 -175
@@ -38,24 +38,38 @@
38
38
  */
39
39
 
40
40
  import { ERC8183JobOps } from "@bnbagent/sdk/erc8183";
41
+ import { maskUrlSecrets } from "@bnbagent/studio-runtime/audit";
41
42
  import { SubmitPermanentlyUnsupportedError } from "@bnbagent/studio-runtime/erc8183";
42
43
  import { getWallet } from "@bnbagent/studio-runtime/wallet";
44
+ import {
45
+ DeliveryTimeoutError,
46
+ deliveryTimeoutSeconds,
47
+ envSeconds,
48
+ minimumDeliveryWindowSeconds,
49
+ withTimeout,
50
+ } from "./deliveryPolicy.js";
43
51
  import { limitCommerceOperation } from "./requestLimits.js";
44
52
  import * as defaultSigning from "./signing.js";
45
53
 
54
+ function safeLogText(value: unknown): string {
55
+ const text =
56
+ value instanceof Error
57
+ ? (value.stack ?? `${value.name}: ${value.message}`)
58
+ : String(value ?? "");
59
+ return maskUrlSecrets(text);
60
+ }
61
+
46
62
  const log = {
47
- info: (msg: string) => console.log(`[seller-agent.core] ${msg}`),
48
- warn: (msg: string) => console.warn(`[seller-agent.core] WARNING ${msg}`),
63
+ info: (msg: string) => console.log(`[seller-agent.core] ${safeLogText(msg)}`),
64
+ warn: (msg: string) =>
65
+ console.warn(`[seller-agent.core] WARNING ${safeLogText(msg)}`),
49
66
  error: (msg: string, e?: unknown) =>
50
- console.error(`[seller-agent.core] ERROR ${msg}`, e ?? ""),
67
+ console.error(
68
+ `[seller-agent.core] ERROR ${safeLogText(msg)}`,
69
+ safeLogText(e),
70
+ ),
51
71
  };
52
72
 
53
- /** Read a positive timeout (seconds) from the env, falling back to `dflt`. */
54
- function envSeconds(name: string, dflt: number): number {
55
- const v = Number(process.env[name] || dflt);
56
- return Number.isFinite(v) && v > 0 ? v : dflt;
57
- }
58
-
59
73
  // Background-task ceilings. notifyFunded ACKs immediately and delivers in a
60
74
  // BACKGROUND task; AgentCore keeps the scale-to-zero microVM warm
61
75
  // (HEALTHY_BUSY) while isBusy() is true. A delivery (LLM text + on-chain
@@ -65,43 +79,10 @@ function envSeconds(name: string, dflt: number): number {
65
79
  // billing memory the whole time. A timed-out job is treated as TRANSIENT
66
80
  // (not dropped): the funded job stays on-chain and a later sweep re-delivers
67
81
  // it idempotently. (Read lazily so tests can tune them via the env.)
68
- const jobDeliveryTimeoutSeconds = () =>
69
- envSeconds("NOTIFY_DELIVERY_TIMEOUT_SECONDS", 600);
70
82
  const sweepTimeoutSeconds = () => envSeconds("NOTIFY_SWEEP_TIMEOUT_SECONDS", 60);
71
83
  const preverifyTimeoutSeconds = () =>
72
84
  envSeconds("NOTIFY_PREVERIFY_TIMEOUT_SECONDS", 30);
73
85
 
74
- /** Rejection raised by {@link withTimeout} when the deadline fires. */
75
- export class DeliveryTimeoutError extends Error {}
76
-
77
- /**
78
- * Race `work` against a deadline, aborting `controller` when it fires.
79
- *
80
- * JS cannot hard-cancel an arbitrary promise the way asyncio.wait_for
81
- * cancels a coroutine: the abort signal stops the LLM call (the AI SDK
82
- * honours it), and the on-chain layers are idempotent — `verifySignedJob`
83
- * returns non-OK for an already-SUBMITTED job and `submitResult` re-verifies
84
- * FUNDED — so an orphaned straggler can never double-deliver.
85
- */
86
- async function withTimeout<T>(
87
- work: Promise<T>,
88
- seconds: number,
89
- controller?: AbortController,
90
- ): Promise<T> {
91
- let timer: ReturnType<typeof setTimeout> | undefined;
92
- const deadline = new Promise<never>((_, reject) => {
93
- timer = setTimeout(() => {
94
- controller?.abort();
95
- reject(new DeliveryTimeoutError(`timed out after ${seconds}s`));
96
- }, seconds * 1000);
97
- });
98
- try {
99
- return await Promise.race([work, deadline]);
100
- } finally {
101
- clearTimeout(timer);
102
- }
103
- }
104
-
105
86
  /**
106
87
  * The LLM work hook: produce the deliverable text for a prompt.
107
88
  *
@@ -125,6 +106,7 @@ export interface SigningApi {
125
106
  ): Promise<Record<string, unknown>>;
126
107
  verifySignedJob(
127
108
  jobId: number,
109
+ minimumRemainingSeconds?: number,
128
110
  ): Promise<{ ok: boolean; reason: string; permanent: boolean }>;
129
111
  jobSpec(
130
112
  jobId: number,
@@ -284,7 +266,7 @@ export class SellerCore {
284
266
  // Time-bounded: a hung RPC must not stall the ack path. On timeout we
285
267
  // fall through to accept-and-re-verify below.
286
268
  const v = await withTimeout(
287
- this.signing.verifySignedJob(jobId),
269
+ this.signing.verifySignedJob(jobId, minimumDeliveryWindowSeconds()),
288
270
  preverifyTimeoutSeconds(),
289
271
  );
290
272
  if (!v.ok && v.permanent) {
@@ -351,7 +333,7 @@ export class SellerCore {
351
333
  verified
352
334
  ? this.doWorkAndSubmit(jobId, controller.signal)
353
335
  : this.fulfillJob(jobId, controller.signal),
354
- jobDeliveryTimeoutSeconds(),
336
+ deliveryTimeoutSeconds(),
355
337
  controller,
356
338
  );
357
339
  log.info(`notify_funded job ${jobId} → ${JSON.stringify(result)}`);
@@ -367,7 +349,7 @@ export class SellerCore {
367
349
  if (e instanceof DeliveryTimeoutError) {
368
350
  // Transient by design — leave terminal false so a later sweep retries.
369
351
  log.warn(
370
- `background delivery of job ${jobId} timed out after ${jobDeliveryTimeoutSeconds()}s; will retry`,
352
+ `background delivery of job ${jobId} timed out after ${deliveryTimeoutSeconds()}s; will retry`,
371
353
  );
372
354
  } else {
373
355
  log.error(`background delivery of job ${jobId} failed`, e);
@@ -399,7 +381,10 @@ export class SellerCore {
399
381
  jobId: number,
400
382
  abortSignal: AbortSignal,
401
383
  ): Promise<Record<string, unknown>> {
402
- const v = await this.signing.verifySignedJob(jobId);
384
+ const v = await this.signing.verifySignedJob(
385
+ jobId,
386
+ minimumDeliveryWindowSeconds(),
387
+ );
403
388
  if (!v.ok) {
404
389
  return { ok: false, job_id: jobId, skip: v.permanent, reason: v.reason };
405
390
  }
@@ -30,6 +30,7 @@ import * as cr from "@bnbagent/studio-runtime/tools";
30
30
  import { loadStudioToml } from "@bnbagent/studio-runtime/config";
31
31
  import { tool, type ToolSet } from "ai";
32
32
  import { z } from "zod";
33
+ import { READ_TOOL_CATALOG } from "./readToolCatalog.js";
33
34
 
34
35
  /**
35
36
  * The project-wide default network (`[network].default`) — tool calls that
@@ -46,51 +47,34 @@ function defaultNetwork(): string {
46
47
  }
47
48
  }
48
49
 
49
- const networkArg = z
50
- .string()
51
- .optional()
52
- .describe("studio network name (defaults to the project's [network].default)");
53
-
54
50
  export const LLM_READ_TOOLS: ToolSet = {
55
51
  // --- Wallet & chain basics ---
56
52
  wallet_info: tool({
57
- description:
58
- "Describe the agent's active wallet (address, kind, key location).",
59
- inputSchema: z.object({}),
53
+ description: READ_TOOL_CATALOG.wallet_info.description,
54
+ inputSchema: z.object(READ_TOOL_CATALOG.wallet_info.inputSchema),
60
55
  execute: async () => cr.walletInfo(),
61
56
  }),
62
57
  balance_native: tool({
63
- description:
64
- "Native BNB balance of an address (defaults to the agent's own wallet).",
65
- inputSchema: z.object({
66
- address: z.string().optional().describe("0x address; omit for own wallet"),
67
- network: networkArg,
68
- }),
58
+ description: READ_TOOL_CATALOG.balance_native.description,
59
+ inputSchema: z.object(READ_TOOL_CATALOG.balance_native.inputSchema),
69
60
  execute: async ({ address, network }) =>
70
61
  cr.balanceNative(address ?? null, network ?? defaultNetwork()),
71
62
  }),
72
63
  balance_u: tool({
73
64
  // requires [u_token] in studio.toml
74
- description:
75
- "$U (payment token) balance of an address (defaults to the agent's own wallet).",
76
- inputSchema: z.object({
77
- address: z.string().optional().describe("0x address; omit for own wallet"),
78
- network: networkArg,
79
- }),
65
+ description: READ_TOOL_CATALOG.balance_u.description,
66
+ inputSchema: z.object(READ_TOOL_CATALOG.balance_u.inputSchema),
80
67
  execute: async ({ address, network }) =>
81
68
  cr.balanceU(address ?? null, network ?? defaultNetwork()),
82
69
  }),
83
70
  network_info: tool({
84
- description: "Chain id / RPC / token info for a studio network.",
85
- inputSchema: z.object({ network: networkArg }),
71
+ description: READ_TOOL_CATALOG.network_info.description,
72
+ inputSchema: z.object(READ_TOOL_CATALOG.network_info.inputSchema),
86
73
  execute: async ({ network }) => cr.networkInfo(network ?? defaultNetwork()),
87
74
  }),
88
75
  tx_status: tool({
89
- description: "Status + receipt summary of a transaction hash.",
90
- inputSchema: z.object({
91
- tx_hash: z.string().describe("0x transaction hash"),
92
- network: networkArg,
93
- }),
76
+ description: READ_TOOL_CATALOG.tx_status.description,
77
+ inputSchema: z.object(READ_TOOL_CATALOG.tx_status.inputSchema),
94
78
  execute: async ({ tx_hash, network }) =>
95
79
  cr.txStatus(tx_hash, network ?? defaultNetwork()),
96
80
  }),
@@ -106,21 +90,15 @@ export const LLM_READ_TOOLS: ToolSet = {
106
90
  // --- ERC-8004 identity (read-only lookups the LLM may want for context) ---
107
91
  agent_info: tool({
108
92
  // requires [erc8004] in studio.toml
109
- description: "ERC-8004 identity record for an agent id.",
110
- inputSchema: z.object({
111
- agent_id: z.number().int().describe("ERC-8004 agent id"),
112
- network: networkArg,
113
- }),
93
+ description: READ_TOOL_CATALOG.agent_info.description,
94
+ inputSchema: z.object(READ_TOOL_CATALOG.agent_info.inputSchema),
114
95
  execute: async ({ agent_id, network }) =>
115
96
  cr.agentInfo(agent_id, network ?? defaultNetwork()),
116
97
  }),
117
98
  agent_by_address: tool({
118
99
  // requires [erc8004] in studio.toml
119
- description: "Look up an ERC-8004 agent registration by wallet address.",
120
- inputSchema: z.object({
121
- address: z.string().describe("0x wallet address"),
122
- network: networkArg,
123
- }),
100
+ description: READ_TOOL_CATALOG.agent_by_address.description,
101
+ inputSchema: z.object(READ_TOOL_CATALOG.agent_by_address.inputSchema),
124
102
  execute: async ({ address, network }) =>
125
103
  cr.agentByAddress(address, network ?? defaultNetwork()),
126
104
  }),
@@ -128,22 +106,15 @@ export const LLM_READ_TOOLS: ToolSet = {
128
106
  // --- ERC-8183 jobs (READ-ONLY status/list — writes live in signing.ts) ---
129
107
  job_status: tool({
130
108
  // requires [erc8183] in studio.toml
131
- description: "Read-only ERC-8183 job summary (status, budget, deliverable URL).",
132
- inputSchema: z.object({
133
- job_id: z.number().int().describe("on-chain job id"),
134
- network: networkArg,
135
- }),
109
+ description: READ_TOOL_CATALOG.job_status.description,
110
+ inputSchema: z.object(READ_TOOL_CATALOG.job_status.inputSchema),
136
111
  execute: async ({ job_id, network }) =>
137
112
  cr.jobStatus(job_id, network ?? defaultNetwork()),
138
113
  }),
139
114
  job_list: tool({
140
115
  // requires [erc8183] in studio.toml
141
- description: "List recent ERC-8183 jobs (optionally only this agent's).",
142
- inputSchema: z.object({
143
- limit: z.number().int().optional(),
144
- mine: z.boolean().optional().describe("only jobs assigned to this agent"),
145
- network: networkArg,
146
- }),
116
+ description: READ_TOOL_CATALOG.job_list.description,
117
+ inputSchema: z.object(READ_TOOL_CATALOG.job_list.inputSchema),
147
118
  execute: async ({ limit, mine, network }) =>
148
119
  cr.jobList({ limit, mine, network: network ?? defaultNetwork() }),
149
120
  }),
@@ -225,7 +225,8 @@ export function buildRunWork(): RunWork {
225
225
 
226
226
  function hasErc8183Rail(cfg: TomlTable): boolean {
227
227
  const payments = asTable(cfg.payments);
228
- return asTable(payments?.erc8183) !== null;
228
+ const rail = asTable(payments?.erc8183);
229
+ return rail !== null && rail.enabled !== false;
229
230
  }
230
231
 
231
232
  function asTable(value: unknown): TomlTable | null {
@@ -29,7 +29,7 @@ node = [
29
29
  # on either cloud.
30
30
  "@aws-sdk/client-secrets-manager@^3.600.0",
31
31
  "@bnbagent/studio-runtime",
32
- "@bnbagent/sdk@0.5.5",
32
+ "@bnbagent/sdk@0.5.6",
33
33
  # The LLM work hook (generateText + tools) and the model factory
34
34
  # (model.ts buildModel — studio.toml [llm] + the provider key env).
35
35
  "ai@^7.0.29",
@@ -11,8 +11,9 @@ node = ["@altananetwork/sdk@0.7.1"]
11
11
  node = ["@turnkey/sdk-server@8.1.0", "@turnkey/viem@0.14.34"]
12
12
 
13
13
  [env]
14
- # kind='twak': the twak CLI reads the unlock password from the environment —
15
- # it never goes on a command line.
14
+ # kind='twak': Studio generates this unlock password. Runtime operations read
15
+ # it from the environment; upstream twak requires it briefly on the one-time
16
+ # wallet-create child argv, which Studio never prints.
16
17
  TWAK_WALLET_PASSWORD = "unlocks ~/.twak/wallet.json (twak kind); set in .studio/.env.local"
17
18
  # kind='turnkey': P-256 API credential; the signing key never leaves Turnkey's enclave.
18
19
  TURNKEY_API_PRIVATE_KEY = "Turnkey P-256 API private key (turnkey kind); set in .studio/.env.local"
@@ -6,7 +6,7 @@ status = "v0.0.x"
6
6
  [dependencies]
7
7
  node = [
8
8
  "@bnbagent/studio-runtime",
9
- "@bnbagent/sdk@0.5.5",
9
+ "@bnbagent/sdk@0.5.6",
10
10
  "ai@^7.0.29", # AI SDK `tool` wrappers around the buyer functions
11
11
  "zod@^3.25.0", # tool input schemas
12
12
  ]
@@ -9,6 +9,16 @@ description: The single entry point for bnbagent-studio - a TypeScript CLI (`bag
9
9
 
10
10
  Invoked as `/bnbagent-studio <ask>`? Treat `<ask>` as the user's intent and route it through the decision tree below, exactly like a natural-language ask.
11
11
 
12
+ ## Runtime preflight
13
+
14
+ This skill requires `bag` **0.0.13 or newer**. Before following any workflow,
15
+ run the read-only command `bag --version`. If `bag` is missing or older, stop
16
+ and ask the user to run `npm install -g @bnbagent/studio-cli@latest`, then
17
+ repeat the version check. Never install or upgrade a global executable without
18
+ the user's approval.
19
+
20
+ <!-- bag-compatibility: >=0.0.13 -->
21
+
12
22
  ## The single seller runtime model (the invariants)
13
23
 
14
24
  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.
@@ -22,6 +32,7 @@ One deployed runtime, one signer: a single valuable Agent serves the selected fa
22
32
  | Create a brand new single seller project from zero | `references/bnbagent-studio-scaffolding-agent.md` |
23
33
  | Add wallet / the single seller runtime to an existing TypeScript agent | `references/bnbagent-studio-adding-to-project.md` |
24
34
  | Run / debug / dev / doctor / RPC / balance / incident triage | `references/bnbagent-studio-operating.md` |
35
+ | Report a CLI problem or share product feedback | Run `bag feedback`, review the local JSON bundle before attaching it, and read `references/bnbagent-studio-operating.md`; Studio never uploads or submits it automatically |
25
36
  | Implement what the Agent sells, tune pricing, publish over A2A and/or MCP, defend disputes (seller flow) | `references/bnbagent-studio-selling-via-8183.md` |
26
37
  | Sell one paid or FREE HTTP request through the selected B402-backed x402 or MPP rail (pricing choice; paid merchant application, RSA key, credentials, IP allowlist, activation) | `references/bnbagent-studio-selling-via-b402.md` |
27
38
  | Deploy / redeploy / status / logs / destroy | Run `bag deploy` and explicitly choose a provider. Non-interactive deploy requires `--provider bnb\|aws\|azure --yes` (and `--allow-multiple` when keeping another provider active). Read `references/bnbagent-studio-use-bnb-trial.md`, `references/bnbagent-studio-use-aws-agentcore.md`, or `references/bnbagent-studio-use-azure-foundry.md` for the selected provider. `bag deploy status` lists every recorded provider; multi-deployment logs/verify/destroy require `--provider`. |
@@ -54,7 +65,7 @@ Treat ERC-8183 amounts as decimal strings at CLI/config boundaries and `bigint`
54
65
 
55
66
  ## CLI groups at a glance
56
67
 
57
- `init`, `scan`, `recipe`, `skills`, `wallet`, `erc8004`, `erc8183`, `x402`, `mpp`, `agents`, `config`, `env`, `dev`, `doctor`, `audit`, `deploy`, `platform`, `llm`, `bundle`, `budget` - see `bag --help` for details. `bag deploy [--provider bnb\|aws\|azure] [--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.15`.
68
+ `init`, `scan`, `recipe`, `skills`, `wallet`, `erc8004`, `erc8183`, `x402`, `mpp`, `agents`, `config`, `env`, `dev`, `doctor`, `feedback`, `audit`, `deploy`, `platform`, `llm`, `bundle`, `budget` - see `bag --help` for details. `bag deploy [--provider bnb\|aws\|azure] [--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.15`.
58
69
 
59
70
  ## Tool surface
60
71
 
@@ -46,6 +46,7 @@ This playbook covers **generic ops**: dev / doctor / balances / RPC / incident t
46
46
  | "wallet balance is wrong" | Check both tBNB (gas) and U (token); see balance section |
47
47
  | "is it deployed?" / "deploy status" | `bag deploy status` lists every locally recorded BNB/AWS deployment and asks `bnbagent-deploy` for live state; add `--no-probe` for record-only output |
48
48
  | "deploy logs" / "verify" / "tear it down" | With one recorded deployment, `bag deploy {logs,verify,destroy}` selects it automatically. With multiple, choose interactively or pass `--provider bnb\|aws` in automation. Provider lifecycle and status/log calls go through `bnbagent-deploy`; ERC-8004 reconciliation remains a Studio chain operation. `bag platform credit` shows the BNB trial countdown. |
49
+ | "report this error" / "send feedback" | Run `bag feedback`. It builds an inspectable, owner-readable JSON bundle from the last command's allowlisted lifecycle data, shows the release-pinned Google Form ID, asks before opening it, and never uploads or submits anything. If no production Form ID has been provisioned, keep the bundle local; do not substitute a project-provided form. Review the file before manually attaching it; `.studio/audit-log.jsonl`, argv, output, paths, credentials, addresses, and transaction hashes are excluded. |
49
50
 
50
51
  ## Common ops procedures
51
52
 
@@ -69,7 +69,7 @@ The fields (give the user all of them at once):
69
69
  | 1 | **Project name** | Must start with a letter, contain ASCII letters and digits only, and be at most 23 characters. `bag init` rejects `-`, `_`, `.`, and overlong names instead of renaming them. For example: `newsagent`, `twcopywriter`. | (required) |
70
70
  | 2 | **Network** | `bsc-testnet` / `bsc-mainnet` | `bsc-testnet` |
71
71
  | 3 | **LLM provider** | `pieverse-llm` / `openrouter` / `openai` / `anthropic` / `bedrock` | `pieverse-llm` |
72
- | 4 | **Wallet kind** (`--wallet-kind`) | `evm-local` (encrypted local keystore at the workspace root; `bag wallet new` creates it, `--private-key` imports an existing key; CodeZip deploy) / `twak` (**fully supported, opt in with `--wallet-kind twak`** - Trust Wallet Agent Kit CLI 0.20.0, self-custody encrypted mnemonic in a **project-dedicated** home `.studio/twak`, isolated from your main `~/.twak`; created manually with `HOME=<ws>/.studio/twak twak wallet create`, then `bag wallet new` adopts; container deploy. Reuse an existing wallet across agents with `--twak-home <path>`) / `altana` (bounded-session custody - admin keystore stays local, deploys ship ONLY the `ALTANA_SESSION` secret; zip deploy; not compatible with `pieverse-llm` or a paid b402 rail; flow: `references/bnbagent-studio-using-altana-wallet.md`) | `evm-local` |
72
+ | 4 | **Wallet kind** (`--wallet-kind`) | `evm-local` (encrypted local keystore at the workspace root; Studio generates its password on the new-wallet happy path; `--evm-keystore <v3-file>` copies and adopts an existing encrypted keystore with its original password; `--private-key` imports an existing raw key; CodeZip deploy) / `twak` (**fully supported, opt in with `--wallet-kind twak`** - project-pinned Trust Wallet CLI 0.20.0, self-custody encrypted mnemonic in a **project-dedicated** home `.studio/twak`, isolated from the main `~/.twak`; `bag wallet twak-init` performs credential init/verification, password generation, wallet creation/adoption, and address anchoring without the upstream wizard; container deploy. Reuse an existing wallet across agents with `--twak-home <path>`) / `altana` (bounded-session custody - admin keystore stays local, deploys ship ONLY the `ALTANA_SESSION` secret; zip deploy; not compatible with `pieverse-llm` or a paid b402 rail; flow: `references/bnbagent-studio-using-altana-wallet.md`) | `evm-local` |
73
73
  | 5 | **Storage** | `local` (offline only) / `ipfs` / `s3` / `azure-blob`. Self-hosted deploys use BYOS credentials; managed-platform deploys receive an operator-owned storage endpoint and scoped token through the sealed runtime-secret channel. | `local` |
74
74
  | 6 | **Protocol faces** (`--protocols`) | any non-empty subset of `A2A`, `MCP`, `X402` | `A2A,X402` (`A2A` for Altana) |
75
75
  | 7 | **LLM model** | provider catalogue; for `pieverse-llm` the default `auto/free` runs at $0/token | `auto/free` |
@@ -136,9 +136,11 @@ Build a TodoWrite list. The shape depends on the `wallet kind`. The canonical 8-
136
136
  >
137
137
  > If Bun is missing at deploy time, **PAUSE** and have the USER install it (Bun 1.3+, or set `BNBAGENT_DEPLOY_COMMAND`). Likewise for `bag dev --container` only: the npm `@aws/agentcore` CLI must be present and must win on PATH over the incompatible `bedrock-agentcore-starter-toolkit` shim - global tools on the user's machine, so the user installs them, not you.
138
138
 
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.
139
+ > **Onboarding note.** On a human TTY, `bag init` runs steps 3, 4 and 6 automatically (a new evm-local wallet gets a CSPRNG password without prompting; `--evm-keystore` instead asks for the existing file's original password; then Studio 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
141
  1. `bag init <name> --llm-provider <p> --network <n> --storage-provider <s> --wallet-kind <k> [--protocols <comma-list>] [--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). Omit `--protocols` and `--rails` for the default A2A + X402 faces with both ERC-8183 and B402 rails (all wallet kinds, altana included — its paid B402 payout lands at the admin address). Pass either flag when the user chose another face/rail combination (`--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
+
143
+ To reuse an encrypted evm-local wallet at scaffold time, add `--wallet-kind evm-local --evm-keystore /secure/path/wallet.json`. The input must be Web3 Secret Storage v3. Studio copies it to the private workspace wallet directory and, on a TTY, verifies it using its original password; it never generates a replacement password. For an already-created project that points at a shared wallet directory, set the original password locally and run `bag wallet new --keystore-dir /secure/path/wallets`; the directory and anchor are persisted for later commands.
142
144
  > **Current storage choices:** `--storage-provider` accepts `local`, `ipfs`, `s3`, and `azure-blob`. The latter three are BYOS only for self-hosted deployment; a managed-platform deployment replaces the writer with its injected API endpoint/token.
143
145
  >
144
146
  > **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.
@@ -148,44 +150,19 @@ Build a TodoWrite list. The shape depends on the `wallet kind`. The canonical 8-
148
150
  pnpm install # npm install works too - tooling is the user's choice
149
151
  ```
150
152
  The sub-project's `package.json` carries its deps: `@bnbagent/studio-runtime` (the runtime lib, pinned to the scaffolding CLI's exact version - NOT the CLI itself) + `@bnbagent/sdk` + `ai` (the AI SDK) plus the **protocol-specific** group - A2A adds `@a2a-js/sdk` + `express`, MCP adds `@modelcontextprotocol/sdk` instead (an A2A-only deploy never ships the MCP SDK, and an MCP-only deploy never ships the A2A one). For local dev against unreleased libs, `bag init` vendors local `.tgz` tarballs and points the manifest at them automatically.
151
- 3. **Set the wallet password** - the USER does this, NOT you.
152
-
153
- 🔒 **SECURITY - never route the wallet password through the chat.** It encrypts the key material that is the Agent's sole signing key. Do **NOT** ask the user to type it into the chat, and do **NOT** run any command with the password on the command line (`bag env set <PW_VAR> <literal>`, `twak wallet create --password <literal>`, …) - it would land in the session transcript, be sent to the model API, and hit shell history / `ps`.
154
-
155
- Tell the user to set it **themselves, in their own terminal**, so it never reaches you. The env var depends on the wallet kind:
156
- - **twak** → `TWAK_WALLET_PASSWORD` (the twak CLI reads it itself)
157
- - **evm-local** → `WALLET_PASSWORD`
158
-
159
- ```bash
160
- # In YOUR OWN terminal (not via the agent): open .studio/.env.local and
161
- # set the line for your wallet kind:
162
- # TWAK_WALLET_PASSWORD=<a strong password you choose> # twak
163
- # WALLET_PASSWORD=<a strong password you choose> # evm-local
164
- # Save it. Do not paste the password into this chat.
165
- ```
166
-
167
- `bag` auto-loads `.studio/.env.local` (resolved via the project root), so once the line is set you do NOT need to `source` it or `cd` anywhere special - `bag wallet new` / `bag llm activate` will read it. Wait for the user to confirm they've set it before continuing.
153
+ 3. **Keep all wallet secrets out of chat.** Never ask for, read, echo, or log a wallet password, NaaS Access ID/HMAC secret, mnemonic, or private key. Studio generates new-wallet passwords with the OS cryptographic random source and stores them in `.studio/.env.local` (0600). There is no weak-random fallback.
168
154
 
169
155
  4. **Create / adopt the wallet** - depends on the wallet kind:
170
- - **twak** (fully supported - opt in with `--wallet-kind twak`): `bag init` writes `[wallet].twak_home = "../../.studio/twak"` - a **project-dedicated** wallet isolated from your main `~/.twak`, so a deploy never pushes the main wallet's key material to Secrets Manager. Setting it up is a ONE-TIME job: the user runs the interactive 3-step `twak setup` wizard themselves in their own terminal, then `bag wallet twak-init` creates the wallet (so the password never goes into a command line). Start with the wizard:
171
-
172
- ```bash
173
- HOME=<workspace>/.studio/twak twak setup
174
- ```
175
156
 
176
- **Tell the user exactly what to pick at each wizard step** - it is not obvious and a wrong pick is dangerous:
177
- - **Step 1 (API credentials):** paste Access ID + HMAC secret from https://portal.trustwallet.com/dashboard/apps; WalletConnect Project ID → leave blank, ENTER.
178
- - **Step 2 (wire up harnesses): SELECT NONE, press ENTER** (don't press SPACE/`a`) - never wire twak's signing MCP into Claude Code / Cursor / etc.; studio keeps signing in fixed code, not MCP.
179
- - **Step 3 (wallet): pick `3) Skip for now`**, then create the wallet with the standalone command below. (`1) Create a new agent wallet` persists the password via the OS keychain, which fails on keychain-less environments - "OS keychain cannot persist passwords here … headless / Docker" - and studio unlocks via `TWAK_WALLET_PASSWORD` env anyway. NEVER pick `2) Use WalletConnect` = your main wallet.)
157
+ - **twak**: the generated project pins `@trustwallet/cli@0.20.0`; no global CLI install is needed. `bag init` on a human TTY delegates the full happy path to Studio. For a `--no-onboard` or agent-driven scaffold, ask the user to create a NaaS app at https://portal.trustwallet.com/dashboard/apps and run this command in their terminal, where Studio can collect both values through hidden prompts:
180
158
 
181
159
  ```bash
182
- cd app/agent && bag wallet twak-init # hidden prompt
183
- printf %s "$PW" | bag wallet twak-init --password-stdin # CI / scripts
160
+ cd app/agent && bag wallet twak-init
184
161
  ```
185
162
 
186
- Use UPPER + lower + digit; put that same password in `.studio/.env.local` as `TWAK_WALLET_PASSWORD`. `twak-init` passes `--no-keychain`, which keeps the password out of the OS keychain (no macOS prompt); studio unlocks via the env. (If a macOS prompt _loops_ on a manual `twak` run, do NOT "Reset Default Keychain" - `pkill -9 -f twak`, then re-run with `--no-keychain`.) It also adopts the address into `studio.toml` and echoes it - confirm it's the intended wallet before funding/deploy, and there is no separate `bag wallet new` step. The manual equivalent is `HOME=<workspace>/.studio/twak twak wallet create --password <StrongPw> --no-keychain` followed by `bag wallet new`. Full detail: the `bnbagent-studio-using-twak-wallet.md` reference (in the router skill's `references/` directory). To reuse an EXISTING wallet across agents, scaffold with `bag init --twak-home <path-to-its-home>` instead. Reusing your main `~/.twak` is opt-in only (`--twak-home ~`) and discouraged. Full detail: the `bnbagent-studio-using-twak-wallet.md` reference (in the router skill's `references/` directory).
163
+ Do not run or explain the upstream `twak setup` wizard. `twak-init` uses the project-pinned binary, runs `twak init --json`, verifies an authenticated read, generates and persists `TWAK_WALLET_PASSWORD`, creates/adopts the project-dedicated no-keychain wallet, and anchors the address. No separate `bag wallet new` step exists. For CI, the user may place `TWAK_ACCESS_ID` + `TWAK_HMAC_SECRET` in `.studio/.env.local` themselves; never route them through chat. Reuse an existing wallet only with explicit `--twak-home <home-style-path>`; `--twak-home ~` is a discouraged opt-in to the main wallet. Load `bnbagent-studio-using-twak-wallet.md` for the exact security and deploy contract.
187
164
 
188
- - **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).
165
+ - **evm-local** (default): the new-wallet TTY happy path generates `WALLET_PASSWORD` with the OS CSPRNG, persists it privately, then creates the encrypted keystore in-process. After `--no-onboard`, run `(cd app/agent && bag wallet new --generate-password)` for the same non-interactive safe path. It refuses to replace an existing or anchored wallet. For an existing encrypted v3 file, prefer `bag init ... --wallet-kind evm-local --evm-keystore <path>` and use the file's original password; Studio copies and verifies the file without re-encrypting it. To import a raw key, use the hidden `bag wallet new --private-key` prompt or stdin; never pass the key inline.
189
166
 
190
167
  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):
191
168
  - **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/
@@ -231,7 +208,7 @@ For each todo item:
231
208
 
232
209
  **Stop and ask the user** at:
233
210
 
234
- - Step 3 (password): the USER sets it **themselves, in their own terminal** - never through the chat or on a command line (see Step 3's security note). They edit `.studio/.env.local` and set `TWAK_WALLET_PASSWORD` (twak) or `WALLET_PASSWORD` (evm-local). `bag` auto-loads that file, so once it's set `bag wallet new` / `bag llm activate` pick it up - no `source`/`cd` needed. Wait for the user to confirm before continuing.
211
+ - Step 3 (wallet secrets): do not stop for a brand-new evm-local wallet; `bag wallet new --generate-password` uses the OS CSPRNG and writes the private local env file itself. Stop only when the selected flow requires an existing secret: an imported v3 keystore needs its original `WALLET_PASSWORD`, an existing TWAK wallet needs its original `TWAK_WALLET_PASSWORD`, and TWAK credential initialization needs the NaaS values. The USER enters those **themselves, in their own terminal**; never request or route them through chat, argv, or logs. Wait only for completion, not for the value.
235
212
  - Step 5 (funding): OPTIONAL - only stop here if the user explicitly wants a paid LLM model, on-chain settle, or to pay ERC-8183 buys now. Otherwise skip; the `auto/free` default needs no funds.
236
213
  - Step 6 (Pieverse activation): zero-deposit, so it just works - no funding precheck needed. If `bag llm activate` fails on connectivity, retry once.
237
214
  - Step 6b (deliverable storage): skip for `local` and for every managed-platform deploy. Self-hosted IPFS needs its write endpoint, S3 needs namespaced keys unless ambient identity is deliberately configured, and Azure Blob needs a write SAS/service principal unless ambient identity is deliberately configured. Do not ask for secret values in chat; have the user set them with `bag env set`. Self-hosted deploy readiness blocks incomplete storage configuration.
@@ -278,7 +255,7 @@ Edit (from workspace root):
278
255
 
279
256
  - **U is 18 decimals** (not 6 like USDC). The `@bnbagent/studio-runtime/networks` `toRaw`/`fromRaw` helpers handle this.
280
257
  - **`buy_workflow`'s `deadline_minutes`** is the seller's _submission_ window. The on-chain job lifetime is automatically `deadline_minutes + 24h dispute_window`.
281
- - **`bag init` runs wallet onboarding only on a human TTY** (evm-local: prompts for the password and runs `bag wallet new`; twak: adopts the existing twak wallet - never creates one, since `twak wallet create` puts the password on argv). When **Claude Code** runs `bag init` (non-interactively, via the shell tool) that auto-flow does NOT fire, so this skill drives Step 3/4 explicitly - use `--no-onboard` to make the behavior deterministic. This skill bridges the gap by collecting the wallet kind upfront and calling the right form.
258
+ - **`bag init` runs wallet onboarding only on a human TTY** (evm-local: generates and privately persists the password, then creates the keystore; twak: delegates credential init/verification, password generation, create/adopt, and address anchoring to `bag wallet twak-init`). When an agent runs `bag init` non-interactively, use `--no-onboard` for deterministic scaffolding, then follow Step 4 without collecting secrets in chat.
282
259
  - **The agent is the sole key-holder; the key material never enters the deploy package.** For **evm-local** the encrypted keystore lives at the **workspace root** `.studio/wallets/` (outside the `app/agent/` codeLocation, so no packaging path can bundle it); for **twak** the mnemonic lives at `~/.twak` (or `.studio/twak/`), never in the repo. Either way it is injected at deploy via AWS Secrets Manager (default `--secrets-mode secretsmanager`) - `WALLET_KEYSTORE_JSON` / `WALLET_PASSWORD` for evm-local, `TWAK_WALLET_JSON` / `TWAK_CREDENTIALS_JSON` / `TWAK_WALLET_PASSWORD` for twak - reconstructed at cold start, never in the package; the testnet-only `--secrets-mode envvars` fallback is refused on mainnet.
283
260
  - **AgentCore seller endpoints are never anonymous.** `bag deploy --provider aws` provisions the Cognito OAuth2 pool + buyer M2M client itself and prints the token URL / client id / scope to hand to buyers (`bag deploy provision-cognito` is deprecated - a deploy uses its own pool regardless). Locally, `bag dev` runs without Cognito env, so the card omits the scheme and is reachable without a token.
284
261
  - **ERC-8183 does NOT require ERC-8004** at the protocol level (commerce contract doesn't check the identity registry). Local two-agent dev can run end-to-end without ever touching 8004. Use 8004 only when you actually want discoverable identity.
@@ -85,11 +85,11 @@ B402 allowlists the merchant's **outbound** (egress) IPs, the addresses the agen
85
85
  curl ipinfo.io/ip
86
86
  ```
87
87
 
88
- 3. **Self-hosted AgentCore egress (self-deploys)**: operate a restricted B402 Relay on a host with a fixed public egress IP, such as a user-managed VPS, and submit that IP. Set the runtime `B402_BASE_URL` to the Relay base URL. The Relay exposes only `supported`, `verify`, and `settle`, fixes the upstream facilitator, and forwards the signed body and Tesla header allowlist without holding the merchant private key or automatically retrying a settlement transport failure. See the [self-hosted x402 gateway guide](https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway.md) for a TypeScript example.
88
+ 3. **Self-hosted AgentCore egress (self-deploys)**: operate a restricted B402 Relay on a host with a fixed public egress IP, such as a user-managed VPS, and submit that IP. Set the runtime `B402_BASE_URL` to the Relay base URL. The Relay exposes only `supported`, `verify`, and `settle`, fixes the upstream facilitator, and forwards the signed body and Tesla header allowlist without holding the merchant private key or automatically retrying a settlement transport failure. Review the public [BNB Agent Studio deployment guide](https://docs.bnbchain.org/developer-kit/bnbchain-studio/deployment/) for provider constraints; the Studio source checkout keeps the TypeScript gateway example at `docs/guides/self-hosted-x402-gateway.md`.
89
89
 
90
90
  As an alternative, use AWS-supported AgentCore VPC mode with a private subnet, NAT Gateway, and Elastic IP. Submit the Elastic IP and keep `B402_BASE_URL` pointed at the facilitator. Studio does not deploy or manage that AWS network.
91
91
 
92
- 4. **Self-hosted Azure Foundry egress (self-deploys)**: Foundry hosted-agent containers have floating egress just like AgentCore, so the agent must NOT call the facilitator directly. Run the envelope gateway on a Container Apps workload-profiles environment whose subnet has a NAT Gateway with a Standard static public IP, co-host a restricted B402 forwarder there (the AWS guide's Relay example works verbatim — the NAT Gateway replaces its fixed-IP host requirement), point the runtime `B402_BASE_URL` at that forwarder, and submit the NAT Gateway IP. The environment type and VNet cannot be changed after creation; the full recipe (subnet sizing, ingress caveats, minReplicas) is in the [Azure self-hosted x402 gateway guide](https://github.com/bnb-chain/bnbagent-studio/blob/main/docs/guides/self-hosted-x402-gateway-azure.md). Studio does not deploy or manage that Azure network.
92
+ 4. **Self-hosted Azure Foundry egress (self-deploys)**: Foundry hosted-agent containers have floating egress just like AgentCore, so the agent must NOT call the facilitator directly. Run the envelope gateway on a Container Apps workload-profiles environment whose subnet has a NAT Gateway with a Standard static public IP, co-host a restricted B402 forwarder there (the AWS guide's Relay example works verbatim — the NAT Gateway replaces its fixed-IP host requirement), point the runtime `B402_BASE_URL` at that forwarder, and submit the NAT Gateway IP. The environment type and VNet cannot be changed after creation. Review the public [BNB Agent Studio deployment guide](https://docs.bnbchain.org/developer-kit/bnbchain-studio/deployment/) for provider constraints; the Studio source checkout keeps the subnet, ingress, and `minReplicas` recipe at `docs/guides/self-hosted-x402-gateway-azure.md`. Studio does not deploy or manage that Azure network.
93
93
 
94
94
  Do not add the public inbound gateway IP, a transient build-runner IP, or guessed addresses. If the whitelist endpoint is unreachable, stop onboarding and confirm the platform environment with the operator.
95
95
 
@@ -154,7 +154,7 @@ bag deploy --provider bnb # managed platform
154
154
  bag deploy --provider aws # self-hosted AgentCore
155
155
  ```
156
156
 
157
- 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.
157
+ 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. Review the public [BNB Agent Studio deployment guide](https://docs.bnbchain.org/developer-kit/bnbchain-studio/deployment/) for provider constraints; the Studio source checkout keeps the complete gateway wrapper, response parser, Relay example, and direct-invocation fallback at `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.
158
158
 
159
159
  ## Hard rules
160
160
 
@@ -11,7 +11,7 @@ Altana separates trusted administration from runtime authority:
11
11
  - `.studio/wallets/altana-session.json` is the one bounded, expiring runtime session and must stay mode `0600`.
12
12
  - `WALLET_PASSWORD` is admin-only. The Agent gets `ALTANA_SESSION`, never the password or admin keystore.
13
13
  - Generic signing is refused. ERC-8183 uses `sessionQuoteSigner()` and the approved quote checker.
14
- - The generated project pins `@bnbagent/sdk@0.5.5` and `@altananetwork/sdk@0.7.1`; doctor, readiness, and runtime loading reject version drift. SDK 0.5.4 introduced selector-bound calls, removed session-key token approvals, and requires an admin-provisioned bounded Commerce allowance. Projects upgrading from an older SDK must update it, re-grant with `bag wallet session grant --force`, and redeploy.
14
+ - The generated project pins `@bnbagent/sdk@0.5.6` and `@altananetwork/sdk@0.7.1`; doctor, readiness, and runtime loading reject version drift. SDK 0.5.4 introduced selector-bound calls, removed session-key token approvals, and requires an admin-provisioned bounded Commerce allowance. Projects upgrading from SDK versions older than 0.5.4 must update it, re-grant with `bag wallet session grant --force`, and redeploy.
15
15
  - Deployment ships ONLY the serialized session as the `ALTANA_SESSION` runtime secret; the admin keystore and `WALLET_PASSWORD` never leave the operator machine. Renewal after expiry: `bag wallet session grant --force`, then re-run `bag deploy`. Readiness fails on a missing/expired/address-mismatched session, a group/world-readable session file, a session inside the artifact root, or an unresolvable project-local `@altananetwork/sdk`; it warns under 7 days remaining. `bag deploy verify` needs `--skip-register` (no generic signing for the ERC-8004 register).
16
16
  - Altana refuses generic message signing, so Pieverse SIWE cannot authenticate `bag llm activate` or runtime credit renewal. `bag init --wallet-kind altana --llm-provider pieverse-llm` is rejected outright; use OpenRouter, OpenAI, or Anthropic (API-key providers). `bag llm activate` and `bag doctor` also flag the combination on projects edited by hand.
17
17
 
@@ -26,13 +26,18 @@ bag init <name> --wallet-kind altana --destination self --no-onboard
26
26
  # Edit <name>/.studio/.env.local and set WALLET_PASSWORD first.
27
27
  cd <name>/app/agent
28
28
  bag wallet new
29
- # fund the printed admin address with ~0.05 tBNB + U
29
+ # after GitHub login, request the configured tBNB + U grant for the admin
30
+ bag wallet fund
30
31
  bag wallet session grant
31
32
  bag wallet session status
32
33
  bag doctor
33
34
  bag dev
34
35
  ```
35
36
 
37
+ `bag wallet fund` always uses the Altana admin's encrypted keystore for an
38
+ EIP-191 ownership signature and sends funds to that same admin address. The
39
+ bounded runtime session is not involved in faucet claims.
40
+
36
41
  Interactive grant recommendations are 10 U/day, 30 days, register=yes. In a non-TTY, pass `--budget-u`, `--expiry-days`, optional `--no-register`, and `--yes`. Stdout from a successful grant is only the session public key.
37
42
 
38
43
  The grant persists the owner-only session, provisions a Commerce allowance no higher than its U-token cap, and approves the quote checker. Every relay-backed management write must return `CONFIRMED`; `PENDING` fails closed and preserves the session file. If either setup step fails after the paid grant, repair both with: