@yemi33/minions 0.1.1032 → 0.1.1034
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 +77 -0
- package/dashboard/js/modal-qa.js +1 -0
- package/dashboard.js +52 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,82 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1034 (2026-04-16)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- Doc-chat performance — debounced persistence, session TTL, smart disk reads (#1164)
|
|
7
|
+
- split getStatus() into fast/slow state tiers with separate TTLs (#1170)
|
|
8
|
+
- Cache _countWorktrees() with 30s TTL (#1166)
|
|
9
|
+
- Pre-cache gzipped status buffer alongside JSON cache (#1171)
|
|
10
|
+
- parallelize ADO and GitHub PR polling with Promise.allSettled (#1172)
|
|
11
|
+
- route implement items to dedicated implement playbook (#1115)
|
|
12
|
+
|
|
13
|
+
### Fixes
|
|
14
|
+
- use locked write for failed-with-PR reconciliation in cleanup.js (#1169)
|
|
15
|
+
- prevent test from corrupting live meeting state
|
|
16
|
+
- scheduler enabled field falsy check treats undefined as disabled (#1160)
|
|
17
|
+
- update branch-dedup tests to use canonical PR IDs after merge with master (#1146)
|
|
18
|
+
- auto-review not firing for manually-linked PRs with autoObserve=true
|
|
19
|
+
- mark PR abandoned on 404 instead of silently retrying each tick
|
|
20
|
+
- replace undefined PROJECTS with config.projects in checkWatches (#1108)
|
|
21
|
+
- write permission for publish workflow
|
|
22
|
+
- run tests inline and post check runs for publish PRs
|
|
23
|
+
- add maxBuffer to all GitHub CLI spawns + paginate PR list (#1130)
|
|
24
|
+
- add required CI checks for PRs + update publish for auto-merge
|
|
25
|
+
- revert to PR merge now that stale status check is removed
|
|
26
|
+
- guard live review check against undefined vote/state values (#1132)
|
|
27
|
+
- push version bump directly to master instead of via PR
|
|
28
|
+
- add push-triggered CI for chore/publish branches
|
|
29
|
+
- publish workflow chore PRs failing to merge
|
|
30
|
+
- harden KB ordering
|
|
31
|
+
- harden audited state transitions
|
|
32
|
+
|
|
33
|
+
### Other
|
|
34
|
+
- [E2E] Dashboard & engine perf: gzip cache, tiered status, lock backoff, polling parallelization (#1174)
|
|
35
|
+
- Fix doc chat session isolation
|
|
36
|
+
- test(pipeline): add unit tests for CRUD, stage execution, run lifecycle (#1162)
|
|
37
|
+
- chore: test publish after removing stale status check
|
|
38
|
+
- chore: trigger publish test
|
|
39
|
+
- chore: test publish workflow fix
|
|
40
|
+
- chore: trigger publish workflow test
|
|
41
|
+
|
|
42
|
+
## 0.1.1033 (2026-04-16)
|
|
43
|
+
|
|
44
|
+
### Features
|
|
45
|
+
- split getStatus() into fast/slow state tiers with separate TTLs (#1170)
|
|
46
|
+
- Cache _countWorktrees() with 30s TTL (#1166)
|
|
47
|
+
- Pre-cache gzipped status buffer alongside JSON cache (#1171)
|
|
48
|
+
- parallelize ADO and GitHub PR polling with Promise.allSettled (#1172)
|
|
49
|
+
- route implement items to dedicated implement playbook (#1115)
|
|
50
|
+
|
|
51
|
+
### Fixes
|
|
52
|
+
- use locked write for failed-with-PR reconciliation in cleanup.js (#1169)
|
|
53
|
+
- prevent test from corrupting live meeting state
|
|
54
|
+
- scheduler enabled field falsy check treats undefined as disabled (#1160)
|
|
55
|
+
- update branch-dedup tests to use canonical PR IDs after merge with master (#1146)
|
|
56
|
+
- auto-review not firing for manually-linked PRs with autoObserve=true
|
|
57
|
+
- mark PR abandoned on 404 instead of silently retrying each tick
|
|
58
|
+
- replace undefined PROJECTS with config.projects in checkWatches (#1108)
|
|
59
|
+
- write permission for publish workflow
|
|
60
|
+
- run tests inline and post check runs for publish PRs
|
|
61
|
+
- add maxBuffer to all GitHub CLI spawns + paginate PR list (#1130)
|
|
62
|
+
- add required CI checks for PRs + update publish for auto-merge
|
|
63
|
+
- revert to PR merge now that stale status check is removed
|
|
64
|
+
- guard live review check against undefined vote/state values (#1132)
|
|
65
|
+
- push version bump directly to master instead of via PR
|
|
66
|
+
- add push-triggered CI for chore/publish branches
|
|
67
|
+
- publish workflow chore PRs failing to merge
|
|
68
|
+
- harden KB ordering
|
|
69
|
+
- harden audited state transitions
|
|
70
|
+
|
|
71
|
+
### Other
|
|
72
|
+
- [E2E] Dashboard & engine perf: gzip cache, tiered status, lock backoff, polling parallelization (#1174)
|
|
73
|
+
- Fix doc chat session isolation
|
|
74
|
+
- test(pipeline): add unit tests for CRUD, stage execution, run lifecycle (#1162)
|
|
75
|
+
- chore: test publish after removing stale status check
|
|
76
|
+
- chore: trigger publish test
|
|
77
|
+
- chore: test publish workflow fix
|
|
78
|
+
- chore: trigger publish workflow test
|
|
79
|
+
|
|
3
80
|
## 0.1.1032 (2026-04-16)
|
|
4
81
|
|
|
5
82
|
### Features
|
package/dashboard/js/modal-qa.js
CHANGED
|
@@ -428,6 +428,7 @@ async function _processQaMessage(message, selection, opts) {
|
|
|
428
428
|
selection: selection,
|
|
429
429
|
filePath: capturedFilePath || null,
|
|
430
430
|
model: window._lastStatus?.autoMode?.ccModel || undefined,
|
|
431
|
+
contentHash: capturedDocContext.content ? capturedDocContext.content.length + ':' + capturedDocContext.content.charCodeAt(0) + ':' + capturedDocContext.content.charCodeAt(capturedDocContext.content.length - 1) : undefined,
|
|
431
432
|
}),
|
|
432
433
|
});
|
|
433
434
|
const data = await res.json();
|
package/dashboard.js
CHANGED
|
@@ -852,16 +852,18 @@ async function executeCCActions(actions) {
|
|
|
852
852
|
// Session store for doc modals — keyed by filePath or title, persisted to disk
|
|
853
853
|
const CC_SESSIONS_PATH = path.join(ENGINE_DIR, 'cc-sessions.json');
|
|
854
854
|
const DOC_SESSIONS_PATH = path.join(ENGINE_DIR, 'doc-sessions.json');
|
|
855
|
+
const DOC_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 604800000 — 7 days
|
|
855
856
|
const docSessions = new Map(); // key → { sessionId, lastActiveAt, turnCount }
|
|
856
857
|
|
|
857
858
|
// Load persisted doc sessions on startup
|
|
858
859
|
try {
|
|
859
860
|
const saved = safeJson(DOC_SESSIONS_PATH);
|
|
860
861
|
if (saved && typeof saved === 'object') {
|
|
862
|
+
const now = Date.now();
|
|
861
863
|
for (const [key, s] of Object.entries(saved)) {
|
|
862
|
-
if (s.turnCount
|
|
863
|
-
|
|
864
|
-
|
|
864
|
+
if (s.turnCount >= CC_SESSION_MAX_TURNS) continue;
|
|
865
|
+
if (s.lastActiveAt && now - new Date(s.lastActiveAt).getTime() > DOC_SESSION_TTL_MS) continue;
|
|
866
|
+
docSessions.set(key, s);
|
|
865
867
|
}
|
|
866
868
|
}
|
|
867
869
|
} catch { /* optional */ }
|
|
@@ -872,6 +874,25 @@ function persistDocSessions() {
|
|
|
872
874
|
safeWrite(DOC_SESSIONS_PATH, obj);
|
|
873
875
|
}
|
|
874
876
|
|
|
877
|
+
// Debounced variant — coalesces rapid writes (e.g. back-to-back doc-chat turns)
|
|
878
|
+
let _persistDocSessionsTimer = null;
|
|
879
|
+
function schedulePersistDocSessions() {
|
|
880
|
+
if (_persistDocSessionsTimer) clearTimeout(_persistDocSessionsTimer);
|
|
881
|
+
_persistDocSessionsTimer = setTimeout(() => {
|
|
882
|
+
_persistDocSessionsTimer = null;
|
|
883
|
+
persistDocSessions();
|
|
884
|
+
}, 5000); // 5s debounce — rapid turns produce one write per burst
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/** Flush any pending debounced write immediately (call on shutdown). */
|
|
888
|
+
function flushPendingDocSessions() {
|
|
889
|
+
if (_persistDocSessionsTimer) {
|
|
890
|
+
clearTimeout(_persistDocSessionsTimer);
|
|
891
|
+
_persistDocSessionsTimer = null;
|
|
892
|
+
persistDocSessions();
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
875
896
|
// Resolve session from any store (CC global or doc-specific)
|
|
876
897
|
function resolveSession(store, key) {
|
|
877
898
|
if (store === 'cc') {
|
|
@@ -885,6 +906,11 @@ function resolveSession(store, key) {
|
|
|
885
906
|
persistDocSessions();
|
|
886
907
|
return null;
|
|
887
908
|
}
|
|
909
|
+
if (s.lastActiveAt && Date.now() - new Date(s.lastActiveAt).getTime() > DOC_SESSION_TTL_MS) {
|
|
910
|
+
docSessions.delete(key);
|
|
911
|
+
persistDocSessions();
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
888
914
|
return s;
|
|
889
915
|
}
|
|
890
916
|
|
|
@@ -909,7 +935,7 @@ function updateSession(store, key, sessionId, existing) {
|
|
|
909
935
|
turnCount: (existing && prev ? prev.turnCount : 0) + 1,
|
|
910
936
|
_docHash: prev?._docHash || null,
|
|
911
937
|
});
|
|
912
|
-
|
|
938
|
+
schedulePersistDocSessions();
|
|
913
939
|
}
|
|
914
940
|
}
|
|
915
941
|
|
|
@@ -972,7 +998,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
972
998
|
safeWrite(path.join(ENGINE_DIR, 'cc-session.json'), ccSession);
|
|
973
999
|
} else if (sessionKey) {
|
|
974
1000
|
docSessions.delete(sessionKey);
|
|
975
|
-
|
|
1001
|
+
schedulePersistDocSessions();
|
|
976
1002
|
}
|
|
977
1003
|
}
|
|
978
1004
|
|
|
@@ -1007,6 +1033,12 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
1007
1033
|
return result;
|
|
1008
1034
|
}
|
|
1009
1035
|
|
|
1036
|
+
// Lightweight content fingerprint — same algorithm used browser-side (no crypto needed)
|
|
1037
|
+
function contentFingerprint(str) {
|
|
1038
|
+
if (!str) return '';
|
|
1039
|
+
return str.length + ':' + str.charCodeAt(0) + ':' + str.charCodeAt(str.length - 1);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1010
1042
|
// Doc-specific wrapper — adds document context, parses ---DOCUMENT---
|
|
1011
1043
|
async function ccDocCall({ message, document, title, filePath, selection, canEdit, isJson, model, freshSession, onAbortReady }) {
|
|
1012
1044
|
const sessionKey = filePath || title;
|
|
@@ -1047,7 +1079,7 @@ async function ccDocCall({ message, document, title, filePath, selection, canEdi
|
|
|
1047
1079
|
// One-shot call — discard the session ccCall just stored so it cannot
|
|
1048
1080
|
// bleed into future interactions under the same key.
|
|
1049
1081
|
docSessions.delete(sessionKey);
|
|
1050
|
-
|
|
1082
|
+
schedulePersistDocSessions();
|
|
1051
1083
|
} else if (result.code === 0 && result.sessionId) {
|
|
1052
1084
|
// Store doc hash for next call's unchanged check
|
|
1053
1085
|
const session = resolveSession('doc', sessionKey);
|
|
@@ -3221,7 +3253,14 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3221
3253
|
try { shared.sanitizePath(body.filePath, MINIONS_DIR); } catch { return jsonReply(res, 400, { error: 'path must be under minions directory' }); }
|
|
3222
3254
|
fullPath = path.resolve(MINIONS_DIR, body.filePath);
|
|
3223
3255
|
const diskContent = safeRead(fullPath);
|
|
3224
|
-
if (diskContent !== null)
|
|
3256
|
+
if (diskContent !== null) {
|
|
3257
|
+
// If client sent a contentHash and it matches disk, skip replacement — client copy is fresh
|
|
3258
|
+
if (body.contentHash && contentFingerprint(diskContent) === body.contentHash) {
|
|
3259
|
+
// body.document is already current — no override needed
|
|
3260
|
+
} else {
|
|
3261
|
+
currentContent = diskContent;
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3225
3264
|
}
|
|
3226
3265
|
|
|
3227
3266
|
const { answer, content, actions } = await ccDocCall({
|
|
@@ -4741,7 +4780,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
4741
4780
|
{ method: 'GET', path: /^\/api\/knowledge\/([^/]+)\/([^?]+)/, desc: 'Read a specific knowledge base entry', handler: handleKnowledgeRead },
|
|
4742
4781
|
|
|
4743
4782
|
// Doc chat
|
|
4744
|
-
{ method: 'POST', path: '/api/doc-chat', desc: 'Minions-aware doc Q&A + editing via CC session', params: 'message, document, title?, filePath?, selection?', handler: handleDocChat },
|
|
4783
|
+
{ method: 'POST', path: '/api/doc-chat', desc: 'Minions-aware doc Q&A + editing via CC session', params: 'message, document, title?, filePath?, selection?, contentHash?', handler: handleDocChat },
|
|
4745
4784
|
|
|
4746
4785
|
// Inbox
|
|
4747
4786
|
{ method: 'POST', path: '/api/inbox/persist', desc: 'Promote an inbox item to team notes', params: 'name', handler: handleInboxPersist },
|
|
@@ -5102,3 +5141,8 @@ server.on('error', e => {
|
|
|
5102
5141
|
}
|
|
5103
5142
|
process.exit(1);
|
|
5104
5143
|
});
|
|
5144
|
+
|
|
5145
|
+
// ── Graceful shutdown: flush debounced writes ──────────────────────────────
|
|
5146
|
+
server.on('close', () => flushPendingDocSessions());
|
|
5147
|
+
process.on('SIGTERM', () => { flushPendingDocSessions(); process.exit(0); });
|
|
5148
|
+
process.on('SIGINT', () => { flushPendingDocSessions(); process.exit(0); });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1034",
|
|
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"
|