agentbnb 8.0.1 → 8.2.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/{conductor-mode-2GSLHVN6.js → chunk-P4LOYSLA.js} +624 -260
- package/dist/cli/index.js +15 -5
- package/dist/conductor-mode-TFCVCQHU.js +266 -0
- package/dist/{server-MHMAYXWZ.js → server-LMY2A3GT.js} +2 -4
- package/dist/skills/agentbnb/bootstrap.js +717 -13
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/agentbnb/bootstrap.test.ts +34 -0
- package/skills/agentbnb/bootstrap.ts +28 -0
- package/skills/agentbnb/openclaw-tools.test.ts +328 -0
- package/skills/agentbnb/openclaw-tools.ts +297 -0
- package/dist/chunk-B2VJTKO5.js +0 -393
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw tool factory — bridges AgentBnB MCP tool handlers into
|
|
3
|
+
* OpenClaw's native plugin tool system.
|
|
4
|
+
*
|
|
5
|
+
* Each tool delegates to the existing `handle*()` functions from src/mcp/tools/.
|
|
6
|
+
* Context is lazily constructed and per-agent cached to avoid process.env mutation.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { ensureIdentity } from '../../src/identity/identity.js';
|
|
13
|
+
import type { AgentBnBConfig } from '../../src/cli/config.js';
|
|
14
|
+
import type { McpServerContext } from '../../src/mcp/server.js';
|
|
15
|
+
import { handleDiscover } from '../../src/mcp/tools/discover.js';
|
|
16
|
+
import { handleRequest } from '../../src/mcp/tools/request.js';
|
|
17
|
+
import { handleConduct } from '../../src/mcp/tools/conduct.js';
|
|
18
|
+
import { handleStatus } from '../../src/mcp/tools/status.js';
|
|
19
|
+
import { handlePublish } from '../../src/mcp/tools/publish.js';
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Types
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
/** Subset of OpenClaw's plugin tool context passed to tool factories. */
|
|
26
|
+
export interface OpenClawToolContext {
|
|
27
|
+
workspaceDir?: string;
|
|
28
|
+
agentDir?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** OpenClaw-compatible tool result. */
|
|
32
|
+
export interface AgentToolResult {
|
|
33
|
+
content: string;
|
|
34
|
+
details?: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** OpenClaw-compatible tool shape. */
|
|
38
|
+
export interface AgentTool {
|
|
39
|
+
name: string;
|
|
40
|
+
label: string;
|
|
41
|
+
description: string;
|
|
42
|
+
parameters: Record<string, unknown>;
|
|
43
|
+
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<AgentToolResult>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Context cache — per-agent isolation keyed by configDir
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
const contextCache = new Map<string, McpServerContext>();
|
|
51
|
+
|
|
52
|
+
/** Clears the per-agent context cache. Useful for tests and daemon restarts. */
|
|
53
|
+
export function resetContextCache(): void {
|
|
54
|
+
contextCache.clear();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolves the configDir from OpenClaw tool context.
|
|
59
|
+
*
|
|
60
|
+
* Priority:
|
|
61
|
+
* 1. agentDir (if ends with .agentbnb, use directly; else append .agentbnb)
|
|
62
|
+
* 2. workspaceDir + /.agentbnb
|
|
63
|
+
* 3. Fallback: ~/.agentbnb
|
|
64
|
+
*/
|
|
65
|
+
export function resolveConfigDir(toolCtx: OpenClawToolContext): string {
|
|
66
|
+
if (toolCtx.agentDir) {
|
|
67
|
+
return toolCtx.agentDir.endsWith('.agentbnb')
|
|
68
|
+
? toolCtx.agentDir
|
|
69
|
+
: join(toolCtx.agentDir, '.agentbnb');
|
|
70
|
+
}
|
|
71
|
+
if (toolCtx.workspaceDir) {
|
|
72
|
+
return join(toolCtx.workspaceDir, '.agentbnb');
|
|
73
|
+
}
|
|
74
|
+
return join(homedir(), '.agentbnb');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Constructs an McpServerContext from OpenClaw tool context.
|
|
79
|
+
* Per-agent cached — same configDir reuses the same context.
|
|
80
|
+
*
|
|
81
|
+
* Reads config directly from configDir/config.json instead of mutating
|
|
82
|
+
* process.env.AGENTBNB_DIR, which avoids race conditions in the shared
|
|
83
|
+
* OpenClaw daemon process.
|
|
84
|
+
*/
|
|
85
|
+
export function buildMcpContext(toolCtx: OpenClawToolContext): McpServerContext {
|
|
86
|
+
const configDir = resolveConfigDir(toolCtx);
|
|
87
|
+
|
|
88
|
+
const cached = contextCache.get(configDir);
|
|
89
|
+
if (cached) return cached;
|
|
90
|
+
|
|
91
|
+
const configPath = join(configDir, 'config.json');
|
|
92
|
+
if (!existsSync(configPath)) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`AgentBnB not initialized at ${configDir}. Run \`agentbnb init\` or activate the plugin first.`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8')) as AgentBnBConfig;
|
|
99
|
+
const identity = ensureIdentity(configDir, config.owner);
|
|
100
|
+
|
|
101
|
+
const ctx: McpServerContext = { configDir, config, identity };
|
|
102
|
+
contextCache.set(configDir, ctx);
|
|
103
|
+
return ctx;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Result conversion
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Converts MCP result format to OpenClaw AgentToolResult.
|
|
112
|
+
* MCP returns `{ content: [{type:'text', text: '<json>'}] }`.
|
|
113
|
+
* OpenClaw expects `{ content: string, details?: unknown }`.
|
|
114
|
+
*/
|
|
115
|
+
export function toAgentToolResult(
|
|
116
|
+
mcpResult: { content: Array<{ type: string; text: string }> },
|
|
117
|
+
): AgentToolResult {
|
|
118
|
+
const text = mcpResult.content[0]?.text ?? '{}';
|
|
119
|
+
let details: unknown;
|
|
120
|
+
try {
|
|
121
|
+
details = JSON.parse(text);
|
|
122
|
+
} catch {
|
|
123
|
+
details = undefined;
|
|
124
|
+
}
|
|
125
|
+
return { content: text, details };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Tool creators
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
/** Creates the agentbnb-discover tool. */
|
|
133
|
+
export function createDiscoverTool(toolCtx: OpenClawToolContext): AgentTool {
|
|
134
|
+
return {
|
|
135
|
+
name: 'agentbnb-discover',
|
|
136
|
+
label: 'AgentBnB Discover',
|
|
137
|
+
description:
|
|
138
|
+
'Search for agent capabilities on the AgentBnB network. Returns matching capability cards from both local and remote registries.',
|
|
139
|
+
parameters: {
|
|
140
|
+
type: 'object',
|
|
141
|
+
properties: {
|
|
142
|
+
query: { type: 'string', description: 'Natural language search query' },
|
|
143
|
+
level: {
|
|
144
|
+
type: 'number',
|
|
145
|
+
description: 'Filter by capability level (1=Atomic, 2=Pipeline, 3=Environment)',
|
|
146
|
+
},
|
|
147
|
+
online_only: { type: 'boolean', description: 'Only show online agents' },
|
|
148
|
+
},
|
|
149
|
+
required: ['query'],
|
|
150
|
+
},
|
|
151
|
+
async execute(_toolCallId, params) {
|
|
152
|
+
const ctx = buildMcpContext(toolCtx);
|
|
153
|
+
const result = await handleDiscover(
|
|
154
|
+
params as { query: string; level?: number; online_only?: boolean },
|
|
155
|
+
ctx,
|
|
156
|
+
);
|
|
157
|
+
return toAgentToolResult(result);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Creates the agentbnb-request tool. */
|
|
163
|
+
export function createRequestTool(toolCtx: OpenClawToolContext): AgentTool {
|
|
164
|
+
return {
|
|
165
|
+
name: 'agentbnb-request',
|
|
166
|
+
label: 'AgentBnB Request',
|
|
167
|
+
description:
|
|
168
|
+
'Request execution of a skill from another agent on the AgentBnB network. Handles credit escrow automatically.',
|
|
169
|
+
parameters: {
|
|
170
|
+
type: 'object',
|
|
171
|
+
properties: {
|
|
172
|
+
query: {
|
|
173
|
+
type: 'string',
|
|
174
|
+
description: 'Search query to find a matching capability (auto-request mode)',
|
|
175
|
+
},
|
|
176
|
+
card_id: { type: 'string', description: 'Direct card ID to request (skips search)' },
|
|
177
|
+
skill_id: { type: 'string', description: 'Specific skill within a v2.0 card' },
|
|
178
|
+
params: { type: 'object', description: 'Input parameters for the capability' },
|
|
179
|
+
max_cost: {
|
|
180
|
+
type: 'number',
|
|
181
|
+
description: 'Maximum credits to spend (default: 50)',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
required: [],
|
|
185
|
+
},
|
|
186
|
+
async execute(_toolCallId, params) {
|
|
187
|
+
const ctx = buildMcpContext(toolCtx);
|
|
188
|
+
const result = await handleRequest(
|
|
189
|
+
params as {
|
|
190
|
+
query?: string;
|
|
191
|
+
card_id?: string;
|
|
192
|
+
skill_id?: string;
|
|
193
|
+
params?: Record<string, unknown>;
|
|
194
|
+
max_cost?: number;
|
|
195
|
+
},
|
|
196
|
+
ctx,
|
|
197
|
+
);
|
|
198
|
+
return toAgentToolResult(result);
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Creates the agentbnb-conduct tool. */
|
|
204
|
+
export function createConductTool(toolCtx: OpenClawToolContext): AgentTool {
|
|
205
|
+
return {
|
|
206
|
+
name: 'agentbnb-conduct',
|
|
207
|
+
label: 'AgentBnB Conduct',
|
|
208
|
+
description:
|
|
209
|
+
'Orchestrate a complex task across multiple agents on the AgentBnB network. Decomposes the task, matches sub-tasks to agents, and executes the pipeline.',
|
|
210
|
+
parameters: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: {
|
|
213
|
+
task: { type: 'string', description: 'Natural language task description' },
|
|
214
|
+
plan_only: {
|
|
215
|
+
type: 'boolean',
|
|
216
|
+
description: 'If true, return execution plan without executing',
|
|
217
|
+
},
|
|
218
|
+
max_budget: {
|
|
219
|
+
type: 'number',
|
|
220
|
+
description: 'Maximum credits to spend (default: 100)',
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
required: ['task'],
|
|
224
|
+
},
|
|
225
|
+
async execute(_toolCallId, params) {
|
|
226
|
+
const ctx = buildMcpContext(toolCtx);
|
|
227
|
+
const result = await handleConduct(
|
|
228
|
+
params as { task: string; plan_only?: boolean; max_budget?: number },
|
|
229
|
+
ctx,
|
|
230
|
+
);
|
|
231
|
+
return toAgentToolResult(result);
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Creates the agentbnb-status tool. */
|
|
237
|
+
export function createStatusTool(toolCtx: OpenClawToolContext): AgentTool {
|
|
238
|
+
return {
|
|
239
|
+
name: 'agentbnb-status',
|
|
240
|
+
label: 'AgentBnB Status',
|
|
241
|
+
description:
|
|
242
|
+
'Check your AgentBnB agent status: identity, credit balance, and configuration.',
|
|
243
|
+
parameters: {
|
|
244
|
+
type: 'object',
|
|
245
|
+
properties: {},
|
|
246
|
+
required: [],
|
|
247
|
+
},
|
|
248
|
+
async execute(_toolCallId, _params) {
|
|
249
|
+
const ctx = buildMcpContext(toolCtx);
|
|
250
|
+
const result = await handleStatus(ctx);
|
|
251
|
+
return toAgentToolResult(result);
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Creates the agentbnb-publish tool. */
|
|
257
|
+
export function createPublishTool(toolCtx: OpenClawToolContext): AgentTool {
|
|
258
|
+
return {
|
|
259
|
+
name: 'agentbnb-publish',
|
|
260
|
+
label: 'AgentBnB Publish',
|
|
261
|
+
description:
|
|
262
|
+
'Publish a capability card to the AgentBnB network. Stores locally and optionally syncs to remote registry.',
|
|
263
|
+
parameters: {
|
|
264
|
+
type: 'object',
|
|
265
|
+
properties: {
|
|
266
|
+
card_json: {
|
|
267
|
+
type: 'string',
|
|
268
|
+
description: 'JSON string of the capability card to publish',
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
required: ['card_json'],
|
|
272
|
+
},
|
|
273
|
+
async execute(_toolCallId, params) {
|
|
274
|
+
const ctx = buildMcpContext(toolCtx);
|
|
275
|
+
const result = await handlePublish(params as { card_json: string }, ctx);
|
|
276
|
+
return toAgentToolResult(result);
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
// Factory
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Creates all 5 AgentBnB tools for an OpenClaw bot.
|
|
287
|
+
* Called by the plugin's `register()` method via `api.registerTool()`.
|
|
288
|
+
*/
|
|
289
|
+
export function createAllTools(toolCtx: OpenClawToolContext): AgentTool[] {
|
|
290
|
+
return [
|
|
291
|
+
createDiscoverTool(toolCtx),
|
|
292
|
+
createRequestTool(toolCtx),
|
|
293
|
+
createConductTool(toolCtx),
|
|
294
|
+
createStatusTool(toolCtx),
|
|
295
|
+
createPublishTool(toolCtx),
|
|
296
|
+
];
|
|
297
|
+
}
|
package/dist/chunk-B2VJTKO5.js
DELETED
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getFeedbackForProvider,
|
|
3
|
-
signEscrowReceipt
|
|
4
|
-
} from "./chunk-7EF3HYVZ.js";
|
|
5
|
-
import {
|
|
6
|
-
AgentBnBError
|
|
7
|
-
} from "./chunk-WVY2W7AA.js";
|
|
8
|
-
|
|
9
|
-
// src/gateway/client.ts
|
|
10
|
-
import { randomUUID } from "crypto";
|
|
11
|
-
async function requestCapability(opts) {
|
|
12
|
-
const { gatewayUrl, token, cardId, params = {}, timeoutMs = 3e5, escrowReceipt, identity } = opts;
|
|
13
|
-
const id = randomUUID();
|
|
14
|
-
const payload = {
|
|
15
|
-
jsonrpc: "2.0",
|
|
16
|
-
id,
|
|
17
|
-
method: "capability.execute",
|
|
18
|
-
params: {
|
|
19
|
-
card_id: cardId,
|
|
20
|
-
...params,
|
|
21
|
-
...escrowReceipt ? { escrow_receipt: escrowReceipt } : {}
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
const headers = { "Content-Type": "application/json" };
|
|
25
|
-
if (identity) {
|
|
26
|
-
const signature = signEscrowReceipt(payload, identity.privateKey);
|
|
27
|
-
headers["X-Agent-Id"] = identity.agentId;
|
|
28
|
-
headers["X-Agent-Public-Key"] = identity.publicKey;
|
|
29
|
-
headers["X-Agent-Signature"] = signature;
|
|
30
|
-
} else if (token) {
|
|
31
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
32
|
-
}
|
|
33
|
-
const controller = new AbortController();
|
|
34
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
35
|
-
let response;
|
|
36
|
-
try {
|
|
37
|
-
response = await fetch(`${gatewayUrl}/rpc`, {
|
|
38
|
-
method: "POST",
|
|
39
|
-
headers,
|
|
40
|
-
body: JSON.stringify(payload),
|
|
41
|
-
signal: controller.signal
|
|
42
|
-
});
|
|
43
|
-
} catch (err) {
|
|
44
|
-
clearTimeout(timer);
|
|
45
|
-
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
46
|
-
throw new AgentBnBError(
|
|
47
|
-
isTimeout ? "Request timed out" : `Network error: ${String(err)}`,
|
|
48
|
-
isTimeout ? "TIMEOUT" : "NETWORK_ERROR"
|
|
49
|
-
);
|
|
50
|
-
} finally {
|
|
51
|
-
clearTimeout(timer);
|
|
52
|
-
}
|
|
53
|
-
const body = await response.json();
|
|
54
|
-
if (body.error) {
|
|
55
|
-
throw new AgentBnBError(body.error.message, `RPC_ERROR_${body.error.code}`);
|
|
56
|
-
}
|
|
57
|
-
return body.result;
|
|
58
|
-
}
|
|
59
|
-
async function requestViaRelay(relay, opts) {
|
|
60
|
-
try {
|
|
61
|
-
return await relay.request({
|
|
62
|
-
targetOwner: opts.targetOwner,
|
|
63
|
-
cardId: opts.cardId,
|
|
64
|
-
skillId: opts.skillId,
|
|
65
|
-
params: opts.params ?? {},
|
|
66
|
-
requester: opts.requester,
|
|
67
|
-
escrowReceipt: opts.escrowReceipt,
|
|
68
|
-
timeoutMs: opts.timeoutMs
|
|
69
|
-
});
|
|
70
|
-
} catch (err) {
|
|
71
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
72
|
-
if (message.includes("timeout")) {
|
|
73
|
-
throw new AgentBnBError(message, "TIMEOUT");
|
|
74
|
-
}
|
|
75
|
-
if (message.includes("offline")) {
|
|
76
|
-
throw new AgentBnBError(message, "AGENT_OFFLINE");
|
|
77
|
-
}
|
|
78
|
-
throw new AgentBnBError(message, "RELAY_ERROR");
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// src/utils/interpolation.ts
|
|
83
|
-
function resolvePath(obj, path) {
|
|
84
|
-
const segments = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter((s) => s.length > 0);
|
|
85
|
-
let current = obj;
|
|
86
|
-
for (const segment of segments) {
|
|
87
|
-
if (current === null || current === void 0) {
|
|
88
|
-
return void 0;
|
|
89
|
-
}
|
|
90
|
-
if (typeof current !== "object") {
|
|
91
|
-
return void 0;
|
|
92
|
-
}
|
|
93
|
-
current = current[segment];
|
|
94
|
-
}
|
|
95
|
-
return current;
|
|
96
|
-
}
|
|
97
|
-
function interpolate(template, context) {
|
|
98
|
-
return template.replace(/\$\{([^}]+)\}/g, (_match, expression) => {
|
|
99
|
-
const resolved = resolvePath(context, expression.trim());
|
|
100
|
-
if (resolved === void 0 || resolved === null) {
|
|
101
|
-
return "";
|
|
102
|
-
}
|
|
103
|
-
if (typeof resolved === "object") {
|
|
104
|
-
return JSON.stringify(resolved);
|
|
105
|
-
}
|
|
106
|
-
return String(resolved);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
function interpolateObject(obj, context) {
|
|
110
|
-
const result = {};
|
|
111
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
112
|
-
result[key] = interpolateValue(value, context);
|
|
113
|
-
}
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
function interpolateValue(value, context) {
|
|
117
|
-
if (typeof value === "string") {
|
|
118
|
-
return interpolate(value, context);
|
|
119
|
-
}
|
|
120
|
-
if (Array.isArray(value)) {
|
|
121
|
-
return value.map((item) => interpolateValue(item, context));
|
|
122
|
-
}
|
|
123
|
-
if (value !== null && typeof value === "object") {
|
|
124
|
-
return interpolateObject(value, context);
|
|
125
|
-
}
|
|
126
|
-
return value;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// src/feedback/reputation.ts
|
|
130
|
-
var QUALITY_SCORES = {
|
|
131
|
-
excellent: 1,
|
|
132
|
-
good: 0.8,
|
|
133
|
-
acceptable: 0.6,
|
|
134
|
-
poor: 0.3,
|
|
135
|
-
failed: 0
|
|
136
|
-
};
|
|
137
|
-
var COST_VALUE_SCORES = {
|
|
138
|
-
great: 1,
|
|
139
|
-
fair: 0.6,
|
|
140
|
-
overpriced: 0.2
|
|
141
|
-
};
|
|
142
|
-
var DECAY_DAYS = 30;
|
|
143
|
-
var WEIGHTS = {
|
|
144
|
-
rating: 0.4,
|
|
145
|
-
quality: 0.3,
|
|
146
|
-
would_reuse: 0.2,
|
|
147
|
-
cost_value: 0.1
|
|
148
|
-
};
|
|
149
|
-
function computeReputation(feedbacks) {
|
|
150
|
-
if (feedbacks.length === 0) return 0.5;
|
|
151
|
-
const now = Date.now();
|
|
152
|
-
let weightedSum = 0;
|
|
153
|
-
let totalWeight = 0;
|
|
154
|
-
for (const fb of feedbacks) {
|
|
155
|
-
const feedbackDate = new Date(fb.timestamp).getTime();
|
|
156
|
-
const ageDays = Math.max(0, (now - feedbackDate) / (1e3 * 60 * 60 * 24));
|
|
157
|
-
const recencyWeight = Math.exp(-ageDays / DECAY_DAYS);
|
|
158
|
-
const ratingScore = (fb.rating - 1) / 4;
|
|
159
|
-
const qualityScore = QUALITY_SCORES[fb.result_quality];
|
|
160
|
-
const reuseScore = fb.would_reuse ? 1 : 0;
|
|
161
|
-
const costScore = COST_VALUE_SCORES[fb.cost_value_ratio];
|
|
162
|
-
const componentScore = WEIGHTS.rating * ratingScore + WEIGHTS.quality * qualityScore + WEIGHTS.would_reuse * reuseScore + WEIGHTS.cost_value * costScore;
|
|
163
|
-
weightedSum += recencyWeight * componentScore;
|
|
164
|
-
totalWeight += recencyWeight;
|
|
165
|
-
}
|
|
166
|
-
if (totalWeight === 0) return 0.5;
|
|
167
|
-
const raw = weightedSum / totalWeight;
|
|
168
|
-
return Math.max(0, Math.min(1, raw));
|
|
169
|
-
}
|
|
170
|
-
function getReputationScore(db, agentId) {
|
|
171
|
-
const feedbacks = getFeedbackForProvider(db, agentId);
|
|
172
|
-
return computeReputation(feedbacks);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// src/registry/matcher.ts
|
|
176
|
-
function searchCards(db, query, filters = {}) {
|
|
177
|
-
const words = query.trim().split(/\s+/).map((w) => w.replace(/["*^{}():]/g, "")).filter((w) => w.length > 0);
|
|
178
|
-
if (words.length === 0) return [];
|
|
179
|
-
const ftsQuery = words.map((w) => `"${w}"`).join(" OR ");
|
|
180
|
-
const conditions = [];
|
|
181
|
-
const params = [ftsQuery];
|
|
182
|
-
if (filters.level !== void 0) {
|
|
183
|
-
conditions.push(`json_extract(cc.data, '$.level') = ?`);
|
|
184
|
-
params.push(filters.level);
|
|
185
|
-
}
|
|
186
|
-
if (filters.online !== void 0) {
|
|
187
|
-
conditions.push(`json_extract(cc.data, '$.availability.online') = ?`);
|
|
188
|
-
params.push(filters.online ? 1 : 0);
|
|
189
|
-
}
|
|
190
|
-
const whereClause = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
191
|
-
const sql = `
|
|
192
|
-
SELECT cc.data
|
|
193
|
-
FROM capability_cards cc
|
|
194
|
-
JOIN cards_fts ON cc.rowid = cards_fts.rowid
|
|
195
|
-
WHERE cards_fts MATCH ?
|
|
196
|
-
${whereClause}
|
|
197
|
-
ORDER BY bm25(cards_fts)
|
|
198
|
-
LIMIT 50
|
|
199
|
-
`;
|
|
200
|
-
const stmt = db.prepare(sql);
|
|
201
|
-
const rows = stmt.all(...params);
|
|
202
|
-
const results = rows.map((row) => JSON.parse(row.data));
|
|
203
|
-
let filtered = results;
|
|
204
|
-
if (filters.apis_used && filters.apis_used.length > 0) {
|
|
205
|
-
const requiredApis = filters.apis_used;
|
|
206
|
-
filtered = filtered.filter((card) => {
|
|
207
|
-
const cardApis = card.metadata?.apis_used ?? [];
|
|
208
|
-
return requiredApis.every((api) => cardApis.includes(api));
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
if (filters.min_reputation !== void 0 && filters.min_reputation > 0) {
|
|
212
|
-
filtered = applyReputationFilter(db, filtered, filters.min_reputation);
|
|
213
|
-
}
|
|
214
|
-
return filtered;
|
|
215
|
-
}
|
|
216
|
-
function filterCards(db, filters) {
|
|
217
|
-
const conditions = [];
|
|
218
|
-
const params = [];
|
|
219
|
-
if (filters.level !== void 0) {
|
|
220
|
-
conditions.push(`json_extract(data, '$.level') = ?`);
|
|
221
|
-
params.push(filters.level);
|
|
222
|
-
}
|
|
223
|
-
if (filters.online !== void 0) {
|
|
224
|
-
conditions.push(`json_extract(data, '$.availability.online') = ?`);
|
|
225
|
-
params.push(filters.online ? 1 : 0);
|
|
226
|
-
}
|
|
227
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
228
|
-
const sql = `SELECT data FROM capability_cards ${whereClause}`;
|
|
229
|
-
const stmt = db.prepare(sql);
|
|
230
|
-
const rows = stmt.all(...params);
|
|
231
|
-
let cards = rows.map((row) => JSON.parse(row.data));
|
|
232
|
-
if (filters.min_reputation !== void 0 && filters.min_reputation > 0) {
|
|
233
|
-
cards = applyReputationFilter(db, cards, filters.min_reputation);
|
|
234
|
-
}
|
|
235
|
-
return cards;
|
|
236
|
-
}
|
|
237
|
-
function applyReputationFilter(db, cards, minReputation) {
|
|
238
|
-
const owners = [...new Set(cards.map((c) => c.owner))];
|
|
239
|
-
const reputationMap = /* @__PURE__ */ new Map();
|
|
240
|
-
for (const owner of owners) {
|
|
241
|
-
reputationMap.set(owner, getReputationScore(db, owner));
|
|
242
|
-
}
|
|
243
|
-
return cards.filter((card) => {
|
|
244
|
-
const score = reputationMap.get(card.owner) ?? 0.5;
|
|
245
|
-
return score >= minReputation;
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
function buildReputationMap(db, owners) {
|
|
249
|
-
const unique = [...new Set(owners)];
|
|
250
|
-
const map = /* @__PURE__ */ new Map();
|
|
251
|
-
for (const owner of unique) {
|
|
252
|
-
map.set(owner, getReputationScore(db, owner));
|
|
253
|
-
}
|
|
254
|
-
return map;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// src/autonomy/tiers.ts
|
|
258
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
259
|
-
var DEFAULT_AUTONOMY_CONFIG = {
|
|
260
|
-
tier1_max_credits: 0,
|
|
261
|
-
tier2_max_credits: 0
|
|
262
|
-
};
|
|
263
|
-
function getAutonomyTier(creditAmount, config) {
|
|
264
|
-
if (creditAmount < config.tier1_max_credits) return 1;
|
|
265
|
-
if (creditAmount < config.tier2_max_credits) return 2;
|
|
266
|
-
return 3;
|
|
267
|
-
}
|
|
268
|
-
function insertAuditEvent(db, event) {
|
|
269
|
-
const isShareEvent = event.type === "auto_share" || event.type === "auto_share_notify" || event.type === "auto_share_pending";
|
|
270
|
-
const cardId = isShareEvent ? "system" : event.card_id;
|
|
271
|
-
const creditsCharged = isShareEvent ? 0 : event.credits;
|
|
272
|
-
const stmt = db.prepare(`
|
|
273
|
-
INSERT INTO request_log (
|
|
274
|
-
id, card_id, card_name, requester, status, latency_ms, credits_charged,
|
|
275
|
-
created_at, skill_id, action_type, tier_invoked
|
|
276
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
277
|
-
`);
|
|
278
|
-
stmt.run(
|
|
279
|
-
randomUUID2(),
|
|
280
|
-
cardId,
|
|
281
|
-
"autonomy-audit",
|
|
282
|
-
"self",
|
|
283
|
-
"success",
|
|
284
|
-
0,
|
|
285
|
-
creditsCharged,
|
|
286
|
-
(/* @__PURE__ */ new Date()).toISOString(),
|
|
287
|
-
event.skill_id,
|
|
288
|
-
event.type,
|
|
289
|
-
event.tier_invoked
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// src/cli/remote-registry.ts
|
|
294
|
-
var RegistryTimeoutError = class extends AgentBnBError {
|
|
295
|
-
constructor(url) {
|
|
296
|
-
super(
|
|
297
|
-
`Registry at ${url} did not respond within 5s. Showing local results only.`,
|
|
298
|
-
"REGISTRY_TIMEOUT"
|
|
299
|
-
);
|
|
300
|
-
this.name = "RegistryTimeoutError";
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
var RegistryConnectionError = class extends AgentBnBError {
|
|
304
|
-
constructor(url) {
|
|
305
|
-
super(
|
|
306
|
-
`Cannot reach ${url}. Is the registry running? Showing local results only.`,
|
|
307
|
-
"REGISTRY_CONNECTION"
|
|
308
|
-
);
|
|
309
|
-
this.name = "RegistryConnectionError";
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
var RegistryAuthError = class extends AgentBnBError {
|
|
313
|
-
constructor(url) {
|
|
314
|
-
super(
|
|
315
|
-
`Authentication failed for ${url}. Run \`agentbnb config set token <your-token>\`.`,
|
|
316
|
-
"REGISTRY_AUTH"
|
|
317
|
-
);
|
|
318
|
-
this.name = "RegistryAuthError";
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
async function fetchRemoteCards(registryUrl, params, timeoutMs = 5e3) {
|
|
322
|
-
let cardsUrl;
|
|
323
|
-
try {
|
|
324
|
-
cardsUrl = new URL("/cards", registryUrl);
|
|
325
|
-
} catch {
|
|
326
|
-
throw new AgentBnBError(`Invalid registry URL: ${registryUrl}`, "INVALID_REGISTRY_URL");
|
|
327
|
-
}
|
|
328
|
-
const searchParams = new URLSearchParams();
|
|
329
|
-
if (params.q !== void 0) searchParams.set("q", params.q);
|
|
330
|
-
if (params.level !== void 0) searchParams.set("level", String(params.level));
|
|
331
|
-
if (params.online !== void 0) searchParams.set("online", String(params.online));
|
|
332
|
-
if (params.tag !== void 0) searchParams.set("tag", params.tag);
|
|
333
|
-
searchParams.set("limit", "100");
|
|
334
|
-
cardsUrl.search = searchParams.toString();
|
|
335
|
-
const controller = new AbortController();
|
|
336
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
337
|
-
let response;
|
|
338
|
-
try {
|
|
339
|
-
response = await fetch(cardsUrl.toString(), { signal: controller.signal });
|
|
340
|
-
} catch (err) {
|
|
341
|
-
clearTimeout(timer);
|
|
342
|
-
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
343
|
-
if (isTimeout) {
|
|
344
|
-
throw new RegistryTimeoutError(registryUrl);
|
|
345
|
-
}
|
|
346
|
-
throw new RegistryConnectionError(registryUrl);
|
|
347
|
-
} finally {
|
|
348
|
-
clearTimeout(timer);
|
|
349
|
-
}
|
|
350
|
-
if (response.status === 401 || response.status === 403) {
|
|
351
|
-
throw new RegistryAuthError(registryUrl);
|
|
352
|
-
}
|
|
353
|
-
if (!response.ok) {
|
|
354
|
-
throw new RegistryConnectionError(registryUrl);
|
|
355
|
-
}
|
|
356
|
-
const body = await response.json();
|
|
357
|
-
return body.items;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// src/autonomy/pending-requests.ts
|
|
361
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
362
|
-
function listPendingRequests(db) {
|
|
363
|
-
const rows = db.prepare(`SELECT * FROM pending_requests WHERE status = 'pending' ORDER BY created_at DESC`).all();
|
|
364
|
-
return rows;
|
|
365
|
-
}
|
|
366
|
-
function resolvePendingRequest(db, id, resolution) {
|
|
367
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
368
|
-
const result = db.prepare(
|
|
369
|
-
`UPDATE pending_requests SET status = ?, resolved_at = ? WHERE id = ?`
|
|
370
|
-
).run(resolution, now, id);
|
|
371
|
-
if (result.changes === 0) {
|
|
372
|
-
throw new AgentBnBError(
|
|
373
|
-
`Pending request not found: ${id}`,
|
|
374
|
-
"NOT_FOUND"
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
export {
|
|
380
|
-
interpolateObject,
|
|
381
|
-
computeReputation,
|
|
382
|
-
searchCards,
|
|
383
|
-
filterCards,
|
|
384
|
-
buildReputationMap,
|
|
385
|
-
requestCapability,
|
|
386
|
-
requestViaRelay,
|
|
387
|
-
DEFAULT_AUTONOMY_CONFIG,
|
|
388
|
-
getAutonomyTier,
|
|
389
|
-
insertAuditEvent,
|
|
390
|
-
listPendingRequests,
|
|
391
|
-
resolvePendingRequest,
|
|
392
|
-
fetchRemoteCards
|
|
393
|
-
};
|