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
package/dist/index.js
CHANGED
|
@@ -48,7 +48,9 @@ var CapabilityCardSchema = z.object({
|
|
|
48
48
|
tags: z.array(z.string()).optional()
|
|
49
49
|
}).optional(),
|
|
50
50
|
created_at: z.string().datetime().optional(),
|
|
51
|
-
updated_at: z.string().datetime().optional()
|
|
51
|
+
updated_at: z.string().datetime().optional(),
|
|
52
|
+
/** Exact-match capability type key for network routing (e.g. 'task_decomposition'). Optional — backward-compatible. */
|
|
53
|
+
capability_type: z.string().optional()
|
|
52
54
|
});
|
|
53
55
|
var SkillSchema = z.object({
|
|
54
56
|
/** Stable skill identifier, e.g. 'tts-elevenlabs'. Used for gateway routing and idle tracking. */
|
|
@@ -58,6 +60,25 @@ var SkillSchema = z.object({
|
|
|
58
60
|
level: z.union([z.literal(1), z.literal(2), z.literal(3)]),
|
|
59
61
|
/** Optional grouping category, e.g. 'tts' | 'video_gen' | 'code_review'. */
|
|
60
62
|
category: z.string().optional(),
|
|
63
|
+
/** Exact-match capability type key for network routing (e.g. 'task_decomposition'). Optional — per-skill routing hint. */
|
|
64
|
+
capability_type: z.string().optional(),
|
|
65
|
+
/**
|
|
66
|
+
* Multi-value capability routing tags — what this skill IS / offers to the outside.
|
|
67
|
+
* Used by Conductor for precise skill-level matching.
|
|
68
|
+
* Example: ["audio_generation", "audio_editing", "content_production"]
|
|
69
|
+
*/
|
|
70
|
+
capability_types: z.array(z.string()).optional(),
|
|
71
|
+
/**
|
|
72
|
+
* Capabilities this skill internally depends on when executing.
|
|
73
|
+
* Used by Conductor for decomposition planning and cost estimation.
|
|
74
|
+
* Example: ["tts", "sound_effects", "audio_mixing"]
|
|
75
|
+
*/
|
|
76
|
+
requires_capabilities: z.array(z.string()).optional(),
|
|
77
|
+
/**
|
|
78
|
+
* Publishing visibility. 'private' skills are excluded from published CapabilityCards.
|
|
79
|
+
* Defaults to 'public' when omitted.
|
|
80
|
+
*/
|
|
81
|
+
visibility: z.enum(["public", "private"]).optional(),
|
|
61
82
|
inputs: z.array(IOSchemaSchema),
|
|
62
83
|
outputs: z.array(IOSchemaSchema),
|
|
63
84
|
pricing: z.object({
|
|
@@ -144,6 +165,8 @@ var CapabilityCardV2Schema = z.object({
|
|
|
144
165
|
_internal: z.record(z.unknown()).optional(),
|
|
145
166
|
/** Public gateway URL where this agent accepts requests. Populated on remote publish. */
|
|
146
167
|
gateway_url: z.string().url().optional(),
|
|
168
|
+
/** Exact-match capability type key for network routing (e.g. 'task_decomposition'). Optional — backward-compatible. */
|
|
169
|
+
capability_type: z.string().optional(),
|
|
147
170
|
created_at: z.string().datetime().optional(),
|
|
148
171
|
updated_at: z.string().datetime().optional()
|
|
149
172
|
});
|
|
@@ -191,11 +214,27 @@ function createRequestLogTable(db) {
|
|
|
191
214
|
db.exec("ALTER TABLE request_log ADD COLUMN tier_invoked INTEGER");
|
|
192
215
|
} catch {
|
|
193
216
|
}
|
|
217
|
+
try {
|
|
218
|
+
db.exec("ALTER TABLE request_log ADD COLUMN failure_reason TEXT");
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
db.exec("ALTER TABLE request_log ADD COLUMN team_id TEXT");
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
db.exec("ALTER TABLE request_log ADD COLUMN role TEXT");
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
db.exec("ALTER TABLE request_log ADD COLUMN capability_type TEXT");
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
194
233
|
}
|
|
195
234
|
function insertRequestLog(db, entry) {
|
|
196
235
|
const stmt = db.prepare(`
|
|
197
|
-
INSERT INTO request_log (id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked)
|
|
198
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
236
|
+
INSERT INTO request_log (id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked, failure_reason, team_id, role, capability_type)
|
|
237
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
199
238
|
`);
|
|
200
239
|
stmt.run(
|
|
201
240
|
entry.id,
|
|
@@ -208,7 +247,11 @@ function insertRequestLog(db, entry) {
|
|
|
208
247
|
entry.created_at,
|
|
209
248
|
entry.skill_id ?? null,
|
|
210
249
|
entry.action_type ?? null,
|
|
211
|
-
entry.tier_invoked ?? null
|
|
250
|
+
entry.tier_invoked ?? null,
|
|
251
|
+
entry.failure_reason ?? null,
|
|
252
|
+
entry.team_id ?? null,
|
|
253
|
+
entry.role ?? null,
|
|
254
|
+
entry.capability_type ?? null
|
|
212
255
|
);
|
|
213
256
|
}
|
|
214
257
|
|
|
@@ -322,6 +365,17 @@ var V2_FTS_TRIGGERS = `
|
|
|
322
365
|
FROM json_each(json_extract(new.data, '$.metadata.tags'))),
|
|
323
366
|
''
|
|
324
367
|
)
|
|
368
|
+
|| ' ' || COALESCE(
|
|
369
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
370
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill),
|
|
371
|
+
''
|
|
372
|
+
)
|
|
373
|
+
|| ' ' || COALESCE(
|
|
374
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
375
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill,
|
|
376
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
377
|
+
''
|
|
378
|
+
)
|
|
325
379
|
);
|
|
326
380
|
END;
|
|
327
381
|
|
|
@@ -351,6 +405,17 @@ var V2_FTS_TRIGGERS = `
|
|
|
351
405
|
FROM json_each(json_extract(old.data, '$.metadata.tags'))),
|
|
352
406
|
''
|
|
353
407
|
)
|
|
408
|
+
|| ' ' || COALESCE(
|
|
409
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
410
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill),
|
|
411
|
+
''
|
|
412
|
+
)
|
|
413
|
+
|| ' ' || COALESCE(
|
|
414
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
415
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill,
|
|
416
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
417
|
+
''
|
|
418
|
+
)
|
|
354
419
|
);
|
|
355
420
|
INSERT INTO cards_fts(rowid, id, owner, name, description, tags)
|
|
356
421
|
VALUES (
|
|
@@ -376,6 +441,17 @@ var V2_FTS_TRIGGERS = `
|
|
|
376
441
|
FROM json_each(json_extract(new.data, '$.metadata.tags'))),
|
|
377
442
|
''
|
|
378
443
|
)
|
|
444
|
+
|| ' ' || COALESCE(
|
|
445
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
446
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill),
|
|
447
|
+
''
|
|
448
|
+
)
|
|
449
|
+
|| ' ' || COALESCE(
|
|
450
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
451
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill,
|
|
452
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
453
|
+
''
|
|
454
|
+
)
|
|
379
455
|
);
|
|
380
456
|
END;
|
|
381
457
|
|
|
@@ -405,6 +481,17 @@ var V2_FTS_TRIGGERS = `
|
|
|
405
481
|
FROM json_each(json_extract(old.data, '$.metadata.tags'))),
|
|
406
482
|
''
|
|
407
483
|
)
|
|
484
|
+
|| ' ' || COALESCE(
|
|
485
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
486
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill),
|
|
487
|
+
''
|
|
488
|
+
)
|
|
489
|
+
|| ' ' || COALESCE(
|
|
490
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
491
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill,
|
|
492
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
493
|
+
''
|
|
494
|
+
)
|
|
408
495
|
);
|
|
409
496
|
END;
|
|
410
497
|
`;
|
|
@@ -509,10 +596,17 @@ function migrateV1toV2(db) {
|
|
|
509
596
|
if (skills.length > 0) {
|
|
510
597
|
name = skills.map((s) => String(s["name"] ?? "")).join(" ");
|
|
511
598
|
description = skills.map((s) => String(s["description"] ?? "")).join(" ");
|
|
512
|
-
tags =
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
599
|
+
tags = [
|
|
600
|
+
// tags from metadata.tags[]
|
|
601
|
+
...skills.flatMap((s) => {
|
|
602
|
+
const meta = s["metadata"];
|
|
603
|
+
return meta?.["tags"] ?? [];
|
|
604
|
+
}),
|
|
605
|
+
// capability_type (singular)
|
|
606
|
+
...skills.map((s) => s["capability_type"]).filter((v) => typeof v === "string" && v.length > 0),
|
|
607
|
+
// capability_types[] (plural)
|
|
608
|
+
...skills.flatMap((s) => s["capability_types"] ?? [])
|
|
609
|
+
].join(" ");
|
|
516
610
|
} else {
|
|
517
611
|
name = String(data["name"] ?? "");
|
|
518
612
|
description = String(data["description"] ?? "");
|
|
@@ -589,6 +683,24 @@ function listCards(db, owner) {
|
|
|
589
683
|
}
|
|
590
684
|
return rows.map((row) => JSON.parse(row.data));
|
|
591
685
|
}
|
|
686
|
+
function getCardsByCapabilityType(db, capabilityType) {
|
|
687
|
+
const rows = db.prepare(
|
|
688
|
+
"SELECT data FROM capability_cards WHERE json_extract(data, '$.capability_type') = ?"
|
|
689
|
+
).all(capabilityType);
|
|
690
|
+
return rows.map((row) => JSON.parse(row.data));
|
|
691
|
+
}
|
|
692
|
+
function getCardsBySkillCapability(db, capabilityType) {
|
|
693
|
+
const rows = db.prepare("SELECT data FROM capability_cards").all();
|
|
694
|
+
return rows.map((row) => JSON.parse(row.data)).filter((card) => {
|
|
695
|
+
const skills = card["skills"];
|
|
696
|
+
if (!skills) return false;
|
|
697
|
+
return skills.some((skill) => {
|
|
698
|
+
if (skill["capability_type"] === capabilityType) return true;
|
|
699
|
+
const types = skill["capability_types"];
|
|
700
|
+
return Array.isArray(types) && types.includes(capabilityType);
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
}
|
|
592
704
|
|
|
593
705
|
// src/feedback/reputation.ts
|
|
594
706
|
var QUALITY_SCORES = {
|
|
@@ -692,6 +804,59 @@ function applyReputationFilter(db, cards, minReputation) {
|
|
|
692
804
|
// src/credit/ledger.ts
|
|
693
805
|
import Database2 from "better-sqlite3";
|
|
694
806
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
807
|
+
|
|
808
|
+
// src/credit/reliability-metrics.ts
|
|
809
|
+
var RELIABILITY_METRICS_SCHEMA = `
|
|
810
|
+
CREATE TABLE IF NOT EXISTS provider_reliability_metrics (
|
|
811
|
+
owner TEXT PRIMARY KEY,
|
|
812
|
+
current_streak INTEGER NOT NULL DEFAULT 0,
|
|
813
|
+
longest_streak INTEGER NOT NULL DEFAULT 0,
|
|
814
|
+
total_hires INTEGER NOT NULL DEFAULT 0,
|
|
815
|
+
repeat_hires INTEGER NOT NULL DEFAULT 0,
|
|
816
|
+
feedback_count INTEGER NOT NULL DEFAULT 0,
|
|
817
|
+
feedback_sum REAL NOT NULL DEFAULT 0,
|
|
818
|
+
availability_checks INTEGER NOT NULL DEFAULT 0,
|
|
819
|
+
availability_hits INTEGER NOT NULL DEFAULT 0,
|
|
820
|
+
cycle_start TEXT NOT NULL,
|
|
821
|
+
updated_at TEXT NOT NULL
|
|
822
|
+
);
|
|
823
|
+
`;
|
|
824
|
+
function ensureReliabilityTable(db) {
|
|
825
|
+
db.exec(RELIABILITY_METRICS_SCHEMA);
|
|
826
|
+
}
|
|
827
|
+
function ensureRow(db, owner) {
|
|
828
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
829
|
+
db.prepare(
|
|
830
|
+
`INSERT OR IGNORE INTO provider_reliability_metrics
|
|
831
|
+
(owner, current_streak, longest_streak, total_hires, repeat_hires,
|
|
832
|
+
feedback_count, feedback_sum, availability_checks, availability_hits,
|
|
833
|
+
cycle_start, updated_at)
|
|
834
|
+
VALUES (?, 0, 0, 0, 0, 0, 0, 0, 0, ?, ?)`
|
|
835
|
+
).run(owner, now, now);
|
|
836
|
+
}
|
|
837
|
+
function recordSuccessfulHire(db, providerOwner, consumerOwner) {
|
|
838
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
839
|
+
ensureRow(db, providerOwner);
|
|
840
|
+
const isRepeat = db.prepare(
|
|
841
|
+
`SELECT COUNT(*) as cnt FROM credit_transactions
|
|
842
|
+
WHERE owner = ? AND reason = 'settlement'
|
|
843
|
+
AND reference_id IN (
|
|
844
|
+
SELECT id FROM credit_escrow WHERE owner = ?
|
|
845
|
+
)`
|
|
846
|
+
).get(providerOwner, consumerOwner);
|
|
847
|
+
const repeatIncrement = (isRepeat?.cnt ?? 0) > 0 ? 1 : 0;
|
|
848
|
+
db.prepare(
|
|
849
|
+
`UPDATE provider_reliability_metrics
|
|
850
|
+
SET current_streak = current_streak + 1,
|
|
851
|
+
longest_streak = MAX(longest_streak, current_streak + 1),
|
|
852
|
+
total_hires = total_hires + 1,
|
|
853
|
+
repeat_hires = repeat_hires + ?,
|
|
854
|
+
updated_at = ?
|
|
855
|
+
WHERE owner = ?`
|
|
856
|
+
).run(repeatIncrement, now, providerOwner);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// src/credit/ledger.ts
|
|
695
860
|
var CREDIT_SCHEMA = `
|
|
696
861
|
CREATE TABLE IF NOT EXISTS credit_balances (
|
|
697
862
|
owner TEXT PRIMARY KEY,
|
|
@@ -718,6 +883,22 @@ var CREDIT_SCHEMA = `
|
|
|
718
883
|
settled_at TEXT
|
|
719
884
|
);
|
|
720
885
|
|
|
886
|
+
CREATE TABLE IF NOT EXISTS provider_registry (
|
|
887
|
+
owner TEXT PRIMARY KEY,
|
|
888
|
+
provider_number INTEGER NOT NULL,
|
|
889
|
+
registered_at TEXT NOT NULL
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
CREATE TABLE IF NOT EXISTS demand_vouchers (
|
|
893
|
+
id TEXT PRIMARY KEY,
|
|
894
|
+
owner TEXT NOT NULL,
|
|
895
|
+
amount INTEGER NOT NULL,
|
|
896
|
+
remaining INTEGER NOT NULL,
|
|
897
|
+
created_at TEXT NOT NULL,
|
|
898
|
+
expires_at TEXT NOT NULL,
|
|
899
|
+
is_active INTEGER NOT NULL DEFAULT 1
|
|
900
|
+
);
|
|
901
|
+
|
|
721
902
|
CREATE INDEX IF NOT EXISTS idx_transactions_owner ON credit_transactions(owner, created_at);
|
|
722
903
|
CREATE INDEX IF NOT EXISTS idx_escrow_owner ON credit_escrow(owner);
|
|
723
904
|
`;
|
|
@@ -726,12 +907,46 @@ function openCreditDb(path = ":memory:") {
|
|
|
726
907
|
db.pragma("journal_mode = WAL");
|
|
727
908
|
db.pragma("foreign_keys = ON");
|
|
728
909
|
db.exec(CREDIT_SCHEMA);
|
|
910
|
+
try {
|
|
911
|
+
db.exec("ALTER TABLE credit_escrow ADD COLUMN funding_source TEXT NOT NULL DEFAULT 'balance'");
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
ensureReliabilityTable(db);
|
|
729
915
|
return db;
|
|
730
916
|
}
|
|
731
917
|
function getBalance(db, owner) {
|
|
732
918
|
const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
|
|
733
919
|
return row?.balance ?? 0;
|
|
734
920
|
}
|
|
921
|
+
function registerProvider(db, owner) {
|
|
922
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
923
|
+
const maxRow = db.prepare("SELECT MAX(provider_number) as maxNum FROM provider_registry").get();
|
|
924
|
+
const nextNum = (maxRow?.maxNum ?? 0) + 1;
|
|
925
|
+
db.prepare("INSERT OR IGNORE INTO provider_registry (owner, provider_number, registered_at) VALUES (?, ?, ?)").run(owner, nextNum, now);
|
|
926
|
+
const row = db.prepare("SELECT provider_number FROM provider_registry WHERE owner = ?").get(owner);
|
|
927
|
+
return row.provider_number;
|
|
928
|
+
}
|
|
929
|
+
function getProviderNumber(db, owner) {
|
|
930
|
+
const row = db.prepare("SELECT provider_number FROM provider_registry WHERE owner = ?").get(owner);
|
|
931
|
+
return row?.provider_number ?? null;
|
|
932
|
+
}
|
|
933
|
+
function getProviderBonus(providerNumber) {
|
|
934
|
+
if (providerNumber <= 50) return 2;
|
|
935
|
+
if (providerNumber <= 200) return 1.5;
|
|
936
|
+
return 1;
|
|
937
|
+
}
|
|
938
|
+
function getActiveVoucher(db, owner) {
|
|
939
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
940
|
+
const row = db.prepare(
|
|
941
|
+
"SELECT id, remaining, expires_at FROM demand_vouchers WHERE owner = ? AND is_active = 1 AND remaining > 0 AND expires_at > ? ORDER BY created_at ASC LIMIT 1"
|
|
942
|
+
).get(owner, now);
|
|
943
|
+
return row ?? null;
|
|
944
|
+
}
|
|
945
|
+
function consumeVoucher(db, voucherId, amount) {
|
|
946
|
+
db.prepare(
|
|
947
|
+
"UPDATE demand_vouchers SET remaining = remaining - ? WHERE id = ? AND remaining >= ?"
|
|
948
|
+
).run(amount, voucherId, amount);
|
|
949
|
+
}
|
|
735
950
|
function recordEarning(db, owner, amount, _cardId, receiptNonce) {
|
|
736
951
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
737
952
|
db.transaction(() => {
|
|
@@ -753,29 +968,42 @@ function recordEarning(db, owner, amount, _cardId, receiptNonce) {
|
|
|
753
968
|
|
|
754
969
|
// src/gateway/server.ts
|
|
755
970
|
import Fastify from "fastify";
|
|
971
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
756
972
|
|
|
757
973
|
// src/gateway/execute.ts
|
|
758
974
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
759
975
|
|
|
760
976
|
// src/credit/escrow.ts
|
|
761
977
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
978
|
+
var NETWORK_FEE_RATE = 0.05;
|
|
762
979
|
function holdEscrow(db, owner, amount, cardId) {
|
|
763
980
|
const escrowId = randomUUID4();
|
|
764
981
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
765
982
|
const hold = db.transaction(() => {
|
|
766
|
-
const
|
|
767
|
-
if (
|
|
768
|
-
|
|
983
|
+
const voucher = getActiveVoucher(db, owner);
|
|
984
|
+
if (voucher && voucher.remaining >= amount) {
|
|
985
|
+
consumeVoucher(db, voucher.id, amount);
|
|
986
|
+
db.prepare(
|
|
987
|
+
"INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
988
|
+
).run(escrowId, owner, amount, cardId, "held", now, "voucher");
|
|
989
|
+
db.prepare(
|
|
990
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
991
|
+
).run(randomUUID4(), owner, -amount, "voucher_hold", escrowId, now);
|
|
992
|
+
} else {
|
|
993
|
+
const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
|
|
994
|
+
if (!row || row.balance < amount) {
|
|
995
|
+
throw new AgentBnBError("Insufficient credits", "INSUFFICIENT_CREDITS");
|
|
996
|
+
}
|
|
997
|
+
db.prepare(
|
|
998
|
+
"UPDATE credit_balances SET balance = balance - ?, updated_at = ? WHERE owner = ? AND balance >= ?"
|
|
999
|
+
).run(amount, now, owner, amount);
|
|
1000
|
+
db.prepare(
|
|
1001
|
+
"INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
1002
|
+
).run(escrowId, owner, amount, cardId, "held", now, "balance");
|
|
1003
|
+
db.prepare(
|
|
1004
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
1005
|
+
).run(randomUUID4(), owner, -amount, "escrow_hold", escrowId, now);
|
|
769
1006
|
}
|
|
770
|
-
db.prepare(
|
|
771
|
-
"UPDATE credit_balances SET balance = balance - ?, updated_at = ? WHERE owner = ? AND balance >= ?"
|
|
772
|
-
).run(amount, now, owner, amount);
|
|
773
|
-
db.prepare(
|
|
774
|
-
"INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
775
|
-
).run(escrowId, owner, amount, cardId, "held", now);
|
|
776
|
-
db.prepare(
|
|
777
|
-
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
778
|
-
).run(randomUUID4(), owner, -amount, "escrow_hold", escrowId, now);
|
|
779
1007
|
});
|
|
780
1008
|
hold();
|
|
781
1009
|
return escrowId;
|
|
@@ -783,7 +1011,7 @@ function holdEscrow(db, owner, amount, cardId) {
|
|
|
783
1011
|
function settleEscrow(db, escrowId, recipientOwner) {
|
|
784
1012
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
785
1013
|
const settle = db.transaction(() => {
|
|
786
|
-
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
1014
|
+
const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
787
1015
|
if (!escrow) {
|
|
788
1016
|
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
789
1017
|
}
|
|
@@ -793,25 +1021,61 @@ function settleEscrow(db, escrowId, recipientOwner) {
|
|
|
793
1021
|
"ESCROW_ALREADY_SETTLED"
|
|
794
1022
|
);
|
|
795
1023
|
}
|
|
1024
|
+
const feeAmount = Math.floor(escrow.amount * NETWORK_FEE_RATE);
|
|
1025
|
+
const providerAmount = escrow.amount - feeAmount;
|
|
796
1026
|
db.prepare(
|
|
797
1027
|
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
798
1028
|
).run(recipientOwner, now);
|
|
799
1029
|
db.prepare(
|
|
800
1030
|
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
801
|
-
).run(
|
|
1031
|
+
).run(providerAmount, now, recipientOwner);
|
|
1032
|
+
if (feeAmount > 0) {
|
|
1033
|
+
db.prepare(
|
|
1034
|
+
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
1035
|
+
).run("platform_treasury", now);
|
|
1036
|
+
db.prepare(
|
|
1037
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
1038
|
+
).run(feeAmount, now, "platform_treasury");
|
|
1039
|
+
db.prepare(
|
|
1040
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
1041
|
+
).run(randomUUID4(), "platform_treasury", feeAmount, "network_fee", escrowId, now);
|
|
1042
|
+
}
|
|
802
1043
|
db.prepare(
|
|
803
1044
|
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
804
1045
|
).run("settled", now, escrowId);
|
|
805
1046
|
db.prepare(
|
|
806
1047
|
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
807
|
-
).run(randomUUID4(), recipientOwner,
|
|
1048
|
+
).run(randomUUID4(), recipientOwner, providerAmount, "settlement", escrowId, now);
|
|
1049
|
+
let providerNum = getProviderNumber(db, recipientOwner);
|
|
1050
|
+
if (providerNum === null) {
|
|
1051
|
+
providerNum = registerProvider(db, recipientOwner);
|
|
1052
|
+
}
|
|
1053
|
+
const bonus = getProviderBonus(providerNum);
|
|
1054
|
+
if (bonus > 1) {
|
|
1055
|
+
const bonusAmount = Math.floor(providerAmount * (bonus - 1));
|
|
1056
|
+
if (bonusAmount > 0) {
|
|
1057
|
+
db.prepare(
|
|
1058
|
+
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
1059
|
+
).run("platform_treasury", now);
|
|
1060
|
+
db.prepare(
|
|
1061
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
1062
|
+
).run(bonusAmount, now, recipientOwner);
|
|
1063
|
+
db.prepare(
|
|
1064
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
1065
|
+
).run(randomUUID4(), recipientOwner, bonusAmount, "provider_bonus", escrowId, now);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
try {
|
|
1069
|
+
recordSuccessfulHire(db, recipientOwner, escrow.owner);
|
|
1070
|
+
} catch {
|
|
1071
|
+
}
|
|
808
1072
|
});
|
|
809
1073
|
settle();
|
|
810
1074
|
}
|
|
811
1075
|
function releaseEscrow(db, escrowId) {
|
|
812
1076
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
813
1077
|
const release = db.transaction(() => {
|
|
814
|
-
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
1078
|
+
const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
815
1079
|
if (!escrow) {
|
|
816
1080
|
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
817
1081
|
}
|
|
@@ -836,7 +1100,7 @@ function releaseEscrow(db, escrowId) {
|
|
|
836
1100
|
function confirmEscrowDebit(db, escrowId) {
|
|
837
1101
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
838
1102
|
const confirm = db.transaction(() => {
|
|
839
|
-
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
1103
|
+
const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
840
1104
|
if (!escrow) {
|
|
841
1105
|
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
842
1106
|
}
|
|
@@ -910,10 +1174,12 @@ function verifyEscrowReceipt(data, signature, publicKey) {
|
|
|
910
1174
|
|
|
911
1175
|
// src/credit/settlement.ts
|
|
912
1176
|
function settleProviderEarning(providerDb, providerOwner, receipt) {
|
|
1177
|
+
const feeAmount = Math.floor(receipt.amount * NETWORK_FEE_RATE);
|
|
1178
|
+
const providerAmount = receipt.amount - feeAmount;
|
|
913
1179
|
recordEarning(
|
|
914
1180
|
providerDb,
|
|
915
1181
|
providerOwner,
|
|
916
|
-
|
|
1182
|
+
providerAmount,
|
|
917
1183
|
receipt.card_id,
|
|
918
1184
|
receipt.nonce
|
|
919
1185
|
);
|
|
@@ -926,7 +1192,50 @@ function releaseRequesterEscrow(requesterDb, escrowId) {
|
|
|
926
1192
|
releaseEscrow(requesterDb, escrowId);
|
|
927
1193
|
}
|
|
928
1194
|
|
|
1195
|
+
// src/cli/config.ts
|
|
1196
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync2 } from "fs";
|
|
1197
|
+
import { homedir } from "os";
|
|
1198
|
+
import { join as join2 } from "path";
|
|
1199
|
+
function getConfigDir() {
|
|
1200
|
+
return process.env["AGENTBNB_DIR"] ?? join2(homedir(), ".agentbnb");
|
|
1201
|
+
}
|
|
1202
|
+
function getConfigPath() {
|
|
1203
|
+
return join2(getConfigDir(), "config.json");
|
|
1204
|
+
}
|
|
1205
|
+
function loadConfig() {
|
|
1206
|
+
const configPath = getConfigPath();
|
|
1207
|
+
if (!existsSync2(configPath)) return null;
|
|
1208
|
+
try {
|
|
1209
|
+
const raw = readFileSync2(configPath, "utf-8");
|
|
1210
|
+
return JSON.parse(raw);
|
|
1211
|
+
} catch {
|
|
1212
|
+
return null;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
929
1216
|
// src/gateway/execute.ts
|
|
1217
|
+
async function notifyTelegramSkillExecuted(opts) {
|
|
1218
|
+
const cfg = loadConfig();
|
|
1219
|
+
if (!cfg?.telegram_notifications) return;
|
|
1220
|
+
const token = cfg.telegram_bot_token ?? process.env["TELEGRAM_BOT_TOKEN"];
|
|
1221
|
+
const chatId = cfg.telegram_chat_id ?? process.env["TELEGRAM_CHAT_ID"];
|
|
1222
|
+
if (!token || !chatId) return;
|
|
1223
|
+
const balance = getBalance(opts.creditDb, opts.owner);
|
|
1224
|
+
const skillLabel = opts.skillId ? `${opts.skillName} (${opts.skillId})` : opts.skillName;
|
|
1225
|
+
const text = [
|
|
1226
|
+
"[AgentBnB] Skill executed",
|
|
1227
|
+
`Skill: ${skillLabel}`,
|
|
1228
|
+
`Requester: ${opts.requester}`,
|
|
1229
|
+
`Earned: +${opts.creditsEarned} credits`,
|
|
1230
|
+
`Balance: ${balance} credits`,
|
|
1231
|
+
`Latency: ${opts.latencyMs}ms`
|
|
1232
|
+
].join("\n");
|
|
1233
|
+
await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
|
|
1234
|
+
method: "POST",
|
|
1235
|
+
headers: { "Content-Type": "application/json" },
|
|
1236
|
+
body: JSON.stringify({ chat_id: chatId, text })
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
930
1239
|
async function executeCapabilityRequest(opts) {
|
|
931
1240
|
const {
|
|
932
1241
|
registryDb,
|
|
@@ -958,7 +1267,8 @@ async function executeCapabilityRequest(opts) {
|
|
|
958
1267
|
status: "failure",
|
|
959
1268
|
latency_ms: 0,
|
|
960
1269
|
credits_charged: 0,
|
|
961
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1270
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1271
|
+
failure_reason: "auth_error"
|
|
962
1272
|
});
|
|
963
1273
|
} catch {
|
|
964
1274
|
}
|
|
@@ -1013,9 +1323,11 @@ async function executeCapabilityRequest(opts) {
|
|
|
1013
1323
|
}
|
|
1014
1324
|
const startMs = Date.now();
|
|
1015
1325
|
const receiptData = isRemoteEscrow ? { receipt_released: true } : void 0;
|
|
1016
|
-
const handleFailure = (status, latencyMs, message) => {
|
|
1326
|
+
const handleFailure = (status, latencyMs, message, failureReason = "bad_execution") => {
|
|
1017
1327
|
if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
|
|
1018
|
-
|
|
1328
|
+
if (failureReason === "bad_execution" || failureReason === "auth_error") {
|
|
1329
|
+
updateReputation(registryDb, cardId, false, latencyMs);
|
|
1330
|
+
}
|
|
1019
1331
|
try {
|
|
1020
1332
|
insertRequestLog(registryDb, {
|
|
1021
1333
|
id: randomUUID5(),
|
|
@@ -1026,7 +1338,8 @@ async function executeCapabilityRequest(opts) {
|
|
|
1026
1338
|
status,
|
|
1027
1339
|
latency_ms: latencyMs,
|
|
1028
1340
|
credits_charged: 0,
|
|
1029
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1341
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1342
|
+
failure_reason: failureReason
|
|
1030
1343
|
});
|
|
1031
1344
|
} catch {
|
|
1032
1345
|
}
|
|
@@ -1056,6 +1369,16 @@ async function executeCapabilityRequest(opts) {
|
|
|
1056
1369
|
});
|
|
1057
1370
|
} catch {
|
|
1058
1371
|
}
|
|
1372
|
+
notifyTelegramSkillExecuted({
|
|
1373
|
+
creditDb,
|
|
1374
|
+
owner: card.owner,
|
|
1375
|
+
skillName: cardName,
|
|
1376
|
+
skillId: resolvedSkillId ?? null,
|
|
1377
|
+
requester,
|
|
1378
|
+
creditsEarned: creditsNeeded,
|
|
1379
|
+
latencyMs
|
|
1380
|
+
}).catch(() => {
|
|
1381
|
+
});
|
|
1059
1382
|
const successResult = isRemoteEscrow ? {
|
|
1060
1383
|
...typeof result === "object" && result !== null ? result : { data: result },
|
|
1061
1384
|
receipt_settled: true,
|
|
@@ -1073,23 +1396,24 @@ async function executeCapabilityRequest(opts) {
|
|
|
1073
1396
|
return handleFailure(
|
|
1074
1397
|
"failure",
|
|
1075
1398
|
Date.now() - startMs,
|
|
1076
|
-
"No skill_id specified and no skills registered on this provider."
|
|
1399
|
+
"No skill_id specified and no skills registered on this provider.",
|
|
1400
|
+
"not_found"
|
|
1077
1401
|
);
|
|
1078
1402
|
}
|
|
1079
1403
|
}
|
|
1080
1404
|
try {
|
|
1081
1405
|
const execResult = await skillExecutor.execute(targetSkillId, params, onProgress);
|
|
1082
1406
|
if (!execResult.success) {
|
|
1083
|
-
return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed");
|
|
1407
|
+
return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed", "bad_execution");
|
|
1084
1408
|
}
|
|
1085
1409
|
return handleSuccess(execResult.result, execResult.latency_ms);
|
|
1086
1410
|
} catch (err) {
|
|
1087
1411
|
const message = err instanceof Error ? err.message : "Execution error";
|
|
1088
|
-
return handleFailure("failure", Date.now() - startMs, message);
|
|
1412
|
+
return handleFailure("failure", Date.now() - startMs, message, "bad_execution");
|
|
1089
1413
|
}
|
|
1090
1414
|
}
|
|
1091
1415
|
if (!handlerUrl) {
|
|
1092
|
-
return handleFailure("failure", Date.now() - startMs, "No skill executor or handler URL configured");
|
|
1416
|
+
return handleFailure("failure", Date.now() - startMs, "No skill executor or handler URL configured", "bad_execution");
|
|
1093
1417
|
}
|
|
1094
1418
|
const controller = new AbortController();
|
|
1095
1419
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -1102,7 +1426,7 @@ async function executeCapabilityRequest(opts) {
|
|
|
1102
1426
|
});
|
|
1103
1427
|
clearTimeout(timer);
|
|
1104
1428
|
if (!response.ok) {
|
|
1105
|
-
return handleFailure("failure", Date.now() - startMs, `Handler returned ${response.status}
|
|
1429
|
+
return handleFailure("failure", Date.now() - startMs, `Handler returned ${response.status}`, "bad_execution");
|
|
1106
1430
|
}
|
|
1107
1431
|
const result = await response.json();
|
|
1108
1432
|
return handleSuccess(result, Date.now() - startMs);
|
|
@@ -1112,7 +1436,8 @@ async function executeCapabilityRequest(opts) {
|
|
|
1112
1436
|
return handleFailure(
|
|
1113
1437
|
isTimeout ? "timeout" : "failure",
|
|
1114
1438
|
Date.now() - startMs,
|
|
1115
|
-
isTimeout ? "Execution timeout" : "Handler error"
|
|
1439
|
+
isTimeout ? "Execution timeout" : "Handler error",
|
|
1440
|
+
isTimeout ? "timeout" : "bad_execution"
|
|
1116
1441
|
);
|
|
1117
1442
|
}
|
|
1118
1443
|
}
|
|
@@ -1131,6 +1456,8 @@ function createGatewayServer(opts) {
|
|
|
1131
1456
|
} = opts;
|
|
1132
1457
|
const fastify = Fastify({ logger: !silent });
|
|
1133
1458
|
const tokenSet = new Set(tokens);
|
|
1459
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
1460
|
+
const OVERLOAD_RETRY_MS = 5e3;
|
|
1134
1461
|
fastify.addHook("onRequest", async (request) => {
|
|
1135
1462
|
if (request.method === "GET" && request.url === "/health") return;
|
|
1136
1463
|
const auth = request.headers.authorization;
|
|
@@ -1196,18 +1523,63 @@ function createGatewayServer(opts) {
|
|
|
1196
1523
|
}
|
|
1197
1524
|
const requester = params.requester ?? "unknown";
|
|
1198
1525
|
const receipt = params.escrow_receipt;
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1526
|
+
if (skillExecutor && skillId && typeof skillExecutor.getSkillConfig === "function") {
|
|
1527
|
+
const skillConfig = skillExecutor.getSkillConfig(skillId);
|
|
1528
|
+
const maxConcurrent = skillConfig?.capacity?.max_concurrent;
|
|
1529
|
+
if (maxConcurrent !== void 0) {
|
|
1530
|
+
const current = inFlight.get(skillId) ?? 0;
|
|
1531
|
+
if (current >= maxConcurrent) {
|
|
1532
|
+
try {
|
|
1533
|
+
insertRequestLog(registryDb, {
|
|
1534
|
+
id: randomUUID6(),
|
|
1535
|
+
card_id: cardId,
|
|
1536
|
+
card_name: "<overload>",
|
|
1537
|
+
requester,
|
|
1538
|
+
status: "failure",
|
|
1539
|
+
latency_ms: 0,
|
|
1540
|
+
credits_charged: 0,
|
|
1541
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1542
|
+
skill_id: skillId,
|
|
1543
|
+
failure_reason: "overload"
|
|
1544
|
+
});
|
|
1545
|
+
} catch {
|
|
1546
|
+
}
|
|
1547
|
+
return reply.status(200).send({
|
|
1548
|
+
jsonrpc: "2.0",
|
|
1549
|
+
id,
|
|
1550
|
+
error: {
|
|
1551
|
+
code: -32e3,
|
|
1552
|
+
message: "overload",
|
|
1553
|
+
data: { error: "overload", retry_after_ms: OVERLOAD_RETRY_MS }
|
|
1554
|
+
}
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
const trackKey = skillId ?? cardId;
|
|
1560
|
+
inFlight.set(trackKey, (inFlight.get(trackKey) ?? 0) + 1);
|
|
1561
|
+
let result;
|
|
1562
|
+
try {
|
|
1563
|
+
result = await executeCapabilityRequest({
|
|
1564
|
+
registryDb,
|
|
1565
|
+
creditDb,
|
|
1566
|
+
cardId,
|
|
1567
|
+
skillId,
|
|
1568
|
+
params,
|
|
1569
|
+
requester,
|
|
1570
|
+
escrowReceipt: receipt,
|
|
1571
|
+
skillExecutor,
|
|
1572
|
+
handlerUrl,
|
|
1573
|
+
timeoutMs
|
|
1574
|
+
});
|
|
1575
|
+
} finally {
|
|
1576
|
+
const next = (inFlight.get(trackKey) ?? 1) - 1;
|
|
1577
|
+
if (next <= 0) {
|
|
1578
|
+
inFlight.delete(trackKey);
|
|
1579
|
+
} else {
|
|
1580
|
+
inFlight.set(trackKey, next);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1211
1583
|
if (result.success) {
|
|
1212
1584
|
return reply.send({ jsonrpc: "2.0", id, result: result.result });
|
|
1213
1585
|
} else {
|
|
@@ -1221,13 +1593,16 @@ function createGatewayServer(opts) {
|
|
|
1221
1593
|
var SkillExecutor = class {
|
|
1222
1594
|
skillMap;
|
|
1223
1595
|
modeMap;
|
|
1596
|
+
concurrencyGuard;
|
|
1224
1597
|
/**
|
|
1225
1598
|
* @param configs - Parsed SkillConfig array (from parseSkillsFile).
|
|
1226
1599
|
* @param modes - Map from skill type string to its executor implementation.
|
|
1600
|
+
* @param concurrencyGuard - Optional ConcurrencyGuard to enforce max_concurrent limits.
|
|
1227
1601
|
*/
|
|
1228
|
-
constructor(configs, modes) {
|
|
1602
|
+
constructor(configs, modes, concurrencyGuard) {
|
|
1229
1603
|
this.skillMap = new Map(configs.map((c) => [c.id, c]));
|
|
1230
1604
|
this.modeMap = modes;
|
|
1605
|
+
this.concurrencyGuard = concurrencyGuard;
|
|
1231
1606
|
}
|
|
1232
1607
|
/**
|
|
1233
1608
|
* Execute a skill by ID with the given input parameters.
|
|
@@ -1260,6 +1635,18 @@ var SkillExecutor = class {
|
|
|
1260
1635
|
latency_ms: Date.now() - startTime
|
|
1261
1636
|
};
|
|
1262
1637
|
}
|
|
1638
|
+
const maxConcurrent = config.capacity?.max_concurrent ?? Infinity;
|
|
1639
|
+
if (this.concurrencyGuard && maxConcurrent !== Infinity) {
|
|
1640
|
+
if (!this.concurrencyGuard.canAccept(skillId, maxConcurrent)) {
|
|
1641
|
+
return {
|
|
1642
|
+
success: false,
|
|
1643
|
+
error: `Skill ${skillId} at capacity (${maxConcurrent} concurrent max)`,
|
|
1644
|
+
latency_ms: Date.now() - startTime,
|
|
1645
|
+
failure_reason: "overload"
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
this.concurrencyGuard.acquire(skillId);
|
|
1649
|
+
}
|
|
1263
1650
|
try {
|
|
1264
1651
|
const modeResult = await mode.execute(config, params, onProgress);
|
|
1265
1652
|
return {
|
|
@@ -1273,6 +1660,10 @@ var SkillExecutor = class {
|
|
|
1273
1660
|
error: message,
|
|
1274
1661
|
latency_ms: Date.now() - startTime
|
|
1275
1662
|
};
|
|
1663
|
+
} finally {
|
|
1664
|
+
if (this.concurrencyGuard && maxConcurrent !== Infinity) {
|
|
1665
|
+
this.concurrencyGuard.release(skillId);
|
|
1666
|
+
}
|
|
1276
1667
|
}
|
|
1277
1668
|
}
|
|
1278
1669
|
/**
|
|
@@ -1293,13 +1684,16 @@ var SkillExecutor = class {
|
|
|
1293
1684
|
return this.skillMap.get(skillId);
|
|
1294
1685
|
}
|
|
1295
1686
|
};
|
|
1296
|
-
function createSkillExecutor(configs, modes) {
|
|
1297
|
-
return new SkillExecutor(configs, modes);
|
|
1687
|
+
function createSkillExecutor(configs, modes, concurrencyGuard) {
|
|
1688
|
+
return new SkillExecutor(configs, modes, concurrencyGuard);
|
|
1298
1689
|
}
|
|
1299
1690
|
|
|
1300
1691
|
// src/skills/skill-config.ts
|
|
1301
1692
|
import { z as z2 } from "zod";
|
|
1302
1693
|
import yaml from "js-yaml";
|
|
1694
|
+
var CapacitySchema = z2.object({
|
|
1695
|
+
max_concurrent: z2.number().positive().int().optional()
|
|
1696
|
+
}).optional();
|
|
1303
1697
|
var PricingSchema = z2.object({
|
|
1304
1698
|
credits_per_call: z2.number().nonnegative(),
|
|
1305
1699
|
credits_per_minute: z2.number().nonnegative().optional(),
|
|
@@ -1321,6 +1715,12 @@ var ApiAuthSchema = z2.discriminatedUnion("type", [
|
|
|
1321
1715
|
password: z2.string()
|
|
1322
1716
|
})
|
|
1323
1717
|
]);
|
|
1718
|
+
var CapabilityDeclarationSchema = {
|
|
1719
|
+
description: z2.string().optional(),
|
|
1720
|
+
capability_types: z2.array(z2.string()).optional(),
|
|
1721
|
+
requires_capabilities: z2.array(z2.string()).optional(),
|
|
1722
|
+
visibility: z2.enum(["public", "private"]).optional()
|
|
1723
|
+
};
|
|
1324
1724
|
var ApiSkillConfigSchema = z2.object({
|
|
1325
1725
|
id: z2.string().min(1),
|
|
1326
1726
|
type: z2.literal("api"),
|
|
@@ -1333,7 +1733,9 @@ var ApiSkillConfigSchema = z2.object({
|
|
|
1333
1733
|
pricing: PricingSchema,
|
|
1334
1734
|
timeout_ms: z2.number().positive().default(3e4),
|
|
1335
1735
|
retries: z2.number().nonnegative().int().default(0),
|
|
1336
|
-
provider: z2.string().optional()
|
|
1736
|
+
provider: z2.string().optional(),
|
|
1737
|
+
capacity: CapacitySchema,
|
|
1738
|
+
...CapabilityDeclarationSchema
|
|
1337
1739
|
});
|
|
1338
1740
|
var PipelineStepSchema = z2.union([
|
|
1339
1741
|
z2.object({
|
|
@@ -1351,7 +1753,9 @@ var PipelineSkillConfigSchema = z2.object({
|
|
|
1351
1753
|
name: z2.string().min(1),
|
|
1352
1754
|
steps: z2.array(PipelineStepSchema).min(1),
|
|
1353
1755
|
pricing: PricingSchema,
|
|
1354
|
-
timeout_ms: z2.number().positive().optional()
|
|
1756
|
+
timeout_ms: z2.number().positive().optional(),
|
|
1757
|
+
capacity: CapacitySchema,
|
|
1758
|
+
...CapabilityDeclarationSchema
|
|
1355
1759
|
});
|
|
1356
1760
|
var OpenClawSkillConfigSchema = z2.object({
|
|
1357
1761
|
id: z2.string().min(1),
|
|
@@ -1360,7 +1764,17 @@ var OpenClawSkillConfigSchema = z2.object({
|
|
|
1360
1764
|
agent_name: z2.string().min(1),
|
|
1361
1765
|
channel: z2.enum(["telegram", "webhook", "process"]),
|
|
1362
1766
|
pricing: PricingSchema,
|
|
1363
|
-
timeout_ms: z2.number().positive().optional()
|
|
1767
|
+
timeout_ms: z2.number().positive().optional(),
|
|
1768
|
+
capacity: CapacitySchema,
|
|
1769
|
+
...CapabilityDeclarationSchema
|
|
1770
|
+
});
|
|
1771
|
+
var ClaudeCodeConfigSchema = z2.object({
|
|
1772
|
+
/** Optional system prompt passed via `-p` flag. */
|
|
1773
|
+
system_prompt: z2.string().optional(),
|
|
1774
|
+
/** Model to use (e.g. 'claude-opus-4-6', 'claude-sonnet-4-6'). */
|
|
1775
|
+
model: z2.string().optional(),
|
|
1776
|
+
/** When true, passes `--dangerously-skip-permissions` to claude CLI. */
|
|
1777
|
+
auto_mode: z2.boolean().default(false)
|
|
1364
1778
|
});
|
|
1365
1779
|
var CommandSkillConfigSchema = z2.object({
|
|
1366
1780
|
id: z2.string().min(1),
|
|
@@ -1370,8 +1784,11 @@ var CommandSkillConfigSchema = z2.object({
|
|
|
1370
1784
|
output_type: z2.enum(["json", "text", "file"]),
|
|
1371
1785
|
allowed_commands: z2.array(z2.string()).optional(),
|
|
1372
1786
|
working_dir: z2.string().optional(),
|
|
1787
|
+
claude_code: ClaudeCodeConfigSchema.optional(),
|
|
1373
1788
|
timeout_ms: z2.number().positive().default(3e4),
|
|
1374
|
-
pricing: PricingSchema
|
|
1789
|
+
pricing: PricingSchema,
|
|
1790
|
+
capacity: CapacitySchema,
|
|
1791
|
+
...CapabilityDeclarationSchema
|
|
1375
1792
|
});
|
|
1376
1793
|
var ConductorSkillConfigSchema = z2.object({
|
|
1377
1794
|
id: z2.string().min(1),
|
|
@@ -1379,7 +1796,9 @@ var ConductorSkillConfigSchema = z2.object({
|
|
|
1379
1796
|
name: z2.string().min(1),
|
|
1380
1797
|
conductor_skill: z2.enum(["orchestrate", "plan"]),
|
|
1381
1798
|
pricing: PricingSchema,
|
|
1382
|
-
timeout_ms: z2.number().positive().optional()
|
|
1799
|
+
timeout_ms: z2.number().positive().optional(),
|
|
1800
|
+
capacity: CapacitySchema,
|
|
1801
|
+
...CapabilityDeclarationSchema
|
|
1383
1802
|
});
|
|
1384
1803
|
var SkillConfigSchema = z2.discriminatedUnion("type", [
|
|
1385
1804
|
ApiSkillConfigSchema,
|
|
@@ -1948,14 +2367,32 @@ var CommandExecutor = class {
|
|
|
1948
2367
|
const cmdConfig = config;
|
|
1949
2368
|
const baseCommand = cmdConfig.command.trim().split(/\s+/)[0] ?? "";
|
|
1950
2369
|
if (cmdConfig.allowed_commands && cmdConfig.allowed_commands.length > 0) {
|
|
1951
|
-
|
|
2370
|
+
const effectiveAllowed = cmdConfig.claude_code ? [.../* @__PURE__ */ new Set([...cmdConfig.allowed_commands, "claude"])] : cmdConfig.allowed_commands;
|
|
2371
|
+
const commandToCheck = cmdConfig.claude_code ? "claude" : baseCommand;
|
|
2372
|
+
if (!effectiveAllowed.includes(commandToCheck)) {
|
|
1952
2373
|
return {
|
|
1953
2374
|
success: false,
|
|
1954
|
-
error: `Command not allowed: "${
|
|
2375
|
+
error: `Command not allowed: "${commandToCheck}". Allowed: ${effectiveAllowed.join(", ")}`
|
|
1955
2376
|
};
|
|
1956
2377
|
}
|
|
1957
2378
|
}
|
|
1958
|
-
|
|
2379
|
+
let interpolatedCommand;
|
|
2380
|
+
if (cmdConfig.claude_code) {
|
|
2381
|
+
const parts = ["claude", "--print"];
|
|
2382
|
+
if (cmdConfig.claude_code.auto_mode) {
|
|
2383
|
+
parts.push("--dangerously-skip-permissions");
|
|
2384
|
+
}
|
|
2385
|
+
if (cmdConfig.claude_code.model) {
|
|
2386
|
+
parts.push("--model", cmdConfig.claude_code.model);
|
|
2387
|
+
}
|
|
2388
|
+
if (cmdConfig.claude_code.system_prompt) {
|
|
2389
|
+
parts.push("-p", shellEscape2(cmdConfig.claude_code.system_prompt));
|
|
2390
|
+
}
|
|
2391
|
+
const interpolatedPrompt = safeInterpolateCommand2(cmdConfig.command, { params });
|
|
2392
|
+
interpolatedCommand = parts.join(" ") + " " + interpolatedPrompt;
|
|
2393
|
+
} else {
|
|
2394
|
+
interpolatedCommand = safeInterpolateCommand2(cmdConfig.command, { params });
|
|
2395
|
+
}
|
|
1959
2396
|
const timeout = cmdConfig.timeout_ms ?? 3e4;
|
|
1960
2397
|
const cwd = cmdConfig.working_dir ?? process.cwd();
|
|
1961
2398
|
let stdout;
|
|
@@ -2017,7 +2454,143 @@ var CommandExecutor = class {
|
|
|
2017
2454
|
};
|
|
2018
2455
|
|
|
2019
2456
|
// src/conductor/task-decomposer.ts
|
|
2020
|
-
import { randomUUID as
|
|
2457
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
2458
|
+
|
|
2459
|
+
// src/conductor/decomposition-validator.ts
|
|
2460
|
+
function validateAndNormalizeSubtasks(raw, context) {
|
|
2461
|
+
try {
|
|
2462
|
+
return _validate(raw, context);
|
|
2463
|
+
} catch {
|
|
2464
|
+
return { valid: [], errors: ["internal validation error"] };
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
function _validate(raw, context) {
|
|
2468
|
+
if (!Array.isArray(raw)) {
|
|
2469
|
+
return { valid: [], errors: ["decomposition output must be an array"] };
|
|
2470
|
+
}
|
|
2471
|
+
if (raw.length === 0) {
|
|
2472
|
+
return { valid: [], errors: [] };
|
|
2473
|
+
}
|
|
2474
|
+
const errors = [];
|
|
2475
|
+
const validItems = [];
|
|
2476
|
+
const validIds = [];
|
|
2477
|
+
for (let i = 0; i < raw.length; i++) {
|
|
2478
|
+
const item = raw[i];
|
|
2479
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
2480
|
+
errors.push(`subtask[${i}]: must be an object`);
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2483
|
+
const obj = item;
|
|
2484
|
+
let itemValid = true;
|
|
2485
|
+
const id = obj["id"];
|
|
2486
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
2487
|
+
errors.push(`subtask[${i}]: id must be a non-empty string`);
|
|
2488
|
+
itemValid = false;
|
|
2489
|
+
}
|
|
2490
|
+
const description = obj["description"];
|
|
2491
|
+
if (typeof description !== "string" || description.length === 0) {
|
|
2492
|
+
errors.push(`subtask[${i}]: description must be a non-empty string`);
|
|
2493
|
+
itemValid = false;
|
|
2494
|
+
}
|
|
2495
|
+
const required_capability = obj["required_capability"];
|
|
2496
|
+
if (typeof required_capability !== "string" || required_capability.length === 0) {
|
|
2497
|
+
errors.push(`subtask[${i}]: required_capability must be a non-empty string`);
|
|
2498
|
+
itemValid = false;
|
|
2499
|
+
}
|
|
2500
|
+
const estimated_credits = obj["estimated_credits"];
|
|
2501
|
+
if (estimated_credits !== void 0) {
|
|
2502
|
+
if (typeof estimated_credits !== "number" || estimated_credits <= 0) {
|
|
2503
|
+
errors.push(`subtask[${i}]: estimated_credits must be a positive number`);
|
|
2504
|
+
itemValid = false;
|
|
2505
|
+
} else if (estimated_credits > context.max_credits) {
|
|
2506
|
+
errors.push(
|
|
2507
|
+
`subtask[${i}]: estimated_credits ${estimated_credits} exceeds max_credits ${context.max_credits}`
|
|
2508
|
+
);
|
|
2509
|
+
itemValid = false;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
if (itemValid) {
|
|
2513
|
+
validItems.push(obj);
|
|
2514
|
+
validIds.push(id);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
const idSet = /* @__PURE__ */ new Set();
|
|
2518
|
+
for (const id of validIds) {
|
|
2519
|
+
if (idSet.has(id)) {
|
|
2520
|
+
errors.push(`duplicate subtask id: ${id}`);
|
|
2521
|
+
} else {
|
|
2522
|
+
idSet.add(id);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
for (let i = 0; i < validItems.length; i++) {
|
|
2526
|
+
const item = validItems[i];
|
|
2527
|
+
const depends_on = item["depends_on"];
|
|
2528
|
+
if (!Array.isArray(depends_on)) continue;
|
|
2529
|
+
for (const dep of depends_on) {
|
|
2530
|
+
if (typeof dep === "string" && !idSet.has(dep)) {
|
|
2531
|
+
errors.push(`subtask[${i}]: depends_on references unknown id '${dep}'`);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
if (errors.length === 0 && validItems.length > 0) {
|
|
2536
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
2537
|
+
const adjList = /* @__PURE__ */ new Map();
|
|
2538
|
+
for (const id of validIds) {
|
|
2539
|
+
inDegree.set(id, 0);
|
|
2540
|
+
adjList.set(id, []);
|
|
2541
|
+
}
|
|
2542
|
+
for (const item of validItems) {
|
|
2543
|
+
const depends_on = item["depends_on"];
|
|
2544
|
+
if (!Array.isArray(depends_on)) continue;
|
|
2545
|
+
for (const dep of depends_on) {
|
|
2546
|
+
if (typeof dep !== "string" || !idSet.has(dep)) continue;
|
|
2547
|
+
adjList.get(dep)?.push(item["id"]);
|
|
2548
|
+
inDegree.set(item["id"], (inDegree.get(item["id"]) ?? 0) + 1);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
const queue = [];
|
|
2552
|
+
for (const [id, deg] of inDegree) {
|
|
2553
|
+
if (deg === 0) queue.push(id);
|
|
2554
|
+
}
|
|
2555
|
+
let processed = 0;
|
|
2556
|
+
while (queue.length > 0) {
|
|
2557
|
+
const current = queue.shift();
|
|
2558
|
+
processed++;
|
|
2559
|
+
for (const neighbor of adjList.get(current) ?? []) {
|
|
2560
|
+
const newDeg = (inDegree.get(neighbor) ?? 0) - 1;
|
|
2561
|
+
inDegree.set(neighbor, newDeg);
|
|
2562
|
+
if (newDeg === 0) queue.push(neighbor);
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
if (processed < validItems.length) {
|
|
2566
|
+
for (const [id, deg] of inDegree) {
|
|
2567
|
+
if (deg > 0) {
|
|
2568
|
+
errors.push(`circular dependency detected involving subtask id: ${id}`);
|
|
2569
|
+
break;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
if (errors.length > 0) {
|
|
2575
|
+
return { valid: [], errors };
|
|
2576
|
+
}
|
|
2577
|
+
const normalized = validItems.map((item) => {
|
|
2578
|
+
const depends_on = Array.isArray(item["depends_on"]) ? item["depends_on"].filter((x) => typeof x === "string") : [];
|
|
2579
|
+
const params = typeof item["params"] === "object" && item["params"] !== null && !Array.isArray(item["params"]) ? item["params"] : {};
|
|
2580
|
+
const estimated_credits = typeof item["estimated_credits"] === "number" ? item["estimated_credits"] : 0;
|
|
2581
|
+
return {
|
|
2582
|
+
id: item["id"],
|
|
2583
|
+
description: item["description"],
|
|
2584
|
+
required_capability: item["required_capability"],
|
|
2585
|
+
params,
|
|
2586
|
+
depends_on,
|
|
2587
|
+
estimated_credits
|
|
2588
|
+
};
|
|
2589
|
+
});
|
|
2590
|
+
return { valid: normalized, errors: [] };
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
// src/conductor/task-decomposer.ts
|
|
2021
2594
|
var TEMPLATES = {
|
|
2022
2595
|
"video-production": {
|
|
2023
2596
|
keywords: ["video", "demo", "clip", "animation"],
|
|
@@ -2112,7 +2685,7 @@ function decompose(task, _availableCapabilities) {
|
|
|
2112
2685
|
for (const template of Object.values(TEMPLATES)) {
|
|
2113
2686
|
const matched = template.keywords.some((kw) => lower.includes(kw));
|
|
2114
2687
|
if (!matched) continue;
|
|
2115
|
-
const ids = template.steps.map(() =>
|
|
2688
|
+
const ids = template.steps.map(() => randomUUID7());
|
|
2116
2689
|
return template.steps.map((step, i) => ({
|
|
2117
2690
|
id: ids[i],
|
|
2118
2691
|
description: step.description,
|
|
@@ -2126,10 +2699,10 @@ function decompose(task, _availableCapabilities) {
|
|
|
2126
2699
|
}
|
|
2127
2700
|
|
|
2128
2701
|
// src/gateway/client.ts
|
|
2129
|
-
import { randomUUID as
|
|
2702
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
2130
2703
|
async function requestCapability(opts) {
|
|
2131
2704
|
const { gatewayUrl, token, cardId, params = {}, timeoutMs = 3e5, escrowReceipt, identity } = opts;
|
|
2132
|
-
const id =
|
|
2705
|
+
const id = randomUUID8();
|
|
2133
2706
|
const payload = {
|
|
2134
2707
|
jsonrpc: "2.0",
|
|
2135
2708
|
id,
|
|
@@ -2199,36 +2772,15 @@ async function requestViaRelay(relay, opts) {
|
|
|
2199
2772
|
}
|
|
2200
2773
|
|
|
2201
2774
|
// src/autonomy/tiers.ts
|
|
2202
|
-
import { randomUUID as
|
|
2775
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
2203
2776
|
|
|
2204
2777
|
// src/autonomy/pending-requests.ts
|
|
2205
|
-
import { randomUUID as
|
|
2778
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
2206
2779
|
|
|
2207
2780
|
// src/cli/peers.ts
|
|
2208
2781
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
2209
2782
|
import { join as join3 } from "path";
|
|
2210
2783
|
|
|
2211
|
-
// src/cli/config.ts
|
|
2212
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync2 } from "fs";
|
|
2213
|
-
import { homedir } from "os";
|
|
2214
|
-
import { join as join2 } from "path";
|
|
2215
|
-
function getConfigDir() {
|
|
2216
|
-
return process.env["AGENTBNB_DIR"] ?? join2(homedir(), ".agentbnb");
|
|
2217
|
-
}
|
|
2218
|
-
function getConfigPath() {
|
|
2219
|
-
return join2(getConfigDir(), "config.json");
|
|
2220
|
-
}
|
|
2221
|
-
function loadConfig() {
|
|
2222
|
-
const configPath = getConfigPath();
|
|
2223
|
-
if (!existsSync2(configPath)) return null;
|
|
2224
|
-
try {
|
|
2225
|
-
const raw = readFileSync2(configPath, "utf-8");
|
|
2226
|
-
return JSON.parse(raw);
|
|
2227
|
-
} catch {
|
|
2228
|
-
return null;
|
|
2229
|
-
}
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
2784
|
// src/cli/remote-registry.ts
|
|
2233
2785
|
var RegistryTimeoutError = class extends AgentBnBError {
|
|
2234
2786
|
constructor(url) {
|
|
@@ -2326,12 +2878,14 @@ function scorePeers(candidates, selfOwner) {
|
|
|
2326
2878
|
const idleRate = internal?.["idle_rate"];
|
|
2327
2879
|
return typeof idleRate === "number" ? idleRate : 1;
|
|
2328
2880
|
});
|
|
2881
|
+
const loadFactors = eligible.map((c) => c.loadFactor ?? 1);
|
|
2329
2882
|
const normSuccess = minMaxNormalize(successRates);
|
|
2330
2883
|
const normCost = minMaxNormalize(costEfficiencies);
|
|
2331
2884
|
const normIdle = minMaxNormalize(idleRates);
|
|
2885
|
+
const normLoad = minMaxNormalize(loadFactors);
|
|
2332
2886
|
const scored = eligible.map((c, i) => ({
|
|
2333
2887
|
...c,
|
|
2334
|
-
rawScore: (normSuccess[i] ?? 0) * (normCost[i] ?? 0) * (normIdle[i] ?? 0)
|
|
2888
|
+
rawScore: (normSuccess[i] ?? 0) * (normCost[i] ?? 0) * (normIdle[i] ?? 0) * (normLoad[i] ?? 0)
|
|
2335
2889
|
}));
|
|
2336
2890
|
scored.sort((a, b) => b.rawScore - a.rawScore);
|
|
2337
2891
|
return scored;
|
|
@@ -2573,6 +3127,12 @@ function computeWaves(subtasks) {
|
|
|
2573
3127
|
async function orchestrate(opts) {
|
|
2574
3128
|
const { subtasks, matches, gatewayToken, resolveAgentUrl, timeoutMs = 3e5, maxBudget, relayClient, requesterOwner } = opts;
|
|
2575
3129
|
const startTime = Date.now();
|
|
3130
|
+
const teamMemberMap = /* @__PURE__ */ new Map();
|
|
3131
|
+
if (opts.team) {
|
|
3132
|
+
for (const member of opts.team.matched) {
|
|
3133
|
+
teamMemberMap.set(member.subtask.id, member);
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
2576
3136
|
if (subtasks.length === 0) {
|
|
2577
3137
|
return {
|
|
2578
3138
|
success: true,
|
|
@@ -2584,6 +3144,7 @@ async function orchestrate(opts) {
|
|
|
2584
3144
|
const results = /* @__PURE__ */ new Map();
|
|
2585
3145
|
const errors = [];
|
|
2586
3146
|
let totalCredits = 0;
|
|
3147
|
+
const traceContext = /* @__PURE__ */ new Map();
|
|
2587
3148
|
const waves = computeWaves(subtasks);
|
|
2588
3149
|
const subtaskMap = new Map(subtasks.map((s) => [s.id, s]));
|
|
2589
3150
|
for (const wave of waves) {
|
|
@@ -2620,7 +3181,11 @@ async function orchestrate(opts) {
|
|
|
2620
3181
|
subtask.params,
|
|
2621
3182
|
interpContext
|
|
2622
3183
|
);
|
|
2623
|
-
const
|
|
3184
|
+
const teamMember = teamMemberMap.get(taskId);
|
|
3185
|
+
const teamId = opts.team?.team_id ?? null;
|
|
3186
|
+
const taskCapabilityType = teamMember?.capability_type ?? null;
|
|
3187
|
+
const agentOwner = teamMember?.agent ?? m.selected_agent;
|
|
3188
|
+
const primary = resolveAgentUrl(agentOwner);
|
|
2624
3189
|
try {
|
|
2625
3190
|
let res;
|
|
2626
3191
|
if (primary.url.startsWith("relay://") && relayClient) {
|
|
@@ -2641,7 +3206,7 @@ async function orchestrate(opts) {
|
|
|
2641
3206
|
timeoutMs
|
|
2642
3207
|
});
|
|
2643
3208
|
}
|
|
2644
|
-
return { taskId, result: res, credits: m.credits };
|
|
3209
|
+
return { taskId, result: res, credits: m.credits, team_id: teamId, capability_type: taskCapabilityType };
|
|
2645
3210
|
} catch (primaryErr) {
|
|
2646
3211
|
if (m.alternatives.length > 0) {
|
|
2647
3212
|
const alt = m.alternatives[0];
|
|
@@ -2666,7 +3231,7 @@ async function orchestrate(opts) {
|
|
|
2666
3231
|
timeoutMs
|
|
2667
3232
|
});
|
|
2668
3233
|
}
|
|
2669
|
-
return { taskId, result: altRes, credits: alt.credits };
|
|
3234
|
+
return { taskId, result: altRes, credits: alt.credits, team_id: teamId, capability_type: taskCapabilityType };
|
|
2670
3235
|
} catch (altErr) {
|
|
2671
3236
|
throw new Error(
|
|
2672
3237
|
`Task ${taskId}: primary (${m.selected_agent}) failed: ${primaryErr instanceof Error ? primaryErr.message : String(primaryErr)}; alternative (${alt.agent}) failed: ${altErr instanceof Error ? altErr.message : String(altErr)}`
|
|
@@ -2681,9 +3246,10 @@ async function orchestrate(opts) {
|
|
|
2681
3246
|
);
|
|
2682
3247
|
for (const settlement of waveResults) {
|
|
2683
3248
|
if (settlement.status === "fulfilled") {
|
|
2684
|
-
const { taskId, result, credits } = settlement.value;
|
|
3249
|
+
const { taskId, result, credits, team_id, capability_type } = settlement.value;
|
|
2685
3250
|
results.set(taskId, result);
|
|
2686
3251
|
totalCredits += credits;
|
|
3252
|
+
traceContext.set(taskId, { team_id: team_id ?? null, capability_type: capability_type ?? null });
|
|
2687
3253
|
} else {
|
|
2688
3254
|
errors.push(settlement.reason instanceof Error ? settlement.reason.message : String(settlement.reason));
|
|
2689
3255
|
}
|
|
@@ -2694,7 +3260,8 @@ async function orchestrate(opts) {
|
|
|
2694
3260
|
results,
|
|
2695
3261
|
total_credits: totalCredits,
|
|
2696
3262
|
latency_ms: Date.now() - startTime,
|
|
2697
|
-
errors: errors.length > 0 ? errors : void 0
|
|
3263
|
+
errors: errors.length > 0 ? errors : void 0,
|
|
3264
|
+
trace: traceContext.size > 0 ? traceContext : void 0
|
|
2698
3265
|
};
|
|
2699
3266
|
}
|
|
2700
3267
|
|
|
@@ -2743,6 +3310,96 @@ var BudgetManager = class {
|
|
|
2743
3310
|
}
|
|
2744
3311
|
};
|
|
2745
3312
|
|
|
3313
|
+
// src/conductor/team-formation.ts
|
|
3314
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
3315
|
+
function selectByStrategy(matches, strategy) {
|
|
3316
|
+
if (matches.length === 0) return void 0;
|
|
3317
|
+
if (strategy === "balanced") {
|
|
3318
|
+
return matches[0];
|
|
3319
|
+
}
|
|
3320
|
+
if (strategy === "quality_optimized") {
|
|
3321
|
+
return matches.reduce((best, m) => m.score > best.score ? m : best, matches[0]);
|
|
3322
|
+
}
|
|
3323
|
+
return matches.reduce((best, m) => {
|
|
3324
|
+
if (m.credits < best.credits) return m;
|
|
3325
|
+
if (m.credits === best.credits && m.score > best.score) return m;
|
|
3326
|
+
return best;
|
|
3327
|
+
}, matches[0]);
|
|
3328
|
+
}
|
|
3329
|
+
async function formTeam(opts) {
|
|
3330
|
+
const { subtasks, strategy, db, conductorOwner, registryUrl } = opts;
|
|
3331
|
+
const team_id = randomUUID11();
|
|
3332
|
+
if (subtasks.length === 0) {
|
|
3333
|
+
return { team_id, strategy, matched: [], unrouted: [] };
|
|
3334
|
+
}
|
|
3335
|
+
const matched = [];
|
|
3336
|
+
const unrouted = [];
|
|
3337
|
+
for (const subtask of subtasks) {
|
|
3338
|
+
const skillCards = getCardsBySkillCapability(db, subtask.required_capability).filter((c) => c.owner !== conductorOwner);
|
|
3339
|
+
if (skillCards.length > 0) {
|
|
3340
|
+
const candidates = skillCards.map((card) => {
|
|
3341
|
+
const skills = card.skills ?? [];
|
|
3342
|
+
const matchingSkill = skills.find(
|
|
3343
|
+
(s) => s.capability_type === subtask.required_capability || (s.capability_types ?? []).includes(subtask.required_capability)
|
|
3344
|
+
);
|
|
3345
|
+
return {
|
|
3346
|
+
subtask_id: subtask.id,
|
|
3347
|
+
selected_agent: card.owner,
|
|
3348
|
+
selected_skill: matchingSkill?.id ?? "",
|
|
3349
|
+
selected_card_id: card.id,
|
|
3350
|
+
score: 1,
|
|
3351
|
+
credits: matchingSkill?.pricing.credits_per_call ?? 0,
|
|
3352
|
+
alternatives: []
|
|
3353
|
+
};
|
|
3354
|
+
});
|
|
3355
|
+
const selected2 = selectByStrategy(candidates, strategy);
|
|
3356
|
+
matched.push({
|
|
3357
|
+
subtask,
|
|
3358
|
+
capability_type: subtask.required_capability,
|
|
3359
|
+
agent: selected2.selected_agent,
|
|
3360
|
+
skill: selected2.selected_skill,
|
|
3361
|
+
card_id: selected2.selected_card_id,
|
|
3362
|
+
credits: selected2.credits,
|
|
3363
|
+
score: selected2.score
|
|
3364
|
+
});
|
|
3365
|
+
continue;
|
|
3366
|
+
}
|
|
3367
|
+
const matchResults = await matchSubTasks({
|
|
3368
|
+
db,
|
|
3369
|
+
subtasks: [subtask],
|
|
3370
|
+
conductorOwner,
|
|
3371
|
+
registryUrl
|
|
3372
|
+
});
|
|
3373
|
+
const m = matchResults[0];
|
|
3374
|
+
if (!m || m.selected_agent === "") {
|
|
3375
|
+
unrouted.push(subtask);
|
|
3376
|
+
continue;
|
|
3377
|
+
}
|
|
3378
|
+
const allCandidates = [
|
|
3379
|
+
m,
|
|
3380
|
+
...m.alternatives.map((alt) => ({
|
|
3381
|
+
subtask_id: m.subtask_id,
|
|
3382
|
+
selected_agent: alt.agent,
|
|
3383
|
+
selected_skill: alt.skill,
|
|
3384
|
+
score: alt.score,
|
|
3385
|
+
credits: alt.credits,
|
|
3386
|
+
alternatives: []
|
|
3387
|
+
}))
|
|
3388
|
+
];
|
|
3389
|
+
const selected = selectByStrategy(allCandidates, strategy);
|
|
3390
|
+
matched.push({
|
|
3391
|
+
subtask,
|
|
3392
|
+
capability_type: subtask.required_capability,
|
|
3393
|
+
agent: selected.selected_agent,
|
|
3394
|
+
skill: selected.selected_skill,
|
|
3395
|
+
card_id: selected === m ? m.selected_card_id : void 0,
|
|
3396
|
+
credits: selected.credits,
|
|
3397
|
+
score: selected.score
|
|
3398
|
+
});
|
|
3399
|
+
}
|
|
3400
|
+
return { team_id, strategy, matched, unrouted };
|
|
3401
|
+
}
|
|
3402
|
+
|
|
2746
3403
|
// src/conductor/conductor-mode.ts
|
|
2747
3404
|
var ConductorMode = class {
|
|
2748
3405
|
db;
|
|
@@ -2781,7 +3438,48 @@ var ConductorMode = class {
|
|
|
2781
3438
|
error: 'Missing or empty "task" parameter'
|
|
2782
3439
|
};
|
|
2783
3440
|
}
|
|
2784
|
-
const
|
|
3441
|
+
const orchestrationDepth = typeof params.orchestration_depth === "number" ? params.orchestration_depth : 0;
|
|
3442
|
+
const decompositionDepth = typeof params.decomposition_depth === "number" ? params.decomposition_depth : 0;
|
|
3443
|
+
if (orchestrationDepth >= 2) {
|
|
3444
|
+
return {
|
|
3445
|
+
success: false,
|
|
3446
|
+
error: "orchestration_depth limit exceeded: max 1 nested orchestration"
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
let subtasks = [];
|
|
3450
|
+
if (decompositionDepth === 0) {
|
|
3451
|
+
const allDecomposers = getCardsByCapabilityType(this.db, "task_decomposition");
|
|
3452
|
+
const externalDecomposers = allDecomposers.filter((c) => c.owner !== this.conductorOwner);
|
|
3453
|
+
if (externalDecomposers.length > 0) {
|
|
3454
|
+
const provider = externalDecomposers[0];
|
|
3455
|
+
try {
|
|
3456
|
+
const providerUrl = this.resolveAgentUrl(provider.owner);
|
|
3457
|
+
const response = await requestCapability({
|
|
3458
|
+
gatewayUrl: providerUrl.url,
|
|
3459
|
+
token: this.gatewayToken,
|
|
3460
|
+
cardId: provider.id,
|
|
3461
|
+
params: {
|
|
3462
|
+
task,
|
|
3463
|
+
decomposition_depth: decompositionDepth + 1,
|
|
3464
|
+
orchestration_depth: orchestrationDepth + 1
|
|
3465
|
+
},
|
|
3466
|
+
timeoutMs: 3e4
|
|
3467
|
+
});
|
|
3468
|
+
if (Array.isArray(response)) {
|
|
3469
|
+
const validation = validateAndNormalizeSubtasks(response, {
|
|
3470
|
+
max_credits: this.maxBudget
|
|
3471
|
+
});
|
|
3472
|
+
if (validation.errors.length === 0) {
|
|
3473
|
+
subtasks = validation.valid;
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
} catch {
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
if (subtasks.length === 0) {
|
|
3481
|
+
subtasks = decompose(task);
|
|
3482
|
+
}
|
|
2785
3483
|
if (subtasks.length === 0) {
|
|
2786
3484
|
return {
|
|
2787
3485
|
success: false,
|
|
@@ -2795,6 +3493,17 @@ var ConductorMode = class {
|
|
|
2795
3493
|
conductorOwner: this.conductorOwner
|
|
2796
3494
|
});
|
|
2797
3495
|
onProgress?.({ step: 2, total: 5, message: `Matched ${matchResults.length} sub-tasks to agents` });
|
|
3496
|
+
let team;
|
|
3497
|
+
if (conductorSkill === "orchestrate") {
|
|
3498
|
+
const strategy = typeof params.formation_strategy === "string" && ["cost_optimized", "quality_optimized", "balanced"].includes(params.formation_strategy) ? params.formation_strategy : "balanced";
|
|
3499
|
+
team = await formTeam({
|
|
3500
|
+
subtasks,
|
|
3501
|
+
strategy,
|
|
3502
|
+
db: this.db,
|
|
3503
|
+
conductorOwner: this.conductorOwner
|
|
3504
|
+
});
|
|
3505
|
+
onProgress?.({ step: 2, total: 5, message: `Formed team: ${team.matched.length} members, ${team.unrouted.length} unrouted` });
|
|
3506
|
+
}
|
|
2798
3507
|
const budgetManager = new BudgetManager(this.creditDb, this.conductorOwner);
|
|
2799
3508
|
const budgetController = new BudgetController(budgetManager, this.maxBudget);
|
|
2800
3509
|
const executionBudget = budgetController.calculateBudget(matchResults);
|
|
@@ -2811,7 +3520,9 @@ var ConductorMode = class {
|
|
|
2811
3520
|
result: {
|
|
2812
3521
|
subtasks,
|
|
2813
3522
|
matches: matchResults,
|
|
2814
|
-
budget: executionBudget
|
|
3523
|
+
budget: executionBudget,
|
|
3524
|
+
team
|
|
3525
|
+
// undefined when no role hints
|
|
2815
3526
|
}
|
|
2816
3527
|
};
|
|
2817
3528
|
}
|
|
@@ -2823,7 +3534,8 @@ var ConductorMode = class {
|
|
|
2823
3534
|
matches: matchMap,
|
|
2824
3535
|
gatewayToken: this.gatewayToken,
|
|
2825
3536
|
resolveAgentUrl: this.resolveAgentUrl,
|
|
2826
|
-
maxBudget: this.maxBudget
|
|
3537
|
+
maxBudget: this.maxBudget,
|
|
3538
|
+
team
|
|
2827
3539
|
});
|
|
2828
3540
|
onProgress?.({ step: 4, total: 5, message: "Pipeline execution complete" });
|
|
2829
3541
|
const resultObj = {};
|
|
@@ -2846,7 +3558,7 @@ var ConductorMode = class {
|
|
|
2846
3558
|
|
|
2847
3559
|
// src/credit/escrow-receipt.ts
|
|
2848
3560
|
import { z as z3 } from "zod";
|
|
2849
|
-
import { randomUUID as
|
|
3561
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
2850
3562
|
var EscrowReceiptSchema = z3.object({
|
|
2851
3563
|
requester_owner: z3.string().min(1),
|
|
2852
3564
|
requester_public_key: z3.string().min(1),
|
|
@@ -2866,7 +3578,7 @@ function createSignedEscrowReceipt(db, privateKey, publicKey, opts) {
|
|
|
2866
3578
|
card_id: opts.cardId,
|
|
2867
3579
|
...opts.skillId ? { skill_id: opts.skillId } : {},
|
|
2868
3580
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2869
|
-
nonce:
|
|
3581
|
+
nonce: randomUUID12()
|
|
2870
3582
|
};
|
|
2871
3583
|
const signature = signEscrowReceipt(receiptData, privateKey);
|
|
2872
3584
|
const receipt = {
|
|
@@ -3237,7 +3949,7 @@ var AgentBnBProvider = class {
|
|
|
3237
3949
|
|
|
3238
3950
|
// src/identity/guarantor.ts
|
|
3239
3951
|
import { z as z5 } from "zod";
|
|
3240
|
-
import { randomUUID as
|
|
3952
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
3241
3953
|
var MAX_AGENTS_PER_GUARANTOR = 10;
|
|
3242
3954
|
var GUARANTOR_CREDIT_POOL = 50;
|
|
3243
3955
|
var GuarantorRecordSchema = z5.object({
|
|
@@ -3276,7 +3988,7 @@ function registerGuarantor(db, githubLogin) {
|
|
|
3276
3988
|
);
|
|
3277
3989
|
}
|
|
3278
3990
|
const record = {
|
|
3279
|
-
id:
|
|
3991
|
+
id: randomUUID13(),
|
|
3280
3992
|
github_login: githubLogin,
|
|
3281
3993
|
agent_count: 0,
|
|
3282
3994
|
credit_pool: GUARANTOR_CREDIT_POOL,
|
|
@@ -3350,7 +4062,7 @@ function getAgentGuarantor(db, agentId) {
|
|
|
3350
4062
|
function initiateGithubAuth() {
|
|
3351
4063
|
return {
|
|
3352
4064
|
auth_url: "https://github.com/login/oauth/authorize?client_id=PLACEHOLDER&scope=read:user",
|
|
3353
|
-
state:
|
|
4065
|
+
state: randomUUID13()
|
|
3354
4066
|
};
|
|
3355
4067
|
}
|
|
3356
4068
|
|
|
@@ -3422,6 +4134,27 @@ var RelayProgressMessageSchema = z6.object({
|
|
|
3422
4134
|
message: z6.string().optional()
|
|
3423
4135
|
// optional status message
|
|
3424
4136
|
});
|
|
4137
|
+
var HeartbeatMessageSchema = z6.object({
|
|
4138
|
+
type: z6.literal("heartbeat"),
|
|
4139
|
+
owner: z6.string().min(1),
|
|
4140
|
+
capacity: z6.object({
|
|
4141
|
+
current_load: z6.number(),
|
|
4142
|
+
max_concurrent: z6.number(),
|
|
4143
|
+
queue_depth: z6.number()
|
|
4144
|
+
}),
|
|
4145
|
+
self_summary: z6.object({
|
|
4146
|
+
capabilities: z6.array(z6.string()),
|
|
4147
|
+
success_rate: z6.number(),
|
|
4148
|
+
credit_balance: z6.number(),
|
|
4149
|
+
total_completed: z6.number(),
|
|
4150
|
+
provider_number: z6.number(),
|
|
4151
|
+
reliability: z6.object({
|
|
4152
|
+
current_streak: z6.number(),
|
|
4153
|
+
repeat_hire_rate: z6.number(),
|
|
4154
|
+
avg_feedback: z6.number()
|
|
4155
|
+
})
|
|
4156
|
+
})
|
|
4157
|
+
});
|
|
3425
4158
|
var RelayMessageSchema = z6.discriminatedUnion("type", [
|
|
3426
4159
|
RegisterMessageSchema,
|
|
3427
4160
|
RegisteredMessageSchema,
|
|
@@ -3430,11 +4163,12 @@ var RelayMessageSchema = z6.discriminatedUnion("type", [
|
|
|
3430
4163
|
RelayResponseMessageSchema,
|
|
3431
4164
|
ResponseMessageSchema,
|
|
3432
4165
|
ErrorMessageSchema,
|
|
3433
|
-
RelayProgressMessageSchema
|
|
4166
|
+
RelayProgressMessageSchema,
|
|
4167
|
+
HeartbeatMessageSchema
|
|
3434
4168
|
]);
|
|
3435
4169
|
|
|
3436
4170
|
// src/relay/websocket-relay.ts
|
|
3437
|
-
import { randomUUID as
|
|
4171
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
3438
4172
|
|
|
3439
4173
|
// src/relay/relay-credit.ts
|
|
3440
4174
|
function lookupCardPrice(registryDb, cardId, skillId) {
|
|
@@ -3492,10 +4226,10 @@ function releaseForRelay(creditDb, escrowId) {
|
|
|
3492
4226
|
}
|
|
3493
4227
|
|
|
3494
4228
|
// src/hub-agent/relay-bridge.ts
|
|
3495
|
-
import { randomUUID as
|
|
4229
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
3496
4230
|
|
|
3497
4231
|
// src/hub-agent/job-queue.ts
|
|
3498
|
-
import { randomUUID as
|
|
4232
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
3499
4233
|
function updateJobStatus(db, jobId, status, result) {
|
|
3500
4234
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3501
4235
|
if (result !== void 0) {
|
|
@@ -3540,6 +4274,7 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
3540
4274
|
const connections = /* @__PURE__ */ new Map();
|
|
3541
4275
|
const pendingRequests = /* @__PURE__ */ new Map();
|
|
3542
4276
|
const rateLimits = /* @__PURE__ */ new Map();
|
|
4277
|
+
const agentCapacities = /* @__PURE__ */ new Map();
|
|
3543
4278
|
let onAgentOnlineCallback;
|
|
3544
4279
|
function checkRateLimit(owner) {
|
|
3545
4280
|
const now = Date.now();
|
|
@@ -3616,7 +4351,7 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
3616
4351
|
function logAgentJoined(owner, cardName, cardId) {
|
|
3617
4352
|
try {
|
|
3618
4353
|
insertRequestLog(db, {
|
|
3619
|
-
id:
|
|
4354
|
+
id: randomUUID16(),
|
|
3620
4355
|
card_id: cardId,
|
|
3621
4356
|
card_name: cardName,
|
|
3622
4357
|
requester: owner,
|
|
@@ -3843,6 +4578,7 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
3843
4578
|
if (!owner) return;
|
|
3844
4579
|
connections.delete(owner);
|
|
3845
4580
|
rateLimits.delete(owner);
|
|
4581
|
+
agentCapacities.delete(owner);
|
|
3846
4582
|
markOwnerOffline(owner);
|
|
3847
4583
|
for (const [reqId, pending] of pendingRequests) {
|
|
3848
4584
|
if (pending.targetOwner === owner) {
|
|
@@ -3876,6 +4612,9 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
3876
4612
|
}
|
|
3877
4613
|
}
|
|
3878
4614
|
}
|
|
4615
|
+
function handleHeartbeat(msg) {
|
|
4616
|
+
agentCapacities.set(msg.owner, msg.capacity);
|
|
4617
|
+
}
|
|
3879
4618
|
void server.register(async (app) => {
|
|
3880
4619
|
app.get("/ws", { websocket: true }, (rawSocket, _request) => {
|
|
3881
4620
|
const socket = rawSocket;
|
|
@@ -3921,15 +4660,23 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
3921
4660
|
case "relay_progress":
|
|
3922
4661
|
handleRelayProgress(msg);
|
|
3923
4662
|
break;
|
|
4663
|
+
case "heartbeat":
|
|
4664
|
+
handleHeartbeat(msg);
|
|
4665
|
+
break;
|
|
3924
4666
|
default:
|
|
3925
4667
|
break;
|
|
3926
4668
|
}
|
|
3927
4669
|
})();
|
|
3928
4670
|
});
|
|
4671
|
+
const pingInterval = setInterval(() => {
|
|
4672
|
+
if (socket.readyState === 1) socket.ping();
|
|
4673
|
+
}, 3e4);
|
|
3929
4674
|
socket.on("close", () => {
|
|
4675
|
+
clearInterval(pingInterval);
|
|
3930
4676
|
handleDisconnect(registeredOwner);
|
|
3931
4677
|
});
|
|
3932
4678
|
socket.on("error", () => {
|
|
4679
|
+
clearInterval(pingInterval);
|
|
3933
4680
|
handleDisconnect(registeredOwner);
|
|
3934
4681
|
});
|
|
3935
4682
|
});
|
|
@@ -3950,6 +4697,7 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
3950
4697
|
}
|
|
3951
4698
|
pendingRequests.clear();
|
|
3952
4699
|
rateLimits.clear();
|
|
4700
|
+
agentCapacities.clear();
|
|
3953
4701
|
},
|
|
3954
4702
|
setOnAgentOnline: (cb) => {
|
|
3955
4703
|
onAgentOnlineCallback = cb;
|
|
@@ -3958,13 +4706,15 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
3958
4706
|
getPendingRequests: () => pendingRequests,
|
|
3959
4707
|
sendMessage: (ws, msg) => {
|
|
3960
4708
|
sendMessage(ws, msg);
|
|
3961
|
-
}
|
|
4709
|
+
},
|
|
4710
|
+
getAgentCapacity: (owner) => agentCapacities.get(owner),
|
|
4711
|
+
getAllCapacities: () => new Map(agentCapacities)
|
|
3962
4712
|
};
|
|
3963
4713
|
}
|
|
3964
4714
|
|
|
3965
4715
|
// src/relay/websocket-client.ts
|
|
3966
4716
|
import WebSocket from "ws";
|
|
3967
|
-
import { randomUUID as
|
|
4717
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
3968
4718
|
var RelayClient = class {
|
|
3969
4719
|
ws = null;
|
|
3970
4720
|
opts;
|
|
@@ -4061,7 +4811,7 @@ var RelayClient = class {
|
|
|
4061
4811
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN || !this.registered) {
|
|
4062
4812
|
throw new Error("Not connected to registry relay");
|
|
4063
4813
|
}
|
|
4064
|
-
const id =
|
|
4814
|
+
const id = randomUUID17();
|
|
4065
4815
|
const timeoutMs = opts.timeoutMs ?? 3e5;
|
|
4066
4816
|
return new Promise((resolve, reject) => {
|
|
4067
4817
|
const timeout = setTimeout(() => {
|
|
@@ -4265,12 +5015,12 @@ var RelayClient = class {
|
|
|
4265
5015
|
};
|
|
4266
5016
|
|
|
4267
5017
|
// src/onboarding/index.ts
|
|
4268
|
-
import { randomUUID as
|
|
5018
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
4269
5019
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
4270
5020
|
import { join as join5 } from "path";
|
|
4271
5021
|
|
|
4272
5022
|
// src/cli/onboarding.ts
|
|
4273
|
-
import { randomUUID as
|
|
5023
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
4274
5024
|
import { createConnection } from "net";
|
|
4275
5025
|
var KNOWN_API_KEYS = [
|
|
4276
5026
|
"OPENAI_API_KEY",
|
|
@@ -4412,7 +5162,7 @@ function capabilitiesToV2Card(capabilities, owner, agentName) {
|
|
|
4412
5162
|
}));
|
|
4413
5163
|
const card = {
|
|
4414
5164
|
spec_version: "2.0",
|
|
4415
|
-
id:
|
|
5165
|
+
id: randomUUID19(),
|
|
4416
5166
|
owner,
|
|
4417
5167
|
agent_name: agentName ?? owner,
|
|
4418
5168
|
skills,
|