llm-orchestrator 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/lib/flow-gate.mjs +26 -4
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-orchestrator",
|
|
3
3
|
"description": "Write /task once — it plans the work, shards it across parallel subagents, gates every phase and verifies before claiming done. Claude Code, Codex, OpenCode, Kilo.",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Bogdan-Gabriel Torcescu",
|
|
7
7
|
"url": "https://www.linkedin.com/in/bogdantorcescu/"
|
package/lib/flow-gate.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* tool inputs or file contents. `doctor` reads it back as the adherence audit.
|
|
15
15
|
*/
|
|
16
16
|
import { createHash, randomUUID } from 'node:crypto';
|
|
17
|
-
import { appendFile, mkdir, readdir, readFile, rename,
|
|
17
|
+
import { appendFile, mkdir, readdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
18
18
|
import { join } from 'node:path';
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -412,18 +412,37 @@ const LOCK_STALE_MS = 5000;
|
|
|
412
412
|
* an atomic mkdir lock; a lock older than LOCK_STALE_MS belongs to a crashed
|
|
413
413
|
* handler and is taken over. Failing to lock in time throws — the gate fails open.
|
|
414
414
|
*/
|
|
415
|
+
/** A lock whose recorded owner process no longer exists was left by a killed handler. */
|
|
416
|
+
async function ownerIsDead(lock) {
|
|
417
|
+
try {
|
|
418
|
+
const pid = Number.parseInt(await readFile(join(lock, 'owner'), 'utf8'), 10);
|
|
419
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
420
|
+
process.kill(pid, 0);
|
|
421
|
+
return false;
|
|
422
|
+
} catch (error) {
|
|
423
|
+
return error.code === 'ESRCH';
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function releaseLock(lock) {
|
|
428
|
+
await rm(lock, { recursive: true, force: true }).catch(() => {});
|
|
429
|
+
}
|
|
430
|
+
|
|
415
431
|
async function withSessionLock(path, work) {
|
|
416
432
|
const lock = `${path}.lock`;
|
|
417
433
|
const deadline = Date.now() + LOCK_WAIT_MS;
|
|
418
434
|
while (true) {
|
|
419
435
|
try {
|
|
420
436
|
await mkdir(lock);
|
|
437
|
+
await writeFile(join(lock, 'owner'), `${process.pid}\n`).catch(() => {});
|
|
421
438
|
break;
|
|
422
439
|
} catch (error) {
|
|
423
440
|
if (error.code !== 'EEXIST') throw error;
|
|
441
|
+
// Harnesses kill hooks that overrun their timeout; a lock they held must not
|
|
442
|
+
// swallow every event behind it until the stale timeout.
|
|
424
443
|
const held = await stat(lock).then((info) => Date.now() - info.mtimeMs).catch(() => 0);
|
|
425
|
-
if (held > LOCK_STALE_MS) {
|
|
426
|
-
await
|
|
444
|
+
if (held > LOCK_STALE_MS || await ownerIsDead(lock)) {
|
|
445
|
+
await releaseLock(lock);
|
|
427
446
|
continue;
|
|
428
447
|
}
|
|
429
448
|
if (Date.now() > deadline) throw new Error('ledger session is locked');
|
|
@@ -433,13 +452,16 @@ async function withSessionLock(path, work) {
|
|
|
433
452
|
try {
|
|
434
453
|
return await work();
|
|
435
454
|
} finally {
|
|
436
|
-
await
|
|
455
|
+
await releaseLock(lock);
|
|
437
456
|
}
|
|
438
457
|
}
|
|
439
458
|
|
|
440
459
|
export async function handleHook({ payload, project, now = Date.now(), cli = 'llm-orchestrator' }) {
|
|
441
460
|
const event = normalizePayload(payload);
|
|
442
461
|
if (event.kind === 'other') return null;
|
|
462
|
+
// Subagent tool calls change nothing (they work under the parent's run), so they
|
|
463
|
+
// take no lock and do no I/O — a busy subagent must not starve the parent's events.
|
|
464
|
+
if (event.kind === 'tool' && event.isSubagent) return null;
|
|
443
465
|
// An explicit run command opts the project in; anything else needs the project to use the core.
|
|
444
466
|
if (!event.run && !(await projectUsesOrchestrator(project))) return null;
|
|
445
467
|
const directory = await ensureLedger(project);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-orchestrator",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Write /task once — it plans the work, shards it across parallel subagents, gates every phase and verifies before claiming done. Claude Code, Codex, OpenCode, Kilo.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|