@yemi33/minions 0.1.417 → 0.1.419

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.419 (2026-04-06)
4
+
5
+ ### Features
6
+ - Fix 6 medium bugs: dispatch pruning, null guards, skill regex, meeting advancement, CLI PID check, pipeline retry
7
+ - Convert remaining lifecycle.js safeWrite calls to mutateJsonFileLocked
8
+
9
+ ### Fixes
10
+ - address review feedback — pipeline.js socket leak and magic numbers
11
+
3
12
  ## 0.1.417 (2026-04-06)
4
13
 
5
14
  ### Features
@@ -87,7 +87,7 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
87
87
  if (reason) item.reason = reason;
88
88
  if (resultSummary) item.resultSummary = resultSummary;
89
89
  delete item.prompt;
90
- if (dispatch.completed.length > 100) {
90
+ if (dispatch.completed.length >= 100) {
91
91
  dispatch.completed = dispatch.completed.slice(-100);
92
92
  }
93
93
  dispatch.completed.push(item);
@@ -901,6 +901,7 @@ function extractSkillsFromOutput(output, agentId, dispatchItem, config) {
901
901
  if (!fullText) fullText = output;
902
902
  const skillBlocks = [];
903
903
  const skillRegex = /```skill\s*\n([\s\S]*?)```/g;
904
+ skillRegex.lastIndex = 0;
904
905
  let match;
905
906
  while ((match = skillRegex.exec(fullText)) !== null) {
906
907
  skillBlocks.push(match[1].trim());
@@ -7,7 +7,8 @@
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
9
  const shared = require('./shared');
10
- const { safeJson, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS } = shared;
10
+ const { safeJson, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, ENGINE_DEFAULTS } = shared;
11
+ const http = require('http');
11
12
  const { parseCronExpr, shouldRunNow } = require('./scheduler');
12
13
 
13
14
  const PIPELINES_DIR = path.join(__dirname, '..', 'pipelines');
@@ -313,18 +314,34 @@ function executeApiStage(stage, stageState, run) {
313
314
  for (const call of calls) {
314
315
  const url = `http://localhost:${process.env.MINIONS_PORT || 7331}${call.endpoint}`;
315
316
  const body = typeof call.body === 'string' ? call.body : JSON.stringify(call.body || {});
316
- // Fire and forget — use Node's http module
317
- try {
318
- const http = require('http');
319
- const parsed = new URL(url);
320
- const req = http.request({
321
- hostname: parsed.hostname, port: parsed.port, path: parsed.pathname,
322
- method: call.method || 'POST',
323
- headers: { 'Content-Type': 'application/json' },
324
- });
325
- req.write(body);
326
- req.end();
327
- } catch (e) { log('warn', `Pipeline API call failed: ${e.message}`); }
317
+ const maxAttempts = ENGINE_DEFAULTS.pipelineApiRetries;
318
+ const retryDelay = ENGINE_DEFAULTS.pipelineApiRetryDelay;
319
+ const makeRequest = (attempt) => {
320
+ try {
321
+ const parsed = new URL(url);
322
+ const req = http.request({
323
+ hostname: parsed.hostname, port: parsed.port, path: parsed.pathname,
324
+ method: call.method || 'POST',
325
+ headers: { 'Content-Type': 'application/json' },
326
+ }, (res) => {
327
+ res.resume(); // drain body to free socket
328
+ if (res.statusCode >= 400) {
329
+ log('warn', `Pipeline API call to ${call.endpoint} returned ${res.statusCode} (attempt ${attempt})`);
330
+ if (attempt < maxAttempts) setTimeout(() => makeRequest(attempt + 1), retryDelay);
331
+ }
332
+ });
333
+ req.on('error', (err) => {
334
+ log('warn', `Pipeline API call to ${call.endpoint} failed: ${err.message} (attempt ${attempt})`);
335
+ if (attempt < maxAttempts) setTimeout(() => makeRequest(attempt + 1), retryDelay);
336
+ });
337
+ req.write(body);
338
+ req.end();
339
+ } catch (e) {
340
+ log('warn', `Pipeline API call to ${call.endpoint} threw: ${e.message} (attempt ${attempt})`);
341
+ if (attempt < maxAttempts) setTimeout(() => makeRequest(attempt + 1), retryDelay);
342
+ }
343
+ };
344
+ makeRequest(1);
328
345
  }
329
346
  return { status: PIPELINE_STATUS.COMPLETED, completedAt: ts() };
330
347
  }
package/engine/shared.js CHANGED
@@ -438,6 +438,8 @@ const ENGINE_DEFAULTS = {
438
438
  evalMaxIterations: 3, // max review→fix cycles before escalating to human
439
439
  evalMaxCost: null, // USD ceiling per work item across all eval iterations; null = no limit (gather baseline data first)
440
440
  maxRetries: 3, // max dispatch retries before marking work item as failed
441
+ pipelineApiRetries: 2, // max attempts for pipeline API calls
442
+ pipelineApiRetryDelay: 2000, // ms delay between pipeline API retries
441
443
  };
442
444
 
443
445
  // ─── Status & Type Constants ─────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.417",
3
+ "version": "0.1.419",
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"