agentbnb 8.2.2 → 8.3.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 (62) hide show
  1. package/dist/{card-EX2EYGCZ.js → card-BN643ZOY.js} +6 -2
  2. package/dist/card-T2XJZA5A.js +92 -0
  3. package/dist/{chunk-3LWBH7P3.js → chunk-4NFJ3VYZ.js} +20 -1
  4. package/dist/chunk-5AIYALBX.js +857 -0
  5. package/dist/chunk-6QMDJVMS.js +238 -0
  6. package/dist/{chunk-LKLKYXLV.js → chunk-74LZDEDT.js} +6 -4
  7. package/dist/{chunk-GKVTD4EZ.js → chunk-77KGEDH4.js} +1 -1
  8. package/dist/{chunk-QCGIG7WW.js → chunk-7IQE34QK.js} +14 -7
  9. package/dist/{chunk-QHZGOG3O.js → chunk-D242QZCR.js} +168 -41
  10. package/dist/chunk-EE3V3DXK.js +60 -0
  11. package/dist/{chunk-RYISHSHB.js → chunk-F3KIEVJ2.js} +207 -265
  12. package/dist/{chunk-XBGVQMQJ.js → chunk-FELGHDCA.js} +16 -39
  13. package/dist/{chunk-EJKW57ZV.js → chunk-GIEJVKZZ.js} +1 -1
  14. package/dist/{chunk-WVY2W7AA.js → chunk-I7KWA7OB.js} +20 -0
  15. package/dist/{chunk-4IPJJRTP.js → chunk-IGQNP3ZO.js} +5 -2
  16. package/dist/chunk-NQANA6WH.js +797 -0
  17. package/dist/{chunk-Z4MCGKTL.js → chunk-NX27AFPA.js} +15 -2
  18. package/dist/{chunk-Z2GEFFDO.js → chunk-O4Q7BRG6.js} +2 -2
  19. package/dist/{chunk-SSK653A6.js → chunk-PQIP7EXY.js} +6 -0
  20. package/dist/{chunk-EG6RS4JC.js → chunk-QFPXZITP.js} +20 -65
  21. package/dist/chunk-R4F4XII4.js +264 -0
  22. package/dist/{chunk-DYQOFGGI.js → chunk-RVBW2QXU.js} +178 -49
  23. package/dist/{chunk-CQFBNTGT.js → chunk-S7DZHKCG.js} +25 -12
  24. package/dist/chunk-U6LP4KWN.js +238 -0
  25. package/dist/{chunk-MWOXW7JQ.js → chunk-VJ7XBEY6.js} +24 -16
  26. package/dist/chunk-WTHMHNKC.js +129 -0
  27. package/dist/{chunk-OCSU2S6W.js → chunk-WX3GZVFG.js} +2 -1
  28. package/dist/{chunk-CKOOVZOI.js → chunk-YKMBFQC2.js} +37 -5
  29. package/dist/{chunk-S3V6R3EN.js → chunk-ZU2TP7CN.js} +70 -27
  30. package/dist/cli/index.js +211 -278
  31. package/dist/client-OKJJ3UP2.js +19 -0
  32. package/dist/client-UQBGCIPA.js +20 -0
  33. package/dist/conduct-4JDMWBQD.js +22 -0
  34. package/dist/{conduct-AZFLNUX3.js → conduct-VYYBCPHA.js} +14 -13
  35. package/dist/{conductor-mode-WKB42PYM.js → conductor-mode-OPGQJFLA.js} +12 -8
  36. package/dist/{conductor-mode-PLTB6MS3.js → conductor-mode-SBDCRIX6.js} +16 -11
  37. package/dist/execute-FZLQGIXB.js +14 -0
  38. package/dist/execute-TEZPQ5WP.js +15 -0
  39. package/dist/index.d.ts +172 -11
  40. package/dist/index.js +1529 -433
  41. package/dist/{process-guard-GH5LRNWO.js → process-guard-TNSUNHSR.js} +1 -1
  42. package/dist/{publish-capability-QDR2QIZ2.js → publish-capability-HVYILTPR.js} +4 -3
  43. package/dist/{reliability-metrics-QG7WC5QK.js → reliability-metrics-G7LPUYJD.js} +3 -1
  44. package/dist/reliability-metrics-RRUKJ4ME.js +20 -0
  45. package/dist/{request-NX7GSPIG.js → request-KJNKR27T.js} +96 -43
  46. package/dist/{serve-skill-E6EJQYAK.js → serve-skill-GC6NIQ5T.js} +10 -11
  47. package/dist/{server-VBCT32FC.js → server-YV3XPTX5.js} +11 -10
  48. package/dist/{service-coordinator-KMSA6BST.js → service-coordinator-RY5AKUZS.js} +420 -171
  49. package/dist/{skill-config-FETXPNVP.js → skill-config-5O2VR546.js} +1 -1
  50. package/dist/skills/agentbnb/bootstrap.js +550 -231
  51. package/dist/websocket-client-3U27WJUU.js +7 -0
  52. package/dist/{websocket-client-4Z5P54RU.js → websocket-client-SNDF3B6N.js} +1 -1
  53. package/package.json +18 -12
  54. package/skills/agentbnb/install.sh +0 -0
  55. package/dist/chunk-MCED4GDW.js +0 -1572
  56. package/dist/chunk-NWIQJ2CL.js +0 -108
  57. package/dist/chunk-WNXXLCV5.js +0 -32
  58. package/dist/client-XOLP5IUZ.js +0 -12
  59. package/dist/conduct-VPUYTNEA.js +0 -21
  60. package/dist/execute-NNDCXTN4.js +0 -13
  61. package/dist/execute-RIRHTIBU.js +0 -16
  62. package/dist/websocket-client-QOVARTRN.js +0 -7
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeCapabilityBatch,
3
3
  executeCapabilityRequest
4
- } from "./chunk-XBGVQMQJ.js";
4
+ } from "./chunk-FELGHDCA.js";
5
5
  import {
6
6
  StructuredFeedbackSchema
7
7
  } from "./chunk-AUBHR7HH.js";
@@ -16,21 +16,20 @@ import {
16
16
  getPricingStats,
17
17
  resolveSelfCli,
18
18
  stopAnnouncement
19
- } from "./chunk-Z2GEFFDO.js";
19
+ } from "./chunk-O4Q7BRG6.js";
20
20
  import {
21
21
  createLedger,
22
22
  deriveAgentId,
23
23
  identityAuthPlugin
24
- } from "./chunk-LKLKYXLV.js";
25
- import "./chunk-WNXXLCV5.js";
24
+ } from "./chunk-74LZDEDT.js";
26
25
  import {
27
26
  DEFAULT_AUTONOMY_CONFIG,
28
27
  getAutonomyTier,
29
28
  insertAuditEvent,
30
29
  listPendingRequests,
31
30
  resolvePendingRequest
32
- } from "./chunk-GKVTD4EZ.js";
33
- import "./chunk-CQFBNTGT.js";
31
+ } from "./chunk-77KGEDH4.js";
32
+ import "./chunk-S7DZHKCG.js";
34
33
  import {
35
34
  bootstrapAgent,
36
35
  buildReputationMap,
@@ -39,24 +38,28 @@ import {
39
38
  getBalance,
40
39
  getTransactions,
41
40
  holdEscrow,
41
+ markEscrowAbandoned,
42
+ markEscrowProgressing,
43
+ markEscrowStarted,
42
44
  migrateOwner,
43
45
  openCreditDb,
44
46
  releaseEscrow,
45
47
  searchCards,
46
48
  settleEscrow
47
- } from "./chunk-RYISHSHB.js";
48
- import "./chunk-NWIQJ2CL.js";
49
+ } from "./chunk-F3KIEVJ2.js";
50
+ import {
51
+ RelayMessageSchema
52
+ } from "./chunk-PQIP7EXY.js";
49
53
  import {
50
54
  generateKeyPair,
51
55
  verifyEscrowReceipt
52
- } from "./chunk-EJKW57ZV.js";
56
+ } from "./chunk-GIEJVKZZ.js";
57
+ import "./chunk-U6LP4KWN.js";
53
58
  import {
54
59
  getConfigDir
55
60
  } from "./chunk-75OC6E4F.js";
56
61
  import {
57
- RelayMessageSchema
58
- } from "./chunk-SSK653A6.js";
59
- import {
62
+ attachCanonicalAgentId,
60
63
  getActivityFeed,
61
64
  getCard,
62
65
  getCardsBySkillCapability,
@@ -75,20 +78,24 @@ import {
75
78
  updateCard,
76
79
  updateSkillAvailability,
77
80
  updateSkillIdleRate
78
- } from "./chunk-S3V6R3EN.js";
81
+ } from "./chunk-5AIYALBX.js";
82
+ import "./chunk-WTHMHNKC.js";
79
83
  import {
80
84
  AgentBnBError,
81
85
  AnyCardSchema
82
- } from "./chunk-WVY2W7AA.js";
86
+ } from "./chunk-I7KWA7OB.js";
83
87
  import {
84
88
  ApiSkillConfigSchema,
85
89
  parseSkillsFile
86
- } from "./chunk-OCSU2S6W.js";
90
+ } from "./chunk-WX3GZVFG.js";
87
91
 
88
92
  // src/runtime/agent-runtime.ts
89
93
  import { readFileSync, existsSync } from "fs";
90
94
 
91
95
  // src/skills/executor.ts
96
+ function buildTimeoutError(skillId, timeoutMs) {
97
+ return `Skill "${skillId}" timed out after ${timeoutMs}ms`;
98
+ }
92
99
  var SkillExecutor = class {
93
100
  skillMap;
94
101
  modeMap;
@@ -147,7 +154,20 @@ var SkillExecutor = class {
147
154
  this.concurrencyGuard.acquire(skillId);
148
155
  }
149
156
  try {
150
- const modeResult = await mode.execute(config, params, onProgress);
157
+ const configuredTimeoutMs = typeof config.timeout_ms === "number" ? config.timeout_ms : void 0;
158
+ const modeExecution = mode.execute(config, params, onProgress);
159
+ const modeResult = configuredTimeoutMs === void 0 ? await modeExecution : await new Promise((resolve, reject) => {
160
+ const timeout = setTimeout(() => {
161
+ reject(new Error(buildTimeoutError(skillId, configuredTimeoutMs)));
162
+ }, configuredTimeoutMs);
163
+ modeExecution.then((value) => {
164
+ clearTimeout(timeout);
165
+ resolve(value);
166
+ }).catch((err) => {
167
+ clearTimeout(timeout);
168
+ reject(err);
169
+ });
170
+ });
151
171
  return {
152
172
  ...modeResult,
153
173
  latency_ms: Date.now() - startTime
@@ -345,6 +365,15 @@ var ApiExecutor = class {
345
365
  import { execFile } from "child_process";
346
366
  import { promisify } from "util";
347
367
  var execFileAsync = promisify(execFile);
368
+ function isTimedOutCommandError(err) {
369
+ if (!(err instanceof Error)) {
370
+ return false;
371
+ }
372
+ const timeoutCode = err.code;
373
+ const signal = err.signal;
374
+ const killed = err.killed;
375
+ return timeoutCode === "ETIMEDOUT" || err.message.includes("timed out") || killed === true && typeof signal === "string";
376
+ }
348
377
  function shellEscape(value) {
349
378
  return "'" + value.replace(/'/g, "'\\''") + "'";
350
379
  }
@@ -396,6 +425,8 @@ var PipelineExecutor = class {
396
425
  async execute(config, params, onProgress) {
397
426
  const pipelineConfig = config;
398
427
  const steps = pipelineConfig.steps ?? [];
428
+ const pipelineTimeoutMs = pipelineConfig.timeout_ms;
429
+ const deadline = typeof pipelineTimeoutMs === "number" ? Date.now() + pipelineTimeoutMs : void 0;
399
430
  if (steps.length === 0) {
400
431
  return { success: true, result: null };
401
432
  }
@@ -417,11 +448,41 @@ var PipelineExecutor = class {
417
448
  context
418
449
  );
419
450
  let stepResult;
451
+ const runWithRemainingDeadline = async (operation) => {
452
+ if (deadline === void 0) {
453
+ return operation();
454
+ }
455
+ const remainingMs = deadline - Date.now();
456
+ if (remainingMs <= 0) {
457
+ throw new Error(`pipeline timed out after ${pipelineTimeoutMs}ms`);
458
+ }
459
+ return new Promise((resolve, reject) => {
460
+ const timeout = setTimeout(() => {
461
+ reject(new Error(`pipeline timed out after ${pipelineTimeoutMs}ms`));
462
+ }, remainingMs);
463
+ operation().then((value) => {
464
+ clearTimeout(timeout);
465
+ resolve(value);
466
+ }).catch((err) => {
467
+ clearTimeout(timeout);
468
+ reject(err);
469
+ });
470
+ });
471
+ };
420
472
  if ("skill_id" in step && step.skill_id) {
421
- const subResult = await this.skillExecutor.execute(
422
- step.skill_id,
423
- resolvedInputs
424
- );
473
+ let subResult;
474
+ try {
475
+ subResult = await runWithRemainingDeadline(() => this.skillExecutor.execute(
476
+ step.skill_id,
477
+ resolvedInputs
478
+ ));
479
+ } catch (err) {
480
+ const message = err instanceof Error ? err.message : String(err);
481
+ return {
482
+ success: false,
483
+ error: `Step ${i} failed: ${message}`
484
+ };
485
+ }
425
486
  if (!subResult.success) {
426
487
  return {
427
488
  success: false,
@@ -435,10 +496,16 @@ var PipelineExecutor = class {
435
496
  context
436
497
  );
437
498
  try {
438
- const { stdout } = await execFileAsync("/bin/sh", ["-c", interpolatedCommand], { timeout: 3e4 });
499
+ const remainingMs = deadline === void 0 ? void 0 : deadline - Date.now();
500
+ if (remainingMs !== void 0 && remainingMs <= 0) {
501
+ throw new Error(`pipeline timed out after ${pipelineTimeoutMs}ms`);
502
+ }
503
+ const { stdout } = await execFileAsync("/bin/sh", ["-c", interpolatedCommand], {
504
+ timeout: remainingMs !== void 0 ? remainingMs : 3e4
505
+ });
439
506
  stepResult = stdout.trim();
440
507
  } catch (err) {
441
- const message = err instanceof Error ? err.message : String(err);
508
+ const message = pipelineTimeoutMs !== void 0 && isTimedOutCommandError(err) ? `pipeline timed out after ${pipelineTimeoutMs}ms` : err instanceof Error ? err.message : String(err);
442
509
  return {
443
510
  success: false,
444
511
  error: `Step ${i} failed: ${message}`
@@ -525,17 +592,43 @@ function executeProcess(config, payload) {
525
592
  error: `Invalid agent name: "${config.agent_name}" (only alphanumeric, hyphens, underscores, dots allowed)`
526
593
  };
527
594
  }
528
- const inputJson = JSON.stringify(payload);
595
+ const skillId = config.id;
596
+ const message = `[AgentBnB Rental Request]
597
+ You are executing the "${skillId}" skill for an AgentBnB network rental.
598
+ Read your skills/${skillId}/SKILL.md for detailed instructions.
599
+
600
+ Input parameters:
601
+ ${JSON.stringify(payload.params ?? {}, null, 2)}
602
+
603
+ IMPORTANT: Return ONLY a JSON object as your response.
604
+ Do NOT include explanations, markdown formatting, or code blocks.
605
+ The JSON should contain the output fields specified in your SKILL.md.
606
+ If you cannot complete the task, return: {"error": "reason"}`;
529
607
  try {
530
- const stdout = execFileSync("openclaw", ["run", config.agent_name, "--input", inputJson], {
608
+ const stdout = execFileSync("openclaw", [
609
+ "agent",
610
+ "--agent",
611
+ config.agent_name,
612
+ "--message",
613
+ message,
614
+ "--json",
615
+ "--local"
616
+ ], {
531
617
  timeout: timeoutMs
532
618
  });
533
619
  const text = stdout.toString().trim();
534
- const result = JSON.parse(text);
535
- return { success: true, result };
620
+ try {
621
+ const parsed = JSON.parse(text);
622
+ return { success: true, result: parsed };
623
+ } catch {
624
+ return {
625
+ success: false,
626
+ error: `OpenClaw process channel returned invalid JSON: ${text}`
627
+ };
628
+ }
536
629
  } catch (err) {
537
- const message = err instanceof Error ? err.message : String(err);
538
- return { success: false, error: message };
630
+ const message2 = err instanceof Error ? err.message : String(err);
631
+ return { success: false, error: message2 };
539
632
  }
540
633
  }
541
634
  async function executeTelegram(config, payload) {
@@ -944,23 +1037,27 @@ var AgentRuntime = class {
944
1037
  }
945
1038
  const modes = /* @__PURE__ */ new Map();
946
1039
  if (this.conductorEnabled) {
947
- const { ConductorMode } = await import("./conductor-mode-PLTB6MS3.js");
948
- const { registerConductorCard, CONDUCTOR_OWNER } = await import("./card-EX2EYGCZ.js");
1040
+ const { ConductorMode } = await import("./conductor-mode-SBDCRIX6.js");
1041
+ const { registerConductorCard, CONDUCTOR_OWNER } = await import("./card-T2XJZA5A.js");
949
1042
  const { loadPeers } = await import("./peers-K7FSHPN3.js");
950
1043
  registerConductorCard(this.registryDb);
951
1044
  const resolveAgentUrl = (owner) => {
952
1045
  const peers = loadPeers();
953
- const peer = peers.find((p) => p.name.toLowerCase() === owner.toLowerCase());
1046
+ const matchingCards = listCards(this.registryDb, owner);
1047
+ const candidateNames = /* @__PURE__ */ new Set([owner.toLowerCase()]);
1048
+ for (const card of matchingCards) {
1049
+ candidateNames.add(card.owner.toLowerCase());
1050
+ if (typeof card.agent_id === "string" && card.agent_id.length > 0) {
1051
+ candidateNames.add(card.agent_id.toLowerCase());
1052
+ }
1053
+ }
1054
+ const peer = peers.find((p) => candidateNames.has(p.name.toLowerCase()));
954
1055
  if (!peer) {
955
1056
  throw new Error(
956
1057
  `No peer found for agent owner "${owner}". Add with: agentbnb connect ${owner} <url> <token>`
957
1058
  );
958
1059
  }
959
- const stmt = this.registryDb.prepare(
960
- "SELECT id FROM capability_cards WHERE owner = ? LIMIT 1"
961
- );
962
- const row = stmt.get(owner);
963
- const cardId = row?.id ?? owner;
1060
+ const cardId = matchingCards[0]?.id ?? owner;
964
1061
  return { url: peer.url, cardId };
965
1062
  };
966
1063
  const conductorMode = new ConductorMode({
@@ -1002,7 +1099,8 @@ var AgentRuntime = class {
1002
1099
  }
1003
1100
  /**
1004
1101
  * Recovers orphaned escrows by releasing them.
1005
- * Orphaned escrows are 'held' escrows older than the configured age threshold.
1102
+ * Orphaned escrows are stale 'held' or 'abandoned' escrows older than the
1103
+ * configured age threshold. In-flight started/progressing escrows are not touched.
1006
1104
  * Errors during individual release are swallowed (escrow may have settled between query and release).
1007
1105
  */
1008
1106
  async recoverOrphanedEscrows() {
@@ -1010,7 +1108,7 @@ var AgentRuntime = class {
1010
1108
  Date.now() - this.orphanedEscrowAgeMinutes * 60 * 1e3
1011
1109
  ).toISOString();
1012
1110
  const orphaned = this.creditDb.prepare(
1013
- "SELECT id FROM credit_escrow WHERE status = 'held' AND created_at < ?"
1111
+ "SELECT id FROM credit_escrow WHERE status IN ('held', 'abandoned') AND created_at < ?"
1014
1112
  ).all(cutoff);
1015
1113
  for (const row of orphaned) {
1016
1114
  try {
@@ -1361,7 +1459,9 @@ function processEscrowSettle(creditDb, escrowId, success, providerAgentId, signa
1361
1459
  throw new Error("Invalid consumer signature on escrow settle");
1362
1460
  }
1363
1461
  }
1364
- const escrowRow = creditDb.prepare("SELECT amount, owner FROM credit_escrow WHERE id = ? AND status = ?").get(escrowId, "held");
1462
+ const escrowRow = creditDb.prepare(
1463
+ "SELECT amount, owner FROM credit_escrow WHERE id = ? AND status IN ('held', 'started', 'progressing', 'abandoned')"
1464
+ ).get(escrowId);
1365
1465
  if (!escrowRow) {
1366
1466
  throw new Error(`Escrow not found or already settled: ${escrowId}`);
1367
1467
  }
@@ -1692,7 +1792,16 @@ function handleJobRelayResponse(opts) {
1692
1792
  // src/relay/websocket-relay.ts
1693
1793
  var RATE_LIMIT_MAX = 60;
1694
1794
  var RATE_LIMIT_WINDOW_MS = 6e4;
1695
- var RELAY_TIMEOUT_MS = 3e5;
1795
+ var RELAY_IDLE_TIMEOUT_MS = 3e4;
1796
+ var RELAY_HARD_TIMEOUT_MS = 3e5;
1797
+ var RELAY_DISCONNECT_GRACE_MS = RELAY_HARD_TIMEOUT_MS + 3e4;
1798
+ function readTimeoutOverride(envKey, fallbackMs) {
1799
+ const raw = process.env[envKey];
1800
+ if (!raw) return fallbackMs;
1801
+ const parsed = Number(raw);
1802
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallbackMs;
1803
+ return Math.floor(parsed);
1804
+ }
1696
1805
  function registerWebSocketRelay(server, db, creditDb) {
1697
1806
  const connections = /* @__PURE__ */ new Map();
1698
1807
  const agentIdToOwner = /* @__PURE__ */ new Map();
@@ -1706,6 +1815,136 @@ function registerWebSocketRelay(server, db, creditDb) {
1706
1815
  if (connections.has(target)) return target;
1707
1816
  return void 0;
1708
1817
  }
1818
+ function clearPendingTimers(pending) {
1819
+ clearTimeout(pending.timeout);
1820
+ if (pending.idleTimeout) clearTimeout(pending.idleTimeout);
1821
+ if (pending.hardTimeout) clearTimeout(pending.hardTimeout);
1822
+ if (pending.graceTimeout) clearTimeout(pending.graceTimeout);
1823
+ }
1824
+ function setPendingTimer(pending, timerType, timer) {
1825
+ const existing = pending[timerType];
1826
+ if (existing) clearTimeout(existing);
1827
+ pending[timerType] = timer;
1828
+ pending.timeout = timer;
1829
+ }
1830
+ function scheduleIdleTimeout(requestId, pending) {
1831
+ const timeoutMs = readTimeoutOverride("AGENTBNB_RELAY_IDLE_TIMEOUT_MS", RELAY_IDLE_TIMEOUT_MS);
1832
+ const timer = setTimeout(() => {
1833
+ const current = pendingRequests.get(requestId);
1834
+ if (!current || (current.lifecycle ?? "held") !== "held") return;
1835
+ pendingRequests.delete(requestId);
1836
+ clearPendingTimers(current);
1837
+ if (current.escrowId && creditDb) {
1838
+ try {
1839
+ releaseForRelay(creditDb, current.escrowId);
1840
+ } catch (e) {
1841
+ console.error("[relay] escrow release on idle timeout failed:", e);
1842
+ }
1843
+ }
1844
+ const originWs = connections.get(current.originOwner);
1845
+ if (originWs && originWs.readyState === 1) {
1846
+ sendMessage(originWs, {
1847
+ type: "response",
1848
+ id: requestId,
1849
+ error: { code: -32603, message: "Relay request timeout" }
1850
+ });
1851
+ }
1852
+ }, timeoutMs);
1853
+ setPendingTimer(pending, "idleTimeout", timer);
1854
+ }
1855
+ function scheduleHardTimeout(requestId, pending) {
1856
+ const timeoutMs = readTimeoutOverride("AGENTBNB_RELAY_HARD_TIMEOUT_MS", RELAY_HARD_TIMEOUT_MS);
1857
+ const timer = setTimeout(() => {
1858
+ const current = pendingRequests.get(requestId);
1859
+ if (!current || current.lifecycle === "abandoned") return;
1860
+ pendingRequests.delete(requestId);
1861
+ clearPendingTimers(current);
1862
+ if (current.escrowId && creditDb) {
1863
+ try {
1864
+ releaseForRelay(creditDb, current.escrowId);
1865
+ } catch (e) {
1866
+ console.error("[relay] escrow release on hard timeout failed:", e);
1867
+ }
1868
+ }
1869
+ const originWs = connections.get(current.originOwner);
1870
+ if (originWs && originWs.readyState === 1) {
1871
+ sendMessage(originWs, {
1872
+ type: "response",
1873
+ id: requestId,
1874
+ error: { code: -32603, message: "Relay request timeout" }
1875
+ });
1876
+ }
1877
+ }, timeoutMs);
1878
+ setPendingTimer(pending, "hardTimeout", timer);
1879
+ }
1880
+ function scheduleGraceTimeout(requestId, pending) {
1881
+ const timeoutMs = readTimeoutOverride(
1882
+ "AGENTBNB_RELAY_DISCONNECT_GRACE_MS",
1883
+ RELAY_DISCONNECT_GRACE_MS
1884
+ );
1885
+ const timer = setTimeout(() => {
1886
+ const current = pendingRequests.get(requestId);
1887
+ if (!current || current.lifecycle !== "abandoned") return;
1888
+ pendingRequests.delete(requestId);
1889
+ clearPendingTimers(current);
1890
+ if (current.escrowId && creditDb) {
1891
+ try {
1892
+ releaseForRelay(creditDb, current.escrowId);
1893
+ } catch (e) {
1894
+ console.error("[relay] escrow release after grace timeout failed:", e);
1895
+ }
1896
+ }
1897
+ }, timeoutMs);
1898
+ setPendingTimer(pending, "graceTimeout", timer);
1899
+ }
1900
+ function transitionPendingToStarted(requestId, pending) {
1901
+ if (pending.lifecycle === "started" || pending.lifecycle === "progressing" || pending.lifecycle === "abandoned") {
1902
+ return;
1903
+ }
1904
+ pending.lifecycle = "started";
1905
+ pending.startedAt = Date.now();
1906
+ if (pending.idleTimeout) clearTimeout(pending.idleTimeout);
1907
+ if (pending.graceTimeout) clearTimeout(pending.graceTimeout);
1908
+ if (pending.escrowId && creditDb) {
1909
+ try {
1910
+ markEscrowStarted(creditDb, pending.escrowId);
1911
+ } catch (e) {
1912
+ console.error("[relay] escrow transition to started failed:", e);
1913
+ }
1914
+ }
1915
+ scheduleHardTimeout(requestId, pending);
1916
+ }
1917
+ function transitionPendingToProgressing(requestId, pending) {
1918
+ if (pending.lifecycle === "abandoned") {
1919
+ return;
1920
+ }
1921
+ if ((pending.lifecycle ?? "held") === "held") {
1922
+ transitionPendingToStarted(requestId, pending);
1923
+ }
1924
+ pending.lifecycle = "progressing";
1925
+ if (pending.escrowId && creditDb) {
1926
+ try {
1927
+ markEscrowProgressing(creditDb, pending.escrowId);
1928
+ } catch (e) {
1929
+ console.error("[relay] escrow transition to progressing failed:", e);
1930
+ }
1931
+ }
1932
+ }
1933
+ function transitionPendingToAbandoned(requestId, pending) {
1934
+ if (pending.lifecycle === "abandoned") return;
1935
+ pending.lifecycle = "abandoned";
1936
+ pending.abandonedAt = Date.now();
1937
+ if (pending.idleTimeout) clearTimeout(pending.idleTimeout);
1938
+ if (pending.hardTimeout) clearTimeout(pending.hardTimeout);
1939
+ if (pending.escrowId && creditDb) {
1940
+ try {
1941
+ markEscrowAbandoned(creditDb, pending.escrowId);
1942
+ } catch (e) {
1943
+ console.error("[relay] escrow transition to abandoned failed:", e);
1944
+ }
1945
+ }
1946
+ scheduleGraceTimeout(requestId, pending);
1947
+ }
1709
1948
  function checkRateLimit(owner) {
1710
1949
  const now = Date.now();
1711
1950
  const entry = rateLimits.get(owner);
@@ -1755,15 +1994,20 @@ function registerWebSocketRelay(server, db, creditDb) {
1755
1994
  } catch {
1756
1995
  }
1757
1996
  }
1758
- function upsertCard(cardData, owner) {
1759
- const parsed = AnyCardSchema.safeParse(cardData);
1997
+ function upsertCard(cardData, owner, agentId) {
1998
+ const parsed = AnyCardSchema.safeParse(
1999
+ agentId ? { ...cardData, agent_id: agentId } : cardData
2000
+ );
1760
2001
  if (!parsed.success) {
1761
2002
  throw new AgentBnBError(
1762
2003
  `Card validation failed: ${parsed.error.message}`,
1763
2004
  "VALIDATION_ERROR"
1764
2005
  );
1765
2006
  }
1766
- const card = { ...parsed.data, availability: { ...parsed.data.availability, online: true } };
2007
+ const card = attachCanonicalAgentId(db, {
2008
+ ...parsed.data,
2009
+ availability: { ...parsed.data.availability, online: true }
2010
+ });
1767
2011
  const cardId = card.id;
1768
2012
  const now = (/* @__PURE__ */ new Date()).toISOString();
1769
2013
  const existing = db.prepare("SELECT id FROM capability_cards WHERE id = ?").get(cardId);
@@ -1817,7 +2061,7 @@ function registerWebSocketRelay(server, db, creditDb) {
1817
2061
  agentIdToOwner.set(agentEntry.agent_id, owner);
1818
2062
  for (const agentCard of agentEntry.cards) {
1819
2063
  try {
1820
- upsertCard(agentCard, owner);
2064
+ upsertCard(agentCard, owner, agentEntry.agent_id);
1821
2065
  } catch {
1822
2066
  }
1823
2067
  }
@@ -1831,7 +2075,7 @@ function registerWebSocketRelay(server, db, creditDb) {
1831
2075
  }
1832
2076
  let cardId;
1833
2077
  try {
1834
- cardId = upsertCard(card, owner);
2078
+ cardId = upsertCard(card, owner, msg.agent_id);
1835
2079
  } catch (err) {
1836
2080
  console.error(`[relay] card validation failed for ${owner}:`, err instanceof Error ? err.message : err);
1837
2081
  cardId = card.id ?? owner;
@@ -1841,7 +2085,7 @@ function registerWebSocketRelay(server, db, creditDb) {
1841
2085
  if (msg.cards && msg.cards.length > 0) {
1842
2086
  for (const extraCard of msg.cards) {
1843
2087
  try {
1844
- upsertCard(extraCard, owner);
2088
+ upsertCard(extraCard, owner, msg.agent_id);
1845
2089
  } catch {
1846
2090
  }
1847
2091
  }
@@ -1896,23 +2140,17 @@ function registerWebSocketRelay(server, db, creditDb) {
1896
2140
  console.error("[relay] credit hold error (non-fatal):", err);
1897
2141
  }
1898
2142
  }
1899
- const timeout = setTimeout(() => {
1900
- const pending = pendingRequests.get(msg.id);
1901
- pendingRequests.delete(msg.id);
1902
- if (pending?.escrowId && creditDb) {
1903
- try {
1904
- releaseForRelay(creditDb, pending.escrowId);
1905
- } catch (e) {
1906
- console.error("[relay] escrow release on timeout failed:", e);
1907
- }
1908
- }
1909
- sendMessage(ws, {
1910
- type: "response",
1911
- id: msg.id,
1912
- error: { code: -32603, message: "Relay request timeout" }
1913
- });
1914
- }, RELAY_TIMEOUT_MS);
1915
- pendingRequests.set(msg.id, { originOwner: fromOwner, creditOwner, timeout, escrowId, targetOwner: msg.target_owner });
2143
+ const pending = {
2144
+ originOwner: fromOwner,
2145
+ creditOwner,
2146
+ timeout: setTimeout(() => void 0, 1),
2147
+ escrowId,
2148
+ targetOwner: targetKey ?? msg.target_owner,
2149
+ lifecycle: "held",
2150
+ createdAt: Date.now()
2151
+ };
2152
+ pendingRequests.set(msg.id, pending);
2153
+ scheduleIdleTimeout(msg.id, pending);
1916
2154
  sendMessage(targetWs, {
1917
2155
  type: "incoming_request",
1918
2156
  id: msg.id,
@@ -1924,30 +2162,23 @@ function registerWebSocketRelay(server, db, creditDb) {
1924
2162
  escrow_receipt: msg.escrow_receipt
1925
2163
  });
1926
2164
  }
2165
+ function handleRelayStarted(msg) {
2166
+ const pending = pendingRequests.get(msg.id);
2167
+ if (!pending) return;
2168
+ transitionPendingToStarted(msg.id, pending);
2169
+ const originWs = connections.get(pending.originOwner);
2170
+ if (originWs && originWs.readyState === 1) {
2171
+ sendMessage(originWs, {
2172
+ type: "relay_started",
2173
+ id: msg.id,
2174
+ message: msg.message
2175
+ });
2176
+ }
2177
+ }
1927
2178
  function handleRelayProgress(msg) {
1928
2179
  const pending = pendingRequests.get(msg.id);
1929
2180
  if (!pending) return;
1930
- clearTimeout(pending.timeout);
1931
- const newTimeout = setTimeout(() => {
1932
- const p = pendingRequests.get(msg.id);
1933
- pendingRequests.delete(msg.id);
1934
- if (p?.escrowId && creditDb) {
1935
- try {
1936
- releaseForRelay(creditDb, p.escrowId);
1937
- } catch (e) {
1938
- console.error("[relay] escrow release on progress timeout failed:", e);
1939
- }
1940
- }
1941
- const originWs2 = connections.get(pending.originOwner);
1942
- if (originWs2 && originWs2.readyState === 1) {
1943
- sendMessage(originWs2, {
1944
- type: "response",
1945
- id: msg.id,
1946
- error: { code: -32603, message: "Relay request timeout" }
1947
- });
1948
- }
1949
- }, RELAY_TIMEOUT_MS);
1950
- pending.timeout = newTimeout;
2181
+ transitionPendingToProgressing(msg.id, pending);
1951
2182
  const originWs = connections.get(pending.originOwner);
1952
2183
  if (originWs && originWs.readyState === 1) {
1953
2184
  sendMessage(originWs, {
@@ -1961,7 +2192,7 @@ function registerWebSocketRelay(server, db, creditDb) {
1961
2192
  function handleRelayResponse(msg) {
1962
2193
  const pending = pendingRequests.get(msg.id);
1963
2194
  if (!pending) return;
1964
- clearTimeout(pending.timeout);
2195
+ clearPendingTimers(pending);
1965
2196
  pendingRequests.delete(msg.id);
1966
2197
  if (pending.jobId && creditDb) {
1967
2198
  try {
@@ -2024,16 +2255,15 @@ function registerWebSocketRelay(server, db, creditDb) {
2024
2255
  connections.delete(owner);
2025
2256
  rateLimits.delete(owner);
2026
2257
  agentCapacities.delete(owner);
2027
- for (const [agentId, o] of agentIdToOwner) {
2028
- if (o === owner) {
2258
+ for (const [agentId, mappedOwner] of Array.from(agentIdToOwner.entries())) {
2259
+ if (mappedOwner === owner) {
2029
2260
  agentIdToOwner.delete(agentId);
2030
- break;
2031
2261
  }
2032
2262
  }
2033
2263
  markOwnerOffline(owner);
2034
2264
  for (const [reqId, pending] of pendingRequests) {
2035
2265
  if (pending.targetOwner === owner) {
2036
- clearTimeout(pending.timeout);
2266
+ clearPendingTimers(pending);
2037
2267
  pendingRequests.delete(reqId);
2038
2268
  if (pending.escrowId && creditDb) {
2039
2269
  try {
@@ -2051,14 +2281,19 @@ function registerWebSocketRelay(server, db, creditDb) {
2051
2281
  });
2052
2282
  }
2053
2283
  } else if (pending.originOwner === owner) {
2054
- clearTimeout(pending.timeout);
2055
- pendingRequests.delete(reqId);
2056
- if (pending.escrowId && creditDb) {
2057
- try {
2058
- releaseForRelay(creditDb, pending.escrowId);
2059
- } catch (e) {
2060
- console.error("[relay] escrow release on requester disconnect failed:", e);
2284
+ const lifecycle = pending.lifecycle ?? "held";
2285
+ if (lifecycle === "held") {
2286
+ clearPendingTimers(pending);
2287
+ pendingRequests.delete(reqId);
2288
+ if (pending.escrowId && creditDb) {
2289
+ try {
2290
+ releaseForRelay(creditDb, pending.escrowId);
2291
+ } catch (e) {
2292
+ console.error("[relay] escrow release on requester disconnect failed:", e);
2293
+ }
2061
2294
  }
2295
+ } else {
2296
+ transitionPendingToAbandoned(reqId, pending);
2062
2297
  }
2063
2298
  }
2064
2299
  }
@@ -2101,7 +2336,9 @@ function registerWebSocketRelay(server, db, creditDb) {
2101
2336
  return;
2102
2337
  }
2103
2338
  try {
2104
- const escrow = creditDb.prepare("SELECT card_id FROM credit_escrow WHERE id = ? AND status = ?").get(msg.escrow_id, "held");
2339
+ const escrow = creditDb.prepare(
2340
+ "SELECT card_id FROM credit_escrow WHERE id = ? AND status IN ('held', 'started', 'progressing', 'abandoned')"
2341
+ ).get(msg.escrow_id);
2105
2342
  if (!escrow) {
2106
2343
  sendMessage(ws, { type: "error", code: "escrow_not_found", message: `Escrow not found: ${msg.escrow_id}`, request_id: msg.request_id });
2107
2344
  return;
@@ -2198,6 +2435,9 @@ function registerWebSocketRelay(server, db, creditDb) {
2198
2435
  case "relay_response":
2199
2436
  handleRelayResponse(msg);
2200
2437
  break;
2438
+ case "relay_started":
2439
+ handleRelayStarted(msg);
2440
+ break;
2201
2441
  case "relay_progress":
2202
2442
  handleRelayProgress(msg);
2203
2443
  break;
@@ -2244,7 +2484,7 @@ function registerWebSocketRelay(server, db, creditDb) {
2244
2484
  }
2245
2485
  connections.clear();
2246
2486
  for (const [, pending] of pendingRequests) {
2247
- clearTimeout(pending.timeout);
2487
+ clearPendingTimers(pending);
2248
2488
  }
2249
2489
  pendingRequests.clear();
2250
2490
  rateLimits.clear();
@@ -3522,6 +3762,9 @@ function stripInternal(card) {
3522
3762
  const { _internal: _, ...publicCard } = card;
3523
3763
  return publicCard;
3524
3764
  }
3765
+ function buildSqlPlaceholders(count) {
3766
+ return Array.from({ length: count }, () => "?").join(", ");
3767
+ }
3525
3768
  function createRegistryServer(opts) {
3526
3769
  const { registryDb: db, silent = false } = opts;
3527
3770
  const server = Fastify2({ logger: !silent });
@@ -3931,11 +4174,11 @@ function createRegistryServer(opts) {
3931
4174
  const card = result.data;
3932
4175
  const now = (/* @__PURE__ */ new Date()).toISOString();
3933
4176
  if (card.spec_version === "2.0") {
3934
- const cardWithTimestamps = {
4177
+ const cardWithTimestamps = attachCanonicalAgentId(db, {
3935
4178
  ...card,
3936
4179
  created_at: card.created_at ?? now,
3937
4180
  updated_at: now
3938
- };
4181
+ });
3939
4182
  db.prepare(
3940
4183
  `INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
3941
4184
  VALUES (?, ?, ?, ?, ?)`
@@ -4056,19 +4299,16 @@ function createRegistryServer(opts) {
4056
4299
  if (ownerCards.length === 0) {
4057
4300
  return reply.status(404).send({ error: "Agent not found" });
4058
4301
  }
4059
- const memberStmt = db.prepare(
4060
- "SELECT MIN(created_at) as earliest, MAX(created_at) as latest FROM capability_cards WHERE owner = ?"
4302
+ const resolvedOwner = ownerCards[0]?.owner ?? owner;
4303
+ const ownerCardIds = ownerCards.map((card) => card.id);
4304
+ const cardIdPlaceholders = buildSqlPlaceholders(ownerCardIds.length);
4305
+ const joinedAt = ownerCards.map((card) => card.created_at).filter((value) => typeof value === "string" && value.length > 0).sort((left, right) => left.localeCompare(right))[0] ?? (/* @__PURE__ */ new Date()).toISOString();
4306
+ const latestCardUpdate = ownerCards.map((card) => card.updated_at ?? card.created_at).filter((value) => typeof value === "string" && value.length > 0).sort((left, right) => right.localeCompare(left))[0] ?? joinedAt;
4307
+ const lastActiveStmt = db.prepare(
4308
+ `SELECT MAX(created_at) as last_req FROM request_log WHERE card_id IN (${cardIdPlaceholders})`
4061
4309
  );
4062
- const memberRow = memberStmt.get(owner);
4063
- const joinedAt = memberRow?.earliest ?? (/* @__PURE__ */ new Date()).toISOString();
4064
- const lastActiveStmt = db.prepare(`
4065
- SELECT MAX(rl.created_at) as last_req
4066
- FROM request_log rl
4067
- INNER JOIN capability_cards cc ON rl.card_id = cc.id
4068
- WHERE cc.owner = ?
4069
- `);
4070
- const lastActiveRow = lastActiveStmt.get(owner);
4071
- const lastActive = lastActiveRow?.last_req ?? memberRow?.latest ?? joinedAt;
4310
+ const lastActiveRow = lastActiveStmt.get(...ownerCardIds);
4311
+ const lastActive = lastActiveRow?.last_req ?? latestCardUpdate ?? joinedAt;
4072
4312
  const metricsStmt = db.prepare(`
4073
4313
  SELECT
4074
4314
  SUM(CASE WHEN rl.failure_reason IS NULL OR rl.failure_reason IN ('bad_execution','auth_error')
@@ -4078,10 +4318,9 @@ function createRegistryServer(opts) {
4078
4318
  COUNT(DISTINCT rl.requester) as unique_requesters,
4079
4319
  COUNT(DISTINCT CASE WHEN rl.status = 'success' THEN rl.requester END) as repeat_success_requesters
4080
4320
  FROM request_log rl
4081
- INNER JOIN capability_cards cc ON rl.card_id = cc.id
4082
- WHERE cc.owner = ? AND rl.action_type IS NULL
4321
+ WHERE rl.card_id IN (${cardIdPlaceholders}) AND rl.action_type IS NULL
4083
4322
  `);
4084
- const metricsRow = metricsStmt.get(owner);
4323
+ const metricsRow = metricsStmt.get(...ownerCardIds);
4085
4324
  const totalExec = metricsRow?.total ?? 0;
4086
4325
  const successExec = metricsRow?.successes ?? 0;
4087
4326
  const successRate = totalExec > 0 ? successExec / totalExec : 0;
@@ -4095,25 +4334,23 @@ function createRegistryServer(opts) {
4095
4334
  COUNT(*) as count,
4096
4335
  SUM(CASE WHEN rl.status = 'success' THEN 1 ELSE 0 END) as success
4097
4336
  FROM request_log rl
4098
- INNER JOIN capability_cards cc ON rl.card_id = cc.id
4099
- WHERE cc.owner = ? AND rl.action_type IS NULL
4337
+ WHERE rl.card_id IN (${cardIdPlaceholders}) AND rl.action_type IS NULL
4100
4338
  AND rl.created_at >= DATE('now', '-7 days')
4101
4339
  GROUP BY DATE(rl.created_at)
4102
4340
  ORDER BY day ASC
4103
4341
  `);
4104
- const trend_7d = trendStmt.all(owner).map((r) => ({ date: r.day, count: r.count, success: r.success }));
4342
+ const trend_7d = trendStmt.all(...ownerCardIds).map((r) => ({ date: r.day, count: r.count, success: r.success }));
4105
4343
  let performanceTier = 0;
4106
4344
  if (totalExec > 10) performanceTier = 1;
4107
4345
  if (totalExec > 50 && successRate >= 0.85) performanceTier = 2;
4108
4346
  const proofsStmt = db.prepare(`
4109
4347
  SELECT rl.card_name, rl.status, rl.latency_ms, rl.id, rl.created_at
4110
4348
  FROM request_log rl
4111
- INNER JOIN capability_cards cc ON rl.card_id = cc.id
4112
- WHERE cc.owner = ? AND rl.action_type IS NULL
4349
+ WHERE rl.card_id IN (${cardIdPlaceholders}) AND rl.action_type IS NULL
4113
4350
  ORDER BY rl.created_at DESC
4114
4351
  LIMIT 10
4115
4352
  `);
4116
- const proofRows = proofsStmt.all(owner);
4353
+ const proofRows = proofsStmt.all(...ownerCardIds);
4117
4354
  const statusToOutcomeClass = (s) => {
4118
4355
  if (s === "success") return "completed";
4119
4356
  if (s === "timeout") return "cancelled";
@@ -4139,22 +4376,20 @@ function createRegistryServer(opts) {
4139
4376
  const activityStmt = db.prepare(`
4140
4377
  SELECT rl.id, rl.card_name, rl.requester, rl.status, rl.credits_charged, rl.created_at
4141
4378
  FROM request_log rl
4142
- INNER JOIN capability_cards cc ON rl.card_id = cc.id
4143
- WHERE cc.owner = ?
4379
+ WHERE rl.card_id IN (${cardIdPlaceholders})
4144
4380
  ORDER BY rl.created_at DESC
4145
4381
  LIMIT 10
4146
4382
  `);
4147
- const recentActivity = activityStmt.all(owner);
4383
+ const recentActivity = activityStmt.all(...ownerCardIds);
4148
4384
  const skillCount = ownerCards.reduce((sum, card) => sum + (card.skills?.length ?? 1), 0);
4149
4385
  const creditsStmt = db.prepare(`
4150
- SELECT SUM(CASE WHEN rl.status = 'success' THEN rl.credits_charged ELSE 0 END) as credits_earned
4151
- FROM capability_cards cc
4152
- LEFT JOIN request_log rl ON rl.card_id = cc.id
4153
- WHERE cc.owner = ?
4386
+ SELECT COALESCE(SUM(CASE WHEN rl.status = 'success' THEN rl.credits_charged ELSE 0 END), 0) as credits_earned
4387
+ FROM request_log rl
4388
+ WHERE rl.card_id IN (${cardIdPlaceholders})
4154
4389
  `);
4155
- const creditsRow = creditsStmt.get(owner);
4390
+ const creditsRow = creditsStmt.get(...ownerCardIds);
4156
4391
  const response = {
4157
- owner,
4392
+ owner: resolvedOwner,
4158
4393
  agent_name: v2Card?.agent_name,
4159
4394
  short_description: v2Card?.short_description,
4160
4395
  joined_at: joinedAt,
@@ -4186,7 +4421,7 @@ function createRegistryServer(opts) {
4186
4421
  return reply.send({
4187
4422
  ...response,
4188
4423
  profile: {
4189
- owner,
4424
+ owner: resolvedOwner,
4190
4425
  skill_count: skillCount,
4191
4426
  success_rate: successRate > 0 ? successRate : null,
4192
4427
  total_earned: creditsRow?.credits_earned ?? 0,
@@ -4458,7 +4693,7 @@ function createRegistryServer(opts) {
4458
4693
  return { card_id: target.cardId, skill_id: target.skillId };
4459
4694
  }
4460
4695
  if (!relayClient) {
4461
- const { RelayClient } = await import("./websocket-client-QOVARTRN.js");
4696
+ const { RelayClient } = await import("./websocket-client-3U27WJUU.js");
4462
4697
  relayClient = new RelayClient({
4463
4698
  registryUrl: relayRegistryUrl,
4464
4699
  owner: relayRequesterOwner,
@@ -4480,7 +4715,7 @@ function createRegistryServer(opts) {
4480
4715
  });
4481
4716
  await relayClient.connect();
4482
4717
  }
4483
- const { requestViaRelay } = await import("./client-XOLP5IUZ.js");
4718
+ const { requestViaRelay } = await import("./client-UQBGCIPA.js");
4484
4719
  return requestViaRelay(relayClient, {
4485
4720
  targetOwner: target.owner,
4486
4721
  cardId: target.cardId,
@@ -4736,7 +4971,7 @@ function createRegistryServer(opts) {
4736
4971
  if (!opts.creditDb) {
4737
4972
  return reply.code(404).send({ error: "Credit system not enabled" });
4738
4973
  }
4739
- const { getReliabilityMetrics } = await import("./reliability-metrics-QG7WC5QK.js");
4974
+ const { getReliabilityMetrics } = await import("./reliability-metrics-RRUKJ4ME.js");
4740
4975
  const metrics = getReliabilityMetrics(opts.creditDb, owner);
4741
4976
  if (!metrics) {
4742
4977
  return reply.code(404).send({ error: "No reliability data for this provider" });
@@ -4754,38 +4989,37 @@ function createRegistryServer(opts) {
4754
4989
  }
4755
4990
  }, async (request, reply) => {
4756
4991
  const { owner } = request.params;
4757
- const rows = db.prepare(
4758
- "SELECT id, data FROM capability_cards WHERE owner = ?"
4759
- ).all(owner);
4992
+ const cards = listCards(db, owner);
4760
4993
  const agents = [];
4761
- for (const row of rows) {
4994
+ for (const card of cards) {
4762
4995
  try {
4763
- const card = JSON.parse(row.data);
4996
+ const rawCard = card;
4997
+ const providerIdentity = typeof card.agent_id === "string" && card.agent_id.length > 0 ? card.agent_id : card.owner;
4764
4998
  let earnings = 0;
4765
4999
  let spend = 0;
4766
5000
  if (opts.creditDb) {
4767
5001
  const earningRow = opts.creditDb.prepare(
4768
5002
  "SELECT COALESCE(SUM(amount), 0) as total FROM credit_transactions WHERE owner = ? AND reason = 'settlement' AND amount > 0"
4769
- ).get(owner);
5003
+ ).get(providerIdentity);
4770
5004
  earnings = earningRow.total;
4771
5005
  const spendRow = opts.creditDb.prepare(
4772
5006
  "SELECT COALESCE(SUM(ABS(amount)), 0) as total FROM credit_transactions WHERE owner = ? AND reason = 'escrow_hold'"
4773
- ).get(owner);
5007
+ ).get(providerIdentity);
4774
5008
  spend = spendRow.total;
4775
5009
  }
4776
5010
  const successCount = db.prepare(
4777
5011
  "SELECT COUNT(*) as cnt FROM request_log WHERE card_id = ? AND status = 'success' AND (action_type IS NULL OR action_type = 'auto_share')"
4778
- ).get(row.id).cnt;
5012
+ ).get(card.id).cnt;
4779
5013
  const failureCount = db.prepare(
4780
5014
  "SELECT COUNT(*) as cnt FROM request_log WHERE card_id = ? AND status IN ('failure', 'timeout', 'refunded') AND (action_type IS NULL OR action_type = 'auto_share')"
4781
- ).get(row.id).cnt;
5015
+ ).get(card.id).cnt;
4782
5016
  const totalExec = successCount + failureCount;
4783
5017
  const successRate = totalExec > 0 ? successCount / totalExec : 0;
4784
5018
  let failureBreakdown = {};
4785
5019
  try {
4786
5020
  const failureRows = db.prepare(
4787
5021
  "SELECT failure_reason, COUNT(*) as cnt FROM request_log WHERE card_id = ? AND status IN ('failure', 'timeout', 'refunded') AND failure_reason IS NOT NULL GROUP BY failure_reason"
4788
- ).all(row.id);
5022
+ ).all(card.id);
4789
5023
  for (const fr of failureRows) {
4790
5024
  failureBreakdown[fr.failure_reason] = fr.cnt;
4791
5025
  }
@@ -4793,12 +5027,12 @@ function createRegistryServer(opts) {
4793
5027
  }
4794
5028
  let reliability = null;
4795
5029
  if (opts.creditDb) {
4796
- const { getReliabilityMetrics } = await import("./reliability-metrics-QG7WC5QK.js");
4797
- reliability = getReliabilityMetrics(opts.creditDb, owner);
5030
+ const { getReliabilityMetrics } = await import("./reliability-metrics-RRUKJ4ME.js");
5031
+ reliability = getReliabilityMetrics(opts.creditDb, providerIdentity);
4798
5032
  }
4799
5033
  agents.push({
4800
- id: row.id,
4801
- name: card.name ?? card.agent_name ?? owner,
5034
+ id: card.id,
5035
+ name: (typeof rawCard["name"] === "string" ? rawCard["name"] : void 0) ?? (typeof rawCard["agent_name"] === "string" ? rawCard["agent_name"] : void 0) ?? card.owner,
4802
5036
  online: card.availability?.online ?? false,
4803
5037
  current_load: 0,
4804
5038
  // Will be populated from relay heartbeat data in future
@@ -4916,6 +5150,32 @@ import { spawn as spawn2 } from "child_process";
4916
5150
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
4917
5151
  import { join as join2 } from "path";
4918
5152
  import { randomUUID as randomUUID7 } from "crypto";
5153
+ function buildFallbackRelayCard(owner) {
5154
+ return {
5155
+ id: randomUUID7(),
5156
+ owner,
5157
+ name: owner,
5158
+ description: "Agent registered via CLI",
5159
+ spec_version: "1.0",
5160
+ level: 1,
5161
+ inputs: [],
5162
+ outputs: [],
5163
+ pricing: { credits_per_call: 1 },
5164
+ availability: { online: true }
5165
+ };
5166
+ }
5167
+ function buildRelayRegistrationCards(owner, localCards) {
5168
+ if (localCards.length === 0) {
5169
+ return {
5170
+ primaryCard: buildFallbackRelayCard(owner),
5171
+ additionalCards: []
5172
+ };
5173
+ }
5174
+ return {
5175
+ primaryCard: localCards[0],
5176
+ additionalCards: localCards.slice(1)
5177
+ };
5178
+ }
4919
5179
  var ServiceCoordinator = class {
4920
5180
  config;
4921
5181
  guard;
@@ -5060,8 +5320,11 @@ var ServiceCoordinator = class {
5060
5320
  console.log("Conductor mode enabled \u2014 orchestrate/plan skills available via gateway");
5061
5321
  }
5062
5322
  if (opts.conductorEnabled && this.config.conductor?.public) {
5063
- const { buildConductorCard } = await import("./card-EX2EYGCZ.js");
5064
- const conductorCard = buildConductorCard(this.config.owner);
5323
+ const { buildConductorCard } = await import("./card-T2XJZA5A.js");
5324
+ const conductorCard = attachCanonicalAgentId(
5325
+ this.runtime.registryDb,
5326
+ buildConductorCard(this.config.owner)
5327
+ );
5065
5328
  const now = (/* @__PURE__ */ new Date()).toISOString();
5066
5329
  const existing = this.runtime.registryDb.prepare("SELECT id FROM capability_cards WHERE id = ?").get(conductorCard.id);
5067
5330
  if (existing) {
@@ -5117,27 +5380,11 @@ var ServiceCoordinator = class {
5117
5380
  }
5118
5381
  }
5119
5382
  if (opts.registryUrl && opts.relay) {
5120
- const { RelayClient } = await import("./websocket-client-QOVARTRN.js");
5121
- const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("./execute-RIRHTIBU.js");
5122
- const cards = listCards(this.runtime.registryDb, this.config.owner);
5123
- const card = cards[0] ?? {
5124
- id: randomUUID7(),
5125
- owner: this.config.owner,
5126
- name: this.config.owner,
5127
- description: "Agent registered via CLI",
5128
- spec_version: "1.0",
5129
- level: 1,
5130
- inputs: [],
5131
- outputs: [],
5132
- pricing: { credits_per_call: 1 },
5133
- availability: { online: true }
5134
- };
5135
- const additionalCards = [];
5383
+ const { RelayClient } = await import("./websocket-client-3U27WJUU.js");
5384
+ const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("./execute-TEZPQ5WP.js");
5385
+ const localCards = listCards(this.runtime.registryDb, this.config.owner);
5386
+ const { primaryCard, additionalCards } = buildRelayRegistrationCards(this.config.owner, localCards);
5136
5387
  if (this.config.conductor?.public) {
5137
- const { buildConductorCard } = await import("./card-EX2EYGCZ.js");
5138
- additionalCards.push(
5139
- buildConductorCard(this.config.owner)
5140
- );
5141
5388
  console.log("Conductor card will be published to registry (conductor.public: true)");
5142
5389
  }
5143
5390
  this.relayClient = new RelayClient({
@@ -5145,9 +5392,10 @@ var ServiceCoordinator = class {
5145
5392
  owner: this.config.owner,
5146
5393
  agent_id: this.config.agent_id,
5147
5394
  token: this.config.token,
5148
- card,
5395
+ card: primaryCard,
5149
5396
  cards: additionalCards.length > 0 ? additionalCards : void 0,
5150
5397
  onRequest: async (req) => {
5398
+ this.relayClient?.sendStarted(req.id, "provider acknowledged");
5151
5399
  const onProgress = (info) => {
5152
5400
  this.relayClient.sendProgress(req.id, info);
5153
5401
  };
@@ -5427,6 +5675,7 @@ function sleep2(ms) {
5427
5675
  }
5428
5676
  export {
5429
5677
  ServiceCoordinator,
5678
+ buildRelayRegistrationCards,
5430
5679
  loadPersistedRuntime,
5431
5680
  resolveNodeExecutable
5432
5681
  };