agentbnb 7.0.0-beta.1 → 7.0.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-REW7BSWW.js → card-EX2EYGCZ.js} +1 -1
- package/dist/{chunk-PU7LXOQ3.js → chunk-3LWBH7P3.js} +72 -3
- package/dist/{chunk-2HSUPCBT.js → chunk-5AAFG2V2.js} +3 -3
- package/dist/{chunk-GO4FVRVN.js → chunk-5GME4KJZ.js} +5 -5
- package/dist/{chunk-VPQ44XKE.js → chunk-64AK4FJM.js} +2 -2
- package/dist/{chunk-K5FO42YF.js → chunk-7EF3HYVZ.js} +24 -1
- package/dist/{chunk-EAD4A4KG.js → chunk-ALX4WS3A.js} +2 -2
- package/dist/{chunk-ETGOKDFR.js → chunk-B2VJTKO5.js} +2 -2
- package/dist/{chunk-PGDBUUGR.js → chunk-C537SFHV.js} +5 -5
- package/dist/{chunk-F53QQIM2.js → chunk-CUONY5TO.js} +1 -1
- package/dist/{chunk-J2K5S5MX.js → chunk-D6RKW2XG.js} +67 -1
- package/dist/{chunk-APEG4QIN.js → chunk-E2OKP5CY.js} +4 -4
- package/dist/{chunk-FK2MDNTB.js → chunk-FTZTEHYG.js} +1 -1
- package/dist/{chunk-Y7T6IMM3.js → chunk-GKVTD4EZ.js} +1 -1
- package/dist/{chunk-VMH2YS2I.js → chunk-KF3TZHA5.js} +1 -1
- package/dist/{chunk-574W3HHE.js → chunk-LJM7FHPM.js} +1 -1
- package/dist/{chunk-KA2VIEGM.js → chunk-O2OYBAVR.js} +1 -1
- package/dist/{chunk-PSQHUZ7X.js → chunk-OH7BP5NP.js} +1 -1
- package/dist/{chunk-EHSHB7TY.js → chunk-SSK653A6.js} +67 -2
- package/dist/{chunk-BP3L2TET.js → chunk-TBJ3FZKZ.js} +2 -2
- package/dist/{chunk-3CIMVISQ.js → chunk-WVY2W7AA.js} +4 -0
- package/dist/{chunk-DUW6RX6I.js → chunk-X32NE6V4.js} +1 -1
- package/dist/{chunk-CWYPTQRQ.js → chunk-YHY7OG6S.js} +5 -5
- package/dist/{chunk-TW65F5EU.js → chunk-Z4MCGKTL.js} +6 -2
- package/dist/cli/index.js +44 -23
- package/dist/{client-HRYRJKSA.js → client-HKV3QWZ3.js} +3 -3
- package/dist/{conduct-JNYJCDHQ.js → conduct-W6XF6DJW.js} +13 -13
- package/dist/conduct-YB64OHI6.js +22 -0
- package/dist/{conductor-mode-2VVFMKVE.js → conductor-mode-2GSLHVN6.js} +3 -3
- package/dist/{conductor-mode-VGUU54QI.js → conductor-mode-AKREGDIU.js} +10 -10
- package/dist/{execute-MOXSSA3Q.js → execute-AYQWORVH.js} +6 -6
- package/dist/{execute-I4PKSNJM.js → execute-EPE6MZLT.js} +3 -3
- package/dist/index.d.ts +262 -10
- package/dist/index.js +438 -26
- package/dist/{process-guard-QCCBGILS.js → process-guard-GH5LRNWO.js} +1 -1
- package/dist/{publish-capability-TS6CNR5G.js → publish-capability-AH2HDW54.js} +3 -3
- package/dist/{request-E7TA7COA.js → request-HCCXSKAY.js} +12 -12
- package/dist/{serve-skill-HIOWYKRU.js → serve-skill-SZAQT5T5.js} +8 -8
- package/dist/{server-I63CXFX3.js → server-MHMAYXWZ.js} +11 -11
- package/dist/{service-coordinator-XBNT3SMU.js → service-coordinator-WGH6B2VT.js} +375 -48
- package/dist/skills/agentbnb/bootstrap.js +393 -58
- package/dist/{websocket-client-PFGVTXNE.js → websocket-client-4Z5P54RU.js} +1 -1
- package/dist/websocket-client-QOVARTRN.js +7 -0
- package/package.json +17 -11
- package/skills/agentbnb/bootstrap.test.ts +9 -0
- package/skills/agentbnb/bootstrap.ts +51 -26
- package/skills/agentbnb/install.sh +0 -0
- package/dist/conduct-KJUD2RTB.js +0 -22
- package/dist/websocket-client-5MH6QRJK.js +0 -7
package/dist/index.js
CHANGED
|
@@ -17,6 +17,8 @@ var CapabilityCardSchema = z.object({
|
|
|
17
17
|
spec_version: z.literal("1.0").default("1.0"),
|
|
18
18
|
id: z.string().uuid(),
|
|
19
19
|
owner: z.string().min(1),
|
|
20
|
+
/** V8: Cryptographic agent identity (Ed25519 public key hash). */
|
|
21
|
+
agent_id: z.string().optional(),
|
|
20
22
|
name: z.string().min(1).max(100),
|
|
21
23
|
description: z.string().max(500),
|
|
22
24
|
level: z.union([z.literal(1), z.literal(2), z.literal(3)]),
|
|
@@ -139,6 +141,8 @@ var CapabilityCardV2Schema = z.object({
|
|
|
139
141
|
spec_version: z.literal("2.0"),
|
|
140
142
|
id: z.string().uuid(),
|
|
141
143
|
owner: z.string().min(1),
|
|
144
|
+
/** V8: Cryptographic agent identity (Ed25519 public key hash). */
|
|
145
|
+
agent_id: z.string().optional(),
|
|
142
146
|
/** Agent display name — was 'name' in v1.0. */
|
|
143
147
|
agent_name: z.string().min(1).max(100),
|
|
144
148
|
/** Short one-liner shown in Hub v2 Identity Header. */
|
|
@@ -856,6 +860,26 @@ function recordSuccessfulHire(db, providerOwner, consumerOwner) {
|
|
|
856
860
|
).run(repeatIncrement, now, providerOwner);
|
|
857
861
|
}
|
|
858
862
|
|
|
863
|
+
// src/identity/agent-identity.ts
|
|
864
|
+
var AGENTS_SCHEMA = `
|
|
865
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
866
|
+
agent_id TEXT PRIMARY KEY,
|
|
867
|
+
display_name TEXT NOT NULL,
|
|
868
|
+
public_key TEXT NOT NULL,
|
|
869
|
+
operator_id TEXT,
|
|
870
|
+
server_id TEXT,
|
|
871
|
+
legacy_owner TEXT,
|
|
872
|
+
created_at TEXT NOT NULL,
|
|
873
|
+
updated_at TEXT NOT NULL
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
CREATE INDEX IF NOT EXISTS idx_agents_operator ON agents(operator_id);
|
|
877
|
+
CREATE INDEX IF NOT EXISTS idx_agents_legacy_owner ON agents(legacy_owner);
|
|
878
|
+
`;
|
|
879
|
+
function ensureAgentsTable(db) {
|
|
880
|
+
db.exec(AGENTS_SCHEMA);
|
|
881
|
+
}
|
|
882
|
+
|
|
859
883
|
// src/credit/ledger.ts
|
|
860
884
|
var CREDIT_SCHEMA = `
|
|
861
885
|
CREATE TABLE IF NOT EXISTS credit_balances (
|
|
@@ -912,6 +936,7 @@ function openCreditDb(path = ":memory:") {
|
|
|
912
936
|
} catch {
|
|
913
937
|
}
|
|
914
938
|
ensureReliabilityTable(db);
|
|
939
|
+
ensureAgentsTable(db);
|
|
915
940
|
return db;
|
|
916
941
|
}
|
|
917
942
|
function getBalance(db, owner) {
|
|
@@ -2309,7 +2334,8 @@ var OpenClawBridge = class {
|
|
|
2309
2334
|
};
|
|
2310
2335
|
|
|
2311
2336
|
// src/skills/command-executor.ts
|
|
2312
|
-
import {
|
|
2337
|
+
import { spawn } from "child_process";
|
|
2338
|
+
var KILL_GRACE_MS = 5e3;
|
|
2313
2339
|
function shellEscape2(value) {
|
|
2314
2340
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
2315
2341
|
}
|
|
@@ -2335,29 +2361,89 @@ function safeInterpolateCommand2(template, context) {
|
|
|
2335
2361
|
return shellEscape2(String(current));
|
|
2336
2362
|
});
|
|
2337
2363
|
}
|
|
2338
|
-
function
|
|
2364
|
+
function spawnWithKill(command, options, registry) {
|
|
2339
2365
|
return new Promise((resolve, reject) => {
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2366
|
+
const child = spawn("/bin/sh", ["-c", `${command} < /dev/null`], {
|
|
2367
|
+
cwd: options.cwd,
|
|
2368
|
+
env: options.env,
|
|
2369
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2370
|
+
detached: true
|
|
2371
|
+
});
|
|
2372
|
+
registry.add(child);
|
|
2373
|
+
let stdout = "";
|
|
2374
|
+
let stderr = "";
|
|
2375
|
+
let timedOut = false;
|
|
2376
|
+
let killed = false;
|
|
2377
|
+
let killTimer;
|
|
2378
|
+
child.stdout.on("data", (chunk) => {
|
|
2379
|
+
if (stdout.length < options.maxBuffer) {
|
|
2380
|
+
stdout += chunk.toString();
|
|
2381
|
+
}
|
|
2382
|
+
});
|
|
2383
|
+
child.stderr.on("data", (chunk) => {
|
|
2384
|
+
if (stderr.length < options.maxBuffer) {
|
|
2385
|
+
stderr += chunk.toString();
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
const killGroup = (signal) => {
|
|
2389
|
+
try {
|
|
2390
|
+
process.kill(-child.pid, signal);
|
|
2391
|
+
} catch {
|
|
2392
|
+
try {
|
|
2393
|
+
child.kill(signal);
|
|
2394
|
+
} catch {
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
};
|
|
2398
|
+
const timeoutId = setTimeout(() => {
|
|
2399
|
+
timedOut = true;
|
|
2400
|
+
killGroup("SIGTERM");
|
|
2401
|
+
killTimer = setTimeout(() => {
|
|
2402
|
+
if (!killed) {
|
|
2403
|
+
killGroup("SIGKILL");
|
|
2404
|
+
}
|
|
2405
|
+
}, KILL_GRACE_MS);
|
|
2406
|
+
killTimer.unref();
|
|
2407
|
+
}, options.timeout);
|
|
2408
|
+
child.on("close", (_code, _signal) => {
|
|
2409
|
+
killed = true;
|
|
2410
|
+
clearTimeout(timeoutId);
|
|
2411
|
+
if (killTimer) clearTimeout(killTimer);
|
|
2412
|
+
registry.delete(child);
|
|
2413
|
+
if (timedOut) {
|
|
2414
|
+
const err = new Error(`Command timed out after ${options.timeout}ms`);
|
|
2415
|
+
err.code = "ETIMEDOUT";
|
|
2416
|
+
reject(err);
|
|
2417
|
+
} else if (_code !== 0) {
|
|
2418
|
+
const err = new Error(stderr.trim() || `Process exited with code ${_code}`);
|
|
2419
|
+
Object.assign(err, { stderr: stderr.trim() });
|
|
2420
|
+
reject(err);
|
|
2346
2421
|
} else {
|
|
2347
|
-
resolve({ stdout
|
|
2422
|
+
resolve({ stdout, stderr });
|
|
2348
2423
|
}
|
|
2349
2424
|
});
|
|
2425
|
+
child.on("error", (err) => {
|
|
2426
|
+
killed = true;
|
|
2427
|
+
clearTimeout(timeoutId);
|
|
2428
|
+
registry.delete(child);
|
|
2429
|
+
reject(err);
|
|
2430
|
+
});
|
|
2350
2431
|
});
|
|
2351
2432
|
}
|
|
2352
2433
|
var CommandExecutor = class {
|
|
2434
|
+
/** Active child processes — killed on shutdown(). */
|
|
2435
|
+
activeProcesses = /* @__PURE__ */ new Set();
|
|
2436
|
+
/** In-flight execution count per skill ID for concurrency limiting. */
|
|
2437
|
+
inflight = /* @__PURE__ */ new Map();
|
|
2353
2438
|
/**
|
|
2354
2439
|
* Execute a command skill with the provided parameters.
|
|
2355
2440
|
*
|
|
2356
2441
|
* Steps:
|
|
2357
|
-
* 1.
|
|
2358
|
-
* 2.
|
|
2359
|
-
* 3.
|
|
2360
|
-
* 4.
|
|
2442
|
+
* 1. Concurrency check: reject if at capacity.max_concurrent limit.
|
|
2443
|
+
* 2. Security check: base command must be in `allowed_commands` if set.
|
|
2444
|
+
* 3. Interpolate `config.command` using `{ params }` context.
|
|
2445
|
+
* 4. Run via spawn with SIGTERM→SIGKILL timeout handling.
|
|
2446
|
+
* 5. Parse stdout based on `output_type`: text | json | file.
|
|
2361
2447
|
*
|
|
2362
2448
|
* @param config - Validated CommandSkillConfig.
|
|
2363
2449
|
* @param params - Input parameters passed by the caller.
|
|
@@ -2365,6 +2451,16 @@ var CommandExecutor = class {
|
|
|
2365
2451
|
*/
|
|
2366
2452
|
async execute(config, params) {
|
|
2367
2453
|
const cmdConfig = config;
|
|
2454
|
+
const maxConcurrent = cmdConfig.capacity?.max_concurrent;
|
|
2455
|
+
if (maxConcurrent !== void 0) {
|
|
2456
|
+
const current = this.inflight.get(cmdConfig.id) ?? 0;
|
|
2457
|
+
if (current >= maxConcurrent) {
|
|
2458
|
+
return {
|
|
2459
|
+
success: false,
|
|
2460
|
+
error: `Skill "${cmdConfig.id}" at max concurrency (${maxConcurrent}). Try again later.`
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2368
2464
|
const baseCommand = cmdConfig.command.trim().split(/\s+/)[0] ?? "";
|
|
2369
2465
|
if (cmdConfig.allowed_commands && cmdConfig.allowed_commands.length > 0) {
|
|
2370
2466
|
const effectiveAllowed = cmdConfig.claude_code ? [.../* @__PURE__ */ new Set([...cmdConfig.allowed_commands, "claude"])] : cmdConfig.allowed_commands;
|
|
@@ -2395,31 +2491,33 @@ var CommandExecutor = class {
|
|
|
2395
2491
|
}
|
|
2396
2492
|
const timeout = cmdConfig.timeout_ms ?? 3e4;
|
|
2397
2493
|
const cwd = cmdConfig.working_dir ?? process.cwd();
|
|
2398
|
-
let stdout;
|
|
2399
2494
|
const env = { ...process.env };
|
|
2400
2495
|
delete env["CLAUDECODE"];
|
|
2496
|
+
this.inflight.set(cmdConfig.id, (this.inflight.get(cmdConfig.id) ?? 0) + 1);
|
|
2497
|
+
let stdout;
|
|
2401
2498
|
try {
|
|
2402
|
-
const result = await
|
|
2499
|
+
const result = await spawnWithKill(interpolatedCommand, {
|
|
2403
2500
|
timeout,
|
|
2404
2501
|
cwd,
|
|
2405
2502
|
env,
|
|
2406
2503
|
maxBuffer: 10 * 1024 * 1024
|
|
2407
2504
|
// 10 MB
|
|
2408
|
-
});
|
|
2505
|
+
}, this.activeProcesses);
|
|
2409
2506
|
stdout = result.stdout;
|
|
2410
2507
|
} catch (err) {
|
|
2508
|
+
this.decrementInflight(cmdConfig.id);
|
|
2411
2509
|
if (err instanceof Error) {
|
|
2412
|
-
const
|
|
2413
|
-
|
|
2414
|
-
if (message.includes("timed out") || message.includes("ETIMEDOUT") || err.code === "ETIMEDOUT") {
|
|
2510
|
+
const code = err.code;
|
|
2511
|
+
if (code === "ETIMEDOUT" || err.message.includes("timed out")) {
|
|
2415
2512
|
return {
|
|
2416
2513
|
success: false,
|
|
2417
2514
|
error: `Command timed out after ${timeout}ms`
|
|
2418
2515
|
};
|
|
2419
2516
|
}
|
|
2517
|
+
const stderrContent = err.stderr ?? "";
|
|
2420
2518
|
return {
|
|
2421
2519
|
success: false,
|
|
2422
|
-
error: stderrContent.trim() || message
|
|
2520
|
+
error: stderrContent.trim() || err.message
|
|
2423
2521
|
};
|
|
2424
2522
|
}
|
|
2425
2523
|
return {
|
|
@@ -2427,6 +2525,7 @@ var CommandExecutor = class {
|
|
|
2427
2525
|
error: String(err)
|
|
2428
2526
|
};
|
|
2429
2527
|
}
|
|
2528
|
+
this.decrementInflight(cmdConfig.id);
|
|
2430
2529
|
const rawOutput = stdout.trim();
|
|
2431
2530
|
switch (cmdConfig.output_type) {
|
|
2432
2531
|
case "text":
|
|
@@ -2451,6 +2550,48 @@ var CommandExecutor = class {
|
|
|
2451
2550
|
};
|
|
2452
2551
|
}
|
|
2453
2552
|
}
|
|
2553
|
+
/**
|
|
2554
|
+
* Kill all active child processes. Called during service shutdown
|
|
2555
|
+
* to prevent zombie processes.
|
|
2556
|
+
*/
|
|
2557
|
+
shutdown() {
|
|
2558
|
+
for (const child of this.activeProcesses) {
|
|
2559
|
+
try {
|
|
2560
|
+
process.kill(-child.pid, "SIGTERM");
|
|
2561
|
+
} catch {
|
|
2562
|
+
try {
|
|
2563
|
+
child.kill("SIGTERM");
|
|
2564
|
+
} catch {
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
const pid = child.pid;
|
|
2568
|
+
const timer = setTimeout(() => {
|
|
2569
|
+
try {
|
|
2570
|
+
process.kill(-pid, "SIGKILL");
|
|
2571
|
+
} catch {
|
|
2572
|
+
}
|
|
2573
|
+
}, KILL_GRACE_MS);
|
|
2574
|
+
timer.unref();
|
|
2575
|
+
}
|
|
2576
|
+
this.activeProcesses.clear();
|
|
2577
|
+
}
|
|
2578
|
+
/** Returns the number of currently active child processes. */
|
|
2579
|
+
get activeCount() {
|
|
2580
|
+
return this.activeProcesses.size;
|
|
2581
|
+
}
|
|
2582
|
+
/** Returns the in-flight count for a specific skill ID. */
|
|
2583
|
+
getInflight(skillId) {
|
|
2584
|
+
return this.inflight.get(skillId) ?? 0;
|
|
2585
|
+
}
|
|
2586
|
+
/** Decrement the inflight counter for a skill ID. */
|
|
2587
|
+
decrementInflight(skillId) {
|
|
2588
|
+
const current = this.inflight.get(skillId) ?? 0;
|
|
2589
|
+
if (current <= 1) {
|
|
2590
|
+
this.inflight.delete(skillId);
|
|
2591
|
+
} else {
|
|
2592
|
+
this.inflight.set(skillId, current - 1);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2454
2595
|
};
|
|
2455
2596
|
|
|
2456
2597
|
// src/conductor/task-decomposer.ts
|
|
@@ -3561,6 +3702,7 @@ import { z as z3 } from "zod";
|
|
|
3561
3702
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
3562
3703
|
var EscrowReceiptSchema = z3.object({
|
|
3563
3704
|
requester_owner: z3.string().min(1),
|
|
3705
|
+
requester_agent_id: z3.string().optional(),
|
|
3564
3706
|
requester_public_key: z3.string().min(1),
|
|
3565
3707
|
amount: z3.number().positive(),
|
|
3566
3708
|
card_id: z3.string().min(1),
|
|
@@ -3573,6 +3715,7 @@ function createSignedEscrowReceipt(db, privateKey, publicKey, opts) {
|
|
|
3573
3715
|
const escrowId = holdEscrow(db, opts.owner, opts.amount, opts.cardId);
|
|
3574
3716
|
const receiptData = {
|
|
3575
3717
|
requester_owner: opts.owner,
|
|
3718
|
+
...opts.agent_id ? { requester_agent_id: opts.agent_id } : {},
|
|
3576
3719
|
requester_public_key: publicKey.toString("hex"),
|
|
3577
3720
|
amount: opts.amount,
|
|
3578
3721
|
card_id: opts.cardId,
|
|
@@ -4071,11 +4214,22 @@ import { z as z6 } from "zod";
|
|
|
4071
4214
|
var RegisterMessageSchema = z6.object({
|
|
4072
4215
|
type: z6.literal("register"),
|
|
4073
4216
|
owner: z6.string().min(1),
|
|
4217
|
+
/** V8: Cryptographic agent identity. When present, used as the canonical key. */
|
|
4218
|
+
agent_id: z6.string().optional(),
|
|
4219
|
+
/** V8 Phase 3: Server identifier for multi-agent delegation. */
|
|
4220
|
+
server_id: z6.string().optional(),
|
|
4074
4221
|
token: z6.string().min(1),
|
|
4075
4222
|
card: z6.record(z6.unknown()),
|
|
4076
4223
|
// CapabilityCard (validated separately)
|
|
4077
|
-
cards: z6.array(z6.record(z6.unknown())).optional()
|
|
4224
|
+
cards: z6.array(z6.record(z6.unknown())).optional(),
|
|
4078
4225
|
// Additional cards (e.g., conductor card)
|
|
4226
|
+
/** V8 Phase 3: Additional agents served by this server (multi-agent registration). */
|
|
4227
|
+
agents: z6.array(z6.object({
|
|
4228
|
+
agent_id: z6.string().min(1),
|
|
4229
|
+
display_name: z6.string().min(1),
|
|
4230
|
+
cards: z6.array(z6.record(z6.unknown())),
|
|
4231
|
+
delegation_token: z6.record(z6.unknown()).optional()
|
|
4232
|
+
})).optional()
|
|
4079
4233
|
});
|
|
4080
4234
|
var RegisteredMessageSchema = z6.object({
|
|
4081
4235
|
type: z6.literal("registered"),
|
|
@@ -4085,6 +4239,8 @@ var RelayRequestMessageSchema = z6.object({
|
|
|
4085
4239
|
type: z6.literal("relay_request"),
|
|
4086
4240
|
id: z6.string().uuid(),
|
|
4087
4241
|
target_owner: z6.string().min(1),
|
|
4242
|
+
/** V8: Target agent's cryptographic identity. Preferred over target_owner. */
|
|
4243
|
+
target_agent_id: z6.string().optional(),
|
|
4088
4244
|
card_id: z6.string(),
|
|
4089
4245
|
skill_id: z6.string().optional(),
|
|
4090
4246
|
params: z6.record(z6.unknown()).default({}),
|
|
@@ -4155,6 +4311,52 @@ var HeartbeatMessageSchema = z6.object({
|
|
|
4155
4311
|
})
|
|
4156
4312
|
})
|
|
4157
4313
|
});
|
|
4314
|
+
var EscrowHoldMessageSchema = z6.object({
|
|
4315
|
+
type: z6.literal("escrow_hold"),
|
|
4316
|
+
consumer_agent_id: z6.string().min(1),
|
|
4317
|
+
provider_agent_id: z6.string().min(1),
|
|
4318
|
+
skill_id: z6.string().min(1),
|
|
4319
|
+
amount: z6.number().positive(),
|
|
4320
|
+
request_id: z6.string().uuid(),
|
|
4321
|
+
signature: z6.string().optional(),
|
|
4322
|
+
public_key: z6.string().optional()
|
|
4323
|
+
});
|
|
4324
|
+
var EscrowHoldConfirmedMessageSchema = z6.object({
|
|
4325
|
+
type: z6.literal("escrow_hold_confirmed"),
|
|
4326
|
+
request_id: z6.string(),
|
|
4327
|
+
escrow_id: z6.string(),
|
|
4328
|
+
hold_amount: z6.number(),
|
|
4329
|
+
consumer_remaining: z6.number()
|
|
4330
|
+
});
|
|
4331
|
+
var EscrowSettleMessageSchema = z6.object({
|
|
4332
|
+
type: z6.literal("escrow_settle"),
|
|
4333
|
+
escrow_id: z6.string().min(1),
|
|
4334
|
+
request_id: z6.string().uuid(),
|
|
4335
|
+
success: z6.boolean(),
|
|
4336
|
+
failure_reason: z6.enum(["bad_execution", "overload", "timeout", "auth_error", "not_found"]).optional(),
|
|
4337
|
+
result_hash: z6.string().optional(),
|
|
4338
|
+
signature: z6.string().optional(),
|
|
4339
|
+
public_key: z6.string().optional(),
|
|
4340
|
+
consumer_agent_id: z6.string().optional()
|
|
4341
|
+
});
|
|
4342
|
+
var EscrowSettledMessageSchema = z6.object({
|
|
4343
|
+
type: z6.literal("escrow_settled"),
|
|
4344
|
+
escrow_id: z6.string(),
|
|
4345
|
+
request_id: z6.string(),
|
|
4346
|
+
provider_earned: z6.number(),
|
|
4347
|
+
network_fee: z6.number(),
|
|
4348
|
+
consumer_remaining: z6.number(),
|
|
4349
|
+
provider_balance: z6.number()
|
|
4350
|
+
});
|
|
4351
|
+
var BalanceSyncMessageSchema = z6.object({
|
|
4352
|
+
type: z6.literal("balance_sync"),
|
|
4353
|
+
agent_id: z6.string().min(1)
|
|
4354
|
+
});
|
|
4355
|
+
var BalanceSyncResponseMessageSchema = z6.object({
|
|
4356
|
+
type: z6.literal("balance_sync_response"),
|
|
4357
|
+
agent_id: z6.string(),
|
|
4358
|
+
balance: z6.number()
|
|
4359
|
+
});
|
|
4158
4360
|
var RelayMessageSchema = z6.discriminatedUnion("type", [
|
|
4159
4361
|
RegisterMessageSchema,
|
|
4160
4362
|
RegisteredMessageSchema,
|
|
@@ -4164,7 +4366,13 @@ var RelayMessageSchema = z6.discriminatedUnion("type", [
|
|
|
4164
4366
|
ResponseMessageSchema,
|
|
4165
4367
|
ErrorMessageSchema,
|
|
4166
4368
|
RelayProgressMessageSchema,
|
|
4167
|
-
HeartbeatMessageSchema
|
|
4369
|
+
HeartbeatMessageSchema,
|
|
4370
|
+
EscrowHoldMessageSchema,
|
|
4371
|
+
EscrowHoldConfirmedMessageSchema,
|
|
4372
|
+
EscrowSettleMessageSchema,
|
|
4373
|
+
EscrowSettledMessageSchema,
|
|
4374
|
+
BalanceSyncMessageSchema,
|
|
4375
|
+
BalanceSyncResponseMessageSchema
|
|
4168
4376
|
]);
|
|
4169
4377
|
|
|
4170
4378
|
// src/relay/websocket-relay.ts
|
|
@@ -4225,6 +4433,78 @@ function releaseForRelay(creditDb, escrowId) {
|
|
|
4225
4433
|
releaseEscrow(creditDb, escrowId);
|
|
4226
4434
|
}
|
|
4227
4435
|
|
|
4436
|
+
// src/relay/relay-escrow.ts
|
|
4437
|
+
function verifyRelaySignature(data, signature, publicKeyHex) {
|
|
4438
|
+
try {
|
|
4439
|
+
const publicKeyBuf = Buffer.from(publicKeyHex, "hex");
|
|
4440
|
+
return verifyEscrowReceipt(data, signature, publicKeyBuf);
|
|
4441
|
+
} catch {
|
|
4442
|
+
return false;
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
function processEscrowHold(creditDb, consumerAgentId, providerAgentId, skillId, amount, requestId, signature, publicKeyHex) {
|
|
4446
|
+
if (signature && publicKeyHex) {
|
|
4447
|
+
const signData = {
|
|
4448
|
+
consumer_agent_id: consumerAgentId,
|
|
4449
|
+
provider_agent_id: providerAgentId,
|
|
4450
|
+
skill_id: skillId,
|
|
4451
|
+
amount,
|
|
4452
|
+
request_id: requestId
|
|
4453
|
+
};
|
|
4454
|
+
if (!verifyRelaySignature(signData, signature, publicKeyHex)) {
|
|
4455
|
+
throw new Error("Invalid consumer signature on escrow hold");
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
const escrowId = holdEscrow(creditDb, consumerAgentId, amount, `${providerAgentId}:${skillId}`);
|
|
4459
|
+
const remaining = getBalance(creditDb, consumerAgentId);
|
|
4460
|
+
return {
|
|
4461
|
+
escrow_id: escrowId,
|
|
4462
|
+
hold_amount: amount,
|
|
4463
|
+
consumer_remaining: remaining
|
|
4464
|
+
};
|
|
4465
|
+
}
|
|
4466
|
+
function processEscrowSettle(creditDb, escrowId, success, providerAgentId, signature, publicKeyHex, consumerAgentId) {
|
|
4467
|
+
if (signature && publicKeyHex && consumerAgentId) {
|
|
4468
|
+
const signData = {
|
|
4469
|
+
escrow_id: escrowId,
|
|
4470
|
+
success,
|
|
4471
|
+
consumer_agent_id: consumerAgentId
|
|
4472
|
+
};
|
|
4473
|
+
if (!verifyRelaySignature(signData, signature, publicKeyHex)) {
|
|
4474
|
+
throw new Error("Invalid consumer signature on escrow settle");
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4477
|
+
const escrowRow = creditDb.prepare("SELECT amount, owner FROM credit_escrow WHERE id = ? AND status = ?").get(escrowId, "held");
|
|
4478
|
+
if (!escrowRow) {
|
|
4479
|
+
throw new Error(`Escrow not found or already settled: ${escrowId}`);
|
|
4480
|
+
}
|
|
4481
|
+
const NETWORK_FEE_RATE2 = 0.05;
|
|
4482
|
+
if (success) {
|
|
4483
|
+
settleEscrow(creditDb, escrowId, providerAgentId);
|
|
4484
|
+
const networkFee = Math.floor(escrowRow.amount * NETWORK_FEE_RATE2);
|
|
4485
|
+
const providerAmount = escrowRow.amount - networkFee;
|
|
4486
|
+
return {
|
|
4487
|
+
escrow_id: escrowId,
|
|
4488
|
+
provider_earned: providerAmount,
|
|
4489
|
+
network_fee: networkFee,
|
|
4490
|
+
consumer_remaining: getBalance(creditDb, escrowRow.owner),
|
|
4491
|
+
provider_balance: getBalance(creditDb, providerAgentId)
|
|
4492
|
+
};
|
|
4493
|
+
} else {
|
|
4494
|
+
releaseEscrow(creditDb, escrowId);
|
|
4495
|
+
return {
|
|
4496
|
+
escrow_id: escrowId,
|
|
4497
|
+
provider_earned: 0,
|
|
4498
|
+
network_fee: 0,
|
|
4499
|
+
consumer_remaining: getBalance(creditDb, escrowRow.owner),
|
|
4500
|
+
provider_balance: getBalance(creditDb, providerAgentId)
|
|
4501
|
+
};
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
function settleWithNetworkFee(creditDb, escrowId, providerOwner) {
|
|
4505
|
+
return processEscrowSettle(creditDb, escrowId, true, providerOwner);
|
|
4506
|
+
}
|
|
4507
|
+
|
|
4228
4508
|
// src/hub-agent/relay-bridge.ts
|
|
4229
4509
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
4230
4510
|
|
|
@@ -4272,10 +4552,17 @@ var RATE_LIMIT_WINDOW_MS = 6e4;
|
|
|
4272
4552
|
var RELAY_TIMEOUT_MS = 3e5;
|
|
4273
4553
|
function registerWebSocketRelay(server, db, creditDb) {
|
|
4274
4554
|
const connections = /* @__PURE__ */ new Map();
|
|
4555
|
+
const agentIdToOwner = /* @__PURE__ */ new Map();
|
|
4275
4556
|
const pendingRequests = /* @__PURE__ */ new Map();
|
|
4276
4557
|
const rateLimits = /* @__PURE__ */ new Map();
|
|
4277
4558
|
const agentCapacities = /* @__PURE__ */ new Map();
|
|
4278
4559
|
let onAgentOnlineCallback;
|
|
4560
|
+
function resolveConnectionKey(target) {
|
|
4561
|
+
const ownerFromAgentId = agentIdToOwner.get(target);
|
|
4562
|
+
if (ownerFromAgentId && connections.has(ownerFromAgentId)) return ownerFromAgentId;
|
|
4563
|
+
if (connections.has(target)) return target;
|
|
4564
|
+
return void 0;
|
|
4565
|
+
}
|
|
4279
4566
|
function checkRateLimit(owner) {
|
|
4280
4567
|
const now = Date.now();
|
|
4281
4568
|
const entry = rateLimits.get(owner);
|
|
@@ -4379,6 +4666,20 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
4379
4666
|
}
|
|
4380
4667
|
}
|
|
4381
4668
|
connections.set(owner, ws);
|
|
4669
|
+
if (msg.agent_id) {
|
|
4670
|
+
agentIdToOwner.set(msg.agent_id, owner);
|
|
4671
|
+
}
|
|
4672
|
+
if (msg.agents && msg.agents.length > 0) {
|
|
4673
|
+
for (const agentEntry of msg.agents) {
|
|
4674
|
+
agentIdToOwner.set(agentEntry.agent_id, owner);
|
|
4675
|
+
for (const agentCard of agentEntry.cards) {
|
|
4676
|
+
try {
|
|
4677
|
+
upsertCard(agentCard, owner);
|
|
4678
|
+
} catch {
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4382
4683
|
const isEphemeral = owner.includes(":req:");
|
|
4383
4684
|
if (isEphemeral) {
|
|
4384
4685
|
const cardId2 = card.id ?? owner;
|
|
@@ -4422,12 +4723,13 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
4422
4723
|
});
|
|
4423
4724
|
return;
|
|
4424
4725
|
}
|
|
4425
|
-
const
|
|
4726
|
+
const targetKey = resolveConnectionKey(msg.target_agent_id ?? msg.target_owner);
|
|
4727
|
+
const targetWs = targetKey ? connections.get(targetKey) : void 0;
|
|
4426
4728
|
if (!targetWs || targetWs.readyState !== 1) {
|
|
4427
4729
|
sendMessage(ws, {
|
|
4428
4730
|
type: "response",
|
|
4429
4731
|
id: msg.id,
|
|
4430
|
-
error: { code: -32603, message: `Agent offline: ${msg.target_owner}` }
|
|
4732
|
+
error: { code: -32603, message: `Agent offline: ${msg.target_agent_id ?? msg.target_owner}` }
|
|
4431
4733
|
});
|
|
4432
4734
|
return;
|
|
4433
4735
|
}
|
|
@@ -4541,7 +4843,7 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
4541
4843
|
if (pending.escrowId && creditDb) {
|
|
4542
4844
|
try {
|
|
4543
4845
|
if (msg.error === void 0) {
|
|
4544
|
-
|
|
4846
|
+
settleWithNetworkFee(creditDb, pending.escrowId, pending.targetOwner);
|
|
4545
4847
|
} else {
|
|
4546
4848
|
releaseForRelay(creditDb, pending.escrowId);
|
|
4547
4849
|
}
|
|
@@ -4579,6 +4881,12 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
4579
4881
|
connections.delete(owner);
|
|
4580
4882
|
rateLimits.delete(owner);
|
|
4581
4883
|
agentCapacities.delete(owner);
|
|
4884
|
+
for (const [agentId, o] of agentIdToOwner) {
|
|
4885
|
+
if (o === owner) {
|
|
4886
|
+
agentIdToOwner.delete(agentId);
|
|
4887
|
+
break;
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4582
4890
|
markOwnerOffline(owner);
|
|
4583
4891
|
for (const [reqId, pending] of pendingRequests) {
|
|
4584
4892
|
if (pending.targetOwner === owner) {
|
|
@@ -4615,6 +4923,96 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
4615
4923
|
function handleHeartbeat(msg) {
|
|
4616
4924
|
agentCapacities.set(msg.owner, msg.capacity);
|
|
4617
4925
|
}
|
|
4926
|
+
function handleEscrowHold(ws, msg) {
|
|
4927
|
+
if (!creditDb) {
|
|
4928
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
4929
|
+
return;
|
|
4930
|
+
}
|
|
4931
|
+
try {
|
|
4932
|
+
const result = processEscrowHold(
|
|
4933
|
+
creditDb,
|
|
4934
|
+
msg.consumer_agent_id,
|
|
4935
|
+
msg.provider_agent_id,
|
|
4936
|
+
msg.skill_id,
|
|
4937
|
+
msg.amount,
|
|
4938
|
+
msg.request_id,
|
|
4939
|
+
msg.signature,
|
|
4940
|
+
msg.public_key
|
|
4941
|
+
);
|
|
4942
|
+
sendMessage(ws, {
|
|
4943
|
+
type: "escrow_hold_confirmed",
|
|
4944
|
+
request_id: msg.request_id,
|
|
4945
|
+
escrow_id: result.escrow_id,
|
|
4946
|
+
hold_amount: result.hold_amount,
|
|
4947
|
+
consumer_remaining: result.consumer_remaining
|
|
4948
|
+
});
|
|
4949
|
+
} catch (err) {
|
|
4950
|
+
const errMsg = err instanceof Error ? err.message : "Escrow hold failed";
|
|
4951
|
+
const code = errMsg.includes("INSUFFICIENT_CREDITS") ? "insufficient_credits" : "escrow_hold_failed";
|
|
4952
|
+
sendMessage(ws, { type: "error", code, message: errMsg, request_id: msg.request_id });
|
|
4953
|
+
}
|
|
4954
|
+
}
|
|
4955
|
+
function handleEscrowSettle(ws, msg) {
|
|
4956
|
+
if (!creditDb) {
|
|
4957
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
4958
|
+
return;
|
|
4959
|
+
}
|
|
4960
|
+
try {
|
|
4961
|
+
const escrow = creditDb.prepare("SELECT card_id FROM credit_escrow WHERE id = ? AND status = ?").get(msg.escrow_id, "held");
|
|
4962
|
+
if (!escrow) {
|
|
4963
|
+
sendMessage(ws, { type: "error", code: "escrow_not_found", message: `Escrow not found: ${msg.escrow_id}`, request_id: msg.request_id });
|
|
4964
|
+
return;
|
|
4965
|
+
}
|
|
4966
|
+
const providerAgentId = escrow.card_id.split(":")[0];
|
|
4967
|
+
const result = processEscrowSettle(
|
|
4968
|
+
creditDb,
|
|
4969
|
+
msg.escrow_id,
|
|
4970
|
+
msg.success,
|
|
4971
|
+
providerAgentId,
|
|
4972
|
+
msg.signature,
|
|
4973
|
+
msg.public_key,
|
|
4974
|
+
msg.consumer_agent_id
|
|
4975
|
+
);
|
|
4976
|
+
sendMessage(ws, {
|
|
4977
|
+
type: "escrow_settled",
|
|
4978
|
+
escrow_id: result.escrow_id,
|
|
4979
|
+
request_id: msg.request_id,
|
|
4980
|
+
provider_earned: result.provider_earned,
|
|
4981
|
+
network_fee: result.network_fee,
|
|
4982
|
+
consumer_remaining: result.consumer_remaining,
|
|
4983
|
+
provider_balance: result.provider_balance
|
|
4984
|
+
});
|
|
4985
|
+
const providerKey = resolveConnectionKey(providerAgentId);
|
|
4986
|
+
if (providerKey) {
|
|
4987
|
+
const providerWs = connections.get(providerKey);
|
|
4988
|
+
if (providerWs && providerWs.readyState === 1) {
|
|
4989
|
+
sendMessage(providerWs, {
|
|
4990
|
+
type: "escrow_settled",
|
|
4991
|
+
escrow_id: result.escrow_id,
|
|
4992
|
+
request_id: msg.request_id,
|
|
4993
|
+
provider_earned: result.provider_earned,
|
|
4994
|
+
network_fee: result.network_fee,
|
|
4995
|
+
consumer_remaining: result.consumer_remaining,
|
|
4996
|
+
provider_balance: result.provider_balance
|
|
4997
|
+
});
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
} catch (err) {
|
|
5001
|
+
sendMessage(ws, { type: "error", code: "escrow_settle_failed", message: err instanceof Error ? err.message : "Settlement failed", request_id: msg.request_id });
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
function handleBalanceSync(ws, msg) {
|
|
5005
|
+
if (!creditDb) {
|
|
5006
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
5007
|
+
return;
|
|
5008
|
+
}
|
|
5009
|
+
const balance = getBalance(creditDb, msg.agent_id);
|
|
5010
|
+
sendMessage(ws, {
|
|
5011
|
+
type: "balance_sync_response",
|
|
5012
|
+
agent_id: msg.agent_id,
|
|
5013
|
+
balance
|
|
5014
|
+
});
|
|
5015
|
+
}
|
|
4618
5016
|
void server.register(async (app) => {
|
|
4619
5017
|
app.get("/ws", { websocket: true }, (rawSocket, _request) => {
|
|
4620
5018
|
const socket = rawSocket;
|
|
@@ -4663,6 +5061,16 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
4663
5061
|
case "heartbeat":
|
|
4664
5062
|
handleHeartbeat(msg);
|
|
4665
5063
|
break;
|
|
5064
|
+
// V8 Phase 2: Explicit escrow messages
|
|
5065
|
+
case "escrow_hold":
|
|
5066
|
+
handleEscrowHold(socket, msg);
|
|
5067
|
+
break;
|
|
5068
|
+
case "escrow_settle":
|
|
5069
|
+
handleEscrowSettle(socket, msg);
|
|
5070
|
+
break;
|
|
5071
|
+
case "balance_sync":
|
|
5072
|
+
handleBalanceSync(socket, msg);
|
|
5073
|
+
break;
|
|
4666
5074
|
default:
|
|
4667
5075
|
break;
|
|
4668
5076
|
}
|
|
@@ -4745,9 +5153,12 @@ var RelayClient = class {
|
|
|
4745
5153
|
this.send({
|
|
4746
5154
|
type: "register",
|
|
4747
5155
|
owner: this.opts.owner,
|
|
5156
|
+
...this.opts.agent_id ? { agent_id: this.opts.agent_id } : {},
|
|
5157
|
+
...this.opts.server_id ? { server_id: this.opts.server_id } : {},
|
|
4748
5158
|
token: this.opts.token,
|
|
4749
5159
|
card: this.opts.card,
|
|
4750
|
-
...this.opts.cards && this.opts.cards.length > 0 ? { cards: this.opts.cards } : {}
|
|
5160
|
+
...this.opts.cards && this.opts.cards.length > 0 ? { cards: this.opts.cards } : {},
|
|
5161
|
+
...this.opts.agents && this.opts.agents.length > 0 ? { agents: this.opts.agents } : {}
|
|
4751
5162
|
});
|
|
4752
5163
|
});
|
|
4753
5164
|
this.ws.on("message", (raw) => {
|
|
@@ -4823,6 +5234,7 @@ var RelayClient = class {
|
|
|
4823
5234
|
type: "relay_request",
|
|
4824
5235
|
id,
|
|
4825
5236
|
target_owner: opts.targetOwner,
|
|
5237
|
+
...opts.targetAgentId ? { target_agent_id: opts.targetAgentId } : {},
|
|
4826
5238
|
card_id: opts.cardId,
|
|
4827
5239
|
skill_id: opts.skillId,
|
|
4828
5240
|
params: opts.params,
|