@yemi33/minions 0.1.1778 → 0.1.1779
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 +3 -1
- package/dashboard/js/command-center.js +24 -6
- package/dashboard/js/modal-qa.js +41 -4
- package/dashboard.js +92 -23
- package/engine/copilot-models.json +1 -1
- package/package.json +1 -1
- package/prompts/cc-system.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -103,15 +103,15 @@ function _ccActiveTab() {
|
|
|
103
103
|
|
|
104
104
|
// Build a plain-text transcript from a tab's stored messages — sent on every
|
|
105
105
|
// initial request so the server can carry it over if the session has to reset
|
|
106
|
-
// (runtime switch, system-prompt change)
|
|
107
|
-
//
|
|
106
|
+
// (runtime switch, system-prompt change) or if the previous turn has local
|
|
107
|
+
// action results the runtime session never saw.
|
|
108
108
|
var CC_TRANSCRIPT_MAX_TURNS = 20;
|
|
109
109
|
function _ccBuildTranscript(tab) {
|
|
110
110
|
if (!tab || !Array.isArray(tab.messages) || tab.messages.length === 0) return [];
|
|
111
111
|
var out = [];
|
|
112
112
|
for (var i = 0; i < tab.messages.length; i++) {
|
|
113
113
|
var m = tab.messages[i];
|
|
114
|
-
if (!m || (m.role !== 'user' && m.role !== 'assistant')) continue;
|
|
114
|
+
if (!m || (m.role !== 'user' && m.role !== 'assistant' && m.role !== 'action' && m.role !== 'system')) continue;
|
|
115
115
|
var html = typeof m.html === 'string' ? m.html : '';
|
|
116
116
|
var tmp = document.createElement('div');
|
|
117
117
|
tmp.innerHTML = html;
|
|
@@ -141,6 +141,23 @@ function _ccMergeStreamText(prev, incoming) {
|
|
|
141
141
|
return current + '\n\n' + next;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
var CC_DISPATCH_ACTION_ALIASES = ['fix', 'implement', 'explore', 'review', 'test'];
|
|
145
|
+
function _ccNormalizeDispatchAction(action) {
|
|
146
|
+
if (!action || typeof action !== 'object' || typeof action.type !== 'string') return action;
|
|
147
|
+
var type = action.type.trim().toLowerCase();
|
|
148
|
+
if (type === 'dispatch') {
|
|
149
|
+
if (action.type === 'dispatch') return action;
|
|
150
|
+
var dispatchAction = Object.assign({}, action);
|
|
151
|
+
dispatchAction.type = 'dispatch';
|
|
152
|
+
return dispatchAction;
|
|
153
|
+
}
|
|
154
|
+
if (CC_DISPATCH_ACTION_ALIASES.indexOf(type) < 0) return action;
|
|
155
|
+
var normalized = Object.assign({}, action);
|
|
156
|
+
normalized.type = 'dispatch';
|
|
157
|
+
if (!normalized.workType) normalized.workType = type;
|
|
158
|
+
return normalized;
|
|
159
|
+
}
|
|
160
|
+
|
|
144
161
|
async function _ccDashboardHealth() {
|
|
145
162
|
var controller = new AbortController();
|
|
146
163
|
var timer = setTimeout(function() { controller.abort(); }, 3000);
|
|
@@ -745,7 +762,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId) {
|
|
|
745
762
|
if (!isReconnect && res.status === 429 && (!activeTab._429retries || activeTab._429retries < 3)) {
|
|
746
763
|
activeTab._429retries = (activeTab._429retries || 0) + 1;
|
|
747
764
|
await new Promise(function(r) { setTimeout(r, 1500); });
|
|
748
|
-
return await _ccConsumeStream({ message: message, tabId: activeTabId, sessionId: activeTab.sessionId || null }, false);
|
|
765
|
+
return await _ccConsumeStream({ message: message, tabId: activeTabId, sessionId: activeTab.sessionId || null, transcript: _ccBuildTranscript(activeTab) }, false);
|
|
749
766
|
}
|
|
750
767
|
activeTab._429retries = 0;
|
|
751
768
|
var errText = await res.text();
|
|
@@ -994,6 +1011,7 @@ function _tagServerExecuted(actions, actionResults) {
|
|
|
994
1011
|
}
|
|
995
1012
|
|
|
996
1013
|
async function ccExecuteAction(action, targetTabId) {
|
|
1014
|
+
action = _ccNormalizeDispatchAction(action);
|
|
997
1015
|
var status = document.createElement('div');
|
|
998
1016
|
status.style.cssText = 'padding:4px 10px;border-radius:4px;font-size:10px;align-self:flex-start;border:1px dashed var(--border);color:var(--muted)';
|
|
999
1017
|
|
|
@@ -1010,7 +1028,7 @@ async function ccExecuteAction(action, targetTabId) {
|
|
|
1010
1028
|
status.style.color = action._serverDuplicate ? 'var(--orange)' : 'var(--green)';
|
|
1011
1029
|
}
|
|
1012
1030
|
ccAddMessage('action', status.outerHTML, false, targetTabId);
|
|
1013
|
-
if (['dispatch','
|
|
1031
|
+
if (['dispatch','create-meeting'].includes(action.type)) wakeEngine();
|
|
1014
1032
|
refresh();
|
|
1015
1033
|
return;
|
|
1016
1034
|
}
|
|
@@ -1023,7 +1041,7 @@ async function ccExecuteAction(action, targetTabId) {
|
|
|
1023
1041
|
case 'explore':
|
|
1024
1042
|
case 'review':
|
|
1025
1043
|
case 'test': {
|
|
1026
|
-
var workType = action.workType ||
|
|
1044
|
+
var workType = action.workType || 'implement';
|
|
1027
1045
|
// Forward both singular (`agent`) and plural (`agents`) hint shapes —
|
|
1028
1046
|
// the LLM emits either depending on phrasing ("assign to lambert" vs
|
|
1029
1047
|
// "dispatch to dallas, ralph"). The server-side handler promotes a
|
package/dashboard/js/modal-qa.js
CHANGED
|
@@ -28,6 +28,7 @@ let _qaAbortController = null;
|
|
|
28
28
|
let _qaQueue = []; // queued messages while processing
|
|
29
29
|
const QA_QUEUE_CAP = 10; // max queued messages
|
|
30
30
|
const QA_STREAM_STALL_MS = 6 * 60 * 1000; // allow the full doc-chat timeout before treating heartbeat-only streams as stalled
|
|
31
|
+
const QA_TRANSCRIPT_MAX_TURNS = 20;
|
|
31
32
|
let _qaSessionKey = ''; // key for current conversation (title or filePath)
|
|
32
33
|
|
|
33
34
|
const QA_STICKY_BOTTOM_PX = 80;
|
|
@@ -97,6 +98,40 @@ function _qaCloneQueue(queue) {
|
|
|
97
98
|
return Array.isArray(queue) ? queue.map(item => ({ ...item })) : [];
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
function _qaBuildTranscript(history, currentMessage) {
|
|
102
|
+
if (!Array.isArray(history) || history.length === 0) return [];
|
|
103
|
+
const current = typeof currentMessage === 'string' ? currentMessage.trim() : '';
|
|
104
|
+
const out = [];
|
|
105
|
+
for (let i = 0; i < history.length; i++) {
|
|
106
|
+
const m = history[i];
|
|
107
|
+
if (!m || (m.role !== 'user' && m.role !== 'assistant' && m.role !== 'action' && m.role !== 'system')) continue;
|
|
108
|
+
const text = typeof m.text === 'string' ? m.text.trim() : '';
|
|
109
|
+
if (!text) continue;
|
|
110
|
+
if (current && m.role === 'user' && text === current && i === history.length - 1) continue;
|
|
111
|
+
out.push({ role: m.role, text });
|
|
112
|
+
}
|
|
113
|
+
return out.slice(-QA_TRANSCRIPT_MAX_TURNS);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function _qaSummarizeActionContext(actions, actionResults) {
|
|
117
|
+
if (!Array.isArray(actions) || actions.length === 0) return '';
|
|
118
|
+
const lines = [];
|
|
119
|
+
for (let i = 0; i < actions.length; i++) {
|
|
120
|
+
const action = actions[i] || {};
|
|
121
|
+
const result = Array.isArray(actionResults) ? actionResults[i] : null;
|
|
122
|
+
const type = action.type || 'action';
|
|
123
|
+
const subject = result?.id || action.id || action.title || action.file || action.target || action.endpoint || '';
|
|
124
|
+
if (result?.error) {
|
|
125
|
+
lines.push(`${type}${subject ? ' ' + subject : ''} failed: ${result.error}`);
|
|
126
|
+
} else if (result?.ok || action._serverExecuted) {
|
|
127
|
+
lines.push(`${type}${subject ? ' ' + subject : ''} completed${result?.duplicate ? ' (duplicate)' : ''}${result?.warning ? ': ' + result.warning : ''}`);
|
|
128
|
+
} else {
|
|
129
|
+
lines.push(`${type}${subject ? ' ' + subject : ''} emitted`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
}
|
|
134
|
+
|
|
100
135
|
function _qaGetRuntime(key) {
|
|
101
136
|
if (!key) return null;
|
|
102
137
|
let runtime = _qaRuntime.get(key);
|
|
@@ -609,6 +644,7 @@ async function _processQaMessage(message, selection, opts) {
|
|
|
609
644
|
filePath: capturedFilePath || null,
|
|
610
645
|
model: window._lastStatus?.autoMode?.ccModel || undefined,
|
|
611
646
|
contentHash: capturedDocContext.content ? (function(s) { const m = Math.floor(s.length / 2); return s.length + ':' + s.charCodeAt(0) + ':' + s.charCodeAt(m) + ':' + s.charCodeAt(s.length - 1); })(capturedDocContext.content) : undefined,
|
|
647
|
+
transcript: _qaBuildTranscript(runtime.history, message),
|
|
612
648
|
}),
|
|
613
649
|
});
|
|
614
650
|
let sessionDocContext = { ...capturedDocContext };
|
|
@@ -692,20 +728,21 @@ async function _processQaMessage(message, selection, opts) {
|
|
|
692
728
|
if (rawErrorHtml) _qaInsertBeforeQueued(tmp, rawErrorHtml);
|
|
693
729
|
});
|
|
694
730
|
|
|
695
|
-
runtime.history.push({ role: 'user', text: message });
|
|
696
|
-
runtime.history.push({ role: 'assistant', text: finalText || '' });
|
|
697
|
-
if (_qaIsActiveSession(sessionKey)) _qaHistory = runtime.history.slice();
|
|
698
|
-
|
|
699
731
|
_qaNotifySidebar(capturedFilePath);
|
|
732
|
+
runtime.history.push({ role: 'user', text: message });
|
|
733
|
+
runtime.history.push({ role: 'assistant', text: bodyText || finalText || '' });
|
|
700
734
|
if (evt.actions && evt.actions.length > 0) {
|
|
701
735
|
if (evt.actionResults && typeof _tagServerExecuted === 'function') _tagServerExecuted(evt.actions, evt.actionResults);
|
|
702
736
|
for (const action of evt.actions) await ccExecuteAction(action);
|
|
737
|
+
const actionContext = _qaSummarizeActionContext(evt.actions, evt.actionResults);
|
|
738
|
+
if (actionContext) runtime.history.push({ role: 'action', text: actionContext });
|
|
703
739
|
} else if (evt.actionParseError) {
|
|
704
740
|
const warning = '<div class="modal-qa-a" style="color:var(--red)">Actions block emitted but JSON could not be parsed — no actions were executed. Resend or rephrase. (' + escHtml(String(evt.actionParseError).slice(0, 200)) + ')</div>';
|
|
705
741
|
updatedThreadHtml = _qaMutateThreadHtml(sessionKey, tmp => {
|
|
706
742
|
_qaInsertBeforeQueued(tmp, warning);
|
|
707
743
|
});
|
|
708
744
|
}
|
|
745
|
+
if (_qaIsActiveSession(sessionKey)) _qaHistory = runtime.history.slice();
|
|
709
746
|
|
|
710
747
|
if (evt.edited && evt.content) {
|
|
711
748
|
const display = evt.content.replace(/^---[\s\S]*?---\n*/m, '');
|
package/dashboard.js
CHANGED
|
@@ -1366,10 +1366,36 @@ function _readCcTabSessions({ prune = true } = {}) {
|
|
|
1366
1366
|
|
|
1367
1367
|
const CC_CARRYOVER_MAX_TURNS = 20;
|
|
1368
1368
|
const CC_CARRYOVER_PER_MSG_CHARS = 2000;
|
|
1369
|
+
const CC_TRANSCRIPT_DIALOG_ROLES = new Set(['user', 'assistant']);
|
|
1370
|
+
const CC_TRANSCRIPT_CONTEXT_ROLES = new Set(['user', 'assistant', 'action', 'system']);
|
|
1369
1371
|
|
|
1370
|
-
function
|
|
1372
|
+
function _normalizeTranscriptRole(role) {
|
|
1373
|
+
const value = String(role || '').toLowerCase();
|
|
1374
|
+
return CC_TRANSCRIPT_CONTEXT_ROLES.has(value) ? value : null;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
function _transcriptHasCarryoverContext(transcript, { outOfBandOnly = false, currentMessage } = {}) {
|
|
1378
|
+
if (!Array.isArray(transcript)) return false;
|
|
1379
|
+
const current = typeof currentMessage === 'string' ? currentMessage.trim() : '';
|
|
1380
|
+
return transcript.some(m => {
|
|
1381
|
+
const role = _normalizeTranscriptRole(m?.role);
|
|
1382
|
+
if (!role || typeof m.text !== 'string' || !m.text.trim()) return false;
|
|
1383
|
+
if (outOfBandOnly && CC_TRANSCRIPT_DIALOG_ROLES.has(role)) return false;
|
|
1384
|
+
return !(current && role === 'user' && m.text.trim() === current);
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
function _buildTranscriptCarryover(transcript, { previousRuntime, currentMessage, outOfBandOnly = false } = {}) {
|
|
1371
1389
|
if (!Array.isArray(transcript) || transcript.length === 0) return '';
|
|
1372
|
-
let filtered = transcript
|
|
1390
|
+
let filtered = transcript
|
|
1391
|
+
.map(m => {
|
|
1392
|
+
const role = _normalizeTranscriptRole(m?.role);
|
|
1393
|
+
return role && typeof m?.text === 'string' && m.text.trim()
|
|
1394
|
+
? { role, text: m.text }
|
|
1395
|
+
: null;
|
|
1396
|
+
})
|
|
1397
|
+
.filter(Boolean);
|
|
1398
|
+
if (outOfBandOnly) filtered = filtered.filter(m => !CC_TRANSCRIPT_DIALOG_ROLES.has(m.role));
|
|
1373
1399
|
const current = typeof currentMessage === 'string' ? currentMessage.trim() : '';
|
|
1374
1400
|
if (current && filtered.length > 0) {
|
|
1375
1401
|
const last = filtered[filtered.length - 1];
|
|
@@ -1378,11 +1404,19 @@ function _buildTranscriptCarryover(transcript, { previousRuntime, currentMessage
|
|
|
1378
1404
|
if (filtered.length === 0) return '';
|
|
1379
1405
|
const recent = filtered.slice(-CC_CARRYOVER_MAX_TURNS);
|
|
1380
1406
|
const truncated = filtered.length > recent.length;
|
|
1381
|
-
const header =
|
|
1382
|
-
? `--- Previous
|
|
1383
|
-
:
|
|
1407
|
+
const header = outOfBandOnly
|
|
1408
|
+
? `--- Previous out-of-band UI/server events (carried over) ---`
|
|
1409
|
+
: previousRuntime
|
|
1410
|
+
? `--- Previous conversation (carried over from ${previousRuntime} session) ---`
|
|
1411
|
+
: `--- Previous conversation (carried over) ---`;
|
|
1384
1412
|
const lines = recent.map(m => {
|
|
1385
|
-
const who = m.role === 'user'
|
|
1413
|
+
const who = m.role === 'user'
|
|
1414
|
+
? 'User'
|
|
1415
|
+
: m.role === 'assistant'
|
|
1416
|
+
? 'Assistant'
|
|
1417
|
+
: m.role === 'action'
|
|
1418
|
+
? 'Action result'
|
|
1419
|
+
: 'System note';
|
|
1386
1420
|
let text = m.text.trim();
|
|
1387
1421
|
if (text.length > CC_CARRYOVER_PER_MSG_CHARS) text = text.slice(0, CC_CARRYOVER_PER_MSG_CHARS) + '… [truncated]';
|
|
1388
1422
|
return `${who}: ${text}`;
|
|
@@ -1751,6 +1785,19 @@ function _extractActionsJson(segment) {
|
|
|
1751
1785
|
return null;
|
|
1752
1786
|
}
|
|
1753
1787
|
|
|
1788
|
+
const CC_DISPATCH_ACTION_ALIASES = new Set(['fix', 'implement', 'explore', 'review', 'test']);
|
|
1789
|
+
|
|
1790
|
+
function normalizeCCAction(action) {
|
|
1791
|
+
if (!action || typeof action !== 'object') return action;
|
|
1792
|
+
if (typeof action.type !== 'string') return action;
|
|
1793
|
+
const type = action.type.trim().toLowerCase();
|
|
1794
|
+
if (type === 'dispatch') {
|
|
1795
|
+
return action.type === 'dispatch' ? action : { ...action, type: 'dispatch' };
|
|
1796
|
+
}
|
|
1797
|
+
if (!CC_DISPATCH_ACTION_ALIASES.has(type)) return action;
|
|
1798
|
+
return { ...action, type: 'dispatch', workType: action.workType || type };
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1754
1801
|
function parseCCActions(text) {
|
|
1755
1802
|
let actions = [];
|
|
1756
1803
|
let displayText = stripCCActionsForDisplay(text);
|
|
@@ -1791,6 +1838,7 @@ function parseCCActions(text) {
|
|
|
1791
1838
|
parseError = null; // legacy fallback recovered actions
|
|
1792
1839
|
}
|
|
1793
1840
|
}
|
|
1841
|
+
actions = actions.map(normalizeCCAction);
|
|
1794
1842
|
const result = { text: displayText, actions };
|
|
1795
1843
|
if (parseError && actions.length === 0) {
|
|
1796
1844
|
result._actionParseError = parseError;
|
|
@@ -2186,7 +2234,8 @@ function _ccValidateAction(action) {
|
|
|
2186
2234
|
|
|
2187
2235
|
async function executeCCActions(actions) {
|
|
2188
2236
|
const results = [];
|
|
2189
|
-
for (const
|
|
2237
|
+
for (const rawAction of actions) {
|
|
2238
|
+
const action = normalizeCCAction(rawAction);
|
|
2190
2239
|
const validationError = _ccValidateAction(action);
|
|
2191
2240
|
if (validationError) {
|
|
2192
2241
|
results.push({ type: action?.type || 'unknown', error: validationError });
|
|
@@ -2195,7 +2244,7 @@ async function executeCCActions(actions) {
|
|
|
2195
2244
|
try {
|
|
2196
2245
|
switch (action.type) {
|
|
2197
2246
|
case 'dispatch': case 'fix': case 'implement': case 'explore': case 'review': case 'test': {
|
|
2198
|
-
const workType = routing.normalizeWorkType(action.workType ||
|
|
2247
|
+
const workType = routing.normalizeWorkType(action.workType || WORK_TYPE.IMPLEMENT, WORK_TYPE.IMPLEMENT);
|
|
2199
2248
|
const id = 'W-' + shared.uid();
|
|
2200
2249
|
const project = action.project || '';
|
|
2201
2250
|
const prRef = getWorkItemPrRef(action);
|
|
@@ -2661,12 +2710,14 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
2661
2710
|
const existing = resolveSession(store, sessionKey);
|
|
2662
2711
|
let sessionId = existing ? existing.sessionId : null;
|
|
2663
2712
|
const resumeNeedsCarryover = !!sessionId && _ccRuntimeNeedsResumeCarryover(shared.resolveCcCli(CONFIG.engine));
|
|
2713
|
+
const resumeHasOutOfBandCarryover = !!sessionId && _transcriptHasCarryoverContext(transcript, { outOfBandOnly: true, currentMessage: message });
|
|
2714
|
+
const freshNeedsCarryover = _transcriptHasCarryoverContext(transcript, { currentMessage: message });
|
|
2664
2715
|
|
|
2665
|
-
function buildPrompt({ includePreamble = true, includeCarryover = false } = {}) {
|
|
2716
|
+
function buildPrompt({ includePreamble = true, includeCarryover = false, outOfBandOnly = false } = {}) {
|
|
2666
2717
|
const parts = (!skipStatePreamble && includePreamble) ? [`## Current Minions State (${new Date().toISOString().slice(0, 16)})\n\n${buildCCStatePreamble()}`] : [];
|
|
2667
2718
|
if (extraContext) parts.push(extraContext);
|
|
2668
2719
|
if (includeCarryover) {
|
|
2669
|
-
const carryover = _buildTranscriptCarryover(transcript, { currentMessage: message });
|
|
2720
|
+
const carryover = _buildTranscriptCarryover(transcript, { currentMessage: message, outOfBandOnly });
|
|
2670
2721
|
if (carryover) parts.push(carryover);
|
|
2671
2722
|
}
|
|
2672
2723
|
parts.push(message);
|
|
@@ -2677,7 +2728,11 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
2677
2728
|
|
|
2678
2729
|
// Attempt 1: resume existing session — skip preamble (session already has context)
|
|
2679
2730
|
if (sessionId && maxTurns > 1) {
|
|
2680
|
-
const p1 = llm.callLLM(buildPrompt({
|
|
2731
|
+
const p1 = llm.callLLM(buildPrompt({
|
|
2732
|
+
includePreamble: false,
|
|
2733
|
+
includeCarryover: resumeNeedsCarryover || resumeHasOutOfBandCarryover,
|
|
2734
|
+
outOfBandOnly: !resumeNeedsCarryover,
|
|
2735
|
+
}), '', {
|
|
2681
2736
|
timeout, label, model, maxTurns, allowedTools, sessionId, effort: ccEffort, direct: true,
|
|
2682
2737
|
engineConfig: CONFIG.engine,
|
|
2683
2738
|
});
|
|
@@ -2714,7 +2769,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
2714
2769
|
}
|
|
2715
2770
|
|
|
2716
2771
|
// Attempt 2: fresh session (include preamble for full context)
|
|
2717
|
-
const freshPrompt = buildPrompt({ includeCarryover:
|
|
2772
|
+
const freshPrompt = buildPrompt({ includeCarryover: freshNeedsCarryover });
|
|
2718
2773
|
const p2 = llm.callLLM(freshPrompt, systemPrompt, {
|
|
2719
2774
|
timeout, label, model, maxTurns, allowedTools, effort: ccEffort, direct: true,
|
|
2720
2775
|
engineConfig: CONFIG.engine,
|
|
@@ -2762,12 +2817,14 @@ async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext
|
|
|
2762
2817
|
const existing = resolveSession(store, sessionKey);
|
|
2763
2818
|
let sessionId = existing ? existing.sessionId : null;
|
|
2764
2819
|
const resumeNeedsCarryover = !!sessionId && _ccRuntimeNeedsResumeCarryover(shared.resolveCcCli(CONFIG.engine));
|
|
2820
|
+
const resumeHasOutOfBandCarryover = !!sessionId && _transcriptHasCarryoverContext(transcript, { outOfBandOnly: true, currentMessage: message });
|
|
2821
|
+
const freshNeedsCarryover = _transcriptHasCarryoverContext(transcript, { currentMessage: message });
|
|
2765
2822
|
|
|
2766
|
-
function buildPrompt({ includePreamble = true, includeCarryover = false } = {}) {
|
|
2823
|
+
function buildPrompt({ includePreamble = true, includeCarryover = false, outOfBandOnly = false } = {}) {
|
|
2767
2824
|
const parts = (!skipStatePreamble && includePreamble) ? [`## Current Minions State (${new Date().toISOString().slice(0, 16)})\n\n${buildCCStatePreamble()}`] : [];
|
|
2768
2825
|
if (extraContext) parts.push(extraContext);
|
|
2769
2826
|
if (includeCarryover) {
|
|
2770
|
-
const carryover = _buildTranscriptCarryover(transcript, { currentMessage: message });
|
|
2827
|
+
const carryover = _buildTranscriptCarryover(transcript, { currentMessage: message, outOfBandOnly });
|
|
2771
2828
|
if (carryover) parts.push(carryover);
|
|
2772
2829
|
}
|
|
2773
2830
|
parts.push(message);
|
|
@@ -2777,7 +2834,11 @@ async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext
|
|
|
2777
2834
|
let result;
|
|
2778
2835
|
|
|
2779
2836
|
if (sessionId && maxTurns > 1) {
|
|
2780
|
-
const p1 = llm.callLLMStreaming(buildPrompt({
|
|
2837
|
+
const p1 = llm.callLLMStreaming(buildPrompt({
|
|
2838
|
+
includePreamble: false,
|
|
2839
|
+
includeCarryover: resumeNeedsCarryover || resumeHasOutOfBandCarryover,
|
|
2840
|
+
outOfBandOnly: !resumeNeedsCarryover,
|
|
2841
|
+
}), '', {
|
|
2781
2842
|
timeout, label, model, maxTurns, allowedTools, sessionId, effort: ccEffort, direct: true,
|
|
2782
2843
|
engineConfig: CONFIG.engine,
|
|
2783
2844
|
onChunk,
|
|
@@ -2814,7 +2875,7 @@ async function ccCallStreaming(message, { store = 'cc', sessionKey, extraContext
|
|
|
2814
2875
|
}
|
|
2815
2876
|
|
|
2816
2877
|
if (onRetry) onRetry(2);
|
|
2817
|
-
const freshPrompt = buildPrompt({ includeCarryover:
|
|
2878
|
+
const freshPrompt = buildPrompt({ includeCarryover: freshNeedsCarryover });
|
|
2818
2879
|
const p2 = llm.callLLMStreaming(freshPrompt, systemPrompt, {
|
|
2819
2880
|
timeout, label, model, maxTurns, allowedTools, effort: ccEffort, direct: true,
|
|
2820
2881
|
engineConfig: CONFIG.engine,
|
|
@@ -3186,7 +3247,7 @@ function _makeDocChatStreamStripper(onChunk) {
|
|
|
3186
3247
|
}
|
|
3187
3248
|
|
|
3188
3249
|
// Doc-specific wrapper — adds document context, parses ---DOCUMENT---
|
|
3189
|
-
async function ccDocCall({ message, document, title, filePath, selection, canEdit, isJson, model, freshSession, onAbortReady }) {
|
|
3250
|
+
async function ccDocCall({ message, document, title, filePath, selection, canEdit, isJson, model, freshSession, transcript, onAbortReady }) {
|
|
3190
3251
|
const sessionKey = filePath || title;
|
|
3191
3252
|
const docSlice = String(document || '');
|
|
3192
3253
|
|
|
@@ -3213,6 +3274,7 @@ async function ccDocCall({ message, document, title, filePath, selection, canEdi
|
|
|
3213
3274
|
allowedTools: 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch',
|
|
3214
3275
|
skipStatePreamble: true,
|
|
3215
3276
|
systemPrompt: DOC_CHAT_SYSTEM_PROMPT,
|
|
3277
|
+
transcript,
|
|
3216
3278
|
...(model ? { model } : {}),
|
|
3217
3279
|
onAbortReady,
|
|
3218
3280
|
});
|
|
@@ -3250,7 +3312,7 @@ async function ccDocCall({ message, document, title, filePath, selection, canEdi
|
|
|
3250
3312
|
return _parseDocChatResultText(result.text);
|
|
3251
3313
|
}
|
|
3252
3314
|
|
|
3253
|
-
async function ccDocCallStreaming({ message, document, title, filePath, selection, canEdit, isJson, model, freshSession, onAbortReady, onChunk, onToolUse, onRetry }) {
|
|
3315
|
+
async function ccDocCallStreaming({ message, document, title, filePath, selection, canEdit, isJson, model, freshSession, transcript, onAbortReady, onChunk, onToolUse, onRetry }) {
|
|
3254
3316
|
const sessionKey = filePath || title;
|
|
3255
3317
|
const docSlice = String(document || '');
|
|
3256
3318
|
const streamStripper = _makeDocChatStreamStripper(onChunk);
|
|
@@ -3273,6 +3335,7 @@ async function ccDocCallStreaming({ message, document, title, filePath, selectio
|
|
|
3273
3335
|
allowedTools: 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch',
|
|
3274
3336
|
skipStatePreamble: true,
|
|
3275
3337
|
systemPrompt: DOC_CHAT_SYSTEM_PROMPT,
|
|
3338
|
+
transcript,
|
|
3276
3339
|
...(model ? { model } : {}),
|
|
3277
3340
|
onAbortReady,
|
|
3278
3341
|
onChunk: streamStripper,
|
|
@@ -5433,6 +5496,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5433
5496
|
filePath: body.filePath, selection: body.selection, canEdit, isJson,
|
|
5434
5497
|
model: body.model || undefined,
|
|
5435
5498
|
freshSession: !!body.freshSession,
|
|
5499
|
+
transcript: body.transcript,
|
|
5436
5500
|
onAbortReady: (abort) => { _docAbort = abort; },
|
|
5437
5501
|
});
|
|
5438
5502
|
const actionResults = await executeDocChatActions(actions);
|
|
@@ -5523,6 +5587,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5523
5587
|
filePath: body.filePath, selection: body.selection, canEdit, isJson,
|
|
5524
5588
|
model: body.model || undefined,
|
|
5525
5589
|
freshSession: !!body.freshSession,
|
|
5590
|
+
transcript: body.transcript,
|
|
5526
5591
|
onAbortReady: (abort) => { _docAbort = abort; },
|
|
5527
5592
|
onChunk: (text) => { writeDocEvent({ type: 'chunk', text }); },
|
|
5528
5593
|
onToolUse: (name, input) => { writeDocEvent({ type: 'tool', name, input: _lightToolInput(input) }); },
|
|
@@ -6279,11 +6344,14 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
6279
6344
|
const wasResume = !!tabSessionId;
|
|
6280
6345
|
const sessionId = tabSessionId || null;
|
|
6281
6346
|
const resumeNeedsCarryover = wasResume && _ccRuntimeNeedsResumeCarryover(currentRuntime);
|
|
6347
|
+
const resumeHasOutOfBandCarryover = wasResume && _transcriptHasCarryoverContext(body.transcript, { outOfBandOnly: true, currentMessage: body.message });
|
|
6282
6348
|
const preamble = wasResume ? '' : buildCCStatePreamble();
|
|
6283
|
-
const
|
|
6349
|
+
const includeFullCarryover = sessionReset || resumeNeedsCarryover;
|
|
6350
|
+
const carryover = (includeFullCarryover || resumeHasOutOfBandCarryover)
|
|
6284
6351
|
? _buildTranscriptCarryover(body.transcript, {
|
|
6285
6352
|
previousRuntime: sessionReset ? previousRuntime : null,
|
|
6286
6353
|
currentMessage: body.message,
|
|
6354
|
+
outOfBandOnly: !includeFullCarryover,
|
|
6287
6355
|
})
|
|
6288
6356
|
: '';
|
|
6289
6357
|
const prompt = _joinCcPromptParts(preamble, carryover, body.message);
|
|
@@ -7592,8 +7660,8 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
7592
7660
|
{ method: 'GET', path: /^\/api\/knowledge\/([^/]+)\/([^?]+)/, template: '/api/knowledge/:category/:file', desc: 'Read a specific knowledge base entry', handler: handleKnowledgeRead },
|
|
7593
7661
|
|
|
7594
7662
|
// Doc chat
|
|
7595
|
-
{ 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 },
|
|
7596
|
-
{ method: 'POST', path: '/api/doc-chat/stream', desc: 'Streaming doc chat — SSE with text chunks and tool progress', params: 'message, document, title?, filePath?, selection?, contentHash?', handler: handleDocChatStream },
|
|
7663
|
+
{ method: 'POST', path: '/api/doc-chat', desc: 'Minions-aware doc Q&A + editing via CC session', params: 'message, document, title?, filePath?, selection?, contentHash?, transcript?', handler: handleDocChat },
|
|
7664
|
+
{ method: 'POST', path: '/api/doc-chat/stream', desc: 'Streaming doc chat — SSE with text chunks and tool progress', params: 'message, document, title?, filePath?, selection?, contentHash?, transcript?', handler: handleDocChatStream },
|
|
7597
7665
|
|
|
7598
7666
|
// Inbox
|
|
7599
7667
|
{ method: 'POST', path: '/api/inbox/persist', desc: 'Promote an inbox item to team notes', params: 'name', handler: handleInboxPersist },
|
|
@@ -7617,8 +7685,8 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
7617
7685
|
// Command Center
|
|
7618
7686
|
{ method: 'POST', path: '/api/command-center/new-session', desc: 'Clear active CC session', handler: handleCommandCenterNewSession },
|
|
7619
7687
|
{ method: 'POST', path: '/api/command-center/abort', desc: 'Abort an in-flight CC request for a tab', params: 'tabId?', handler: handleCommandCenterAbort },
|
|
7620
|
-
{ method: 'POST', path: '/api/command-center', desc: 'Conversational command center with full minions context', params: 'message, sessionId?', handler: handleCommandCenter },
|
|
7621
|
-
{ method: 'POST', path: '/api/command-center/stream', desc: 'Streaming CC — SSE with text chunks as they arrive', params: 'message, tabId?', handler: handleCommandCenterStream },
|
|
7688
|
+
{ method: 'POST', path: '/api/command-center', desc: 'Conversational command center with full minions context', params: 'message, tabId?, sessionId?, transcript?', handler: handleCommandCenter },
|
|
7689
|
+
{ method: 'POST', path: '/api/command-center/stream', desc: 'Streaming CC — SSE with text chunks as they arrive', params: 'message, tabId?, sessionId?, transcript?', handler: handleCommandCenterStream },
|
|
7622
7690
|
{ method: 'GET', path: '/api/cc-sessions', desc: 'List CC session metadata for all tabs', handler: handleCCSessionsList },
|
|
7623
7691
|
{ method: 'DELETE', path: /^\/api\/cc-sessions\/([\w-]+)$/, template: '/api/cc-sessions/:id', desc: 'Delete a CC session by tab ID', handler: handleCCSessionDelete },
|
|
7624
7692
|
|
|
@@ -8022,6 +8090,7 @@ module.exports = {
|
|
|
8022
8090
|
buildCCStatePreamble,
|
|
8023
8091
|
_routesAsMeta,
|
|
8024
8092
|
_buildTranscriptCarryover,
|
|
8093
|
+
_transcriptHasCarryoverContext,
|
|
8025
8094
|
_ccRuntimeNeedsResumeCarryover,
|
|
8026
8095
|
_joinCcPromptParts,
|
|
8027
8096
|
_captureApiRoutesMeta,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1779",
|
|
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"
|
package/prompts/cc-system.md
CHANGED
|
@@ -85,13 +85,13 @@ I'll dispatch dallas to fix that bug.
|
|
|
85
85
|
|
|
86
86
|
**Required fields per action type — server rejects with an error if missing:**
|
|
87
87
|
|
|
88
|
-
- `dispatch
|
|
88
|
+
- `dispatch`: `title` is REQUIRED. `description` recommended. `project` REQUIRED when multiple projects are configured (server returns the list of known names if you guess wrong). For agent hints emit either `agents: ["dallas"]` (array, preferred) or `agent: "dallas"` (string — auto-promoted server-side). Unknown agent names error. Always emit `"type":"dispatch"` for dispatch-like work and preserve the semantic intent in `workType` (`fix`, `implement`, `explore`, `review`, or `test`) instead of using those words as action types.
|
|
89
89
|
- `build-and-test`: `pr` REQUIRED (number, ID, or URL).
|
|
90
90
|
- `note`: `title` and `content` (or `description`) REQUIRED.
|
|
91
91
|
- `knowledge`: `title`, `content`, and `category` REQUIRED. Valid categories: architecture, conventions, project-notes, build-reports, reviews.
|
|
92
92
|
|
|
93
93
|
Core action types:
|
|
94
|
-
- **dispatch**: title (REQUIRED), workType, priority (low/medium/high), agents[] or agent (optional — both shapes accepted), project (REQUIRED when multi-project unless `pr` resolves to a tracked PR), description, pr (optional PR number/id/url for work that targets an existing PR), scope (`"fan-out"` only when the user explicitly asks to fan out to all agents)
|
|
94
|
+
- **dispatch**: title (REQUIRED), workType, priority (low/medium/high), agents[] or agent (optional — both shapes accepted), project (REQUIRED when multi-project unless `pr` resolves to a tracked PR), description, pr (optional PR number/id/url for work that targets an existing PR), scope (`"fan-out"` only when the user explicitly asks to fan out to all agents). Do not emit `type:"fix"` or `type:"implement"`; emit `type:"dispatch"` with `workType:"fix"` or `workType:"implement"`.
|
|
95
95
|
workTypes: `explore` (research/report only, NO PR), `ask` (answer/report, NO PR), `implement` (new code, PR REQUIRED), `fix` (standalone bug fix creates a PR; include `pr` when fixing review comments/build failures on an existing PR), `review` (code review, NO PR), `test` (tests, PR if new), `verify` (merge/build/maintenance, NO PR)
|
|
96
96
|
If the user wants a design/architecture artifact committed through a PR, dispatch `implement` or `docs` rather than `explore`.
|
|
97
97
|
When the user names a specific agent ("assign this to lambert"), put exactly that one name in `agents` (e.g. `"agents": ["lambert"]`). A single-agent assignment is hard-pinned by the server — it will queue for that agent only and skip the routing table. If the user explicitly asks for fan-out/all agents, set `scope: "fan-out"`.
|