agentbnb 5.1.11 → 7.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +245 -39
- package/dist/{card-RSGDCHCV.js → card-REW7BSWW.js} +1 -1
- package/dist/{chunk-FLY3WIQR.js → chunk-2HSUPCBT.js} +3 -3
- package/dist/{chunk-WGZ5AGOX.js → chunk-3CIMVISQ.js} +24 -1
- package/dist/{chunk-NH2FIERR.js → chunk-574W3HHE.js} +1 -1
- package/dist/{chunk-WTXRY7R2.js → chunk-APEG4QIN.js} +157 -9
- package/dist/chunk-BP3L2TET.js +148 -0
- package/dist/{chunk-NLAWT4DT.js → chunk-CWYPTQRQ.js} +7 -7
- package/dist/{chunk-UKT6H7YT.js → chunk-DUW6RX6I.js} +5 -2
- package/dist/chunk-EAD4A4KG.js +430 -0
- package/dist/{chunk-QT7TEVNV.js → chunk-EHSHB7TY.js} +23 -1
- package/dist/{chunk-B5FTAGFN.js → chunk-ETGOKDFR.js} +75 -75
- package/dist/{chunk-5KFI5X7B.js → chunk-F53QQIM2.js} +1 -1
- package/dist/{chunk-MLS6IGGG.js → chunk-FK2MDNTB.js} +117 -117
- package/dist/{chunk-EGUOAHCW.js → chunk-GO4FVRVN.js} +15 -13
- package/dist/{chunk-CRFCWD6V.js → chunk-J2K5S5MX.js} +136 -173
- package/dist/chunk-K5FO42YF.js +1136 -0
- package/dist/{chunk-DFBX3BBD.js → chunk-KA2VIEGM.js} +211 -16
- package/dist/chunk-NWIQJ2CL.js +108 -0
- package/dist/chunk-OCSU2S6W.js +168 -0
- package/dist/{chunk-QQFBFV4V.js → chunk-PGDBUUGR.js} +60 -19
- package/dist/{chunk-QITOPASZ.js → chunk-PSQHUZ7X.js} +1 -1
- package/dist/{chunk-C6KPAFCC.js → chunk-PU7LXOQ3.js} +23 -1
- package/dist/{chunk-JOY533UH.js → chunk-TW65F5EU.js} +1 -1
- package/dist/{chunk-ZX5623ER.js → chunk-VMH2YS2I.js} +1 -1
- package/dist/{chunk-XND2DWTZ.js → chunk-VPQ44XKE.js} +2 -2
- package/dist/{chunk-CSATDXZC.js → chunk-Y7T6IMM3.js} +1 -1
- package/dist/cli/index.js +755 -379
- package/dist/{client-T5MTY3CS.js → client-HRYRJKSA.js} +3 -3
- package/dist/{conduct-WU3VEXB6.js → conduct-JNYJCDHQ.js} +14 -13
- package/dist/conduct-KJUD2RTB.js +22 -0
- package/dist/{conductor-mode-ZMTFZGJP.js → conductor-mode-2VVFMKVE.js} +313 -14
- package/dist/conductor-mode-VGUU54QI.js +276 -0
- package/dist/execute-I4PKSNJM.js +12 -0
- package/dist/execute-MOXSSA3Q.js +15 -0
- package/dist/index.d.ts +795 -2
- package/dist/index.js +861 -111
- package/dist/{process-guard-CC7CNRQJ.js → process-guard-QCCBGILS.js} +1 -1
- package/dist/publish-capability-TS6CNR5G.js +12 -0
- package/dist/reliability-metrics-QG7WC5QK.js +18 -0
- package/dist/{request-VOXBFUOG.js → request-E7TA7COA.js} +19 -18
- package/dist/{serve-skill-IH7UAJNR.js → serve-skill-HIOWYKRU.js} +13 -11
- package/dist/{server-JVQW2TID.js → server-I63CXFX3.js} +17 -16
- package/dist/{service-coordinator-EYRDTHL5.js → service-coordinator-XBNT3SMU.js} +369 -260
- package/dist/skill-config-FETXPNVP.js +22 -0
- package/dist/skills/agentbnb/bootstrap.js +430 -84
- package/dist/websocket-client-5MH6QRJK.js +7 -0
- package/dist/{websocket-client-WRN3HO73.js → websocket-client-PFGVTXNE.js} +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -1
- package/skills/agentbnb/SKILL.md +35 -0
- package/skills/agentbnb/bootstrap.ts +126 -8
- package/skills/agentbnb/install.sh +49 -9
- package/dist/chunk-EANI2N2V.js +0 -309
- package/dist/chunk-EPIWHNB2.js +0 -946
- package/dist/conduct-6LKIJJKQ.js +0 -21
- package/dist/conductor-mode-Q4IIDY5E.js +0 -123
- package/dist/execute-4D4ITQCL.js +0 -10
- package/dist/execute-T7Y6RKSW.js +0 -13
- package/dist/websocket-client-6IIDGXKB.js +0 -7
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {
|
|
2
|
+
insertCard
|
|
3
|
+
} from "./chunk-KA2VIEGM.js";
|
|
4
|
+
import {
|
|
5
|
+
CapabilityCardSchema
|
|
6
|
+
} from "./chunk-3CIMVISQ.js";
|
|
7
|
+
|
|
8
|
+
// src/skills/publish-capability.ts
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
function parseSoulMd(content) {
|
|
11
|
+
const lines = content.split("\n");
|
|
12
|
+
let name = "";
|
|
13
|
+
let description = "";
|
|
14
|
+
const capabilities = [];
|
|
15
|
+
const unknownSections = [];
|
|
16
|
+
let currentSection = null;
|
|
17
|
+
let currentCapabilityName = "";
|
|
18
|
+
let currentCapabilityLines = [];
|
|
19
|
+
let currentCapabilityPricing = void 0;
|
|
20
|
+
let descriptionLines = [];
|
|
21
|
+
let pastFirstH1 = false;
|
|
22
|
+
let pastFirstH2 = false;
|
|
23
|
+
const flushCapability = () => {
|
|
24
|
+
if (currentCapabilityName) {
|
|
25
|
+
const cap = {
|
|
26
|
+
name: currentCapabilityName,
|
|
27
|
+
description: currentCapabilityLines.join(" ").trim()
|
|
28
|
+
};
|
|
29
|
+
if (currentCapabilityPricing !== void 0) {
|
|
30
|
+
cap.pricing = currentCapabilityPricing;
|
|
31
|
+
}
|
|
32
|
+
capabilities.push(cap);
|
|
33
|
+
currentCapabilityName = "";
|
|
34
|
+
currentCapabilityLines = [];
|
|
35
|
+
currentCapabilityPricing = void 0;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
const trimmed = line.trim();
|
|
40
|
+
if (/^# /.test(trimmed) && !pastFirstH1) {
|
|
41
|
+
name = trimmed.slice(2).trim();
|
|
42
|
+
pastFirstH1 = true;
|
|
43
|
+
currentSection = "preamble";
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (/^## /.test(trimmed)) {
|
|
47
|
+
flushCapability();
|
|
48
|
+
const capName = trimmed.slice(3).trim();
|
|
49
|
+
currentCapabilityName = capName;
|
|
50
|
+
currentSection = "capability";
|
|
51
|
+
pastFirstH2 = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (/^#{3,} /.test(trimmed)) {
|
|
55
|
+
const sectionName = trimmed.replace(/^#+\s*/, "");
|
|
56
|
+
if (!unknownSections.includes(sectionName)) {
|
|
57
|
+
unknownSections.push(sectionName);
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (trimmed === "") continue;
|
|
62
|
+
if (currentSection === "preamble" && !pastFirstH2) {
|
|
63
|
+
descriptionLines.push(trimmed);
|
|
64
|
+
} else if (currentSection === "capability") {
|
|
65
|
+
const pricingMatch = trimmed.match(/^pricing:\s*(\d+(?:\.\d+)?)$/i);
|
|
66
|
+
if (pricingMatch) {
|
|
67
|
+
const val = parseFloat(pricingMatch[1]);
|
|
68
|
+
if (!isNaN(val) && val >= 0) {
|
|
69
|
+
currentCapabilityPricing = val;
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
currentCapabilityLines.push(trimmed);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
flushCapability();
|
|
77
|
+
if (descriptionLines.length > 0) {
|
|
78
|
+
description = descriptionLines[0] ?? "";
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
name,
|
|
82
|
+
description,
|
|
83
|
+
level: 2,
|
|
84
|
+
capabilities,
|
|
85
|
+
unknownSections
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function publishFromSoul(db, soulContent, owner) {
|
|
89
|
+
const parsed = parseSoulMd(soulContent);
|
|
90
|
+
const capsSummary = parsed.capabilities.map((c) => c.name).join(", ");
|
|
91
|
+
const description = parsed.description.length > 0 ? parsed.description.slice(0, 500) : capsSummary.slice(0, 500);
|
|
92
|
+
const card = {
|
|
93
|
+
spec_version: "1.0",
|
|
94
|
+
id: randomUUID(),
|
|
95
|
+
owner,
|
|
96
|
+
name: parsed.name || "Unknown Agent",
|
|
97
|
+
description,
|
|
98
|
+
level: parsed.level,
|
|
99
|
+
inputs: [
|
|
100
|
+
{
|
|
101
|
+
name: "input",
|
|
102
|
+
type: "text",
|
|
103
|
+
description: "Input for the capability",
|
|
104
|
+
required: true
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
outputs: [
|
|
108
|
+
{
|
|
109
|
+
name: "output",
|
|
110
|
+
type: "text",
|
|
111
|
+
description: "Output from the capability",
|
|
112
|
+
required: true
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
pricing: {
|
|
116
|
+
credits_per_call: 10
|
|
117
|
+
},
|
|
118
|
+
availability: {
|
|
119
|
+
online: true
|
|
120
|
+
},
|
|
121
|
+
metadata: {
|
|
122
|
+
tags: parsed.capabilities.map((c) => c.name.toLowerCase().replace(/\s+/g, "-"))
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
CapabilityCardSchema.parse(card);
|
|
126
|
+
insertCard(db, card);
|
|
127
|
+
return card;
|
|
128
|
+
}
|
|
129
|
+
function skillConfigToSkill(config) {
|
|
130
|
+
return {
|
|
131
|
+
id: config.id,
|
|
132
|
+
name: config.name,
|
|
133
|
+
description: config.description ?? "",
|
|
134
|
+
level: 2,
|
|
135
|
+
inputs: [],
|
|
136
|
+
outputs: [],
|
|
137
|
+
pricing: config.pricing,
|
|
138
|
+
...config.capability_types !== void 0 && { capability_types: config.capability_types },
|
|
139
|
+
...config.requires_capabilities !== void 0 && { requires_capabilities: config.requires_capabilities },
|
|
140
|
+
...config.visibility !== void 0 && { visibility: config.visibility }
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export {
|
|
145
|
+
parseSoulMd,
|
|
146
|
+
publishFromSoul,
|
|
147
|
+
skillConfigToSkill
|
|
148
|
+
};
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RelayClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TW65F5EU.js";
|
|
4
4
|
import {
|
|
5
5
|
BudgetController,
|
|
6
6
|
ORCHESTRATION_FEE,
|
|
7
7
|
decompose,
|
|
8
8
|
matchSubTasks,
|
|
9
9
|
orchestrate
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-APEG4QIN.js";
|
|
11
11
|
import {
|
|
12
12
|
BudgetManager
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GO4FVRVN.js";
|
|
14
|
+
import {
|
|
15
|
+
openCreditDb
|
|
16
|
+
} from "./chunk-J2K5S5MX.js";
|
|
14
17
|
import {
|
|
15
18
|
loadPeers
|
|
16
19
|
} from "./chunk-5AH3CMOX.js";
|
|
@@ -19,10 +22,7 @@ import {
|
|
|
19
22
|
} from "./chunk-75OC6E4F.js";
|
|
20
23
|
import {
|
|
21
24
|
openDatabase
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import {
|
|
24
|
-
openCreditDb
|
|
25
|
-
} from "./chunk-EANI2N2V.js";
|
|
25
|
+
} from "./chunk-KA2VIEGM.js";
|
|
26
26
|
|
|
27
27
|
// src/cli/conduct.ts
|
|
28
28
|
async function conductAction(task, opts) {
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
+
NETWORK_FEE_RATE,
|
|
2
3
|
confirmEscrowDebit,
|
|
3
4
|
recordEarning,
|
|
4
5
|
releaseEscrow
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-J2K5S5MX.js";
|
|
6
7
|
|
|
7
8
|
// src/credit/settlement.ts
|
|
8
9
|
function settleProviderEarning(providerDb, providerOwner, receipt) {
|
|
10
|
+
const feeAmount = Math.floor(receipt.amount * NETWORK_FEE_RATE);
|
|
11
|
+
const providerAmount = receipt.amount - feeAmount;
|
|
9
12
|
recordEarning(
|
|
10
13
|
providerDb,
|
|
11
14
|
providerOwner,
|
|
12
|
-
|
|
15
|
+
providerAmount,
|
|
13
16
|
receipt.card_id,
|
|
14
17
|
receipt.nonce
|
|
15
18
|
);
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NETWORK_FEE_RATE,
|
|
3
|
+
confirmEscrowDebit,
|
|
4
|
+
getBalance,
|
|
5
|
+
getCard,
|
|
6
|
+
holdEscrow,
|
|
7
|
+
insertRequestLog,
|
|
8
|
+
recordEarning,
|
|
9
|
+
releaseEscrow,
|
|
10
|
+
settleEscrow,
|
|
11
|
+
updateReputation,
|
|
12
|
+
verifyEscrowReceipt
|
|
13
|
+
} from "./chunk-K5FO42YF.js";
|
|
14
|
+
import {
|
|
15
|
+
loadConfig
|
|
16
|
+
} from "./chunk-IVOYM3WG.js";
|
|
17
|
+
import {
|
|
18
|
+
AgentBnBError
|
|
19
|
+
} from "./chunk-3CIMVISQ.js";
|
|
20
|
+
|
|
21
|
+
// src/gateway/execute.ts
|
|
22
|
+
import { randomUUID } from "crypto";
|
|
23
|
+
|
|
24
|
+
// src/credit/settlement.ts
|
|
25
|
+
function settleProviderEarning(providerDb, providerOwner, receipt) {
|
|
26
|
+
const feeAmount = Math.floor(receipt.amount * NETWORK_FEE_RATE);
|
|
27
|
+
const providerAmount = receipt.amount - feeAmount;
|
|
28
|
+
recordEarning(
|
|
29
|
+
providerDb,
|
|
30
|
+
providerOwner,
|
|
31
|
+
providerAmount,
|
|
32
|
+
receipt.card_id,
|
|
33
|
+
receipt.nonce
|
|
34
|
+
);
|
|
35
|
+
return { settled: true };
|
|
36
|
+
}
|
|
37
|
+
function settleRequesterEscrow(requesterDb, escrowId) {
|
|
38
|
+
confirmEscrowDebit(requesterDb, escrowId);
|
|
39
|
+
}
|
|
40
|
+
function releaseRequesterEscrow(requesterDb, escrowId) {
|
|
41
|
+
releaseEscrow(requesterDb, escrowId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/gateway/execute.ts
|
|
45
|
+
async function notifyTelegramSkillExecuted(opts) {
|
|
46
|
+
const cfg = loadConfig();
|
|
47
|
+
if (!cfg?.telegram_notifications) return;
|
|
48
|
+
const token = cfg.telegram_bot_token ?? process.env["TELEGRAM_BOT_TOKEN"];
|
|
49
|
+
const chatId = cfg.telegram_chat_id ?? process.env["TELEGRAM_CHAT_ID"];
|
|
50
|
+
if (!token || !chatId) return;
|
|
51
|
+
const balance = getBalance(opts.creditDb, opts.owner);
|
|
52
|
+
const skillLabel = opts.skillId ? `${opts.skillName} (${opts.skillId})` : opts.skillName;
|
|
53
|
+
const text = [
|
|
54
|
+
"[AgentBnB] Skill executed",
|
|
55
|
+
`Skill: ${skillLabel}`,
|
|
56
|
+
`Requester: ${opts.requester}`,
|
|
57
|
+
`Earned: +${opts.creditsEarned} credits`,
|
|
58
|
+
`Balance: ${balance} credits`,
|
|
59
|
+
`Latency: ${opts.latencyMs}ms`
|
|
60
|
+
].join("\n");
|
|
61
|
+
await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
body: JSON.stringify({ chat_id: chatId, text })
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async function executeCapabilityRequest(opts) {
|
|
68
|
+
const {
|
|
69
|
+
registryDb,
|
|
70
|
+
creditDb,
|
|
71
|
+
cardId,
|
|
72
|
+
skillId,
|
|
73
|
+
params,
|
|
74
|
+
requester,
|
|
75
|
+
escrowReceipt: receipt,
|
|
76
|
+
skillExecutor,
|
|
77
|
+
handlerUrl,
|
|
78
|
+
timeoutMs = 3e5,
|
|
79
|
+
onProgress,
|
|
80
|
+
relayAuthorized = false
|
|
81
|
+
} = opts;
|
|
82
|
+
const card = getCard(registryDb, cardId);
|
|
83
|
+
if (!card) {
|
|
84
|
+
return { success: false, error: { code: -32602, message: `Card not found: ${cardId}` } };
|
|
85
|
+
}
|
|
86
|
+
if (requester === card.owner && !relayAuthorized) {
|
|
87
|
+
const msg = `Self-request blocked: requester (${requester}) is the card owner. Set AGENTBNB_DIR to your agent's config directory before calling agentbnb request.`;
|
|
88
|
+
try {
|
|
89
|
+
insertRequestLog(registryDb, {
|
|
90
|
+
id: randomUUID(),
|
|
91
|
+
card_id: cardId,
|
|
92
|
+
card_name: card.name,
|
|
93
|
+
skill_id: skillId,
|
|
94
|
+
requester,
|
|
95
|
+
status: "failure",
|
|
96
|
+
latency_ms: 0,
|
|
97
|
+
credits_charged: 0,
|
|
98
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
99
|
+
failure_reason: "auth_error"
|
|
100
|
+
});
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
return { success: false, error: { code: -32603, message: msg } };
|
|
104
|
+
}
|
|
105
|
+
let creditsNeeded;
|
|
106
|
+
let cardName;
|
|
107
|
+
let resolvedSkillId;
|
|
108
|
+
const rawCard = card;
|
|
109
|
+
if (Array.isArray(rawCard["skills"])) {
|
|
110
|
+
const v2card = card;
|
|
111
|
+
const skill = skillId ? v2card.skills.find((s) => s.id === skillId) : v2card.skills[0];
|
|
112
|
+
if (!skill) {
|
|
113
|
+
return { success: false, error: { code: -32602, message: `Skill not found: ${skillId}` } };
|
|
114
|
+
}
|
|
115
|
+
creditsNeeded = skill.pricing.credits_per_call;
|
|
116
|
+
cardName = skill.name;
|
|
117
|
+
resolvedSkillId = skill.id;
|
|
118
|
+
} else {
|
|
119
|
+
creditsNeeded = card.pricing.credits_per_call;
|
|
120
|
+
cardName = card.name;
|
|
121
|
+
}
|
|
122
|
+
let escrowId = null;
|
|
123
|
+
let isRemoteEscrow = false;
|
|
124
|
+
if (relayAuthorized) {
|
|
125
|
+
} else if (receipt) {
|
|
126
|
+
const { signature, ...receiptData2 } = receipt;
|
|
127
|
+
const publicKeyBuf = Buffer.from(receipt.requester_public_key, "hex");
|
|
128
|
+
const valid = verifyEscrowReceipt(receiptData2, signature, publicKeyBuf);
|
|
129
|
+
if (!valid) {
|
|
130
|
+
return { success: false, error: { code: -32603, message: "Invalid escrow receipt signature" } };
|
|
131
|
+
}
|
|
132
|
+
if (receipt.amount < creditsNeeded) {
|
|
133
|
+
return { success: false, error: { code: -32603, message: "Insufficient escrow amount" } };
|
|
134
|
+
}
|
|
135
|
+
const receiptAge = Date.now() - new Date(receipt.timestamp).getTime();
|
|
136
|
+
if (receiptAge > 5 * 60 * 1e3) {
|
|
137
|
+
return { success: false, error: { code: -32603, message: "Escrow receipt expired" } };
|
|
138
|
+
}
|
|
139
|
+
isRemoteEscrow = true;
|
|
140
|
+
} else {
|
|
141
|
+
try {
|
|
142
|
+
const balance = getBalance(creditDb, requester);
|
|
143
|
+
if (balance < creditsNeeded) {
|
|
144
|
+
return { success: false, error: { code: -32603, message: "Insufficient credits" } };
|
|
145
|
+
}
|
|
146
|
+
escrowId = holdEscrow(creditDb, requester, creditsNeeded, cardId);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
|
|
149
|
+
return { success: false, error: { code: -32603, message: msg } };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const startMs = Date.now();
|
|
153
|
+
const receiptData = isRemoteEscrow ? { receipt_released: true } : void 0;
|
|
154
|
+
const handleFailure = (status, latencyMs, message, failureReason = "bad_execution") => {
|
|
155
|
+
if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
|
|
156
|
+
if (failureReason === "bad_execution" || failureReason === "auth_error") {
|
|
157
|
+
updateReputation(registryDb, cardId, false, latencyMs);
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
insertRequestLog(registryDb, {
|
|
161
|
+
id: randomUUID(),
|
|
162
|
+
card_id: cardId,
|
|
163
|
+
card_name: cardName,
|
|
164
|
+
skill_id: resolvedSkillId,
|
|
165
|
+
requester,
|
|
166
|
+
status,
|
|
167
|
+
latency_ms: latencyMs,
|
|
168
|
+
credits_charged: 0,
|
|
169
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
170
|
+
failure_reason: failureReason
|
|
171
|
+
});
|
|
172
|
+
} catch {
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
error: { code: -32603, message, ...receiptData ? { data: receiptData } : {} }
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
const handleSuccess = (result, latencyMs) => {
|
|
180
|
+
if (isRemoteEscrow && receipt) {
|
|
181
|
+
settleProviderEarning(creditDb, card.owner, receipt);
|
|
182
|
+
} else if (escrowId) {
|
|
183
|
+
settleEscrow(creditDb, escrowId, card.owner);
|
|
184
|
+
}
|
|
185
|
+
updateReputation(registryDb, cardId, true, latencyMs);
|
|
186
|
+
try {
|
|
187
|
+
insertRequestLog(registryDb, {
|
|
188
|
+
id: randomUUID(),
|
|
189
|
+
card_id: cardId,
|
|
190
|
+
card_name: cardName,
|
|
191
|
+
skill_id: resolvedSkillId,
|
|
192
|
+
requester,
|
|
193
|
+
status: "success",
|
|
194
|
+
latency_ms: latencyMs,
|
|
195
|
+
credits_charged: creditsNeeded,
|
|
196
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
197
|
+
});
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
notifyTelegramSkillExecuted({
|
|
201
|
+
creditDb,
|
|
202
|
+
owner: card.owner,
|
|
203
|
+
skillName: cardName,
|
|
204
|
+
skillId: resolvedSkillId ?? null,
|
|
205
|
+
requester,
|
|
206
|
+
creditsEarned: creditsNeeded,
|
|
207
|
+
latencyMs
|
|
208
|
+
}).catch(() => {
|
|
209
|
+
});
|
|
210
|
+
const successResult = isRemoteEscrow ? {
|
|
211
|
+
...typeof result === "object" && result !== null ? result : { data: result },
|
|
212
|
+
receipt_settled: true,
|
|
213
|
+
receipt_nonce: receipt.nonce
|
|
214
|
+
} : result;
|
|
215
|
+
return { success: true, result: successResult };
|
|
216
|
+
};
|
|
217
|
+
if (skillExecutor) {
|
|
218
|
+
let targetSkillId = resolvedSkillId ?? skillId;
|
|
219
|
+
if (!targetSkillId) {
|
|
220
|
+
const available = skillExecutor.listSkills();
|
|
221
|
+
if (available.length > 0) {
|
|
222
|
+
targetSkillId = available[0];
|
|
223
|
+
} else {
|
|
224
|
+
return handleFailure(
|
|
225
|
+
"failure",
|
|
226
|
+
Date.now() - startMs,
|
|
227
|
+
"No skill_id specified and no skills registered on this provider.",
|
|
228
|
+
"not_found"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const execResult = await skillExecutor.execute(targetSkillId, params, onProgress);
|
|
234
|
+
if (!execResult.success) {
|
|
235
|
+
return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed", "bad_execution");
|
|
236
|
+
}
|
|
237
|
+
return handleSuccess(execResult.result, execResult.latency_ms);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
const message = err instanceof Error ? err.message : "Execution error";
|
|
240
|
+
return handleFailure("failure", Date.now() - startMs, message, "bad_execution");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (!handlerUrl) {
|
|
244
|
+
return handleFailure("failure", Date.now() - startMs, "No skill executor or handler URL configured", "bad_execution");
|
|
245
|
+
}
|
|
246
|
+
const controller = new AbortController();
|
|
247
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
248
|
+
try {
|
|
249
|
+
const response = await fetch(handlerUrl, {
|
|
250
|
+
method: "POST",
|
|
251
|
+
headers: { "Content-Type": "application/json" },
|
|
252
|
+
body: JSON.stringify({ card_id: cardId, skill_id: resolvedSkillId, params }),
|
|
253
|
+
signal: controller.signal
|
|
254
|
+
});
|
|
255
|
+
clearTimeout(timer);
|
|
256
|
+
if (!response.ok) {
|
|
257
|
+
return handleFailure("failure", Date.now() - startMs, `Handler returned ${response.status}`, "bad_execution");
|
|
258
|
+
}
|
|
259
|
+
const result = await response.json();
|
|
260
|
+
return handleSuccess(result, Date.now() - startMs);
|
|
261
|
+
} catch (err) {
|
|
262
|
+
clearTimeout(timer);
|
|
263
|
+
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
264
|
+
return handleFailure(
|
|
265
|
+
isTimeout ? "timeout" : "failure",
|
|
266
|
+
Date.now() - startMs,
|
|
267
|
+
isTimeout ? "Execution timeout" : "Handler error",
|
|
268
|
+
isTimeout ? "timeout" : "bad_execution"
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async function executeCapabilityBatch(options) {
|
|
273
|
+
const { requests, strategy, total_budget, registryDb, creditDb, owner } = options;
|
|
274
|
+
if (requests.length === 0) {
|
|
275
|
+
return { results: [], total_credits_spent: 0, total_credits_refunded: 0, success: true };
|
|
276
|
+
}
|
|
277
|
+
const sumMaxCredits = requests.reduce((acc, r) => acc + r.max_credits, 0);
|
|
278
|
+
if (sumMaxCredits > total_budget) {
|
|
279
|
+
return {
|
|
280
|
+
results: requests.map((_, i) => ({
|
|
281
|
+
request_index: i,
|
|
282
|
+
status: "skipped",
|
|
283
|
+
credits_spent: 0,
|
|
284
|
+
credits_refunded: 0,
|
|
285
|
+
error: `Total requested credits (${sumMaxCredits}) exceeds total_budget (${total_budget})`
|
|
286
|
+
})),
|
|
287
|
+
total_credits_spent: 0,
|
|
288
|
+
total_credits_refunded: 0,
|
|
289
|
+
success: false
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const executeItem = async (item, index) => {
|
|
293
|
+
const card = getCard(registryDb, item.skill_id);
|
|
294
|
+
if (!card) {
|
|
295
|
+
return {
|
|
296
|
+
request_index: index,
|
|
297
|
+
status: "failed",
|
|
298
|
+
credits_spent: 0,
|
|
299
|
+
credits_refunded: 0,
|
|
300
|
+
error: `Card/skill not found: ${item.skill_id}`
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const rawCard = card;
|
|
304
|
+
let creditsNeeded;
|
|
305
|
+
let resolvedSkillId;
|
|
306
|
+
if (Array.isArray(rawCard["skills"])) {
|
|
307
|
+
const v2card = card;
|
|
308
|
+
const skill = v2card.skills[0];
|
|
309
|
+
if (!skill) {
|
|
310
|
+
return {
|
|
311
|
+
request_index: index,
|
|
312
|
+
status: "failed",
|
|
313
|
+
credits_spent: 0,
|
|
314
|
+
credits_refunded: 0,
|
|
315
|
+
error: `No skills defined on card: ${item.skill_id}`
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
creditsNeeded = skill.pricing.credits_per_call;
|
|
319
|
+
resolvedSkillId = skill.id;
|
|
320
|
+
} else {
|
|
321
|
+
creditsNeeded = card.pricing.credits_per_call;
|
|
322
|
+
}
|
|
323
|
+
if (creditsNeeded > item.max_credits) {
|
|
324
|
+
return {
|
|
325
|
+
request_index: index,
|
|
326
|
+
status: "failed",
|
|
327
|
+
credits_spent: 0,
|
|
328
|
+
credits_refunded: 0,
|
|
329
|
+
error: `Skill costs ${creditsNeeded} credits but max_credits is ${item.max_credits}`
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
let escrowId;
|
|
333
|
+
try {
|
|
334
|
+
const balance = getBalance(creditDb, owner);
|
|
335
|
+
if (balance < creditsNeeded) {
|
|
336
|
+
return {
|
|
337
|
+
request_index: index,
|
|
338
|
+
status: "failed",
|
|
339
|
+
credits_spent: 0,
|
|
340
|
+
credits_refunded: 0,
|
|
341
|
+
error: "Insufficient credits"
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
escrowId = holdEscrow(creditDb, owner, creditsNeeded, card.id);
|
|
345
|
+
} catch (err) {
|
|
346
|
+
const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
|
|
347
|
+
return {
|
|
348
|
+
request_index: index,
|
|
349
|
+
status: "failed",
|
|
350
|
+
credits_spent: 0,
|
|
351
|
+
credits_refunded: 0,
|
|
352
|
+
error: msg
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
const startMs = Date.now();
|
|
356
|
+
const latencyMs = Date.now() - startMs;
|
|
357
|
+
settleEscrow(creditDb, escrowId, card.owner);
|
|
358
|
+
updateReputation(registryDb, card.id, true, latencyMs);
|
|
359
|
+
try {
|
|
360
|
+
insertRequestLog(registryDb, {
|
|
361
|
+
id: randomUUID(),
|
|
362
|
+
card_id: card.id,
|
|
363
|
+
card_name: card.name,
|
|
364
|
+
skill_id: resolvedSkillId,
|
|
365
|
+
requester: owner,
|
|
366
|
+
status: "success",
|
|
367
|
+
latency_ms: latencyMs,
|
|
368
|
+
credits_charged: creditsNeeded,
|
|
369
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
370
|
+
});
|
|
371
|
+
} catch {
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
request_index: index,
|
|
375
|
+
status: "success",
|
|
376
|
+
result: { card_id: card.id, skill_id: resolvedSkillId },
|
|
377
|
+
credits_spent: creditsNeeded,
|
|
378
|
+
credits_refunded: 0
|
|
379
|
+
};
|
|
380
|
+
};
|
|
381
|
+
let results;
|
|
382
|
+
if (strategy === "sequential") {
|
|
383
|
+
results = [];
|
|
384
|
+
let stopped = false;
|
|
385
|
+
for (let i = 0; i < requests.length; i++) {
|
|
386
|
+
if (stopped) {
|
|
387
|
+
results.push({
|
|
388
|
+
request_index: i,
|
|
389
|
+
status: "skipped",
|
|
390
|
+
credits_spent: 0,
|
|
391
|
+
credits_refunded: 0,
|
|
392
|
+
error: "Skipped due to earlier failure"
|
|
393
|
+
});
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
const result = await executeItem(requests[i], i);
|
|
397
|
+
results.push(result);
|
|
398
|
+
if (result.status === "failed") {
|
|
399
|
+
stopped = true;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
const settled = await Promise.allSettled(
|
|
404
|
+
requests.map((item, i) => executeItem(item, i))
|
|
405
|
+
);
|
|
406
|
+
results = settled.map((outcome, i) => {
|
|
407
|
+
if (outcome.status === "fulfilled") {
|
|
408
|
+
return outcome.value;
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
request_index: i,
|
|
412
|
+
status: "failed",
|
|
413
|
+
credits_spent: 0,
|
|
414
|
+
credits_refunded: 0,
|
|
415
|
+
error: outcome.reason instanceof Error ? outcome.reason.message : "Unknown error"
|
|
416
|
+
};
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
const total_credits_spent = results.reduce((acc, r) => acc + r.credits_spent, 0);
|
|
420
|
+
const total_credits_refunded = results.reduce((acc, r) => acc + r.credits_refunded, 0);
|
|
421
|
+
const success = results.every((r) => r.status === "success");
|
|
422
|
+
return { results, total_credits_spent, total_credits_refunded, success };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export {
|
|
426
|
+
settleRequesterEscrow,
|
|
427
|
+
releaseRequesterEscrow,
|
|
428
|
+
executeCapabilityRequest,
|
|
429
|
+
executeCapabilityBatch
|
|
430
|
+
};
|
|
@@ -66,6 +66,27 @@ var RelayProgressMessageSchema = z.object({
|
|
|
66
66
|
message: z.string().optional()
|
|
67
67
|
// optional status message
|
|
68
68
|
});
|
|
69
|
+
var HeartbeatMessageSchema = z.object({
|
|
70
|
+
type: z.literal("heartbeat"),
|
|
71
|
+
owner: z.string().min(1),
|
|
72
|
+
capacity: z.object({
|
|
73
|
+
current_load: z.number(),
|
|
74
|
+
max_concurrent: z.number(),
|
|
75
|
+
queue_depth: z.number()
|
|
76
|
+
}),
|
|
77
|
+
self_summary: z.object({
|
|
78
|
+
capabilities: z.array(z.string()),
|
|
79
|
+
success_rate: z.number(),
|
|
80
|
+
credit_balance: z.number(),
|
|
81
|
+
total_completed: z.number(),
|
|
82
|
+
provider_number: z.number(),
|
|
83
|
+
reliability: z.object({
|
|
84
|
+
current_streak: z.number(),
|
|
85
|
+
repeat_hire_rate: z.number(),
|
|
86
|
+
avg_feedback: z.number()
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
});
|
|
69
90
|
var RelayMessageSchema = z.discriminatedUnion("type", [
|
|
70
91
|
RegisterMessageSchema,
|
|
71
92
|
RegisteredMessageSchema,
|
|
@@ -74,7 +95,8 @@ var RelayMessageSchema = z.discriminatedUnion("type", [
|
|
|
74
95
|
RelayResponseMessageSchema,
|
|
75
96
|
ResponseMessageSchema,
|
|
76
97
|
ErrorMessageSchema,
|
|
77
|
-
RelayProgressMessageSchema
|
|
98
|
+
RelayProgressMessageSchema,
|
|
99
|
+
HeartbeatMessageSchema
|
|
78
100
|
]);
|
|
79
101
|
|
|
80
102
|
export {
|