@yemi33/minions 0.1.963 → 0.1.964
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 +2 -2
- package/dashboard/js/command-center.js +18 -4
- package/dashboard.js +21 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.964 (2026-04-14)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
6
|
- add quality standard reminder to all agent and CC prompts
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
- fix dashboard plan-pause nested lock violation (#968)
|
|
26
26
|
|
|
27
27
|
### Fixes
|
|
28
|
+
- CC stale lock auto-release + queue drain on abort
|
|
28
29
|
- CC streaming 'tabId' TDZ error on new tab first message
|
|
29
30
|
- fix tabId scope and close-event lock race in CC handlers
|
|
30
31
|
- fix CC queued message 'already processing' and thinking UX stacking
|
|
@@ -44,7 +45,6 @@
|
|
|
44
45
|
- stop perpetual ADO poll retry when token unavailable (#1021)
|
|
45
46
|
- await discoverPipelineWork instead of fire-and-forget .catch (#1020)
|
|
46
47
|
- add unhandledRejection/uncaughtException handlers to engine process (#1019)
|
|
47
|
-
- show doc-chat badges on Notes and KB items (#1016)
|
|
48
48
|
|
|
49
49
|
### Other
|
|
50
50
|
- refactor(dashboard): extract _releaseCCTab helper and CC_LOCK_WAIT_MS constant
|
|
@@ -388,14 +388,28 @@ async function ccSend() {
|
|
|
388
388
|
}
|
|
389
389
|
var wasAborted = await _ccDoSend(message, false, originTabId);
|
|
390
390
|
|
|
391
|
-
// Flush queued messages to the ORIGINAL tab, even if user switched tabs
|
|
391
|
+
// Flush queued messages to the ORIGINAL tab, even if user switched tabs.
|
|
392
|
+
// If the user aborted, drain the queue — the session was killed server-side so sending
|
|
393
|
+
// queued messages would fail or create confusing behavior.
|
|
394
|
+
if (wasAborted && tab._queue && tab._queue.length > 0) {
|
|
395
|
+
var drained = tab._queue.length;
|
|
396
|
+
tab._queue = [];
|
|
397
|
+
_renderQueueIndicator();
|
|
398
|
+
ccAddMessage('system', '<div style="text-align:center;padding:4px 12px;font-size:11px;color:var(--muted);background:var(--surface2);border-radius:6px;margin:4px 0">' + drained + ' queued message' + (drained > 1 ? 's' : '') + ' cleared after stop</div>', false, originTabId);
|
|
399
|
+
}
|
|
392
400
|
while (tab._queue && tab._queue.length > 0) {
|
|
393
|
-
if (wasAborted) {
|
|
394
|
-
await new Promise(function(r) { setTimeout(r, 1500); });
|
|
395
|
-
}
|
|
396
401
|
var next = tab._queue.shift();
|
|
397
402
|
_renderQueueIndicator();
|
|
398
403
|
wasAborted = await _ccDoSend(next, false, originTabId);
|
|
404
|
+
if (wasAborted) {
|
|
405
|
+
var drained2 = tab._queue.length;
|
|
406
|
+
if (drained2 > 0) {
|
|
407
|
+
tab._queue = [];
|
|
408
|
+
_renderQueueIndicator();
|
|
409
|
+
ccAddMessage('system', '<div style="text-align:center;padding:4px 12px;font-size:11px;color:var(--muted);background:var(--surface2);border-radius:6px;margin:4px 0">' + drained2 + ' queued message' + (drained2 > 1 ? 's' : '') + ' cleared after stop</div>', false, originTabId);
|
|
410
|
+
}
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
399
413
|
}
|
|
400
414
|
}
|
|
401
415
|
|
package/dashboard.js
CHANGED
|
@@ -470,11 +470,25 @@ setInterval(() => {
|
|
|
470
470
|
// Only invalidated by: system prompt change, explicit clear, or tab close.
|
|
471
471
|
const CC_SESSION_MAX_TURNS = Infinity;
|
|
472
472
|
let ccSession = { sessionId: null, createdAt: null, lastActiveAt: null, turnCount: 0 };
|
|
473
|
-
const ccInFlightTabs = new
|
|
473
|
+
const ccInFlightTabs = new Map(); // tabId → timestamp — per-tab in-flight tracking for parallel CC requests
|
|
474
474
|
const ccInFlightAborts = new Map(); // tabId → abortFn — lets a new request kill the stale LLM
|
|
475
475
|
const CC_INFLIGHT_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes — auto-release if request hangs
|
|
476
476
|
const CC_LOCK_WAIT_MS = 200; // grace period for previous handler's finally to release lock
|
|
477
477
|
function _releaseCCTab(tabId) { ccInFlightTabs.delete(tabId); ccInFlightAborts.delete(tabId); }
|
|
478
|
+
function _ccTabIsInFlight(tabId) {
|
|
479
|
+
if (!ccInFlightTabs.has(tabId)) return false;
|
|
480
|
+
// Auto-release stale locks — if a request has been in-flight longer than CC_INFLIGHT_TIMEOUT_MS,
|
|
481
|
+
// the LLM likely hung or the finally block never ran. Release the lock so new requests can proceed.
|
|
482
|
+
const startedAt = ccInFlightTabs.get(tabId);
|
|
483
|
+
if (startedAt && Date.now() - startedAt > CC_INFLIGHT_TIMEOUT_MS) {
|
|
484
|
+
console.log(`[CC] Auto-releasing stale lock for tab ${tabId} (held ${Math.round((Date.now() - startedAt) / 1000)}s)`);
|
|
485
|
+
const staleAbort = ccInFlightAborts.get(tabId);
|
|
486
|
+
if (staleAbort) { try { staleAbort(); } catch {} }
|
|
487
|
+
_releaseCCTab(tabId);
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
478
492
|
|
|
479
493
|
// _ccPromptHash computed after CC_STATIC_SYSTEM_PROMPT is defined (see below)
|
|
480
494
|
|
|
@@ -3403,13 +3417,13 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3403
3417
|
|
|
3404
3418
|
// Per-tab concurrency guard
|
|
3405
3419
|
tabId = body.tabId || 'default';
|
|
3406
|
-
if (
|
|
3420
|
+
if (_ccTabIsInFlight(tabId)) {
|
|
3407
3421
|
await new Promise(r => setTimeout(r, CC_LOCK_WAIT_MS));
|
|
3408
|
-
if (
|
|
3422
|
+
if (_ccTabIsInFlight(tabId)) {
|
|
3409
3423
|
return jsonReply(res, 429, { error: 'This tab is already processing — wait or open a new tab.' });
|
|
3410
3424
|
}
|
|
3411
3425
|
}
|
|
3412
|
-
ccInFlightTabs.
|
|
3426
|
+
ccInFlightTabs.set(tabId, Date.now());
|
|
3413
3427
|
|
|
3414
3428
|
try {
|
|
3415
3429
|
let sessionReset = false;
|
|
@@ -3478,16 +3492,16 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3478
3492
|
const body = await readBody(req);
|
|
3479
3493
|
if (!body.message) { res.statusCode = 400; res.end('message required'); return; }
|
|
3480
3494
|
tabId = body.tabId || 'default';
|
|
3481
|
-
if (
|
|
3495
|
+
if (_ccTabIsInFlight(tabId)) {
|
|
3482
3496
|
// Previous request still in-flight — abort its LLM (handles keep-alive abort where close event didn't fire)
|
|
3483
3497
|
const prevAbort = ccInFlightAborts.get(tabId);
|
|
3484
3498
|
if (prevAbort) { prevAbort(); }
|
|
3485
3499
|
await new Promise(r => setTimeout(r, CC_LOCK_WAIT_MS)); // let previous finally run and release the lock
|
|
3486
|
-
if (
|
|
3500
|
+
if (_ccTabIsInFlight(tabId)) {
|
|
3487
3501
|
res.statusCode = 429; res.end('This tab is already processing'); return;
|
|
3488
3502
|
}
|
|
3489
3503
|
}
|
|
3490
|
-
ccInFlightTabs.
|
|
3504
|
+
ccInFlightTabs.set(tabId, Date.now());
|
|
3491
3505
|
|
|
3492
3506
|
res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' });
|
|
3493
3507
|
let _ccStreamAbort = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.964",
|
|
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"
|