Files
gnome-jarvis/vendor/agent-harness/index.js
T
snxraven d47e9e260f
Rolling release / release (push) Failing after 1m49s
Updates
2026-09-12 19:36:06 -04:00

166 lines
5.1 KiB
JavaScript

/**
* Standalone agent harness. Local QVAC inference, sandbox tools, JSONL sessions.
*/
const path = require('path');
const { EventEmitter } = require('events');
const loop = require('./agent/loop.js');
const sessions = require('./agent/sessions.js');
const sandbox = require('./agent/sandbox.js');
const customTools = require('./agent/custom-tools.js');
const mcp = require('./agent/mcp.js');
const engine = require('./lib/qvac.js');
const catalog = require('./lib/catalog.js');
function emitBridge(emitter) {
return function emit(_kind, payload) {
const ev = payload || {};
emitter.emit('event', ev);
if (ev.type) emitter.emit(ev.type, ev);
};
}
function wrapSession(summary, opts) {
const emitter = new EventEmitter();
const emit = emitBridge(emitter);
const sessionId = summary.id;
function on(type, fn) {
emitter.on(type, fn);
return () => emitter.off(type, fn);
}
async function prompt(text, payload) {
loop.markLive(sessionId);
try {
const session = sessions.load(sessionId);
const jobId = 'job_' + Date.now().toString(36);
const merged = Object.assign(
{
permissionMode: opts.permissionMode || 'ask',
webFetch: opts.webFetch === true,
system: opts.system,
maxTurns: opts.maxTurns,
maxShellCalls: opts.maxShellCalls,
maxToolRounds: opts.maxToolRounds,
voice: opts.voice,
},
payload || {}
);
return await loop.runTurn({
session,
userText: text,
emit,
jobId,
payload: merged,
});
} finally {
loop.finishLive(sessionId);
}
}
async function compact() {
const session = sessions.load(sessionId);
const ctxSize = (engine.getLoaded() && engine.getLoaded().ctxSize) || 8192;
const toolDefs = [];
const compactOpts = {
budgetTokens: require('./agent/compaction.js').historyBudget(ctxSize, toolDefs, 0, 0, { voice: !!opts.voice }),
tools: toolDefs,
voice: !!opts.voice,
};
session.history = opts.voice
? require('./agent/compaction.js').compact(session.history, compactOpts)
: await require('./agent/compaction.js').compactWithLlm(session.history, Object.assign({}, compactOpts, {
complete: (o) => engine.complete(Object.assign({}, o, { desktopVision: false })),
}));
sessions.replaceHistory(sessionId, session.history);
return session.history;
}
return {
id: sessionId,
cwd: summary.cwd,
model: summary.model,
on,
prompt,
compact,
permit(jobId, toolCallId, decision) {
const d = decision === true || decision === 'allow' || decision === 'always' ? decision : 'deny';
loop.resolvePermission(jobId, toolCallId, d === true ? 'allow' : d);
},
answer(toolCallId, choice) {
loop.resolveAsk(null, toolCallId, choice);
},
planDecision(decision) {
loop.resolvePlanDecision(sessionId, decision);
},
addTool(tool) {
customTools.register(sessionId, tool);
const session = sessions.load(sessionId);
session.customTools = customTools.list(sessionId);
sessions.saveSummary(session);
return session.customTools;
},
cancel() {
loop.cancel(sessionId);
},
async dispose() {
loop.forget(sessionId);
customTools.clear(sessionId);
sessions.remove(sessionId);
},
load() {
return sessions.load(sessionId);
},
};
}
async function create(opts) {
opts = opts || {};
const cwd = path.resolve(opts.cwd || process.cwd());
const extraRoots = Array.isArray(opts.roots) ? opts.roots.map((r) => path.resolve(r)) : [];
sandbox.setGrants({ roots: [cwd].concat(extraRoots) });
const model = opts.model || 'qwen3.5-4b';
const summary = sessions.create({
origin: opts.origin || 'local',
cwd,
workspace: cwd,
model,
title: opts.title || path.basename(cwd),
hostWorkspace: opts.hostWorkspace !== false,
builtinTools: opts.builtinTools === false ? false : Array.isArray(opts.builtinTools) ? opts.builtinTools : undefined,
goal: opts.goal || null,
planMode: opts.planMode,
sessionId: opts.sessionId,
});
customTools.setSession(summary.id, { hostWorkspace: summary.hostWorkspace });
if (opts.tools) {
customTools.register(summary.id, opts.tools);
summary.customTools = customTools.list(summary.id);
sessions.saveSummary(summary);
}
if (Array.isArray(opts.mcp)) {
for (const spec of opts.mcp) await mcp.register(spec, opts);
}
return wrapSession(summary, opts);
}
function load(sessionId, opts) {
const summary = sessions.load(sessionId);
if (summary.cwd) sandbox.setGrants({ roots: [path.resolve(summary.cwd)].concat(opts && opts.roots ? opts.roots : []) });
if (summary.customTools) customTools.register(summary.id, summary.customTools);
customTools.setSession(summary.id, { hostWorkspace: summary.hostWorkspace });
return wrapSession(summary, opts || {});
}
module.exports = {
create,
load,
list: () => sessions.list(),
catalog: () => catalog.listCatalog(),
suggest: (hw) => catalog.suggestProfile(hw),
engine,
mcp,
sandbox,
};