@yemi33/minions 0.1.1974 → 0.1.1976
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/dashboard/js/render-work-items.js +29 -0
- package/dashboard/styles.css +1 -0
- package/docs/managed-spawn.md +9 -0
- package/engine/managed-spawn.js +51 -1
- package/engine.js +63 -4
- package/package.json +1 -1
|
@@ -51,6 +51,15 @@ function wiRow(item) {
|
|
|
51
51
|
(item._reopened ? ' <span style="font-size:9px;color:var(--purple);margin-left:4px" title="This item was reopened from a previously completed state">reopened</span>' : '') +
|
|
52
52
|
(item._pendingReason && item.status === 'pending' && item._pendingReason !== 'already_dispatched' ? ' <span style="font-size:9px;color:var(--muted);margin-left:4px" title="Pending reason: ' + escapeHtml(item._pendingReason) + '">' + escapeHtml(item._pendingReason.replace(/_/g, ' ')) + '</span>' : '') +
|
|
53
53
|
(item._pendingReason === 'already_dispatched' && item.status === 'pending' ? ' <span style="font-size:9px;color:var(--blue);margin-left:4px" title="In dispatch queue, waiting to be assigned">queued</span>' : '') +
|
|
54
|
+
(item._managedSpawnPartial && Array.isArray(item._managedSpawnPartial.failed) && item._managedSpawnPartial.failed.length
|
|
55
|
+
? ' <span style="font-size:9px;color:var(--yellow);margin-left:4px;border:1px solid var(--yellow);padding:0 4px;border-radius:6px" title="managed-spawn: '
|
|
56
|
+
+ escapeHtml(((item._managedSpawnPartial.healthy || []).length) + '/' + (((item._managedSpawnPartial.healthy || []).length) + item._managedSpawnPartial.failed.length))
|
|
57
|
+
+ ' healthy — failed: '
|
|
58
|
+
+ escapeHtml(item._managedSpawnPartial.failed.map(function(f){ return (f && f.name) || '?'; }).join(', '))
|
|
59
|
+
+ '. Click row for details.">⚠ managed-spawn: '
|
|
60
|
+
+ escapeHtml(((item._managedSpawnPartial.healthy || []).length) + '/' + (((item._managedSpawnPartial.healthy || []).length) + item._managedSpawnPartial.failed.length))
|
|
61
|
+
+ ' healthy</span>'
|
|
62
|
+
: '') +
|
|
54
63
|
(item._skipReason && item.status === 'pending' ? ' <span style="font-size:9px;color:var(--yellow);margin-left:4px" title="Dispatch blocked: ' + escapeHtml(item._skipReason) + (item._blockedBy ? ' (by ' + escapeHtml(item._blockedBy) + ')' : '') + '">' + escapeHtml(item._skipReason.replace(/_/g, ' ')) + (item._blockedBy ? ' <span style="color:var(--muted)">(' + escapeHtml(item._blockedBy) + ')</span>' : '') + '</span>' : '') +
|
|
55
64
|
(item.status === 'failed' ? ' ' + wiRetryBtn(item) : '') +
|
|
56
65
|
'</td>' +
|
|
@@ -472,6 +481,26 @@ function openWorkItemDetail(id) {
|
|
|
472
481
|
if (item.completedAt) html += field('Completed', escapeHtml(formatLocalDateTime(item.completedAt)));
|
|
473
482
|
if (item.failReason) html += field('Failure Reason', '<span style="color:var(--red)">' + escapeHtml(item.failReason) + '</span>');
|
|
474
483
|
if (item._pendingReason && item.status === 'pending') html += field('Pending Reason', item._pendingReason === 'already_dispatched' ? 'Queued — waiting for available agent slot' : escapeHtml(item._pendingReason.replace(/_/g, ' ')));
|
|
484
|
+
if (item._managedSpawnPartial && Array.isArray(item._managedSpawnPartial.failed) && item._managedSpawnPartial.failed.length) {
|
|
485
|
+
var _msp = item._managedSpawnPartial;
|
|
486
|
+
var _mspHealthy = Array.isArray(_msp.healthy) ? _msp.healthy : [];
|
|
487
|
+
var _mspTotal = _mspHealthy.length + _msp.failed.length;
|
|
488
|
+
var _mspBody = '<div style="color:var(--yellow);font-size:11px;margin-bottom:6px">'
|
|
489
|
+
+ escapeHtml(_mspHealthy.length + '/' + _mspTotal) + ' specs healthy. Failed: '
|
|
490
|
+
+ escapeHtml(_msp.failed.map(function(f){ return (f && f.name) || '?'; }).join(', '))
|
|
491
|
+
+ (_msp.evaluated_at ? ' <span style="color:var(--muted)">(evaluated ' + escapeHtml(_msp.evaluated_at) + ')</span>' : '')
|
|
492
|
+
+ '</div>';
|
|
493
|
+
_mspBody += _msp.failed.map(function(f) {
|
|
494
|
+
var name = escapeHtml((f && f.name) || '?');
|
|
495
|
+
var reason = escapeHtml((f && f.reason) || 'unknown');
|
|
496
|
+
var tail = (f && f.log_tail) || '';
|
|
497
|
+
return '<details style="margin-bottom:6px"><summary style="cursor:pointer;font-size:11px"><strong>' + name + '</strong> — ' + reason + '</summary>'
|
|
498
|
+
+ '<pre style="font-size:10px;max-height:240px;overflow:auto;padding:6px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);margin:4px 0 0 0">'
|
|
499
|
+
+ escapeHtml(tail || '(no log tail captured)')
|
|
500
|
+
+ '</pre></details>';
|
|
501
|
+
}).join('');
|
|
502
|
+
html += field('Managed-spawn partial failure', _mspBody);
|
|
503
|
+
}
|
|
475
504
|
if (item._skipReason && item.status === 'pending') html += field('Dispatch Blocked', '<span style="color:var(--yellow)">' + escapeHtml(item._skipReason.replace(/_/g, ' ')) + '</span>' + (item._blockedBy ? ' — blocked by <strong>' + escapeHtml(item._blockedBy) + '</strong>' : ''));
|
|
476
505
|
// Defensive: CC dispatches can land here with these fields as strings
|
|
477
506
|
// (e.g. acceptanceCriteria: "fix the login bug"). Coerce to arrays so
|
package/dashboard/styles.css
CHANGED
|
@@ -571,6 +571,7 @@
|
|
|
571
571
|
.dispatch-type.decompose { background: rgba(188,140,255,0.2); color: var(--purple); }
|
|
572
572
|
.dispatch-type.manual { background: rgba(139,148,158,0.15); color: var(--muted); }
|
|
573
573
|
.dispatch-type.docs { background: rgba(56,189,248,0.15); color: #38bdf8; }
|
|
574
|
+
.dispatch-type.setup { background: rgba(20,184,166,0.15); color: #14b8a6; }
|
|
574
575
|
.dispatch-agent { font-weight: 600; color: var(--text); }
|
|
575
576
|
.dispatch-task { flex: 1; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
576
577
|
.dispatch-time { font-size: var(--text-sm); color: var(--border); }
|
package/docs/managed-spawn.md
CHANGED
|
@@ -59,6 +59,14 @@ The sidecar lives at `<MINIONS_DIR>/agents/<agentId>/managed-spawn.json` and is
|
|
|
59
59
|
|
|
60
60
|
The renderer in [`buildManagedSpawnHint`](../engine/managed-spawn.js) at `engine/managed-spawn.js:419` emits this exact shape (with allowlist + cap reminders) into the agent's prompt whenever the work item has `meta.managed_spawn: true`. Treat the rendered hint as the source of truth — if this doc and the hint drift, the hint wins.
|
|
61
61
|
|
|
62
|
+
### Smoke-test before writing the sidecar (mandatory, W-mpbpexrg00110661)
|
|
63
|
+
|
|
64
|
+
The hint mandates that agents run each `cmd args` in the declared `cwd` for at least 5 seconds AND confirm the healthcheck endpoint returns the expected status BEFORE writing `managed-spawn.json`. Sidecars written from guessed-at commands are how dispatches silently lose specs: the engine spawns what the agent declared; if the command crashes on first launch, the engine kills the spec, removes it from state, and (for partial failures) marks the WI with `_managedSpawnPartial` rather than demoting it — the agent's primary work succeeded by validator standards. The cost of guessing wrong is the operator finding out hours later that half the stack is down.
|
|
65
|
+
|
|
66
|
+
Real failure class to internalize: workspace-filter resolution (`bun -F <name>`, `pnpm --filter <name>`) failing because workspace deps were never installed in the worktree. Exits 1 in <1s with `No packages matched the filter`. Smoke-test catches it in seconds; an operator finding it catches it in hours (incident W-mpbolwvt000gb9b1: 1 of 3 Constellation specs survived, WI showed `done`).
|
|
67
|
+
|
|
68
|
+
Both a PowerShell (Windows) and bash (macOS/Linux) example are inlined in the hint — see `buildManagedSpawnHint` for the canonical patterns.
|
|
69
|
+
|
|
62
70
|
## Healthcheck examples
|
|
63
71
|
|
|
64
72
|
### HTTP — most common
|
|
@@ -220,6 +228,7 @@ All knobs live under `engine.managedSpawn` in `engine/shared.js:1500` (`ENGINE_D
|
|
|
220
228
|
|---|---|---|
|
|
221
229
|
| Dispatch ERROR `failure_class: invalid-managed-spawn` | Sidecar schema/allowlist violation | Read inbox alert; the validator includes a precise reason. Non-retryable — fix and re-dispatch. |
|
|
222
230
|
| Dispatch ERROR `failure_class: managed-spawn-healthcheck` | `timeout_s` elapsed before any spec became healthy | Check `engine/managed-logs/<name>.log` for the child's crash output. Sibling spawns are left alive. Retryable. |
|
|
231
|
+
| WI shows yellow `⚠ managed-spawn: N/M healthy` chip on dashboard | Partial healthcheck failure: ≥1 spec passed, ≥1 failed. WI keeps `status: done` (the agent's primary work — getting an accepted sidecar — succeeded); annotation `_managedSpawnPartial = { healthy, failed[{name, reason, log_tail}], evaluated_at }` lives on the WI. Click the row to see per-spec failure reasons + last 20 log lines. Dispatch is still recorded ERROR with `failure_class: managed-spawn-healthcheck-failed`. Restart the failing spec via `POST /api/managed-processes/restart` once you've fixed the root cause (often: workspace deps not installed; see smoke-test rule in the hint). W-mpbpexrg00110661. |
|
|
223
232
|
| Spec gone after `minions restart` | Bun child died with parent (the original failure mode) | Should be fixed by item 2's detached-spawn pattern. If it recurs, verify `bin/minions.js spawnDashboard` semantics still work for the runtime — that's the canonical reference. |
|
|
224
233
|
| Spec listed `alive: true, healthy: false` for >30s | Healthcheck loop self-detected service degradation | The spec did not pass a subsequent healthcheck. Inspect the service; restart via API once recovered. |
|
|
225
234
|
| Stale row sticks around with dead PID | Spec killed outside Minions | Wait one sweep cycle (~30 min) or call `POST /api/managed-processes/kill` manually. |
|
package/engine/managed-spawn.js
CHANGED
|
@@ -480,7 +480,7 @@ function buildManagedSpawnHint(opts) {
|
|
|
480
480
|
'1. Reads your sidecar after you exit.',
|
|
481
481
|
'2. Spawns each spec detached (the working Windows pattern is centralised in the engine — you do **not** need to write `Start-Process` or `spawn({ detached: true })` yourself).',
|
|
482
482
|
'3. Drives the healthcheck loop until each spec passes its first check (within `timeout_s`).',
|
|
483
|
-
'4. **Fails this dispatch (ERROR) if any spec fails its healthcheck.** Surviving siblings stay alive; failing PIDs are killed.',
|
|
483
|
+
'4. **Fails this dispatch (ERROR) if any spec fails its healthcheck.** Surviving siblings stay alive; failing PIDs are killed. When at least one spec stays healthy (partial failure) the engine also writes a `_managedSpawnPartial` annotation onto the WI (status stays `done`) so the dashboard surfaces a warning chip instead of silently swallowing the failure. When ALL specs fail the WI is demoted to FAILED via the normal force-demote path.',
|
|
484
484
|
'5. Auto-injects a `## Live managed processes` block into downstream agents\' prompts (scoped to your project) so the next dispatch can find the service without you telling it.',
|
|
485
485
|
'6. Sweeps dead PIDs / TTL-expired specs every ' + (limits.sweepEvery || 30) + ' ticks; kills + unlinks at TTL.',
|
|
486
486
|
'',
|
|
@@ -496,6 +496,56 @@ function buildManagedSpawnHint(opts) {
|
|
|
496
496
|
'',
|
|
497
497
|
'If your file is invalid the engine marks this dispatch ERROR with `failure_class: invalid-managed-spawn` (non-retryable) — fix the file shape, don\'t retry blindly.',
|
|
498
498
|
'',
|
|
499
|
+
'### Mandatory: smoke-test before writing the sidecar',
|
|
500
|
+
'',
|
|
501
|
+
'Before writing `managed-spawn.json`, run each `cmd args` in the declared `cwd` for at least 5 seconds. Confirm the process stays alive AND its healthcheck endpoint returns the expected status. Kill the test process. THEN write the sidecar. Sidecars written from guessed-at commands are how dispatches silently lose specs — the engine spawns what you declared; if you declared a command that crashes, the engine kills it and removes it from state without alerting the WI as failed. The cost of guessing wrong is the user finding out hours later that half the stack is down.',
|
|
502
|
+
'',
|
|
503
|
+
'Concrete failure class to avoid: workspace-filter resolution (e.g. `bun -F <name>` or `pnpm --filter <name>`) failing because the workspace deps were never installed in the worktree. The command exits 1 in <1s with `No packages matched the filter` and the engine kills the spec without surfacing anything beyond a partial-healthcheck annotation. Smoke-test catches this in seconds; the user finding out catches it in hours.',
|
|
504
|
+
'',
|
|
505
|
+
'Smoke-test pattern (PowerShell — Windows):',
|
|
506
|
+
'',
|
|
507
|
+
'```powershell',
|
|
508
|
+
'# Replace <cwd>, <cmd>, <args...>, <healthcheck-url>, <expected-status> with your spec values.',
|
|
509
|
+
'Push-Location <cwd>',
|
|
510
|
+
'$proc = Start-Process -FilePath <cmd> -ArgumentList \'<arg1>\',\'<arg2>\' -PassThru -WindowStyle Hidden -RedirectStandardOutput smoke.out -RedirectStandardError smoke.err',
|
|
511
|
+
'try {',
|
|
512
|
+
' $deadline = (Get-Date).AddSeconds(15)',
|
|
513
|
+
' $healthy = $false',
|
|
514
|
+
' while ((Get-Date) -lt $deadline) {',
|
|
515
|
+
' if ($proc.HasExited) { throw "spec exited early with code $($proc.ExitCode); tail smoke.err for the reason" }',
|
|
516
|
+
' try {',
|
|
517
|
+
' $r = Invoke-WebRequest -Uri \'<healthcheck-url>\' -UseBasicParsing -TimeoutSec 2',
|
|
518
|
+
' if ($r.StatusCode -eq <expected-status>) { $healthy = $true; break }',
|
|
519
|
+
' } catch { Start-Sleep -Milliseconds 500 }',
|
|
520
|
+
' }',
|
|
521
|
+
' if (-not $healthy) { throw "spec stayed alive but healthcheck never returned <expected-status> within 15s" }',
|
|
522
|
+
' Write-Host "smoke-test OK — spec stayed alive AND healthcheck passed"',
|
|
523
|
+
'} finally {',
|
|
524
|
+
' if (-not $proc.HasExited) { Stop-Process -Id $proc.Id -Force }',
|
|
525
|
+
' Pop-Location',
|
|
526
|
+
'}',
|
|
527
|
+
'```',
|
|
528
|
+
'',
|
|
529
|
+
'Smoke-test pattern (bash — macOS/Linux):',
|
|
530
|
+
'',
|
|
531
|
+
'```bash',
|
|
532
|
+
'# Replace <cwd>, <cmd>, <args...>, <healthcheck-url>, <expected-status> with your spec values.',
|
|
533
|
+
'cd <cwd>',
|
|
534
|
+
'<cmd> <arg1> <arg2> >smoke.out 2>smoke.err &',
|
|
535
|
+
'pid=$!',
|
|
536
|
+
'healthy=0',
|
|
537
|
+
'for _ in $(seq 1 15); do',
|
|
538
|
+
' if ! kill -0 "$pid" 2>/dev/null; then echo "spec exited early; tail smoke.err for the reason" >&2; break; fi',
|
|
539
|
+
' if [ "$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 2 \'<healthcheck-url>\')" = "<expected-status>" ]; then healthy=1; break; fi',
|
|
540
|
+
' sleep 1',
|
|
541
|
+
'done',
|
|
542
|
+
'kill "$pid" 2>/dev/null || true',
|
|
543
|
+
'wait "$pid" 2>/dev/null || true',
|
|
544
|
+
'[ "$healthy" = 1 ] && echo "smoke-test OK — spec stayed alive AND healthcheck passed" || { echo "smoke-test FAILED" >&2; exit 1; }',
|
|
545
|
+
'```',
|
|
546
|
+
'',
|
|
547
|
+
'A passing smoke-test is the entry gate to writing the sidecar — not a nice-to-have. If you skip it, you are betting the WI completion against a command you never ran.',
|
|
548
|
+
'',
|
|
499
549
|
'### Verify before exit',
|
|
500
550
|
'',
|
|
501
551
|
'After you write the file, query the engine to confirm acceptance:',
|
package/engine.js
CHANGED
|
@@ -2393,12 +2393,35 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
2393
2393
|
try { managedSpawn.removeManagedSpec(f.name); }
|
|
2394
2394
|
catch (e) { log('warn', `managed-spawn healthcheck: cleanup failed for ${f.name}: ${e.message}`); }
|
|
2395
2395
|
}
|
|
2396
|
+
// W-mpbpexrg00110661 — capture per-spec log tails (last 20 lines) so
|
|
2397
|
+
// the WI annotation can surface them in the dashboard chip without
|
|
2398
|
+
// re-reading the log files later. Use the same tailManagedLog helper
|
|
2399
|
+
// the inbox alert uses; cap each tail to 2KB to bound the WI write.
|
|
2400
|
+
const _failedDetails = failed.map(f => {
|
|
2401
|
+
let tail = '';
|
|
2402
|
+
try { tail = (managedSpawn.tailManagedLog(f.name, 20) || '').slice(-2048); }
|
|
2403
|
+
catch (_e) { tail = ''; }
|
|
2404
|
+
return { name: f.name, reason: f.error, log_tail: tail };
|
|
2405
|
+
});
|
|
2406
|
+
const _survivedNames = items.filter(it => !failed.some(f => f.name === it.name)).map(it => it.name);
|
|
2396
2407
|
managedSpawnHealthcheckFailure = {
|
|
2397
2408
|
failed: failed,
|
|
2398
|
-
survivedNames:
|
|
2409
|
+
survivedNames: _survivedNames,
|
|
2410
|
+
// partial = at least one spec survived. Drives the
|
|
2411
|
+
// processWorkItemFailure: false override below so the WI stays
|
|
2412
|
+
// `done` (the agent's primary work — declaring an accepted sidecar
|
|
2413
|
+
// — succeeded; the dashboard surfaces the warning via
|
|
2414
|
+
// _managedSpawnPartial instead of demoting to FAILED).
|
|
2415
|
+
partial: _survivedNames.length > 0,
|
|
2416
|
+
annotation: {
|
|
2417
|
+
healthy: _survivedNames,
|
|
2418
|
+
failed: _failedDetails,
|
|
2419
|
+
evaluated_at: new Date().toISOString(),
|
|
2420
|
+
},
|
|
2399
2421
|
};
|
|
2400
|
-
log('warn', `managed-spawn healthcheck: ${failed.length}/${items.length} spec(s) failed for ${agentId} (${id})
|
|
2401
|
-
|
|
2422
|
+
log('warn', `managed-spawn healthcheck: ${failed.length}/${items.length} spec(s) failed for ${agentId} (${id})` +
|
|
2423
|
+
(managedSpawnHealthcheckFailure.partial ? ' (partial — WI stays done, annotated)' : ' (total — WI will be demoted)') +
|
|
2424
|
+
`; ` + failed.map(f => `${f.name}=${f.error}`).join('; '));
|
|
2402
2425
|
try {
|
|
2403
2426
|
const wiId = dispatchItem.meta?.item?.id || '';
|
|
2404
2427
|
const logTails = failed.map(f => {
|
|
@@ -2468,7 +2491,18 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
2468
2491
|
: FAILURE_CLASS.INVALID_KEEP_PROCESSES_SCHEMA)
|
|
2469
2492
|
: null;
|
|
2470
2493
|
const completeOpts = managedSpawnHealthcheckFail
|
|
2471
|
-
? {
|
|
2494
|
+
? {
|
|
2495
|
+
...completionOpts,
|
|
2496
|
+
failureClass: FAILURE_CLASS.MANAGED_SPAWN_HEALTHCHECK_FAILED,
|
|
2497
|
+
agentRetryable: false,
|
|
2498
|
+
// W-mpbpexrg00110661 — partial healthcheck failure: at least one spec
|
|
2499
|
+
// stayed healthy. The agent's primary work (declaring + getting an
|
|
2500
|
+
// accepted sidecar) succeeded, so don't force-demote the WI. The
|
|
2501
|
+
// _managedSpawnPartial annotation written after completeDispatch is
|
|
2502
|
+
// the visibility lever. Total failure (no survivors) still
|
|
2503
|
+
// force-demotes through the normal FORCE_DEMOTE_FAILURE_CLASSES path.
|
|
2504
|
+
...(managedSpawnHealthcheckFailure.partial ? { processWorkItemFailure: false } : {}),
|
|
2505
|
+
}
|
|
2472
2506
|
: (managedSpawnAcceptanceFail
|
|
2473
2507
|
? { ...completionOpts, failureClass: FAILURE_CLASS.INVALID_MANAGED_SPAWN, agentRetryable: false }
|
|
2474
2508
|
: (keepProcessesAcceptanceFail
|
|
@@ -2581,6 +2615,31 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
2581
2615
|
|
|
2582
2616
|
completeDispatch(id, effectiveResult, errorReason, resultSummary, completeOpts);
|
|
2583
2617
|
|
|
2618
|
+
// W-mpbpexrg00110661 — surface managed-spawn partial-healthcheck failures
|
|
2619
|
+
// on the WI so the dashboard renders a warning chip instead of silently
|
|
2620
|
+
// swallowing the loss. This runs regardless of partial vs total failure:
|
|
2621
|
+
// - Partial (survivors > 0): WI status stayed `done` because we passed
|
|
2622
|
+
// processWorkItemFailure:false above; the annotation tells operators
|
|
2623
|
+
// half the stack is down.
|
|
2624
|
+
// - Total (no survivors): WI was force-demoted to FAILED by the
|
|
2625
|
+
// FORCE_DEMOTE_FAILURE_CLASSES path; the annotation still attaches
|
|
2626
|
+
// for forensics so the dashboard can show which specs failed and
|
|
2627
|
+
// their log tails without operators having to dig through
|
|
2628
|
+
// engine/managed-logs/.
|
|
2629
|
+
if (managedSpawnHealthcheckFailure && managedSpawnHealthcheckFailure.annotation && dispatchItem.meta?.item?.id) {
|
|
2630
|
+
try {
|
|
2631
|
+
const wiPath = resolveWorkItemPath(dispatchItem.meta);
|
|
2632
|
+
if (wiPath) {
|
|
2633
|
+
mutateWorkItems(wiPath, items => {
|
|
2634
|
+
const wi = items.find(i => i.id === dispatchItem.meta.item.id);
|
|
2635
|
+
if (!wi) return items;
|
|
2636
|
+
wi._managedSpawnPartial = managedSpawnHealthcheckFailure.annotation;
|
|
2637
|
+
return items;
|
|
2638
|
+
});
|
|
2639
|
+
}
|
|
2640
|
+
} catch (e) { log('warn', `managed-spawn partial-healthcheck: failed to annotate WI: ${e.message}`); }
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2584
2643
|
// W-mp6k7ywi000fa33c / W-mp7i902u000l991f — surface the keep_processes
|
|
2585
2644
|
// rejection on the WI so the dashboard pending-reason area shows the
|
|
2586
2645
|
// missing structure instead of a bare failure_class label. _pendingReason
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1976",
|
|
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"
|