lib/project-background-tasks.js
Rama main · réplica de lectura del repositorio original.
// Read-only inspection of native background work through its existing query.
function remember(session, tasks) {
var generation = session._sdkQueryGeneration || 0;
var catalog = session.backgroundTaskCatalog;
if (!catalog || catalog.generation !== generation) catalog = { generation: generation, tasks: [] };
var next = (tasks || []).slice(0, 64).map(function (task) {
var old = catalog.tasks.find(function (item) { return item.task_id === task.task_id && String(item.run_id || '') === String(task.run_id || ''); });
return Object.assign(old || {}, task, { active: true });
});
catalog.tasks.forEach(function (old) {
if (!next.some(function (task) { return task.task_id === old.task_id; })) next.push(Object.assign(old, { active: false }));
});
catalog.tasks = next.slice(0, 64); session.backgroundTaskCatalog = catalog;
return catalog.tasks;
}
function observeMessage(session, parsed, send) {
if (!parsed || parsed.messageRole !== 'assistant' || !Array.isArray(parsed.content)) return false;
var id = session.taskIdMap && session.taskIdMap[parsed.parentToolUseId];
var catalog = session.backgroundTaskCatalog;
if (!id || !catalog || catalog.generation !== (session._sdkQueryGeneration || 0)) return false;
var task = catalog.tasks.find(function (item) { return item.task_id === id && item.active; });
if (!task) return false;
var text = parsed.content.filter(function (part) { return part && part.type === 'text' && typeof part.text === 'string'; }).map(function (part) { return part.text; }).join('\n').slice(0, 1200);
if (!text.trim()) return false;
var id = parsed.uuid || String(Date.now());
var preview = { id: id, text: text, vendor: session.vendor || null, timestamp: task.preview && task.preview.id === id ? task.preview.timestamp : Date.now() };
if (task.preview && task.preview.id === preview.id && task.preview.text === preview.text) return false;
task.preview = preview;
send(session, { type: 'background_task_preview', taskId: task.task_id, runId: task.run_id || null, sessionOriginId: session.sessionOriginId, preview: preview, live: true });
return true;
}
function attach(ctx) {
var pending = new WeakSet();
function authorized(ws, session) {
if (!session || ctx.getSessionForWs(ws) !== session || ws.readyState !== 1 || session.destroying) return false;
var users = ctx.usersModule;
if (!users.isMultiUser()) return !session.ownerId;
var user = ws._clayUser;
return !!(user && users.findUserById(user.id) && users.canAccessProject(user.id, (ctx.getProjectAccess ? ctx.getProjectAccess() : { visibility: "public" })) &&
users.canAccessSession(user.id, session, (ctx.getProjectAccess ? ctx.getProjectAccess() : { visibility: "public" })));
}
async function read(ws, msg) {
var session = ctx.getSessionForWs(ws), query = session && session.queryInstance;
var generation = session && session._sdkQueryGeneration;
var catalog = session && session.backgroundTaskCatalog;
var task = catalog && catalog.generation === (generation || 0) && catalog.tasks.find(function (item) {
return item.task_id === msg.taskId && String(item.run_id || '') === String(msg.runId || '');
});
var actor = ws._clayUser && ws._clayUser.id, cwd = session && session.cwd, owner = session && session.ownerId;
var osIdentity = session && ctx.getLinuxUserForSession ? ctx.getLinuxUserForSession(session) : null;
var requestId = typeof msg.requestId === 'string' ? msg.requestId.slice(0, 120) : null;
function current() { return authorized(ws, session) && ctx.sm.sessions.get(session.localId) === session &&
session.sessionOriginId === msg.sessionOriginId && session.queryInstance === query && session._sdkQueryGeneration === generation &&
session.cwd === cwd && session.ownerId === owner && (!ctx.getLinuxUserForSession || ctx.getLinuxUserForSession(session) === osIdentity) && (ws._clayUser && ws._clayUser.id) === actor && session.backgroundTaskCatalog === catalog &&
catalog.tasks.indexOf(task) >= 0; }
function reply(data) { if (authorized(ws, session)) ctx.sendTo(ws, Object.assign({ type: 'background_task_read', requestId: requestId,
taskId: msg.taskId, runId: msg.runId || null, sessionOriginId: msg.sessionOriginId }, data)); }
if (!requestId || !task || !current() || !query || msg.type === 'background_task_read' && typeof query.readBackgroundTask !== 'function') {
reply({ error: 'This task is unavailable in the current session.' }); return;
}
var reading = msg.type === 'background_task_read';
if (reading && pending.has(ws)) { reply({ error: 'A task read is already in progress.' }); return; }
if (reading) pending.add(ws);
try {
var data;
if (msg.type === 'background_task_stop') {
if (!task.active || typeof query.stopTask !== 'function') throw new Error('Task is no longer running.');
await query.stopTask(task.task_id); data = { stopRequested: true };
} else data = await query.readBackgroundTask({ taskId: task.task_id, runId: task.run_id || null, offset: msg.offset });
if (current()) reply({ data: data });
} catch (error) { if (current()) reply({ error: 'Task transcript or output is not available yet.' }); }
finally { if (reading) pending.delete(ws); }
}
return { handleMessage: function (ws, msg) { if (msg.type !== 'background_task_read' && msg.type !== 'background_task_stop') return false; read(ws, msg); return true; } };
}
module.exports = { attach: attach, remember: remember, observeMessage: observeMessage };