agentbnb 9.1.1 → 9.2.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.
Files changed (54) hide show
  1. package/README.md +22 -0
  2. package/dist/{card-UF465O7O.js → card-L3ZPPBVI.js} +3 -3
  3. package/dist/{chunk-LENX5NUW.js → chunk-2KSRFDKF.js} +56 -3
  4. package/dist/{chunk-TCA63C42.js → chunk-563ZZUOA.js} +61 -27
  5. package/dist/chunk-5FXLZ5FX.js +16 -0
  6. package/dist/{chunk-ELFGYC22.js → chunk-7YJOBVWN.js} +3 -0
  7. package/dist/{chunk-5CC6O6SO.js → chunk-AA25Z6FW.js} +1 -1
  8. package/dist/{chunk-WARYO57F.js → chunk-COOBXNXU.js} +6 -6
  9. package/dist/{chunk-I4E5ERDN.js → chunk-CPSV5WR2.js} +1 -1
  10. package/dist/{process-guard-6324CZDC.js → chunk-DG4FQ4MD.js} +1 -1
  11. package/dist/{chunk-MPS4RE7T.js → chunk-DT2IEL5U.js} +11 -0
  12. package/dist/chunk-EMVVFP2L.js +54 -0
  13. package/dist/{chunk-O44N3KR7.js → chunk-FVLHEI3Y.js} +5 -5
  14. package/dist/chunk-GIK2AZQH.js +23 -0
  15. package/dist/{chunk-W6LOCBWQ.js → chunk-IWAK4WHK.js} +1 -1
  16. package/dist/{chunk-Y7CO3VLF.js → chunk-NISX3N7K.js} +16 -7
  17. package/dist/{chunk-2HH2F3DM.js → chunk-NLGLGR2K.js} +55 -2
  18. package/dist/{chunk-AW4VSROG.js → chunk-OFIRWD6B.js} +1 -1
  19. package/dist/{chunk-AMABG5SI.js → chunk-OI46BKQF.js} +12 -12
  20. package/dist/{chunk-F2CIPAN2.js → chunk-OTAZIF65.js} +2 -2
  21. package/dist/{chunk-ZYOMPJGG.js → chunk-OXU4QJSZ.js} +3 -3
  22. package/dist/{chunk-RVOZHVM7.js → chunk-PFAEZI32.js} +10 -10
  23. package/dist/{chunk-D7NH6YLM.js → chunk-UJXDBOKV.js} +12 -1
  24. package/dist/{chunk-G4TF4LB4.js → chunk-VDYHCI5F.js} +22 -1
  25. package/dist/{chunk-QG2LLVXP.js → chunk-WA23XRTN.js} +1 -1
  26. package/dist/cli/index.js +797 -107
  27. package/dist/{client-XOSXFC7Q.js → client-KL67WZVJ.js} +2 -2
  28. package/dist/{conduct-UT6ZYSJD.js → conduct-65BGO2EU.js} +16 -14
  29. package/dist/{conduct-MALC6HEK.js → conduct-AALDEKTH.js} +16 -14
  30. package/dist/{conductor-mode-3WLLERB4.js → conductor-mode-6S6ADNLW.js} +18 -18
  31. package/dist/{conductor-mode-UJKMO2GW.js → conductor-mode-HJHU4XLT.js} +4 -4
  32. package/dist/{credits-action-KOUJNR36.js → credits-action-CLLPNRDT.js} +17 -8
  33. package/dist/{did-action-UHUYMA4Y.js → did-action-ERXWCVEJ.js} +3 -3
  34. package/dist/{execute-UFMGTXET.js → execute-R5STYWLD.js} +13 -13
  35. package/dist/{execute-3RADNI74.js → execute-YMPHTJPN.js} +3 -3
  36. package/dist/{openclaw-setup-HEWZZOY7.js → openclaw-setup-HUOBTGN4.js} +17 -15
  37. package/dist/{openclaw-skills-5XLQFRWT.js → openclaw-skills-74372B6I.js} +2 -2
  38. package/dist/process-guard-IUMZ2GSD.js +8 -0
  39. package/dist/{publish-capability-LM4RSQXX.js → publish-capability-OYXXXYAU.js} +4 -4
  40. package/dist/remote-registry-5TM7DMCO.js +16 -0
  41. package/dist/{request-LID2N42Y.js → request-NEA66RCW.js} +66 -24
  42. package/dist/{serve-skill-CDNSHTEE.js → serve-skill-VKNRBVWE.js} +19 -18
  43. package/dist/{server-QIAO3YSK.js → server-LNT4YQZ7.js} +24 -20
  44. package/dist/{service-coordinator-FB44QL7L.js → service-coordinator-RE2KPWO4.js} +312 -54
  45. package/dist/{session-action-GYITLYOE.js → session-action-UBWJTQVQ.js} +19 -4
  46. package/dist/signing-AQTKYJDB.js +16 -0
  47. package/dist/skills/agentbnb/bootstrap.js +430 -43
  48. package/dist/{store-C4DLIXYM.js → store-GJJFFEQZ.js} +3 -3
  49. package/dist/{vc-action-BWGNQ77Y.js → vc-action-72TQVMY2.js} +15 -5
  50. package/package.json +12 -18
  51. package/skills/agentbnb/install.sh +0 -0
  52. package/dist/{chunk-YNBZLXYS.js → chunk-65GNX2KC.js} +0 -0
  53. package/dist/{chunk-7VZ4M4CT.js → chunk-QE42IJC4.js} +6 -6
  54. package/dist/{daemon-ETXXE4IS.js → daemon-OM2K3U7J.js} +1 -1
@@ -1,14 +1,3 @@
1
- import {
2
- createLedger,
3
- deriveAgentId,
4
- ensureIdentity,
5
- executeCapabilityBatch,
6
- executeCapabilityRequest,
7
- identityAuthPlugin,
8
- loadOrRepairIdentity,
9
- notifyProviderEvent,
10
- syncCreditsFromRegistry
11
- } from "../../chunk-2HH2F3DM.js";
12
1
  import {
13
2
  AutoRequestor,
14
3
  BudgetController,
@@ -27,7 +16,19 @@ import {
27
16
  requestViaTemporaryRelay,
28
17
  resolvePendingRequest,
29
18
  verifyUCAN
30
- } from "../../chunk-F2CIPAN2.js";
19
+ } from "../../chunk-OTAZIF65.js";
20
+ import {
21
+ createLedger,
22
+ deriveAgentId,
23
+ ensureIdentity,
24
+ executeCapabilityBatch,
25
+ executeCapabilityRequest,
26
+ identityAuthPlugin,
27
+ loadOrRepairIdentity,
28
+ notifyProviderEvent,
29
+ syncCreditsFromRegistry,
30
+ tryVerifyIdentity
31
+ } from "../../chunk-NLGLGR2K.js";
31
32
  import {
32
33
  NETWORK_FEE_RATE,
33
34
  bootstrapAgent,
@@ -37,6 +38,7 @@ import {
37
38
  fetchRemoteCards,
38
39
  filterCards,
39
40
  getBalance,
41
+ getEscrowStatus,
40
42
  getTransactions,
41
43
  holdEscrow,
42
44
  markEscrowAbandoned,
@@ -48,7 +50,7 @@ import {
48
50
  releaseEscrow,
49
51
  searchCards,
50
52
  settleEscrow
51
- } from "../../chunk-MPS4RE7T.js";
53
+ } from "../../chunk-DT2IEL5U.js";
52
54
  import {
53
55
  attachCanonicalAgentId,
54
56
  getCard,
@@ -80,13 +82,13 @@ import "../../chunk-EE3V3DXK.js";
80
82
  import {
81
83
  requestCapability,
82
84
  requestViaRelay
83
- } from "../../chunk-W6LOCBWQ.js";
85
+ } from "../../chunk-IWAK4WHK.js";
84
86
  import {
85
87
  generateKeyPair,
86
88
  loadKeyPair,
87
89
  signEscrowReceipt,
88
90
  verifyEscrowReceipt
89
- } from "../../chunk-YNBZLXYS.js";
91
+ } from "../../chunk-65GNX2KC.js";
90
92
  import {
91
93
  AgentBnBError,
92
94
  AnyCardSchema
@@ -1504,7 +1506,7 @@ var AgentRuntime = class {
1504
1506
  }
1505
1507
  const modes = /* @__PURE__ */ new Map();
1506
1508
  if (this.conductorEnabled) {
1507
- const { ConductorMode } = await import("../../conductor-mode-UJKMO2GW.js");
1509
+ const { ConductorMode } = await import("../../conductor-mode-HJHU4XLT.js");
1508
1510
  const { registerConductorCard, CONDUCTOR_OWNER } = await import("../../card-U2HQRPYN.js");
1509
1511
  const { loadPeers: loadPeers2 } = await import("../../peers-IOVCBWAI.js");
1510
1512
  registerConductorCard(this.registryDb);
@@ -3776,6 +3778,75 @@ function initiateGithubAuth() {
3776
3778
  };
3777
3779
  }
3778
3780
 
3781
+ // src/registry/hub-identities.ts
3782
+ import { randomBytes as randomBytes2, createHash } from "crypto";
3783
+ var HUB_IDENTITIES_SCHEMA = `
3784
+ CREATE TABLE IF NOT EXISTS hub_identities (
3785
+ email TEXT PRIMARY KEY,
3786
+ agent_id TEXT NOT NULL UNIQUE,
3787
+ public_key TEXT NOT NULL,
3788
+ encrypted_private_key TEXT NOT NULL,
3789
+ kdf_salt TEXT NOT NULL,
3790
+ display_name TEXT NOT NULL,
3791
+ created_at TEXT NOT NULL
3792
+ );
3793
+
3794
+ CREATE INDEX IF NOT EXISTS idx_hub_identities_agent_id
3795
+ ON hub_identities(agent_id);
3796
+ `;
3797
+ var CHALLENGES_SCHEMA = `
3798
+ CREATE TABLE IF NOT EXISTS hub_challenges (
3799
+ challenge TEXT PRIMARY KEY,
3800
+ expires_at TEXT NOT NULL,
3801
+ consumed_at TEXT
3802
+ );
3803
+ `;
3804
+ function ensureHubIdentitiesTables(db) {
3805
+ db.exec(HUB_IDENTITIES_SCHEMA);
3806
+ db.exec(CHALLENGES_SCHEMA);
3807
+ }
3808
+ function deriveAgentId2(publicKeyHex) {
3809
+ const hash = createHash("sha256").update(publicKeyHex, "hex").digest("hex");
3810
+ return `agent-${hash.slice(0, 16)}`;
3811
+ }
3812
+ var CHALLENGE_TTL_MS = 10 * 60 * 1e3;
3813
+ function createChallenge(db) {
3814
+ const challenge = randomBytes2(32).toString("hex");
3815
+ const expires_at = new Date(Date.now() + CHALLENGE_TTL_MS).toISOString();
3816
+ db.prepare("INSERT INTO hub_challenges (challenge, expires_at) VALUES (?, ?)").run(challenge, expires_at);
3817
+ return { challenge, expires_at };
3818
+ }
3819
+ function consumeChallenge(db, challenge) {
3820
+ const row = db.prepare("SELECT expires_at, consumed_at FROM hub_challenges WHERE challenge = ?").get(challenge);
3821
+ if (!row) return false;
3822
+ if (row.consumed_at) return false;
3823
+ if (new Date(row.expires_at).getTime() < Date.now()) return false;
3824
+ const consumed_at = (/* @__PURE__ */ new Date()).toISOString();
3825
+ db.prepare("UPDATE hub_challenges SET consumed_at = ? WHERE challenge = ?").run(consumed_at, challenge);
3826
+ return true;
3827
+ }
3828
+ function pruneChallenges(db) {
3829
+ const cutoff = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
3830
+ db.prepare("DELETE FROM hub_challenges WHERE expires_at < ? OR consumed_at IS NOT NULL").run(cutoff);
3831
+ }
3832
+ function registerHubIdentity(db, input) {
3833
+ const agent_id = deriveAgentId2(input.public_key);
3834
+ const created_at = (/* @__PURE__ */ new Date()).toISOString();
3835
+ db.prepare(`
3836
+ INSERT INTO hub_identities (email, agent_id, public_key, encrypted_private_key, kdf_salt, display_name, created_at)
3837
+ VALUES (?, ?, ?, ?, ?, ?, ?)
3838
+ `).run(input.email, agent_id, input.public_key, input.encrypted_private_key, input.kdf_salt, input.display_name, created_at);
3839
+ return { ...input, agent_id, created_at };
3840
+ }
3841
+ function getHubIdentityByEmail(db, email) {
3842
+ const row = db.prepare("SELECT * FROM hub_identities WHERE email = ?").get(email);
3843
+ return row ?? null;
3844
+ }
3845
+ function getHubIdentityByAgentId(db, agent_id) {
3846
+ const row = db.prepare("SELECT * FROM hub_identities WHERE agent_id = ?").get(agent_id);
3847
+ return row ?? null;
3848
+ }
3849
+
3779
3850
  // src/registry/free-tier.ts
3780
3851
  function initFreeTierTable(db) {
3781
3852
  db.exec(`
@@ -3873,6 +3944,39 @@ async function creditRoutesPlugin(fastify, options) {
3873
3944
  const transactions = getTransactions(creditDb, owner, { limit, after: since });
3874
3945
  return reply.send({ owner, transactions, limit });
3875
3946
  });
3947
+ fastify.get("/api/credits/escrow/:id", {
3948
+ schema: {
3949
+ tags: ["credits"],
3950
+ summary: "Get escrow status by ID (public, no auth required)",
3951
+ params: {
3952
+ type: "object",
3953
+ properties: { id: { type: "string" } },
3954
+ required: ["id"]
3955
+ },
3956
+ response: {
3957
+ 200: {
3958
+ type: "object",
3959
+ properties: {
3960
+ id: { type: "string" },
3961
+ owner: { type: "string" },
3962
+ amount: { type: "number" },
3963
+ card_id: { type: "string" },
3964
+ status: { type: "string" },
3965
+ created_at: { type: "string" },
3966
+ settled_at: { type: ["string", "null"] }
3967
+ }
3968
+ },
3969
+ 404: { type: "object", properties: { error: { type: "string" } } }
3970
+ }
3971
+ }
3972
+ }, async (request, reply) => {
3973
+ const { id } = request.params;
3974
+ const escrow = getEscrowStatus(creditDb, id);
3975
+ if (!escrow) {
3976
+ return reply.code(404).send({ error: "Escrow record not found" });
3977
+ }
3978
+ return reply.send(escrow);
3979
+ });
3876
3980
  await fastify.register(async (scope) => {
3877
3981
  identityAuthPlugin(scope, { agentDb: creditDb });
3878
3982
  scope.post("/api/credits/hold", {
@@ -5805,6 +5909,121 @@ function createRegistryServer(opts) {
5805
5909
  throw err;
5806
5910
  }
5807
5911
  });
5912
+ ensureHubIdentitiesTables(db);
5913
+ try {
5914
+ pruneChallenges(db);
5915
+ } catch {
5916
+ }
5917
+ api.get("/api/agents/challenge", {
5918
+ schema: {
5919
+ tags: ["hub-auth"],
5920
+ summary: "Get a registration challenge",
5921
+ response: {
5922
+ 200: {
5923
+ type: "object",
5924
+ properties: {
5925
+ challenge: { type: "string" },
5926
+ expires_at: { type: "string" }
5927
+ }
5928
+ }
5929
+ }
5930
+ }
5931
+ }, async (_request, reply) => {
5932
+ const { challenge, expires_at } = createChallenge(db);
5933
+ return reply.send({ challenge, expires_at });
5934
+ });
5935
+ api.post("/api/agents/register", {
5936
+ schema: {
5937
+ tags: ["hub-auth"],
5938
+ summary: "Register a new Hub-managed agent identity",
5939
+ body: {
5940
+ type: "object",
5941
+ required: ["email", "public_key", "encrypted_private_key", "kdf_salt", "display_name", "challenge", "signature"],
5942
+ properties: {
5943
+ email: { type: "string", format: "email" },
5944
+ public_key: { type: "string" },
5945
+ encrypted_private_key: { type: "string" },
5946
+ kdf_salt: { type: "string" },
5947
+ display_name: { type: "string" },
5948
+ challenge: { type: "string" },
5949
+ signature: { type: "string" }
5950
+ }
5951
+ },
5952
+ response: {
5953
+ 201: { type: "object", additionalProperties: true },
5954
+ 400: { type: "object", properties: { error: { type: "string" } } },
5955
+ 409: { type: "object", properties: { error: { type: "string" } } }
5956
+ }
5957
+ }
5958
+ }, async (request, reply) => {
5959
+ const body = request.body;
5960
+ if (!consumeChallenge(db, body.challenge)) {
5961
+ return reply.code(400).send({ error: "Invalid or expired challenge" });
5962
+ }
5963
+ if (!/^[0-9a-fA-F]+$/.test(body.public_key) || body.public_key.length % 2 !== 0) {
5964
+ return reply.code(400).send({ error: "Invalid public_key format" });
5965
+ }
5966
+ try {
5967
+ const { verifyEscrowReceipt: verifyEscrowReceipt2 } = await import("../../signing-AQTKYJDB.js");
5968
+ const publicKeyBuffer = Buffer.from(body.public_key, "hex");
5969
+ const valid = verifyEscrowReceipt2({ challenge: body.challenge }, body.signature, publicKeyBuffer);
5970
+ if (!valid) {
5971
+ return reply.code(400).send({ error: "Invalid signature" });
5972
+ }
5973
+ } catch (err) {
5974
+ const msg = err instanceof Error ? err.message : String(err);
5975
+ return reply.code(400).send({ error: `Signature verification failed: ${msg}` });
5976
+ }
5977
+ const existing = getHubIdentityByEmail(db, body.email.toLowerCase());
5978
+ if (existing) {
5979
+ return reply.code(409).send({ error: "Email already registered" });
5980
+ }
5981
+ const agent_id = deriveAgentId2(body.public_key);
5982
+ const existingByAgentId = getHubIdentityByAgentId(db, agent_id);
5983
+ if (existingByAgentId) {
5984
+ return reply.code(409).send({ error: "Agent already registered" });
5985
+ }
5986
+ const identity = registerHubIdentity(db, {
5987
+ email: body.email.toLowerCase(),
5988
+ public_key: body.public_key,
5989
+ encrypted_private_key: body.encrypted_private_key,
5990
+ kdf_salt: body.kdf_salt,
5991
+ display_name: body.display_name
5992
+ });
5993
+ return reply.code(201).send({
5994
+ agent_id: identity.agent_id,
5995
+ did: `did:agentbnb:${identity.agent_id}`,
5996
+ created_at: identity.created_at
5997
+ });
5998
+ });
5999
+ api.post("/api/agents/login", {
6000
+ schema: {
6001
+ tags: ["hub-auth"],
6002
+ summary: "Fetch encrypted identity blob for login",
6003
+ body: {
6004
+ type: "object",
6005
+ required: ["email"],
6006
+ properties: { email: { type: "string", format: "email" } }
6007
+ },
6008
+ response: {
6009
+ 200: { type: "object", additionalProperties: true },
6010
+ 404: { type: "object", properties: { error: { type: "string" } } }
6011
+ }
6012
+ }
6013
+ }, async (request, reply) => {
6014
+ const body = request.body;
6015
+ const identity = getHubIdentityByEmail(db, body.email.toLowerCase());
6016
+ if (!identity) {
6017
+ return reply.code(404).send({ error: "Identity not found" });
6018
+ }
6019
+ return reply.send({
6020
+ agent_id: identity.agent_id,
6021
+ public_key: identity.public_key,
6022
+ encrypted_private_key: identity.encrypted_private_key,
6023
+ kdf_salt: identity.kdf_salt,
6024
+ display_name: identity.display_name
6025
+ });
6026
+ });
5808
6027
  api.post("/api/identity/link", {
5809
6028
  schema: {
5810
6029
  tags: ["identity"],
@@ -6104,7 +6323,7 @@ function createRegistryServer(opts) {
6104
6323
  });
6105
6324
  await relayClient.connect();
6106
6325
  }
6107
- const { requestViaRelay: requestViaRelay2 } = await import("../../client-XOSXFC7Q.js");
6326
+ const { requestViaRelay: requestViaRelay2 } = await import("../../client-KL67WZVJ.js");
6108
6327
  return requestViaRelay2(relayClient, {
6109
6328
  targetOwner: target.owner,
6110
6329
  cardId: target.cardId,
@@ -6123,12 +6342,24 @@ function createRegistryServer(opts) {
6123
6342
  const ownerApiKey = opts.ownerApiKey;
6124
6343
  const ownerName = opts.ownerName;
6125
6344
  void api.register(async (ownerRoutes) => {
6126
- ownerRoutes.addHook("onRequest", async (request, reply) => {
6345
+ ownerRoutes.addHook("preHandler", async (request, reply) => {
6127
6346
  const auth = request.headers.authorization;
6128
6347
  const token = auth?.startsWith("Bearer ") ? auth.slice(7).trim() : null;
6129
- if (!token || token !== ownerApiKey) {
6130
- return reply.status(401).send({ error: "Unauthorized" });
6348
+ if (token === ownerApiKey) {
6349
+ request.agentId = ownerName;
6350
+ return;
6351
+ }
6352
+ const didResult = await tryVerifyIdentity(request, {});
6353
+ if (didResult.valid) {
6354
+ const hubIdentity = getHubIdentityByAgentId(db, didResult.agentId);
6355
+ if (!hubIdentity) {
6356
+ return reply.status(401).send({ error: "Agent not registered on this Hub" });
6357
+ }
6358
+ request.agentId = didResult.agentId;
6359
+ request.agentPublicKey = didResult.publicKey;
6360
+ return;
6131
6361
  }
6362
+ return reply.status(401).send({ error: "Unauthorized" });
6132
6363
  });
6133
6364
  ownerRoutes.get("/me", {
6134
6365
  schema: {
@@ -6139,13 +6370,14 @@ function createRegistryServer(opts) {
6139
6370
  200: { type: "object", properties: { owner: { type: "string" }, balance: { type: "number" } } }
6140
6371
  }
6141
6372
  }
6142
- }, async (_request, reply) => {
6373
+ }, async (request, reply) => {
6374
+ const identity = request.agentId ?? ownerName;
6143
6375
  let balance = 0;
6144
6376
  if (opts.creditDb) {
6145
6377
  const ledger = createLedger({ db: opts.creditDb });
6146
- balance = await ledger.getBalance(ownerName);
6378
+ balance = await ledger.getBalance(identity);
6147
6379
  }
6148
- return reply.send({ owner: ownerName, balance });
6380
+ return reply.send({ owner: identity, balance });
6149
6381
  });
6150
6382
  ownerRoutes.get("/requests", {
6151
6383
  schema: {
@@ -6767,6 +6999,56 @@ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
6767
6999
  import { join as join4 } from "path";
6768
7000
  import { randomUUID as randomUUID9 } from "crypto";
6769
7001
  import { Cron as Cron2 } from "croner";
7002
+
7003
+ // src/utils/runtime-mode.ts
7004
+ function isTestMode() {
7005
+ return process.env["AGENTBNB_TEST_MODE"] === "1" || process.env["NODE_ENV"] === "test" || process.env["VITEST"] === "true";
7006
+ }
7007
+ function isOfflineMode() {
7008
+ return process.env["AGENTBNB_OFFLINE"] === "1";
7009
+ }
7010
+ function shouldSkipNetwork() {
7011
+ return isTestMode() || isOfflineMode();
7012
+ }
7013
+
7014
+ // src/utils/network-probe.ts
7015
+ var PROBE_TIMEOUT_MS = 2e3;
7016
+ async function probeRegistry(registryUrl, timeoutMs = PROBE_TIMEOUT_MS) {
7017
+ if (shouldSkipNetwork()) return false;
7018
+ const controller = new AbortController();
7019
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
7020
+ try {
7021
+ const url = `${registryUrl.replace(/\/$/, "")}/health`;
7022
+ const res = await fetch(url, { method: "GET", signal: controller.signal });
7023
+ return res.ok;
7024
+ } catch {
7025
+ return false;
7026
+ } finally {
7027
+ clearTimeout(timer);
7028
+ }
7029
+ }
7030
+
7031
+ // src/utils/with-timeout.ts
7032
+ var TimeoutError = class extends Error {
7033
+ constructor(timeoutMs) {
7034
+ super(`Operation timed out after ${timeoutMs}ms`);
7035
+ this.name = "TimeoutError";
7036
+ }
7037
+ };
7038
+ async function withTimeout(promise, timeoutMs) {
7039
+ let timer;
7040
+ const timeoutPromise = new Promise((_, reject) => {
7041
+ timer = setTimeout(() => reject(new TimeoutError(timeoutMs)), timeoutMs);
7042
+ });
7043
+ try {
7044
+ return await Promise.race([promise, timeoutPromise]);
7045
+ } finally {
7046
+ if (timer) clearTimeout(timer);
7047
+ }
7048
+ }
7049
+
7050
+ // src/runtime/service-coordinator.ts
7051
+ var STARTUP_SYNC_BUDGET_MS = 3e3;
6770
7052
  function buildFallbackRelayCard(owner) {
6771
7053
  return {
6772
7054
  id: randomUUID9(),
@@ -6970,11 +7252,28 @@ var ServiceCoordinator = class {
6970
7252
  this.runtime.registerJob(idleJob);
6971
7253
  console.log("IdleMonitor started (60s poll interval, 70% idle threshold)");
6972
7254
  if (this.config.registry) {
6973
- const startupSync = await syncCreditsFromRegistry(this.config, this.runtime.creditDb);
6974
- if (startupSync.synced) {
6975
- console.log(`[agentbnb] credits synced: ${startupSync.remoteBalance} (was ${startupSync.localWas})`);
7255
+ if (shouldSkipNetwork()) {
7256
+ console.warn("[agentbnb] credit sync skipped: test/offline mode");
6976
7257
  } else {
6977
- console.warn(`[agentbnb] credit sync skipped: ${startupSync.error}`);
7258
+ const reachable = await probeRegistry(this.config.registry);
7259
+ if (!reachable) {
7260
+ console.warn("[agentbnb] credit sync skipped: registry unreachable (will retry every 5m)");
7261
+ } else {
7262
+ try {
7263
+ const startupSync = await withTimeout(
7264
+ syncCreditsFromRegistry(this.config, this.runtime.creditDb),
7265
+ STARTUP_SYNC_BUDGET_MS
7266
+ );
7267
+ if (startupSync.synced) {
7268
+ console.log(`[agentbnb] credits synced: ${startupSync.remoteBalance} (was ${startupSync.localWas})`);
7269
+ } else {
7270
+ console.warn(`[agentbnb] credit sync skipped: ${startupSync.error}`);
7271
+ }
7272
+ } catch (err) {
7273
+ const reason = err instanceof TimeoutError ? `timeout after ${STARTUP_SYNC_BUDGET_MS}ms` : err.message;
7274
+ console.warn(`[agentbnb] credit sync skipped: ${reason} (will retry every 5m)`);
7275
+ }
7276
+ }
6978
7277
  }
6979
7278
  this.creditSyncJob = new Cron2("*/5 * * * *", async () => {
6980
7279
  const result = await syncCreditsFromRegistry(this.config, this.runtime.creditDb);
@@ -7015,7 +7314,7 @@ var ServiceCoordinator = class {
7015
7314
  }
7016
7315
  if (opts.registryUrl && opts.relay) {
7017
7316
  const { RelayClient: RelayClient2 } = await import("../../websocket-client-RT4KLJL4.js");
7018
- const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../../execute-3RADNI74.js");
7317
+ const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../../execute-YMPHTJPN.js");
7019
7318
  const localCards = listCards(this.runtime.registryDb, this.config.owner);
7020
7319
  const { primaryCard, additionalCards } = buildRelayRegistrationCards(this.config.owner, localCards);
7021
7320
  if (this.config.conductor?.public) {
@@ -7761,6 +8060,56 @@ async function handleDiscover(args, ctx) {
7761
8060
 
7762
8061
  // src/mcp/tools/request.ts
7763
8062
  import { z as z9 } from "zod";
8063
+
8064
+ // src/autonomy/consumer-autonomy.ts
8065
+ var DEFAULT_CONSUMER_AUTONOMY = {
8066
+ session_budget: 50,
8067
+ single_request_max: 20,
8068
+ multi_skill_policy: "notify"
8069
+ };
8070
+ function createSessionState() {
8071
+ return { totalSpent: 0, paidCallCount: 0 };
8072
+ }
8073
+ function checkConsumerBudget(config, session, estimatedCost) {
8074
+ if (estimatedCost <= 0) {
8075
+ return { allowed: true };
8076
+ }
8077
+ if (estimatedCost > config.single_request_max) {
8078
+ return {
8079
+ allowed: false,
8080
+ error: `Request cost (${estimatedCost} credits) exceeds single_request_max (${config.single_request_max}). Adjust consumer_autonomy.single_request_max in config to allow higher-cost requests.`
8081
+ };
8082
+ }
8083
+ if (session.totalSpent + estimatedCost > config.session_budget) {
8084
+ return {
8085
+ allowed: false,
8086
+ error: `Session budget exceeded: spent ${session.totalSpent} + requested ${estimatedCost} = ${session.totalSpent + estimatedCost} credits, but session_budget is ${config.session_budget}. This session has already made ${session.paidCallCount} paid call(s).`
8087
+ };
8088
+ }
8089
+ if (session.paidCallCount > 0) {
8090
+ if (config.multi_skill_policy === "block") {
8091
+ return {
8092
+ allowed: false,
8093
+ error: `Multi-skill block: this would be paid call #${session.paidCallCount + 1} in this session (total spent: ${session.totalSpent}, this request: ${estimatedCost} credits). consumer_autonomy.multi_skill_policy is "block". Only one paid skill call is allowed per session unless policy is changed.`
8094
+ };
8095
+ }
8096
+ if (config.multi_skill_policy === "notify") {
8097
+ return {
8098
+ allowed: true,
8099
+ warning: `This is paid call #${session.paidCallCount + 1} in this session. Cumulative spend: ${session.totalSpent} + ${estimatedCost} = ${session.totalSpent + estimatedCost} credits (session budget: ${config.session_budget}).`
8100
+ };
8101
+ }
8102
+ }
8103
+ return { allowed: true };
8104
+ }
8105
+ function recordConsumerSpend(session, creditsSpent) {
8106
+ if (creditsSpent > 0) {
8107
+ session.totalSpent += creditsSpent;
8108
+ session.paidCallCount += 1;
8109
+ }
8110
+ }
8111
+
8112
+ // src/mcp/tools/request.ts
7764
8113
  var requestInputSchema = {
7765
8114
  query: z9.string().optional().describe("Search query to find a matching capability (auto-request mode)"),
7766
8115
  card_id: z9.string().optional().describe("Direct card ID to request (skips search)"),
@@ -7798,7 +8147,17 @@ function deriveTimeoutHintFromCard(remoteCard, skillId) {
7798
8147
  async function handleRequest(args, ctx) {
7799
8148
  try {
7800
8149
  const maxCost = args.max_cost ?? 50;
8150
+ const consumerConfig = ctx.config.consumer_autonomy ?? DEFAULT_CONSUMER_AUTONOMY;
8151
+ if (!ctx.consumerSession) {
8152
+ ctx.consumerSession = createSessionState();
8153
+ }
7801
8154
  if (args.query) {
8155
+ const budgetCheck = checkConsumerBudget(consumerConfig, ctx.consumerSession, maxCost);
8156
+ if (!budgetCheck.allowed) {
8157
+ return {
8158
+ content: [{ type: "text", text: JSON.stringify({ success: false, error: budgetCheck.error, budget_exceeded: true }) }]
8159
+ };
8160
+ }
7802
8161
  const registryDb = openDatabase(ctx.config.db_path);
7803
8162
  const creditDb = openCreditDb(ctx.config.credit_db_path);
7804
8163
  registryDb.pragma("busy_timeout = 5000");
@@ -7822,8 +8181,12 @@ async function handleRequest(args, ctx) {
7822
8181
  maxCostCredits: maxCost,
7823
8182
  params: args.params
7824
8183
  });
8184
+ const creditsUsed = typeof result?.creditsSpent === "number" ? result.creditsSpent : 0;
8185
+ recordConsumerSpend(ctx.consumerSession, creditsUsed);
8186
+ const response = { success: true, ...result };
8187
+ if (budgetCheck.warning) response.spend_warning = budgetCheck.warning;
7825
8188
  return {
7826
- content: [{ type: "text", text: JSON.stringify({ success: true, ...result }, null, 2) }]
8189
+ content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
7827
8190
  };
7828
8191
  } finally {
7829
8192
  registryDb.close();
@@ -7890,17 +8253,23 @@ async function handleRequest(args, ctx) {
7890
8253
  const targetAgentId = typeof remoteCard["agent_id"] === "string" ? remoteCard["agent_id"] : void 0;
7891
8254
  const gatewayUrl = remoteCard["gateway_url"];
7892
8255
  const timeoutHint = deriveTimeoutHintFromCard(remoteCard, args.skill_id);
8256
+ let remoteCost = 0;
8257
+ const remoteSkills = remoteCard["skills"];
8258
+ if (Array.isArray(remoteSkills)) {
8259
+ const matchedSkill = args.skill_id ? remoteSkills.find((s) => s.id === args.skill_id) : remoteSkills[0];
8260
+ remoteCost = matchedSkill?.pricing?.credits_per_call ?? 0;
8261
+ } else {
8262
+ const remotePricing = remoteCard["pricing"];
8263
+ remoteCost = remotePricing?.credits_per_call ?? 0;
8264
+ }
7893
8265
  if (gatewayUrl) {
7894
- let remoteCost = 0;
7895
- const remoteSkills = remoteCard["skills"];
7896
- if (Array.isArray(remoteSkills)) {
7897
- const matchedSkill = args.skill_id ? remoteSkills.find((s) => s.id === args.skill_id) : remoteSkills[0];
7898
- remoteCost = matchedSkill?.pricing?.credits_per_call ?? 0;
7899
- } else {
7900
- const remotePricing = remoteCard["pricing"];
7901
- remoteCost = remotePricing?.credits_per_call ?? 0;
7902
- }
7903
8266
  if (remoteCost > 0) {
8267
+ const budgetCheck = checkConsumerBudget(consumerConfig, ctx.consumerSession, remoteCost);
8268
+ if (!budgetCheck.allowed) {
8269
+ return {
8270
+ content: [{ type: "text", text: JSON.stringify({ success: false, error: budgetCheck.error, budget_exceeded: true }) }]
8271
+ };
8272
+ }
7904
8273
  if (!targetOwner) {
7905
8274
  return {
7906
8275
  content: [{ type: "text", text: JSON.stringify({ success: false, error: "Paid remote request requires a target owner for relay routing" }) }]
@@ -7917,8 +8286,11 @@ async function handleRequest(args, ctx) {
7917
8286
  params: { ...args.params ?? {}, ...args.skill_id ? { skill_id: args.skill_id } : {}, requester: ctx.config.owner },
7918
8287
  timeoutMs: args.timeout_ms
7919
8288
  });
8289
+ recordConsumerSpend(ctx.consumerSession, remoteCost);
8290
+ const response = { success: true, result, creditsSpent: remoteCost };
8291
+ if (budgetCheck.warning) response.spend_warning = budgetCheck.warning;
7920
8292
  return {
7921
- content: [{ type: "text", text: JSON.stringify({ success: true, result }, null, 2) }]
8293
+ content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
7922
8294
  };
7923
8295
  } else {
7924
8296
  const result = await requestCapability({
@@ -7936,6 +8308,14 @@ async function handleRequest(args, ctx) {
7936
8308
  }
7937
8309
  }
7938
8310
  if (targetOwner) {
8311
+ if (remoteCost > 0) {
8312
+ const budgetCheck = checkConsumerBudget(consumerConfig, ctx.consumerSession, remoteCost);
8313
+ if (!budgetCheck.allowed) {
8314
+ return {
8315
+ content: [{ type: "text", text: JSON.stringify({ success: false, error: budgetCheck.error, budget_exceeded: true }) }]
8316
+ };
8317
+ }
8318
+ }
7939
8319
  const result = await requestViaTemporaryRelay({
7940
8320
  registryUrl: ctx.config.registry,
7941
8321
  owner: ctx.config.owner,
@@ -7947,8 +8327,13 @@ async function handleRequest(args, ctx) {
7947
8327
  params: { ...args.params ?? {}, ...args.skill_id ? { skill_id: args.skill_id } : {} },
7948
8328
  timeoutMs: args.timeout_ms
7949
8329
  });
8330
+ if (remoteCost > 0) {
8331
+ recordConsumerSpend(ctx.consumerSession, remoteCost);
8332
+ }
8333
+ const response = { success: true, result };
8334
+ if (remoteCost > 0) response.creditsSpent = remoteCost;
7950
8335
  return {
7951
- content: [{ type: "text", text: JSON.stringify({ success: true, result }, null, 2) }]
8336
+ content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
7952
8337
  };
7953
8338
  }
7954
8339
  return {
@@ -8059,7 +8444,7 @@ async function conductAction(task, opts) {
8059
8444
  );
8060
8445
  };
8061
8446
  let relay;
8062
- if (config.registry) {
8447
+ if (config.registry && !shouldSkipNetwork()) {
8063
8448
  relay = new RelayClient({
8064
8449
  registryUrl: config.registry,
8065
8450
  owner: config.owner,
@@ -8069,8 +8454,10 @@ async function conductAction(task, opts) {
8069
8454
  silent: true
8070
8455
  });
8071
8456
  try {
8072
- await relay.connect();
8073
- } catch {
8457
+ await withTimeout(relay.connect(), 1e4);
8458
+ } catch (err) {
8459
+ if (err instanceof TimeoutError) {
8460
+ }
8074
8461
  relay = void 0;
8075
8462
  }
8076
8463
  }
@@ -12,11 +12,11 @@ import {
12
12
  updateReputation,
13
13
  updateSkillAvailability,
14
14
  updateSkillIdleRate
15
- } from "./chunk-7VZ4M4CT.js";
16
- import "./chunk-J4RFJVXI.js";
17
- import "./chunk-UVCNMRPS.js";
15
+ } from "./chunk-QE42IJC4.js";
18
16
  import "./chunk-4XTYT4JW.js";
19
17
  import "./chunk-GZUTU6IZ.js";
18
+ import "./chunk-J4RFJVXI.js";
19
+ import "./chunk-UVCNMRPS.js";
20
20
  import "./chunk-3RG5ZIWI.js";
21
21
  export {
22
22
  attachCanonicalAgentId,