Files
gnome-jarvis/vendor/agent-harness/agent/custom-tools.js
T
snxraven a4073b9020
Rolling release / release (push) Successful in 6m36s
Check Point
2026-09-12 09:07:09 -04:00

153 lines
4.2 KiB
JavaScript

/**
* Page-registered custom tools (schemas only; handlers stay in the page).
* No Bare imports — unit-testable on Node.
*/
const toolSet = require('./tool-set.js');
const NAME_RE = /^[a-zA-Z][a-zA-Z0-9_]{0,63}$/;
const MAX_TOOLS = 32;
const MAX_DESC = 2000;
const bySession = new Map();
const sessionOpts = new Map();
const handlers = new Map();
const permissions = new Map();
function setReserved(names) {
reserved = new Set(names);
}
let reserved = new Set(toolSet.ALWAYS_RESERVED.concat(toolSet.ALWAYS_BUILTIN_RESERVED));
function setSession(sessionId, opts) {
if (!sessionId) return;
sessionOpts.set(sessionId, {
hostWorkspace: toolSet.parseHostWorkspace(opts),
});
}
function isReserved(name, sessionId) {
if (reserved.has(name)) return true;
if (!toolSet.isHostWorkspaceTool(name)) return false;
const flags = sessionId ? sessionOpts.get(sessionId) : null;
if (flags && flags.hostWorkspace === false) return false;
return true;
}
function normalizeSchema(raw, sessionId) {
if (!raw || typeof raw !== 'object') throw new Error('tool schema required');
let name = raw.name;
let description = raw.description;
let parameters = raw.parameters;
if (raw.type === 'function' && raw.function) {
name = raw.function.name;
description = raw.function.description;
parameters = raw.function.parameters;
}
if (!NAME_RE.test(String(name || ''))) throw new Error('invalid tool name');
if (isReserved(name, sessionId)) throw new Error('tool name is reserved: ' + name);
const desc = String(description || '').slice(0, MAX_DESC);
let params = parameters && typeof parameters === 'object' ? parameters : { type: 'object', properties: {} };
if (params.type && params.type !== 'object') {
throw new Error('tool parameters must be a JSON object schema');
}
return {
type: 'function',
name,
description: desc || name,
parameters: {
type: 'object',
properties: params.properties && typeof params.properties === 'object' ? params.properties : {},
required: Array.isArray(params.required) ? params.required.map(String) : undefined,
},
};
}
function list(sessionId) {
const m = bySession.get(sessionId);
return m ? Array.from(m.values()) : [];
}
function register(sessionId, tools) {
if (!sessionId) throw new Error('sessionId required');
const arr = Array.isArray(tools) ? tools : [tools];
let map = bySession.get(sessionId);
if (!map) {
map = new Map();
bySession.set(sessionId, map);
}
const out = [];
for (const t of arr) {
const schema = normalizeSchema(t, sessionId);
if (map.size >= MAX_TOOLS && !map.has(schema.name)) {
throw new Error('too many custom tools (max ' + MAX_TOOLS + ')');
}
map.set(schema.name, schema);
let gates = permissions.get(sessionId);
if (!gates) permissions.set(sessionId, gates = new Map());
gates.set(schema.name, t.permission || 'write');
if (typeof t.execute === 'function') {
let h = handlers.get(sessionId);
if (!h) {
h = new Map();
handlers.set(sessionId, h);
}
h.set(schema.name, t.execute);
}
out.push(schema);
}
return list(sessionId);
}
function unregister(sessionId, name) {
const map = bySession.get(sessionId);
if (map && name) map.delete(name);
const h = handlers.get(sessionId);
if (h && name) h.delete(name);
permissions.get(sessionId)?.delete(name);
return list(sessionId);
}
function clear(sessionId) {
bySession.delete(sessionId);
sessionOpts.delete(sessionId);
handlers.delete(sessionId);
permissions.delete(sessionId);
}
function has(sessionId, name) {
const map = bySession.get(sessionId);
return !!(map && map.has(name));
}
function getHandler(sessionId, name) {
const h = handlers.get(sessionId);
return h && h.get(name);
}
function needsPermission(sessionId, name, mode) {
return mode !== 'always-approve' && has(sessionId, name) && permissions.get(sessionId)?.get(name) !== 'read';
}
function defs(sessionId) {
return list(sessionId);
}
module.exports = {
needsPermission,
NAME_RE,
MAX_TOOLS,
setReserved,
setSession,
isReserved,
normalizeSchema,
register,
unregister,
list,
clear,
has,
getHandler,
defs,
};