agentbnb 5.1.11 → 6.0.0

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 (48) hide show
  1. package/dist/{card-RSGDCHCV.js → card-REW7BSWW.js} +1 -1
  2. package/dist/{chunk-EPIWHNB2.js → chunk-2TLZ6G2B.js} +446 -373
  3. package/dist/{chunk-WGZ5AGOX.js → chunk-3CIMVISQ.js} +24 -1
  4. package/dist/{chunk-NH2FIERR.js → chunk-574W3HHE.js} +1 -1
  5. package/dist/{chunk-B5FTAGFN.js → chunk-7XHDSWRD.js} +75 -75
  6. package/dist/{chunk-NLAWT4DT.js → chunk-7YLFLC5C.js} +6 -6
  7. package/dist/chunk-BP3L2TET.js +148 -0
  8. package/dist/{chunk-EGUOAHCW.js → chunk-C2T4BMRW.js} +12 -12
  9. package/dist/{chunk-5KFI5X7B.js → chunk-F53QQIM2.js} +1 -1
  10. package/dist/chunk-JR6TJDIF.js +425 -0
  11. package/dist/{chunk-DFBX3BBD.js → chunk-KA2VIEGM.js} +211 -16
  12. package/dist/chunk-NQTE577Q.js +159 -0
  13. package/dist/{chunk-WTXRY7R2.js → chunk-NYV3NE5Z.js} +157 -9
  14. package/dist/{chunk-UKT6H7YT.js → chunk-OZXCRLP3.js} +1 -1
  15. package/dist/{chunk-QITOPASZ.js → chunk-PSQHUZ7X.js} +1 -1
  16. package/dist/{chunk-EANI2N2V.js → chunk-RVYQSC6L.js} +2 -99
  17. package/dist/{chunk-MLS6IGGG.js → chunk-TQDV254A.js} +1 -1
  18. package/dist/{chunk-QQFBFV4V.js → chunk-TR6UZDNX.js} +57 -18
  19. package/dist/{chunk-ZX5623ER.js → chunk-VMH2YS2I.js} +1 -1
  20. package/dist/{chunk-XND2DWTZ.js → chunk-VPQ44XKE.js} +2 -2
  21. package/dist/{chunk-CSATDXZC.js → chunk-Y7T6IMM3.js} +1 -1
  22. package/dist/{chunk-FLY3WIQR.js → chunk-YRRVFTDR.js} +3 -3
  23. package/dist/cli/index.js +261 -125
  24. package/dist/{client-T5MTY3CS.js → client-HRYRJKSA.js} +3 -3
  25. package/dist/{conduct-WU3VEXB6.js → conduct-LF6FYPLD.js} +11 -11
  26. package/dist/conduct-QAFZIEY6.js +21 -0
  27. package/dist/{conductor-mode-ZMTFZGJP.js → conductor-mode-NUDQLZFM.js} +309 -13
  28. package/dist/conductor-mode-YQ6QSPPT.js +275 -0
  29. package/dist/{execute-4D4ITQCL.js → execute-ITHIYYOX.js} +4 -3
  30. package/dist/execute-PNJFABVJ.js +14 -0
  31. package/dist/index.d.ts +555 -0
  32. package/dist/index.js +592 -83
  33. package/dist/{process-guard-CC7CNRQJ.js → process-guard-QCCBGILS.js} +1 -1
  34. package/dist/publish-capability-TS6CNR5G.js +12 -0
  35. package/dist/{request-VOXBFUOG.js → request-P6QCTCCG.js} +14 -14
  36. package/dist/{serve-skill-IH7UAJNR.js → serve-skill-EZOL7UYN.js} +10 -9
  37. package/dist/{server-JVQW2TID.js → server-3G6ZTASA.js} +16 -16
  38. package/dist/{service-coordinator-EYRDTHL5.js → service-coordinator-CRSE4GWC.js} +174 -242
  39. package/dist/skill-config-4W5W5O6T.js +22 -0
  40. package/dist/skills/agentbnb/bootstrap.js +227 -67
  41. package/package.json +1 -1
  42. package/skills/agentbnb/SKILL.md +35 -0
  43. package/skills/agentbnb/bootstrap.ts +126 -8
  44. package/skills/agentbnb/install.sh +49 -9
  45. package/dist/chunk-CRFCWD6V.js +0 -366
  46. package/dist/conduct-6LKIJJKQ.js +0 -21
  47. package/dist/conductor-mode-Q4IIDY5E.js +0 -123
  48. package/dist/execute-T7Y6RKSW.js +0 -13
@@ -1,24 +1,8 @@
1
- import {
2
- confirmEscrowDebit,
3
- getBalance,
4
- holdEscrow,
5
- initFeedbackTable,
6
- recordEarning,
7
- releaseEscrow,
8
- settleEscrow,
9
- verifyEscrowReceipt
10
- } from "./chunk-CRFCWD6V.js";
11
1
  import {
12
2
  AgentBnBError,
13
3
  AnyCardSchema,
14
4
  CapabilityCardSchema
15
- } from "./chunk-WGZ5AGOX.js";
16
-
17
- // src/gateway/execute.ts
18
- import { randomUUID as randomUUID2 } from "crypto";
19
-
20
- // src/registry/store.ts
21
- import Database from "better-sqlite3";
5
+ } from "./chunk-3CIMVISQ.js";
22
6
 
23
7
  // src/registry/request-log.ts
24
8
  var SINCE_MS = {
@@ -54,11 +38,27 @@ function createRequestLogTable(db) {
54
38
  db.exec("ALTER TABLE request_log ADD COLUMN tier_invoked INTEGER");
55
39
  } catch {
56
40
  }
41
+ try {
42
+ db.exec("ALTER TABLE request_log ADD COLUMN failure_reason TEXT");
43
+ } catch {
44
+ }
45
+ try {
46
+ db.exec("ALTER TABLE request_log ADD COLUMN team_id TEXT");
47
+ } catch {
48
+ }
49
+ try {
50
+ db.exec("ALTER TABLE request_log ADD COLUMN role TEXT");
51
+ } catch {
52
+ }
53
+ try {
54
+ db.exec("ALTER TABLE request_log ADD COLUMN capability_type TEXT");
55
+ } catch {
56
+ }
57
57
  }
58
58
  function insertRequestLog(db, entry) {
59
59
  const stmt = db.prepare(`
60
- INSERT INTO request_log (id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked)
61
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
60
+ 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)
61
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
62
62
  `);
63
63
  stmt.run(
64
64
  entry.id,
@@ -71,7 +71,11 @@ function insertRequestLog(db, entry) {
71
71
  entry.created_at,
72
72
  entry.skill_id ?? null,
73
73
  entry.action_type ?? null,
74
- entry.tier_invoked ?? null
74
+ entry.tier_invoked ?? null,
75
+ entry.failure_reason ?? null,
76
+ entry.team_id ?? null,
77
+ entry.role ?? null,
78
+ entry.capability_type ?? null
75
79
  );
76
80
  }
77
81
  function getSkillRequestCount(db, skillId, windowMs) {
@@ -113,7 +117,7 @@ function getRequestLog(db, limit = 10, since) {
113
117
  if (since !== void 0) {
114
118
  const cutoff = new Date(Date.now() - SINCE_MS[since]).toISOString();
115
119
  const stmt2 = db.prepare(`
116
- SELECT id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked
120
+ SELECT 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
117
121
  FROM request_log
118
122
  WHERE created_at >= ?
119
123
  ORDER BY created_at DESC
@@ -122,7 +126,7 @@ function getRequestLog(db, limit = 10, since) {
122
126
  return stmt2.all(cutoff, limit);
123
127
  }
124
128
  const stmt = db.prepare(`
125
- SELECT id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked
129
+ SELECT id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked, failure_reason, team_id, role
126
130
  FROM request_log
127
131
  ORDER BY created_at DESC
128
132
  LIMIT ?
@@ -130,8 +134,104 @@ function getRequestLog(db, limit = 10, since) {
130
134
  return stmt.all(limit);
131
135
  }
132
136
 
133
- // src/evolution/store.ts
137
+ // src/registry/store.ts
138
+ import Database from "better-sqlite3";
139
+
140
+ // src/feedback/store.ts
134
141
  import { randomUUID } from "crypto";
142
+ function initFeedbackTable(db) {
143
+ db.exec(`
144
+ CREATE TABLE IF NOT EXISTS feedback (
145
+ id TEXT PRIMARY KEY,
146
+ transaction_id TEXT NOT NULL,
147
+ provider_agent TEXT NOT NULL,
148
+ skill_id TEXT NOT NULL,
149
+ requester_agent TEXT NOT NULL,
150
+ rating INTEGER NOT NULL,
151
+ latency_ms INTEGER NOT NULL,
152
+ result_quality TEXT NOT NULL,
153
+ quality_details TEXT,
154
+ would_reuse INTEGER NOT NULL,
155
+ cost_value_ratio TEXT NOT NULL,
156
+ timestamp TEXT NOT NULL,
157
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
158
+ );
159
+
160
+ CREATE INDEX IF NOT EXISTS feedback_provider_idx ON feedback(provider_agent);
161
+ CREATE INDEX IF NOT EXISTS feedback_skill_idx ON feedback(skill_id);
162
+ `);
163
+ }
164
+ function insertFeedback(db, feedback) {
165
+ const id = randomUUID();
166
+ const now = (/* @__PURE__ */ new Date()).toISOString();
167
+ db.prepare(`
168
+ INSERT INTO feedback (
169
+ id, transaction_id, provider_agent, skill_id, requester_agent,
170
+ rating, latency_ms, result_quality, quality_details,
171
+ would_reuse, cost_value_ratio, timestamp, created_at
172
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
173
+ `).run(
174
+ id,
175
+ feedback.transaction_id,
176
+ feedback.provider_agent,
177
+ feedback.skill_id,
178
+ feedback.requester_agent,
179
+ feedback.rating,
180
+ feedback.latency_ms,
181
+ feedback.result_quality,
182
+ feedback.quality_details ?? null,
183
+ feedback.would_reuse ? 1 : 0,
184
+ feedback.cost_value_ratio,
185
+ feedback.timestamp,
186
+ now
187
+ );
188
+ return id;
189
+ }
190
+ function getFeedbackForSkill(db, skillId, limit = 20) {
191
+ const rows = db.prepare(`
192
+ SELECT * FROM feedback
193
+ WHERE skill_id = ?
194
+ ORDER BY timestamp DESC
195
+ LIMIT ?
196
+ `).all(skillId, limit);
197
+ return rows.map(rowToFeedback);
198
+ }
199
+ function getFeedbackForProvider(db, providerAgent, sinceDays) {
200
+ let rows;
201
+ if (sinceDays !== void 0) {
202
+ rows = db.prepare(`
203
+ SELECT * FROM feedback
204
+ WHERE provider_agent = ?
205
+ AND timestamp >= datetime('now', ? || ' days')
206
+ ORDER BY timestamp DESC
207
+ `).all(providerAgent, `-${sinceDays}`);
208
+ } else {
209
+ rows = db.prepare(`
210
+ SELECT * FROM feedback
211
+ WHERE provider_agent = ?
212
+ ORDER BY timestamp DESC
213
+ `).all(providerAgent);
214
+ }
215
+ return rows.map(rowToFeedback);
216
+ }
217
+ function rowToFeedback(row) {
218
+ return {
219
+ transaction_id: row["transaction_id"],
220
+ provider_agent: row["provider_agent"],
221
+ skill_id: row["skill_id"],
222
+ requester_agent: row["requester_agent"],
223
+ rating: row["rating"],
224
+ latency_ms: row["latency_ms"],
225
+ result_quality: row["result_quality"],
226
+ quality_details: row["quality_details"] ?? void 0,
227
+ would_reuse: row["would_reuse"] === 1,
228
+ cost_value_ratio: row["cost_value_ratio"],
229
+ timestamp: row["timestamp"]
230
+ };
231
+ }
232
+
233
+ // src/evolution/store.ts
234
+ import { randomUUID as randomUUID2 } from "crypto";
135
235
  function initEvolutionTable(db) {
136
236
  db.exec(`
137
237
  CREATE TABLE IF NOT EXISTS evolution_versions (
@@ -151,7 +251,7 @@ function initEvolutionTable(db) {
151
251
  `);
152
252
  }
153
253
  function insertEvolution(db, ev) {
154
- const id = randomUUID();
254
+ const id = randomUUID2();
155
255
  const now = (/* @__PURE__ */ new Date()).toISOString();
156
256
  db.prepare(`
157
257
  INSERT INTO evolution_versions (
@@ -233,6 +333,17 @@ var V2_FTS_TRIGGERS = `
233
333
  FROM json_each(json_extract(new.data, '$.metadata.tags'))),
234
334
  ''
235
335
  )
336
+ || ' ' || COALESCE(
337
+ (SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
338
+ FROM json_each(json_extract(new.data, '$.skills')) AS skill),
339
+ ''
340
+ )
341
+ || ' ' || COALESCE(
342
+ (SELECT group_concat(cap_type.value, ' ')
343
+ FROM json_each(json_extract(new.data, '$.skills')) AS skill,
344
+ json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
345
+ ''
346
+ )
236
347
  );
237
348
  END;
238
349
 
@@ -262,6 +373,17 @@ var V2_FTS_TRIGGERS = `
262
373
  FROM json_each(json_extract(old.data, '$.metadata.tags'))),
263
374
  ''
264
375
  )
376
+ || ' ' || COALESCE(
377
+ (SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
378
+ FROM json_each(json_extract(old.data, '$.skills')) AS skill),
379
+ ''
380
+ )
381
+ || ' ' || COALESCE(
382
+ (SELECT group_concat(cap_type.value, ' ')
383
+ FROM json_each(json_extract(old.data, '$.skills')) AS skill,
384
+ json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
385
+ ''
386
+ )
265
387
  );
266
388
  INSERT INTO cards_fts(rowid, id, owner, name, description, tags)
267
389
  VALUES (
@@ -287,6 +409,17 @@ var V2_FTS_TRIGGERS = `
287
409
  FROM json_each(json_extract(new.data, '$.metadata.tags'))),
288
410
  ''
289
411
  )
412
+ || ' ' || COALESCE(
413
+ (SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
414
+ FROM json_each(json_extract(new.data, '$.skills')) AS skill),
415
+ ''
416
+ )
417
+ || ' ' || COALESCE(
418
+ (SELECT group_concat(cap_type.value, ' ')
419
+ FROM json_each(json_extract(new.data, '$.skills')) AS skill,
420
+ json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
421
+ ''
422
+ )
290
423
  );
291
424
  END;
292
425
 
@@ -316,6 +449,17 @@ var V2_FTS_TRIGGERS = `
316
449
  FROM json_each(json_extract(old.data, '$.metadata.tags'))),
317
450
  ''
318
451
  )
452
+ || ' ' || COALESCE(
453
+ (SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
454
+ FROM json_each(json_extract(old.data, '$.skills')) AS skill),
455
+ ''
456
+ )
457
+ || ' ' || COALESCE(
458
+ (SELECT group_concat(cap_type.value, ' ')
459
+ FROM json_each(json_extract(old.data, '$.skills')) AS skill,
460
+ json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
461
+ ''
462
+ )
319
463
  );
320
464
  END;
321
465
  `;
@@ -420,10 +564,17 @@ function migrateV1toV2(db) {
420
564
  if (skills.length > 0) {
421
565
  name = skills.map((s) => String(s["name"] ?? "")).join(" ");
422
566
  description = skills.map((s) => String(s["description"] ?? "")).join(" ");
423
- tags = skills.flatMap((s) => {
424
- const meta = s["metadata"];
425
- return meta?.["tags"] ?? [];
426
- }).join(" ");
567
+ tags = [
568
+ // tags from metadata.tags[]
569
+ ...skills.flatMap((s) => {
570
+ const meta = s["metadata"];
571
+ return meta?.["tags"] ?? [];
572
+ }),
573
+ // capability_type (singular)
574
+ ...skills.map((s) => s["capability_type"]).filter((v) => typeof v === "string" && v.length > 0),
575
+ // capability_types[] (plural)
576
+ ...skills.flatMap((s) => s["capability_types"] ?? [])
577
+ ].join(" ");
427
578
  } else {
428
579
  name = String(data["name"] ?? "");
429
580
  description = String(data["description"] ?? "");
@@ -562,366 +713,271 @@ function listCards(db, owner) {
562
713
  }
563
714
  return rows.map((row) => JSON.parse(row.data));
564
715
  }
716
+ function getCardsByCapabilityType(db, capabilityType) {
717
+ const rows = db.prepare(
718
+ "SELECT data FROM capability_cards WHERE json_extract(data, '$.capability_type') = ?"
719
+ ).all(capabilityType);
720
+ return rows.map((row) => JSON.parse(row.data));
721
+ }
722
+ function getCardsBySkillCapability(db, capabilityType) {
723
+ const rows = db.prepare("SELECT data FROM capability_cards").all();
724
+ return rows.map((row) => JSON.parse(row.data)).filter((card) => {
725
+ const skills = card["skills"];
726
+ if (!skills) return false;
727
+ return skills.some((skill) => {
728
+ if (skill["capability_type"] === capabilityType) return true;
729
+ const types = skill["capability_types"];
730
+ return Array.isArray(types) && types.includes(capabilityType);
731
+ });
732
+ });
733
+ }
734
+
735
+ // src/credit/ledger.ts
736
+ import Database2 from "better-sqlite3";
737
+ import { randomUUID as randomUUID3 } from "crypto";
738
+ var CREDIT_SCHEMA = `
739
+ CREATE TABLE IF NOT EXISTS credit_balances (
740
+ owner TEXT PRIMARY KEY,
741
+ balance INTEGER NOT NULL DEFAULT 0,
742
+ updated_at TEXT NOT NULL
743
+ );
744
+
745
+ CREATE TABLE IF NOT EXISTS credit_transactions (
746
+ id TEXT PRIMARY KEY,
747
+ owner TEXT NOT NULL,
748
+ amount INTEGER NOT NULL,
749
+ reason TEXT NOT NULL,
750
+ reference_id TEXT,
751
+ created_at TEXT NOT NULL
752
+ );
565
753
 
566
- // src/credit/settlement.ts
567
- function settleProviderEarning(providerDb, providerOwner, receipt) {
568
- recordEarning(
569
- providerDb,
570
- providerOwner,
571
- receipt.amount,
572
- receipt.card_id,
573
- receipt.nonce
754
+ CREATE TABLE IF NOT EXISTS credit_escrow (
755
+ id TEXT PRIMARY KEY,
756
+ owner TEXT NOT NULL,
757
+ amount INTEGER NOT NULL,
758
+ card_id TEXT NOT NULL,
759
+ status TEXT NOT NULL DEFAULT 'held',
760
+ created_at TEXT NOT NULL,
761
+ settled_at TEXT
574
762
  );
575
- return { settled: true };
763
+
764
+ CREATE INDEX IF NOT EXISTS idx_transactions_owner ON credit_transactions(owner, created_at);
765
+ CREATE INDEX IF NOT EXISTS idx_escrow_owner ON credit_escrow(owner);
766
+ `;
767
+ function openCreditDb(path = ":memory:") {
768
+ const db = new Database2(path);
769
+ db.pragma("journal_mode = WAL");
770
+ db.pragma("foreign_keys = ON");
771
+ db.exec(CREDIT_SCHEMA);
772
+ return db;
576
773
  }
577
- function settleRequesterEscrow(requesterDb, escrowId) {
578
- confirmEscrowDebit(requesterDb, escrowId);
774
+ function bootstrapAgent(db, owner, amount = 100) {
775
+ const now = (/* @__PURE__ */ new Date()).toISOString();
776
+ db.transaction(() => {
777
+ const result = db.prepare("INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, ?, ?)").run(owner, amount, now);
778
+ if (result.changes > 0) {
779
+ db.prepare(
780
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
781
+ ).run(randomUUID3(), owner, amount, "bootstrap", null, now);
782
+ }
783
+ })();
579
784
  }
580
- function releaseRequesterEscrow(requesterDb, escrowId) {
581
- releaseEscrow(requesterDb, escrowId);
785
+ function getBalance(db, owner) {
786
+ const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
787
+ return row?.balance ?? 0;
582
788
  }
583
-
584
- // src/gateway/execute.ts
585
- async function executeCapabilityRequest(opts) {
586
- const {
587
- registryDb,
588
- creditDb,
589
- cardId,
590
- skillId,
591
- params,
592
- requester,
593
- escrowReceipt: receipt,
594
- skillExecutor,
595
- handlerUrl,
596
- timeoutMs = 3e5,
597
- onProgress,
598
- relayAuthorized = false
599
- } = opts;
600
- const card = getCard(registryDb, cardId);
601
- if (!card) {
602
- return { success: false, error: { code: -32602, message: `Card not found: ${cardId}` } };
603
- }
604
- if (requester === card.owner && !relayAuthorized) {
605
- const msg = `Self-request blocked: requester (${requester}) is the card owner. Set AGENTBNB_DIR to your agent's config directory before calling agentbnb request.`;
606
- try {
607
- insertRequestLog(registryDb, {
608
- id: randomUUID2(),
609
- card_id: cardId,
610
- card_name: card.name,
611
- skill_id: skillId,
612
- requester,
613
- status: "failure",
614
- latency_ms: 0,
615
- credits_charged: 0,
616
- created_at: (/* @__PURE__ */ new Date()).toISOString()
617
- });
618
- } catch {
789
+ function getTransactions(db, owner, limit = 100) {
790
+ return db.prepare(
791
+ "SELECT id, owner, amount, reason, reference_id, created_at FROM credit_transactions WHERE owner = ? ORDER BY created_at DESC LIMIT ?"
792
+ ).all(owner, limit);
793
+ }
794
+ function recordEarning(db, owner, amount, _cardId, receiptNonce) {
795
+ const now = (/* @__PURE__ */ new Date()).toISOString();
796
+ db.transaction(() => {
797
+ const existing = db.prepare(
798
+ "SELECT id FROM credit_transactions WHERE reference_id = ? AND reason = 'remote_earning'"
799
+ ).get(receiptNonce);
800
+ if (existing) return;
801
+ db.prepare(
802
+ "INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
803
+ ).run(owner, now);
804
+ db.prepare(
805
+ "UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
806
+ ).run(amount, now, owner);
807
+ db.prepare(
808
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
809
+ ).run(randomUUID3(), owner, amount, "remote_earning", receiptNonce, now);
810
+ })();
811
+ }
812
+ function migrateOwner(db, oldOwner, newOwner) {
813
+ if (oldOwner === newOwner) return;
814
+ const now = (/* @__PURE__ */ new Date()).toISOString();
815
+ db.transaction(() => {
816
+ const oldRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(oldOwner);
817
+ if (!oldRow) return;
818
+ const newRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(newOwner);
819
+ if (newRow) {
820
+ db.prepare("UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?").run(oldRow.balance, now, newOwner);
821
+ } else {
822
+ db.prepare("UPDATE credit_balances SET owner = ?, updated_at = ? WHERE owner = ?").run(newOwner, now, oldOwner);
619
823
  }
620
- return { success: false, error: { code: -32603, message: msg } };
621
- }
622
- let creditsNeeded;
623
- let cardName;
624
- let resolvedSkillId;
625
- const rawCard = card;
626
- if (Array.isArray(rawCard["skills"])) {
627
- const v2card = card;
628
- const skill = skillId ? v2card.skills.find((s) => s.id === skillId) : v2card.skills[0];
629
- if (!skill) {
630
- return { success: false, error: { code: -32602, message: `Skill not found: ${skillId}` } };
824
+ if (newRow) {
825
+ db.prepare("DELETE FROM credit_balances WHERE owner = ?").run(oldOwner);
631
826
  }
632
- creditsNeeded = skill.pricing.credits_per_call;
633
- cardName = skill.name;
634
- resolvedSkillId = skill.id;
635
- } else {
636
- creditsNeeded = card.pricing.credits_per_call;
637
- cardName = card.name;
638
- }
639
- let escrowId = null;
640
- let isRemoteEscrow = false;
641
- if (relayAuthorized) {
642
- } else if (receipt) {
643
- const { signature, ...receiptData2 } = receipt;
644
- const publicKeyBuf = Buffer.from(receipt.requester_public_key, "hex");
645
- const valid = verifyEscrowReceipt(receiptData2, signature, publicKeyBuf);
646
- if (!valid) {
647
- return { success: false, error: { code: -32603, message: "Invalid escrow receipt signature" } };
827
+ db.prepare("UPDATE credit_transactions SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
828
+ db.prepare("UPDATE credit_escrow SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
829
+ })();
830
+ }
831
+
832
+ // src/credit/escrow.ts
833
+ import { randomUUID as randomUUID4 } from "crypto";
834
+ function holdEscrow(db, owner, amount, cardId) {
835
+ const escrowId = randomUUID4();
836
+ const now = (/* @__PURE__ */ new Date()).toISOString();
837
+ const hold = db.transaction(() => {
838
+ const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
839
+ if (!row || row.balance < amount) {
840
+ throw new AgentBnBError("Insufficient credits", "INSUFFICIENT_CREDITS");
648
841
  }
649
- if (receipt.amount < creditsNeeded) {
650
- return { success: false, error: { code: -32603, message: "Insufficient escrow amount" } };
842
+ db.prepare(
843
+ "UPDATE credit_balances SET balance = balance - ?, updated_at = ? WHERE owner = ? AND balance >= ?"
844
+ ).run(amount, now, owner, amount);
845
+ db.prepare(
846
+ "INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at) VALUES (?, ?, ?, ?, ?, ?)"
847
+ ).run(escrowId, owner, amount, cardId, "held", now);
848
+ db.prepare(
849
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
850
+ ).run(randomUUID4(), owner, -amount, "escrow_hold", escrowId, now);
851
+ });
852
+ hold();
853
+ return escrowId;
854
+ }
855
+ function settleEscrow(db, escrowId, recipientOwner) {
856
+ const now = (/* @__PURE__ */ new Date()).toISOString();
857
+ const settle = db.transaction(() => {
858
+ const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
859
+ if (!escrow) {
860
+ throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
651
861
  }
652
- const receiptAge = Date.now() - new Date(receipt.timestamp).getTime();
653
- if (receiptAge > 5 * 60 * 1e3) {
654
- return { success: false, error: { code: -32603, message: "Escrow receipt expired" } };
862
+ if (escrow.status !== "held") {
863
+ throw new AgentBnBError(
864
+ `Escrow ${escrowId} is already ${escrow.status}`,
865
+ "ESCROW_ALREADY_SETTLED"
866
+ );
655
867
  }
656
- isRemoteEscrow = true;
657
- } else {
658
- try {
659
- const balance = getBalance(creditDb, requester);
660
- if (balance < creditsNeeded) {
661
- return { success: false, error: { code: -32603, message: "Insufficient credits" } };
662
- }
663
- escrowId = holdEscrow(creditDb, requester, creditsNeeded, cardId);
664
- } catch (err) {
665
- const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
666
- return { success: false, error: { code: -32603, message: msg } };
868
+ db.prepare(
869
+ "INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
870
+ ).run(recipientOwner, now);
871
+ db.prepare(
872
+ "UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
873
+ ).run(escrow.amount, now, recipientOwner);
874
+ db.prepare(
875
+ "UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
876
+ ).run("settled", now, escrowId);
877
+ db.prepare(
878
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
879
+ ).run(randomUUID4(), recipientOwner, escrow.amount, "settlement", escrowId, now);
880
+ });
881
+ settle();
882
+ }
883
+ function releaseEscrow(db, escrowId) {
884
+ const now = (/* @__PURE__ */ new Date()).toISOString();
885
+ const release = db.transaction(() => {
886
+ const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
887
+ if (!escrow) {
888
+ throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
667
889
  }
668
- }
669
- const startMs = Date.now();
670
- const receiptData = isRemoteEscrow ? { receipt_released: true } : void 0;
671
- const handleFailure = (status, latencyMs, message) => {
672
- if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
673
- updateReputation(registryDb, cardId, false, latencyMs);
674
- try {
675
- insertRequestLog(registryDb, {
676
- id: randomUUID2(),
677
- card_id: cardId,
678
- card_name: cardName,
679
- skill_id: resolvedSkillId,
680
- requester,
681
- status,
682
- latency_ms: latencyMs,
683
- credits_charged: 0,
684
- created_at: (/* @__PURE__ */ new Date()).toISOString()
685
- });
686
- } catch {
890
+ if (escrow.status !== "held") {
891
+ throw new AgentBnBError(
892
+ `Escrow ${escrowId} is already ${escrow.status}`,
893
+ "ESCROW_ALREADY_SETTLED"
894
+ );
687
895
  }
688
- return {
689
- success: false,
690
- error: { code: -32603, message, ...receiptData ? { data: receiptData } : {} }
691
- };
692
- };
693
- const handleSuccess = (result, latencyMs) => {
694
- if (isRemoteEscrow && receipt) {
695
- settleProviderEarning(creditDb, card.owner, receipt);
696
- } else if (escrowId) {
697
- settleEscrow(creditDb, escrowId, card.owner);
896
+ db.prepare(
897
+ "UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
898
+ ).run(escrow.amount, now, escrow.owner);
899
+ db.prepare(
900
+ "UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
901
+ ).run("released", now, escrowId);
902
+ db.prepare(
903
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
904
+ ).run(randomUUID4(), escrow.owner, escrow.amount, "refund", escrowId, now);
905
+ });
906
+ release();
907
+ }
908
+ function confirmEscrowDebit(db, escrowId) {
909
+ const now = (/* @__PURE__ */ new Date()).toISOString();
910
+ const confirm = db.transaction(() => {
911
+ const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
912
+ if (!escrow) {
913
+ throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
698
914
  }
699
- updateReputation(registryDb, cardId, true, latencyMs);
700
- try {
701
- insertRequestLog(registryDb, {
702
- id: randomUUID2(),
703
- card_id: cardId,
704
- card_name: cardName,
705
- skill_id: resolvedSkillId,
706
- requester,
707
- status: "success",
708
- latency_ms: latencyMs,
709
- credits_charged: creditsNeeded,
710
- created_at: (/* @__PURE__ */ new Date()).toISOString()
711
- });
712
- } catch {
915
+ if (escrow.status !== "held") {
916
+ throw new AgentBnBError(
917
+ `Escrow ${escrowId} is already ${escrow.status}`,
918
+ "ESCROW_ALREADY_SETTLED"
919
+ );
713
920
  }
714
- const successResult = isRemoteEscrow ? {
715
- ...typeof result === "object" && result !== null ? result : { data: result },
716
- receipt_settled: true,
717
- receipt_nonce: receipt.nonce
718
- } : result;
719
- return { success: true, result: successResult };
921
+ db.prepare(
922
+ "UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
923
+ ).run("settled", now, escrowId);
924
+ db.prepare(
925
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
926
+ ).run(randomUUID4(), escrow.owner, 0, "remote_settlement_confirmed", escrowId, now);
927
+ });
928
+ confirm();
929
+ }
930
+
931
+ // src/credit/signing.ts
932
+ import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
933
+ import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
934
+ import { join } from "path";
935
+ function generateKeyPair() {
936
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
937
+ publicKeyEncoding: { type: "spki", format: "der" },
938
+ privateKeyEncoding: { type: "pkcs8", format: "der" }
939
+ });
940
+ return {
941
+ publicKey: Buffer.from(publicKey),
942
+ privateKey: Buffer.from(privateKey)
720
943
  };
721
- if (skillExecutor) {
722
- let targetSkillId = resolvedSkillId ?? skillId;
723
- if (!targetSkillId) {
724
- const available = skillExecutor.listSkills();
725
- if (available.length > 0) {
726
- targetSkillId = available[0];
727
- } else {
728
- return handleFailure(
729
- "failure",
730
- Date.now() - startMs,
731
- "No skill_id specified and no skills registered on this provider."
732
- );
733
- }
734
- }
735
- try {
736
- const execResult = await skillExecutor.execute(targetSkillId, params, onProgress);
737
- if (!execResult.success) {
738
- return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed");
739
- }
740
- return handleSuccess(execResult.result, execResult.latency_ms);
741
- } catch (err) {
742
- const message = err instanceof Error ? err.message : "Execution error";
743
- return handleFailure("failure", Date.now() - startMs, message);
744
- }
745
- }
746
- if (!handlerUrl) {
747
- return handleFailure("failure", Date.now() - startMs, "No skill executor or handler URL configured");
748
- }
749
- const controller = new AbortController();
750
- const timer = setTimeout(() => controller.abort(), timeoutMs);
751
- try {
752
- const response = await fetch(handlerUrl, {
753
- method: "POST",
754
- headers: { "Content-Type": "application/json" },
755
- body: JSON.stringify({ card_id: cardId, skill_id: resolvedSkillId, params }),
756
- signal: controller.signal
757
- });
758
- clearTimeout(timer);
759
- if (!response.ok) {
760
- return handleFailure("failure", Date.now() - startMs, `Handler returned ${response.status}`);
761
- }
762
- const result = await response.json();
763
- return handleSuccess(result, Date.now() - startMs);
764
- } catch (err) {
765
- clearTimeout(timer);
766
- const isTimeout = err instanceof Error && err.name === "AbortError";
767
- return handleFailure(
768
- isTimeout ? "timeout" : "failure",
769
- Date.now() - startMs,
770
- isTimeout ? "Execution timeout" : "Handler error"
771
- );
772
- }
773
944
  }
774
- async function executeCapabilityBatch(options) {
775
- const { requests, strategy, total_budget, registryDb, creditDb, owner } = options;
776
- if (requests.length === 0) {
777
- return { results: [], total_credits_spent: 0, total_credits_refunded: 0, success: true };
778
- }
779
- const sumMaxCredits = requests.reduce((acc, r) => acc + r.max_credits, 0);
780
- if (sumMaxCredits > total_budget) {
781
- return {
782
- results: requests.map((_, i) => ({
783
- request_index: i,
784
- status: "skipped",
785
- credits_spent: 0,
786
- credits_refunded: 0,
787
- error: `Total requested credits (${sumMaxCredits}) exceeds total_budget (${total_budget})`
788
- })),
789
- total_credits_spent: 0,
790
- total_credits_refunded: 0,
791
- success: false
792
- };
945
+ function saveKeyPair(configDir, keys) {
946
+ const privatePath = join(configDir, "private.key");
947
+ const publicPath = join(configDir, "public.key");
948
+ writeFileSync(privatePath, keys.privateKey);
949
+ chmodSync(privatePath, 384);
950
+ writeFileSync(publicPath, keys.publicKey);
951
+ }
952
+ function loadKeyPair(configDir) {
953
+ const privatePath = join(configDir, "private.key");
954
+ const publicPath = join(configDir, "public.key");
955
+ if (!existsSync(privatePath) || !existsSync(publicPath)) {
956
+ throw new AgentBnBError("Keypair not found. Run `agentbnb init` to generate one.", "KEYPAIR_NOT_FOUND");
793
957
  }
794
- const executeItem = async (item, index) => {
795
- const card = getCard(registryDb, item.skill_id);
796
- if (!card) {
797
- return {
798
- request_index: index,
799
- status: "failed",
800
- credits_spent: 0,
801
- credits_refunded: 0,
802
- error: `Card/skill not found: ${item.skill_id}`
803
- };
804
- }
805
- const rawCard = card;
806
- let creditsNeeded;
807
- let resolvedSkillId;
808
- if (Array.isArray(rawCard["skills"])) {
809
- const v2card = card;
810
- const skill = v2card.skills[0];
811
- if (!skill) {
812
- return {
813
- request_index: index,
814
- status: "failed",
815
- credits_spent: 0,
816
- credits_refunded: 0,
817
- error: `No skills defined on card: ${item.skill_id}`
818
- };
819
- }
820
- creditsNeeded = skill.pricing.credits_per_call;
821
- resolvedSkillId = skill.id;
822
- } else {
823
- creditsNeeded = card.pricing.credits_per_call;
824
- }
825
- if (creditsNeeded > item.max_credits) {
826
- return {
827
- request_index: index,
828
- status: "failed",
829
- credits_spent: 0,
830
- credits_refunded: 0,
831
- error: `Skill costs ${creditsNeeded} credits but max_credits is ${item.max_credits}`
832
- };
833
- }
834
- let escrowId;
835
- try {
836
- const balance = getBalance(creditDb, owner);
837
- if (balance < creditsNeeded) {
838
- return {
839
- request_index: index,
840
- status: "failed",
841
- credits_spent: 0,
842
- credits_refunded: 0,
843
- error: "Insufficient credits"
844
- };
845
- }
846
- escrowId = holdEscrow(creditDb, owner, creditsNeeded, card.id);
847
- } catch (err) {
848
- const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
849
- return {
850
- request_index: index,
851
- status: "failed",
852
- credits_spent: 0,
853
- credits_refunded: 0,
854
- error: msg
855
- };
856
- }
857
- const startMs = Date.now();
858
- const latencyMs = Date.now() - startMs;
859
- settleEscrow(creditDb, escrowId, card.owner);
860
- updateReputation(registryDb, card.id, true, latencyMs);
861
- try {
862
- insertRequestLog(registryDb, {
863
- id: randomUUID2(),
864
- card_id: card.id,
865
- card_name: card.name,
866
- skill_id: resolvedSkillId,
867
- requester: owner,
868
- status: "success",
869
- latency_ms: latencyMs,
870
- credits_charged: creditsNeeded,
871
- created_at: (/* @__PURE__ */ new Date()).toISOString()
872
- });
873
- } catch {
874
- }
875
- return {
876
- request_index: index,
877
- status: "success",
878
- result: { card_id: card.id, skill_id: resolvedSkillId },
879
- credits_spent: creditsNeeded,
880
- credits_refunded: 0
881
- };
958
+ return {
959
+ publicKey: readFileSync(publicPath),
960
+ privateKey: readFileSync(privatePath)
882
961
  };
883
- let results;
884
- if (strategy === "sequential") {
885
- results = [];
886
- let stopped = false;
887
- for (let i = 0; i < requests.length; i++) {
888
- if (stopped) {
889
- results.push({
890
- request_index: i,
891
- status: "skipped",
892
- credits_spent: 0,
893
- credits_refunded: 0,
894
- error: "Skipped due to earlier failure"
895
- });
896
- continue;
897
- }
898
- const result = await executeItem(requests[i], i);
899
- results.push(result);
900
- if (result.status === "failed") {
901
- stopped = true;
902
- }
903
- }
904
- } else {
905
- const settled = await Promise.allSettled(
906
- requests.map((item, i) => executeItem(item, i))
907
- );
908
- results = settled.map((outcome, i) => {
909
- if (outcome.status === "fulfilled") {
910
- return outcome.value;
911
- }
912
- return {
913
- request_index: i,
914
- status: "failed",
915
- credits_spent: 0,
916
- credits_refunded: 0,
917
- error: outcome.reason instanceof Error ? outcome.reason.message : "Unknown error"
918
- };
919
- });
962
+ }
963
+ function canonicalJson(data) {
964
+ return JSON.stringify(data, Object.keys(data).sort());
965
+ }
966
+ function signEscrowReceipt(data, privateKey) {
967
+ const message = Buffer.from(canonicalJson(data), "utf-8");
968
+ const keyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
969
+ const signature = sign(null, message, keyObject);
970
+ return signature.toString("base64url");
971
+ }
972
+ function verifyEscrowReceipt(data, signature, publicKey) {
973
+ try {
974
+ const message = Buffer.from(canonicalJson(data), "utf-8");
975
+ const keyObject = createPublicKey({ key: publicKey, format: "der", type: "spki" });
976
+ const sigBuffer = Buffer.from(signature, "base64url");
977
+ return verify(null, message, keyObject, sigBuffer);
978
+ } catch {
979
+ return false;
920
980
  }
921
- const total_credits_spent = results.reduce((acc, r) => acc + r.credits_spent, 0);
922
- const total_credits_refunded = results.reduce((acc, r) => acc + r.credits_refunded, 0);
923
- const success = results.every((r) => r.status === "success");
924
- return { results, total_credits_spent, total_credits_refunded, success };
925
981
  }
926
982
 
927
983
  export {
@@ -929,6 +985,9 @@ export {
929
985
  getSkillRequestCount,
930
986
  getActivityFeed,
931
987
  getRequestLog,
988
+ insertFeedback,
989
+ getFeedbackForSkill,
990
+ getFeedbackForProvider,
932
991
  insertEvolution,
933
992
  getLatestEvolution,
934
993
  getEvolutionHistory,
@@ -936,11 +995,25 @@ export {
936
995
  insertCard,
937
996
  getCard,
938
997
  updateCard,
998
+ updateReputation,
939
999
  updateSkillAvailability,
940
1000
  updateSkillIdleRate,
941
1001
  listCards,
942
- settleRequesterEscrow,
943
- releaseRequesterEscrow,
944
- executeCapabilityRequest,
945
- executeCapabilityBatch
1002
+ getCardsByCapabilityType,
1003
+ getCardsBySkillCapability,
1004
+ openCreditDb,
1005
+ bootstrapAgent,
1006
+ getBalance,
1007
+ getTransactions,
1008
+ recordEarning,
1009
+ migrateOwner,
1010
+ holdEscrow,
1011
+ settleEscrow,
1012
+ releaseEscrow,
1013
+ confirmEscrowDebit,
1014
+ generateKeyPair,
1015
+ saveKeyPair,
1016
+ loadKeyPair,
1017
+ signEscrowReceipt,
1018
+ verifyEscrowReceipt
946
1019
  };