@bnbagent/studio-cli 0.0.13-alpha.8 → 0.0.14-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/bag.js +3556 -1565
- package/dist/{chunk-NTDWVEW2.js → chunk-QVYWAJEK.js} +228 -72
- package/dist/{deployCli-ZESBUWQB.js → deployCli-F3AOM5UO.js} +1 -2
- package/package.json +2 -2
- 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 +4 -3
- 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/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/wallet/recipe.toml +3 -2
- 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 +6 -1
- 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
|
@@ -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) =>
|
|
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(
|
|
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
|
-
|
|
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 ${
|
|
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(
|
|
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
|
-
|
|
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 {
|