@yemi33/minions 0.1.2216 → 0.1.2217
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/engine/cli.js +8 -2
- package/engine/consolidation.js +1 -1
- package/engine/lifecycle.js +1 -1
- package/engine/pipeline.js +48 -5
- package/package.json +1 -1
package/engine/cli.js
CHANGED
|
@@ -1255,7 +1255,10 @@ const commands = {
|
|
|
1255
1255
|
}
|
|
1256
1256
|
const control = getControl();
|
|
1257
1257
|
if (control.pid && control.pid !== process.pid) {
|
|
1258
|
-
|
|
1258
|
+
// killGracefully so the engine's SIGTERM graceful-shutdown handler runs
|
|
1259
|
+
// and recurses into the child tree (bare process.kill defaults to a
|
|
1260
|
+
// non-recursing SIGTERM that is Windows-broken — CLAUDE.md Footgun #4).
|
|
1261
|
+
shared.killGracefully({ pid: control.pid });
|
|
1259
1262
|
}
|
|
1260
1263
|
mutateControl(() => ({ state: 'stopped', stopped_at: e.ts() }));
|
|
1261
1264
|
e.log('info', 'Engine stopped');
|
|
@@ -1793,7 +1796,10 @@ const commands = {
|
|
|
1793
1796
|
let pidNum = NaN;
|
|
1794
1797
|
try { pidNum = shared.validatePid(raw); } catch { /* invalid — skip */ }
|
|
1795
1798
|
if (pidNum > 0) {
|
|
1796
|
-
|
|
1799
|
+
// killImmediate force-kills the whole process tree (bare process.kill
|
|
1800
|
+
// defaults to a non-recursing SIGTERM that is Windows-broken and
|
|
1801
|
+
// leaks child processes — CLAUDE.md Footgun #4).
|
|
1802
|
+
try { shared.killImmediate({ pid: pidNum }); console.log(`Killed process ${pidNum} (${fileName})`); }
|
|
1797
1803
|
catch { console.log(`Process ${pidNum} already dead`); }
|
|
1798
1804
|
} else {
|
|
1799
1805
|
console.log(`Skipping ${fileName}: invalid or empty PID`);
|
package/engine/consolidation.js
CHANGED
|
@@ -541,7 +541,7 @@ function reconcileAndAppendToAgentMemory(item, knownAgents, config) {
|
|
|
541
541
|
try { safeWrite(memPath + '.bak', beforeLock); }
|
|
542
542
|
catch (err) { log('warn', `agent-memory reconcile: backup write failed for ${agent}: ${err.message}`); }
|
|
543
543
|
|
|
544
|
-
const next = pruneAgentMemoryToBudget(updated + entry, agent);
|
|
544
|
+
const next = pruneAgentMemoryToBudget(updated + entry, agent, _pruneOptsFromConfig(config));
|
|
545
545
|
safeWrite(memPath, next);
|
|
546
546
|
reconciled = true;
|
|
547
547
|
const skippedCount = skipped.length;
|
package/engine/lifecycle.js
CHANGED
|
@@ -469,7 +469,7 @@ function archivePlan(planFile, plan, projects, config) {
|
|
|
469
469
|
const mdFiles = fs.readdirSync(PLANS_DIR).filter(f => f.endsWith('.md'));
|
|
470
470
|
for (const md of mdFiles) {
|
|
471
471
|
const mdContent = shared.safeRead(path.join(PLANS_DIR, md)) || '';
|
|
472
|
-
if (mdContent.includes(projectName) || mdContent.includes(plan.plan_summary?.slice(0, 40) || '___nomatch___')) {
|
|
472
|
+
if ((projectName && mdContent.includes(projectName)) || mdContent.includes(plan.plan_summary?.slice(0, 40) || '___nomatch___')) {
|
|
473
473
|
try {
|
|
474
474
|
const mdDest = shared.moveFileNoClobber(path.join(PLANS_DIR, md), planArchiveDir, md);
|
|
475
475
|
log('info', `Archived source plan: plans/archive/${path.basename(mdDest)}`);
|
package/engine/pipeline.js
CHANGED
|
@@ -126,6 +126,24 @@ function updateRunStage(pipelineId, runId, stageId, updates) {
|
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// Like updateRunStage, but creates the stage entry when the run never seeded it.
|
|
130
|
+
// startRun only seeds top-level `pipeline.stages` ids; nested parallel sub-stages
|
|
131
|
+
// live under `stage.stages`, so the run's `stages` map has no slot for them until
|
|
132
|
+
// the parallel executor persists one. updateRunStage no-ops on a missing entry, so
|
|
133
|
+
// the parallel path needs this upsert to make sub-stage state survive a disk
|
|
134
|
+
// round-trip (without it the cross-tick PARALLEL completion check reads each sub
|
|
135
|
+
// id as undefined forever and the parent wedges in RUNNING — P-9c1a4e7b).
|
|
136
|
+
function upsertRunStage(pipelineId, runId, stageId, state) {
|
|
137
|
+
mutatePipelineRuns((data) => {
|
|
138
|
+
const runs = data[pipelineId] || [];
|
|
139
|
+
const run = runs.find(r => r.runId === runId);
|
|
140
|
+
if (!run) return data;
|
|
141
|
+
if (!run.stages || typeof run.stages !== 'object') run.stages = {};
|
|
142
|
+
run.stages[stageId] = { ...(run.stages[stageId] || {}), ...state };
|
|
143
|
+
return data;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
129
147
|
function completeRun(pipelineId, runId, status) {
|
|
130
148
|
// Stages whose status is in this set are considered "terminal" and must not
|
|
131
149
|
// be touched by the run-close sweep. Anything else (RUNNING, PENDING,
|
|
@@ -829,12 +847,17 @@ function executeConditionStage(stage, stageState, run, pipeline, config) {
|
|
|
829
847
|
|
|
830
848
|
async function executeParallelStage(stage, stageState, run, pipeline, config) {
|
|
831
849
|
const subStages = stage.stages || [];
|
|
832
|
-
const subResults = {};
|
|
833
850
|
for (const sub of subStages) {
|
|
834
851
|
if (!run.stages[sub.id] || run.stages[sub.id].status === PIPELINE_STATUS.PENDING) {
|
|
835
852
|
const result = await executeStage(sub, run, pipeline, config);
|
|
836
|
-
|
|
837
|
-
run.stages[sub.id] =
|
|
853
|
+
const subState = { ...(run.stages[sub.id] || {}), ...result, startedAt: ts() };
|
|
854
|
+
run.stages[sub.id] = subState;
|
|
855
|
+
// Persist sub-stage state to disk. getActiveRun re-reads the run fresh from
|
|
856
|
+
// disk every tick, so a sub-stage mutated only on the in-memory run object is
|
|
857
|
+
// invisible to the next tick's PARALLEL completion check — the parent then
|
|
858
|
+
// wedges in RUNNING forever. updateRunStage only mutates an existing entry and
|
|
859
|
+
// sub-stage ids are never seeded by startRun, so upsert to seed + persist.
|
|
860
|
+
upsertRunStage(pipeline.id, run.runId, sub.id, subState);
|
|
838
861
|
}
|
|
839
862
|
}
|
|
840
863
|
// Parent is running until all subs complete
|
|
@@ -966,9 +989,29 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
966
989
|
return stageState.status === PIPELINE_STATUS.COMPLETED;
|
|
967
990
|
case STAGE_TYPE.PARALLEL: {
|
|
968
991
|
const subIds = artifacts.subStages || [];
|
|
992
|
+
if (subIds.length === 0) return true;
|
|
993
|
+
const subDefs = stage.stages || [];
|
|
969
994
|
return subIds.every(id => {
|
|
970
|
-
const
|
|
971
|
-
return
|
|
995
|
+
const subState = run.stages[id];
|
|
996
|
+
if (!subState) return false; // sub-stage state not yet persisted — not complete
|
|
997
|
+
if (subState.status === PIPELINE_STATUS.COMPLETED || subState.status === PIPELINE_STATUS.FAILED) return true;
|
|
998
|
+
// Async sub-stages (task/meeting/plan/…) persist as RUNNING; their underlying
|
|
999
|
+
// work (work items, meetings, …) finishes on a later tick. The driver never
|
|
1000
|
+
// re-executes a RUNNING parallel parent, so this completion check is the only
|
|
1001
|
+
// place that advances them: evaluate each sub through its own completion logic
|
|
1002
|
+
// and promote its persisted status so the run closes cleanly (no false
|
|
1003
|
+
// non-terminal anomaly) and the dashboard reflects reality (P-9c1a4e7b).
|
|
1004
|
+
const subDef = subDefs.find(s => s.id === id);
|
|
1005
|
+
if (!subDef) return false;
|
|
1006
|
+
if (isStageComplete(subDef, subState, run, config)) {
|
|
1007
|
+
subState.status = PIPELINE_STATUS.COMPLETED;
|
|
1008
|
+
subState.completedAt = subState.completedAt || ts();
|
|
1009
|
+
updateRunStage(run.pipelineId, run.runId, id, {
|
|
1010
|
+
status: PIPELINE_STATUS.COMPLETED, completedAt: subState.completedAt,
|
|
1011
|
+
});
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
1014
|
+
return false;
|
|
972
1015
|
});
|
|
973
1016
|
}
|
|
974
1017
|
default:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2217",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|