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.
- package/README.md +29 -4
- package/dist/{card-6KL6L4GF.js → card-U2HQRPYN.js} +4 -1
- package/dist/{card-NKQFB3HD.js → card-UF465O7O.js} +4 -1
- package/dist/{chunk-77HAL2ZL.js → chunk-2HH2F3DM.js} +189 -68
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-4HLGFR72.js +155 -0
- package/dist/chunk-4XTYT4JW.js +147 -0
- package/dist/{chunk-NLQCHO7N.js → chunk-7VZ4M4CT.js} +7 -134
- package/dist/{chunk-UQCQ2JCG.js → chunk-AMABG5SI.js} +7 -7
- package/dist/{chunk-N3TXLBGK.js → chunk-AW4VSROG.js} +1 -1
- package/dist/{chunk-PG3CLSAH.js → chunk-BPPFY72X.js} +7 -134
- package/dist/{chunk-Z4IDXMSP.js → chunk-D7NH6YLM.js} +6 -1
- package/dist/{chunk-SLZBE2I5.js → chunk-F2CIPAN2.js} +19 -14
- package/dist/{chunk-FUGWPKXN.js → chunk-G4TF4LB4.js} +1 -1
- package/dist/chunk-GZUTU6IZ.js +153 -0
- package/dist/{chunk-QEDVPJKP.js → chunk-I4E5ERDN.js} +20 -16
- package/dist/{chunk-74OZGLIT.js → chunk-LENX5NUW.js} +1 -1
- package/dist/{chunk-BNS76U6K.js → chunk-MPS4RE7T.js} +26 -20
- package/dist/{chunk-ERT77HKY.js → chunk-O44N3KR7.js} +2 -2
- package/dist/chunk-Q5OFZ2JR.js +292 -0
- package/dist/{chunk-YJ3RGKPU.js → chunk-QG2LLVXP.js} +6 -2
- package/dist/chunk-QXRNW4OJ.js +35 -0
- package/dist/{chunk-76YORWFJ.js → chunk-RVOZHVM7.js} +186 -63
- package/dist/{chunk-2SOHHB2O.js → chunk-TCA63C42.js} +39 -16
- package/dist/{chunk-UR3MISL2.js → chunk-UPNREF4L.js} +1 -1
- package/dist/{chunk-SMQDT7CT.js → chunk-WARYO57F.js} +7 -3
- package/dist/{chunk-DYJ7YGBM.js → chunk-WOVESOQ7.js} +237 -124
- package/dist/{chunk-I3RRMAAD.js → chunk-Y7CO3VLF.js} +8 -8
- package/dist/{chunk-FMKBCO2Q.js → chunk-ZYOMPJGG.js} +2 -2
- package/dist/cli/index.js +147 -63
- package/dist/{client-YB3IYO3S.js → client-XOSXFC7Q.js} +1 -0
- package/dist/conduct-MALC6HEK.js +30 -0
- package/dist/{conduct-URYWMA5T.js → conduct-UT6ZYSJD.js} +16 -12
- package/dist/{conductor-mode-2UFN6BUL.js → conductor-mode-3WLLERB4.js} +23 -19
- package/dist/{conductor-mode-NRSVP2AU.js → conductor-mode-UJKMO2GW.js} +15 -11
- package/dist/{config-IRWLG6IW.js → config-ZFWBAGDU.js} +1 -0
- package/dist/{credits-action-24EPLUHG.js → credits-action-KOUJNR36.js} +7 -5
- package/dist/{daemon-A7DXZIQW.js → daemon-ETXXE4IS.js} +2 -1
- package/dist/{did-action-MQLDT4RF.js → did-action-UHUYMA4Y.js} +2 -1
- package/dist/{execute-DNRNU3HM.js → execute-3RADNI74.js} +9 -5
- package/dist/{execute-2Z3XIUHR.js → execute-UFMGTXET.js} +12 -8
- package/dist/index.d.ts +412 -275
- package/dist/index.js +1198 -391
- package/dist/{openclaw-setup-WA625DZA.js → openclaw-setup-HEWZZOY7.js} +21 -17
- package/dist/{openclaw-skills-76ZWXHFM.js → openclaw-skills-5XLQFRWT.js} +7 -6
- package/dist/{peers-F2EWUMVQ.js → peers-7BMU2775.js} +1 -0
- package/dist/{peers-CJ7T4RJO.js → peers-IOVCBWAI.js} +1 -0
- package/dist/{process-guard-QDBIOLY4.js → process-guard-6324CZDC.js} +1 -0
- package/dist/provider-events-GTTJPYHS.js +13 -0
- package/dist/{publish-capability-FOCHYNYE.js → publish-capability-LM4RSQXX.js} +5 -2
- package/dist/{reliability-metrics-JSOY3PNW.js → reliability-metrics-22JTZGB4.js} +1 -0
- package/dist/{reliability-metrics-KKUFFVB6.js → reliability-metrics-MIJ3TJWL.js} +1 -0
- package/dist/{request-KPKWBL5W.js → request-LID2N42Y.js} +17 -13
- package/dist/request-log-2D253WML.js +17 -0
- package/dist/request-log-SIGTGOFA.js +16 -0
- package/dist/{scanner-GP4AOCW6.js → scanner-EFU6NBEJ.js} +1 -0
- package/dist/{schema-7BSSLZ4S.js → schema-FABVZKSI.js} +1 -0
- package/dist/{serve-skill-QSUIK3ZF.js → serve-skill-CDNSHTEE.js} +17 -13
- package/dist/{server-OCCAVVDF.js → server-QIAO3YSK.js} +23 -15
- package/dist/{service-coordinator-4JAUUNUL.js → service-coordinator-FB44QL7L.js} +721 -69
- package/dist/session-5AIRM7YF.js +144 -0
- package/dist/session-action-GYITLYOE.js +131 -0
- package/dist/{skill-config-5O2VR546.js → skill-config-VYNF7BCY.js} +1 -0
- package/dist/{skill-wrap-YLCJMFEJ.js → skill-wrap-IAZHOYM4.js} +1 -0
- package/dist/skills/agentbnb/bootstrap.js +750 -98
- package/dist/{store-S22F3I7G.js → store-C4DLIXYM.js} +4 -1
- package/dist/{vc-action-SUD7TMN2.js → vc-action-BWGNQ77Y.js} +2 -1
- package/dist/websocket-client-FCPZOE4S.js +9 -0
- package/dist/websocket-client-RT4KLJL4.js +8 -0
- package/dist/{writer-4QJ3U3WE.js → writer-V7JBWKKZ.js} +1 -0
- package/package.json +3 -1
- package/dist/chunk-3466S65P.js +0 -179
- package/dist/conduct-UAEEMVFD.js +0 -26
- package/dist/websocket-client-5CRE36Z5.js +0 -7
- 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
|
-
|
|
16
|
-
|
|
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-
|
|
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-
|
|
44
|
+
} from "./chunk-ZYOMPJGG.js";
|
|
44
45
|
import {
|
|
45
46
|
createLedger,
|
|
46
47
|
identityAuthPlugin
|
|
47
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-LENX5NUW.js";
|
|
48
49
|
import {
|
|
49
50
|
deriveAgentId
|
|
50
51
|
} from "./chunk-5CC6O6SO.js";
|
|
51
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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(
|
|
640
|
-
|
|
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: ${
|
|
709
|
+
error: `OpenClaw process channel returned invalid JSON: ${jsonText.slice(0, 500)}`
|
|
645
710
|
};
|
|
646
711
|
}
|
|
647
712
|
} catch (err) {
|
|
648
|
-
const
|
|
649
|
-
return { success: false, error:
|
|
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
|
-
|
|
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 =
|
|
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-
|
|
1069
|
-
const { registerConductorCard, CONDUCTOR_OWNER } = await import("./card-
|
|
1070
|
-
const { loadPeers } = await import("./peers-
|
|
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
|
|
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
|
|
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
|
|
1460
|
-
|
|
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
|
|
1840
|
-
var
|
|
1841
|
-
var
|
|
1842
|
-
var
|
|
1843
|
-
var
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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-
|
|
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:
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
6063
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
5412
6064
|
import { join as join2 } from "path";
|
|
5413
|
-
import { randomUUID as
|
|
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:
|
|
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-
|
|
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-
|
|
5663
|
-
const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("./execute-
|
|
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 =
|
|
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) {
|