@yemi33/minions 0.1.2406 → 0.1.2408
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/refresh.js +24 -7
- package/dashboard.js +2 -5
- package/engine/comment-format.js +44 -5
- package/engine/meeting.js +20 -1
- package/package.json +1 -1
package/dashboard/js/refresh.js
CHANGED
|
@@ -62,11 +62,11 @@ const _pageCounters = {
|
|
|
62
62
|
return ws.length + '|' + ws.reduce(function(m, w) { return Math.max(m, new Date(w.last_triggered || 0).getTime() || 0); }, 0);
|
|
63
63
|
},
|
|
64
64
|
meetings: function(d) {
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
return
|
|
65
|
+
// One server summary owns both the active count and signature. Do not mix
|
|
66
|
+
// independently resolving total/list requests or include terminal history.
|
|
67
|
+
if (!window._lastMeetingsSummary) return null;
|
|
68
|
+
const summary = window._lastMeetingsSummary;
|
|
69
|
+
return (summary.active || 0) + '|' + (summary.sig || '');
|
|
70
70
|
},
|
|
71
71
|
pipelines: function(d) {
|
|
72
72
|
if (!Array.isArray(window._lastPipelines)) return null;
|
|
@@ -97,13 +97,17 @@ const _pageCounters = {
|
|
|
97
97
|
},
|
|
98
98
|
};
|
|
99
99
|
let _prevCounts = {};
|
|
100
|
+
function _isPageActivityRelevant(page, meetingSummary) {
|
|
101
|
+
return page !== 'meetings' || Number(meetingSummary?.active) > 0;
|
|
102
|
+
}
|
|
100
103
|
function _detectPageChanges(data) {
|
|
101
104
|
var changes = {};
|
|
102
105
|
for (var page in _pageCounters) {
|
|
103
106
|
var sig = _pageCounters[page](data);
|
|
104
107
|
if (sig === null) continue; // cache not ready yet — skip both compare and record
|
|
105
108
|
var sigStr = String(sig);
|
|
106
|
-
if (_prevCounts[page] !== undefined && sigStr !== _prevCounts[page]
|
|
109
|
+
if (_prevCounts[page] !== undefined && sigStr !== _prevCounts[page]
|
|
110
|
+
&& _isPageActivityRelevant(page, window._lastMeetingsSummary)) changes[page] = true;
|
|
107
111
|
_prevCounts[page] = sigStr;
|
|
108
112
|
}
|
|
109
113
|
return changes;
|
|
@@ -425,6 +429,18 @@ function _fireCrossSliceRender(key, value) {
|
|
|
425
429
|
return true;
|
|
426
430
|
}
|
|
427
431
|
|
|
432
|
+
let _meetingsSummaryRequestSeq = 0;
|
|
433
|
+
function _applyMeetingsSummary(requestSeq, summary) {
|
|
434
|
+
if (requestSeq !== _meetingsSummaryRequestSeq) return false;
|
|
435
|
+
if (!summary || typeof summary.active !== 'number' || typeof summary.sig !== 'string') return false;
|
|
436
|
+
window._lastMeetingsSummary = summary;
|
|
437
|
+
if (!_isPageActivityRelevant('meetings', summary)) {
|
|
438
|
+
const link = document.querySelector('.sidebar-link[data-page="meetings"]');
|
|
439
|
+
if (link) clearNotifBadge(link);
|
|
440
|
+
}
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
|
|
428
444
|
function _refreshSidebarCounterSummaries() {
|
|
429
445
|
fetch('/api/qa-runs-summary')
|
|
430
446
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
@@ -437,9 +453,10 @@ function _refreshSidebarCounterSummaries() {
|
|
|
437
453
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
438
454
|
.then(function (s) { if (s && typeof s === 'object') window._lastQaSessionsSummary = s; })
|
|
439
455
|
.catch(function () { /* sidebar badge degrades to empty */ });
|
|
456
|
+
const meetingsSummarySeq = ++_meetingsSummaryRequestSeq;
|
|
440
457
|
fetch('/api/meetings-total')
|
|
441
458
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
442
|
-
.then(function (s) {
|
|
459
|
+
.then(function (s) { _applyMeetingsSummary(meetingsSummarySeq, s); })
|
|
443
460
|
.catch(function () { /* optional */ });
|
|
444
461
|
// /api/verify-guides is now fetched in parallel with /api/prd inside the
|
|
445
462
|
// prdProgress block so renderPrd's guideByPlan lookup uses the SAME tick's
|
package/dashboard.js
CHANGED
|
@@ -13498,14 +13498,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
13498
13498
|
builder: () => qaSessionsMod.summarizeActiveSessionsForStatus(),
|
|
13499
13499
|
});
|
|
13500
13500
|
}},
|
|
13501
|
-
{ method: 'GET', path: '/api/meetings-total', desc: '
|
|
13501
|
+
{ method: 'GET', path: '/api/meetings-total', desc: 'Meeting summary {total, active, sig}; active-only fields drive the sidebar activity dot', handler: (req, res) => {
|
|
13502
13502
|
return serveFreshJson(req, res, {
|
|
13503
13503
|
tag: 'meetings-total',
|
|
13504
13504
|
inputs: [path.join(MINIONS_DIR, 'meetings')],
|
|
13505
|
-
builder: () =>
|
|
13506
|
-
const meetings = meetingMod.getMeetings();
|
|
13507
|
-
return { total: Array.isArray(meetings) ? meetings.length : 0 };
|
|
13508
|
-
},
|
|
13505
|
+
builder: () => meetingMod.summarizeMeetingsForStatus(),
|
|
13509
13506
|
});
|
|
13510
13507
|
}},
|
|
13511
13508
|
{ method: 'GET', path: '/api/browser-presence', desc: 'Whether a dashboard browser tab was recently active', handler: (req, res) => {
|
package/engine/comment-format.js
CHANGED
|
@@ -57,7 +57,7 @@ function linkifyBrandTrailer(text) {
|
|
|
57
57
|
// - model concrete runtime execution model (optional)
|
|
58
58
|
// - no field may contain `--` (would close the HTML comment early)
|
|
59
59
|
|
|
60
|
-
const { normalizeExecutionModel } = require('./execution-model');
|
|
60
|
+
const { normalizeExecutionModel, UNKNOWN_MODEL } = require('./execution-model');
|
|
61
61
|
|
|
62
62
|
const AGENT_ID_RE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
63
63
|
const KIND_RE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
@@ -104,24 +104,63 @@ function _encodeMarkerModel(model) {
|
|
|
104
104
|
return encoded.includes('--') ? encoded.replace(/-/g, '%2D') : encoded;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
// A genuinely-absent model must NOT be recorded in the hidden marker — the
|
|
108
|
+
// `unknown` sentinel is the signal that no runtime/model evidence exists, so we
|
|
109
|
+
// omit the segment entirely (W-mryk8hf1) rather than persisting `model=unknown`.
|
|
110
|
+
function _isKnownModel(model) {
|
|
111
|
+
return !!model && model !== UNKNOWN_MODEL;
|
|
112
|
+
}
|
|
113
|
+
|
|
107
114
|
function _buildMarker({ agentId, kind, workItemId, model }) {
|
|
108
115
|
_validateMarkerInputs({ agentId, kind, workItemId, model });
|
|
109
116
|
const wi = (workItemId !== undefined && workItemId !== null) ? ` wi=${workItemId}` : '';
|
|
110
|
-
const modelPart = model ? ` model=${_encodeMarkerModel(model)}` : '';
|
|
117
|
+
const modelPart = _isKnownModel(model) ? ` model=${_encodeMarkerModel(model)}` : '';
|
|
111
118
|
return `<!-- minions:agent=${agentId} kind=${kind}${wi}${modelPart} -->`;
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
const MODEL_SIGNOFF_RE =
|
|
115
122
|
/(\b(?:Review|Reviewed|Fixed|Verified|Rebased) by (?:Minions|\[Minions\]\([^)]+\)) \([^\r\n)]*? · )[^)\r\n]+(\))/g;
|
|
116
123
|
|
|
124
|
+
// Matches the visible sign-off when its model segment is the `unknown` sentinel,
|
|
125
|
+
// capturing everything up to (but excluding) the ` · unknown` so it can be
|
|
126
|
+
// dropped — turning `(Ripley — Explorer · unknown)` into `(Ripley — Explorer)`.
|
|
127
|
+
const UNKNOWN_MODEL_SIGNOFF_RE =
|
|
128
|
+
/(\b(?:Review|Reviewed|Fixed|Verified|Rebased) by (?:Minions|\[Minions\]\([^)]+\)) \([^\r\n)]*?) · unknown(\))/g;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Stamp the concrete runtime execution model into the visible sign-off.
|
|
132
|
+
*
|
|
133
|
+
* Three cases, mirroring the execution-model resolver's evidence outcomes:
|
|
134
|
+
* - known model → replace the model segment with it.
|
|
135
|
+
* - `unknown` sentinel → the resolver positively determined no runtime/
|
|
136
|
+
* model evidence exists, so omit the whole
|
|
137
|
+
* ` · <model>` segment rather than printing it
|
|
138
|
+
* (W-mryk8hf1). The footer degrades to
|
|
139
|
+
* `(<name> — <role>)`.
|
|
140
|
+
* - no opinion (falsy/null) → leave any concrete model the agent wrote
|
|
141
|
+
* intact, but still drop a bare ` · unknown`
|
|
142
|
+
* sentinel so the footer never advertises it.
|
|
143
|
+
*/
|
|
117
144
|
function stampExecutionModel(text, model) {
|
|
145
|
+
if (typeof text !== 'string') return text;
|
|
118
146
|
const normalized = normalizeExecutionModel(model);
|
|
119
|
-
if (
|
|
120
|
-
|
|
147
|
+
if (_isKnownModel(normalized)) {
|
|
148
|
+
return text.replace(MODEL_SIGNOFF_RE, (_match, prefix, suffix) => `${prefix}${normalized}${suffix}`);
|
|
149
|
+
}
|
|
150
|
+
if (normalized === UNKNOWN_MODEL) {
|
|
151
|
+
// prefix ends with " · " — drop that separator along with the model segment.
|
|
152
|
+
return text.replace(MODEL_SIGNOFF_RE, (_match, prefix, suffix) => `${prefix.replace(/ · $/, '')}${suffix}`);
|
|
153
|
+
}
|
|
154
|
+
return text.replace(UNKNOWN_MODEL_SIGNOFF_RE, (_match, prefix, suffix) => `${prefix}${suffix}`);
|
|
121
155
|
}
|
|
122
156
|
|
|
123
157
|
function _stampLeadingMarkerModel(text, model) {
|
|
124
|
-
if (
|
|
158
|
+
if (typeof text !== 'string') return text;
|
|
159
|
+
// Rewrite the leading marker whenever a model value is supplied. A known model
|
|
160
|
+
// is recorded; the `unknown` sentinel rebuilds the marker without `model=`
|
|
161
|
+
// (buildMarker omits it), stripping any pre-existing stale model=. A
|
|
162
|
+
// genuinely-absent (falsy/null) model leaves the marker untouched.
|
|
163
|
+
if (!normalizeExecutionModel(model)) return text;
|
|
125
164
|
return text.replace(MINIONS_COMMENT_MARKER_RE, (marker, agentId, kind, workItemId) => {
|
|
126
165
|
if (marker !== text.slice(0, marker.length)) return marker;
|
|
127
166
|
return _buildMarker({ agentId, kind, workItemId, model });
|
package/engine/meeting.js
CHANGED
|
@@ -399,6 +399,25 @@ function getMeetings() {
|
|
|
399
399
|
return result;
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
+
function summarizeMeetingsForStatus() {
|
|
403
|
+
const meetings = getMeetings();
|
|
404
|
+
const activeEntries = [];
|
|
405
|
+
for (const meeting of meetings) {
|
|
406
|
+
if (!meeting || typeof meeting !== 'object') continue;
|
|
407
|
+
const status = String(meeting.status || '').toLowerCase();
|
|
408
|
+
if (!ACTIVE_MEETING_STATUSES.has(status)) continue;
|
|
409
|
+
const roundValue = Number(meeting.round);
|
|
410
|
+
const round = Number.isFinite(roundValue) ? roundValue : 0;
|
|
411
|
+
activeEntries.push((meeting.id || '') + ':' + status + ':' + round);
|
|
412
|
+
}
|
|
413
|
+
activeEntries.sort();
|
|
414
|
+
return {
|
|
415
|
+
total: meetings.length,
|
|
416
|
+
active: activeEntries.length,
|
|
417
|
+
sig: activeEntries.join(','),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
402
421
|
function getMeeting(id) {
|
|
403
422
|
const filePath = path.join(MEETINGS_DIR, id + '.json');
|
|
404
423
|
const m = safeJson(filePath);
|
|
@@ -1023,7 +1042,7 @@ function checkMeetingTimeouts(config) {
|
|
|
1023
1042
|
}
|
|
1024
1043
|
}
|
|
1025
1044
|
module.exports = {
|
|
1026
|
-
MEETINGS_DIR, getMeetings, getMeeting, saveMeeting, mutateMeeting, createMeeting,
|
|
1045
|
+
MEETINGS_DIR, getMeetings, summarizeMeetingsForStatus, getMeeting, saveMeeting, mutateMeeting, createMeeting,
|
|
1027
1046
|
invalidateMeetingsCache,
|
|
1028
1047
|
discoverMeetingWork, collectMeetingFindings, checkMeetingTimeouts,
|
|
1029
1048
|
addMeetingNote, advanceMeetingRound, endMeeting, archiveMeeting, unarchiveMeeting, deleteMeeting,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2408",
|
|
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"
|