@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.
- package/LICENSE +201 -0
- package/README.md +8 -0
- package/dist/_agentcoreName-DZDWEYD3.js +0 -0
- package/dist/bag.js +3741 -1612
- package/dist/{chunk-H4X2OOLA.js → chunk-CFMETPKS.js} +263 -73
- package/dist/chunk-U7IDQ3K5.js +0 -0
- package/dist/{deployCli-22NMZ4G7.js → deployCli-AVWKY3GN.js} +5 -2
- package/package.json +13 -14
- package/recipes/agent/code/{{PKG}}/deliveryPolicy.ts.tmpl +43 -0
- package/recipes/agent/code/{{PKG}}/readToolCatalog.ts.tmpl +102 -0
- package/recipes/agent/code/{{PKG}}/signing.ts.tmpl +7 -2
- package/recipes/agent/recipe.toml +5 -4
- package/recipes/mpp-buyer/recipe.toml +1 -1
- package/recipes/runtimes/agentcore/code/{{PKG}}/dualMain.ts.tmpl +6 -2
- package/recipes/runtimes/agentcore/code/{{PKG}}/mcpMain.ts.tmpl +173 -134
- package/recipes/runtimes/agentcore/code/{{PKG}}/model.ts.tmpl +7 -0
- package/recipes/runtimes/agentcore/code/{{PKG}}/sellerCore.ts.tmpl +31 -46
- package/recipes/runtimes/agentcore/code/{{PKG}}/tools.ts.tmpl +19 -48
- package/recipes/runtimes/agentcore/code/{{PKG}}/unifiedMain.ts.tmpl +2 -1
- package/recipes/runtimes/agentcore/recipe.toml +1 -1
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/mcpMain.ts.tmpl +173 -134
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/model.ts.tmpl +7 -0
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/sellerCore.ts.tmpl +31 -46
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/tools.ts.tmpl +19 -48
- package/recipes/runtimes/azure-foundry/code/{{PKG}}/unifiedMain.ts.tmpl +2 -1
- package/recipes/runtimes/azure-foundry/recipe.toml +1 -1
- package/recipes/wallet/recipe.toml +3 -2
- package/recipes/x402-buyer/recipe.toml +1 -1
- package/skills/bnbagent-studio.md +12 -1
- package/skills/references/bnbagent-studio-operating.md +1 -0
- package/skills/references/bnbagent-studio-scaffolding-agent.md +11 -34
- package/skills/references/bnbagent-studio-selling-via-b402.md +3 -3
- package/skills/references/bnbagent-studio-using-altana-wallet.md +7 -2
- package/skills/references/bnbagent-studio-using-twak-wallet.md +24 -76
- package/dist/_twak-4XF4H5PL.js +0 -25
- package/dist/chunk-RO726HJG.js +0 -175
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
85
|
-
inputSchema: z.object(
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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 {
|
|
@@ -32,7 +32,7 @@ node = [
|
|
|
32
32
|
# BNBAGENT_RUNTIME_SECRET_ID is set (default secretsmanager mode + platform).
|
|
33
33
|
"@aws-sdk/client-secrets-manager@^3.600.0",
|
|
34
34
|
"@bnbagent/studio-runtime",
|
|
35
|
-
"@bnbagent/sdk@0.5.
|
|
35
|
+
"@bnbagent/sdk@0.5.6",
|
|
36
36
|
# The LLM work hook (generateText + tools) and the model factory.
|
|
37
37
|
"ai@^7.0.29",
|
|
38
38
|
# Tool input schemas (AI SDK tools + MCP registerTool).
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
loadStudioToml,
|
|
61
61
|
type TomlTable,
|
|
62
62
|
} from "@bnbagent/studio-runtime/config";
|
|
63
|
+
import { maskUrlSecrets } from "@bnbagent/studio-runtime/audit";
|
|
63
64
|
import * as cr from "@bnbagent/studio-runtime/tools";
|
|
64
65
|
import {
|
|
65
66
|
ensureAltanaSessionLoaded,
|
|
@@ -84,13 +85,31 @@ import {
|
|
|
84
85
|
limitCommerceOperation,
|
|
85
86
|
requestLimitContext,
|
|
86
87
|
} from "./requestLimits.js";
|
|
88
|
+
import {
|
|
89
|
+
DeliveryTimeoutError,
|
|
90
|
+
deliveryTimeoutSeconds,
|
|
91
|
+
minimumDeliveryWindowSeconds,
|
|
92
|
+
withTimeout,
|
|
93
|
+
} from "./deliveryPolicy.js";
|
|
87
94
|
import * as signing from "./signing.js";
|
|
95
|
+
import { READ_TOOL_CATALOG, type ReadToolName } from "./readToolCatalog.js";
|
|
88
96
|
|
|
89
97
|
const APP_NAME = "agent";
|
|
98
|
+
function safeLogText(value: unknown): string {
|
|
99
|
+
const text =
|
|
100
|
+
value instanceof Error
|
|
101
|
+
? (value.stack ?? `${value.name}: ${value.message}`)
|
|
102
|
+
: String(value ?? "");
|
|
103
|
+
return maskUrlSecrets(text);
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
const log = {
|
|
91
|
-
info: (msg: string) => console.log(`[seller-agent.mcp] ${msg}`),
|
|
107
|
+
info: (msg: string) => console.log(`[seller-agent.mcp] ${safeLogText(msg)}`),
|
|
92
108
|
error: (msg: string, e?: unknown) =>
|
|
93
|
-
console.error(
|
|
109
|
+
console.error(
|
|
110
|
+
`[seller-agent.mcp] ERROR ${safeLogText(msg)}`,
|
|
111
|
+
safeLogText(e),
|
|
112
|
+
),
|
|
94
113
|
};
|
|
95
114
|
|
|
96
115
|
function protocolFailure(scope: string, error: unknown): never {
|
|
@@ -166,7 +185,8 @@ function generatorTag(): string {
|
|
|
166
185
|
|
|
167
186
|
function hasErc8183Rail(cfg: TomlTable): boolean {
|
|
168
187
|
const payments = asTable(cfg.payments);
|
|
169
|
-
|
|
188
|
+
const rail = asTable(payments?.erc8183);
|
|
189
|
+
return rail !== null && rail.enabled !== false;
|
|
170
190
|
}
|
|
171
191
|
|
|
172
192
|
function asTable(value: unknown): TomlTable | null {
|
|
@@ -198,39 +218,46 @@ function flatQuery(query: Record<string, unknown>): Record<string, string> {
|
|
|
198
218
|
// Deferred construction keeps negotiate and unpaid payment challenge paths from
|
|
199
219
|
// building the model, and keeps this module importable without the provider
|
|
200
220
|
// env until a deliverable is actually produced.
|
|
201
|
-
type
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
221
|
+
export type McpRunWork = (
|
|
222
|
+
prompt: string,
|
|
223
|
+
context: { sessionId: string; abortSignal?: AbortSignal },
|
|
224
|
+
) => Promise<string>;
|
|
225
|
+
|
|
226
|
+
/** The MCP delivery hook. Replace this builder for protocol-only projects. */
|
|
227
|
+
export function buildRunWork(): McpRunWork {
|
|
228
|
+
let run: McpRunWork | null = null;
|
|
229
|
+
return async (prompt, context) => {
|
|
230
|
+
if (run === null) {
|
|
231
|
+
const { buildModel } = await import("./model.js");
|
|
232
|
+
const { LLM_READ_TOOLS } = await import("./tools.js");
|
|
233
|
+
const model = buildModel(); // managed model w/ budget-gated LLM-credit auto-renew
|
|
234
|
+
run = async (p, { abortSignal: signal }) => {
|
|
235
|
+
const result = await generateText({
|
|
236
|
+
model,
|
|
237
|
+
system:
|
|
238
|
+
"You are a seller agent. The runtime has already authorized this task " +
|
|
239
|
+
"through its configured commerce rail. Complete the user's task now; " +
|
|
240
|
+
"do not ask for a job ID or additional payment. " +
|
|
241
|
+
"Be concrete and concise. Use the read-only chain tools when on-chain " +
|
|
242
|
+
"context helps. If a paid-data tool such as `buy_with_x402` is " +
|
|
243
|
+
"available to you, USE IT to fetch the data a task needs — those " +
|
|
244
|
+
"merchants (e.g. CoinMarketCap) charge via on-chain wallet payment, " +
|
|
245
|
+
"NOT an API key; never reply that you cannot complete the task for " +
|
|
246
|
+
"lack of an API key.",
|
|
247
|
+
prompt: p,
|
|
248
|
+
// READ-ONLY chain tools; signing is never an LLM tool. To add
|
|
249
|
+
// PAID x402 fetch tools (bag x402 trust + x402-buyer recipe):
|
|
250
|
+
// import { X402_BUYER_TOOLS } from "./x402Buyer.js";
|
|
251
|
+
// tools: { ...LLM_READ_TOOLS, ...X402_BUYER_TOOLS },
|
|
252
|
+
tools: LLM_READ_TOOLS,
|
|
253
|
+
stopWhen: stepCountIs(8),
|
|
254
|
+
abortSignal: signal,
|
|
255
|
+
});
|
|
256
|
+
return result.text.trim();
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return run(prompt, context);
|
|
260
|
+
};
|
|
234
261
|
}
|
|
235
262
|
|
|
236
263
|
// ── MCP server ────────────────────────────────────────────────────────────────
|
|
@@ -282,9 +309,10 @@ async function reportProgress(
|
|
|
282
309
|
|
|
283
310
|
/** Build the seller MCP server, gating commerce tools on the ERC-8183 rail. */
|
|
284
311
|
export function buildMcpServer(
|
|
285
|
-
opts: { commerceSkills?: boolean } = {},
|
|
312
|
+
opts: { commerceSkills?: boolean; runWork?: McpRunWork } = {},
|
|
286
313
|
): McpServer {
|
|
287
314
|
const server = new McpServer({ name: "bnbagent-seller", version: "1.0.0" });
|
|
315
|
+
const runWork = opts.runWork ?? buildRunWork();
|
|
288
316
|
|
|
289
317
|
// ── Commerce tools (signing is FIXED code in signing.ts) ──────────────────
|
|
290
318
|
if (opts.commerceSkills !== false) {
|
|
@@ -366,6 +394,8 @@ export function buildMcpServer(
|
|
|
366
394
|
});
|
|
367
395
|
}
|
|
368
396
|
|
|
397
|
+
const deadlineMs = Date.now() + deliveryTimeoutSeconds() * 1000;
|
|
398
|
+
|
|
369
399
|
// 1/4 — verify the funded job carries THIS agent's signed quote
|
|
370
400
|
// (eth_calls). Honour the `permanent` flag: a permanent failure is
|
|
371
401
|
// terminal ("rejected"); a transient one (chain read hiccup) is
|
|
@@ -373,7 +403,10 @@ export function buildMcpServer(
|
|
|
373
403
|
await reportProgress(extra, 1, 4);
|
|
374
404
|
let verdict: { ok: boolean; reason: string; permanent: boolean };
|
|
375
405
|
try {
|
|
376
|
-
verdict = await
|
|
406
|
+
verdict = await withTimeout(
|
|
407
|
+
signing.verifySignedJob(jid, minimumDeliveryWindowSeconds()),
|
|
408
|
+
Math.max(0.001, (deadlineMs - Date.now()) / 1000),
|
|
409
|
+
);
|
|
377
410
|
} catch (e) {
|
|
378
411
|
// a failed verify is transient; tell the buyer to retry
|
|
379
412
|
log.error(`verify of job ${jid} failed`, e);
|
|
@@ -393,37 +426,68 @@ export function buildMcpServer(
|
|
|
393
426
|
});
|
|
394
427
|
}
|
|
395
428
|
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const task =
|
|
403
|
-
spec !== null
|
|
404
|
-
? JSON.stringify({ task: spec.task, terms: spec.terms })
|
|
405
|
-
: `job ${jid}`;
|
|
406
|
-
const prompt =
|
|
407
|
-
"You accepted and were paid for the following job. Produce the deliverable " +
|
|
408
|
-
`now. Be complete and self-contained.\n\nJOB CONTEXT:\n${task}`;
|
|
409
|
-
work = await runLlm(prompt);
|
|
410
|
-
} catch (e) {
|
|
411
|
-
return protocolFailure(`delivery preparation for job ${jid} failed`, e);
|
|
412
|
-
}
|
|
413
|
-
// Unexpected LLM/RPC faults are logged in full, then surfaced through
|
|
414
|
-
// MCP's isError channel with a generic public message. Only the
|
|
415
|
-
// deterministic SubmitPermanentlyUnsupportedError is a classified
|
|
416
|
-
// "rejected" business result.
|
|
417
|
-
// 3/4 — sign + broadcast the on-chain submit (re-verifies FUNDED inside)
|
|
418
|
-
await reportProgress(extra, 3, 4);
|
|
429
|
+
// Bound only the cancellable work phase. Once submit starts, its
|
|
430
|
+
// broadcast outcome can be ambiguous: the request still has a total
|
|
431
|
+
// deadline, but a submit timeout is UNKNOWN and must be reconciled on
|
|
432
|
+
// chain rather than reported as safe to retry.
|
|
433
|
+
const controller = new AbortController();
|
|
434
|
+
let submitStarted = false;
|
|
419
435
|
let res: { submitTx: string; deliverableUrl: string | null };
|
|
420
436
|
try {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
437
|
+
const work = await withTimeout(
|
|
438
|
+
(async () => {
|
|
439
|
+
// 2/4 — produce the deliverable (THE ONLY LLM CALL).
|
|
440
|
+
await reportProgress(extra, 2, 4);
|
|
441
|
+
const spec = await signing.jobSpec(jid);
|
|
442
|
+
const task =
|
|
443
|
+
spec !== null
|
|
444
|
+
? JSON.stringify({ task: spec.task, terms: spec.terms })
|
|
445
|
+
: `job ${jid}`;
|
|
446
|
+
const prompt =
|
|
447
|
+
"You accepted and were paid for the following job. Produce the deliverable " +
|
|
448
|
+
`now. Be complete and self-contained.\n\nJOB CONTEXT:\n${task}`;
|
|
449
|
+
return runWork(prompt, {
|
|
450
|
+
sessionId: String(jid),
|
|
451
|
+
abortSignal: controller.signal,
|
|
452
|
+
});
|
|
453
|
+
})(),
|
|
454
|
+
Math.max(0.001, (deadlineMs - Date.now()) / 1000),
|
|
455
|
+
controller,
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
// 3/4 — sign + broadcast submit (re-verifies FUNDED inside). Await
|
|
459
|
+
// the definitive result within the remaining request window. The
|
|
460
|
+
// opaque SDK operation cannot be cancelled after broadcast.
|
|
461
|
+
await reportProgress(extra, 3, 4);
|
|
462
|
+
const remainingSeconds = (deadlineMs - Date.now()) / 1000;
|
|
463
|
+
if (remainingSeconds <= 0) {
|
|
464
|
+
throw new DeliveryTimeoutError("delivery deadline reached");
|
|
465
|
+
}
|
|
466
|
+
submitStarted = true;
|
|
467
|
+
res = await withTimeout(
|
|
468
|
+
signing.submitResult(jid, work, {
|
|
469
|
+
job_id: jid,
|
|
470
|
+
generator: generatorTag(),
|
|
471
|
+
built_with: "https://github.com/bnb-chain/bnbagent-studio",
|
|
472
|
+
}),
|
|
473
|
+
remainingSeconds,
|
|
474
|
+
);
|
|
426
475
|
} catch (e) {
|
|
476
|
+
if (e instanceof DeliveryTimeoutError) {
|
|
477
|
+
if (submitStarted) {
|
|
478
|
+
return toolResult({
|
|
479
|
+
status: "unknown",
|
|
480
|
+
job_id: jid,
|
|
481
|
+
reason:
|
|
482
|
+
"submit outcome is unknown; poll the job on-chain and do not retry until reconciled",
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return toolResult({
|
|
486
|
+
status: "retry",
|
|
487
|
+
job_id: jid,
|
|
488
|
+
reason: `delivery timed out after ${deliveryTimeoutSeconds()}s`,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
427
491
|
if (
|
|
428
492
|
e instanceof Error &&
|
|
429
493
|
e.name === "SubmitPermanentlyUnsupportedError"
|
|
@@ -436,7 +500,7 @@ export function buildMcpServer(
|
|
|
436
500
|
reason: "seller wallet does not support result submission",
|
|
437
501
|
});
|
|
438
502
|
}
|
|
439
|
-
return protocolFailure(`
|
|
503
|
+
return protocolFailure(`delivery of job ${jid} failed`, e);
|
|
440
504
|
}
|
|
441
505
|
|
|
442
506
|
// 4/4 — done
|
|
@@ -452,95 +516,76 @@ export function buildMcpServer(
|
|
|
452
516
|
}
|
|
453
517
|
|
|
454
518
|
// ── Read-only chain tools ──────────────────────────────────────────────────
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
inputSchema,
|
|
519
|
+
const roConfig = <Name extends ReadToolName>(name: Name) => ({
|
|
520
|
+
description: READ_TOOL_CATALOG[name].description,
|
|
521
|
+
inputSchema: READ_TOOL_CATALOG[name].inputSchema,
|
|
459
522
|
annotations: READONLY_ANNOTATIONS,
|
|
460
523
|
});
|
|
524
|
+
type ReadArgs<Name extends ReadToolName> = z.infer<
|
|
525
|
+
z.ZodObject<(typeof READ_TOOL_CATALOG)[Name]["inputSchema"]>
|
|
526
|
+
>;
|
|
461
527
|
|
|
462
528
|
server.registerTool(
|
|
463
529
|
"wallet_info",
|
|
464
|
-
roConfig("
|
|
530
|
+
roConfig("wallet_info"),
|
|
465
531
|
async () => toolResult(await cr.walletInfo()),
|
|
466
532
|
);
|
|
467
533
|
server.registerTool(
|
|
468
534
|
"wallet_list",
|
|
469
|
-
roConfig("
|
|
535
|
+
roConfig("wallet_list"),
|
|
470
536
|
async () => toolResult(await cr.walletList()),
|
|
471
537
|
);
|
|
472
538
|
server.registerTool(
|
|
473
539
|
"wallet_address",
|
|
474
|
-
roConfig("
|
|
540
|
+
roConfig("wallet_address"),
|
|
475
541
|
async () => toolResult({ address: await cr.walletAddress() }),
|
|
476
542
|
);
|
|
477
543
|
server.registerTool(
|
|
478
544
|
"balance_native",
|
|
479
|
-
roConfig("
|
|
480
|
-
|
|
481
|
-
network,
|
|
482
|
-
}),
|
|
483
|
-
async (a) =>
|
|
545
|
+
roConfig("balance_native"),
|
|
546
|
+
async (a: ReadArgs<"balance_native">) =>
|
|
484
547
|
toolResult(
|
|
485
548
|
await cr.balanceNative(a.address ?? null, a.network ?? defaultNetwork()),
|
|
486
549
|
),
|
|
487
550
|
);
|
|
488
551
|
server.registerTool(
|
|
489
552
|
"balance_u",
|
|
490
|
-
roConfig("
|
|
491
|
-
|
|
492
|
-
network,
|
|
493
|
-
}),
|
|
494
|
-
async (a) =>
|
|
553
|
+
roConfig("balance_u"),
|
|
554
|
+
async (a: ReadArgs<"balance_u">) =>
|
|
495
555
|
toolResult(
|
|
496
556
|
await cr.balanceU(a.address ?? null, a.network ?? defaultNetwork()),
|
|
497
557
|
),
|
|
498
558
|
);
|
|
499
559
|
server.registerTool(
|
|
500
560
|
"pieverse_usage",
|
|
501
|
-
roConfig(
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
),
|
|
505
|
-
async (a) => toolResult(await cr.pieverseUsage(a.days ?? 7)),
|
|
561
|
+
roConfig("pieverse_usage"),
|
|
562
|
+
async (a: ReadArgs<"pieverse_usage">) =>
|
|
563
|
+
toolResult(await cr.pieverseUsage(a.days ?? 7)),
|
|
506
564
|
);
|
|
507
565
|
server.registerTool(
|
|
508
566
|
"agent_info",
|
|
509
|
-
roConfig("
|
|
510
|
-
|
|
511
|
-
network,
|
|
512
|
-
}),
|
|
513
|
-
async (a) =>
|
|
567
|
+
roConfig("agent_info"),
|
|
568
|
+
async (a: ReadArgs<"agent_info">) =>
|
|
514
569
|
toolResult(await cr.agentInfo(a.agent_id, a.network ?? defaultNetwork())),
|
|
515
570
|
);
|
|
516
571
|
server.registerTool(
|
|
517
572
|
"agent_by_address",
|
|
518
|
-
roConfig("
|
|
519
|
-
|
|
520
|
-
network,
|
|
521
|
-
}),
|
|
522
|
-
async (a) =>
|
|
573
|
+
roConfig("agent_by_address"),
|
|
574
|
+
async (a: ReadArgs<"agent_by_address">) =>
|
|
523
575
|
toolResult(
|
|
524
576
|
await cr.agentByAddress(a.address, a.network ?? defaultNetwork()),
|
|
525
577
|
),
|
|
526
578
|
);
|
|
527
579
|
server.registerTool(
|
|
528
580
|
"job_status",
|
|
529
|
-
roConfig("
|
|
530
|
-
|
|
531
|
-
network,
|
|
532
|
-
}),
|
|
533
|
-
async (a) =>
|
|
581
|
+
roConfig("job_status"),
|
|
582
|
+
async (a: ReadArgs<"job_status">) =>
|
|
534
583
|
toolResult(await cr.jobStatus(a.job_id, a.network ?? defaultNetwork())),
|
|
535
584
|
);
|
|
536
585
|
server.registerTool(
|
|
537
586
|
"job_list",
|
|
538
|
-
roConfig("
|
|
539
|
-
|
|
540
|
-
mine: z.boolean().optional(),
|
|
541
|
-
network,
|
|
542
|
-
}),
|
|
543
|
-
async (a) =>
|
|
587
|
+
roConfig("job_list"),
|
|
588
|
+
async (a: ReadArgs<"job_list">) =>
|
|
544
589
|
toolResult(
|
|
545
590
|
await cr.jobList({
|
|
546
591
|
limit: a.limit,
|
|
@@ -551,39 +596,28 @@ export function buildMcpServer(
|
|
|
551
596
|
);
|
|
552
597
|
server.registerTool(
|
|
553
598
|
"job_count",
|
|
554
|
-
roConfig("
|
|
555
|
-
async (a) =>
|
|
599
|
+
roConfig("job_count"),
|
|
600
|
+
async (a: ReadArgs<"job_count">) =>
|
|
601
|
+
toolResult(await cr.jobCount(a.network ?? defaultNetwork())),
|
|
556
602
|
);
|
|
557
603
|
server.registerTool(
|
|
558
604
|
"tx_status",
|
|
559
|
-
roConfig("
|
|
560
|
-
|
|
561
|
-
network,
|
|
562
|
-
}),
|
|
563
|
-
async (a) =>
|
|
605
|
+
roConfig("tx_status"),
|
|
606
|
+
async (a: ReadArgs<"tx_status">) =>
|
|
564
607
|
toolResult(await cr.txStatus(a.tx_hash, a.network ?? defaultNetwork())),
|
|
565
608
|
);
|
|
566
609
|
server.registerTool(
|
|
567
610
|
"block_info",
|
|
568
|
-
roConfig(
|
|
569
|
-
|
|
570
|
-
{ block: z.string().optional(), network },
|
|
571
|
-
),
|
|
572
|
-
async (a) =>
|
|
611
|
+
roConfig("block_info"),
|
|
612
|
+
async (a: ReadArgs<"block_info">) =>
|
|
573
613
|
toolResult(
|
|
574
614
|
await cr.blockInfo(a.block ?? "latest", a.network ?? defaultNetwork()),
|
|
575
615
|
),
|
|
576
616
|
);
|
|
577
617
|
server.registerTool(
|
|
578
618
|
"contract_call_view",
|
|
579
|
-
roConfig("
|
|
580
|
-
|
|
581
|
-
function_signature: z.string(),
|
|
582
|
-
args: z.array(z.unknown()).optional(),
|
|
583
|
-
output_types: z.array(z.string()).optional(),
|
|
584
|
-
network,
|
|
585
|
-
}),
|
|
586
|
-
async (a) =>
|
|
619
|
+
roConfig("contract_call_view"),
|
|
620
|
+
async (a: ReadArgs<"contract_call_view">) =>
|
|
587
621
|
toolResult(
|
|
588
622
|
await cr.contractCallView(
|
|
589
623
|
a.address,
|
|
@@ -596,8 +630,9 @@ export function buildMcpServer(
|
|
|
596
630
|
);
|
|
597
631
|
server.registerTool(
|
|
598
632
|
"network_info",
|
|
599
|
-
roConfig("
|
|
600
|
-
async (a) =>
|
|
633
|
+
roConfig("network_info"),
|
|
634
|
+
async (a: ReadArgs<"network_info">) =>
|
|
635
|
+
toolResult(await cr.networkInfo(a.network ?? defaultNetwork())),
|
|
601
636
|
);
|
|
602
637
|
|
|
603
638
|
return server;
|
|
@@ -628,9 +663,10 @@ async function main(): Promise<void> {
|
|
|
628
663
|
const sellPath = b402SellPath(cfg);
|
|
629
664
|
const host = process.env.AGENT_BIND_HOST || "0.0.0.0";
|
|
630
665
|
const port = Number(process.env.AGENT_PORT || "8000");
|
|
666
|
+
const runWork = buildRunWork();
|
|
631
667
|
const seller = await B402Seller.create({
|
|
632
668
|
cfg,
|
|
633
|
-
runWork: ({ prompt }) =>
|
|
669
|
+
runWork: ({ prompt }) => runWork(prompt, { sessionId: "b402" }),
|
|
634
670
|
walletAddress: getWallet().address,
|
|
635
671
|
resourceUrl: `${
|
|
636
672
|
process.env.AGENTCORE_RUNTIME_URL ?? `http://localhost:${port}`
|
|
@@ -690,7 +726,10 @@ async function main(): Promise<void> {
|
|
|
690
726
|
delete transports[t.sessionId];
|
|
691
727
|
}
|
|
692
728
|
};
|
|
693
|
-
await buildMcpServer({
|
|
729
|
+
await buildMcpServer({
|
|
730
|
+
commerceSkills: rails.erc8183,
|
|
731
|
+
runWork,
|
|
732
|
+
}).connect(t);
|
|
694
733
|
transport = t;
|
|
695
734
|
}
|
|
696
735
|
await transport.handleRequest(req, res, req.body);
|
|
@@ -57,6 +57,13 @@ import {
|
|
|
57
57
|
export function buildModel(): LanguageModel {
|
|
58
58
|
const cfg = loadStudioToml();
|
|
59
59
|
const llmCfg = (cfg.llm ?? {}) as TomlTable;
|
|
60
|
+
if (String(llmCfg.provider ?? "openrouter") === "none") {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"Protocol-only scaffold: no LLM provider is configured. " +
|
|
63
|
+
"Implement the generated work hook before accepting funded jobs, " +
|
|
64
|
+
"or set [llm].provider and [llm].model in studio.toml.",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
60
67
|
const inner = resolveModel(llmCfg);
|
|
61
68
|
|
|
62
69
|
if (String(llmCfg.provider ?? "openrouter") !== "pieverse-llm") {
|