66 lines
1.4 KiB
JavaScript
66 lines
1.4 KiB
JavaScript
/**
|
|
* Split a tool-call list into sequential vs parallel groups.
|
|
* Same-path write_file / search_replace share a lock key.
|
|
*/
|
|
|
|
const SEQUENTIAL = new Set([
|
|
'ask_user_question',
|
|
'exit_plan_mode',
|
|
'update_goal',
|
|
'enter_plan_mode',
|
|
'task',
|
|
'send_subagent_message',
|
|
'wait_tasks',
|
|
'kill_task',
|
|
]);
|
|
|
|
function isSequential(name) {
|
|
return SEQUENTIAL.has(String(name || ''));
|
|
}
|
|
|
|
function pathLockKey(name, args) {
|
|
args = args || {};
|
|
if (name === 'write_file' || name === 'search_replace') {
|
|
return String(args.path || args.file || '')
|
|
.replace(/\\/g, '/')
|
|
.replace(/\/+/g, '/');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function groups(calls) {
|
|
const out = [];
|
|
let current = null;
|
|
for (const call of calls || []) {
|
|
const seq = isSequential(call && call.name);
|
|
if (seq) {
|
|
if (current) {
|
|
out.push(current);
|
|
current = null;
|
|
}
|
|
out.push({ sequential: true, calls: [call] });
|
|
} else {
|
|
if (!current) current = { sequential: false, calls: [] };
|
|
current.calls.push(call);
|
|
}
|
|
}
|
|
if (current) out.push(current);
|
|
return out;
|
|
}
|
|
|
|
function withPathLock(locks, key, fn) {
|
|
if (!key) return Promise.resolve().then(fn);
|
|
const prev = locks.get(key) || Promise.resolve();
|
|
const curr = prev.then(fn, fn);
|
|
locks.set(
|
|
key,
|
|
curr.then(
|
|
() => {},
|
|
() => {}
|
|
)
|
|
);
|
|
return curr;
|
|
}
|
|
|
|
module.exports = { SEQUENTIAL, isSequential, pathLockKey, groups, withPathLock };
|