Actually vendor harness
This commit is contained in:
Vendored
+156
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 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,
|
||||
},
|
||||
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 = [];
|
||||
session.history = await require('./agent/compaction.js').compactWithLlm(session.history, {
|
||||
budgetTokens: require('./agent/compaction.js').historyBudget(ctxSize, toolDefs, 0),
|
||||
tools: toolDefs,
|
||||
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,
|
||||
};
|
||||
Reference in New Issue
Block a user