agentbnb 5.1.11 → 7.0.0-beta.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 (60) hide show
  1. package/README.md +245 -39
  2. package/dist/{card-RSGDCHCV.js → card-REW7BSWW.js} +1 -1
  3. package/dist/{chunk-FLY3WIQR.js → chunk-2HSUPCBT.js} +3 -3
  4. package/dist/{chunk-WGZ5AGOX.js → chunk-3CIMVISQ.js} +24 -1
  5. package/dist/{chunk-NH2FIERR.js → chunk-574W3HHE.js} +1 -1
  6. package/dist/{chunk-WTXRY7R2.js → chunk-APEG4QIN.js} +157 -9
  7. package/dist/chunk-BP3L2TET.js +148 -0
  8. package/dist/{chunk-NLAWT4DT.js → chunk-CWYPTQRQ.js} +7 -7
  9. package/dist/{chunk-UKT6H7YT.js → chunk-DUW6RX6I.js} +5 -2
  10. package/dist/chunk-EAD4A4KG.js +430 -0
  11. package/dist/{chunk-QT7TEVNV.js → chunk-EHSHB7TY.js} +23 -1
  12. package/dist/{chunk-B5FTAGFN.js → chunk-ETGOKDFR.js} +75 -75
  13. package/dist/{chunk-5KFI5X7B.js → chunk-F53QQIM2.js} +1 -1
  14. package/dist/{chunk-MLS6IGGG.js → chunk-FK2MDNTB.js} +117 -117
  15. package/dist/{chunk-EGUOAHCW.js → chunk-GO4FVRVN.js} +15 -13
  16. package/dist/{chunk-CRFCWD6V.js → chunk-J2K5S5MX.js} +136 -173
  17. package/dist/chunk-K5FO42YF.js +1136 -0
  18. package/dist/{chunk-DFBX3BBD.js → chunk-KA2VIEGM.js} +211 -16
  19. package/dist/chunk-NWIQJ2CL.js +108 -0
  20. package/dist/chunk-OCSU2S6W.js +168 -0
  21. package/dist/{chunk-QQFBFV4V.js → chunk-PGDBUUGR.js} +60 -19
  22. package/dist/{chunk-QITOPASZ.js → chunk-PSQHUZ7X.js} +1 -1
  23. package/dist/{chunk-C6KPAFCC.js → chunk-PU7LXOQ3.js} +23 -1
  24. package/dist/{chunk-JOY533UH.js → chunk-TW65F5EU.js} +1 -1
  25. package/dist/{chunk-ZX5623ER.js → chunk-VMH2YS2I.js} +1 -1
  26. package/dist/{chunk-XND2DWTZ.js → chunk-VPQ44XKE.js} +2 -2
  27. package/dist/{chunk-CSATDXZC.js → chunk-Y7T6IMM3.js} +1 -1
  28. package/dist/cli/index.js +755 -379
  29. package/dist/{client-T5MTY3CS.js → client-HRYRJKSA.js} +3 -3
  30. package/dist/{conduct-WU3VEXB6.js → conduct-JNYJCDHQ.js} +14 -13
  31. package/dist/conduct-KJUD2RTB.js +22 -0
  32. package/dist/{conductor-mode-ZMTFZGJP.js → conductor-mode-2VVFMKVE.js} +313 -14
  33. package/dist/conductor-mode-VGUU54QI.js +276 -0
  34. package/dist/execute-I4PKSNJM.js +12 -0
  35. package/dist/execute-MOXSSA3Q.js +15 -0
  36. package/dist/index.d.ts +795 -2
  37. package/dist/index.js +861 -111
  38. package/dist/{process-guard-CC7CNRQJ.js → process-guard-QCCBGILS.js} +1 -1
  39. package/dist/publish-capability-TS6CNR5G.js +12 -0
  40. package/dist/reliability-metrics-QG7WC5QK.js +18 -0
  41. package/dist/{request-VOXBFUOG.js → request-E7TA7COA.js} +19 -18
  42. package/dist/{serve-skill-IH7UAJNR.js → serve-skill-HIOWYKRU.js} +13 -11
  43. package/dist/{server-JVQW2TID.js → server-I63CXFX3.js} +17 -16
  44. package/dist/{service-coordinator-EYRDTHL5.js → service-coordinator-XBNT3SMU.js} +369 -260
  45. package/dist/skill-config-FETXPNVP.js +22 -0
  46. package/dist/skills/agentbnb/bootstrap.js +430 -84
  47. package/dist/websocket-client-5MH6QRJK.js +7 -0
  48. package/dist/{websocket-client-WRN3HO73.js → websocket-client-PFGVTXNE.js} +1 -1
  49. package/openclaw.plugin.json +2 -2
  50. package/package.json +2 -1
  51. package/skills/agentbnb/SKILL.md +35 -0
  52. package/skills/agentbnb/bootstrap.ts +126 -8
  53. package/skills/agentbnb/install.sh +49 -9
  54. package/dist/chunk-EANI2N2V.js +0 -309
  55. package/dist/chunk-EPIWHNB2.js +0 -946
  56. package/dist/conduct-6LKIJJKQ.js +0 -21
  57. package/dist/conductor-mode-Q4IIDY5E.js +0 -123
  58. package/dist/execute-4D4ITQCL.js +0 -10
  59. package/dist/execute-T7Y6RKSW.js +0 -13
  60. package/dist/websocket-client-6IIDGXKB.js +0 -7
package/dist/index.js CHANGED
@@ -48,7 +48,9 @@ var CapabilityCardSchema = z.object({
48
48
  tags: z.array(z.string()).optional()
49
49
  }).optional(),
50
50
  created_at: z.string().datetime().optional(),
51
- updated_at: z.string().datetime().optional()
51
+ updated_at: z.string().datetime().optional(),
52
+ /** Exact-match capability type key for network routing (e.g. 'task_decomposition'). Optional — backward-compatible. */
53
+ capability_type: z.string().optional()
52
54
  });
53
55
  var SkillSchema = z.object({
54
56
  /** Stable skill identifier, e.g. 'tts-elevenlabs'. Used for gateway routing and idle tracking. */
@@ -58,6 +60,25 @@ var SkillSchema = z.object({
58
60
  level: z.union([z.literal(1), z.literal(2), z.literal(3)]),
59
61
  /** Optional grouping category, e.g. 'tts' | 'video_gen' | 'code_review'. */
60
62
  category: z.string().optional(),
63
+ /** Exact-match capability type key for network routing (e.g. 'task_decomposition'). Optional — per-skill routing hint. */
64
+ capability_type: z.string().optional(),
65
+ /**
66
+ * Multi-value capability routing tags — what this skill IS / offers to the outside.
67
+ * Used by Conductor for precise skill-level matching.
68
+ * Example: ["audio_generation", "audio_editing", "content_production"]
69
+ */
70
+ capability_types: z.array(z.string()).optional(),
71
+ /**
72
+ * Capabilities this skill internally depends on when executing.
73
+ * Used by Conductor for decomposition planning and cost estimation.
74
+ * Example: ["tts", "sound_effects", "audio_mixing"]
75
+ */
76
+ requires_capabilities: z.array(z.string()).optional(),
77
+ /**
78
+ * Publishing visibility. 'private' skills are excluded from published CapabilityCards.
79
+ * Defaults to 'public' when omitted.
80
+ */
81
+ visibility: z.enum(["public", "private"]).optional(),
61
82
  inputs: z.array(IOSchemaSchema),
62
83
  outputs: z.array(IOSchemaSchema),
63
84
  pricing: z.object({
@@ -144,6 +165,8 @@ var CapabilityCardV2Schema = z.object({
144
165
  _internal: z.record(z.unknown()).optional(),
145
166
  /** Public gateway URL where this agent accepts requests. Populated on remote publish. */
146
167
  gateway_url: z.string().url().optional(),
168
+ /** Exact-match capability type key for network routing (e.g. 'task_decomposition'). Optional — backward-compatible. */
169
+ capability_type: z.string().optional(),
147
170
  created_at: z.string().datetime().optional(),
148
171
  updated_at: z.string().datetime().optional()
149
172
  });
@@ -191,11 +214,27 @@ function createRequestLogTable(db) {
191
214
  db.exec("ALTER TABLE request_log ADD COLUMN tier_invoked INTEGER");
192
215
  } catch {
193
216
  }
217
+ try {
218
+ db.exec("ALTER TABLE request_log ADD COLUMN failure_reason TEXT");
219
+ } catch {
220
+ }
221
+ try {
222
+ db.exec("ALTER TABLE request_log ADD COLUMN team_id TEXT");
223
+ } catch {
224
+ }
225
+ try {
226
+ db.exec("ALTER TABLE request_log ADD COLUMN role TEXT");
227
+ } catch {
228
+ }
229
+ try {
230
+ db.exec("ALTER TABLE request_log ADD COLUMN capability_type TEXT");
231
+ } catch {
232
+ }
194
233
  }
195
234
  function insertRequestLog(db, entry) {
196
235
  const stmt = db.prepare(`
197
- INSERT INTO request_log (id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked)
198
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
236
+ INSERT INTO request_log (id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked, failure_reason, team_id, role, capability_type)
237
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
199
238
  `);
200
239
  stmt.run(
201
240
  entry.id,
@@ -208,7 +247,11 @@ function insertRequestLog(db, entry) {
208
247
  entry.created_at,
209
248
  entry.skill_id ?? null,
210
249
  entry.action_type ?? null,
211
- entry.tier_invoked ?? null
250
+ entry.tier_invoked ?? null,
251
+ entry.failure_reason ?? null,
252
+ entry.team_id ?? null,
253
+ entry.role ?? null,
254
+ entry.capability_type ?? null
212
255
  );
213
256
  }
214
257
 
@@ -322,6 +365,17 @@ var V2_FTS_TRIGGERS = `
322
365
  FROM json_each(json_extract(new.data, '$.metadata.tags'))),
323
366
  ''
324
367
  )
368
+ || ' ' || COALESCE(
369
+ (SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
370
+ FROM json_each(json_extract(new.data, '$.skills')) AS skill),
371
+ ''
372
+ )
373
+ || ' ' || COALESCE(
374
+ (SELECT group_concat(cap_type.value, ' ')
375
+ FROM json_each(json_extract(new.data, '$.skills')) AS skill,
376
+ json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
377
+ ''
378
+ )
325
379
  );
326
380
  END;
327
381
 
@@ -351,6 +405,17 @@ var V2_FTS_TRIGGERS = `
351
405
  FROM json_each(json_extract(old.data, '$.metadata.tags'))),
352
406
  ''
353
407
  )
408
+ || ' ' || COALESCE(
409
+ (SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
410
+ FROM json_each(json_extract(old.data, '$.skills')) AS skill),
411
+ ''
412
+ )
413
+ || ' ' || COALESCE(
414
+ (SELECT group_concat(cap_type.value, ' ')
415
+ FROM json_each(json_extract(old.data, '$.skills')) AS skill,
416
+ json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
417
+ ''
418
+ )
354
419
  );
355
420
  INSERT INTO cards_fts(rowid, id, owner, name, description, tags)
356
421
  VALUES (
@@ -376,6 +441,17 @@ var V2_FTS_TRIGGERS = `
376
441
  FROM json_each(json_extract(new.data, '$.metadata.tags'))),
377
442
  ''
378
443
  )
444
+ || ' ' || COALESCE(
445
+ (SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
446
+ FROM json_each(json_extract(new.data, '$.skills')) AS skill),
447
+ ''
448
+ )
449
+ || ' ' || COALESCE(
450
+ (SELECT group_concat(cap_type.value, ' ')
451
+ FROM json_each(json_extract(new.data, '$.skills')) AS skill,
452
+ json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
453
+ ''
454
+ )
379
455
  );
380
456
  END;
381
457
 
@@ -405,6 +481,17 @@ var V2_FTS_TRIGGERS = `
405
481
  FROM json_each(json_extract(old.data, '$.metadata.tags'))),
406
482
  ''
407
483
  )
484
+ || ' ' || COALESCE(
485
+ (SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
486
+ FROM json_each(json_extract(old.data, '$.skills')) AS skill),
487
+ ''
488
+ )
489
+ || ' ' || COALESCE(
490
+ (SELECT group_concat(cap_type.value, ' ')
491
+ FROM json_each(json_extract(old.data, '$.skills')) AS skill,
492
+ json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
493
+ ''
494
+ )
408
495
  );
409
496
  END;
410
497
  `;
@@ -509,10 +596,17 @@ function migrateV1toV2(db) {
509
596
  if (skills.length > 0) {
510
597
  name = skills.map((s) => String(s["name"] ?? "")).join(" ");
511
598
  description = skills.map((s) => String(s["description"] ?? "")).join(" ");
512
- tags = skills.flatMap((s) => {
513
- const meta = s["metadata"];
514
- return meta?.["tags"] ?? [];
515
- }).join(" ");
599
+ tags = [
600
+ // tags from metadata.tags[]
601
+ ...skills.flatMap((s) => {
602
+ const meta = s["metadata"];
603
+ return meta?.["tags"] ?? [];
604
+ }),
605
+ // capability_type (singular)
606
+ ...skills.map((s) => s["capability_type"]).filter((v) => typeof v === "string" && v.length > 0),
607
+ // capability_types[] (plural)
608
+ ...skills.flatMap((s) => s["capability_types"] ?? [])
609
+ ].join(" ");
516
610
  } else {
517
611
  name = String(data["name"] ?? "");
518
612
  description = String(data["description"] ?? "");
@@ -589,6 +683,24 @@ function listCards(db, owner) {
589
683
  }
590
684
  return rows.map((row) => JSON.parse(row.data));
591
685
  }
686
+ function getCardsByCapabilityType(db, capabilityType) {
687
+ const rows = db.prepare(
688
+ "SELECT data FROM capability_cards WHERE json_extract(data, '$.capability_type') = ?"
689
+ ).all(capabilityType);
690
+ return rows.map((row) => JSON.parse(row.data));
691
+ }
692
+ function getCardsBySkillCapability(db, capabilityType) {
693
+ const rows = db.prepare("SELECT data FROM capability_cards").all();
694
+ return rows.map((row) => JSON.parse(row.data)).filter((card) => {
695
+ const skills = card["skills"];
696
+ if (!skills) return false;
697
+ return skills.some((skill) => {
698
+ if (skill["capability_type"] === capabilityType) return true;
699
+ const types = skill["capability_types"];
700
+ return Array.isArray(types) && types.includes(capabilityType);
701
+ });
702
+ });
703
+ }
592
704
 
593
705
  // src/feedback/reputation.ts
594
706
  var QUALITY_SCORES = {
@@ -692,6 +804,59 @@ function applyReputationFilter(db, cards, minReputation) {
692
804
  // src/credit/ledger.ts
693
805
  import Database2 from "better-sqlite3";
694
806
  import { randomUUID as randomUUID3 } from "crypto";
807
+
808
+ // src/credit/reliability-metrics.ts
809
+ var RELIABILITY_METRICS_SCHEMA = `
810
+ CREATE TABLE IF NOT EXISTS provider_reliability_metrics (
811
+ owner TEXT PRIMARY KEY,
812
+ current_streak INTEGER NOT NULL DEFAULT 0,
813
+ longest_streak INTEGER NOT NULL DEFAULT 0,
814
+ total_hires INTEGER NOT NULL DEFAULT 0,
815
+ repeat_hires INTEGER NOT NULL DEFAULT 0,
816
+ feedback_count INTEGER NOT NULL DEFAULT 0,
817
+ feedback_sum REAL NOT NULL DEFAULT 0,
818
+ availability_checks INTEGER NOT NULL DEFAULT 0,
819
+ availability_hits INTEGER NOT NULL DEFAULT 0,
820
+ cycle_start TEXT NOT NULL,
821
+ updated_at TEXT NOT NULL
822
+ );
823
+ `;
824
+ function ensureReliabilityTable(db) {
825
+ db.exec(RELIABILITY_METRICS_SCHEMA);
826
+ }
827
+ function ensureRow(db, owner) {
828
+ const now = (/* @__PURE__ */ new Date()).toISOString();
829
+ db.prepare(
830
+ `INSERT OR IGNORE INTO provider_reliability_metrics
831
+ (owner, current_streak, longest_streak, total_hires, repeat_hires,
832
+ feedback_count, feedback_sum, availability_checks, availability_hits,
833
+ cycle_start, updated_at)
834
+ VALUES (?, 0, 0, 0, 0, 0, 0, 0, 0, ?, ?)`
835
+ ).run(owner, now, now);
836
+ }
837
+ function recordSuccessfulHire(db, providerOwner, consumerOwner) {
838
+ const now = (/* @__PURE__ */ new Date()).toISOString();
839
+ ensureRow(db, providerOwner);
840
+ const isRepeat = db.prepare(
841
+ `SELECT COUNT(*) as cnt FROM credit_transactions
842
+ WHERE owner = ? AND reason = 'settlement'
843
+ AND reference_id IN (
844
+ SELECT id FROM credit_escrow WHERE owner = ?
845
+ )`
846
+ ).get(providerOwner, consumerOwner);
847
+ const repeatIncrement = (isRepeat?.cnt ?? 0) > 0 ? 1 : 0;
848
+ db.prepare(
849
+ `UPDATE provider_reliability_metrics
850
+ SET current_streak = current_streak + 1,
851
+ longest_streak = MAX(longest_streak, current_streak + 1),
852
+ total_hires = total_hires + 1,
853
+ repeat_hires = repeat_hires + ?,
854
+ updated_at = ?
855
+ WHERE owner = ?`
856
+ ).run(repeatIncrement, now, providerOwner);
857
+ }
858
+
859
+ // src/credit/ledger.ts
695
860
  var CREDIT_SCHEMA = `
696
861
  CREATE TABLE IF NOT EXISTS credit_balances (
697
862
  owner TEXT PRIMARY KEY,
@@ -718,6 +883,22 @@ var CREDIT_SCHEMA = `
718
883
  settled_at TEXT
719
884
  );
720
885
 
886
+ CREATE TABLE IF NOT EXISTS provider_registry (
887
+ owner TEXT PRIMARY KEY,
888
+ provider_number INTEGER NOT NULL,
889
+ registered_at TEXT NOT NULL
890
+ );
891
+
892
+ CREATE TABLE IF NOT EXISTS demand_vouchers (
893
+ id TEXT PRIMARY KEY,
894
+ owner TEXT NOT NULL,
895
+ amount INTEGER NOT NULL,
896
+ remaining INTEGER NOT NULL,
897
+ created_at TEXT NOT NULL,
898
+ expires_at TEXT NOT NULL,
899
+ is_active INTEGER NOT NULL DEFAULT 1
900
+ );
901
+
721
902
  CREATE INDEX IF NOT EXISTS idx_transactions_owner ON credit_transactions(owner, created_at);
722
903
  CREATE INDEX IF NOT EXISTS idx_escrow_owner ON credit_escrow(owner);
723
904
  `;
@@ -726,12 +907,46 @@ function openCreditDb(path = ":memory:") {
726
907
  db.pragma("journal_mode = WAL");
727
908
  db.pragma("foreign_keys = ON");
728
909
  db.exec(CREDIT_SCHEMA);
910
+ try {
911
+ db.exec("ALTER TABLE credit_escrow ADD COLUMN funding_source TEXT NOT NULL DEFAULT 'balance'");
912
+ } catch {
913
+ }
914
+ ensureReliabilityTable(db);
729
915
  return db;
730
916
  }
731
917
  function getBalance(db, owner) {
732
918
  const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
733
919
  return row?.balance ?? 0;
734
920
  }
921
+ function registerProvider(db, owner) {
922
+ const now = (/* @__PURE__ */ new Date()).toISOString();
923
+ const maxRow = db.prepare("SELECT MAX(provider_number) as maxNum FROM provider_registry").get();
924
+ const nextNum = (maxRow?.maxNum ?? 0) + 1;
925
+ db.prepare("INSERT OR IGNORE INTO provider_registry (owner, provider_number, registered_at) VALUES (?, ?, ?)").run(owner, nextNum, now);
926
+ const row = db.prepare("SELECT provider_number FROM provider_registry WHERE owner = ?").get(owner);
927
+ return row.provider_number;
928
+ }
929
+ function getProviderNumber(db, owner) {
930
+ const row = db.prepare("SELECT provider_number FROM provider_registry WHERE owner = ?").get(owner);
931
+ return row?.provider_number ?? null;
932
+ }
933
+ function getProviderBonus(providerNumber) {
934
+ if (providerNumber <= 50) return 2;
935
+ if (providerNumber <= 200) return 1.5;
936
+ return 1;
937
+ }
938
+ function getActiveVoucher(db, owner) {
939
+ const now = (/* @__PURE__ */ new Date()).toISOString();
940
+ const row = db.prepare(
941
+ "SELECT id, remaining, expires_at FROM demand_vouchers WHERE owner = ? AND is_active = 1 AND remaining > 0 AND expires_at > ? ORDER BY created_at ASC LIMIT 1"
942
+ ).get(owner, now);
943
+ return row ?? null;
944
+ }
945
+ function consumeVoucher(db, voucherId, amount) {
946
+ db.prepare(
947
+ "UPDATE demand_vouchers SET remaining = remaining - ? WHERE id = ? AND remaining >= ?"
948
+ ).run(amount, voucherId, amount);
949
+ }
735
950
  function recordEarning(db, owner, amount, _cardId, receiptNonce) {
736
951
  const now = (/* @__PURE__ */ new Date()).toISOString();
737
952
  db.transaction(() => {
@@ -753,29 +968,42 @@ function recordEarning(db, owner, amount, _cardId, receiptNonce) {
753
968
 
754
969
  // src/gateway/server.ts
755
970
  import Fastify from "fastify";
971
+ import { randomUUID as randomUUID6 } from "crypto";
756
972
 
757
973
  // src/gateway/execute.ts
758
974
  import { randomUUID as randomUUID5 } from "crypto";
759
975
 
760
976
  // src/credit/escrow.ts
761
977
  import { randomUUID as randomUUID4 } from "crypto";
978
+ var NETWORK_FEE_RATE = 0.05;
762
979
  function holdEscrow(db, owner, amount, cardId) {
763
980
  const escrowId = randomUUID4();
764
981
  const now = (/* @__PURE__ */ new Date()).toISOString();
765
982
  const hold = db.transaction(() => {
766
- const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
767
- if (!row || row.balance < amount) {
768
- throw new AgentBnBError("Insufficient credits", "INSUFFICIENT_CREDITS");
983
+ const voucher = getActiveVoucher(db, owner);
984
+ if (voucher && voucher.remaining >= amount) {
985
+ consumeVoucher(db, voucher.id, amount);
986
+ db.prepare(
987
+ "INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?)"
988
+ ).run(escrowId, owner, amount, cardId, "held", now, "voucher");
989
+ db.prepare(
990
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
991
+ ).run(randomUUID4(), owner, -amount, "voucher_hold", escrowId, now);
992
+ } else {
993
+ const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
994
+ if (!row || row.balance < amount) {
995
+ throw new AgentBnBError("Insufficient credits", "INSUFFICIENT_CREDITS");
996
+ }
997
+ db.prepare(
998
+ "UPDATE credit_balances SET balance = balance - ?, updated_at = ? WHERE owner = ? AND balance >= ?"
999
+ ).run(amount, now, owner, amount);
1000
+ db.prepare(
1001
+ "INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?)"
1002
+ ).run(escrowId, owner, amount, cardId, "held", now, "balance");
1003
+ db.prepare(
1004
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
1005
+ ).run(randomUUID4(), owner, -amount, "escrow_hold", escrowId, now);
769
1006
  }
770
- db.prepare(
771
- "UPDATE credit_balances SET balance = balance - ?, updated_at = ? WHERE owner = ? AND balance >= ?"
772
- ).run(amount, now, owner, amount);
773
- db.prepare(
774
- "INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at) VALUES (?, ?, ?, ?, ?, ?)"
775
- ).run(escrowId, owner, amount, cardId, "held", now);
776
- db.prepare(
777
- "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
778
- ).run(randomUUID4(), owner, -amount, "escrow_hold", escrowId, now);
779
1007
  });
780
1008
  hold();
781
1009
  return escrowId;
@@ -783,7 +1011,7 @@ function holdEscrow(db, owner, amount, cardId) {
783
1011
  function settleEscrow(db, escrowId, recipientOwner) {
784
1012
  const now = (/* @__PURE__ */ new Date()).toISOString();
785
1013
  const settle = db.transaction(() => {
786
- const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
1014
+ const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
787
1015
  if (!escrow) {
788
1016
  throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
789
1017
  }
@@ -793,25 +1021,61 @@ function settleEscrow(db, escrowId, recipientOwner) {
793
1021
  "ESCROW_ALREADY_SETTLED"
794
1022
  );
795
1023
  }
1024
+ const feeAmount = Math.floor(escrow.amount * NETWORK_FEE_RATE);
1025
+ const providerAmount = escrow.amount - feeAmount;
796
1026
  db.prepare(
797
1027
  "INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
798
1028
  ).run(recipientOwner, now);
799
1029
  db.prepare(
800
1030
  "UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
801
- ).run(escrow.amount, now, recipientOwner);
1031
+ ).run(providerAmount, now, recipientOwner);
1032
+ if (feeAmount > 0) {
1033
+ db.prepare(
1034
+ "INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
1035
+ ).run("platform_treasury", now);
1036
+ db.prepare(
1037
+ "UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
1038
+ ).run(feeAmount, now, "platform_treasury");
1039
+ db.prepare(
1040
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
1041
+ ).run(randomUUID4(), "platform_treasury", feeAmount, "network_fee", escrowId, now);
1042
+ }
802
1043
  db.prepare(
803
1044
  "UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
804
1045
  ).run("settled", now, escrowId);
805
1046
  db.prepare(
806
1047
  "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
807
- ).run(randomUUID4(), recipientOwner, escrow.amount, "settlement", escrowId, now);
1048
+ ).run(randomUUID4(), recipientOwner, providerAmount, "settlement", escrowId, now);
1049
+ let providerNum = getProviderNumber(db, recipientOwner);
1050
+ if (providerNum === null) {
1051
+ providerNum = registerProvider(db, recipientOwner);
1052
+ }
1053
+ const bonus = getProviderBonus(providerNum);
1054
+ if (bonus > 1) {
1055
+ const bonusAmount = Math.floor(providerAmount * (bonus - 1));
1056
+ if (bonusAmount > 0) {
1057
+ db.prepare(
1058
+ "INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
1059
+ ).run("platform_treasury", now);
1060
+ db.prepare(
1061
+ "UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
1062
+ ).run(bonusAmount, now, recipientOwner);
1063
+ db.prepare(
1064
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
1065
+ ).run(randomUUID4(), recipientOwner, bonusAmount, "provider_bonus", escrowId, now);
1066
+ }
1067
+ }
1068
+ try {
1069
+ recordSuccessfulHire(db, recipientOwner, escrow.owner);
1070
+ } catch {
1071
+ }
808
1072
  });
809
1073
  settle();
810
1074
  }
811
1075
  function releaseEscrow(db, escrowId) {
812
1076
  const now = (/* @__PURE__ */ new Date()).toISOString();
813
1077
  const release = db.transaction(() => {
814
- const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
1078
+ const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
815
1079
  if (!escrow) {
816
1080
  throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
817
1081
  }
@@ -836,7 +1100,7 @@ function releaseEscrow(db, escrowId) {
836
1100
  function confirmEscrowDebit(db, escrowId) {
837
1101
  const now = (/* @__PURE__ */ new Date()).toISOString();
838
1102
  const confirm = db.transaction(() => {
839
- const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
1103
+ const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
840
1104
  if (!escrow) {
841
1105
  throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
842
1106
  }
@@ -910,10 +1174,12 @@ function verifyEscrowReceipt(data, signature, publicKey) {
910
1174
 
911
1175
  // src/credit/settlement.ts
912
1176
  function settleProviderEarning(providerDb, providerOwner, receipt) {
1177
+ const feeAmount = Math.floor(receipt.amount * NETWORK_FEE_RATE);
1178
+ const providerAmount = receipt.amount - feeAmount;
913
1179
  recordEarning(
914
1180
  providerDb,
915
1181
  providerOwner,
916
- receipt.amount,
1182
+ providerAmount,
917
1183
  receipt.card_id,
918
1184
  receipt.nonce
919
1185
  );
@@ -926,7 +1192,50 @@ function releaseRequesterEscrow(requesterDb, escrowId) {
926
1192
  releaseEscrow(requesterDb, escrowId);
927
1193
  }
928
1194
 
1195
+ // src/cli/config.ts
1196
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync2 } from "fs";
1197
+ import { homedir } from "os";
1198
+ import { join as join2 } from "path";
1199
+ function getConfigDir() {
1200
+ return process.env["AGENTBNB_DIR"] ?? join2(homedir(), ".agentbnb");
1201
+ }
1202
+ function getConfigPath() {
1203
+ return join2(getConfigDir(), "config.json");
1204
+ }
1205
+ function loadConfig() {
1206
+ const configPath = getConfigPath();
1207
+ if (!existsSync2(configPath)) return null;
1208
+ try {
1209
+ const raw = readFileSync2(configPath, "utf-8");
1210
+ return JSON.parse(raw);
1211
+ } catch {
1212
+ return null;
1213
+ }
1214
+ }
1215
+
929
1216
  // src/gateway/execute.ts
1217
+ async function notifyTelegramSkillExecuted(opts) {
1218
+ const cfg = loadConfig();
1219
+ if (!cfg?.telegram_notifications) return;
1220
+ const token = cfg.telegram_bot_token ?? process.env["TELEGRAM_BOT_TOKEN"];
1221
+ const chatId = cfg.telegram_chat_id ?? process.env["TELEGRAM_CHAT_ID"];
1222
+ if (!token || !chatId) return;
1223
+ const balance = getBalance(opts.creditDb, opts.owner);
1224
+ const skillLabel = opts.skillId ? `${opts.skillName} (${opts.skillId})` : opts.skillName;
1225
+ const text = [
1226
+ "[AgentBnB] Skill executed",
1227
+ `Skill: ${skillLabel}`,
1228
+ `Requester: ${opts.requester}`,
1229
+ `Earned: +${opts.creditsEarned} credits`,
1230
+ `Balance: ${balance} credits`,
1231
+ `Latency: ${opts.latencyMs}ms`
1232
+ ].join("\n");
1233
+ await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
1234
+ method: "POST",
1235
+ headers: { "Content-Type": "application/json" },
1236
+ body: JSON.stringify({ chat_id: chatId, text })
1237
+ });
1238
+ }
930
1239
  async function executeCapabilityRequest(opts) {
931
1240
  const {
932
1241
  registryDb,
@@ -958,7 +1267,8 @@ async function executeCapabilityRequest(opts) {
958
1267
  status: "failure",
959
1268
  latency_ms: 0,
960
1269
  credits_charged: 0,
961
- created_at: (/* @__PURE__ */ new Date()).toISOString()
1270
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
1271
+ failure_reason: "auth_error"
962
1272
  });
963
1273
  } catch {
964
1274
  }
@@ -1013,9 +1323,11 @@ async function executeCapabilityRequest(opts) {
1013
1323
  }
1014
1324
  const startMs = Date.now();
1015
1325
  const receiptData = isRemoteEscrow ? { receipt_released: true } : void 0;
1016
- const handleFailure = (status, latencyMs, message) => {
1326
+ const handleFailure = (status, latencyMs, message, failureReason = "bad_execution") => {
1017
1327
  if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
1018
- updateReputation(registryDb, cardId, false, latencyMs);
1328
+ if (failureReason === "bad_execution" || failureReason === "auth_error") {
1329
+ updateReputation(registryDb, cardId, false, latencyMs);
1330
+ }
1019
1331
  try {
1020
1332
  insertRequestLog(registryDb, {
1021
1333
  id: randomUUID5(),
@@ -1026,7 +1338,8 @@ async function executeCapabilityRequest(opts) {
1026
1338
  status,
1027
1339
  latency_ms: latencyMs,
1028
1340
  credits_charged: 0,
1029
- created_at: (/* @__PURE__ */ new Date()).toISOString()
1341
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
1342
+ failure_reason: failureReason
1030
1343
  });
1031
1344
  } catch {
1032
1345
  }
@@ -1056,6 +1369,16 @@ async function executeCapabilityRequest(opts) {
1056
1369
  });
1057
1370
  } catch {
1058
1371
  }
1372
+ notifyTelegramSkillExecuted({
1373
+ creditDb,
1374
+ owner: card.owner,
1375
+ skillName: cardName,
1376
+ skillId: resolvedSkillId ?? null,
1377
+ requester,
1378
+ creditsEarned: creditsNeeded,
1379
+ latencyMs
1380
+ }).catch(() => {
1381
+ });
1059
1382
  const successResult = isRemoteEscrow ? {
1060
1383
  ...typeof result === "object" && result !== null ? result : { data: result },
1061
1384
  receipt_settled: true,
@@ -1073,23 +1396,24 @@ async function executeCapabilityRequest(opts) {
1073
1396
  return handleFailure(
1074
1397
  "failure",
1075
1398
  Date.now() - startMs,
1076
- "No skill_id specified and no skills registered on this provider."
1399
+ "No skill_id specified and no skills registered on this provider.",
1400
+ "not_found"
1077
1401
  );
1078
1402
  }
1079
1403
  }
1080
1404
  try {
1081
1405
  const execResult = await skillExecutor.execute(targetSkillId, params, onProgress);
1082
1406
  if (!execResult.success) {
1083
- return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed");
1407
+ return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed", "bad_execution");
1084
1408
  }
1085
1409
  return handleSuccess(execResult.result, execResult.latency_ms);
1086
1410
  } catch (err) {
1087
1411
  const message = err instanceof Error ? err.message : "Execution error";
1088
- return handleFailure("failure", Date.now() - startMs, message);
1412
+ return handleFailure("failure", Date.now() - startMs, message, "bad_execution");
1089
1413
  }
1090
1414
  }
1091
1415
  if (!handlerUrl) {
1092
- return handleFailure("failure", Date.now() - startMs, "No skill executor or handler URL configured");
1416
+ return handleFailure("failure", Date.now() - startMs, "No skill executor or handler URL configured", "bad_execution");
1093
1417
  }
1094
1418
  const controller = new AbortController();
1095
1419
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -1102,7 +1426,7 @@ async function executeCapabilityRequest(opts) {
1102
1426
  });
1103
1427
  clearTimeout(timer);
1104
1428
  if (!response.ok) {
1105
- return handleFailure("failure", Date.now() - startMs, `Handler returned ${response.status}`);
1429
+ return handleFailure("failure", Date.now() - startMs, `Handler returned ${response.status}`, "bad_execution");
1106
1430
  }
1107
1431
  const result = await response.json();
1108
1432
  return handleSuccess(result, Date.now() - startMs);
@@ -1112,7 +1436,8 @@ async function executeCapabilityRequest(opts) {
1112
1436
  return handleFailure(
1113
1437
  isTimeout ? "timeout" : "failure",
1114
1438
  Date.now() - startMs,
1115
- isTimeout ? "Execution timeout" : "Handler error"
1439
+ isTimeout ? "Execution timeout" : "Handler error",
1440
+ isTimeout ? "timeout" : "bad_execution"
1116
1441
  );
1117
1442
  }
1118
1443
  }
@@ -1131,6 +1456,8 @@ function createGatewayServer(opts) {
1131
1456
  } = opts;
1132
1457
  const fastify = Fastify({ logger: !silent });
1133
1458
  const tokenSet = new Set(tokens);
1459
+ const inFlight = /* @__PURE__ */ new Map();
1460
+ const OVERLOAD_RETRY_MS = 5e3;
1134
1461
  fastify.addHook("onRequest", async (request) => {
1135
1462
  if (request.method === "GET" && request.url === "/health") return;
1136
1463
  const auth = request.headers.authorization;
@@ -1196,18 +1523,63 @@ function createGatewayServer(opts) {
1196
1523
  }
1197
1524
  const requester = params.requester ?? "unknown";
1198
1525
  const receipt = params.escrow_receipt;
1199
- const result = await executeCapabilityRequest({
1200
- registryDb,
1201
- creditDb,
1202
- cardId,
1203
- skillId,
1204
- params,
1205
- requester,
1206
- escrowReceipt: receipt,
1207
- skillExecutor,
1208
- handlerUrl,
1209
- timeoutMs
1210
- });
1526
+ if (skillExecutor && skillId && typeof skillExecutor.getSkillConfig === "function") {
1527
+ const skillConfig = skillExecutor.getSkillConfig(skillId);
1528
+ const maxConcurrent = skillConfig?.capacity?.max_concurrent;
1529
+ if (maxConcurrent !== void 0) {
1530
+ const current = inFlight.get(skillId) ?? 0;
1531
+ if (current >= maxConcurrent) {
1532
+ try {
1533
+ insertRequestLog(registryDb, {
1534
+ id: randomUUID6(),
1535
+ card_id: cardId,
1536
+ card_name: "<overload>",
1537
+ requester,
1538
+ status: "failure",
1539
+ latency_ms: 0,
1540
+ credits_charged: 0,
1541
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
1542
+ skill_id: skillId,
1543
+ failure_reason: "overload"
1544
+ });
1545
+ } catch {
1546
+ }
1547
+ return reply.status(200).send({
1548
+ jsonrpc: "2.0",
1549
+ id,
1550
+ error: {
1551
+ code: -32e3,
1552
+ message: "overload",
1553
+ data: { error: "overload", retry_after_ms: OVERLOAD_RETRY_MS }
1554
+ }
1555
+ });
1556
+ }
1557
+ }
1558
+ }
1559
+ const trackKey = skillId ?? cardId;
1560
+ inFlight.set(trackKey, (inFlight.get(trackKey) ?? 0) + 1);
1561
+ let result;
1562
+ try {
1563
+ result = await executeCapabilityRequest({
1564
+ registryDb,
1565
+ creditDb,
1566
+ cardId,
1567
+ skillId,
1568
+ params,
1569
+ requester,
1570
+ escrowReceipt: receipt,
1571
+ skillExecutor,
1572
+ handlerUrl,
1573
+ timeoutMs
1574
+ });
1575
+ } finally {
1576
+ const next = (inFlight.get(trackKey) ?? 1) - 1;
1577
+ if (next <= 0) {
1578
+ inFlight.delete(trackKey);
1579
+ } else {
1580
+ inFlight.set(trackKey, next);
1581
+ }
1582
+ }
1211
1583
  if (result.success) {
1212
1584
  return reply.send({ jsonrpc: "2.0", id, result: result.result });
1213
1585
  } else {
@@ -1221,13 +1593,16 @@ function createGatewayServer(opts) {
1221
1593
  var SkillExecutor = class {
1222
1594
  skillMap;
1223
1595
  modeMap;
1596
+ concurrencyGuard;
1224
1597
  /**
1225
1598
  * @param configs - Parsed SkillConfig array (from parseSkillsFile).
1226
1599
  * @param modes - Map from skill type string to its executor implementation.
1600
+ * @param concurrencyGuard - Optional ConcurrencyGuard to enforce max_concurrent limits.
1227
1601
  */
1228
- constructor(configs, modes) {
1602
+ constructor(configs, modes, concurrencyGuard) {
1229
1603
  this.skillMap = new Map(configs.map((c) => [c.id, c]));
1230
1604
  this.modeMap = modes;
1605
+ this.concurrencyGuard = concurrencyGuard;
1231
1606
  }
1232
1607
  /**
1233
1608
  * Execute a skill by ID with the given input parameters.
@@ -1260,6 +1635,18 @@ var SkillExecutor = class {
1260
1635
  latency_ms: Date.now() - startTime
1261
1636
  };
1262
1637
  }
1638
+ const maxConcurrent = config.capacity?.max_concurrent ?? Infinity;
1639
+ if (this.concurrencyGuard && maxConcurrent !== Infinity) {
1640
+ if (!this.concurrencyGuard.canAccept(skillId, maxConcurrent)) {
1641
+ return {
1642
+ success: false,
1643
+ error: `Skill ${skillId} at capacity (${maxConcurrent} concurrent max)`,
1644
+ latency_ms: Date.now() - startTime,
1645
+ failure_reason: "overload"
1646
+ };
1647
+ }
1648
+ this.concurrencyGuard.acquire(skillId);
1649
+ }
1263
1650
  try {
1264
1651
  const modeResult = await mode.execute(config, params, onProgress);
1265
1652
  return {
@@ -1273,6 +1660,10 @@ var SkillExecutor = class {
1273
1660
  error: message,
1274
1661
  latency_ms: Date.now() - startTime
1275
1662
  };
1663
+ } finally {
1664
+ if (this.concurrencyGuard && maxConcurrent !== Infinity) {
1665
+ this.concurrencyGuard.release(skillId);
1666
+ }
1276
1667
  }
1277
1668
  }
1278
1669
  /**
@@ -1293,13 +1684,16 @@ var SkillExecutor = class {
1293
1684
  return this.skillMap.get(skillId);
1294
1685
  }
1295
1686
  };
1296
- function createSkillExecutor(configs, modes) {
1297
- return new SkillExecutor(configs, modes);
1687
+ function createSkillExecutor(configs, modes, concurrencyGuard) {
1688
+ return new SkillExecutor(configs, modes, concurrencyGuard);
1298
1689
  }
1299
1690
 
1300
1691
  // src/skills/skill-config.ts
1301
1692
  import { z as z2 } from "zod";
1302
1693
  import yaml from "js-yaml";
1694
+ var CapacitySchema = z2.object({
1695
+ max_concurrent: z2.number().positive().int().optional()
1696
+ }).optional();
1303
1697
  var PricingSchema = z2.object({
1304
1698
  credits_per_call: z2.number().nonnegative(),
1305
1699
  credits_per_minute: z2.number().nonnegative().optional(),
@@ -1321,6 +1715,12 @@ var ApiAuthSchema = z2.discriminatedUnion("type", [
1321
1715
  password: z2.string()
1322
1716
  })
1323
1717
  ]);
1718
+ var CapabilityDeclarationSchema = {
1719
+ description: z2.string().optional(),
1720
+ capability_types: z2.array(z2.string()).optional(),
1721
+ requires_capabilities: z2.array(z2.string()).optional(),
1722
+ visibility: z2.enum(["public", "private"]).optional()
1723
+ };
1324
1724
  var ApiSkillConfigSchema = z2.object({
1325
1725
  id: z2.string().min(1),
1326
1726
  type: z2.literal("api"),
@@ -1333,7 +1733,9 @@ var ApiSkillConfigSchema = z2.object({
1333
1733
  pricing: PricingSchema,
1334
1734
  timeout_ms: z2.number().positive().default(3e4),
1335
1735
  retries: z2.number().nonnegative().int().default(0),
1336
- provider: z2.string().optional()
1736
+ provider: z2.string().optional(),
1737
+ capacity: CapacitySchema,
1738
+ ...CapabilityDeclarationSchema
1337
1739
  });
1338
1740
  var PipelineStepSchema = z2.union([
1339
1741
  z2.object({
@@ -1351,7 +1753,9 @@ var PipelineSkillConfigSchema = z2.object({
1351
1753
  name: z2.string().min(1),
1352
1754
  steps: z2.array(PipelineStepSchema).min(1),
1353
1755
  pricing: PricingSchema,
1354
- timeout_ms: z2.number().positive().optional()
1756
+ timeout_ms: z2.number().positive().optional(),
1757
+ capacity: CapacitySchema,
1758
+ ...CapabilityDeclarationSchema
1355
1759
  });
1356
1760
  var OpenClawSkillConfigSchema = z2.object({
1357
1761
  id: z2.string().min(1),
@@ -1360,7 +1764,17 @@ var OpenClawSkillConfigSchema = z2.object({
1360
1764
  agent_name: z2.string().min(1),
1361
1765
  channel: z2.enum(["telegram", "webhook", "process"]),
1362
1766
  pricing: PricingSchema,
1363
- timeout_ms: z2.number().positive().optional()
1767
+ timeout_ms: z2.number().positive().optional(),
1768
+ capacity: CapacitySchema,
1769
+ ...CapabilityDeclarationSchema
1770
+ });
1771
+ var ClaudeCodeConfigSchema = z2.object({
1772
+ /** Optional system prompt passed via `-p` flag. */
1773
+ system_prompt: z2.string().optional(),
1774
+ /** Model to use (e.g. 'claude-opus-4-6', 'claude-sonnet-4-6'). */
1775
+ model: z2.string().optional(),
1776
+ /** When true, passes `--dangerously-skip-permissions` to claude CLI. */
1777
+ auto_mode: z2.boolean().default(false)
1364
1778
  });
1365
1779
  var CommandSkillConfigSchema = z2.object({
1366
1780
  id: z2.string().min(1),
@@ -1370,8 +1784,11 @@ var CommandSkillConfigSchema = z2.object({
1370
1784
  output_type: z2.enum(["json", "text", "file"]),
1371
1785
  allowed_commands: z2.array(z2.string()).optional(),
1372
1786
  working_dir: z2.string().optional(),
1787
+ claude_code: ClaudeCodeConfigSchema.optional(),
1373
1788
  timeout_ms: z2.number().positive().default(3e4),
1374
- pricing: PricingSchema
1789
+ pricing: PricingSchema,
1790
+ capacity: CapacitySchema,
1791
+ ...CapabilityDeclarationSchema
1375
1792
  });
1376
1793
  var ConductorSkillConfigSchema = z2.object({
1377
1794
  id: z2.string().min(1),
@@ -1379,7 +1796,9 @@ var ConductorSkillConfigSchema = z2.object({
1379
1796
  name: z2.string().min(1),
1380
1797
  conductor_skill: z2.enum(["orchestrate", "plan"]),
1381
1798
  pricing: PricingSchema,
1382
- timeout_ms: z2.number().positive().optional()
1799
+ timeout_ms: z2.number().positive().optional(),
1800
+ capacity: CapacitySchema,
1801
+ ...CapabilityDeclarationSchema
1383
1802
  });
1384
1803
  var SkillConfigSchema = z2.discriminatedUnion("type", [
1385
1804
  ApiSkillConfigSchema,
@@ -1948,14 +2367,32 @@ var CommandExecutor = class {
1948
2367
  const cmdConfig = config;
1949
2368
  const baseCommand = cmdConfig.command.trim().split(/\s+/)[0] ?? "";
1950
2369
  if (cmdConfig.allowed_commands && cmdConfig.allowed_commands.length > 0) {
1951
- if (!cmdConfig.allowed_commands.includes(baseCommand)) {
2370
+ const effectiveAllowed = cmdConfig.claude_code ? [.../* @__PURE__ */ new Set([...cmdConfig.allowed_commands, "claude"])] : cmdConfig.allowed_commands;
2371
+ const commandToCheck = cmdConfig.claude_code ? "claude" : baseCommand;
2372
+ if (!effectiveAllowed.includes(commandToCheck)) {
1952
2373
  return {
1953
2374
  success: false,
1954
- error: `Command not allowed: "${baseCommand}". Allowed: ${cmdConfig.allowed_commands.join(", ")}`
2375
+ error: `Command not allowed: "${commandToCheck}". Allowed: ${effectiveAllowed.join(", ")}`
1955
2376
  };
1956
2377
  }
1957
2378
  }
1958
- const interpolatedCommand = safeInterpolateCommand2(cmdConfig.command, { params });
2379
+ let interpolatedCommand;
2380
+ if (cmdConfig.claude_code) {
2381
+ const parts = ["claude", "--print"];
2382
+ if (cmdConfig.claude_code.auto_mode) {
2383
+ parts.push("--dangerously-skip-permissions");
2384
+ }
2385
+ if (cmdConfig.claude_code.model) {
2386
+ parts.push("--model", cmdConfig.claude_code.model);
2387
+ }
2388
+ if (cmdConfig.claude_code.system_prompt) {
2389
+ parts.push("-p", shellEscape2(cmdConfig.claude_code.system_prompt));
2390
+ }
2391
+ const interpolatedPrompt = safeInterpolateCommand2(cmdConfig.command, { params });
2392
+ interpolatedCommand = parts.join(" ") + " " + interpolatedPrompt;
2393
+ } else {
2394
+ interpolatedCommand = safeInterpolateCommand2(cmdConfig.command, { params });
2395
+ }
1959
2396
  const timeout = cmdConfig.timeout_ms ?? 3e4;
1960
2397
  const cwd = cmdConfig.working_dir ?? process.cwd();
1961
2398
  let stdout;
@@ -2017,7 +2454,143 @@ var CommandExecutor = class {
2017
2454
  };
2018
2455
 
2019
2456
  // src/conductor/task-decomposer.ts
2020
- import { randomUUID as randomUUID6 } from "crypto";
2457
+ import { randomUUID as randomUUID7 } from "crypto";
2458
+
2459
+ // src/conductor/decomposition-validator.ts
2460
+ function validateAndNormalizeSubtasks(raw, context) {
2461
+ try {
2462
+ return _validate(raw, context);
2463
+ } catch {
2464
+ return { valid: [], errors: ["internal validation error"] };
2465
+ }
2466
+ }
2467
+ function _validate(raw, context) {
2468
+ if (!Array.isArray(raw)) {
2469
+ return { valid: [], errors: ["decomposition output must be an array"] };
2470
+ }
2471
+ if (raw.length === 0) {
2472
+ return { valid: [], errors: [] };
2473
+ }
2474
+ const errors = [];
2475
+ const validItems = [];
2476
+ const validIds = [];
2477
+ for (let i = 0; i < raw.length; i++) {
2478
+ const item = raw[i];
2479
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
2480
+ errors.push(`subtask[${i}]: must be an object`);
2481
+ continue;
2482
+ }
2483
+ const obj = item;
2484
+ let itemValid = true;
2485
+ const id = obj["id"];
2486
+ if (typeof id !== "string" || id.length === 0) {
2487
+ errors.push(`subtask[${i}]: id must be a non-empty string`);
2488
+ itemValid = false;
2489
+ }
2490
+ const description = obj["description"];
2491
+ if (typeof description !== "string" || description.length === 0) {
2492
+ errors.push(`subtask[${i}]: description must be a non-empty string`);
2493
+ itemValid = false;
2494
+ }
2495
+ const required_capability = obj["required_capability"];
2496
+ if (typeof required_capability !== "string" || required_capability.length === 0) {
2497
+ errors.push(`subtask[${i}]: required_capability must be a non-empty string`);
2498
+ itemValid = false;
2499
+ }
2500
+ const estimated_credits = obj["estimated_credits"];
2501
+ if (estimated_credits !== void 0) {
2502
+ if (typeof estimated_credits !== "number" || estimated_credits <= 0) {
2503
+ errors.push(`subtask[${i}]: estimated_credits must be a positive number`);
2504
+ itemValid = false;
2505
+ } else if (estimated_credits > context.max_credits) {
2506
+ errors.push(
2507
+ `subtask[${i}]: estimated_credits ${estimated_credits} exceeds max_credits ${context.max_credits}`
2508
+ );
2509
+ itemValid = false;
2510
+ }
2511
+ }
2512
+ if (itemValid) {
2513
+ validItems.push(obj);
2514
+ validIds.push(id);
2515
+ }
2516
+ }
2517
+ const idSet = /* @__PURE__ */ new Set();
2518
+ for (const id of validIds) {
2519
+ if (idSet.has(id)) {
2520
+ errors.push(`duplicate subtask id: ${id}`);
2521
+ } else {
2522
+ idSet.add(id);
2523
+ }
2524
+ }
2525
+ for (let i = 0; i < validItems.length; i++) {
2526
+ const item = validItems[i];
2527
+ const depends_on = item["depends_on"];
2528
+ if (!Array.isArray(depends_on)) continue;
2529
+ for (const dep of depends_on) {
2530
+ if (typeof dep === "string" && !idSet.has(dep)) {
2531
+ errors.push(`subtask[${i}]: depends_on references unknown id '${dep}'`);
2532
+ }
2533
+ }
2534
+ }
2535
+ if (errors.length === 0 && validItems.length > 0) {
2536
+ const inDegree = /* @__PURE__ */ new Map();
2537
+ const adjList = /* @__PURE__ */ new Map();
2538
+ for (const id of validIds) {
2539
+ inDegree.set(id, 0);
2540
+ adjList.set(id, []);
2541
+ }
2542
+ for (const item of validItems) {
2543
+ const depends_on = item["depends_on"];
2544
+ if (!Array.isArray(depends_on)) continue;
2545
+ for (const dep of depends_on) {
2546
+ if (typeof dep !== "string" || !idSet.has(dep)) continue;
2547
+ adjList.get(dep)?.push(item["id"]);
2548
+ inDegree.set(item["id"], (inDegree.get(item["id"]) ?? 0) + 1);
2549
+ }
2550
+ }
2551
+ const queue = [];
2552
+ for (const [id, deg] of inDegree) {
2553
+ if (deg === 0) queue.push(id);
2554
+ }
2555
+ let processed = 0;
2556
+ while (queue.length > 0) {
2557
+ const current = queue.shift();
2558
+ processed++;
2559
+ for (const neighbor of adjList.get(current) ?? []) {
2560
+ const newDeg = (inDegree.get(neighbor) ?? 0) - 1;
2561
+ inDegree.set(neighbor, newDeg);
2562
+ if (newDeg === 0) queue.push(neighbor);
2563
+ }
2564
+ }
2565
+ if (processed < validItems.length) {
2566
+ for (const [id, deg] of inDegree) {
2567
+ if (deg > 0) {
2568
+ errors.push(`circular dependency detected involving subtask id: ${id}`);
2569
+ break;
2570
+ }
2571
+ }
2572
+ }
2573
+ }
2574
+ if (errors.length > 0) {
2575
+ return { valid: [], errors };
2576
+ }
2577
+ const normalized = validItems.map((item) => {
2578
+ const depends_on = Array.isArray(item["depends_on"]) ? item["depends_on"].filter((x) => typeof x === "string") : [];
2579
+ const params = typeof item["params"] === "object" && item["params"] !== null && !Array.isArray(item["params"]) ? item["params"] : {};
2580
+ const estimated_credits = typeof item["estimated_credits"] === "number" ? item["estimated_credits"] : 0;
2581
+ return {
2582
+ id: item["id"],
2583
+ description: item["description"],
2584
+ required_capability: item["required_capability"],
2585
+ params,
2586
+ depends_on,
2587
+ estimated_credits
2588
+ };
2589
+ });
2590
+ return { valid: normalized, errors: [] };
2591
+ }
2592
+
2593
+ // src/conductor/task-decomposer.ts
2021
2594
  var TEMPLATES = {
2022
2595
  "video-production": {
2023
2596
  keywords: ["video", "demo", "clip", "animation"],
@@ -2112,7 +2685,7 @@ function decompose(task, _availableCapabilities) {
2112
2685
  for (const template of Object.values(TEMPLATES)) {
2113
2686
  const matched = template.keywords.some((kw) => lower.includes(kw));
2114
2687
  if (!matched) continue;
2115
- const ids = template.steps.map(() => randomUUID6());
2688
+ const ids = template.steps.map(() => randomUUID7());
2116
2689
  return template.steps.map((step, i) => ({
2117
2690
  id: ids[i],
2118
2691
  description: step.description,
@@ -2126,10 +2699,10 @@ function decompose(task, _availableCapabilities) {
2126
2699
  }
2127
2700
 
2128
2701
  // src/gateway/client.ts
2129
- import { randomUUID as randomUUID7 } from "crypto";
2702
+ import { randomUUID as randomUUID8 } from "crypto";
2130
2703
  async function requestCapability(opts) {
2131
2704
  const { gatewayUrl, token, cardId, params = {}, timeoutMs = 3e5, escrowReceipt, identity } = opts;
2132
- const id = randomUUID7();
2705
+ const id = randomUUID8();
2133
2706
  const payload = {
2134
2707
  jsonrpc: "2.0",
2135
2708
  id,
@@ -2199,36 +2772,15 @@ async function requestViaRelay(relay, opts) {
2199
2772
  }
2200
2773
 
2201
2774
  // src/autonomy/tiers.ts
2202
- import { randomUUID as randomUUID8 } from "crypto";
2775
+ import { randomUUID as randomUUID9 } from "crypto";
2203
2776
 
2204
2777
  // src/autonomy/pending-requests.ts
2205
- import { randomUUID as randomUUID9 } from "crypto";
2778
+ import { randomUUID as randomUUID10 } from "crypto";
2206
2779
 
2207
2780
  // src/cli/peers.ts
2208
2781
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
2209
2782
  import { join as join3 } from "path";
2210
2783
 
2211
- // src/cli/config.ts
2212
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync2 } from "fs";
2213
- import { homedir } from "os";
2214
- import { join as join2 } from "path";
2215
- function getConfigDir() {
2216
- return process.env["AGENTBNB_DIR"] ?? join2(homedir(), ".agentbnb");
2217
- }
2218
- function getConfigPath() {
2219
- return join2(getConfigDir(), "config.json");
2220
- }
2221
- function loadConfig() {
2222
- const configPath = getConfigPath();
2223
- if (!existsSync2(configPath)) return null;
2224
- try {
2225
- const raw = readFileSync2(configPath, "utf-8");
2226
- return JSON.parse(raw);
2227
- } catch {
2228
- return null;
2229
- }
2230
- }
2231
-
2232
2784
  // src/cli/remote-registry.ts
2233
2785
  var RegistryTimeoutError = class extends AgentBnBError {
2234
2786
  constructor(url) {
@@ -2326,12 +2878,14 @@ function scorePeers(candidates, selfOwner) {
2326
2878
  const idleRate = internal?.["idle_rate"];
2327
2879
  return typeof idleRate === "number" ? idleRate : 1;
2328
2880
  });
2881
+ const loadFactors = eligible.map((c) => c.loadFactor ?? 1);
2329
2882
  const normSuccess = minMaxNormalize(successRates);
2330
2883
  const normCost = minMaxNormalize(costEfficiencies);
2331
2884
  const normIdle = minMaxNormalize(idleRates);
2885
+ const normLoad = minMaxNormalize(loadFactors);
2332
2886
  const scored = eligible.map((c, i) => ({
2333
2887
  ...c,
2334
- rawScore: (normSuccess[i] ?? 0) * (normCost[i] ?? 0) * (normIdle[i] ?? 0)
2888
+ rawScore: (normSuccess[i] ?? 0) * (normCost[i] ?? 0) * (normIdle[i] ?? 0) * (normLoad[i] ?? 0)
2335
2889
  }));
2336
2890
  scored.sort((a, b) => b.rawScore - a.rawScore);
2337
2891
  return scored;
@@ -2573,6 +3127,12 @@ function computeWaves(subtasks) {
2573
3127
  async function orchestrate(opts) {
2574
3128
  const { subtasks, matches, gatewayToken, resolveAgentUrl, timeoutMs = 3e5, maxBudget, relayClient, requesterOwner } = opts;
2575
3129
  const startTime = Date.now();
3130
+ const teamMemberMap = /* @__PURE__ */ new Map();
3131
+ if (opts.team) {
3132
+ for (const member of opts.team.matched) {
3133
+ teamMemberMap.set(member.subtask.id, member);
3134
+ }
3135
+ }
2576
3136
  if (subtasks.length === 0) {
2577
3137
  return {
2578
3138
  success: true,
@@ -2584,6 +3144,7 @@ async function orchestrate(opts) {
2584
3144
  const results = /* @__PURE__ */ new Map();
2585
3145
  const errors = [];
2586
3146
  let totalCredits = 0;
3147
+ const traceContext = /* @__PURE__ */ new Map();
2587
3148
  const waves = computeWaves(subtasks);
2588
3149
  const subtaskMap = new Map(subtasks.map((s) => [s.id, s]));
2589
3150
  for (const wave of waves) {
@@ -2620,7 +3181,11 @@ async function orchestrate(opts) {
2620
3181
  subtask.params,
2621
3182
  interpContext
2622
3183
  );
2623
- const primary = resolveAgentUrl(m.selected_agent);
3184
+ const teamMember = teamMemberMap.get(taskId);
3185
+ const teamId = opts.team?.team_id ?? null;
3186
+ const taskCapabilityType = teamMember?.capability_type ?? null;
3187
+ const agentOwner = teamMember?.agent ?? m.selected_agent;
3188
+ const primary = resolveAgentUrl(agentOwner);
2624
3189
  try {
2625
3190
  let res;
2626
3191
  if (primary.url.startsWith("relay://") && relayClient) {
@@ -2641,7 +3206,7 @@ async function orchestrate(opts) {
2641
3206
  timeoutMs
2642
3207
  });
2643
3208
  }
2644
- return { taskId, result: res, credits: m.credits };
3209
+ return { taskId, result: res, credits: m.credits, team_id: teamId, capability_type: taskCapabilityType };
2645
3210
  } catch (primaryErr) {
2646
3211
  if (m.alternatives.length > 0) {
2647
3212
  const alt = m.alternatives[0];
@@ -2666,7 +3231,7 @@ async function orchestrate(opts) {
2666
3231
  timeoutMs
2667
3232
  });
2668
3233
  }
2669
- return { taskId, result: altRes, credits: alt.credits };
3234
+ return { taskId, result: altRes, credits: alt.credits, team_id: teamId, capability_type: taskCapabilityType };
2670
3235
  } catch (altErr) {
2671
3236
  throw new Error(
2672
3237
  `Task ${taskId}: primary (${m.selected_agent}) failed: ${primaryErr instanceof Error ? primaryErr.message : String(primaryErr)}; alternative (${alt.agent}) failed: ${altErr instanceof Error ? altErr.message : String(altErr)}`
@@ -2681,9 +3246,10 @@ async function orchestrate(opts) {
2681
3246
  );
2682
3247
  for (const settlement of waveResults) {
2683
3248
  if (settlement.status === "fulfilled") {
2684
- const { taskId, result, credits } = settlement.value;
3249
+ const { taskId, result, credits, team_id, capability_type } = settlement.value;
2685
3250
  results.set(taskId, result);
2686
3251
  totalCredits += credits;
3252
+ traceContext.set(taskId, { team_id: team_id ?? null, capability_type: capability_type ?? null });
2687
3253
  } else {
2688
3254
  errors.push(settlement.reason instanceof Error ? settlement.reason.message : String(settlement.reason));
2689
3255
  }
@@ -2694,7 +3260,8 @@ async function orchestrate(opts) {
2694
3260
  results,
2695
3261
  total_credits: totalCredits,
2696
3262
  latency_ms: Date.now() - startTime,
2697
- errors: errors.length > 0 ? errors : void 0
3263
+ errors: errors.length > 0 ? errors : void 0,
3264
+ trace: traceContext.size > 0 ? traceContext : void 0
2698
3265
  };
2699
3266
  }
2700
3267
 
@@ -2743,6 +3310,96 @@ var BudgetManager = class {
2743
3310
  }
2744
3311
  };
2745
3312
 
3313
+ // src/conductor/team-formation.ts
3314
+ import { randomUUID as randomUUID11 } from "crypto";
3315
+ function selectByStrategy(matches, strategy) {
3316
+ if (matches.length === 0) return void 0;
3317
+ if (strategy === "balanced") {
3318
+ return matches[0];
3319
+ }
3320
+ if (strategy === "quality_optimized") {
3321
+ return matches.reduce((best, m) => m.score > best.score ? m : best, matches[0]);
3322
+ }
3323
+ return matches.reduce((best, m) => {
3324
+ if (m.credits < best.credits) return m;
3325
+ if (m.credits === best.credits && m.score > best.score) return m;
3326
+ return best;
3327
+ }, matches[0]);
3328
+ }
3329
+ async function formTeam(opts) {
3330
+ const { subtasks, strategy, db, conductorOwner, registryUrl } = opts;
3331
+ const team_id = randomUUID11();
3332
+ if (subtasks.length === 0) {
3333
+ return { team_id, strategy, matched: [], unrouted: [] };
3334
+ }
3335
+ const matched = [];
3336
+ const unrouted = [];
3337
+ for (const subtask of subtasks) {
3338
+ const skillCards = getCardsBySkillCapability(db, subtask.required_capability).filter((c) => c.owner !== conductorOwner);
3339
+ if (skillCards.length > 0) {
3340
+ const candidates = skillCards.map((card) => {
3341
+ const skills = card.skills ?? [];
3342
+ const matchingSkill = skills.find(
3343
+ (s) => s.capability_type === subtask.required_capability || (s.capability_types ?? []).includes(subtask.required_capability)
3344
+ );
3345
+ return {
3346
+ subtask_id: subtask.id,
3347
+ selected_agent: card.owner,
3348
+ selected_skill: matchingSkill?.id ?? "",
3349
+ selected_card_id: card.id,
3350
+ score: 1,
3351
+ credits: matchingSkill?.pricing.credits_per_call ?? 0,
3352
+ alternatives: []
3353
+ };
3354
+ });
3355
+ const selected2 = selectByStrategy(candidates, strategy);
3356
+ matched.push({
3357
+ subtask,
3358
+ capability_type: subtask.required_capability,
3359
+ agent: selected2.selected_agent,
3360
+ skill: selected2.selected_skill,
3361
+ card_id: selected2.selected_card_id,
3362
+ credits: selected2.credits,
3363
+ score: selected2.score
3364
+ });
3365
+ continue;
3366
+ }
3367
+ const matchResults = await matchSubTasks({
3368
+ db,
3369
+ subtasks: [subtask],
3370
+ conductorOwner,
3371
+ registryUrl
3372
+ });
3373
+ const m = matchResults[0];
3374
+ if (!m || m.selected_agent === "") {
3375
+ unrouted.push(subtask);
3376
+ continue;
3377
+ }
3378
+ const allCandidates = [
3379
+ m,
3380
+ ...m.alternatives.map((alt) => ({
3381
+ subtask_id: m.subtask_id,
3382
+ selected_agent: alt.agent,
3383
+ selected_skill: alt.skill,
3384
+ score: alt.score,
3385
+ credits: alt.credits,
3386
+ alternatives: []
3387
+ }))
3388
+ ];
3389
+ const selected = selectByStrategy(allCandidates, strategy);
3390
+ matched.push({
3391
+ subtask,
3392
+ capability_type: subtask.required_capability,
3393
+ agent: selected.selected_agent,
3394
+ skill: selected.selected_skill,
3395
+ card_id: selected === m ? m.selected_card_id : void 0,
3396
+ credits: selected.credits,
3397
+ score: selected.score
3398
+ });
3399
+ }
3400
+ return { team_id, strategy, matched, unrouted };
3401
+ }
3402
+
2746
3403
  // src/conductor/conductor-mode.ts
2747
3404
  var ConductorMode = class {
2748
3405
  db;
@@ -2781,7 +3438,48 @@ var ConductorMode = class {
2781
3438
  error: 'Missing or empty "task" parameter'
2782
3439
  };
2783
3440
  }
2784
- const subtasks = decompose(task);
3441
+ const orchestrationDepth = typeof params.orchestration_depth === "number" ? params.orchestration_depth : 0;
3442
+ const decompositionDepth = typeof params.decomposition_depth === "number" ? params.decomposition_depth : 0;
3443
+ if (orchestrationDepth >= 2) {
3444
+ return {
3445
+ success: false,
3446
+ error: "orchestration_depth limit exceeded: max 1 nested orchestration"
3447
+ };
3448
+ }
3449
+ let subtasks = [];
3450
+ if (decompositionDepth === 0) {
3451
+ const allDecomposers = getCardsByCapabilityType(this.db, "task_decomposition");
3452
+ const externalDecomposers = allDecomposers.filter((c) => c.owner !== this.conductorOwner);
3453
+ if (externalDecomposers.length > 0) {
3454
+ const provider = externalDecomposers[0];
3455
+ try {
3456
+ const providerUrl = this.resolveAgentUrl(provider.owner);
3457
+ const response = await requestCapability({
3458
+ gatewayUrl: providerUrl.url,
3459
+ token: this.gatewayToken,
3460
+ cardId: provider.id,
3461
+ params: {
3462
+ task,
3463
+ decomposition_depth: decompositionDepth + 1,
3464
+ orchestration_depth: orchestrationDepth + 1
3465
+ },
3466
+ timeoutMs: 3e4
3467
+ });
3468
+ if (Array.isArray(response)) {
3469
+ const validation = validateAndNormalizeSubtasks(response, {
3470
+ max_credits: this.maxBudget
3471
+ });
3472
+ if (validation.errors.length === 0) {
3473
+ subtasks = validation.valid;
3474
+ }
3475
+ }
3476
+ } catch {
3477
+ }
3478
+ }
3479
+ }
3480
+ if (subtasks.length === 0) {
3481
+ subtasks = decompose(task);
3482
+ }
2785
3483
  if (subtasks.length === 0) {
2786
3484
  return {
2787
3485
  success: false,
@@ -2795,6 +3493,17 @@ var ConductorMode = class {
2795
3493
  conductorOwner: this.conductorOwner
2796
3494
  });
2797
3495
  onProgress?.({ step: 2, total: 5, message: `Matched ${matchResults.length} sub-tasks to agents` });
3496
+ let team;
3497
+ if (conductorSkill === "orchestrate") {
3498
+ const strategy = typeof params.formation_strategy === "string" && ["cost_optimized", "quality_optimized", "balanced"].includes(params.formation_strategy) ? params.formation_strategy : "balanced";
3499
+ team = await formTeam({
3500
+ subtasks,
3501
+ strategy,
3502
+ db: this.db,
3503
+ conductorOwner: this.conductorOwner
3504
+ });
3505
+ onProgress?.({ step: 2, total: 5, message: `Formed team: ${team.matched.length} members, ${team.unrouted.length} unrouted` });
3506
+ }
2798
3507
  const budgetManager = new BudgetManager(this.creditDb, this.conductorOwner);
2799
3508
  const budgetController = new BudgetController(budgetManager, this.maxBudget);
2800
3509
  const executionBudget = budgetController.calculateBudget(matchResults);
@@ -2811,7 +3520,9 @@ var ConductorMode = class {
2811
3520
  result: {
2812
3521
  subtasks,
2813
3522
  matches: matchResults,
2814
- budget: executionBudget
3523
+ budget: executionBudget,
3524
+ team
3525
+ // undefined when no role hints
2815
3526
  }
2816
3527
  };
2817
3528
  }
@@ -2823,7 +3534,8 @@ var ConductorMode = class {
2823
3534
  matches: matchMap,
2824
3535
  gatewayToken: this.gatewayToken,
2825
3536
  resolveAgentUrl: this.resolveAgentUrl,
2826
- maxBudget: this.maxBudget
3537
+ maxBudget: this.maxBudget,
3538
+ team
2827
3539
  });
2828
3540
  onProgress?.({ step: 4, total: 5, message: "Pipeline execution complete" });
2829
3541
  const resultObj = {};
@@ -2846,7 +3558,7 @@ var ConductorMode = class {
2846
3558
 
2847
3559
  // src/credit/escrow-receipt.ts
2848
3560
  import { z as z3 } from "zod";
2849
- import { randomUUID as randomUUID10 } from "crypto";
3561
+ import { randomUUID as randomUUID12 } from "crypto";
2850
3562
  var EscrowReceiptSchema = z3.object({
2851
3563
  requester_owner: z3.string().min(1),
2852
3564
  requester_public_key: z3.string().min(1),
@@ -2866,7 +3578,7 @@ function createSignedEscrowReceipt(db, privateKey, publicKey, opts) {
2866
3578
  card_id: opts.cardId,
2867
3579
  ...opts.skillId ? { skill_id: opts.skillId } : {},
2868
3580
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2869
- nonce: randomUUID10()
3581
+ nonce: randomUUID12()
2870
3582
  };
2871
3583
  const signature = signEscrowReceipt(receiptData, privateKey);
2872
3584
  const receipt = {
@@ -3237,7 +3949,7 @@ var AgentBnBProvider = class {
3237
3949
 
3238
3950
  // src/identity/guarantor.ts
3239
3951
  import { z as z5 } from "zod";
3240
- import { randomUUID as randomUUID11 } from "crypto";
3952
+ import { randomUUID as randomUUID13 } from "crypto";
3241
3953
  var MAX_AGENTS_PER_GUARANTOR = 10;
3242
3954
  var GUARANTOR_CREDIT_POOL = 50;
3243
3955
  var GuarantorRecordSchema = z5.object({
@@ -3276,7 +3988,7 @@ function registerGuarantor(db, githubLogin) {
3276
3988
  );
3277
3989
  }
3278
3990
  const record = {
3279
- id: randomUUID11(),
3991
+ id: randomUUID13(),
3280
3992
  github_login: githubLogin,
3281
3993
  agent_count: 0,
3282
3994
  credit_pool: GUARANTOR_CREDIT_POOL,
@@ -3350,7 +4062,7 @@ function getAgentGuarantor(db, agentId) {
3350
4062
  function initiateGithubAuth() {
3351
4063
  return {
3352
4064
  auth_url: "https://github.com/login/oauth/authorize?client_id=PLACEHOLDER&scope=read:user",
3353
- state: randomUUID11()
4065
+ state: randomUUID13()
3354
4066
  };
3355
4067
  }
3356
4068
 
@@ -3422,6 +4134,27 @@ var RelayProgressMessageSchema = z6.object({
3422
4134
  message: z6.string().optional()
3423
4135
  // optional status message
3424
4136
  });
4137
+ var HeartbeatMessageSchema = z6.object({
4138
+ type: z6.literal("heartbeat"),
4139
+ owner: z6.string().min(1),
4140
+ capacity: z6.object({
4141
+ current_load: z6.number(),
4142
+ max_concurrent: z6.number(),
4143
+ queue_depth: z6.number()
4144
+ }),
4145
+ self_summary: z6.object({
4146
+ capabilities: z6.array(z6.string()),
4147
+ success_rate: z6.number(),
4148
+ credit_balance: z6.number(),
4149
+ total_completed: z6.number(),
4150
+ provider_number: z6.number(),
4151
+ reliability: z6.object({
4152
+ current_streak: z6.number(),
4153
+ repeat_hire_rate: z6.number(),
4154
+ avg_feedback: z6.number()
4155
+ })
4156
+ })
4157
+ });
3425
4158
  var RelayMessageSchema = z6.discriminatedUnion("type", [
3426
4159
  RegisterMessageSchema,
3427
4160
  RegisteredMessageSchema,
@@ -3430,11 +4163,12 @@ var RelayMessageSchema = z6.discriminatedUnion("type", [
3430
4163
  RelayResponseMessageSchema,
3431
4164
  ResponseMessageSchema,
3432
4165
  ErrorMessageSchema,
3433
- RelayProgressMessageSchema
4166
+ RelayProgressMessageSchema,
4167
+ HeartbeatMessageSchema
3434
4168
  ]);
3435
4169
 
3436
4170
  // src/relay/websocket-relay.ts
3437
- import { randomUUID as randomUUID14 } from "crypto";
4171
+ import { randomUUID as randomUUID16 } from "crypto";
3438
4172
 
3439
4173
  // src/relay/relay-credit.ts
3440
4174
  function lookupCardPrice(registryDb, cardId, skillId) {
@@ -3492,10 +4226,10 @@ function releaseForRelay(creditDb, escrowId) {
3492
4226
  }
3493
4227
 
3494
4228
  // src/hub-agent/relay-bridge.ts
3495
- import { randomUUID as randomUUID13 } from "crypto";
4229
+ import { randomUUID as randomUUID15 } from "crypto";
3496
4230
 
3497
4231
  // src/hub-agent/job-queue.ts
3498
- import { randomUUID as randomUUID12 } from "crypto";
4232
+ import { randomUUID as randomUUID14 } from "crypto";
3499
4233
  function updateJobStatus(db, jobId, status, result) {
3500
4234
  const now = (/* @__PURE__ */ new Date()).toISOString();
3501
4235
  if (result !== void 0) {
@@ -3540,6 +4274,7 @@ function registerWebSocketRelay(server, db, creditDb) {
3540
4274
  const connections = /* @__PURE__ */ new Map();
3541
4275
  const pendingRequests = /* @__PURE__ */ new Map();
3542
4276
  const rateLimits = /* @__PURE__ */ new Map();
4277
+ const agentCapacities = /* @__PURE__ */ new Map();
3543
4278
  let onAgentOnlineCallback;
3544
4279
  function checkRateLimit(owner) {
3545
4280
  const now = Date.now();
@@ -3616,7 +4351,7 @@ function registerWebSocketRelay(server, db, creditDb) {
3616
4351
  function logAgentJoined(owner, cardName, cardId) {
3617
4352
  try {
3618
4353
  insertRequestLog(db, {
3619
- id: randomUUID14(),
4354
+ id: randomUUID16(),
3620
4355
  card_id: cardId,
3621
4356
  card_name: cardName,
3622
4357
  requester: owner,
@@ -3843,6 +4578,7 @@ function registerWebSocketRelay(server, db, creditDb) {
3843
4578
  if (!owner) return;
3844
4579
  connections.delete(owner);
3845
4580
  rateLimits.delete(owner);
4581
+ agentCapacities.delete(owner);
3846
4582
  markOwnerOffline(owner);
3847
4583
  for (const [reqId, pending] of pendingRequests) {
3848
4584
  if (pending.targetOwner === owner) {
@@ -3876,6 +4612,9 @@ function registerWebSocketRelay(server, db, creditDb) {
3876
4612
  }
3877
4613
  }
3878
4614
  }
4615
+ function handleHeartbeat(msg) {
4616
+ agentCapacities.set(msg.owner, msg.capacity);
4617
+ }
3879
4618
  void server.register(async (app) => {
3880
4619
  app.get("/ws", { websocket: true }, (rawSocket, _request) => {
3881
4620
  const socket = rawSocket;
@@ -3921,15 +4660,23 @@ function registerWebSocketRelay(server, db, creditDb) {
3921
4660
  case "relay_progress":
3922
4661
  handleRelayProgress(msg);
3923
4662
  break;
4663
+ case "heartbeat":
4664
+ handleHeartbeat(msg);
4665
+ break;
3924
4666
  default:
3925
4667
  break;
3926
4668
  }
3927
4669
  })();
3928
4670
  });
4671
+ const pingInterval = setInterval(() => {
4672
+ if (socket.readyState === 1) socket.ping();
4673
+ }, 3e4);
3929
4674
  socket.on("close", () => {
4675
+ clearInterval(pingInterval);
3930
4676
  handleDisconnect(registeredOwner);
3931
4677
  });
3932
4678
  socket.on("error", () => {
4679
+ clearInterval(pingInterval);
3933
4680
  handleDisconnect(registeredOwner);
3934
4681
  });
3935
4682
  });
@@ -3950,6 +4697,7 @@ function registerWebSocketRelay(server, db, creditDb) {
3950
4697
  }
3951
4698
  pendingRequests.clear();
3952
4699
  rateLimits.clear();
4700
+ agentCapacities.clear();
3953
4701
  },
3954
4702
  setOnAgentOnline: (cb) => {
3955
4703
  onAgentOnlineCallback = cb;
@@ -3958,13 +4706,15 @@ function registerWebSocketRelay(server, db, creditDb) {
3958
4706
  getPendingRequests: () => pendingRequests,
3959
4707
  sendMessage: (ws, msg) => {
3960
4708
  sendMessage(ws, msg);
3961
- }
4709
+ },
4710
+ getAgentCapacity: (owner) => agentCapacities.get(owner),
4711
+ getAllCapacities: () => new Map(agentCapacities)
3962
4712
  };
3963
4713
  }
3964
4714
 
3965
4715
  // src/relay/websocket-client.ts
3966
4716
  import WebSocket from "ws";
3967
- import { randomUUID as randomUUID15 } from "crypto";
4717
+ import { randomUUID as randomUUID17 } from "crypto";
3968
4718
  var RelayClient = class {
3969
4719
  ws = null;
3970
4720
  opts;
@@ -4061,7 +4811,7 @@ var RelayClient = class {
4061
4811
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN || !this.registered) {
4062
4812
  throw new Error("Not connected to registry relay");
4063
4813
  }
4064
- const id = randomUUID15();
4814
+ const id = randomUUID17();
4065
4815
  const timeoutMs = opts.timeoutMs ?? 3e5;
4066
4816
  return new Promise((resolve, reject) => {
4067
4817
  const timeout = setTimeout(() => {
@@ -4265,12 +5015,12 @@ var RelayClient = class {
4265
5015
  };
4266
5016
 
4267
5017
  // src/onboarding/index.ts
4268
- import { randomUUID as randomUUID17 } from "crypto";
5018
+ import { randomUUID as randomUUID19 } from "crypto";
4269
5019
  import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
4270
5020
  import { join as join5 } from "path";
4271
5021
 
4272
5022
  // src/cli/onboarding.ts
4273
- import { randomUUID as randomUUID16 } from "crypto";
5023
+ import { randomUUID as randomUUID18 } from "crypto";
4274
5024
  import { createConnection } from "net";
4275
5025
  var KNOWN_API_KEYS = [
4276
5026
  "OPENAI_API_KEY",
@@ -4412,7 +5162,7 @@ function capabilitiesToV2Card(capabilities, owner, agentName) {
4412
5162
  }));
4413
5163
  const card = {
4414
5164
  spec_version: "2.0",
4415
- id: randomUUID17(),
5165
+ id: randomUUID19(),
4416
5166
  owner,
4417
5167
  agent_name: agentName ?? owner,
4418
5168
  skills,