agentbnb 2.2.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/card-P5C36VBD.js +81 -0
- package/dist/chunk-2LLXUKMY.js +489 -0
- package/dist/chunk-3Y36WQDV.js +70 -0
- package/dist/chunk-4Q7D24DP.js +257 -0
- package/dist/chunk-BEI5MTNZ.js +91 -0
- package/dist/chunk-QVIGMCHA.js +486 -0
- package/dist/chunk-T7ZJPQHD.js +372 -0
- package/dist/chunk-TQMI73LL.js +125 -0
- package/dist/chunk-ZJCIBK6O.js +192 -0
- package/dist/cli/index.js +715 -1538
- package/dist/conduct-M57F72RK.js +117 -0
- package/dist/conductor-mode-CF6PSRRA.js +112 -0
- package/dist/execute-3T5RF3DP.js +9 -0
- package/dist/index.js +2432 -183
- package/dist/peers-G36URZYB.js +12 -0
- package/dist/websocket-client-5TIQDYQ4.js +275 -0
- package/package.json +5 -2
- package/dist/index.d.ts +0 -676
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findPeer
|
|
3
|
+
} from "./chunk-BEI5MTNZ.js";
|
|
4
|
+
import {
|
|
5
|
+
getBalance,
|
|
6
|
+
holdEscrow,
|
|
7
|
+
releaseEscrow,
|
|
8
|
+
settleEscrow
|
|
9
|
+
} from "./chunk-ZJCIBK6O.js";
|
|
10
|
+
import {
|
|
11
|
+
AgentBnBError
|
|
12
|
+
} from "./chunk-TQMI73LL.js";
|
|
13
|
+
|
|
14
|
+
// src/autonomy/tiers.ts
|
|
15
|
+
import { randomUUID } from "crypto";
|
|
16
|
+
var DEFAULT_AUTONOMY_CONFIG = {
|
|
17
|
+
tier1_max_credits: 0,
|
|
18
|
+
tier2_max_credits: 0
|
|
19
|
+
};
|
|
20
|
+
function getAutonomyTier(creditAmount, config) {
|
|
21
|
+
if (creditAmount < config.tier1_max_credits) return 1;
|
|
22
|
+
if (creditAmount < config.tier2_max_credits) return 2;
|
|
23
|
+
return 3;
|
|
24
|
+
}
|
|
25
|
+
function insertAuditEvent(db, event) {
|
|
26
|
+
const isShareEvent = event.type === "auto_share" || event.type === "auto_share_notify" || event.type === "auto_share_pending";
|
|
27
|
+
const cardId = isShareEvent ? "system" : event.card_id;
|
|
28
|
+
const creditsCharged = isShareEvent ? 0 : event.credits;
|
|
29
|
+
const stmt = db.prepare(`
|
|
30
|
+
INSERT INTO request_log (
|
|
31
|
+
id, card_id, card_name, requester, status, latency_ms, credits_charged,
|
|
32
|
+
created_at, skill_id, action_type, tier_invoked
|
|
33
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
34
|
+
`);
|
|
35
|
+
stmt.run(
|
|
36
|
+
randomUUID(),
|
|
37
|
+
cardId,
|
|
38
|
+
"autonomy-audit",
|
|
39
|
+
"self",
|
|
40
|
+
"success",
|
|
41
|
+
0,
|
|
42
|
+
creditsCharged,
|
|
43
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
44
|
+
event.skill_id,
|
|
45
|
+
event.type,
|
|
46
|
+
event.tier_invoked
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/credit/budget.ts
|
|
51
|
+
var DEFAULT_BUDGET_CONFIG = {
|
|
52
|
+
reserve_credits: 20
|
|
53
|
+
};
|
|
54
|
+
var BudgetManager = class {
|
|
55
|
+
/**
|
|
56
|
+
* Creates a new BudgetManager.
|
|
57
|
+
*
|
|
58
|
+
* @param creditDb - The credit SQLite database instance.
|
|
59
|
+
* @param owner - Agent owner identifier.
|
|
60
|
+
* @param config - Budget configuration. Defaults to DEFAULT_BUDGET_CONFIG (20 credit reserve).
|
|
61
|
+
*/
|
|
62
|
+
constructor(creditDb, owner, config = DEFAULT_BUDGET_CONFIG) {
|
|
63
|
+
this.creditDb = creditDb;
|
|
64
|
+
this.owner = owner;
|
|
65
|
+
this.config = config;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Returns the number of credits available for spending.
|
|
69
|
+
* Computed as: max(0, balance - reserve_credits).
|
|
70
|
+
* Always returns a non-negative number — never goes below zero.
|
|
71
|
+
*
|
|
72
|
+
* @returns Available credits (balance minus reserve, floored at 0).
|
|
73
|
+
*/
|
|
74
|
+
availableCredits() {
|
|
75
|
+
const balance = getBalance(this.creditDb, this.owner);
|
|
76
|
+
return Math.max(0, balance - this.config.reserve_credits);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns true if spending `amount` credits is permitted by budget rules.
|
|
80
|
+
*
|
|
81
|
+
* Rules:
|
|
82
|
+
* - Zero-cost calls (amount <= 0) always return true.
|
|
83
|
+
* - Any positive amount requires availableCredits() >= amount.
|
|
84
|
+
* - If balance is at or below the reserve floor, all positive-cost calls return false.
|
|
85
|
+
*
|
|
86
|
+
* @param amount - Number of credits to spend.
|
|
87
|
+
* @returns true if the spend is allowed, false if it would breach the reserve floor.
|
|
88
|
+
*/
|
|
89
|
+
canSpend(amount) {
|
|
90
|
+
if (amount <= 0) return true;
|
|
91
|
+
return this.availableCredits() >= amount;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/registry/matcher.ts
|
|
96
|
+
function searchCards(db, query, filters = {}) {
|
|
97
|
+
const words = query.trim().split(/\s+/).map((w) => w.replace(/["*^{}():]/g, "")).filter((w) => w.length > 0);
|
|
98
|
+
if (words.length === 0) return [];
|
|
99
|
+
const ftsQuery = words.map((w) => `"${w}"`).join(" OR ");
|
|
100
|
+
const conditions = [];
|
|
101
|
+
const params = [ftsQuery];
|
|
102
|
+
if (filters.level !== void 0) {
|
|
103
|
+
conditions.push(`json_extract(cc.data, '$.level') = ?`);
|
|
104
|
+
params.push(filters.level);
|
|
105
|
+
}
|
|
106
|
+
if (filters.online !== void 0) {
|
|
107
|
+
conditions.push(`json_extract(cc.data, '$.availability.online') = ?`);
|
|
108
|
+
params.push(filters.online ? 1 : 0);
|
|
109
|
+
}
|
|
110
|
+
const whereClause = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
111
|
+
const sql = `
|
|
112
|
+
SELECT cc.data
|
|
113
|
+
FROM capability_cards cc
|
|
114
|
+
JOIN cards_fts ON cc.rowid = cards_fts.rowid
|
|
115
|
+
WHERE cards_fts MATCH ?
|
|
116
|
+
${whereClause}
|
|
117
|
+
ORDER BY bm25(cards_fts)
|
|
118
|
+
LIMIT 50
|
|
119
|
+
`;
|
|
120
|
+
const stmt = db.prepare(sql);
|
|
121
|
+
const rows = stmt.all(...params);
|
|
122
|
+
const results = rows.map((row) => JSON.parse(row.data));
|
|
123
|
+
if (filters.apis_used && filters.apis_used.length > 0) {
|
|
124
|
+
const requiredApis = filters.apis_used;
|
|
125
|
+
return results.filter((card) => {
|
|
126
|
+
const cardApis = card.metadata?.apis_used ?? [];
|
|
127
|
+
return requiredApis.every((api) => cardApis.includes(api));
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return results;
|
|
131
|
+
}
|
|
132
|
+
function filterCards(db, filters) {
|
|
133
|
+
const conditions = [];
|
|
134
|
+
const params = [];
|
|
135
|
+
if (filters.level !== void 0) {
|
|
136
|
+
conditions.push(`json_extract(data, '$.level') = ?`);
|
|
137
|
+
params.push(filters.level);
|
|
138
|
+
}
|
|
139
|
+
if (filters.online !== void 0) {
|
|
140
|
+
conditions.push(`json_extract(data, '$.availability.online') = ?`);
|
|
141
|
+
params.push(filters.online ? 1 : 0);
|
|
142
|
+
}
|
|
143
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
144
|
+
const sql = `SELECT data FROM capability_cards ${whereClause}`;
|
|
145
|
+
const stmt = db.prepare(sql);
|
|
146
|
+
const rows = stmt.all(...params);
|
|
147
|
+
return rows.map((row) => JSON.parse(row.data));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/gateway/client.ts
|
|
151
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
152
|
+
async function requestCapability(opts) {
|
|
153
|
+
const { gatewayUrl, token, cardId, params = {}, timeoutMs = 3e4, escrowReceipt } = opts;
|
|
154
|
+
const id = randomUUID2();
|
|
155
|
+
const payload = {
|
|
156
|
+
jsonrpc: "2.0",
|
|
157
|
+
id,
|
|
158
|
+
method: "capability.execute",
|
|
159
|
+
params: {
|
|
160
|
+
card_id: cardId,
|
|
161
|
+
...params,
|
|
162
|
+
...escrowReceipt ? { escrow_receipt: escrowReceipt } : {}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const controller = new AbortController();
|
|
166
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
167
|
+
let response;
|
|
168
|
+
try {
|
|
169
|
+
response = await fetch(`${gatewayUrl}/rpc`, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: {
|
|
172
|
+
"Content-Type": "application/json",
|
|
173
|
+
Authorization: `Bearer ${token}`
|
|
174
|
+
},
|
|
175
|
+
body: JSON.stringify(payload),
|
|
176
|
+
signal: controller.signal
|
|
177
|
+
});
|
|
178
|
+
} catch (err) {
|
|
179
|
+
clearTimeout(timer);
|
|
180
|
+
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
181
|
+
throw new AgentBnBError(
|
|
182
|
+
isTimeout ? "Request timed out" : `Network error: ${String(err)}`,
|
|
183
|
+
isTimeout ? "TIMEOUT" : "NETWORK_ERROR"
|
|
184
|
+
);
|
|
185
|
+
} finally {
|
|
186
|
+
clearTimeout(timer);
|
|
187
|
+
}
|
|
188
|
+
const body = await response.json();
|
|
189
|
+
if (body.error) {
|
|
190
|
+
throw new AgentBnBError(body.error.message, `RPC_ERROR_${body.error.code}`);
|
|
191
|
+
}
|
|
192
|
+
return body.result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/autonomy/pending-requests.ts
|
|
196
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
197
|
+
function createPendingRequest(db, opts) {
|
|
198
|
+
const id = randomUUID3();
|
|
199
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
200
|
+
const paramsJson = opts.params !== void 0 ? JSON.stringify(opts.params) : null;
|
|
201
|
+
db.prepare(`
|
|
202
|
+
INSERT INTO pending_requests (
|
|
203
|
+
id, skill_query, max_cost_credits, selected_peer, selected_card_id,
|
|
204
|
+
selected_skill_id, credits, status, params, created_at, resolved_at
|
|
205
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?, NULL)
|
|
206
|
+
`).run(
|
|
207
|
+
id,
|
|
208
|
+
opts.skill_query,
|
|
209
|
+
opts.max_cost_credits,
|
|
210
|
+
opts.selected_peer ?? null,
|
|
211
|
+
opts.selected_card_id ?? null,
|
|
212
|
+
opts.selected_skill_id ?? null,
|
|
213
|
+
opts.credits,
|
|
214
|
+
paramsJson,
|
|
215
|
+
now
|
|
216
|
+
);
|
|
217
|
+
return id;
|
|
218
|
+
}
|
|
219
|
+
function listPendingRequests(db) {
|
|
220
|
+
const rows = db.prepare(`SELECT * FROM pending_requests WHERE status = 'pending' ORDER BY created_at DESC`).all();
|
|
221
|
+
return rows;
|
|
222
|
+
}
|
|
223
|
+
function resolvePendingRequest(db, id, resolution) {
|
|
224
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
225
|
+
const result = db.prepare(
|
|
226
|
+
`UPDATE pending_requests SET status = ?, resolved_at = ? WHERE id = ?`
|
|
227
|
+
).run(resolution, now, id);
|
|
228
|
+
if (result.changes === 0) {
|
|
229
|
+
throw new AgentBnBError(
|
|
230
|
+
`Pending request not found: ${id}`,
|
|
231
|
+
"NOT_FOUND"
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/autonomy/auto-request.ts
|
|
237
|
+
function minMaxNormalize(values) {
|
|
238
|
+
if (values.length === 0) return [];
|
|
239
|
+
if (values.length === 1) return [1];
|
|
240
|
+
const min = Math.min(...values);
|
|
241
|
+
const max = Math.max(...values);
|
|
242
|
+
if (max === min) {
|
|
243
|
+
return values.map(() => 1);
|
|
244
|
+
}
|
|
245
|
+
return values.map((v) => (v - min) / (max - min));
|
|
246
|
+
}
|
|
247
|
+
function scorePeers(candidates, selfOwner) {
|
|
248
|
+
const eligible = candidates.filter((c) => c.card.owner !== selfOwner);
|
|
249
|
+
if (eligible.length === 0) return [];
|
|
250
|
+
const successRates = eligible.map((c) => c.card.metadata?.success_rate ?? 0.5);
|
|
251
|
+
const costEfficiencies = eligible.map((c) => c.cost === 0 ? 1 : 1 / c.cost);
|
|
252
|
+
const idleRates = eligible.map((c) => {
|
|
253
|
+
const internal = c.card._internal;
|
|
254
|
+
const idleRate = internal?.idle_rate;
|
|
255
|
+
return typeof idleRate === "number" ? idleRate : 1;
|
|
256
|
+
});
|
|
257
|
+
const normSuccess = minMaxNormalize(successRates);
|
|
258
|
+
const normCost = minMaxNormalize(costEfficiencies);
|
|
259
|
+
const normIdle = minMaxNormalize(idleRates);
|
|
260
|
+
const scored = eligible.map((c, i) => ({
|
|
261
|
+
...c,
|
|
262
|
+
rawScore: (normSuccess[i] ?? 0) * (normCost[i] ?? 0) * (normIdle[i] ?? 0)
|
|
263
|
+
}));
|
|
264
|
+
scored.sort((a, b) => b.rawScore - a.rawScore);
|
|
265
|
+
return scored;
|
|
266
|
+
}
|
|
267
|
+
var AutoRequestor = class {
|
|
268
|
+
owner;
|
|
269
|
+
registryDb;
|
|
270
|
+
creditDb;
|
|
271
|
+
autonomyConfig;
|
|
272
|
+
budgetManager;
|
|
273
|
+
/**
|
|
274
|
+
* Creates a new AutoRequestor.
|
|
275
|
+
*
|
|
276
|
+
* @param opts - Configuration for this AutoRequestor instance.
|
|
277
|
+
*/
|
|
278
|
+
constructor(opts) {
|
|
279
|
+
this.owner = opts.owner;
|
|
280
|
+
this.registryDb = opts.registryDb;
|
|
281
|
+
this.creditDb = opts.creditDb;
|
|
282
|
+
this.autonomyConfig = opts.autonomyConfig;
|
|
283
|
+
this.budgetManager = opts.budgetManager;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Executes an autonomous capability request.
|
|
287
|
+
*
|
|
288
|
+
* Performs the full flow:
|
|
289
|
+
* 1. Search for matching capability cards
|
|
290
|
+
* 2. Filter self-owned and over-budget candidates
|
|
291
|
+
* 3. Score candidates using min-max normalized composite scoring
|
|
292
|
+
* 4. Resolve peer gateway config
|
|
293
|
+
* 5. Check autonomy tier (Tier 3 queues to pending_requests)
|
|
294
|
+
* 6. Check budget reserve
|
|
295
|
+
* 7. Hold escrow
|
|
296
|
+
* 8. Execute via peer gateway
|
|
297
|
+
* 9. Settle or release escrow based on outcome
|
|
298
|
+
* 10. Log audit event (for Tier 2 notifications and all failures)
|
|
299
|
+
*
|
|
300
|
+
* @param need - The capability need to fulfill.
|
|
301
|
+
* @returns The result of the auto-request attempt.
|
|
302
|
+
*/
|
|
303
|
+
async requestWithAutonomy(need) {
|
|
304
|
+
const cards = searchCards(this.registryDb, need.query, { online: true });
|
|
305
|
+
const candidates = [];
|
|
306
|
+
for (const card of cards) {
|
|
307
|
+
const cardAsV2 = card;
|
|
308
|
+
if (Array.isArray(cardAsV2.skills)) {
|
|
309
|
+
for (const skill of cardAsV2.skills) {
|
|
310
|
+
const cost = skill.pricing.credits_per_call;
|
|
311
|
+
if (cost <= need.maxCostCredits) {
|
|
312
|
+
candidates.push({ card, cost, skillId: skill.id });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
const cost = card.pricing.credits_per_call;
|
|
317
|
+
if (cost <= need.maxCostCredits) {
|
|
318
|
+
candidates.push({ card, cost, skillId: void 0 });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const scored = scorePeers(candidates, this.owner);
|
|
323
|
+
if (scored.length === 0) {
|
|
324
|
+
this.logFailure("auto_request_failed", "system", "none", 3, 0, "none", "No eligible peer found");
|
|
325
|
+
return { status: "no_peer", reason: "No eligible peer found" };
|
|
326
|
+
}
|
|
327
|
+
const top = scored[0];
|
|
328
|
+
const peerConfig = findPeer(top.card.owner);
|
|
329
|
+
if (!peerConfig) {
|
|
330
|
+
this.logFailure("auto_request_failed", top.card.id, top.skillId ?? "none", 3, top.cost, top.card.owner, "No gateway config for peer");
|
|
331
|
+
return { status: "no_peer", reason: "No gateway config for peer" };
|
|
332
|
+
}
|
|
333
|
+
const tier = getAutonomyTier(top.cost, this.autonomyConfig);
|
|
334
|
+
if (tier === 3) {
|
|
335
|
+
createPendingRequest(this.registryDb, {
|
|
336
|
+
skill_query: need.query,
|
|
337
|
+
max_cost_credits: need.maxCostCredits,
|
|
338
|
+
credits: top.cost,
|
|
339
|
+
selected_peer: top.card.owner,
|
|
340
|
+
selected_card_id: top.card.id,
|
|
341
|
+
selected_skill_id: top.skillId,
|
|
342
|
+
params: need.params
|
|
343
|
+
});
|
|
344
|
+
insertAuditEvent(this.registryDb, {
|
|
345
|
+
type: "auto_request_pending",
|
|
346
|
+
card_id: top.card.id,
|
|
347
|
+
skill_id: top.skillId ?? top.card.id,
|
|
348
|
+
tier_invoked: 3,
|
|
349
|
+
credits: top.cost,
|
|
350
|
+
peer: top.card.owner
|
|
351
|
+
});
|
|
352
|
+
return {
|
|
353
|
+
status: "tier_blocked",
|
|
354
|
+
reason: "Tier 3: owner approval required",
|
|
355
|
+
peer: top.card.owner
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
if (!this.budgetManager.canSpend(top.cost)) {
|
|
359
|
+
this.logFailure("auto_request_failed", top.card.id, top.skillId ?? "none", tier, top.cost, top.card.owner, "Budget reserve would be breached");
|
|
360
|
+
return { status: "budget_blocked", reason: "Insufficient credits \u2014 reserve floor would be breached" };
|
|
361
|
+
}
|
|
362
|
+
const escrowId = holdEscrow(this.creditDb, this.owner, top.cost, top.card.id);
|
|
363
|
+
try {
|
|
364
|
+
const execResult = await requestCapability({
|
|
365
|
+
gatewayUrl: peerConfig.url,
|
|
366
|
+
token: peerConfig.token,
|
|
367
|
+
cardId: top.card.id,
|
|
368
|
+
params: top.skillId ? { skill_id: top.skillId, ...need.params } : need.params
|
|
369
|
+
});
|
|
370
|
+
settleEscrow(this.creditDb, escrowId, top.card.owner);
|
|
371
|
+
if (tier === 2) {
|
|
372
|
+
insertAuditEvent(this.registryDb, {
|
|
373
|
+
type: "auto_request_notify",
|
|
374
|
+
card_id: top.card.id,
|
|
375
|
+
skill_id: top.skillId ?? top.card.id,
|
|
376
|
+
tier_invoked: 2,
|
|
377
|
+
credits: top.cost,
|
|
378
|
+
peer: top.card.owner
|
|
379
|
+
});
|
|
380
|
+
} else {
|
|
381
|
+
insertAuditEvent(this.registryDb, {
|
|
382
|
+
type: "auto_request",
|
|
383
|
+
card_id: top.card.id,
|
|
384
|
+
skill_id: top.skillId ?? top.card.id,
|
|
385
|
+
tier_invoked: 1,
|
|
386
|
+
credits: top.cost,
|
|
387
|
+
peer: top.card.owner
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
status: "success",
|
|
392
|
+
result: execResult,
|
|
393
|
+
escrowId,
|
|
394
|
+
peer: top.card.owner,
|
|
395
|
+
creditsSpent: top.cost
|
|
396
|
+
};
|
|
397
|
+
} catch (err) {
|
|
398
|
+
releaseEscrow(this.creditDb, escrowId);
|
|
399
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
400
|
+
this.logFailure("auto_request_failed", top.card.id, top.skillId ?? "none", tier, top.cost, top.card.owner, `Execution failed: ${reason}`);
|
|
401
|
+
return {
|
|
402
|
+
status: "failed",
|
|
403
|
+
reason: `Execution failed: ${reason}`,
|
|
404
|
+
peer: top.card.owner
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Logs a failure audit event to request_log.
|
|
410
|
+
* Used for all non-success paths to satisfy REQ-06.
|
|
411
|
+
*/
|
|
412
|
+
logFailure(type, cardId, skillId, tier, credits, peer, reason) {
|
|
413
|
+
insertAuditEvent(this.registryDb, {
|
|
414
|
+
type,
|
|
415
|
+
card_id: cardId,
|
|
416
|
+
skill_id: skillId,
|
|
417
|
+
tier_invoked: tier,
|
|
418
|
+
credits,
|
|
419
|
+
peer,
|
|
420
|
+
reason
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// src/utils/interpolation.ts
|
|
426
|
+
function resolvePath(obj, path) {
|
|
427
|
+
const segments = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter((s) => s.length > 0);
|
|
428
|
+
let current = obj;
|
|
429
|
+
for (const segment of segments) {
|
|
430
|
+
if (current === null || current === void 0) {
|
|
431
|
+
return void 0;
|
|
432
|
+
}
|
|
433
|
+
if (typeof current !== "object") {
|
|
434
|
+
return void 0;
|
|
435
|
+
}
|
|
436
|
+
current = current[segment];
|
|
437
|
+
}
|
|
438
|
+
return current;
|
|
439
|
+
}
|
|
440
|
+
function interpolate(template, context) {
|
|
441
|
+
return template.replace(/\$\{([^}]+)\}/g, (_match, expression) => {
|
|
442
|
+
const resolved = resolvePath(context, expression.trim());
|
|
443
|
+
if (resolved === void 0 || resolved === null) {
|
|
444
|
+
return "";
|
|
445
|
+
}
|
|
446
|
+
if (typeof resolved === "object") {
|
|
447
|
+
return JSON.stringify(resolved);
|
|
448
|
+
}
|
|
449
|
+
return String(resolved);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
function interpolateObject(obj, context) {
|
|
453
|
+
const result = {};
|
|
454
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
455
|
+
result[key] = interpolateValue(value, context);
|
|
456
|
+
}
|
|
457
|
+
return result;
|
|
458
|
+
}
|
|
459
|
+
function interpolateValue(value, context) {
|
|
460
|
+
if (typeof value === "string") {
|
|
461
|
+
return interpolate(value, context);
|
|
462
|
+
}
|
|
463
|
+
if (Array.isArray(value)) {
|
|
464
|
+
return value.map((item) => interpolateValue(item, context));
|
|
465
|
+
}
|
|
466
|
+
if (value !== null && typeof value === "object") {
|
|
467
|
+
return interpolateObject(value, context);
|
|
468
|
+
}
|
|
469
|
+
return value;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export {
|
|
473
|
+
DEFAULT_AUTONOMY_CONFIG,
|
|
474
|
+
getAutonomyTier,
|
|
475
|
+
insertAuditEvent,
|
|
476
|
+
DEFAULT_BUDGET_CONFIG,
|
|
477
|
+
BudgetManager,
|
|
478
|
+
searchCards,
|
|
479
|
+
filterCards,
|
|
480
|
+
requestCapability,
|
|
481
|
+
listPendingRequests,
|
|
482
|
+
resolvePendingRequest,
|
|
483
|
+
scorePeers,
|
|
484
|
+
AutoRequestor,
|
|
485
|
+
interpolateObject
|
|
486
|
+
};
|