open-agents-ai 0.187.489 → 0.187.491
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/index.js +508 -14
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1199,6 +1199,219 @@ var init_tool_executor = __esm({
|
|
|
1199
1199
|
}
|
|
1200
1200
|
});
|
|
1201
1201
|
|
|
1202
|
+
// packages/execution/dist/tools/security-classifier.js
|
|
1203
|
+
function classifyTool(name10) {
|
|
1204
|
+
for (const rule of RULES) {
|
|
1205
|
+
const hit = typeof rule.match === "function" ? rule.match(name10) : rule.match.test(name10);
|
|
1206
|
+
if (hit)
|
|
1207
|
+
return applyOverrides(name10, rule.info);
|
|
1208
|
+
}
|
|
1209
|
+
return applyOverrides(name10, UNKNOWN_DEFAULT);
|
|
1210
|
+
}
|
|
1211
|
+
function applyOverrides(name10, base3) {
|
|
1212
|
+
const raw = process.env["OA_TOOL_OVERRIDES"];
|
|
1213
|
+
if (!raw)
|
|
1214
|
+
return base3;
|
|
1215
|
+
try {
|
|
1216
|
+
const overrides = JSON.parse(raw);
|
|
1217
|
+
const o2 = overrides[name10];
|
|
1218
|
+
if (!o2)
|
|
1219
|
+
return base3;
|
|
1220
|
+
return { ...base3, ...o2 };
|
|
1221
|
+
} catch {
|
|
1222
|
+
return base3;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
function canInvokeTool(args) {
|
|
1226
|
+
const info = classifyTool(args.toolName);
|
|
1227
|
+
const order = ["read", "run", "admin"];
|
|
1228
|
+
if (order.indexOf(args.scope) < order.indexOf(info.requires_scope)) {
|
|
1229
|
+
return {
|
|
1230
|
+
status: 403,
|
|
1231
|
+
reason: `Tool '${args.toolName}' requires '${info.requires_scope}' scope; caller has '${args.scope}'.`
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
if (args.origin === "remote" && !info.off_device_allowed && args.scope !== "admin") {
|
|
1235
|
+
return {
|
|
1236
|
+
status: 403,
|
|
1237
|
+
reason: `Tool '${args.toolName}' is loopback-only (off_device_allowed=false). Remote callers must have 'admin' scope.`
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
return null;
|
|
1241
|
+
}
|
|
1242
|
+
var CRITICAL_SENSITIVE, HARDWARE_DEVICE, AGENT_SPAWN, SHELL_EXEC, SANDBOXED_EXEC, NETWORK_OUTBOUND, NETWORK_BROWSER, LOCAL_WRITE, LOCAL_READ, NETWORK_READ, MEMORY_READ, MEMORY_WRITE, TASK_CONTROL, SCHEDULER_REMINDER, TASK_COMPLETE_NEUTRAL, RULES, UNKNOWN_DEFAULT;
|
|
1243
|
+
var init_security_classifier = __esm({
|
|
1244
|
+
"packages/execution/dist/tools/security-classifier.js"() {
|
|
1245
|
+
"use strict";
|
|
1246
|
+
CRITICAL_SENSITIVE = {
|
|
1247
|
+
categories: ["sensitive"],
|
|
1248
|
+
risk: "critical",
|
|
1249
|
+
requires_scope: "admin",
|
|
1250
|
+
off_device_allowed: false,
|
|
1251
|
+
rationale: "Modifies tool registry, identity, or sponsorship — admin only, loopback only."
|
|
1252
|
+
};
|
|
1253
|
+
HARDWARE_DEVICE = {
|
|
1254
|
+
categories: ["hardware"],
|
|
1255
|
+
risk: "high",
|
|
1256
|
+
requires_scope: "admin",
|
|
1257
|
+
off_device_allowed: false,
|
|
1258
|
+
rationale: "Hardware peripheral access (camera/mic/wifi/BT/SDR/desktop). Loopback-only by default."
|
|
1259
|
+
};
|
|
1260
|
+
AGENT_SPAWN = {
|
|
1261
|
+
categories: ["agent"],
|
|
1262
|
+
risk: "high",
|
|
1263
|
+
requires_scope: "admin",
|
|
1264
|
+
off_device_allowed: false,
|
|
1265
|
+
rationale: "Spawns sub-agents or sends inter-agent messages. Multiplies blast radius."
|
|
1266
|
+
};
|
|
1267
|
+
SHELL_EXEC = {
|
|
1268
|
+
categories: ["exec"],
|
|
1269
|
+
risk: "critical",
|
|
1270
|
+
requires_scope: "run",
|
|
1271
|
+
off_device_allowed: false,
|
|
1272
|
+
rationale: "Executes arbitrary shell commands. Loopback-only — remote callers need admin scope."
|
|
1273
|
+
};
|
|
1274
|
+
SANDBOXED_EXEC = {
|
|
1275
|
+
categories: ["exec"],
|
|
1276
|
+
risk: "high",
|
|
1277
|
+
requires_scope: "run",
|
|
1278
|
+
off_device_allowed: true,
|
|
1279
|
+
rationale: "Sandboxed code execution — bounded blast radius, OK for off-device with auth."
|
|
1280
|
+
};
|
|
1281
|
+
NETWORK_OUTBOUND = {
|
|
1282
|
+
categories: ["network"],
|
|
1283
|
+
risk: "medium",
|
|
1284
|
+
requires_scope: "run",
|
|
1285
|
+
off_device_allowed: true,
|
|
1286
|
+
rationale: "Outbound network call. Off-device callers may invoke with run scope."
|
|
1287
|
+
};
|
|
1288
|
+
NETWORK_BROWSER = {
|
|
1289
|
+
categories: ["network", "exec"],
|
|
1290
|
+
risk: "high",
|
|
1291
|
+
requires_scope: "run",
|
|
1292
|
+
off_device_allowed: false,
|
|
1293
|
+
rationale: "Headless browser — broad network + cookie / session access. Loopback-only."
|
|
1294
|
+
};
|
|
1295
|
+
LOCAL_WRITE = {
|
|
1296
|
+
categories: ["write"],
|
|
1297
|
+
risk: "medium",
|
|
1298
|
+
requires_scope: "run",
|
|
1299
|
+
off_device_allowed: true,
|
|
1300
|
+
rationale: "Local filesystem mutation. Run scope required."
|
|
1301
|
+
};
|
|
1302
|
+
LOCAL_READ = {
|
|
1303
|
+
categories: ["read"],
|
|
1304
|
+
risk: "low",
|
|
1305
|
+
requires_scope: "read",
|
|
1306
|
+
off_device_allowed: true,
|
|
1307
|
+
rationale: "Read-only inspection of local state."
|
|
1308
|
+
};
|
|
1309
|
+
NETWORK_READ = {
|
|
1310
|
+
categories: ["read", "network"],
|
|
1311
|
+
risk: "low",
|
|
1312
|
+
requires_scope: "read",
|
|
1313
|
+
off_device_allowed: true,
|
|
1314
|
+
rationale: "Network read (search/fetch). Safe to expose."
|
|
1315
|
+
};
|
|
1316
|
+
MEMORY_READ = {
|
|
1317
|
+
categories: ["read"],
|
|
1318
|
+
risk: "low",
|
|
1319
|
+
requires_scope: "read",
|
|
1320
|
+
off_device_allowed: true,
|
|
1321
|
+
rationale: "Reads associative memory (no mutation)."
|
|
1322
|
+
};
|
|
1323
|
+
MEMORY_WRITE = {
|
|
1324
|
+
categories: ["write"],
|
|
1325
|
+
risk: "medium",
|
|
1326
|
+
requires_scope: "run",
|
|
1327
|
+
off_device_allowed: true,
|
|
1328
|
+
rationale: "Mutates associative memory store."
|
|
1329
|
+
};
|
|
1330
|
+
TASK_CONTROL = {
|
|
1331
|
+
categories: ["exec"],
|
|
1332
|
+
risk: "medium",
|
|
1333
|
+
requires_scope: "run",
|
|
1334
|
+
off_device_allowed: true,
|
|
1335
|
+
rationale: "Controls background task lifecycle (start/stop/inspect)."
|
|
1336
|
+
};
|
|
1337
|
+
SCHEDULER_REMINDER = {
|
|
1338
|
+
categories: ["write"],
|
|
1339
|
+
risk: "medium",
|
|
1340
|
+
requires_scope: "run",
|
|
1341
|
+
off_device_allowed: true,
|
|
1342
|
+
rationale: "Schedules deferred actions or reminders."
|
|
1343
|
+
};
|
|
1344
|
+
TASK_COMPLETE_NEUTRAL = {
|
|
1345
|
+
categories: ["read"],
|
|
1346
|
+
risk: "low",
|
|
1347
|
+
requires_scope: "read",
|
|
1348
|
+
off_device_allowed: true,
|
|
1349
|
+
rationale: "Signals task completion. No side effects."
|
|
1350
|
+
};
|
|
1351
|
+
RULES = [
|
|
1352
|
+
// ── Critical sensitive: tool registry / identity / worktree / sponsorship
|
|
1353
|
+
{ match: /^(create_tool|manage_tools|skill_build|tool_creator)$/, info: CRITICAL_SENSITIVE },
|
|
1354
|
+
{ match: /^(identity_kernel|reflection_integrity|cohere_constraints)$/, info: CRITICAL_SENSITIVE },
|
|
1355
|
+
{ match: /^(enter_worktree|exit_worktree)$/, info: CRITICAL_SENSITIVE },
|
|
1356
|
+
{ match: /^(aiwg_setup|aiwg_health|aiwg_workflow)$/, info: CRITICAL_SENSITIVE },
|
|
1357
|
+
{ match: /^(expose|sponsor|nexus_register)/, info: CRITICAL_SENSITIVE },
|
|
1358
|
+
// ── Hardware peripherals
|
|
1359
|
+
{ match: /^(camera_capture|audio_capture|audio_playback|audio_analyze|asr_listen)$/, info: HARDWARE_DEVICE },
|
|
1360
|
+
{ match: /^(wifi_control|bluetooth_scan|sdr_scan|flipper_zero|meshtastic|gps_location)$/, info: HARDWARE_DEVICE },
|
|
1361
|
+
{ match: /^(desktop_click|desktop_describe|screenshot)$/, info: HARDWARE_DEVICE },
|
|
1362
|
+
{ match: /^(jibberlink)$/, info: HARDWARE_DEVICE },
|
|
1363
|
+
// audio modem
|
|
1364
|
+
// ── Agent spawning + inter-agent messaging + P2P
|
|
1365
|
+
{ match: /^(full_sub_agent|sub_agent|agent|send_message)$/, info: AGENT_SPAWN },
|
|
1366
|
+
{ match: /^(factory|opencode|cron_agent|autoresearch)$/, info: AGENT_SPAWN },
|
|
1367
|
+
{ match: /^nexus$/, info: AGENT_SPAWN },
|
|
1368
|
+
// P2P invocation
|
|
1369
|
+
{ match: /^debate$/, info: AGENT_SPAWN },
|
|
1370
|
+
// multi-agent debate
|
|
1371
|
+
{ match: /^replay_with_intervention$/, info: AGENT_SPAWN },
|
|
1372
|
+
// ── Shell + REPL (high risk exec, loopback only)
|
|
1373
|
+
{ match: /^shell$/, info: SHELL_EXEC },
|
|
1374
|
+
{ match: /^(repl|repl_exec|background_run)$/, info: SHELL_EXEC },
|
|
1375
|
+
// ── Sandboxed exec (OK for remote with auth)
|
|
1376
|
+
{ match: /^(code_sandbox|cohere_sandbox)$/, info: SANDBOXED_EXEC },
|
|
1377
|
+
// ── Browser automation
|
|
1378
|
+
{ match: /^(browser_action|playwright_browser|web_crawl)$/, info: NETWORK_BROWSER },
|
|
1379
|
+
// ── Network reads (safe)
|
|
1380
|
+
{ match: /^(web_search|web_fetch)$/, info: NETWORK_READ },
|
|
1381
|
+
// ── Network outbound (mutating or remote inference)
|
|
1382
|
+
{ match: /^(image_generate|vision|video_understand)$/, info: NETWORK_OUTBOUND },
|
|
1383
|
+
{ match: /^(transcribe_file|transcribe_url|youtube_download)$/, info: NETWORK_OUTBOUND },
|
|
1384
|
+
{ match: /^(fortemi_bridge)$/, info: NETWORK_OUTBOUND },
|
|
1385
|
+
// ── Memory tools
|
|
1386
|
+
{ match: /^(memory_read|memory_search)$/, info: MEMORY_READ },
|
|
1387
|
+
{ match: /^(memory_write|memory_metabolism|multimodal_memory|visual_memory|embedding_store)$/, info: MEMORY_WRITE },
|
|
1388
|
+
// ── Local writes (fs mutation)
|
|
1389
|
+
{ match: /^(file_write|file_edit|file_patch|batch_edit|notebook_edit|structured_file|image_resize)$/, info: LOCAL_WRITE },
|
|
1390
|
+
{ match: /^(working_notes|todo_write)$/, info: LOCAL_WRITE },
|
|
1391
|
+
{ match: /^(scheduler|reminder|agenda)$/, info: SCHEDULER_REMINDER },
|
|
1392
|
+
{ match: /^(exploration_culture)$/, info: LOCAL_WRITE },
|
|
1393
|
+
// ── Task control
|
|
1394
|
+
{ match: /^(task_status|task_output|task_stop)$/, info: TASK_CONTROL },
|
|
1395
|
+
// ── Local reads (inspection)
|
|
1396
|
+
{ match: /^(file_read|file_explore|list_directory|grep_search|glob_find)$/, info: LOCAL_READ },
|
|
1397
|
+
{ match: /^(image_read|ocr|ocr_pdf|ocr_image_advanced|pdf_to_text|structured_read)$/, info: LOCAL_READ },
|
|
1398
|
+
{ match: /^(symbol_search|impact_analysis|code_neighbors|repo_map|codebase_map|semantic_map|import_graph)$/, info: LOCAL_READ },
|
|
1399
|
+
{ match: /^(diagnostic|git_info|environment_snapshot|process_health|todo_read|explore_tools)$/, info: LOCAL_READ },
|
|
1400
|
+
{ match: /^(log_explore|log_packet|change_log|phase_recall|code_graph)$/, info: LOCAL_READ },
|
|
1401
|
+
{ match: /^skill_(list|execute|read)$/, info: LOCAL_READ },
|
|
1402
|
+
// ── Task completion (neutral signal)
|
|
1403
|
+
{ match: /^task_complete$/, info: TASK_COMPLETE_NEUTRAL }
|
|
1404
|
+
];
|
|
1405
|
+
UNKNOWN_DEFAULT = {
|
|
1406
|
+
categories: ["sensitive"],
|
|
1407
|
+
risk: "high",
|
|
1408
|
+
requires_scope: "admin",
|
|
1409
|
+
off_device_allowed: false,
|
|
1410
|
+
rationale: "Unrecognized tool — defaulting to admin-only, loopback-only. Operators may override via OA_TOOL_OVERRIDES."
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1202
1415
|
// packages/execution/dist/process-kill.js
|
|
1203
1416
|
import { execSync } from "node:child_process";
|
|
1204
1417
|
function killProcessTree(pid, signal = "SIGKILL") {
|
|
@@ -510207,8 +510420,10 @@ __export(dist_exports, {
|
|
|
510207
510420
|
buildMcpToolName: () => buildMcpToolName,
|
|
510208
510421
|
buildSkillsSummary: () => buildSkillsSummary,
|
|
510209
510422
|
buildSubProcessArgs: () => buildSubProcessArgs,
|
|
510423
|
+
canInvokeTool: () => canInvokeTool,
|
|
510210
510424
|
checkConstraints: () => checkConstraints,
|
|
510211
510425
|
checkDesktopDeps: () => checkDesktopDeps,
|
|
510426
|
+
classifyTool: () => classifyTool,
|
|
510212
510427
|
clearExploreNotes: () => clearExploreNotes,
|
|
510213
510428
|
clearImportGraphCache: () => clearImportGraphCache,
|
|
510214
510429
|
clearWorkingNotes: () => clearWorkingNotes,
|
|
@@ -510321,6 +510536,7 @@ var init_dist5 = __esm({
|
|
|
510321
510536
|
"packages/execution/dist/index.js"() {
|
|
510322
510537
|
"use strict";
|
|
510323
510538
|
init_tool_executor();
|
|
510539
|
+
init_security_classifier();
|
|
510324
510540
|
init_shell();
|
|
510325
510541
|
init_debate();
|
|
510326
510542
|
init_replay_with_intervention();
|
|
@@ -576578,6 +576794,23 @@ async function tryRouteV1(ctx3) {
|
|
|
576578
576794
|
if (pathname === "/v1/tools" && method === "GET") {
|
|
576579
576795
|
return handleListTools(ctx3);
|
|
576580
576796
|
}
|
|
576797
|
+
{
|
|
576798
|
+
const m2 = /^\/v1\/tools\/([^/]+)(\/call)?$/.exec(pathname);
|
|
576799
|
+
if (m2) {
|
|
576800
|
+
const toolName = decodeURIComponent(m2[1]);
|
|
576801
|
+
const isCall = !!m2[2];
|
|
576802
|
+
if (isCall && method === "POST") return handleCallTool(ctx3, toolName);
|
|
576803
|
+
if (!isCall && method === "GET") return handleGetTool(ctx3, toolName);
|
|
576804
|
+
sendProblem(ctx3.res, problemDetails({
|
|
576805
|
+
type: P.methodNotAllowed,
|
|
576806
|
+
status: 405,
|
|
576807
|
+
title: `Method ${method} not allowed for ${pathname}`,
|
|
576808
|
+
detail: isCall ? "POST /v1/tools/{name}/call to execute the tool." : "GET /v1/tools/{name} to fetch its schema.",
|
|
576809
|
+
instance: ctx3.requestId
|
|
576810
|
+
}));
|
|
576811
|
+
return true;
|
|
576812
|
+
}
|
|
576813
|
+
}
|
|
576581
576814
|
if (pathname === "/v1/hooks" && method === "GET") {
|
|
576582
576815
|
return handleListHooks(ctx3);
|
|
576583
576816
|
}
|
|
@@ -577621,26 +577854,47 @@ async function handleListTools(ctx3) {
|
|
|
577621
577854
|
sendJson(res, 200, { data: [], pagination: { limit: 50, offset: 0, total: 0, has_more: false } });
|
|
577622
577855
|
return true;
|
|
577623
577856
|
}
|
|
577857
|
+
const classify = mod2.classifyTool;
|
|
577624
577858
|
const tools = [];
|
|
577625
577859
|
for (const [key, value2] of Object.entries(mod2)) {
|
|
577626
577860
|
if (typeof value2 !== "function") continue;
|
|
577627
577861
|
const proto = value2.prototype;
|
|
577628
577862
|
if (!proto || typeof proto.execute !== "function") continue;
|
|
577863
|
+
let inst = null;
|
|
577629
577864
|
try {
|
|
577630
|
-
|
|
577631
|
-
if (typeof inst.name === "string") {
|
|
577632
|
-
tools.push({
|
|
577633
|
-
name: inst.name,
|
|
577634
|
-
class: key,
|
|
577635
|
-
description: inst.description ?? "",
|
|
577636
|
-
parameters: inst.parameters ?? null
|
|
577637
|
-
});
|
|
577638
|
-
}
|
|
577865
|
+
inst = new value2();
|
|
577639
577866
|
} catch {
|
|
577867
|
+
try {
|
|
577868
|
+
inst = new value2(process.cwd());
|
|
577869
|
+
} catch {
|
|
577870
|
+
inst = null;
|
|
577871
|
+
}
|
|
577640
577872
|
}
|
|
577873
|
+
if (!inst || typeof inst.name !== "string") continue;
|
|
577874
|
+
const name10 = inst.name;
|
|
577875
|
+
const security = classify ? classify(name10) : null;
|
|
577876
|
+
tools.push({
|
|
577877
|
+
name: name10,
|
|
577878
|
+
class: key,
|
|
577879
|
+
description: inst.description ?? "",
|
|
577880
|
+
parameters: inst.parameters ?? null,
|
|
577881
|
+
security: security ?? void 0,
|
|
577882
|
+
endpoints: {
|
|
577883
|
+
call: `/v1/tools/${encodeURIComponent(name10)}/call`,
|
|
577884
|
+
schema: `/v1/tools/${encodeURIComponent(name10)}`
|
|
577885
|
+
}
|
|
577886
|
+
});
|
|
577641
577887
|
}
|
|
577888
|
+
tools.sort((a2, b) => a2.name.localeCompare(b.name));
|
|
577889
|
+
const filterCat = url.searchParams.get("category");
|
|
577890
|
+
const filterScope = url.searchParams.get("scope");
|
|
577891
|
+
const filterRisk = url.searchParams.get("risk");
|
|
577892
|
+
let filtered = tools;
|
|
577893
|
+
if (filterCat) filtered = filtered.filter((t2) => t2.security?.categories?.includes(filterCat));
|
|
577894
|
+
if (filterScope) filtered = filtered.filter((t2) => t2.security?.requires_scope === filterScope);
|
|
577895
|
+
if (filterRisk) filtered = filtered.filter((t2) => t2.security?.risk === filterRisk);
|
|
577642
577896
|
const page2 = parsePagination(url.searchParams);
|
|
577643
|
-
const envelope = paginated(
|
|
577897
|
+
const envelope = paginated(filtered, page2);
|
|
577644
577898
|
const etag = computeEtag(envelope);
|
|
577645
577899
|
if (checkNotModified(req2, res, etag)) return true;
|
|
577646
577900
|
sendJson(res, 200, envelope, { etag, cacheControl: "private, max-age=60" });
|
|
@@ -577656,6 +577910,199 @@ async function handleListTools(ctx3) {
|
|
|
577656
577910
|
return true;
|
|
577657
577911
|
}
|
|
577658
577912
|
}
|
|
577913
|
+
async function handleGetTool(ctx3, name10) {
|
|
577914
|
+
const { req: req2, res, requestId } = ctx3;
|
|
577915
|
+
try {
|
|
577916
|
+
const mod2 = await Promise.resolve().then(() => (init_dist5(), dist_exports)).catch(() => null);
|
|
577917
|
+
if (!mod2) {
|
|
577918
|
+
sendProblem(res, problemDetails({
|
|
577919
|
+
type: P.notFound,
|
|
577920
|
+
status: 404,
|
|
577921
|
+
title: "Tool registry unavailable",
|
|
577922
|
+
detail: "@open-agents/execution module failed to load.",
|
|
577923
|
+
instance: requestId
|
|
577924
|
+
}));
|
|
577925
|
+
return true;
|
|
577926
|
+
}
|
|
577927
|
+
const classify = mod2.classifyTool;
|
|
577928
|
+
let found = null;
|
|
577929
|
+
for (const [key, value2] of Object.entries(mod2)) {
|
|
577930
|
+
if (typeof value2 !== "function") continue;
|
|
577931
|
+
const proto = value2.prototype;
|
|
577932
|
+
if (!proto || typeof proto.execute !== "function") continue;
|
|
577933
|
+
let inst2 = null;
|
|
577934
|
+
try {
|
|
577935
|
+
inst2 = new value2();
|
|
577936
|
+
} catch {
|
|
577937
|
+
try {
|
|
577938
|
+
inst2 = new value2(process.cwd());
|
|
577939
|
+
} catch {
|
|
577940
|
+
inst2 = null;
|
|
577941
|
+
}
|
|
577942
|
+
}
|
|
577943
|
+
if (!inst2 || inst2.name !== name10) continue;
|
|
577944
|
+
found = { instance: inst2, className: key };
|
|
577945
|
+
break;
|
|
577946
|
+
}
|
|
577947
|
+
if (!found) {
|
|
577948
|
+
sendProblem(res, problemDetails({
|
|
577949
|
+
type: P.notFound,
|
|
577950
|
+
status: 404,
|
|
577951
|
+
title: `Tool '${name10}' not found`,
|
|
577952
|
+
detail: "No tool with that name is registered in @open-agents/execution.",
|
|
577953
|
+
instance: requestId
|
|
577954
|
+
}));
|
|
577955
|
+
return true;
|
|
577956
|
+
}
|
|
577957
|
+
const inst = found.instance;
|
|
577958
|
+
const security = classify ? classify(name10) : null;
|
|
577959
|
+
const body = {
|
|
577960
|
+
name: name10,
|
|
577961
|
+
class: found.className,
|
|
577962
|
+
description: inst.description ?? "",
|
|
577963
|
+
parameters: inst.parameters ?? null,
|
|
577964
|
+
security: security ?? void 0,
|
|
577965
|
+
endpoints: {
|
|
577966
|
+
call: `/v1/tools/${encodeURIComponent(name10)}/call`,
|
|
577967
|
+
schema: `/v1/tools/${encodeURIComponent(name10)}`
|
|
577968
|
+
}
|
|
577969
|
+
};
|
|
577970
|
+
const etag = computeEtag(body);
|
|
577971
|
+
if (checkNotModified(req2, res, etag)) return true;
|
|
577972
|
+
sendJson(res, 200, body, { etag, cacheControl: "private, max-age=60" });
|
|
577973
|
+
return true;
|
|
577974
|
+
} catch (err) {
|
|
577975
|
+
sendProblem(res, problemDetails({
|
|
577976
|
+
type: P.internalError,
|
|
577977
|
+
status: 500,
|
|
577978
|
+
title: "Tool schema fetch failed",
|
|
577979
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
577980
|
+
instance: requestId
|
|
577981
|
+
}));
|
|
577982
|
+
return true;
|
|
577983
|
+
}
|
|
577984
|
+
}
|
|
577985
|
+
async function handleCallTool(ctx3, name10) {
|
|
577986
|
+
const { req: req2, res, requestId } = ctx3;
|
|
577987
|
+
try {
|
|
577988
|
+
const mod2 = await Promise.resolve().then(() => (init_dist5(), dist_exports)).catch(() => null);
|
|
577989
|
+
if (!mod2) {
|
|
577990
|
+
sendProblem(res, problemDetails({
|
|
577991
|
+
type: P.notFound,
|
|
577992
|
+
status: 404,
|
|
577993
|
+
title: "Tool registry unavailable",
|
|
577994
|
+
detail: "@open-agents/execution module failed to load.",
|
|
577995
|
+
instance: requestId
|
|
577996
|
+
}));
|
|
577997
|
+
return true;
|
|
577998
|
+
}
|
|
577999
|
+
let ToolClass = null;
|
|
578000
|
+
let className = "";
|
|
578001
|
+
for (const [key, value2] of Object.entries(mod2)) {
|
|
578002
|
+
if (typeof value2 !== "function") continue;
|
|
578003
|
+
const proto = value2.prototype;
|
|
578004
|
+
if (!proto || typeof proto.execute !== "function") continue;
|
|
578005
|
+
let probe = null;
|
|
578006
|
+
try {
|
|
578007
|
+
probe = new value2();
|
|
578008
|
+
} catch {
|
|
578009
|
+
try {
|
|
578010
|
+
probe = new value2(process.cwd());
|
|
578011
|
+
} catch {
|
|
578012
|
+
probe = null;
|
|
578013
|
+
}
|
|
578014
|
+
}
|
|
578015
|
+
if (probe?.name === name10) {
|
|
578016
|
+
ToolClass = value2;
|
|
578017
|
+
className = key;
|
|
578018
|
+
break;
|
|
578019
|
+
}
|
|
578020
|
+
}
|
|
578021
|
+
if (!ToolClass) {
|
|
578022
|
+
sendProblem(res, problemDetails({
|
|
578023
|
+
type: P.notFound,
|
|
578024
|
+
status: 404,
|
|
578025
|
+
title: `Tool '${name10}' not found`,
|
|
578026
|
+
detail: "Use GET /v1/tools to list available tools.",
|
|
578027
|
+
instance: requestId
|
|
578028
|
+
}));
|
|
578029
|
+
return true;
|
|
578030
|
+
}
|
|
578031
|
+
const remoteIp = (req2.socket?.remoteAddress || "").replace(/^::ffff:/, "");
|
|
578032
|
+
const origin = /^(127\.\d+\.\d+\.\d+|::1|localhost)$/.test(remoteIp) ? "loopback" : "remote";
|
|
578033
|
+
const reqAuth = req2;
|
|
578034
|
+
const scope = reqAuth._authScope ?? (origin === "loopback" ? "admin" : "read");
|
|
578035
|
+
const canInvoke = mod2.canInvokeTool;
|
|
578036
|
+
const denial = canInvoke ? canInvoke({ toolName: name10, origin, scope }) : null;
|
|
578037
|
+
if (denial) {
|
|
578038
|
+
sendProblem(res, problemDetails({
|
|
578039
|
+
type: P.forbidden,
|
|
578040
|
+
status: denial.status,
|
|
578041
|
+
title: "Tool invocation denied",
|
|
578042
|
+
detail: denial.reason,
|
|
578043
|
+
instance: requestId
|
|
578044
|
+
}));
|
|
578045
|
+
return true;
|
|
578046
|
+
}
|
|
578047
|
+
const body = await parseJsonBodyStrict(req2).catch(() => null);
|
|
578048
|
+
if (body !== null && typeof body !== "object") {
|
|
578049
|
+
sendProblem(res, problemDetails({
|
|
578050
|
+
type: P.invalidRequest,
|
|
578051
|
+
status: 400,
|
|
578052
|
+
title: "Body must be JSON object",
|
|
578053
|
+
detail: "POST {args: {...}, working_dir?: string}",
|
|
578054
|
+
instance: requestId
|
|
578055
|
+
}));
|
|
578056
|
+
return true;
|
|
578057
|
+
}
|
|
578058
|
+
const args = body?.args ?? {};
|
|
578059
|
+
const workingDir = typeof body?.working_dir === "string" ? body.working_dir : process.cwd();
|
|
578060
|
+
let tool = null;
|
|
578061
|
+
try {
|
|
578062
|
+
tool = new ToolClass(workingDir);
|
|
578063
|
+
} catch {
|
|
578064
|
+
try {
|
|
578065
|
+
tool = new ToolClass();
|
|
578066
|
+
} catch {
|
|
578067
|
+
tool = null;
|
|
578068
|
+
}
|
|
578069
|
+
}
|
|
578070
|
+
if (!tool) {
|
|
578071
|
+
sendProblem(res, problemDetails({
|
|
578072
|
+
type: P.internalError,
|
|
578073
|
+
status: 500,
|
|
578074
|
+
title: "Tool instantiation failed",
|
|
578075
|
+
detail: `Could not construct ${className}. Some tools require additional adapters (debate, replay) — use /v1/run for those.`,
|
|
578076
|
+
instance: requestId
|
|
578077
|
+
}));
|
|
578078
|
+
return true;
|
|
578079
|
+
}
|
|
578080
|
+
const result = await tool.execute(args).catch((e2) => ({
|
|
578081
|
+
success: false,
|
|
578082
|
+
output: "",
|
|
578083
|
+
error: e2 instanceof Error ? e2.message : String(e2),
|
|
578084
|
+
durationMs: 0
|
|
578085
|
+
}));
|
|
578086
|
+
sendJson(res, 200, {
|
|
578087
|
+
tool: name10,
|
|
578088
|
+
class: className,
|
|
578089
|
+
result,
|
|
578090
|
+
security: mod2.classifyTool ? mod2.classifyTool(name10) : void 0,
|
|
578091
|
+
origin,
|
|
578092
|
+
scope
|
|
578093
|
+
});
|
|
578094
|
+
return true;
|
|
578095
|
+
} catch (err) {
|
|
578096
|
+
sendProblem(res, problemDetails({
|
|
578097
|
+
type: P.internalError,
|
|
578098
|
+
status: 500,
|
|
578099
|
+
title: "Tool call failed",
|
|
578100
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
578101
|
+
instance: requestId
|
|
578102
|
+
}));
|
|
578103
|
+
return true;
|
|
578104
|
+
}
|
|
578105
|
+
}
|
|
577659
578106
|
async function handleListHooks(ctx3) {
|
|
577660
578107
|
const { res } = ctx3;
|
|
577661
578108
|
sendJson(res, 200, {
|
|
@@ -578303,7 +578750,8 @@ var init_routes_v1 = __esm({
|
|
|
578303
578750
|
internalError: `${PROBLEM_BASE}/internal-error`,
|
|
578304
578751
|
payloadTooLarge: `${PROBLEM_BASE}/payload-too-large`,
|
|
578305
578752
|
notImplemented: `${PROBLEM_BASE}/not-implemented`,
|
|
578306
|
-
aimsViolation: `${PROBLEM_BASE}/aims-violation
|
|
578753
|
+
aimsViolation: `${PROBLEM_BASE}/aims-violation`,
|
|
578754
|
+
methodNotAllowed: `${PROBLEM_BASE}/method-not-allowed`
|
|
578307
578755
|
};
|
|
578308
578756
|
mcpManagerCache = /* @__PURE__ */ new Map();
|
|
578309
578757
|
memoryStoresCache = null;
|
|
@@ -585527,7 +585975,39 @@ function getOpenApiSpec() {
|
|
|
585527
585975
|
"/v1/evaluate": { post: { summary: "Evaluate a run by ID", tags: ["Agentic"], responses: { 200: { description: "Evaluation metrics" }, 404: { description: "Run not found" } } } },
|
|
585528
585976
|
"/v1/index": { post: { summary: "Trigger repository indexing", tags: ["Agentic"], responses: { 202: { description: "Indexing started" } } } },
|
|
585529
585977
|
// ───── P2: Tools / Hooks / Agents / Engines ─────
|
|
585530
|
-
"/v1/tools": {
|
|
585978
|
+
"/v1/tools": {
|
|
585979
|
+
get: {
|
|
585980
|
+
summary: "List agentic tool registry with security metadata",
|
|
585981
|
+
tags: ["Tools"],
|
|
585982
|
+
description: "Returns each tool's name, class, description, JSON-schema parameters, security policy {categories, risk, requires_scope, off_device_allowed, rationale}, and per-tool endpoint hrefs. Filters: ?category=read|write|exec|network|hardware|agent|sensitive · ?scope=read|run|admin · ?risk=low|medium|high|critical. Pagination via ?limit=N&offset=N (default limit=50; pass limit=200 to see all).",
|
|
585983
|
+
responses: { 200: { description: "Paginated tools with security metadata" } }
|
|
585984
|
+
}
|
|
585985
|
+
},
|
|
585986
|
+
"/v1/tools/{name}": {
|
|
585987
|
+
get: {
|
|
585988
|
+
summary: "Single tool schema + security policy",
|
|
585989
|
+
tags: ["Tools"],
|
|
585990
|
+
parameters: [{ name: "name", in: "path", required: true, schema: { type: "string" }, description: "Tool name (snake_case, e.g. file_read)" }],
|
|
585991
|
+
responses: {
|
|
585992
|
+
200: { description: "Tool entry with security info" },
|
|
585993
|
+
404: { description: "Unknown tool name" }
|
|
585994
|
+
}
|
|
585995
|
+
}
|
|
585996
|
+
},
|
|
585997
|
+
"/v1/tools/{name}/call": {
|
|
585998
|
+
post: {
|
|
585999
|
+
summary: "Execute a single tool directly (MCP-style, no agent loop)",
|
|
586000
|
+
tags: ["Tools"],
|
|
586001
|
+
description: "Body: {args: object, working_dir?: string}. Returns the tool's ToolResult {success, output, llmContent?, error?, durationMs} plus security metadata. Auth gating: request scope (read/run/admin) must satisfy tool.requires_scope; non-loopback callers need admin scope unless tool.off_device_allowed=true. Anonymous loopback callers default to admin scope; anonymous remote callers default to read scope.",
|
|
586002
|
+
parameters: [{ name: "name", in: "path", required: true, schema: { type: "string" } }],
|
|
586003
|
+
responses: {
|
|
586004
|
+
200: { description: "Tool result envelope" },
|
|
586005
|
+
403: { description: "Forbidden — scope insufficient or off-device gate" },
|
|
586006
|
+
404: { description: "Unknown tool" },
|
|
586007
|
+
500: { description: "Tool execution failed" }
|
|
586008
|
+
}
|
|
586009
|
+
}
|
|
586010
|
+
},
|
|
585531
586011
|
"/v1/hooks": { get: { summary: "List hook types and counts", tags: ["Tools"], responses: { 200: { description: "Hook registry" } } } },
|
|
585532
586012
|
"/v1/agents": { get: { summary: "List agent types", tags: ["Tools"], responses: { 200: { description: "Agent type registry" } } } },
|
|
585533
586013
|
"/v1/engines": { get: { summary: "List long-running engines", tags: ["Engines"], responses: { 200: { description: "Engine status + state files" } } } },
|
|
@@ -587573,13 +588053,25 @@ function handleHelp(req2, res) {
|
|
|
587573
588053
|
"DELETE /v1/runs/:id": "Cancel a running task"
|
|
587574
588054
|
},
|
|
587575
588055
|
tools_and_skills: {
|
|
587576
|
-
"GET /v1/tools": "List
|
|
588056
|
+
"GET /v1/tools": "List agentic tools w/ security metadata. Filters: ?category=read|write|exec|network|hardware|agent|sensitive ?scope=read|run|admin ?risk=low|medium|high|critical ?limit=N&offset=N",
|
|
588057
|
+
"GET /v1/tools/:name": "Get a single tool's schema + security policy (categories, risk, requires_scope, off_device_allowed)",
|
|
588058
|
+
"POST /v1/tools/:name/call": "Execute a single tool directly (MCP-style). Body: {args: object, working_dir?: string}. Auth-gated by per-tool scope; off-device callers need admin scope unless tool.off_device_allowed=true",
|
|
587577
588059
|
"GET /v1/skills": "List available skills (slash commands)",
|
|
587578
588060
|
"GET /v1/skills/:name": "Get skill details and content",
|
|
587579
588061
|
"GET /v1/commands": "List slash commands",
|
|
587580
588062
|
"POST /v1/commands/:cmd": "Execute a slash command",
|
|
587581
588063
|
"GET /v1/agents": "List available agent types"
|
|
587582
588064
|
},
|
|
588065
|
+
tool_security_model: {
|
|
588066
|
+
scopes: {
|
|
588067
|
+
read: "Pure inspection (file_read, web_search, list_directory, memory_read, ...)",
|
|
588068
|
+
run: "Local mutations + network outbound (file_write, shell, web_fetch, image_generate, ...)",
|
|
588069
|
+
admin: "Hardware peripherals, agent spawning, identity, tool registry mutations"
|
|
588070
|
+
},
|
|
588071
|
+
off_device_policy: "Tools with off_device_allowed=false are loopback-only; remote callers need admin scope to bypass.",
|
|
588072
|
+
anonymous_default: "Loopback callers default to admin scope. Remote callers default to read scope until authenticated.",
|
|
588073
|
+
override: "Operators can override per-tool classification via OA_TOOL_OVERRIDES env var (JSON map of name → partial security info)."
|
|
588074
|
+
},
|
|
587583
588075
|
mcp: {
|
|
587584
588076
|
"GET /v1/mcps": "List connected MCP (Model Context Protocol) servers",
|
|
587585
588077
|
"GET /v1/mcps/:name": "Get MCP server details and available tools",
|
|
@@ -589567,7 +590059,9 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
|
|
|
589567
590059
|
agentic: {
|
|
589568
590060
|
run: { href: `${baseUrl}/v1/run`, description: "Submit autonomous task." },
|
|
589569
590061
|
runs: { href: `${baseUrl}/v1/runs`, description: "List runs." },
|
|
589570
|
-
tools: { href: `${baseUrl}/v1/tools`, description: "Agentic tool registry." },
|
|
590062
|
+
tools: { href: `${baseUrl}/v1/tools`, description: "Agentic tool registry with security metadata. Filters: ?category=, ?scope=, ?risk=." },
|
|
590063
|
+
tool_schema: { href: `${baseUrl}/v1/tools/{name}`, description: "Single-tool schema + security policy." },
|
|
590064
|
+
tool_call: { href: `${baseUrl}/v1/tools/{name}/call`, description: "Execute a single tool directly (MCP-style). POST {args: object, working_dir?: string}. Auth-gated per-tool." },
|
|
589571
590065
|
mcps: { href: `${baseUrl}/v1/mcps`, description: "MCP server registry." }
|
|
589572
590066
|
},
|
|
589573
590067
|
memory: {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-agents-ai",
|
|
3
|
-
"version": "0.187.
|
|
3
|
+
"version": "0.187.491",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "open-agents-ai",
|
|
9
|
-
"version": "0.187.
|
|
9
|
+
"version": "0.187.491",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "CC-BY-NC-4.0",
|
|
12
12
|
"dependencies": {
|
package/package.json
CHANGED