agentbnb 9.0.3 → 9.1.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 (75) hide show
  1. package/README.md +29 -4
  2. package/dist/{card-6KL6L4GF.js → card-U2HQRPYN.js} +4 -1
  3. package/dist/{card-NKQFB3HD.js → card-UF465O7O.js} +4 -1
  4. package/dist/{chunk-77HAL2ZL.js → chunk-2HH2F3DM.js} +189 -68
  5. package/dist/chunk-3RG5ZIWI.js +10 -0
  6. package/dist/chunk-4HLGFR72.js +155 -0
  7. package/dist/chunk-4XTYT4JW.js +147 -0
  8. package/dist/{chunk-NLQCHO7N.js → chunk-7VZ4M4CT.js} +7 -134
  9. package/dist/{chunk-UQCQ2JCG.js → chunk-AMABG5SI.js} +7 -7
  10. package/dist/{chunk-N3TXLBGK.js → chunk-AW4VSROG.js} +1 -1
  11. package/dist/{chunk-PG3CLSAH.js → chunk-BPPFY72X.js} +7 -134
  12. package/dist/{chunk-Z4IDXMSP.js → chunk-D7NH6YLM.js} +6 -1
  13. package/dist/{chunk-SLZBE2I5.js → chunk-F2CIPAN2.js} +19 -14
  14. package/dist/{chunk-FUGWPKXN.js → chunk-G4TF4LB4.js} +1 -1
  15. package/dist/chunk-GZUTU6IZ.js +153 -0
  16. package/dist/{chunk-QEDVPJKP.js → chunk-I4E5ERDN.js} +20 -16
  17. package/dist/{chunk-74OZGLIT.js → chunk-LENX5NUW.js} +1 -1
  18. package/dist/{chunk-BNS76U6K.js → chunk-MPS4RE7T.js} +26 -20
  19. package/dist/{chunk-ERT77HKY.js → chunk-O44N3KR7.js} +2 -2
  20. package/dist/chunk-Q5OFZ2JR.js +292 -0
  21. package/dist/{chunk-YJ3RGKPU.js → chunk-QG2LLVXP.js} +6 -2
  22. package/dist/chunk-QXRNW4OJ.js +35 -0
  23. package/dist/{chunk-76YORWFJ.js → chunk-RVOZHVM7.js} +186 -63
  24. package/dist/{chunk-2SOHHB2O.js → chunk-TCA63C42.js} +39 -16
  25. package/dist/{chunk-UR3MISL2.js → chunk-UPNREF4L.js} +1 -1
  26. package/dist/{chunk-SMQDT7CT.js → chunk-WARYO57F.js} +7 -3
  27. package/dist/{chunk-DYJ7YGBM.js → chunk-WOVESOQ7.js} +237 -124
  28. package/dist/{chunk-I3RRMAAD.js → chunk-Y7CO3VLF.js} +8 -8
  29. package/dist/{chunk-FMKBCO2Q.js → chunk-ZYOMPJGG.js} +2 -2
  30. package/dist/cli/index.js +147 -63
  31. package/dist/{client-YB3IYO3S.js → client-XOSXFC7Q.js} +1 -0
  32. package/dist/conduct-MALC6HEK.js +30 -0
  33. package/dist/{conduct-URYWMA5T.js → conduct-UT6ZYSJD.js} +16 -12
  34. package/dist/{conductor-mode-2UFN6BUL.js → conductor-mode-3WLLERB4.js} +23 -19
  35. package/dist/{conductor-mode-NRSVP2AU.js → conductor-mode-UJKMO2GW.js} +15 -11
  36. package/dist/{config-IRWLG6IW.js → config-ZFWBAGDU.js} +1 -0
  37. package/dist/{credits-action-24EPLUHG.js → credits-action-KOUJNR36.js} +7 -5
  38. package/dist/{daemon-A7DXZIQW.js → daemon-ETXXE4IS.js} +2 -1
  39. package/dist/{did-action-MQLDT4RF.js → did-action-UHUYMA4Y.js} +2 -1
  40. package/dist/{execute-DNRNU3HM.js → execute-3RADNI74.js} +9 -5
  41. package/dist/{execute-2Z3XIUHR.js → execute-UFMGTXET.js} +12 -8
  42. package/dist/index.d.ts +412 -275
  43. package/dist/index.js +1198 -391
  44. package/dist/{openclaw-setup-WA625DZA.js → openclaw-setup-HEWZZOY7.js} +21 -17
  45. package/dist/{openclaw-skills-76ZWXHFM.js → openclaw-skills-5XLQFRWT.js} +7 -6
  46. package/dist/{peers-F2EWUMVQ.js → peers-7BMU2775.js} +1 -0
  47. package/dist/{peers-CJ7T4RJO.js → peers-IOVCBWAI.js} +1 -0
  48. package/dist/{process-guard-QDBIOLY4.js → process-guard-6324CZDC.js} +1 -0
  49. package/dist/provider-events-GTTJPYHS.js +13 -0
  50. package/dist/{publish-capability-FOCHYNYE.js → publish-capability-LM4RSQXX.js} +5 -2
  51. package/dist/{reliability-metrics-JSOY3PNW.js → reliability-metrics-22JTZGB4.js} +1 -0
  52. package/dist/{reliability-metrics-KKUFFVB6.js → reliability-metrics-MIJ3TJWL.js} +1 -0
  53. package/dist/{request-KPKWBL5W.js → request-LID2N42Y.js} +17 -13
  54. package/dist/request-log-2D253WML.js +17 -0
  55. package/dist/request-log-SIGTGOFA.js +16 -0
  56. package/dist/{scanner-GP4AOCW6.js → scanner-EFU6NBEJ.js} +1 -0
  57. package/dist/{schema-7BSSLZ4S.js → schema-FABVZKSI.js} +1 -0
  58. package/dist/{serve-skill-QSUIK3ZF.js → serve-skill-CDNSHTEE.js} +17 -13
  59. package/dist/{server-OCCAVVDF.js → server-QIAO3YSK.js} +23 -15
  60. package/dist/{service-coordinator-4JAUUNUL.js → service-coordinator-FB44QL7L.js} +721 -69
  61. package/dist/session-5AIRM7YF.js +144 -0
  62. package/dist/session-action-GYITLYOE.js +131 -0
  63. package/dist/{skill-config-5O2VR546.js → skill-config-VYNF7BCY.js} +1 -0
  64. package/dist/{skill-wrap-YLCJMFEJ.js → skill-wrap-IAZHOYM4.js} +1 -0
  65. package/dist/skills/agentbnb/bootstrap.js +750 -98
  66. package/dist/{store-S22F3I7G.js → store-C4DLIXYM.js} +4 -1
  67. package/dist/{vc-action-SUD7TMN2.js → vc-action-BWGNQ77Y.js} +2 -1
  68. package/dist/websocket-client-FCPZOE4S.js +9 -0
  69. package/dist/websocket-client-RT4KLJL4.js +8 -0
  70. package/dist/{writer-4QJ3U3WE.js → writer-V7JBWKKZ.js} +1 -0
  71. package/package.json +3 -1
  72. package/dist/chunk-3466S65P.js +0 -179
  73. package/dist/conduct-UAEEMVFD.js +0 -26
  74. package/dist/websocket-client-5CRE36Z5.js +0 -7
  75. package/dist/websocket-client-WHEHIYIZ.js +0 -6
@@ -1,3 +1,6 @@
1
+ import {
2
+ StructuredFeedbackSchema
3
+ } from "./chunk-AUBHR7HH.js";
1
4
  import {
2
5
  ApiSkillConfigSchema,
3
6
  parseSkillsFile
@@ -11,11 +14,9 @@ import {
11
14
  } from "./chunk-3MJT4PZG.js";
12
15
  import {
13
16
  executeCapabilityBatch,
14
- executeCapabilityRequest
15
- } from "./chunk-76YORWFJ.js";
16
- import {
17
- StructuredFeedbackSchema
18
- } from "./chunk-AUBHR7HH.js";
17
+ executeCapabilityRequest,
18
+ notifyProviderEvent
19
+ } from "./chunk-RVOZHVM7.js";
19
20
  import {
20
21
  announceGateway,
21
22
  stopAnnouncement
@@ -28,7 +29,7 @@ import {
28
29
  buildDraftCard,
29
30
  detectApiKeys,
30
31
  getPricingStats
31
- } from "./chunk-FUGWPKXN.js";
32
+ } from "./chunk-G4TF4LB4.js";
32
33
  import {
33
34
  listPendingRequests,
34
35
  resolvePendingRequest
@@ -40,23 +41,24 @@ import {
40
41
  } from "./chunk-G5WKW3ED.js";
41
42
  import {
42
43
  syncCreditsFromRegistry
43
- } from "./chunk-FMKBCO2Q.js";
44
+ } from "./chunk-ZYOMPJGG.js";
44
45
  import {
45
46
  createLedger,
46
47
  identityAuthPlugin
47
- } from "./chunk-74OZGLIT.js";
48
+ } from "./chunk-LENX5NUW.js";
48
49
  import {
49
50
  deriveAgentId
50
51
  } from "./chunk-5CC6O6SO.js";
51
- import "./chunk-ERT77HKY.js";
52
+ import "./chunk-O44N3KR7.js";
52
53
  import "./chunk-ELFGYC22.js";
53
54
  import {
54
55
  buildReputationMap,
55
56
  computeReputation,
56
57
  filterCards,
57
58
  searchCards
58
- } from "./chunk-QEDVPJKP.js";
59
+ } from "./chunk-I4E5ERDN.js";
59
60
  import {
61
+ NETWORK_FEE_RATE,
60
62
  bootstrapAgent,
61
63
  getBalance,
62
64
  getTransactions,
@@ -68,47 +70,61 @@ import {
68
70
  openCreditDb,
69
71
  releaseEscrow,
70
72
  settleEscrow
71
- } from "./chunk-Z4IDXMSP.js";
72
- import {
73
- RelayMessageSchema
74
- } from "./chunk-3466S65P.js";
75
- import {
76
- generateKeyPair,
77
- verifyEscrowReceipt
78
- } from "./chunk-YNBZLXYS.js";
79
- import "./chunk-YDGXKH2T.js";
80
- import {
81
- getConfigDir
82
- } from "./chunk-3XPBFF6H.js";
73
+ } from "./chunk-D7NH6YLM.js";
83
74
  import {
84
75
  attachCanonicalAgentId,
85
- getActivityFeed,
86
76
  getCard,
87
77
  getCardsBySkillCapability,
88
78
  getEvolutionHistory,
89
79
  getFeedbackForProvider,
90
80
  getFeedbackForSkill,
91
81
  getLatestEvolution,
92
- getRequestLog,
93
- getSkillRequestCount,
94
82
  insertCard,
95
83
  insertEvolution,
96
84
  insertFeedback,
97
- insertRequestLog,
98
85
  listCards,
99
86
  openDatabase,
100
87
  updateCard,
101
88
  updateSkillAvailability,
102
89
  updateSkillIdleRate
103
- } from "./chunk-NLQCHO7N.js";
90
+ } from "./chunk-7VZ4M4CT.js";
91
+ import {
92
+ RelayMessageSchema,
93
+ SESSION_MESSAGE_TYPES,
94
+ SessionEndMessageSchema,
95
+ SessionMessageMessageSchema,
96
+ SessionOpenMessageSchema,
97
+ loadSessionConfig
98
+ } from "./chunk-Q5OFZ2JR.js";
99
+ import {
100
+ loadCoreConfig
101
+ } from "./chunk-QXRNW4OJ.js";
102
+ import {
103
+ generateKeyPair,
104
+ verifyEscrowReceipt
105
+ } from "./chunk-YNBZLXYS.js";
106
+ import "./chunk-YDGXKH2T.js";
104
107
  import "./chunk-J4RFJVXI.js";
105
108
  import {
106
109
  AgentBnBError,
107
110
  AnyCardSchema
108
111
  } from "./chunk-UVCNMRPS.js";
112
+ import {
113
+ getConfigDir
114
+ } from "./chunk-3XPBFF6H.js";
115
+ import {
116
+ getActivityFeed,
117
+ getRequestLog,
118
+ getSkillRequestCount,
119
+ insertRequestLog
120
+ } from "./chunk-4XTYT4JW.js";
121
+ import {
122
+ emitProviderEvent
123
+ } from "./chunk-GZUTU6IZ.js";
124
+ import "./chunk-3RG5ZIWI.js";
109
125
 
110
126
  // src/runtime/agent-runtime.ts
111
- import { readFileSync, existsSync } from "fs";
127
+ import { readFileSync as readFileSync2, existsSync } from "fs";
112
128
 
113
129
  // src/skills/executor.ts
114
130
  function buildTimeoutError(skillId, timeoutMs) {
@@ -554,9 +570,42 @@ var PipelineExecutor = class {
554
570
  };
555
571
 
556
572
  // src/skills/openclaw-bridge.ts
557
- import { execFileSync } from "child_process";
573
+ import { spawnSync } from "child_process";
558
574
  var DEFAULT_BASE_URL = "http://localhost:3000";
559
575
  var DEFAULT_TIMEOUT_MS = 6e4;
576
+ function isOpenClawJsonResponse(value) {
577
+ return typeof value === "object" && value !== null && "payloads" in value && Array.isArray(value.payloads) && "meta" in value;
578
+ }
579
+ function parseOpenClawResponse(raw) {
580
+ if (!isOpenClawJsonResponse(raw)) {
581
+ return raw;
582
+ }
583
+ const { payloads, meta } = raw;
584
+ const texts = payloads.map((p) => p.text).filter((t) => typeof t === "string" && t.length > 0);
585
+ const mediaUrls = payloads.map((p) => p.mediaUrl).filter((u) => typeof u === "string" && u.length > 0);
586
+ const openclawMeta = {
587
+ duration_ms: meta.durationMs,
588
+ model: meta.agentMeta?.model,
589
+ provider: meta.agentMeta?.provider
590
+ };
591
+ if (texts.length === 0) {
592
+ return { text: "", media_urls: mediaUrls, _openclaw_meta: openclawMeta };
593
+ }
594
+ const lastText = texts[texts.length - 1];
595
+ try {
596
+ const structured = JSON.parse(lastText);
597
+ if (typeof structured === "object" && structured !== null) {
598
+ return { ...structured, _openclaw_meta: openclawMeta };
599
+ }
600
+ return { result: structured, _openclaw_meta: openclawMeta };
601
+ } catch {
602
+ return {
603
+ text: texts.join("\n\n"),
604
+ media_urls: mediaUrls.length > 0 ? mediaUrls : void 0,
605
+ _openclaw_meta: openclawMeta
606
+ };
607
+ }
608
+ }
560
609
  function buildPayload(config, params) {
561
610
  return {
562
611
  task: config.name,
@@ -623,7 +672,7 @@ Do NOT include explanations, markdown formatting, or code blocks.
623
672
  The JSON should contain the output fields specified in your SKILL.md.
624
673
  If you cannot complete the task, return: {"error": "reason"}`;
625
674
  try {
626
- const stdout = execFileSync("openclaw", [
675
+ const proc = spawnSync("openclaw", [
627
676
  "agent",
628
677
  "--agent",
629
678
  config.agent_name,
@@ -632,21 +681,37 @@ If you cannot complete the task, return: {"error": "reason"}`;
632
681
  "--json",
633
682
  "--local"
634
683
  ], {
635
- timeout: timeoutMs
684
+ timeout: timeoutMs,
685
+ maxBuffer: 10 * 1024 * 1024
686
+ // 10 MB
636
687
  });
637
- const text = stdout.toString().trim();
688
+ if (proc.error) {
689
+ return { success: false, error: proc.error.message };
690
+ }
691
+ const stderrText = proc.stderr?.toString() ?? "";
692
+ const stdoutText = proc.stdout?.toString() ?? "";
693
+ const text = (stderrText || stdoutText).trim();
694
+ if (!text) {
695
+ return {
696
+ success: false,
697
+ error: `OpenClaw process channel returned empty output (exit code ${proc.status})`
698
+ };
699
+ }
700
+ const jsonStart = text.lastIndexOf("\n{");
701
+ const jsonText = jsonStart >= 0 ? text.slice(jsonStart + 1) : text;
638
702
  try {
639
- const parsed = JSON.parse(text);
640
- return { success: true, result: parsed };
703
+ const parsed = JSON.parse(jsonText);
704
+ const result = parseOpenClawResponse(parsed);
705
+ return { success: true, result };
641
706
  } catch {
642
707
  return {
643
708
  success: false,
644
- error: `OpenClaw process channel returned invalid JSON: ${text}`
709
+ error: `OpenClaw process channel returned invalid JSON: ${jsonText.slice(0, 500)}`
645
710
  };
646
711
  }
647
712
  } catch (err) {
648
- const message2 = err instanceof Error ? err.message : String(err);
649
- return { success: false, error: message2 };
713
+ const errMsg = err instanceof Error ? err.message : String(err);
714
+ return { success: false, error: errMsg };
650
715
  }
651
716
  }
652
717
  async function executeTelegram(config, payload) {
@@ -714,7 +779,30 @@ var OpenClawBridge = class {
714
779
 
715
780
  // src/skills/command-executor.ts
716
781
  import { spawn } from "child_process";
782
+ import { readFileSync, statSync } from "fs";
783
+ import { basename, extname } from "path";
717
784
  var KILL_GRACE_MS = 5e3;
785
+ var MAX_INLINE_FILE_BYTES = 5 * 1024 * 1024;
786
+ var MIME_TYPES = {
787
+ ".mp3": "audio/mpeg",
788
+ ".wav": "audio/wav",
789
+ ".ogg": "audio/ogg",
790
+ ".m4a": "audio/mp4",
791
+ ".pdf": "application/pdf",
792
+ ".png": "image/png",
793
+ ".jpg": "image/jpeg",
794
+ ".jpeg": "image/jpeg",
795
+ ".gif": "image/gif",
796
+ ".webp": "image/webp",
797
+ ".svg": "image/svg+xml",
798
+ ".json": "application/json",
799
+ ".txt": "text/plain",
800
+ ".md": "text/markdown",
801
+ ".csv": "text/csv"
802
+ };
803
+ function guessMimeType(filePath) {
804
+ return MIME_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
805
+ }
718
806
  function shellEscape2(value) {
719
807
  return "'" + value.replace(/'/g, "'\\''") + "'";
720
808
  }
@@ -930,8 +1018,34 @@ var CommandExecutor = class {
930
1018
  };
931
1019
  }
932
1020
  }
933
- case "file":
934
- return { success: true, result: { file_path: rawOutput } };
1021
+ case "file": {
1022
+ try {
1023
+ const stats = statSync(rawOutput);
1024
+ if (stats.size > MAX_INLINE_FILE_BYTES) {
1025
+ return {
1026
+ success: false,
1027
+ error: `File too large for inline return: ${stats.size} bytes (max ${MAX_INLINE_FILE_BYTES})`
1028
+ };
1029
+ }
1030
+ const buffer = readFileSync(rawOutput);
1031
+ return {
1032
+ success: true,
1033
+ result: {
1034
+ file: {
1035
+ name: basename(rawOutput),
1036
+ mime_type: guessMimeType(rawOutput),
1037
+ size_bytes: stats.size,
1038
+ data_base64: buffer.toString("base64")
1039
+ },
1040
+ // Keep file_path for backward compat (local workflows)
1041
+ file_path: rawOutput
1042
+ }
1043
+ };
1044
+ } catch (err) {
1045
+ const msg = err instanceof Error ? err.message : String(err);
1046
+ return { success: false, error: `Failed to read file "${rawOutput}": ${msg}` };
1047
+ }
1048
+ }
935
1049
  default:
936
1050
  return {
937
1051
  success: false,
@@ -1060,14 +1174,14 @@ var AgentRuntime = class {
1060
1174
  }
1061
1175
  let configs = [];
1062
1176
  if (hasSkillsYaml) {
1063
- const yamlContent = readFileSync(this.skillsYamlPath, "utf8");
1177
+ const yamlContent = readFileSync2(this.skillsYamlPath, "utf8");
1064
1178
  configs = parseSkillsFile(yamlContent);
1065
1179
  }
1066
1180
  const modes = /* @__PURE__ */ new Map();
1067
1181
  if (this.conductorEnabled) {
1068
- const { ConductorMode } = await import("./conductor-mode-2UFN6BUL.js");
1069
- const { registerConductorCard, CONDUCTOR_OWNER } = await import("./card-NKQFB3HD.js");
1070
- const { loadPeers } = await import("./peers-F2EWUMVQ.js");
1182
+ const { ConductorMode } = await import("./conductor-mode-3WLLERB4.js");
1183
+ const { registerConductorCard, CONDUCTOR_OWNER } = await import("./card-UF465O7O.js");
1184
+ const { loadPeers } = await import("./peers-7BMU2775.js");
1071
1185
  registerConductorCard(this.registryDb);
1072
1186
  const resolveAgentUrl = (owner) => {
1073
1187
  const peers = loadPeers();
@@ -1401,15 +1515,16 @@ import swaggerUi from "@fastify/swagger-ui";
1401
1515
  import fastifyStatic from "@fastify/static";
1402
1516
  import fastifyWebsocket from "@fastify/websocket";
1403
1517
  import { join, dirname } from "path";
1404
- import { randomUUID as randomUUID6 } from "crypto";
1518
+ import { randomUUID as randomUUID7 } from "crypto";
1405
1519
  import { fileURLToPath } from "url";
1406
1520
  import { existsSync as existsSync2 } from "fs";
1407
1521
  import { z as z4 } from "zod";
1408
1522
 
1409
1523
  // src/relay/websocket-relay.ts
1410
- import { randomUUID as randomUUID4 } from "crypto";
1524
+ import { randomUUID as randomUUID5 } from "crypto";
1411
1525
 
1412
1526
  // src/relay/relay-credit.ts
1527
+ var coreConductorFee = loadCoreConfig("economics");
1413
1528
  function lookupCardPrice(registryDb, cardId, skillId) {
1414
1529
  const row = registryDb.prepare("SELECT data FROM capability_cards WHERE id = ?").get(cardId);
1415
1530
  if (!row) return null;
@@ -1456,8 +1571,11 @@ function settleForRelay(creditDb, escrowId, recipientOwner) {
1456
1571
  }
1457
1572
  function calculateConductorFee(totalSubTaskCost) {
1458
1573
  if (totalSubTaskCost <= 0) return 0;
1459
- const fee = Math.ceil(totalSubTaskCost * 0.1);
1460
- return Math.max(1, Math.min(20, fee));
1574
+ const rate = coreConductorFee?.conductor?.sub_task_fee_rate ?? 0.1;
1575
+ const min = coreConductorFee?.conductor?.sub_task_fee_min ?? 1;
1576
+ const max = coreConductorFee?.conductor?.sub_task_fee_max ?? 20;
1577
+ const fee = Math.ceil(totalSubTaskCost * rate);
1578
+ return Math.max(min, Math.min(max, fee));
1461
1579
  }
1462
1580
  function releaseForRelay(creditDb, escrowId) {
1463
1581
  if (escrowId === void 0) return;
@@ -1511,7 +1629,6 @@ function processEscrowSettle(creditDb, escrowId, success, providerAgentId, signa
1511
1629
  if (!escrowRow) {
1512
1630
  throw new Error(`Escrow not found or already settled: ${escrowId}`);
1513
1631
  }
1514
- const NETWORK_FEE_RATE = 0.05;
1515
1632
  if (success) {
1516
1633
  settleEscrow(creditDb, escrowId, providerAgentId);
1517
1634
  const networkFee = Math.floor(escrowRow.amount * NETWORK_FEE_RATE);
@@ -1835,12 +1952,467 @@ function handleJobRelayResponse(opts) {
1835
1952
  }
1836
1953
  }
1837
1954
 
1955
+ // src/session/session-manager.ts
1956
+ import { randomUUID as randomUUID4 } from "crypto";
1957
+
1958
+ // src/session/session-escrow.ts
1959
+ var SessionEscrow = class {
1960
+ constructor(creditDb) {
1961
+ this.creditDb = creditDb;
1962
+ }
1963
+ /** escrowId → { budget, spent } */
1964
+ tracking = /* @__PURE__ */ new Map();
1965
+ /**
1966
+ * Hold the full session budget in escrow.
1967
+ * @returns The escrow ID for tracking.
1968
+ */
1969
+ holdBudget(owner, budget, cardId) {
1970
+ const escrowId = holdEscrow(this.creditDb, owner, budget, cardId);
1971
+ this.tracking.set(escrowId, { budget, spent: 0 });
1972
+ return escrowId;
1973
+ }
1974
+ /**
1975
+ * Record a per-message deduction against the session budget.
1976
+ * Does not touch the DB — tracking is in-memory until settle.
1977
+ */
1978
+ deductMessage(escrowId, rate) {
1979
+ return this.deduct(escrowId, rate);
1980
+ }
1981
+ /**
1982
+ * Record a per-minute deduction against the session budget.
1983
+ */
1984
+ deductMinute(escrowId, rate) {
1985
+ return this.deduct(escrowId, rate);
1986
+ }
1987
+ /**
1988
+ * Settle the session escrow. Pays the provider the actual cost and
1989
+ * refunds the remainder to the requester.
1990
+ */
1991
+ settle(escrowId, providerOwner) {
1992
+ const t = this.tracking.get(escrowId);
1993
+ if (!t) {
1994
+ settleEscrow(this.creditDb, escrowId, providerOwner);
1995
+ return;
1996
+ }
1997
+ if (t.spent <= 0) {
1998
+ releaseEscrow(this.creditDb, escrowId);
1999
+ } else {
2000
+ settleEscrow(this.creditDb, escrowId, providerOwner);
2001
+ }
2002
+ this.tracking.delete(escrowId);
2003
+ }
2004
+ /**
2005
+ * Release the escrow entirely — full refund to requester.
2006
+ */
2007
+ refund(escrowId) {
2008
+ releaseEscrow(this.creditDb, escrowId);
2009
+ this.tracking.delete(escrowId);
2010
+ }
2011
+ /**
2012
+ * Get remaining budget for a session escrow.
2013
+ */
2014
+ getRemainingBudget(escrowId) {
2015
+ const t = this.tracking.get(escrowId);
2016
+ if (!t) return 0;
2017
+ return t.budget - t.spent;
2018
+ }
2019
+ /**
2020
+ * Get total spent for a session escrow.
2021
+ */
2022
+ getSpent(escrowId) {
2023
+ return this.tracking.get(escrowId)?.spent ?? 0;
2024
+ }
2025
+ /**
2026
+ * Check if the budget is exhausted.
2027
+ */
2028
+ isBudgetExhausted(escrowId) {
2029
+ return this.getRemainingBudget(escrowId) <= 0;
2030
+ }
2031
+ /**
2032
+ * Calculate the cost for a single interaction based on pricing model.
2033
+ */
2034
+ calculateCost(pricingModel, rate, durationMinutes) {
2035
+ switch (pricingModel) {
2036
+ case "per_message":
2037
+ return rate;
2038
+ case "per_minute":
2039
+ return Math.ceil(durationMinutes ?? 1) * rate;
2040
+ case "per_session":
2041
+ return rate;
2042
+ }
2043
+ }
2044
+ deduct(escrowId, amount) {
2045
+ const t = this.tracking.get(escrowId);
2046
+ if (!t) {
2047
+ throw new Error(`No session escrow tracking for ${escrowId}`);
2048
+ }
2049
+ t.spent += amount;
2050
+ return { spent: t.spent, remaining: t.budget - t.spent };
2051
+ }
2052
+ };
2053
+
2054
+ // src/session/session-manager.ts
2055
+ var SessionManager = class {
2056
+ sessions = /* @__PURE__ */ new Map();
2057
+ idleTimers = /* @__PURE__ */ new Map();
2058
+ durationTimers = /* @__PURE__ */ new Map();
2059
+ escrow;
2060
+ config;
2061
+ sendToAgent;
2062
+ registryDb;
2063
+ /** Maps agent connection key → set of session IDs they participate in. */
2064
+ agentSessions = /* @__PURE__ */ new Map();
2065
+ constructor(opts) {
2066
+ this.escrow = new SessionEscrow(opts.creditDb);
2067
+ this.config = opts.config ?? loadSessionConfig();
2068
+ this.sendToAgent = opts.sendToAgent;
2069
+ this.registryDb = opts.registryDb ?? null;
2070
+ }
2071
+ /**
2072
+ * Open a new session between requester and provider.
2073
+ */
2074
+ openSession(msg, requesterKey) {
2075
+ const requesterSessions = this.agentSessions.get(requesterKey);
2076
+ if (requesterSessions && requesterSessions.size >= this.config.abuse.max_concurrent_sessions_per_agent) {
2077
+ this.sendToAgent(requesterKey, {
2078
+ type: "session_error",
2079
+ session_id: msg.session_id,
2080
+ code: "MAX_CONCURRENT_SESSIONS",
2081
+ message: `Maximum concurrent sessions (${this.config.abuse.max_concurrent_sessions_per_agent}) reached`
2082
+ });
2083
+ throw new Error("Max concurrent sessions reached");
2084
+ }
2085
+ const escrowId = this.escrow.holdBudget(msg.requester_id, msg.budget, msg.card_id);
2086
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2087
+ const session = {
2088
+ id: msg.session_id,
2089
+ requester_id: msg.requester_id,
2090
+ provider_id: msg.provider_id,
2091
+ skill_id: msg.skill_id,
2092
+ card_id: msg.card_id,
2093
+ status: "open",
2094
+ escrow_id: escrowId,
2095
+ budget: msg.budget,
2096
+ spent: 0,
2097
+ pricing_model: msg.pricing_model,
2098
+ messages: [],
2099
+ created_at: now,
2100
+ updated_at: now
2101
+ };
2102
+ this.sessions.set(session.id, session);
2103
+ this.trackAgentSession(requesterKey, session.id);
2104
+ this.trackAgentSession(msg.provider_id, session.id);
2105
+ this.sendToAgent(requesterKey, {
2106
+ type: "session_ack",
2107
+ session_id: session.id,
2108
+ escrow_id: escrowId,
2109
+ status: "open"
2110
+ });
2111
+ const initialMsg = this.createMessage(session.id, "requester", msg.initial_message);
2112
+ session.messages.push(initialMsg);
2113
+ session.status = "active";
2114
+ session.updated_at = (/* @__PURE__ */ new Date()).toISOString();
2115
+ this.sendToAgent(msg.provider_id, {
2116
+ type: "session_message",
2117
+ session_id: session.id,
2118
+ sender: "requester",
2119
+ content: msg.initial_message
2120
+ });
2121
+ if (session.pricing_model === "per_session") {
2122
+ const cost = this.config.pricing.per_session_flat_rate;
2123
+ this.escrow.deductMessage(escrowId, cost);
2124
+ session.spent = cost;
2125
+ }
2126
+ this.resetIdleTimer(session.id);
2127
+ this.startDurationTimer(session.id);
2128
+ this.emitEvent({
2129
+ event_type: "session.opened",
2130
+ skill_id: session.skill_id,
2131
+ session_id: session.id,
2132
+ requester: session.requester_id,
2133
+ credits: session.budget,
2134
+ duration_ms: 0,
2135
+ metadata: { engine: "session", pricing_model: session.pricing_model }
2136
+ });
2137
+ return session;
2138
+ }
2139
+ /**
2140
+ * Route a message within an active session.
2141
+ */
2142
+ routeMessage(msg, senderKey) {
2143
+ const session = this.sessions.get(msg.session_id);
2144
+ if (!session) {
2145
+ this.sendToAgent(senderKey, {
2146
+ type: "session_error",
2147
+ session_id: msg.session_id,
2148
+ code: "SESSION_NOT_FOUND",
2149
+ message: "Session not found"
2150
+ });
2151
+ return;
2152
+ }
2153
+ if (session.status !== "active") {
2154
+ this.sendToAgent(senderKey, {
2155
+ type: "session_error",
2156
+ session_id: msg.session_id,
2157
+ code: "SESSION_NOT_ACTIVE",
2158
+ message: `Session is ${session.status}, not active`
2159
+ });
2160
+ return;
2161
+ }
2162
+ if (session.messages.length >= this.config.pricing.max_messages_per_session) {
2163
+ this.endSessionInternal(session, "budget_exhausted");
2164
+ return;
2165
+ }
2166
+ if (session.pricing_model === "per_message" && msg.sender === "provider") {
2167
+ const rate = this.config.pricing.per_message_base_rate;
2168
+ const { remaining } = this.escrow.deductMessage(session.escrow_id, rate);
2169
+ session.spent += rate;
2170
+ if (remaining <= 0) {
2171
+ const record2 = this.createMessage(session.id, msg.sender, msg.content, msg.metadata);
2172
+ session.messages.push(record2);
2173
+ session.updated_at = (/* @__PURE__ */ new Date()).toISOString();
2174
+ const targetKey2 = this.getCounterpartyKey(session, msg.sender);
2175
+ this.sendToAgent(targetKey2, {
2176
+ type: "session_message",
2177
+ session_id: session.id,
2178
+ sender: msg.sender,
2179
+ content: msg.content,
2180
+ metadata: msg.metadata
2181
+ });
2182
+ this.endSessionInternal(session, "budget_exhausted");
2183
+ return;
2184
+ }
2185
+ }
2186
+ const record = this.createMessage(session.id, msg.sender, msg.content, msg.metadata);
2187
+ session.messages.push(record);
2188
+ session.updated_at = (/* @__PURE__ */ new Date()).toISOString();
2189
+ const targetKey = this.getCounterpartyKey(session, msg.sender);
2190
+ this.sendToAgent(targetKey, {
2191
+ type: "session_message",
2192
+ session_id: session.id,
2193
+ sender: msg.sender,
2194
+ content: msg.content,
2195
+ metadata: msg.metadata
2196
+ });
2197
+ this.emitEvent({
2198
+ event_type: "session.message",
2199
+ skill_id: session.skill_id,
2200
+ session_id: session.id,
2201
+ requester: session.requester_id,
2202
+ credits: session.spent,
2203
+ duration_ms: Date.now() - new Date(session.created_at).getTime(),
2204
+ metadata: { message_count: session.messages.length, running_cost: session.spent, sender: msg.sender }
2205
+ });
2206
+ this.resetIdleTimer(session.id);
2207
+ }
2208
+ /**
2209
+ * End a session at the request of either party.
2210
+ */
2211
+ endSession(msg, _senderKey) {
2212
+ const session = this.sessions.get(msg.session_id);
2213
+ if (!session) return;
2214
+ if (session.status === "settled" || session.status === "closed") return;
2215
+ this.endSessionInternal(session, msg.reason);
2216
+ }
2217
+ /**
2218
+ * Handle agent disconnection — end all their active sessions.
2219
+ */
2220
+ handleDisconnect(agentKey) {
2221
+ const sessionIds = this.agentSessions.get(agentKey);
2222
+ if (!sessionIds) return;
2223
+ for (const sessionId of sessionIds) {
2224
+ const session = this.sessions.get(sessionId);
2225
+ if (session && session.status !== "settled" && session.status !== "closed") {
2226
+ this.endSessionInternal(session, "error");
2227
+ }
2228
+ }
2229
+ }
2230
+ /** Get a session by ID. */
2231
+ getSession(sessionId) {
2232
+ return this.sessions.get(sessionId);
2233
+ }
2234
+ /** List sessions, optionally filtered by agent. */
2235
+ listSessions(agentId) {
2236
+ if (!agentId) return Array.from(this.sessions.values());
2237
+ return Array.from(this.sessions.values()).filter(
2238
+ (s) => s.requester_id === agentId || s.provider_id === agentId
2239
+ );
2240
+ }
2241
+ /** Clean up all timers (for graceful shutdown). */
2242
+ shutdown() {
2243
+ for (const timer of this.idleTimers.values()) clearTimeout(timer);
2244
+ for (const timer of this.durationTimers.values()) clearTimeout(timer);
2245
+ this.idleTimers.clear();
2246
+ this.durationTimers.clear();
2247
+ }
2248
+ // -------------------------------------------------------------------------
2249
+ // Event emission
2250
+ // -------------------------------------------------------------------------
2251
+ /** Emit a provider event + Telegram notification. Silently no-ops on failure. */
2252
+ emitEvent(event) {
2253
+ if (!this.registryDb) return;
2254
+ try {
2255
+ const emitted = emitProviderEvent(this.registryDb, event);
2256
+ notifyProviderEvent(emitted).catch(() => {
2257
+ });
2258
+ } catch {
2259
+ }
2260
+ }
2261
+ // -------------------------------------------------------------------------
2262
+ // Internal helpers
2263
+ // -------------------------------------------------------------------------
2264
+ endSessionInternal(session, reason) {
2265
+ session.status = "closing";
2266
+ session.end_reason = reason;
2267
+ session.ended_at = (/* @__PURE__ */ new Date()).toISOString();
2268
+ this.clearTimers(session.id);
2269
+ if (session.spent > 0) {
2270
+ this.escrow.settle(session.escrow_id, session.provider_id);
2271
+ } else {
2272
+ this.escrow.refund(session.escrow_id);
2273
+ }
2274
+ const durationMs = Date.now() - new Date(session.created_at).getTime();
2275
+ const settledMsg = {
2276
+ type: "session_settled",
2277
+ session_id: session.id,
2278
+ total_cost: session.spent,
2279
+ messages_count: session.messages.length,
2280
+ duration_seconds: Math.round(durationMs / 1e3),
2281
+ refunded: session.budget - session.spent
2282
+ };
2283
+ session.status = "settled";
2284
+ session.updated_at = (/* @__PURE__ */ new Date()).toISOString();
2285
+ const isFailed = reason === "error";
2286
+ const eventMetadata = {
2287
+ total_messages: session.messages.length,
2288
+ reason,
2289
+ refunded: session.budget - session.spent
2290
+ };
2291
+ if (isFailed) {
2292
+ eventMetadata["last_messages"] = session.messages.slice(-3).map((m) => ({
2293
+ sender: m.sender,
2294
+ content: m.content.slice(0, 200)
2295
+ }));
2296
+ }
2297
+ this.emitEvent({
2298
+ event_type: isFailed ? "session.failed" : "session.ended",
2299
+ skill_id: session.skill_id,
2300
+ session_id: session.id,
2301
+ requester: session.requester_id,
2302
+ credits: session.spent,
2303
+ duration_ms: durationMs,
2304
+ metadata: eventMetadata
2305
+ });
2306
+ this.sendToAgent(session.requester_id, settledMsg);
2307
+ this.sendToAgent(session.provider_id, settledMsg);
2308
+ session.status = "closed";
2309
+ this.untrackAgentSession(session.requester_id, session.id);
2310
+ this.untrackAgentSession(session.provider_id, session.id);
2311
+ }
2312
+ resetIdleTimer(sessionId) {
2313
+ const existing = this.idleTimers.get(sessionId);
2314
+ if (existing) clearTimeout(existing);
2315
+ const timer = setTimeout(() => {
2316
+ const session = this.sessions.get(sessionId);
2317
+ if (session && session.status === "active") {
2318
+ this.endSessionInternal(session, "timeout");
2319
+ }
2320
+ }, this.config.timeouts.idle_timeout_ms);
2321
+ this.idleTimers.set(sessionId, timer);
2322
+ }
2323
+ startDurationTimer(sessionId) {
2324
+ const timer = setTimeout(() => {
2325
+ const session = this.sessions.get(sessionId);
2326
+ if (session && session.status !== "settled" && session.status !== "closed") {
2327
+ this.endSessionInternal(session, "timeout");
2328
+ }
2329
+ }, this.config.timeouts.max_session_duration_ms);
2330
+ this.durationTimers.set(sessionId, timer);
2331
+ }
2332
+ clearTimers(sessionId) {
2333
+ const idle = this.idleTimers.get(sessionId);
2334
+ if (idle) {
2335
+ clearTimeout(idle);
2336
+ this.idleTimers.delete(sessionId);
2337
+ }
2338
+ const dur = this.durationTimers.get(sessionId);
2339
+ if (dur) {
2340
+ clearTimeout(dur);
2341
+ this.durationTimers.delete(sessionId);
2342
+ }
2343
+ }
2344
+ createMessage(sessionId, sender, content, metadata) {
2345
+ return {
2346
+ id: randomUUID4(),
2347
+ session_id: sessionId,
2348
+ sender,
2349
+ content,
2350
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2351
+ metadata
2352
+ };
2353
+ }
2354
+ getCounterpartyKey(session, sender) {
2355
+ return sender === "requester" ? session.provider_id : session.requester_id;
2356
+ }
2357
+ trackAgentSession(agentKey, sessionId) {
2358
+ let set = this.agentSessions.get(agentKey);
2359
+ if (!set) {
2360
+ set = /* @__PURE__ */ new Set();
2361
+ this.agentSessions.set(agentKey, set);
2362
+ }
2363
+ set.add(sessionId);
2364
+ }
2365
+ untrackAgentSession(agentKey, sessionId) {
2366
+ const set = this.agentSessions.get(agentKey);
2367
+ if (set) {
2368
+ set.delete(sessionId);
2369
+ if (set.size === 0) this.agentSessions.delete(agentKey);
2370
+ }
2371
+ }
2372
+ };
2373
+
2374
+ // src/session/session-relay.ts
2375
+ function attachSessionHandler(opts) {
2376
+ const { sessionManager } = opts;
2377
+ return {
2378
+ handleSessionMessage(msg, senderKey) {
2379
+ if (!SESSION_MESSAGE_TYPES.has(msg.type)) return false;
2380
+ switch (msg.type) {
2381
+ case "session_open": {
2382
+ const parsed = SessionOpenMessageSchema.parse(msg);
2383
+ sessionManager.openSession(parsed, senderKey);
2384
+ return true;
2385
+ }
2386
+ case "session_message": {
2387
+ const parsed = SessionMessageMessageSchema.parse(msg);
2388
+ sessionManager.routeMessage(parsed, senderKey);
2389
+ return true;
2390
+ }
2391
+ case "session_end": {
2392
+ const parsed = SessionEndMessageSchema.parse(msg);
2393
+ sessionManager.endSession(parsed, senderKey);
2394
+ return true;
2395
+ }
2396
+ // session_ack, session_settled, session_error are relay→agent only
2397
+ // They should not arrive from agents, but we silently absorb them
2398
+ case "session_ack":
2399
+ case "session_settled":
2400
+ case "session_error":
2401
+ return true;
2402
+ default:
2403
+ return false;
2404
+ }
2405
+ }
2406
+ };
2407
+ }
2408
+
1838
2409
  // src/relay/websocket-relay.ts
1839
- var RATE_LIMIT_MAX = 60;
1840
- var RATE_LIMIT_WINDOW_MS = 6e4;
1841
- var RELAY_IDLE_TIMEOUT_MS = 3e4;
1842
- var RELAY_HARD_TIMEOUT_MS = 3e5;
1843
- var RELAY_DISCONNECT_GRACE_MS = RELAY_HARD_TIMEOUT_MS + 3e4;
2410
+ var coreRelay = loadCoreConfig("relay");
2411
+ var RATE_LIMIT_MAX = coreRelay?.rate_limit_max ?? 60;
2412
+ var RATE_LIMIT_WINDOW_MS = coreRelay?.rate_limit_window_ms ?? 6e4;
2413
+ var RELAY_IDLE_TIMEOUT_MS = coreRelay?.relay_idle_timeout_ms ?? 3e4;
2414
+ var RELAY_HARD_TIMEOUT_MS = coreRelay?.relay_hard_timeout_ms ?? 3e5;
2415
+ var RELAY_DISCONNECT_GRACE_MS = coreRelay?.relay_disconnect_grace_ms ?? RELAY_HARD_TIMEOUT_MS + 3e4;
1844
2416
  function readTimeoutOverride(envKey, fallbackMs) {
1845
2417
  const raw = process.env[envKey];
1846
2418
  if (!raw) return fallbackMs;
@@ -2071,7 +2643,7 @@ function registerWebSocketRelay(server, db, creditDb) {
2071
2643
  function logAgentJoined(owner, cardName, cardId) {
2072
2644
  try {
2073
2645
  insertRequestLog(db, {
2074
- id: randomUUID4(),
2646
+ id: randomUUID5(),
2075
2647
  card_id: cardId,
2076
2648
  card_name: cardName,
2077
2649
  requester: owner,
@@ -2089,6 +2661,19 @@ function registerWebSocketRelay(server, db, creditDb) {
2089
2661
  ws.send(JSON.stringify(msg));
2090
2662
  }
2091
2663
  }
2664
+ function sendToAgentByKey(agentKey, msg) {
2665
+ const connKey = resolveConnectionKey(agentKey);
2666
+ const ws = connKey ? connections.get(connKey) : void 0;
2667
+ if (ws && ws.readyState === 1) {
2668
+ ws.send(JSON.stringify(msg));
2669
+ }
2670
+ }
2671
+ const sessionManager = creditDb ? new SessionManager({
2672
+ creditDb,
2673
+ sendToAgent: sendToAgentByKey,
2674
+ isAgentOnline: (key) => !!resolveConnectionKey(key)
2675
+ }) : void 0;
2676
+ const sessionHandler = sessionManager ? attachSessionHandler({ sessionManager }) : void 0;
2092
2677
  function handleRegister(ws, msg) {
2093
2678
  const { owner, card } = msg;
2094
2679
  const existing = connections.get(owner);
@@ -2308,6 +2893,9 @@ function registerWebSocketRelay(server, db, creditDb) {
2308
2893
  }
2309
2894
  }
2310
2895
  markOwnerOffline(owner);
2896
+ if (sessionManager) {
2897
+ sessionManager.handleDisconnect(owner);
2898
+ }
2311
2899
  for (const [reqId, pending] of pendingRequests) {
2312
2900
  if (pending.targetOwner === owner) {
2313
2901
  clearPendingTimers(pending);
@@ -2502,6 +3090,13 @@ function registerWebSocketRelay(server, db, creditDb) {
2502
3090
  handleBalanceSync(socket, msg);
2503
3091
  break;
2504
3092
  default:
3093
+ if (sessionHandler && registeredOwner) {
3094
+ const handled = sessionHandler.handleSessionMessage(
3095
+ msg,
3096
+ registeredOwner
3097
+ );
3098
+ if (handled) break;
3099
+ }
2505
3100
  break;
2506
3101
  }
2507
3102
  })();
@@ -2536,6 +3131,7 @@ function registerWebSocketRelay(server, db, creditDb) {
2536
3131
  pendingRequests.clear();
2537
3132
  rateLimits.clear();
2538
3133
  agentCapacities.clear();
3134
+ if (sessionManager) sessionManager.shutdown();
2539
3135
  },
2540
3136
  setOnAgentOnline: (cb) => {
2541
3137
  onAgentOnlineCallback = cb;
@@ -2552,7 +3148,7 @@ function registerWebSocketRelay(server, db, creditDb) {
2552
3148
 
2553
3149
  // src/identity/guarantor.ts
2554
3150
  import { z } from "zod";
2555
- import { randomUUID as randomUUID5 } from "crypto";
3151
+ import { randomUUID as randomUUID6 } from "crypto";
2556
3152
  var MAX_AGENTS_PER_GUARANTOR = 10;
2557
3153
  var GUARANTOR_CREDIT_POOL = 50;
2558
3154
  var GuarantorRecordSchema = z.object({
@@ -2591,7 +3187,7 @@ function registerGuarantor(db, githubLogin) {
2591
3187
  );
2592
3188
  }
2593
3189
  const record = {
2594
- id: randomUUID5(),
3190
+ id: randomUUID6(),
2595
3191
  github_login: githubLogin,
2596
3192
  agent_count: 0,
2597
3193
  credit_pool: GUARANTOR_CREDIT_POOL,
@@ -2665,7 +3261,7 @@ function getAgentGuarantor(db, agentId) {
2665
3261
  function initiateGithubAuth() {
2666
3262
  return {
2667
3263
  auth_url: "https://github.com/login/oauth/authorize?client_id=PLACEHOLDER&scope=read:user",
2668
- state: randomUUID5()
3264
+ state: randomUUID6()
2669
3265
  };
2670
3266
  }
2671
3267
 
@@ -4954,14 +5550,14 @@ function createRegistryServer(opts) {
4954
5550
  return { card_id: target.cardId, skill_id: target.skillId };
4955
5551
  }
4956
5552
  if (!relayClient) {
4957
- const { RelayClient } = await import("./websocket-client-5CRE36Z5.js");
5553
+ const { RelayClient } = await import("./websocket-client-FCPZOE4S.js");
4958
5554
  relayClient = new RelayClient({
4959
5555
  registryUrl: relayRegistryUrl,
4960
5556
  owner: relayRequesterOwner,
4961
5557
  token: "batch-token",
4962
5558
  card: {
4963
5559
  spec_version: "1.0",
4964
- id: randomUUID6(),
5560
+ id: randomUUID7(),
4965
5561
  owner: relayRequesterOwner,
4966
5562
  name: relayRequesterOwner,
4967
5563
  description: "Batch requester",
@@ -4976,7 +5572,7 @@ function createRegistryServer(opts) {
4976
5572
  });
4977
5573
  await relayClient.connect();
4978
5574
  }
4979
- const { requestViaRelay } = await import("./client-YB3IYO3S.js");
5575
+ const { requestViaRelay } = await import("./client-XOSXFC7Q.js");
4980
5576
  return requestViaRelay(relayClient, {
4981
5577
  targetOwner: target.owner,
4982
5578
  cardId: target.cardId,
@@ -5204,6 +5800,62 @@ function createRegistryServer(opts) {
5204
5800
  const items = await ledger.getHistory(ownerName, limit);
5205
5801
  return reply.send({ items, limit });
5206
5802
  });
5803
+ ownerRoutes.get("/me/events", {
5804
+ schema: {
5805
+ tags: ["owner"],
5806
+ summary: "Provider event stream",
5807
+ security: [{ bearerAuth: [] }],
5808
+ querystring: {
5809
+ type: "object",
5810
+ properties: {
5811
+ limit: { type: "integer", description: "Max entries (default 50)" },
5812
+ since: { type: "string", description: "ISO timestamp for cursor-based polling" },
5813
+ event_type: { type: "string", description: "Filter by event type" }
5814
+ }
5815
+ }
5816
+ }
5817
+ }, async (request, reply) => {
5818
+ const { getProviderEvents: getEvents } = await import("./provider-events-GTTJPYHS.js");
5819
+ const query = request.query;
5820
+ const limit = query.limit ? parseInt(query.limit, 10) : void 0;
5821
+ const events = getEvents(db, {
5822
+ limit,
5823
+ since: query.since,
5824
+ event_type: query.event_type
5825
+ });
5826
+ return reply.send({ events });
5827
+ });
5828
+ ownerRoutes.get("/me/stats", {
5829
+ schema: {
5830
+ tags: ["owner"],
5831
+ summary: "Aggregated provider stats",
5832
+ security: [{ bearerAuth: [] }],
5833
+ querystring: {
5834
+ type: "object",
5835
+ properties: { period: { type: "string", enum: ["24h", "7d", "30d"] } }
5836
+ }
5837
+ }
5838
+ }, async (request, reply) => {
5839
+ const { getProviderStats: getStats } = await import("./provider-events-GTTJPYHS.js");
5840
+ const query = request.query;
5841
+ const period = query.period ?? "7d";
5842
+ const stats = getStats(db, period);
5843
+ if (opts.creditDb) {
5844
+ const periodMs = { "24h": 864e5, "7d": 6048e5, "30d": 2592e6 }[period];
5845
+ const cutoff = new Date(Date.now() - periodMs).toISOString();
5846
+ try {
5847
+ const row = opts.creditDb.prepare(`
5848
+ SELECT COALESCE(SUM(CASE WHEN amount < 0 AND reason IN ('escrow_hold', 'voucher_hold', 'network_fee') THEN -amount ELSE 0 END), 0) as spent
5849
+ FROM credit_transactions
5850
+ WHERE owner = ? AND created_at >= ?
5851
+ `).get(ownerName, cutoff);
5852
+ stats.total_spending = row?.spent ?? 0;
5853
+ stats.net_pnl = stats.total_earnings - stats.total_spending;
5854
+ } catch {
5855
+ }
5856
+ }
5857
+ return reply.send(stats);
5858
+ });
5207
5859
  });
5208
5860
  }
5209
5861
  api.get("/api/providers/:owner/reliability", {
@@ -5232,7 +5884,7 @@ function createRegistryServer(opts) {
5232
5884
  if (!opts.creditDb) {
5233
5885
  return reply.code(404).send({ error: "Credit system not enabled" });
5234
5886
  }
5235
- const { getReliabilityMetrics } = await import("./reliability-metrics-KKUFFVB6.js");
5887
+ const { getReliabilityMetrics } = await import("./reliability-metrics-MIJ3TJWL.js");
5236
5888
  const metrics = getReliabilityMetrics(opts.creditDb, owner);
5237
5889
  if (!metrics) {
5238
5890
  return reply.code(404).send({ error: "No reliability data for this provider" });
@@ -5288,7 +5940,7 @@ function createRegistryServer(opts) {
5288
5940
  }
5289
5941
  let reliability = null;
5290
5942
  if (opts.creditDb) {
5291
- const { getReliabilityMetrics } = await import("./reliability-metrics-KKUFFVB6.js");
5943
+ const { getReliabilityMetrics } = await import("./reliability-metrics-MIJ3TJWL.js");
5292
5944
  reliability = getReliabilityMetrics(opts.creditDb, providerIdentity);
5293
5945
  }
5294
5946
  agents.push({
@@ -5408,13 +6060,13 @@ var IdleMonitor = class {
5408
6060
 
5409
6061
  // src/runtime/service-coordinator.ts
5410
6062
  import { spawn as spawn2 } from "child_process";
5411
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
6063
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
5412
6064
  import { join as join2 } from "path";
5413
- import { randomUUID as randomUUID7 } from "crypto";
6065
+ import { randomUUID as randomUUID8 } from "crypto";
5414
6066
  import { Cron as Cron2 } from "croner";
5415
6067
  function buildFallbackRelayCard(owner) {
5416
6068
  return {
5417
- id: randomUUID7(),
6069
+ id: randomUUID8(),
5418
6070
  owner,
5419
6071
  name: owner,
5420
6072
  description: "Agent registered via CLI",
@@ -5583,7 +6235,7 @@ var ServiceCoordinator = class {
5583
6235
  console.log("Conductor mode enabled \u2014 orchestrate/plan skills available via gateway");
5584
6236
  }
5585
6237
  if (opts.conductorEnabled && this.config.conductor?.public) {
5586
- const { buildConductorCard } = await import("./card-NKQFB3HD.js");
6238
+ const { buildConductorCard } = await import("./card-UF465O7O.js");
5587
6239
  const conductorCard = attachCanonicalAgentId(
5588
6240
  this.runtime.registryDb,
5589
6241
  buildConductorCard(this.config.owner)
@@ -5659,8 +6311,8 @@ var ServiceCoordinator = class {
5659
6311
  }
5660
6312
  }
5661
6313
  if (opts.registryUrl && opts.relay) {
5662
- const { RelayClient } = await import("./websocket-client-5CRE36Z5.js");
5663
- const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("./execute-2Z3XIUHR.js");
6314
+ const { RelayClient } = await import("./websocket-client-FCPZOE4S.js");
6315
+ const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("./execute-UFMGTXET.js");
5664
6316
  const localCards = listCards(this.runtime.registryDb, this.config.owner);
5665
6317
  const { primaryCard, additionalCards } = buildRelayRegistrationCards(this.config.owner, localCards);
5666
6318
  if (this.config.conductor?.public) {
@@ -5921,7 +6573,7 @@ function loadPersistedRuntime(configDir) {
5921
6573
  const runtimePath = join2(configDir, "runtime.json");
5922
6574
  if (!existsSync3(runtimePath)) return null;
5923
6575
  try {
5924
- const raw = readFileSync2(runtimePath, "utf8");
6576
+ const raw = readFileSync3(runtimePath, "utf8");
5925
6577
  const parsed = JSON.parse(raw);
5926
6578
  const nodeExec = parsed["node_exec"];
5927
6579
  if (typeof nodeExec !== "string" || nodeExec.trim().length === 0) {