opencrowd 0.7.0 → 0.8.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 CHANGED
@@ -84,6 +84,18 @@ There is one wallet: AgentCash's. OpenCrowd shows public addresses,
84
84
  balances, and funding links (`/wallet`, `/fund`) and never manages, copies,
85
85
  or exports keys. See [SECURITY.md](SECURITY.md) for the enforcement model.
86
86
 
87
+ Paid services settle in USDC on the network you prefer when they offer it:
88
+ x402 on Base (the default) or Solana, MPP on Tempo, and x402 on Monad when
89
+ you choose it. Solana uses AgentCash's separate Solana address
90
+ (`~/.agentcash/solana-wallet.json`); `opencrowd wallet accounts` lists every
91
+ network's address and balance. When a service does not offer your preferred
92
+ network, the first of Base, Solana and Tempo whose balance covers the price is
93
+ used.
94
+
95
+ ```sh
96
+ opencrowd config set payment-network solana # or --payment-network solana per run
97
+ ```
98
+
87
99
  ## Spending limits
88
100
 
89
101
  By default, each model or paid tool call can spend up to **$1** automatically,
@@ -137,6 +149,7 @@ opencrowd run --budget 1.00 "Find a service and summarize options"
137
149
  opencrowd run --session <session-id> "Follow up on the previous result"
138
150
  opencrowd config show
139
151
  opencrowd wallet balance
152
+ opencrowd wallet accounts
140
153
  opencrowd doctor
141
154
  opencrowd evals gaia --tier smoke --harness opencrowd,claude,codex
142
155
  opencrowd evals suite --set all --tag baseline --model openai/gpt-5.6-luna --subagent-model off
@@ -50,7 +50,7 @@ var __toESM = (mod4, isNodeMode, target) => (target = mod4 != null ? __create(__
50
50
 
51
51
  // ../../packages/core/dist/agentcash.js
52
52
  import { readFile } from "node:fs/promises";
53
- import { join } from "node:path";
53
+ import { dirname, join } from "node:path";
54
54
  import { homedir } from "node:os";
55
55
  function agentCashWalletPath() {
56
56
  return process.env.AGENTCASH_WALLET_PATH ?? join(homedir(), ".agentcash", "wallet.json");
@@ -65,6 +65,14 @@ async function readAgentCashWallet() {
65
65
  return void 0;
66
66
  }
67
67
  }
68
+ async function readAgentCashSolanaAddress() {
69
+ try {
70
+ const parsed = JSON.parse(await readFile(join(dirname(agentCashWalletPath()), "solana-wallet.json"), "utf8"));
71
+ return typeof parsed.address === "string" && /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(parsed.address) ? parsed.address : void 0;
72
+ } catch {
73
+ return void 0;
74
+ }
75
+ }
68
76
  async function requireAgentCashWallet() {
69
77
  const wallet = await readAgentCashWallet();
70
78
  if (!wallet) {
@@ -80,9 +88,9 @@ var init_agentcash = __esm({
80
88
 
81
89
  // ../../packages/core/dist/ledger.js
82
90
  import { appendFile, mkdir, readFile as readFile2, writeFile } from "node:fs/promises";
83
- import { dirname } from "node:path";
91
+ import { dirname as dirname2 } from "node:path";
84
92
  async function ensureLedger(path3) {
85
- await mkdir(dirname(path3), { recursive: true });
93
+ await mkdir(dirname2(path3), { recursive: true });
86
94
  try {
87
95
  await readFile2(path3, "utf8");
88
96
  } catch (error53) {
@@ -182,7 +190,7 @@ var init_ledger = __esm({
182
190
  // ../../packages/core/dist/paths.js
183
191
  import { lstat, mkdir as mkdir2, open, rename, rm } from "node:fs/promises";
184
192
  import { homedir as homedir2 } from "node:os";
185
- import { dirname as dirname2, isAbsolute, join as join2, relative, resolve, sep } from "node:path";
193
+ import { dirname as dirname3, isAbsolute, join as join2, relative, resolve, sep } from "node:path";
186
194
  import { randomUUID } from "node:crypto";
187
195
  function resolveAgentPaths(options = {}) {
188
196
  if (options.agentHome) {
@@ -218,7 +226,7 @@ async function containedPath(root, requested) {
218
226
  if (error53.code !== "ENOENT")
219
227
  throw error53;
220
228
  }
221
- const parent = dirname2(current);
229
+ const parent = dirname3(current);
222
230
  if (parent === current)
223
231
  break;
224
232
  current = parent;
@@ -226,7 +234,7 @@ async function containedPath(root, requested) {
226
234
  return target;
227
235
  }
228
236
  async function atomicWrite(path3, content) {
229
- await mkdir2(dirname2(path3), { recursive: true });
237
+ await mkdir2(dirname3(path3), { recursive: true });
230
238
  const temp = `${path3}.${randomUUID()}.tmp`;
231
239
  const handle = await open(temp, "wx", 384);
232
240
  try {
@@ -237,7 +245,7 @@ async function atomicWrite(path3, content) {
237
245
  }
238
246
  try {
239
247
  await rename(temp, path3);
240
- const dir = await open(dirname2(path3), "r");
248
+ const dir = await open(dirname3(path3), "r");
241
249
  try {
242
250
  await dir.sync();
243
251
  } finally {
@@ -255,10 +263,10 @@ var init_paths = __esm({
255
263
 
256
264
  // ../../packages/core/dist/artifacts.js
257
265
  import { mkdir as mkdir3, readFile as readFile3 } from "node:fs/promises";
258
- import { dirname as dirname3, join as join3, relative as relative2 } from "node:path";
266
+ import { dirname as dirname4, join as join3, relative as relative2 } from "node:path";
259
267
  async function saveArtifact(state, path3, content, metadata) {
260
268
  const target = await safeArtifactPath(state, path3);
261
- await mkdir3(dirname3(target), { recursive: true });
269
+ await mkdir3(dirname4(target), { recursive: true });
262
270
  await atomicWrite(target, content);
263
271
  const rel = relative2(state.sessionDir, target);
264
272
  await appendLedgerEntry(state.ledgerPath, {
@@ -16115,12 +16123,49 @@ var init_spending = __esm({
16115
16123
  });
16116
16124
 
16117
16125
  // ../../packages/protocol/dist/financial-state.js
16118
- var usdc, FinancialStateSchema;
16126
+ function atomicUsdc(value) {
16127
+ const amount = BigInt(value);
16128
+ return `${amount / 1000000n}.${(amount % 1000000n).toString().padStart(6, "0")}`;
16129
+ }
16130
+ function parseNetworkBalances(raw) {
16131
+ if (!Array.isArray(raw))
16132
+ return void 0;
16133
+ const rows = raw.flatMap((entry) => {
16134
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
16135
+ return [];
16136
+ const row = entry;
16137
+ const balance = decimal(row.balance_usdc) ?? atomic(row.balance_atomic ?? row.balanceAtomic);
16138
+ const reserved = decimal(row.reserved_usdc) ?? atomic(row.reserved_atomic ?? row.reservedAtomic);
16139
+ const available = decimal(row.available_usdc) ?? atomic(row.available_atomic ?? row.availableAtomic);
16140
+ const status = row.balance_status ?? row.status;
16141
+ const parsed = NetworkBalanceSchema.safeParse({
16142
+ network: row.network,
16143
+ ...typeof row.caip2 === "string" ? { caip2: row.caip2 } : {},
16144
+ ...typeof row.address === "string" || row.address === null ? { address: row.address } : {},
16145
+ balance_status: status === "available" || status === "unavailable" || status === "no_address" ? status : balance ? "available" : "unavailable",
16146
+ balance_usdc: balance ?? null,
16147
+ ...reserved ? { reserved_usdc: reserved } : {},
16148
+ ...available ? { available_usdc: available } : {}
16149
+ });
16150
+ return parsed.success ? [parsed.data] : [];
16151
+ });
16152
+ return rows.length ? rows : void 0;
16153
+ }
16154
+ var usdc, NetworkBalanceSchema, FinancialStateSchema, atomic, decimal;
16119
16155
  var init_financial_state = __esm({
16120
16156
  "../../packages/protocol/dist/financial-state.js"() {
16121
16157
  "use strict";
16122
16158
  init_zod();
16123
16159
  usdc = external_exports.string().regex(/^\d+\.\d{6}$/);
16160
+ NetworkBalanceSchema = external_exports.object({
16161
+ network: external_exports.string().min(1).max(40),
16162
+ caip2: external_exports.string().max(100).optional(),
16163
+ address: external_exports.string().max(100).nullable().optional(),
16164
+ balance_status: external_exports.enum(["available", "unavailable", "no_address"]),
16165
+ balance_usdc: usdc.nullable(),
16166
+ reserved_usdc: usdc.optional(),
16167
+ available_usdc: usdc.nullable().optional()
16168
+ });
16124
16169
  FinancialStateSchema = external_exports.discriminatedUnion("status", [
16125
16170
  external_exports.object({
16126
16171
  status: external_exports.literal("available"),
@@ -16131,7 +16176,8 @@ var init_financial_state = __esm({
16131
16176
  balance_status: external_exports.enum(["available", "unavailable"]),
16132
16177
  balance_usdc: usdc.nullable(),
16133
16178
  reserved_usdc: usdc,
16134
- available_usdc: usdc.nullable()
16179
+ available_usdc: usdc.nullable(),
16180
+ networks: external_exports.array(NetworkBalanceSchema).max(16).optional()
16135
16181
  }),
16136
16182
  current_run: external_exports.object({
16137
16183
  inference_spent_usdc: usdc,
@@ -16146,6 +16192,8 @@ var init_financial_state = __esm({
16146
16192
  }),
16147
16193
  external_exports.object({ status: external_exports.literal("unavailable"), reason: external_exports.string() })
16148
16194
  ]);
16195
+ atomic = (value) => typeof value === "string" && /^\d+$/.test(value) ? atomicUsdc(value) : typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? atomicUsdc(BigInt(value)) : void 0;
16196
+ decimal = (value) => typeof value === "string" && /^\d+\.\d{6}$/.test(value) ? value : void 0;
16149
16197
  }
16150
16198
  });
16151
16199
 
@@ -16316,12 +16364,12 @@ async function reserveBudget(state, amountCents, action) {
16316
16364
  assertCents(amountCents, "reservation");
16317
16365
  if (state.query) {
16318
16366
  const committed = state.spentCents - state.query.startSpentCents + state.reservedCents;
16319
- const atomic = (cents) => String(Math.round(cents * 1e4));
16367
+ const atomic2 = (cents) => String(Math.round(cents * 1e4));
16320
16368
  const check3 = spendingDecision({
16321
- amount: atomic(amountCents),
16322
- perCall: atomic(state.perCallCents ?? 100),
16323
- queryLimit: atomic(state.query.limitCents),
16324
- queryCommitted: atomic(committed)
16369
+ amount: atomic2(amountCents),
16370
+ perCall: atomic2(state.perCallCents ?? 100),
16371
+ queryLimit: atomic2(state.query.limitCents),
16372
+ queryCommitted: atomic2(committed)
16325
16373
  });
16326
16374
  const id = createHash("sha256").update(JSON.stringify([state.query.id, action?.identity ?? "call", amountCents])).digest("hex");
16327
16375
  const approved = state.query.approved?.[id] !== void 0 && state.query.approved[id] >= amountCents;
@@ -16412,7 +16460,7 @@ var init_budget = __esm({
16412
16460
 
16413
16461
  // ../../packages/core/dist/config.js
16414
16462
  import { mkdir as mkdir5, readFile as readFile5 } from "node:fs/promises";
16415
- import { dirname as dirname4, join as join5 } from "node:path";
16463
+ import { dirname as dirname5, join as join5 } from "node:path";
16416
16464
  function configDir(paths2) {
16417
16465
  return (paths2 ?? resolveAgentPaths()).config;
16418
16466
  }
@@ -16431,7 +16479,7 @@ async function loadConfig(paths2) {
16431
16479
  }
16432
16480
  }
16433
16481
  async function saveConfig(config4, paths2) {
16434
- await mkdir5(dirname4(configPath(paths2)), { recursive: true });
16482
+ await mkdir5(dirname5(configPath(paths2)), { recursive: true });
16435
16483
  await atomicWrite(configPath(paths2), `${JSON.stringify(config4, null, 2)}
16436
16484
  `);
16437
16485
  }
@@ -16447,7 +16495,7 @@ function normalizeConfig(value) {
16447
16495
  const proxyDefaults = providerDefaults(raw["openrouter-x402-proxy"] ?? raw.x402, DEFAULT_CONFIG["openrouter-x402-proxy"]);
16448
16496
  const mcpServers = recordValue(raw.mcpServers);
16449
16497
  const servers = { ...DEFAULT_CONFIG.mcpServers, ...mcpServers };
16450
- if (servers.crowdcode?.command === "npx" && ["0.5", "0.5.2", "0.5.3", "0.6.0"].some((version6) => JSON.stringify(servers.crowdcode.args) === JSON.stringify(["--yes", `crowdcode-mcp@${version6}`]))) {
16498
+ if (servers.crowdcode?.command === "npx" && ["0.5", "0.5.2", "0.5.3", "0.6.0", "0.7.0"].some((version6) => JSON.stringify(servers.crowdcode.args) === JSON.stringify(["--yes", `crowdcode-mcp@${version6}`]))) {
16451
16499
  servers.crowdcode = { ...servers.crowdcode, args: [...DEFAULT_CONFIG.mcpServers.crowdcode.args] };
16452
16500
  }
16453
16501
  return {
@@ -16471,6 +16519,8 @@ function parsePaymentNetwork(value) {
16471
16519
  const normalized = typeof value === "string" ? value.trim().toLowerCase() : void 0;
16472
16520
  if (normalized === "monad" || normalized === "eip155:143")
16473
16521
  return "monad";
16522
+ if (normalized === "solana" || normalized === "solana:5eykt4usfv8p8njdtrepy1vzqkqzkvdp")
16523
+ return "solana";
16474
16524
  if (normalized === "base" || normalized === "eip155:8453")
16475
16525
  return "base";
16476
16526
  return void 0;
@@ -16480,7 +16530,7 @@ function resolvePaymentNetwork(config4) {
16480
16530
  if (env !== void 0 && env.trim() !== "") {
16481
16531
  const parsed = parsePaymentNetwork(env);
16482
16532
  if (!parsed)
16483
- throw new Error("OPENCROWD_PAYMENT_NETWORK must be base or monad");
16533
+ throw new Error(`OPENCROWD_PAYMENT_NETWORK must be ${PAYMENT_NETWORKS.join(", ")}`);
16484
16534
  return parsed;
16485
16535
  }
16486
16536
  return config4?.paymentNetwork ?? DEFAULT_CONFIG.paymentNetwork;
@@ -16506,17 +16556,18 @@ function stringValue(value) {
16506
16556
  function numberValue(value) {
16507
16557
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
16508
16558
  }
16509
- var DEFAULT_CONFIG;
16559
+ var PAYMENT_NETWORKS, DEFAULT_CONFIG;
16510
16560
  var init_config = __esm({
16511
16561
  "../../packages/core/dist/config.js"() {
16512
16562
  "use strict";
16513
16563
  init_dist();
16514
16564
  init_paths();
16565
+ PAYMENT_NETWORKS = ["base", "solana", "monad"];
16515
16566
  DEFAULT_CONFIG = {
16516
16567
  configVersion: 3,
16517
16568
  mcpServers: {
16518
16569
  agentcash: { command: "npx", args: ["--yes", "agentcash@0.17"] },
16519
- crowdcode: { command: "npx", args: ["--yes", "crowdcode-mcp@0.7.0"] }
16570
+ crowdcode: { command: "npx", args: ["--yes", "crowdcode-mcp@0.8.0"] }
16520
16571
  },
16521
16572
  // GAIA provider benchmark (2026-08-25): same-model BlockRun matched the
16522
16573
  // current route's accuracy and completed the sample 6.7x faster. The
@@ -16544,7 +16595,7 @@ var init_config = __esm({
16544
16595
 
16545
16596
  // ../../packages/core/dist/conversation.js
16546
16597
  import { appendFile as appendFile2, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile2 } from "node:fs/promises";
16547
- import { dirname as dirname5, join as join6 } from "node:path";
16598
+ import { dirname as dirname6, join as join6 } from "node:path";
16548
16599
  function conversationPath(session) {
16549
16600
  return join6(session.sessionDir, CONVERSATION_FILE);
16550
16601
  }
@@ -16557,7 +16608,7 @@ async function appendConversationMessage(session, message) {
16557
16608
  }
16558
16609
  async function appendConversationEntry(session, entry) {
16559
16610
  const path3 = conversationPath(session);
16560
- await mkdir6(dirname5(path3), { recursive: true });
16611
+ await mkdir6(dirname6(path3), { recursive: true });
16561
16612
  await appendFile2(path3, `${JSON.stringify(entry)}
16562
16613
  `, "utf8");
16563
16614
  }
@@ -16600,7 +16651,7 @@ async function clearConversation(session) {
16600
16651
  }
16601
16652
  const archivePath = join6("context", `cleared-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.jsonl`);
16602
16653
  const absoluteArchivePath = join6(session.sessionDir, archivePath);
16603
- await mkdir6(dirname5(absoluteArchivePath), { recursive: true });
16654
+ await mkdir6(dirname6(absoluteArchivePath), { recursive: true });
16604
16655
  await writeFile2(absoluteArchivePath, messages.map((message) => `${JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), message })}
16605
16656
  `).join(""), "utf8");
16606
16657
  await writeFile2(conversationPath(session), `${JSON.stringify({ type: "clear", timestamp: (/* @__PURE__ */ new Date()).toISOString(), archive_path: archivePath })}
@@ -17021,9 +17072,17 @@ async function executeTool(name, args, context) {
17021
17072
  return ok({ content: await readArtifact(context.session, requiredString(args.path, "path")) });
17022
17073
  case "list_files":
17023
17074
  return ok(await listArtifacts(context.session, optionalString(args.prefix, "prefix") ?? ""));
17024
- case "run_shell":
17075
+ case "run_shell": {
17025
17076
  context.onProgress?.({ type: "running_shell", message: "Running gated shell command" });
17026
- return ok(await runShell(context.session, requiredString(args.command, "command"), optionalString(args.cwd, "cwd") ?? context.session.workspaceRoot, shellTimeoutMs(args.timeout_ms), { signal: context.signal }));
17077
+ const result = await runShell(context.session, requiredString(args.command, "command"), optionalString(args.cwd, "cwd") ?? context.session.workspaceRoot, shellTimeoutMs(args.timeout_ms), { signal: context.signal });
17078
+ if (result.timed_out || result.exit_code !== 0)
17079
+ return {
17080
+ ok: false,
17081
+ error: result.timed_out ? "Shell command timed out; inspect partial output before retrying." : result.exit_code === null ? "Shell command was terminated; inspect stderr and resource limits before retrying." : `Shell command exited with code ${result.exit_code}; inspect stderr and partial output before choosing the next approach.`,
17082
+ data: result
17083
+ };
17084
+ return ok(result);
17085
+ }
17027
17086
  case "spawn_subagent":
17028
17087
  case "check_subagents":
17029
17088
  return {
@@ -46189,7 +46248,7 @@ async function getCallsStatus(client, parameters) {
46189
46248
  params: [id]
46190
46249
  });
46191
46250
  }
46192
- const { atomic = false, chainId, receipts, version: version6 = "2.0.0", ...response } = await getStatus(parameters.id);
46251
+ const { atomic: atomic2 = false, chainId, receipts, version: version6 = "2.0.0", ...response } = await getStatus(parameters.id);
46193
46252
  const [status, statusCode] = (() => {
46194
46253
  const statusCode2 = response.status;
46195
46254
  if (statusCode2 >= 100 && statusCode2 < 200)
@@ -46206,7 +46265,7 @@ async function getCallsStatus(client, parameters) {
46206
46265
  })();
46207
46266
  return {
46208
46267
  ...response,
46209
- atomic,
46268
+ atomic: atomic2,
46210
46269
  // @ts-expect-error: for backwards compatibility
46211
46270
  chainId: chainId ? hexToNumber(chainId) : void 0,
46212
46271
  receipts: receipts?.map((receipt) => ({
@@ -56964,23 +57023,123 @@ var init_monad = __esm({
56964
57023
  }
56965
57024
  });
56966
57025
 
57026
+ // ../../packages/economy/dist/offers.js
57027
+ function localRails(preferred = "base") {
57028
+ return preferred === "monad" ? ["x402-base", "x402-solana", "mppx", "x402-monad"] : ["x402-base", "x402-solana", "mppx"];
57029
+ }
57030
+ function describeRails(rails) {
57031
+ const labels = rails.filter((rail) => rail !== "unsupported").map((rail) => NETWORK_LABEL[RAIL_NETWORK[rail]]);
57032
+ return labels.length > 1 ? `${labels.slice(0, -1).join(", ")} or ${labels.at(-1)}` : labels[0] ?? "nothing";
57033
+ }
57034
+ function networkLabel(offer) {
57035
+ if (!offer.network_name)
57036
+ return `${offer.protocol} on ${offer.network || "an unknown network"}`;
57037
+ return offer.rail === "unsupported" ? `${offer.protocol} on ${offer.network_name} (unsupported token or scheme)` : NETWORK_LABEL[offer.network_name];
57038
+ }
57039
+ function networkName(value) {
57040
+ const network = typeof value === "string" ? value.trim().toLowerCase() : "";
57041
+ if (network === BASE_NETWORK || network === "base")
57042
+ return "base";
57043
+ if (network === SOLANA_NETWORK.toLowerCase() || network === "solana" || network === "solana:mainnet")
57044
+ return "solana";
57045
+ if (network === TEMPO_NETWORK || network === "tempo" || network === "tempo:4217" || network === "tempo:mainnet")
57046
+ return "tempo";
57047
+ if (network === MONAD_NETWORK || network === "monad")
57048
+ return "monad";
57049
+ return void 0;
57050
+ }
57051
+ function railForNetwork(value) {
57052
+ const name = networkName(value);
57053
+ return name ? NETWORK_RAIL[name] : void 0;
57054
+ }
57055
+ function classifyOffer(option) {
57056
+ const protocol = option?.protocol === "mpp" ? "mpp" : "x402";
57057
+ const network = String(option?.network ?? "");
57058
+ const name = networkName(network);
57059
+ const asset = String(option?.asset ?? "");
57060
+ const atomic2 = Number(option?.amount ?? option?.maxAmountRequired);
57061
+ const decimals = typeof option?.decimals === "number" ? option.decimals : 6;
57062
+ const price_usd = Number.isFinite(atomic2) && atomic2 >= 0 ? atomic2 / 10 ** decimals : void 0;
57063
+ const scheme = option?.scheme;
57064
+ let rail = "unsupported";
57065
+ if (protocol === "x402" && name === "base" && asset.toLowerCase() === BASE_USDC)
57066
+ rail = "x402-base";
57067
+ else if (protocol === "x402" && name === "solana" && asset === SOLANA_USDC && (scheme === void 0 || scheme === "exact"))
57068
+ rail = "x402-solana";
57069
+ else if (protocol === "x402" && name === "monad" && asset.toLowerCase() === MONAD_USDC.toLowerCase() && (scheme === void 0 || scheme === "exact"))
57070
+ rail = "x402-monad";
57071
+ else if (protocol === "mpp" && name === "tempo" && asset.toLowerCase() === TEMPO_USDC && (option.paymentMethod === void 0 || option.paymentMethod === "tempo") && (option.intent === void 0 || option.intent === "charge"))
57072
+ rail = "mppx";
57073
+ return {
57074
+ network,
57075
+ ...name ? { network_name: name } : {},
57076
+ protocol,
57077
+ rail,
57078
+ ...price_usd !== void 0 ? { price_usd } : {},
57079
+ ...typeof option?.payTo === "string" ? { pay_to: option.payTo } : {}
57080
+ };
57081
+ }
57082
+ function selectOffer(offers, selection) {
57083
+ const preferred = selection.preferred ?? "base";
57084
+ const payable = offers.filter((offer) => offer.rail !== "unsupported" && selection.rails.includes(offer.rail));
57085
+ const cheapest = (name) => payable.filter((offer) => offer.network_name === name).sort((a, b) => (a.price_usd ?? Infinity) - (b.price_usd ?? Infinity))[0];
57086
+ const order = [preferred, ...FALLBACK_ORDER.filter((name) => name !== preferred)];
57087
+ const first = cheapest(preferred);
57088
+ if (first)
57089
+ return first;
57090
+ const candidates = order.map(cheapest).filter((offer) => offer !== void 0);
57091
+ const balances = selection.balances;
57092
+ return balances && candidates.find((offer) => (balances[offer.network_name] ?? 0) >= (offer.price_usd ?? 0)) || candidates[0];
57093
+ }
57094
+ function payableNetworks(offers, rails) {
57095
+ return [...new Set(offers.filter((offer) => offer.rail !== "unsupported" && rails.includes(offer.rail)).map((offer) => offer.network_name))];
57096
+ }
57097
+ var BASE_NETWORK, BASE_USDC, SOLANA_NETWORK, SOLANA_USDC, TEMPO_NETWORK, TEMPO_USDC, RAIL_NETWORK, NETWORK_CAIP2, NETWORK_RAIL, NETWORK_LABEL, FALLBACK_ORDER, HOSTED_RAILS;
57098
+ var init_offers = __esm({
57099
+ "../../packages/economy/dist/offers.js"() {
57100
+ "use strict";
57101
+ init_monad();
57102
+ BASE_NETWORK = "eip155:8453";
57103
+ BASE_USDC = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
57104
+ SOLANA_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
57105
+ SOLANA_USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
57106
+ TEMPO_NETWORK = "eip155:4217";
57107
+ TEMPO_USDC = "0x20c000000000000000000000b9537d11c60e8b50";
57108
+ RAIL_NETWORK = {
57109
+ "x402-base": "base",
57110
+ "x402-solana": "solana",
57111
+ "x402-monad": "monad",
57112
+ mppx: "tempo"
57113
+ };
57114
+ NETWORK_CAIP2 = { base: BASE_NETWORK, solana: SOLANA_NETWORK, tempo: TEMPO_NETWORK, monad: MONAD_NETWORK };
57115
+ NETWORK_RAIL = { base: "x402-base", solana: "x402-solana", monad: "x402-monad", tempo: "mppx" };
57116
+ NETWORK_LABEL = {
57117
+ base: "x402 USDC on Base",
57118
+ solana: "x402 USDC on Solana",
57119
+ tempo: "MPP USDC.e on Tempo",
57120
+ monad: "x402 USDC on Monad"
57121
+ };
57122
+ FALLBACK_ORDER = ["base", "solana", "tempo", "monad"];
57123
+ HOSTED_RAILS = ["x402-base", "x402-solana", "mppx"];
57124
+ }
57125
+ });
57126
+
56967
57127
  // ../../packages/economy/dist/discovery.js
56968
- var BASE_USDC, BASE_NETWORK, isBaseOffer, BaseServiceDiscovery;
57128
+ var BaseServiceDiscovery;
56969
57129
  var init_discovery = __esm({
56970
57130
  "../../packages/economy/dist/discovery.js"() {
56971
57131
  "use strict";
56972
57132
  init_dist3();
56973
- init_monad();
56974
- BASE_USDC = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
56975
- BASE_NETWORK = "eip155:8453";
56976
- isBaseOffer = (offer) => offer?.protocol === "x402" && [BASE_NETWORK, "base"].includes(offer.network) && String(offer.asset).toLowerCase() === BASE_USDC;
57133
+ init_offers();
56977
57134
  BaseServiceDiscovery = class {
56978
57135
  searchApi;
56979
57136
  options;
56980
57137
  cache = /* @__PURE__ */ new Map();
57138
+ rails;
56981
57139
  constructor(searchApi, options = {}) {
56982
57140
  this.searchApi = searchApi;
56983
57141
  this.options = options;
57142
+ this.rails = options.rails ?? localRails(options.paymentNetwork);
56984
57143
  }
56985
57144
  async inspect(input2) {
56986
57145
  const key = `${input2.method ?? "POST"} ${input2.url}`;
@@ -56998,26 +57157,57 @@ var init_discovery = __esm({
56998
57157
  const advisory = result.advisories.find((a) => a.method === (input2.method ?? "POST").toUpperCase());
56999
57158
  if (!advisory)
57000
57159
  return { ok: false, error: "The endpoint does not advertise this method" };
57001
- const monadOffer = this.options.paymentNetwork === "monad" ? advisory.paymentOptions?.find(isMonadOffer) : void 0;
57002
- const offer = monadOffer ?? advisory.paymentOptions?.find(isBaseOffer);
57160
+ const offers = (advisory.paymentOptions ?? []).map((option) => classifyOffer(option));
57161
+ const offer = await this.select(offers);
57162
+ const payable = offers.filter((candidate) => candidate.rail !== "unsupported" && this.rails.includes(candidate.rail));
57163
+ const ceiling = this.options.negotiated && offer ? Math.max(...payable.map((candidate) => candidate.price_usd ?? 0)) : offer?.price_usd;
57003
57164
  const read = ["siwx", "unprotected"].includes(advisory.authMode ?? "");
57004
57165
  const data = {
57005
57166
  endpoint: input2.url,
57006
57167
  method: advisory.method,
57007
- rail: monadOffer ? "x402-monad" : offer || read ? "x402-base" : "unsupported",
57168
+ rail: offer ? offer.rail : read ? "x402-base" : "unsupported",
57008
57169
  payable: !!offer,
57009
57170
  authMode: advisory.authMode,
57010
- network: monadOffer ? MONAD_NETWORK : offer ? BASE_NETWORK : void 0,
57011
- price_usd: offer ? Number("amount" in offer ? offer.amount : "maxAmountRequired" in offer ? offer.maxAmountRequired : 0) / 1e6 : read ? 0 : void 0,
57171
+ network: offer?.network_name ? NETWORK_CAIP2[offer.network_name] : void 0,
57172
+ // The payee of the selected offer: receipts on some rails (x402 Solana) omit it, and reviews need it.
57173
+ ...offer?.pay_to ? { pay_to: offer.pay_to } : {},
57174
+ price_usd: offer ? ceiling ?? 0 : read ? 0 : void 0,
57175
+ ...offers.length ? { offers: offers.map((candidate) => ({
57176
+ network: candidate.network_name ?? candidate.network,
57177
+ protocol: candidate.protocol,
57178
+ price_usd: candidate.price_usd,
57179
+ payable: payable.includes(candidate)
57180
+ })) } : {},
57012
57181
  input_schema: advisory.inputSchema,
57013
57182
  output_schema: advisory.outputSchema,
57014
57183
  description: advisory.summary,
57015
- note: read ? "Read with read_service; wallet authentication is automatic. Never resubmit a pending generation job." : monadOffer ? "Pays x402 USDC on Monad (eip155:143). Inspect with sample_body for the exact quote." : `Only x402 USDC on Base${this.options.paymentNetwork === "monad" ? " or Monad" : ""} is enabled. Inspect with sample_body for the exact quote.`
57184
+ note: read ? "Read with read_service; wallet authentication is automatic. Never resubmit a pending generation job." : this.note(offers, offer)
57016
57185
  };
57017
57186
  if (input2.body === void 0)
57018
57187
  this.cache.set(key, { until: Date.now() + 6e4, data });
57019
57188
  return { ok: true, data };
57020
57189
  }
57190
+ /** Balances are read only when they can change the choice: the preferred network is absent and several are payable. */
57191
+ async select(offers) {
57192
+ const selection = { preferred: this.options.paymentNetwork, rails: this.rails };
57193
+ const networks = payableNetworks(offers, this.rails);
57194
+ const needsBalances = !this.options.negotiated && this.options.balances && networks.length > 1 && !networks.includes(this.options.paymentNetwork ?? "base");
57195
+ const balances = needsBalances ? await this.options.balances().catch(() => void 0) : void 0;
57196
+ return selectOffer(offers, { ...selection, balances });
57197
+ }
57198
+ note(offers, offer) {
57199
+ const quote = "Inspect with sample_body for the exact quote.";
57200
+ if (!offer) {
57201
+ const offered = [...new Set(offers.map(networkLabel))];
57202
+ return `${offered.length ? `This service offers ${offered.join(", ")}` : "This service advertises no recognised payment option"}; payable here: ${describeRails(this.rails)}. ${quote}`;
57203
+ }
57204
+ if (this.options.negotiated) {
57205
+ const networks = payableNetworks(offers, this.rails).map((name) => networkLabel({ ...offer, network_name: name, rail: "x402-base" }));
57206
+ return `The hosted gateway pays the cheapest of ${networks.join(", ")} that your balance on that network covers; price_usd is the ceiling across them. ${quote}`;
57207
+ }
57208
+ const preferred = this.options.paymentNetwork ?? "base";
57209
+ return `Pays ${networkLabel(offer)}${offer.network_name === preferred ? "" : ` (${preferred} is not offered)`}. ${quote}`;
57210
+ }
57021
57211
  async verified(candidates, limit) {
57022
57212
  const services = [];
57023
57213
  for (let i = 0; i < candidates.length && services.length < limit; i += 4) {
@@ -57031,7 +57221,7 @@ var init_discovery = __esm({
57031
57221
  method: c.method ?? "POST",
57032
57222
  name: c.summary ?? c.name,
57033
57223
  price_usd: schema.price_usd,
57034
- payment_provider: "x402",
57224
+ payment_provider: schema.rail === "mppx" ? "mppx" : "x402",
57035
57225
  network: schema.network ?? BASE_NETWORK,
57036
57226
  payable: true
57037
57227
  } : void 0;
@@ -57053,14 +57243,14 @@ var init_discovery = __esm({
57053
57243
  const candidates = Array.isArray(body?.results) ? body.results : Array.isArray(body?.services) ? body.services : [];
57054
57244
  return { ok: true, data: {
57055
57245
  services: await this.verified(candidates, limit),
57056
- note: `AgentCash discovery, verified x402 USDC on ${this.options.paymentNetwork === "monad" ? "Monad or Base" : "Base"}. New/unreviewed services are allowed. Inspect the chosen endpoint before paying.`
57246
+ note: `AgentCash discovery, verified ${describeRails(this.rails)}. New/unreviewed services are allowed. Inspect the chosen endpoint before paying.`
57057
57247
  } };
57058
57248
  }
57059
57249
  async discoverEndpoints(origin) {
57060
57250
  const result = await discoverOriginSchema({ target: origin, signal: AbortSignal.timeout(1e4) });
57061
57251
  if (!result.found)
57062
57252
  return { ok: false, error: result.message ?? "Provider discovery failed" };
57063
- const candidates = result.endpoints.filter((e6) => e6.protocols?.includes("x402")).map((e6) => ({ ...e6, origin: result.origin }));
57253
+ const candidates = result.endpoints.filter((e6) => e6.protocols?.some((protocol) => protocol === "x402" || protocol === "mpp")).map((e6) => ({ ...e6, origin: result.origin }));
57064
57254
  return { ok: true, data: {
57065
57255
  services: await this.verified(candidates, 50),
57066
57256
  guidance: result.guidance,
@@ -57154,15 +57344,32 @@ function parsePaymentEvidence(record3, requestedRail) {
57154
57344
  }
57155
57345
  const network = stringValue2(payment?.network ?? record3.network ?? receipt?.network)?.toLowerCase();
57156
57346
  const protocol = stringValue2(payment?.protocol ?? record3.protocol ?? record3.paymentProtocol)?.toLowerCase();
57157
- const rail = network === "tempo" || protocol === "mpp" || protocol === "mppx" ? "mppx" : network === "base" || network === "eip155:8453" || protocol === "x402" ? "x402-base" : network === void 0 && protocol === void 0 ? requestedRail : "unsupported";
57347
+ const named = networkName(network);
57348
+ const rail = named === "tempo" || protocol === "mpp" || protocol === "mppx" ? "mppx" : named === "solana" ? "x402-solana" : named === "base" || network === void 0 && protocol === "x402" ? requestedRail === "x402-solana" && named === void 0 ? "x402-solana" : "x402-base" : network === void 0 && protocol === void 0 ? requestedRail : "unsupported";
57349
+ if (rail === "x402-solana" && !reference.startsWith("x402:"))
57350
+ reference = `x402:solana:${reference}`;
57158
57351
  return {
57159
57352
  paidUsd,
57160
57353
  rail,
57161
57354
  reference,
57355
+ payer: stringValue2(payment?.payer ?? receipt?.payer),
57162
57356
  proof,
57163
57357
  payTo: stringValue2(payment?.payTo ?? payment?.pay_to ?? payment?.recipient ?? receipt?.payTo ?? receipt?.pay_to ?? receipt?.recipient ?? receipt?.payee)
57164
57358
  };
57165
57359
  }
57360
+ function parseWalletAccounts(data) {
57361
+ const record3 = objectValue3(data);
57362
+ const rows = Array.isArray(data) ? data : Array.isArray(record3?.accounts) ? record3.accounts : [];
57363
+ return rows.map(objectValue3).filter((row) => row !== void 0 && stringValue2(row.network) !== void 0).map((row) => {
57364
+ const usdc2 = numberValue2(row.balance ?? row.usdc ?? row.balanceUsd);
57365
+ return {
57366
+ network: stringValue2(row.network),
57367
+ ...stringValue2(row.address) ? { address: stringValue2(row.address) } : {},
57368
+ ...usdc2 !== void 0 ? { usdc: usdc2 } : {},
57369
+ ...stringValue2(row.depositLink) ? { depositLink: stringValue2(row.depositLink) } : {}
57370
+ };
57371
+ });
57372
+ }
57166
57373
  function normalizedHeaders(value) {
57167
57374
  const headers = objectValue3(value) ?? {};
57168
57375
  return Object.fromEntries(Object.entries(headers).map(([key, child]) => [key.toLowerCase(), child]));
@@ -57207,34 +57414,71 @@ function moneyValue(value) {
57207
57414
  const cleaned = value.replace(/[$,\s]|usd(c?)/gi, "");
57208
57415
  return cleaned === "" ? void 0 : numberValue2(cleaned);
57209
57416
  }
57210
- var McpAgentCashAdapter;
57417
+ var ACCOUNTS_TTL_MS, McpAgentCashAdapter;
57211
57418
  var init_agentcash2 = __esm({
57212
57419
  "../../packages/economy/dist/agentcash.js"() {
57213
57420
  "use strict";
57214
57421
  init_discovery();
57215
57422
  init_dist2();
57216
57423
  init_monad();
57424
+ init_offers();
57425
+ ACCOUNTS_TTL_MS = 3e4;
57217
57426
  McpAgentCashAdapter = class {
57218
57427
  connection;
57219
57428
  options;
57220
57429
  discovery;
57221
57430
  monad;
57431
+ accountsCache;
57222
57432
  constructor(connection, options = {}) {
57223
57433
  this.connection = connection;
57224
57434
  this.options = options;
57225
- this.discovery = new BaseServiceDiscovery((query, options2) => this.connection.call("search", { query, ...options2 }), { paymentNetwork: options.paymentNetwork });
57435
+ this.discovery = new BaseServiceDiscovery((query, options2) => this.connection.call("search", { query, ...options2 }), { paymentNetwork: options.paymentNetwork, balances: () => this.networkBalances() });
57226
57436
  this.monad = options.monad ?? new MonadX402Payer();
57227
57437
  }
57228
57438
  read(url3) {
57229
57439
  return this.connection.call("fetch", { url: url3, method: "GET", maxAmount: 1e-9, paymentNetwork: "base", paymentProtocol: "x402" });
57230
57440
  }
57231
57441
  async getBalance() {
57232
- const [result, monad] = await Promise.all([this.connection.call("get_balance", {}), this.monadBalance()]);
57442
+ const [result, monad, accounts] = await Promise.all([
57443
+ this.connection.call("get_balance", {}),
57444
+ this.monadBalance(),
57445
+ this.accounts(true).catch(() => void 0)
57446
+ ]);
57233
57447
  if (!result.ok)
57234
57448
  return { ok: false, data: result.data, error: result.error };
57235
- const data = result.data && typeof result.data === "object" && !Array.isArray(result.data) ? { ...result.data, monad } : { agentcash: result.data, monad };
57449
+ const data = result.data && typeof result.data === "object" && !Array.isArray(result.data) ? { ...result.data, ...accounts ? { accounts } : {}, monad } : { agentcash: result.data, ...accounts ? { accounts } : {}, monad };
57236
57450
  return { ok: true, data };
57237
57451
  }
57452
+ /** AgentCash's per-network accounts (`list_accounts`), briefly cached for offer selection. */
57453
+ accounts(refresh = false) {
57454
+ if (!refresh && this.accountsCache && this.accountsCache.until > Date.now())
57455
+ return this.accountsCache.accounts;
57456
+ const accounts = this.connection.call("list_accounts", {}).then((result) => {
57457
+ if (!result.ok)
57458
+ throw new Error(result.error ?? "list_accounts failed");
57459
+ return parseWalletAccounts(result.data);
57460
+ });
57461
+ this.accountsCache = { until: Date.now() + ACCOUNTS_TTL_MS, accounts };
57462
+ accounts.catch(() => {
57463
+ this.accountsCache = void 0;
57464
+ });
57465
+ return accounts;
57466
+ }
57467
+ /** Spendable USDC per network, for choosing among a service's offers. */
57468
+ async networkBalances() {
57469
+ const balances = {};
57470
+ for (const account of await this.accounts()) {
57471
+ const name = networkName(account.network);
57472
+ if (name && account.usdc !== void 0)
57473
+ balances[name] = (balances[name] ?? 0) + account.usdc;
57474
+ }
57475
+ if (this.options.paymentNetwork === "monad") {
57476
+ const monad = await this.monadBalance();
57477
+ if (typeof monad.usdc === "number")
57478
+ balances.monad = monad.usdc;
57479
+ }
57480
+ return balances;
57481
+ }
57238
57482
  async monadBalance() {
57239
57483
  const preferred = this.options.paymentNetwork === "monad";
57240
57484
  try {
@@ -57259,7 +57503,7 @@ var init_agentcash2 = __esm({
57259
57503
  async fetch(request2) {
57260
57504
  if (request2.rail === "x402-monad")
57261
57505
  return this.monad.fetch(request2);
57262
- const railParams = request2.rail === "mppx" ? { paymentNetwork: "tempo", paymentProtocol: "mpp" } : { paymentNetwork: "base", paymentProtocol: "x402" };
57506
+ const railParams = request2.rail === "mppx" ? { paymentNetwork: "tempo", paymentProtocol: "mpp" } : request2.rail === "x402-solana" ? { paymentNetwork: "solana", paymentProtocol: "x402" } : { paymentNetwork: "base", paymentProtocol: "x402" };
57263
57507
  const result = await this.connection.call("fetch", {
57264
57508
  url: request2.url,
57265
57509
  method: request2.method,
@@ -57289,7 +57533,7 @@ var init_agentcash2 = __esm({
57289
57533
 
57290
57534
  // ../../packages/economy/dist/approvals.js
57291
57535
  import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile3 } from "node:fs/promises";
57292
- import { dirname as dirname6, join as join9 } from "node:path";
57536
+ import { dirname as dirname7, join as join9 } from "node:path";
57293
57537
  function isApprovalMode(value) {
57294
57538
  return value === "ask" || value === "auto" || value === "off";
57295
57539
  }
@@ -57308,7 +57552,7 @@ async function loadApprovalRules(path3 = approvalRulesPath()) {
57308
57552
  }
57309
57553
  }
57310
57554
  async function saveApprovalRules(rules, path3 = approvalRulesPath()) {
57311
- await mkdir7(dirname6(path3), { recursive: true });
57555
+ await mkdir7(dirname7(path3), { recursive: true });
57312
57556
  await writeFile3(path3, `${JSON.stringify({ services: rules }, null, 2)}
57313
57557
  `, "utf8");
57314
57558
  }
@@ -57421,6 +57665,8 @@ var init_crowdcode = __esm({
57421
57665
  ...review.paymentProvider ? { payment_provider: review.paymentProvider } : {},
57422
57666
  ...review.paymentProof ? { payment_proof: review.paymentProof } : {},
57423
57667
  ...review.paymentTargetRef ? { payment_target_ref: review.paymentTargetRef } : {},
57668
+ ...review.payer ? { payer: review.payer } : {},
57669
+ ...review.payerSignature ? { payer_signature: review.payerSignature } : {},
57424
57670
  ...review.taskContext ? { task_context: review.taskContext } : {}
57425
57671
  });
57426
57672
  const payload = result.data && typeof result.data === "object" ? result.data : {};
@@ -57433,7 +57679,7 @@ var init_crowdcode = __esm({
57433
57679
 
57434
57680
  // ../../packages/economy/dist/crowdcode-preferences.js
57435
57681
  import { mkdir as mkdir8, readFile as readFile8, rename as rename2, writeFile as writeFile4 } from "node:fs/promises";
57436
- import { dirname as dirname7, join as join10 } from "node:path";
57682
+ import { dirname as dirname8, join as join10 } from "node:path";
57437
57683
  import { randomUUID as randomUUID4 } from "node:crypto";
57438
57684
  var CrowdCodePreferences;
57439
57685
  var init_crowdcode_preferences = __esm({
@@ -57464,7 +57710,7 @@ var init_crowdcode_preferences = __esm({
57464
57710
  }
57465
57711
  async write(scope, enabled) {
57466
57712
  const path3 = this.path(scope), temporary = `${path3}.${randomUUID4()}.tmp`;
57467
- await mkdir8(dirname7(path3), { recursive: true });
57713
+ await mkdir8(dirname8(path3), { recursive: true });
57468
57714
  await writeFile4(temporary, JSON.stringify({ enabled }) + "\n", { mode: 384 });
57469
57715
  await rename2(temporary, path3);
57470
57716
  }
@@ -57510,7 +57756,7 @@ var init_media = __esm({
57510
57756
 
57511
57757
  // ../../packages/economy/dist/purchases.js
57512
57758
  import { appendFile as appendFile3, mkdir as mkdir9, readFile as readFile9 } from "node:fs/promises";
57513
- import { dirname as dirname8, join as join11 } from "node:path";
57759
+ import { dirname as dirname9, join as join11 } from "node:path";
57514
57760
  import { randomUUID as randomUUID5 } from "node:crypto";
57515
57761
  function purchasesPath(session) {
57516
57762
  return join11(session.sessionDir, "purchases.jsonl");
@@ -57538,7 +57784,7 @@ async function skipPendingReviews(session) {
57538
57784
  }
57539
57785
  async function appendPurchaseEvent(session, event) {
57540
57786
  const path3 = purchasesPath(session);
57541
- await mkdir9(dirname8(path3), { recursive: true });
57787
+ await mkdir9(dirname9(path3), { recursive: true });
57542
57788
  await appendFile3(path3, `${JSON.stringify(event)}
57543
57789
  `, "utf8");
57544
57790
  }
@@ -57672,13 +57918,14 @@ function numberArg(value) {
57672
57918
  function slugUrl(url3) {
57673
57919
  return url3.replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "").slice(0, 80) || "service";
57674
57920
  }
57675
- var GATEWAY_TOOL_NAMES, EconomyGateway, GATEWAY_TOOL_DEFINITIONS;
57921
+ var GATEWAY_TOOL_NAMES, VERIFIED_RAILS, EconomyGateway, GATEWAY_TOOL_DEFINITIONS;
57676
57922
  var init_gateway = __esm({
57677
57923
  "../../packages/economy/dist/gateway.js"() {
57678
57924
  "use strict";
57679
57925
  init_crowdcode_preferences();
57680
57926
  init_media();
57681
57927
  init_dist2();
57928
+ init_offers();
57682
57929
  init_approvals();
57683
57930
  init_purchases();
57684
57931
  GATEWAY_TOOL_NAMES = [
@@ -57695,6 +57942,7 @@ var init_gateway = __esm({
57695
57942
  "review_paid_service",
57696
57943
  "bridge_usdc"
57697
57944
  ];
57945
+ VERIFIED_RAILS = ["x402-base", "x402-solana", "x402-monad", "mppx"];
57698
57946
  EconomyGateway = class {
57699
57947
  options;
57700
57948
  /**
@@ -57838,11 +58086,13 @@ var init_gateway = __esm({
57838
58086
  const evidence = await this.serviceEvidence(endpoint);
57839
58087
  const rail = railFromSchema(schema.data);
57840
58088
  const priceCeilingCents = priceCeilingFromSchema(schema.data);
58089
+ const payTo = schema.data && typeof schema.data === "object" && typeof schema.data.pay_to === "string" ? schema.data.pay_to : void 0;
57841
58090
  await this.rememberInspection(inspectionKey(endpoint, method), {
57842
58091
  endpoint,
57843
58092
  method,
57844
58093
  rail,
57845
58094
  priceCeilingCents,
58095
+ ...payTo ? { payTo } : {},
57846
58096
  evidence,
57847
58097
  inspectedAt: (/* @__PURE__ */ new Date()).toISOString()
57848
58098
  });
@@ -57888,10 +58138,11 @@ var init_gateway = __esm({
57888
58138
  if (!inspection) {
57889
58139
  return { ok: false, error: `inspect_paid_service must run for ${method} ${endpoint} before call_paid_service` };
57890
58140
  }
57891
- if (inspection.rail !== "x402-base" && inspection.rail !== "x402-monad") {
58141
+ const rails = this.options.paymentRails ?? VERIFIED_RAILS;
58142
+ if (!rails.includes(inspection.rail)) {
57892
58143
  return {
57893
58144
  ok: false,
57894
- error: "this endpoint settles on a payment rail OpenCrowd does not support for automatic payment (allowed: x402 USDC on Base or Monad)"
58145
+ error: `this endpoint offers no payment rail this agent can pay automatically (payable: ${describeRails(rails)})`
57895
58146
  };
57896
58147
  }
57897
58148
  const requestedCostCents = intArg(args.max_cost_cents) ?? inspection.priceCeilingCents;
@@ -57955,7 +58206,7 @@ var init_gateway = __esm({
57955
58206
  const artifact = result.ambiguityReason === "transport" ? void 0 : await saveArtifact(session, `service-calls/${Date.now()}-${slugUrl(endpoint)}.json`, JSON.stringify({ status: result.status, body: result.data }, null, 2), { endpoint, method });
57956
58207
  const purchaseId = newPurchaseId();
57957
58208
  const railUsed = result.payment?.rail ?? inspection.rail;
57958
- const supportedRail = railUsed === "x402-base" || railUsed === "x402-monad" || railUsed === "mppx";
58209
+ const supportedRail = VERIFIED_RAILS.includes(railUsed);
57959
58210
  const record3 = {
57960
58211
  purchase_id: purchaseId,
57961
58212
  session_id: session.sessionId,
@@ -57971,7 +58222,9 @@ var init_gateway = __esm({
57971
58222
  // Confirmed paid successes AND paid failures require reviews; free,
57972
58223
  // SIWX, and unknown outcomes create no paid receipt to review.
57973
58224
  review_required: paid && supportedRail && await this.crowdcodeEnabled(),
57974
- evidence: result.payment,
58225
+ // x402 Solana receipts carry no payee; CrowdCode needs it to verify a new
58226
+ // service, so use the inspected offer's when the payment settled on that rail.
58227
+ evidence: result.payment && !result.payment.payTo && inspection.payTo && railUsed === inspection.rail ? { ...result.payment, payTo: inspection.payTo } : result.payment,
57975
58228
  notes: result.ambiguous ? `${result.ambiguityReason ?? "ambiguous"}: payment state unknown; never auto-retried` : result.error
57976
58229
  };
57977
58230
  await appendPurchase(session, record3);
@@ -58119,6 +58372,8 @@ var init_gateway = __esm({
58119
58372
  paymentProvider: evidence?.reference ? state.record.rail === "mppx" ? "mppx" : "x402" : void 0,
58120
58373
  paymentProof: evidence?.proof,
58121
58374
  paymentTargetRef: evidence?.reference ? evidence.payTo : void 0,
58375
+ // Solana pays from a different address than the EVM reviewer key.
58376
+ ...state.record.rail === "x402-solana" && evidence?.payer ? { payer: evidence.payer } : {},
58122
58377
  taskContext: stringArg(args.task_context)
58123
58378
  });
58124
58379
  if (!result.ok) {
@@ -58528,6 +58783,7 @@ var init_dist4 = __esm({
58528
58783
  init_purchases();
58529
58784
  init_runtime();
58530
58785
  init_discovery();
58786
+ init_offers();
58531
58787
  }
58532
58788
  });
58533
58789
 
@@ -58660,10 +58916,10 @@ var init_context2 = __esm({
58660
58916
 
58661
58917
  // ../../packages/agent-runtime/dist/knowledge.js
58662
58918
  import { access, cp, mkdir as mkdir10, readFile as readFile11, writeFile as writeFile6 } from "node:fs/promises";
58663
- import { dirname as dirname9, join as join14, resolve as resolve4 } from "node:path";
58919
+ import { dirname as dirname10, join as join14, resolve as resolve4 } from "node:path";
58664
58920
  import { fileURLToPath } from "node:url";
58665
58921
  function bundledKnowledgeDir() {
58666
- return resolve4(dirname9(fileURLToPath(import.meta.url)), "..", "knowledge");
58922
+ return resolve4(dirname10(fileURLToPath(import.meta.url)), "..", "knowledge");
58667
58923
  }
58668
58924
  function knowledgeDisabledByEnv() {
58669
58925
  const value = process.env.OPENCROWD_KNOWLEDGE_DIR?.trim().toLowerCase();
@@ -80681,9 +80937,12 @@ function rejectedRequest(status, text) {
80681
80937
  async function readHostedFinancialState(options) {
80682
80938
  try {
80683
80939
  const reply = await postHostedTool(options, "economy.balance", {}, { timeoutMs: 1e4 });
80684
- const parsed = FinancialStateSchema.safeParse(reply.data?.financial_state);
80685
- if (reply.ok && parsed.success && parsed.data.status === "available" && parsed.data.run_id === options.runId)
80686
- return parsed.data;
80940
+ const data = reply.data;
80941
+ const parsed = FinancialStateSchema.safeParse(data?.financial_state);
80942
+ if (reply.ok && parsed.success && parsed.data.status === "available" && parsed.data.run_id === options.runId) {
80943
+ const networks = parsed.data.wallet.networks ?? parseNetworkBalances(data?.networks);
80944
+ return networks ? { ...parsed.data, wallet: { ...parsed.data.wallet, networks } } : parsed.data;
80945
+ }
80687
80946
  } catch {
80688
80947
  }
80689
80948
  return { status: "unavailable", reason: "Hosted financial state could not be read. Do not infer balance or spending from the local session budget." };
@@ -80801,13 +81060,14 @@ function createHostedEconomy(options) {
80801
81060
  approvalMode: "auto",
80802
81061
  hostedSpending: true,
80803
81062
  minServiceScore: 2,
81063
+ paymentRails: HOSTED_RAILS,
80804
81064
  // Hosted rules live with the agent's session, never in a user config dir.
80805
81065
  approvalRulesPath: join20(options.session.sessionDir, "approval-rules.json")
80806
81066
  });
80807
81067
  }
80808
81068
  function hostedDynamicTools(economy) {
80809
81069
  const tools = economyTools(economy, { hidden: HIDDEN_HOSTED_TOOLS });
80810
- return { ...tools, definitions: tools.definitions.map((tool) => tool.name === "get_wallet_status" ? { ...tool, description: "Read this hosted agent's USDC wallet balance and authoritative current-run inference spending, service spending, payment holds and authorized budget. Read-only." } : tool) };
81070
+ return { ...tools, definitions: tools.definitions.map((tool) => tool.name === "get_wallet_status" ? { ...tool, description: "Read this hosted agent's USDC wallet balances (per network when reported: Base, Solana, Tempo) and authoritative current-run inference spending, service spending, payment holds and authorized budget. A payment on a network needs that network's balance. Read-only." } : tool) };
80811
81071
  }
80812
81072
  function parseListedService(raw) {
80813
81073
  const row = objectValue5(raw);
@@ -80937,8 +81197,6 @@ var init_hosted_economy = __esm({
80937
81197
  socket;
80938
81198
  fetcher;
80939
81199
  reconcileDelayMs;
80940
- /** Rails observed by inspection; `fetch` refuses anything but Base x402 regardless of the caller's rail. */
80941
- inspectedRails = /* @__PURE__ */ new Map();
80942
81200
  constructor(directory, socket, fetcher, reconcileDelayMs = RECONCILE_DELAY_MS) {
80943
81201
  this.directory = directory;
80944
81202
  this.socket = socket;
@@ -80954,7 +81212,7 @@ var init_hosted_economy = __esm({
80954
81212
  if (!reply.ok || result?.status !== 200)
80955
81213
  return { ok: false, error: reply.error ?? "AgentCash search unavailable" };
80956
81214
  return { ok: true, data: JSON.parse(result.body) };
80957
- });
81215
+ }, { rails: HOSTED_RAILS, negotiated: true });
80958
81216
  }
80959
81217
  discovery;
80960
81218
  async read(url3) {
@@ -80987,15 +81245,8 @@ var init_hosted_economy = __esm({
80987
81245
  }
80988
81246
  async fetch(request2) {
80989
81247
  const refuse = (error53) => ({ ok: false, ambiguous: false, data: void 0, error: error53 });
80990
- const inspected = this.inspectedRails.get(`${request2.method.toUpperCase()} ${request2.url}`);
80991
- if (request2.rail === "mppx" || inspected === "mppx") {
80992
- return refuse("this service settles on MPP/Tempo; hosted agents can only pay x402 services on Base");
80993
- }
80994
- if (request2.rail === "x402-monad" || inspected === "x402-monad") {
80995
- return refuse("this service was inspected for Monad settlement; hosted agents can only pay x402 services on Base");
80996
- }
80997
- if (request2.rail === "unsupported" || inspected === "unsupported") {
80998
- return refuse("this service settles on a payment network hosted agents cannot pay (only x402 USDC on Base)");
81248
+ if (!HOSTED_RAILS.includes(request2.rail)) {
81249
+ return refuse(`this service settles on a payment network hosted agents cannot pay; hosted agents pay ${describeRails(HOSTED_RAILS)}`);
80999
81250
  }
81000
81251
  const serviceId = await this.directory.resolveServiceId(request2.url) ?? `url:${createHash4("sha256").update(request2.url).digest("hex")}`;
81001
81252
  const method = request2.method.toUpperCase();
@@ -81033,6 +81284,7 @@ var init_hosted_economy = __esm({
81033
81284
  const status = numberValue6(data.status);
81034
81285
  const outcome = data.outcome === "paid_success" ? "paid_success" : "paid_failure";
81035
81286
  const paidAtomic = numberValue6(data.amount_atomic);
81287
+ const network = stringValue5(data.network);
81036
81288
  return {
81037
81289
  ok: outcome === "paid_success",
81038
81290
  ambiguous: false,
@@ -81041,10 +81293,12 @@ var init_hosted_economy = __esm({
81041
81293
  authMode: "paid",
81042
81294
  error: outcome === "paid_success" ? void 0 : `the paid call returned HTTP ${status ?? "?"}`,
81043
81295
  payment: {
81044
- rail: "x402-base",
81296
+ rail: network ? railForNetwork(network) ?? "unsupported" : "x402-base",
81045
81297
  paidUsd: paidAtomic === void 0 ? void 0 : paidAtomic / USDC_ATOMIC_PER_USD,
81298
+ // The bare transaction is how the hosted gateway finds its settled attempt for the review.
81046
81299
  reference: stringValue5(data.transaction),
81047
- payTo: stringValue5(data.pay_to)
81300
+ payTo: stringValue5(data.pay_to),
81301
+ payer: stringValue5(data.payer)
81048
81302
  }
81049
81303
  };
81050
81304
  }
@@ -81359,17 +81613,19 @@ var init_worker = __esm({
81359
81613
  if (command.type === "run.resume") {
81360
81614
  if (!run)
81361
81615
  return reject("unknown_run", "Run does not exist");
81362
- if (!run.outcome || !isWaiting(run.outcome) || command.payload.sessionId !== run.sessionId || command.payload.checkpointId && command.payload.checkpointId !== run.checkpointId || run.pendingOperationId && command.payload.operationId !== run.pendingOperationId) {
81363
- return reject("run_not_resumable", "Resume does not match the waiting operation and checkpoint");
81364
- }
81365
- const causes = {
81366
- waiting_for_approval: "approval_granted",
81367
- waiting_for_funds: "funds_available",
81368
- waiting_for_delegation: "delegation_renewed",
81369
- payment_unknown: "payment_reconciled"
81370
- };
81371
- if (causes[run.outcome] !== command.payload.cause)
81372
- return reject("run_not_resumable", "Resume cause does not resolve this wait");
81616
+ if (!previous) {
81617
+ if (!run.outcome || !isWaiting(run.outcome) || command.payload.sessionId !== run.sessionId || command.payload.checkpointId && command.payload.checkpointId !== run.checkpointId || run.pendingOperationId && command.payload.operationId !== run.pendingOperationId) {
81618
+ return reject("run_not_resumable", "Resume does not match the waiting operation and checkpoint");
81619
+ }
81620
+ const causes = {
81621
+ waiting_for_approval: "approval_granted",
81622
+ waiting_for_funds: "funds_available",
81623
+ waiting_for_delegation: "delegation_renewed",
81624
+ payment_unknown: "payment_reconciled"
81625
+ };
81626
+ if (causes[run.outcome] !== command.payload.cause)
81627
+ return reject("run_not_resumable", "Resume cause does not resolve this wait");
81628
+ }
81373
81629
  } else if (run && !previous) {
81374
81630
  return reject("conflicting_duplicate", "Run ID already exists");
81375
81631
  } else if (!run) {
@@ -81442,8 +81698,22 @@ var init_worker = __esm({
81442
81698
  throw new Error("tool request digest mismatch");
81443
81699
  if (saved?.result)
81444
81700
  return saved.result;
81445
- if (saved && name === "run_shell")
81446
- throw new Error("shell result was lost; execution requires manual reconciliation");
81701
+ if (saved && name === "run_shell") {
81702
+ const output4 = { ok: false, error: "Shell command was interrupted and its result was lost. It was not rerun. Inspect existing files and installed packages before choosing a different approach; do not assume the command succeeded or repeat side effects blindly." };
81703
+ await this.mutate(() => {
81704
+ run.tools[id].result = output4;
81705
+ });
81706
+ await this.emit("tool.finished", {
81707
+ toolCallId: id,
81708
+ toolName: name,
81709
+ status: "error",
81710
+ durationMs: 0,
81711
+ turn: run.checkpoint?.turn ?? 0,
81712
+ error: output4.error,
81713
+ details: captureDetail({ output: output4 })
81714
+ }, runId, run.commandId);
81715
+ return output4;
81716
+ }
81447
81717
  if (saved && !saved.approvalPending && name === "call_paid_service")
81448
81718
  throw new Error("paid call result was lost; the payment requires manual reconciliation");
81449
81719
  await this.mutate(() => {
@@ -81885,14 +82155,14 @@ async function runAgentTaskDetailed(session, task, options = {}) {
81885
82155
  const knowledge = options.capabilityIndex === void 0 && options.knowledge !== false && options.dynamicTools ? await loadKnowledgeTree(session, options.knowledge ?? {}) : void 0;
81886
82156
  const capabilityIndex = options.capabilityIndex ?? (knowledge ? renderCapabilityIndex(knowledge.l0) : void 0);
81887
82157
  const systemPromptParts = [
81888
- options.hosted ? "You are a hosted OpenCrowd agent running in the cloud with your own USDC wallet on Base." : "You are the local OpenCrowd agent, a CLI agent with a USDC wallet running on the user's personal machine.",
82158
+ options.hosted ? "You are a hosted OpenCrowd agent running in the cloud with your own USDC wallets on Base, Solana and Tempo." : "You are the local OpenCrowd agent, a CLI agent with a USDC wallet running on the user's personal machine.",
81889
82159
  "Try to solve the user's task completely. Use local files and bash when they are sufficient, but remember they are bounded by the user's installed tools, credentials, network, open ports, and process lifetime.",
81890
82160
  "Before repeatedly fighting the local environment, decide whether the task needs external capability such as hosting, persistent infrastructure, remote compute, specialized APIs, live data, or access the user's device does not have."
81891
82161
  ];
81892
82162
  if (options.hosted)
81893
- systemPromptParts.push("You operate this service for its owner. Inference and paid tools debit its USDC wallet. When authorized to build or sell a service, take concrete steps within the owner's configured scope and budget. Ask only for missing information or permissions that materially block progress. Report actual costs and outcomes. Financial sustainability is the owner's business objective; respect their stop controls.", "You are a continuous autonomous agent, not a turn-based assistant. Maintain unfinished goals, promised follow-ups, recurring work, and conditions worth monitoring. When future work would clearly help, proactively create a persistent automation instead of waiting for the owner to say schedule. Use a one-time automation for a future reminder or follow-up and a cron automation for monitoring, recurring reports, or maintenance. It runs in this same conversation. Never create duplicates or noisy schedules, and always tell the owner exactly what you created, its timezone, and next run.", "Before finishing a substantive task, consider whether a follow-up, reminder, monitoring loop, recurring report, or maintenance check would materially advance the owner's goal; create one when the need is clear, but do not schedule ordinary one-off work.", "Financial state: the current hosted financial snapshot is authoritative; historical local session budget figures are not wallet balances and may omit inference spending. Wallet balance, pending payment holds, settled inference spend, settled service spend, and remaining run budget are separate. Spending totals cover only the identified current run. A budget limit does not supply funds. An unavailable balance is unknown, not zero. The snapshot before a model call excludes that call's eventual charge; never claim a response was free or that you spent nothing merely because no paid tool ran. Use get_budget_status or get_wallet_status for refreshed financial state. Respect approval mode and per-call limits; over-limit requests require the owner's approval.");
82163
+ systemPromptParts.push("You operate this service for its owner. Inference and paid tools debit its USDC wallet. When authorized to build or sell a service, take concrete steps within the owner's configured scope and budget. Ask only for missing information or permissions that materially block progress. Report actual costs and outcomes. Financial sustainability is the owner's business objective; respect their stop controls.", "You are a continuous autonomous agent, not a turn-based assistant. Maintain unfinished goals, promised follow-ups, recurring work, and conditions worth monitoring. When future work would clearly help, proactively create a persistent automation instead of waiting for the owner to say schedule. Use a one-time automation for a future reminder or follow-up and a cron automation for monitoring, recurring reports, or maintenance. It runs in this same conversation. Never create duplicates or noisy schedules, and always tell the owner exactly what you created, its timezone, and next run.", "Before finishing a substantive task, consider whether a follow-up, reminder, monitoring loop, recurring report, or maintenance check would materially advance the owner's goal; create one when the need is clear, but do not schedule ordinary one-off work.", "Financial state: the current hosted financial snapshot is authoritative; historical local session budget figures are not wallet balances and may omit inference spending. Wallet balance, pending payment holds, settled inference spend, settled service spend, and remaining run budget are separate. When the snapshot lists wallet networks, each network's balance is separate: a payment on a network needs that network's available balance, and the total only says whether any funds exist. Spending totals cover only the identified current run. A budget limit does not supply funds. An unavailable balance is unknown, not zero. The snapshot before a model call excludes that call's eventual charge; never claim a response was free or that you spent nothing merely because no paid tool ran. Use get_budget_status or get_wallet_status for refreshed financial state. Respect approval mode and per-call limits; over-limit requests require the owner's approval.");
81894
82164
  if (options.dynamicTools) {
81895
- systemPromptParts.push("When the local computer is not the right environment, or after one clear local capability failure, buy external capability: find_paid_service to discover, inspect_paid_service to see the exact schema/price/reputation, call_paid_service to execute through the enforced purchase lifecycle, and review_paid_service for the required review after every confirmed paid call (success or failure). The same review tool accepts unpaid experiences; review relevant free results or failed attempts without inventing payment evidence. Distinguish provider faults from local errors and uncertain failures.", ...options.hosted ? ["Discover services through AgentCash search and provider catalogs. You can use new services without prior CrowdCode reviews. Only x402 USDC on Base is payable, within the user's configured limits; over-limit quotes request approval. Use read_service for authenticated job polling and never resubmit a pending generation job."] : [], capabilityIndex ?? DEFAULT_CAPABILITY_INDEX, "When a task needs current web facts, search results, or unfamiliar page content, make one paid web search your FIRST move \u2014 do not serially guess URLs with curl; one paid search replaces minutes of blind fetching and costs less than the LLM turns it saves.", "Approval, budget, reputation, and payment rails are enforced in code \u2014 you cannot bypass them, so state costs plainly and never invent payment details.", "When the user says CrowdCode on/off, call set_crowdcode_enabled; default to session scope unless they ask for a persistent default. Use list_my_reviews and delete_my_review for user-directed review management, including while off. Before finishing a substantive task, consider concrete paid services that would fix real shortcomings and submit qualifying demand with request_service, unless CrowdCode is off. Never invent a purchase or demand.", "Each tool result includes the budget before and after that tool call. Never ask for wallet private keys or secrets.", ...options.promptSections ?? []);
82165
+ systemPromptParts.push("When the local computer is not the right environment, or after one clear local capability failure, buy external capability: find_paid_service to discover, inspect_paid_service to see the exact schema/price/reputation, call_paid_service to execute through the enforced purchase lifecycle, and review_paid_service for the required review after every confirmed paid call (success or failure). The same review tool accepts unpaid experiences; review relevant free results or failed attempts without inventing payment evidence. Distinguish provider faults from local errors and uncertain failures.", ...options.hosted ? ["Discover services through AgentCash search and provider catalogs. You can use new services without prior CrowdCode reviews. Payable: x402 USDC on Base or Solana and MPP USDC.e on Tempo; the hosted gateway pays the cheapest offered network your balance on that network covers. Other networks, including Monad, are not payable here. Payments stay within the user's configured limits; over-limit quotes request approval. Use read_service for authenticated job polling and never resubmit a pending generation job."] : [], capabilityIndex ?? DEFAULT_CAPABILITY_INDEX, "When a task needs current web facts, search results, or unfamiliar page content, make one paid web search your FIRST move \u2014 do not serially guess URLs with curl; one paid search replaces minutes of blind fetching and costs less than the LLM turns it saves.", "Approval, budget, reputation, and payment rails are enforced in code \u2014 you cannot bypass them, so state costs plainly and never invent payment details.", "When the user says CrowdCode on/off, call set_crowdcode_enabled; default to session scope unless they ask for a persistent default. Use list_my_reviews and delete_my_review for user-directed review management, including while off. Before finishing a substantive task, consider concrete paid services that would fix real shortcomings and submit qualifying demand with request_service, unless CrowdCode is off. Never invent a purchase or demand.", "Each tool result includes the budget before and after that tool call. Never ask for wallet private keys or secrets.", ...options.promptSections ?? []);
81896
82166
  } else {
81897
82167
  systemPromptParts.push("Paid external services are unavailable in this run; work with local tools only and say so if the task truly requires external capability.");
81898
82168
  }
@@ -81907,7 +82177,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
81907
82177
  systemPromptParts.push("Using secrets from chat: call vault_request to send one HTTPS request with a vault secret injected, writing {{SECRET_NAME}} where the value belongs (for example an authorization header). The platform substitutes the real value only for hosts the user approved for that secret, refreshes OAuth tokens automatically, and never returns the value to you. If it reports that a host needs approval, tell the user to allow it in the Vault tab and stop; never look for paid third-party services to do what an approved secret already lets you do. To upload a file (an image or video you rendered with run_shell, where ffmpeg is installed), pass body_artifact with its path under artifacts/ and, for multipart APIs, body_field plus form_fields; files over an API's chunk limit can be split with run_shell first.");
81908
82178
  }
81909
82179
  if (options.hosted && hostedTools.includes("run_shell")) {
81910
- systemPromptParts.push("Tooling: ffmpeg, python3 with pip and venv, node and npm are installed; the system image is read-only, but `pip install --user <package>` and `npm install` persist on this machine's volume between runs, so install once and reuse.");
82180
+ systemPromptParts.push("Tooling: ffmpeg, python3 with pip and venv, node and npm are installed; the system image is read-only, but `pip install --user <package>` and `npm install` persist on this machine's volume between runs, so install once and reuse.", "Resource recovery: before installing large ML dependencies, check available memory, disk space and Python version. A killed install or OOM is a failed approach, not the end of the task. Inspect partial installs and preserve user files, then choose a materially lighter backend or an external service within the user's budget. Verify package metadata: optional extras add dependencies and do not remove default ones; never invent an extra such as basic-pitch[tflite]. For audio-to-MIDI, prefer a verified TFLite or ONNX inference setup in an isolated venv over full TensorFlow; test on a short audio segment before processing the whole file. If no viable path remains, report the concrete blocker and useful artifacts. Do not claim success until the output has been checked.");
81911
82181
  }
81912
82182
  systemPromptParts.push("End by calling complete_session with a concise final message.");
81913
82183
  if (options.subagent) {
@@ -83250,6 +83520,16 @@ async function runDoctor(log) {
83250
83520
  return "connected; balance readable";
83251
83521
  }
83252
83522
  },
83523
+ {
83524
+ name: "solana account",
83525
+ run: async () => {
83526
+ const address2 = await readAgentCashSolanaAddress();
83527
+ if (!address2) {
83528
+ throw new Error("skipped: no AgentCash Solana wallet yet (AgentCash creates it on first use)");
83529
+ }
83530
+ return `${address2} (x402 USDC on Solana via AgentCash; preferred payment network: ${resolvePaymentNetwork(config4)})`;
83531
+ }
83532
+ },
83253
83533
  {
83254
83534
  name: "monad usdc",
83255
83535
  run: async () => {
@@ -83312,7 +83592,7 @@ var init_doctor = __esm({
83312
83592
 
83313
83593
  // ../../packages/evals/dist/gaia.js
83314
83594
  import { mkdir as mkdir13, readFile as readFile16, writeFile as writeFile7, access as access2 } from "node:fs/promises";
83315
- import { dirname as dirname11, join as join25 } from "node:path";
83595
+ import { dirname as dirname12, join as join25 } from "node:path";
83316
83596
  import { homedir as homedir7 } from "node:os";
83317
83597
  function isUnsupportedModality(fileName) {
83318
83598
  if (!fileName) {
@@ -83431,7 +83711,7 @@ async function cachedDownload(path3, url3, options, binary = false) {
83431
83711
  if (!response.ok) {
83432
83712
  throw new Error(`GAIA download failed: ${response.status} ${response.statusText} for ${url3}`);
83433
83713
  }
83434
- await mkdir13(dirname11(path3), { recursive: true });
83714
+ await mkdir13(dirname12(path3), { recursive: true });
83435
83715
  if (binary) {
83436
83716
  const buffer2 = Buffer.from(await response.arrayBuffer());
83437
83717
  await writeFile7(path3, buffer2);
@@ -84188,7 +84468,7 @@ var init_runner = __esm({
84188
84468
  // ../../packages/evals/dist/tasks.js
84189
84469
  import { readFile as readFile19 } from "node:fs/promises";
84190
84470
  import { createHash as createHash7 } from "node:crypto";
84191
- import { dirname as dirname12, join as join28 } from "node:path";
84471
+ import { dirname as dirname13, join as join28 } from "node:path";
84192
84472
  import { fileURLToPath as fileURLToPath2 } from "node:url";
84193
84473
  import { homedir as homedir8 } from "node:os";
84194
84474
  async function loadTaskSet(name, options = {}) {
@@ -84210,7 +84490,7 @@ async function loadTaskSet(name, options = {}) {
84210
84490
  }
84211
84491
  }
84212
84492
  function tasksDir() {
84213
- return join28(dirname12(fileURLToPath2(import.meta.url)), "..", "tasks");
84493
+ return join28(dirname13(fileURLToPath2(import.meta.url)), "..", "tasks");
84214
84494
  }
84215
84495
  async function loadUsageTasks() {
84216
84496
  const raw = JSON.parse(await readFile19(join28(tasksDir(), "usage.json"), "utf8"));
@@ -84269,7 +84549,7 @@ async function loadAssistantBench(options = {}) {
84269
84549
  text = `${(body.rows ?? []).map((item) => JSON.stringify(item.row)).join("\n")}
84270
84550
  `;
84271
84551
  const { mkdir: mkdir21, writeFile: writeFile13 } = await import("node:fs/promises");
84272
- await mkdir21(dirname12(cachePath), { recursive: true });
84552
+ await mkdir21(dirname13(cachePath), { recursive: true });
84273
84553
  await writeFile13(cachePath, text, "utf8");
84274
84554
  }
84275
84555
  const rows = text.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line));
@@ -84778,7 +85058,7 @@ var init_suite = __esm({
84778
85058
 
84779
85059
  // ../../packages/evals/dist/evidence.js
84780
85060
  import { mkdir as mkdir17, readFile as readFile21, writeFile as writeFile10 } from "node:fs/promises";
84781
- import { dirname as dirname13 } from "node:path";
85061
+ import { dirname as dirname14 } from "node:path";
84782
85062
  async function fetchCrowdCodeEvidence(options = {}) {
84783
85063
  const cachePath = options.cachePath;
84784
85064
  if (cachePath && options.useCache !== false) {
@@ -84795,7 +85075,7 @@ async function fetchCrowdCodeEvidence(options = {}) {
84795
85075
  const exported = await fetchGatedExport(fetchImpl, baseUrl, exportToken, log);
84796
85076
  if (exported) {
84797
85077
  if (cachePath) {
84798
- await mkdir17(dirname13(cachePath), { recursive: true });
85078
+ await mkdir17(dirname14(cachePath), { recursive: true });
84799
85079
  await writeFile10(cachePath, `${JSON.stringify(exported, null, 1)}
84800
85080
  `, "utf8");
84801
85081
  }
@@ -84826,7 +85106,7 @@ async function fetchCrowdCodeEvidence(options = {}) {
84826
85106
  }
84827
85107
  };
84828
85108
  if (cachePath) {
84829
- await mkdir17(dirname13(cachePath), { recursive: true });
85109
+ await mkdir17(dirname14(cachePath), { recursive: true });
84830
85110
  await writeFile10(cachePath, `${JSON.stringify(bundle, null, 1)}
84831
85111
  `, "utf8");
84832
85112
  }
@@ -84996,7 +85276,7 @@ var init_evidence = __esm({
84996
85276
  // ../../packages/evals/dist/knowledge.js
84997
85277
  import { createHash as createHash8 } from "node:crypto";
84998
85278
  import { mkdir as mkdir18, readdir as readdir2, readFile as readFile22, writeFile as writeFile11 } from "node:fs/promises";
84999
- import { dirname as dirname14, join as join31, relative as relative4 } from "node:path";
85279
+ import { dirname as dirname15, join as join31, relative as relative4 } from "node:path";
85000
85280
  function approxTokens(text) {
85001
85281
  return Math.ceil(text.length / 4);
85002
85282
  }
@@ -85088,7 +85368,7 @@ async function writeTree(dir, tree, meta4) {
85088
85368
  await mkdir18(dir, { recursive: true });
85089
85369
  for (const [name, content] of Object.entries(tree)) {
85090
85370
  const path3 = join31(dir, name);
85091
- await mkdir18(dirname14(path3), { recursive: true });
85371
+ await mkdir18(dirname15(path3), { recursive: true });
85092
85372
  await writeFile11(path3, content.endsWith("\n") ? content : `${content}
85093
85373
  `, "utf8");
85094
85374
  }
@@ -86013,27 +86293,40 @@ async function walletSummary(options = {}) {
86013
86293
  return inFlight;
86014
86294
  }
86015
86295
  async function fetchWalletSummary() {
86016
- const wallet = await readAgentCashWallet();
86296
+ const [wallet, solanaFile] = await Promise.all([readAgentCashWallet(), readAgentCashSolanaAddress()]);
86017
86297
  try {
86018
86298
  const runtime = await sharedEconomyRuntime();
86019
86299
  const balance = await runtime.agentcash.getBalance();
86020
86300
  if (balance.ok) {
86021
- const { monad, ...agentcash } = balance.data && typeof balance.data === "object" && !Array.isArray(balance.data) ? balance.data : { agentcash: balance.data };
86301
+ const { monad, accounts: rawAccounts, ...agentcash } = balance.data && typeof balance.data === "object" && !Array.isArray(balance.data) ? balance.data : { agentcash: balance.data };
86022
86302
  const monadUsdc = monad?.usdc;
86023
86303
  const agentcashTotal = parseUsdCents("agentcash" in agentcash && Object.keys(agentcash).length === 1 ? agentcash.agentcash : agentcash);
86024
86304
  const monadCents = typeof monadUsdc === "number" ? Math.round(monadUsdc * 100) : void 0;
86305
+ const accounts = rawAccounts === void 0 ? void 0 : parseWalletAccounts(rawAccounts);
86306
+ const solana = accounts?.find((account) => account.network === "solana");
86025
86307
  return {
86026
86308
  address: wallet?.address,
86027
86309
  totalCents: agentcashTotal === void 0 && monadCents === void 0 ? void 0 : (agentcashTotal ?? 0) + (monadCents ?? 0),
86028
86310
  monadCents,
86311
+ solanaAddress: solana?.address ?? solanaFile,
86312
+ solanaCents: solana?.usdc === void 0 ? void 0 : Math.round(solana.usdc * 100),
86313
+ accounts,
86029
86314
  raw: balance.data
86030
86315
  };
86031
86316
  }
86032
- return { address: wallet?.address, error: balance.error };
86317
+ return { address: wallet?.address, solanaAddress: solanaFile, error: balance.error };
86033
86318
  } catch (error53) {
86034
- return { address: wallet?.address, error: error53.message };
86319
+ return { address: wallet?.address, solanaAddress: solanaFile, error: error53.message };
86035
86320
  }
86036
86321
  }
86322
+ function walletNetworkRows(summary) {
86323
+ const rows = {};
86324
+ for (const account of summary.accounts ?? []) {
86325
+ rows[account.network] = `${account.usdc === void 0 ? "unknown" : `${account.usdc.toFixed(6)} USDC`}${account.address ? ` \xB7 ${account.address}` : ""}`;
86326
+ }
86327
+ if (summary.monadCents !== void 0) rows.monad = `${(summary.monadCents / 100).toFixed(2)} USDC \xB7 ${summary.address ?? "unknown"}`;
86328
+ return rows;
86329
+ }
86037
86330
  function parseUsdCents(data) {
86038
86331
  const record3 = data && typeof data === "object" && !Array.isArray(data) ? data : void 0;
86039
86332
  if (!record3) {
@@ -86108,6 +86401,7 @@ var COMMAND_REGISTRY = [
86108
86401
  rows.wallet = wallet.address ?? "no AgentCash wallet found";
86109
86402
  rows.wallet_balance = wallet.totalCents !== void 0 ? formatCents2(wallet.totalCents) : wallet.error ? `unavailable (${wallet.error})` : "unknown";
86110
86403
  if (wallet.monadCents !== void 0) rows.wallet_monad_usdc = formatCents2(wallet.monadCents);
86404
+ if (wallet.solanaAddress) rows.wallet_solana = `${wallet.solanaAddress}${wallet.solanaCents !== void 0 ? ` \xB7 ${formatCents2(wallet.solanaCents)}` : ""}`;
86111
86405
  }
86112
86406
  return { kind: "text", label: "Status", body: renderKeyValues(rows) };
86113
86407
  }
@@ -87284,7 +87578,7 @@ async function startTui(options = {}) {
87284
87578
  async function main(argv) {
87285
87579
  const paymentNetwork = readOption(argv, "--payment-network");
87286
87580
  if (argv.includes("--payment-network")) {
87287
- if (!parsePaymentNetwork(paymentNetwork)) throw new Error("--payment-network must be base or monad");
87581
+ if (!parsePaymentNetwork(paymentNetwork)) throw new Error(`--payment-network must be ${PAYMENT_NETWORKS.join(", ")}`);
87288
87582
  process.env.OPENCROWD_PAYMENT_NETWORK = paymentNetwork;
87289
87583
  argv = argv.filter((_arg, index2) => !isConsumedOption(argv, index2, ["--payment-network"]));
87290
87584
  }
@@ -87548,7 +87842,7 @@ async function configCommand(args) {
87548
87842
  case "payment-network": {
87549
87843
  const paymentNetwork = parsePaymentNetwork(value);
87550
87844
  if (!paymentNetwork) {
87551
- throw new Error("payment-network must be base or monad");
87845
+ throw new Error(`payment-network must be ${PAYMENT_NETWORKS.join(", ")}`);
87552
87846
  }
87553
87847
  await updateConfig({ paymentNetwork });
87554
87848
  savedValue = paymentNetwork;
@@ -87646,19 +87940,32 @@ async function walletCommand(args) {
87646
87940
  if (!wallet) {
87647
87941
  throw new Error("No AgentCash wallet found. Install agentcash (its wallet is created automatically on first use) and retry.");
87648
87942
  }
87649
- const result = { address: wallet.address, networks: "base, tempo, solana, monad (eip155:143)", asset: "USDC", preferred_payment_network: resolvePaymentNetwork(await loadConfig()) };
87943
+ const solana = await readAgentCashSolanaAddress();
87944
+ const result = {
87945
+ address: wallet.address,
87946
+ networks: "base, tempo, monad (eip155:143)",
87947
+ ...solana ? { solana_address: solana } : {},
87948
+ asset: "USDC",
87949
+ preferred_payment_network: resolvePaymentNetwork(await loadConfig())
87950
+ };
87650
87951
  printValue("Wallet", result, { json: json3, pretty: renderKeyValues(asRecord(result)) });
87651
87952
  return;
87652
87953
  }
87653
- if (action === "balance") {
87954
+ if (action === "balance" || action === "accounts") {
87654
87955
  const summary = await walletSummary();
87655
87956
  if (summary.error) {
87656
87957
  throw new Error(`wallet balance unavailable: ${summary.error}`);
87657
87958
  }
87658
- printValue("Wallet", summary.raw ?? summary, { json: json3, pretty: renderKeyValues(asRecord(summary.raw ?? summary)) });
87959
+ const rows = walletNetworkRows(summary);
87960
+ const pretty = Object.keys(rows).length ? renderKeyValues({ ...summary.totalCents !== void 0 ? { total: formatCents2(summary.totalCents) } : {}, ...rows }) : renderKeyValues(asRecord(summary.raw ?? summary));
87961
+ printValue(
87962
+ "Wallet",
87963
+ action === "accounts" ? { accounts: summary.accounts ?? [], ...summary.solanaAddress ? { solana_address: summary.solanaAddress } : {} } : summary.raw ?? summary,
87964
+ { json: json3, pretty }
87965
+ );
87659
87966
  return;
87660
87967
  }
87661
- throw new Error("wallet supports address, balance, limits [--per-call USD] [--per-query USD]");
87968
+ throw new Error("wallet supports address, balance, accounts, limits [--per-call USD] [--per-query USD]");
87662
87969
  }
87663
87970
  async function modelsCommand(args) {
87664
87971
  const json3 = args.includes("--json");
@@ -87796,14 +88103,16 @@ function printHelp() {
87796
88103
  opencrowd config set provider|model|submodel|budget|approval|payment-network <value>
87797
88104
  opencrowd ledger [--json] show [--session <id>]
87798
88105
  opencrowd resume <session-id> [--approve-once | --query-budget <usd> | --decline] [--workspace <dir>]
87799
- opencrowd wallet [--json] address|balance
88106
+ opencrowd wallet [--json] address|balance|accounts
87800
88107
  opencrowd wallet limits [--per-call <usd>] [--per-query <usd>]
87801
88108
  opencrowd models [--json] list
87802
88109
  opencrowd doctor
87803
88110
  opencrowd --version
87804
88111
 
87805
88112
  Global options:
87806
- --payment-network base|monad prefer this x402 network when a service offers several
88113
+ --payment-network base|solana|monad
88114
+ prefer this network when a service offers several; otherwise the first of
88115
+ Base, Solana, Tempo (MPP) your balance covers
87807
88116
  (also OPENCROWD_PAYMENT_NETWORK or config payment-network; default base)
87808
88117
  opencrowd evals gaia [--tier smoke|level1|full] [--harness opencrowd,claude,codex] [--parallel <n>] [--hf-token <token>] [--auto] [--model <model>] [--subagent-model <model|off>] [--yes]
87809
88118
  opencrowd evals evolve [--out <dir>] [--generations <n>] [--candidates <n>] [--model <agent model>] [--proposer <model,model>] [--seed-trees <dir,dir>] [--baseline-report <report.json>] [--parallel <n>]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrowd",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "A CLI agent with its own wallet — discovers x402 services, pays in USDC, and completes tasks under a local budget",
5
5
  "type": "module",
6
6
  "license": "MIT",