@yemi33/minions 0.1.566 → 0.1.568
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 +4 -1
- package/dashboard/js/render-other.js +11 -1
- package/dashboard.js +22 -7
- package/engine/lifecycle.js +8 -1
- package/engine/llm.js +4 -2
- package/engine/shared.js +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -59,11 +59,20 @@ function renderMetrics(metrics) {
|
|
|
59
59
|
merged.prsApproved += tm.prsApproved || 0;
|
|
60
60
|
merged.prsRejected += tm.prsRejected || 0;
|
|
61
61
|
merged.reviewsDone += tm.reviewsDone || 0;
|
|
62
|
+
merged.totalRuntimeMs = (merged.totalRuntimeMs || 0) + (tm.totalRuntimeMs || 0);
|
|
62
63
|
}
|
|
63
64
|
rows.push(['Temp Agents (' + temps.length + ')', merged]);
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
function fmtAvgRuntime(m) {
|
|
68
|
+
const total = m.totalRuntimeMs || 0;
|
|
69
|
+
const count = (m.tasksCompleted || 0) + (m.tasksErrored || 0);
|
|
70
|
+
if (!count || !total) return '-';
|
|
71
|
+
const avgMin = total / count / 60000;
|
|
72
|
+
return avgMin < 1 ? '<1m' : Math.round(avgMin) + 'm';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let html = '<table class="pr-table"><thead><tr><th>Agent</th><th>Done</th><th>Errors</th><th>PRs</th><th>Approved</th><th>Rejected</th><th>Rate</th><th>Reviews</th><th>Avg Runtime</th></tr></thead><tbody>';
|
|
67
76
|
for (const [id, m] of rows) {
|
|
68
77
|
const rate = m.prsCreated > 0 ? Math.round((m.prsApproved / m.prsCreated) * 100) + '%' : '-';
|
|
69
78
|
const rateColor = m.prsCreated > 0 ? (m.prsApproved / m.prsCreated >= 0.7 ? 'var(--green)' : 'var(--red)') : 'var(--muted)';
|
|
@@ -76,6 +85,7 @@ function renderMetrics(metrics) {
|
|
|
76
85
|
'<td style="color:' + (m.prsRejected > 0 ? 'var(--red)' : 'var(--muted)') + '">' + (m.prsRejected || 0) + '</td>' +
|
|
77
86
|
'<td style="color:' + rateColor + ';font-weight:600">' + rate + '</td>' +
|
|
78
87
|
'<td>' + (m.reviewsDone || 0) + '</td>' +
|
|
88
|
+
'<td style="color:var(--muted)">' + fmtAvgRuntime(m) + '</td>' +
|
|
79
89
|
'</tr>';
|
|
80
90
|
}
|
|
81
91
|
html += '</tbody></table>';
|
package/dashboard.js
CHANGED
|
@@ -368,6 +368,8 @@ function getStatus() {
|
|
|
368
368
|
decompose: CONFIG.engine?.autoDecompose !== false,
|
|
369
369
|
tempAgents: !!CONFIG.engine?.allowTempAgents,
|
|
370
370
|
inboxThreshold: CONFIG.engine?.inboxConsolidateThreshold || shared.ENGINE_DEFAULTS.inboxConsolidateThreshold,
|
|
371
|
+
ccModel: CONFIG.engine?.ccModel || shared.ENGINE_DEFAULTS.ccModel,
|
|
372
|
+
ccEffort: CONFIG.engine?.ccEffort || shared.ENGINE_DEFAULTS.ccEffort,
|
|
371
373
|
},
|
|
372
374
|
initialized: !!(CONFIG.agents && Object.keys(CONFIG.agents).length > 0),
|
|
373
375
|
installId: safeRead(path.join(MINIONS_DIR, '.install-id')).trim() || null,
|
|
@@ -568,7 +570,7 @@ Available action types:
|
|
|
568
570
|
- **schedule**: Create or update a scheduled task. Fields: id (unique slug), title, cron (3-field: minute hour dayOfWeek), workType (implement/test/explore/ask/review/fix), project (optional), agent (optional), description (optional), priority (optional), enabled (default true). Example cron: "0 9 2" = every Tuesday at 9am.
|
|
569
571
|
- **delete-schedule**: Delete a scheduled task. Fields: id.
|
|
570
572
|
- **create-meeting**: Start a team meeting. Fields: title (short meeting name), agenda (detailed text — what agents should investigate/debate, numbered items work best), agents (array of agent IDs), rounds (optional, default 3), project (optional).
|
|
571
|
-
- **set-config**: Update engine settings. Fields: setting (setting name), value (new value). Valid settings: autoApprovePlans (bool), autoDecompose (bool), allowTempAgents (bool), maxConcurrent (number), maxTurns (number). Example: { "type": "set-config", "setting": "autoApprovePlans", "value": true }
|
|
573
|
+
- **set-config**: Update engine settings. Fields: setting (setting name), value (new value). Valid settings: autoApprovePlans (bool), autoDecompose (bool), allowTempAgents (bool), maxConcurrent (number), maxTurns (number), ccModel (sonnet/haiku/opus), ccEffort (null/low/medium/high). Example: { "type": "set-config", "setting": "autoApprovePlans", "value": true }
|
|
572
574
|
- **edit-pipeline**: Update an existing pipeline. Fields: id (pipeline ID), title (optional), stages (optional, JSON array — omit "agent" on stages unless the user specifically requests one; the engine routes to any available agent by default), trigger (optional, { cron: "minute hour dow" } or null for manual).
|
|
573
575
|
- **unpin**: Remove a pinned note. Fields: title (exact title of the pinned note to remove)
|
|
574
576
|
- **archive-plan**: Archive a completed/paused plan. Fields: file (PRD .json or plan .md filename)
|
|
@@ -801,7 +803,9 @@ function updateSession(store, key, sessionId, existing) {
|
|
|
801
803
|
* @param {number} opts.maxTurns - Max tool-use turns
|
|
802
804
|
* @param {string} opts.allowedTools - Comma-separated tool list
|
|
803
805
|
*/
|
|
804
|
-
async function ccCall(message, { store = 'cc', sessionKey, extraContext, label = 'command-center', timeout = 900000, maxTurns = 50, allowedTools = 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch', skipStatePreamble = false, model
|
|
806
|
+
async function ccCall(message, { store = 'cc', sessionKey, extraContext, label = 'command-center', timeout = 900000, maxTurns = 50, allowedTools = 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch', skipStatePreamble = false, model } = {}) {
|
|
807
|
+
if (!model) model = CONFIG.engine?.ccModel || shared.ENGINE_DEFAULTS.ccModel;
|
|
808
|
+
const ccEffort = CONFIG.engine?.ccEffort || shared.ENGINE_DEFAULTS.ccEffort;
|
|
805
809
|
const existing = resolveSession(store, sessionKey);
|
|
806
810
|
let sessionId = existing ? existing.sessionId : null;
|
|
807
811
|
|
|
@@ -817,7 +821,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
817
821
|
// Attempt 1: resume existing session — skip preamble (session already has context)
|
|
818
822
|
if (sessionId && maxTurns > 1) {
|
|
819
823
|
result = await llm.callLLM(buildPrompt({ includePreamble: false }), '', {
|
|
820
|
-
timeout, label, model, maxTurns, allowedTools, sessionId,
|
|
824
|
+
timeout, label, model, maxTurns, allowedTools, sessionId, effort: ccEffort,
|
|
821
825
|
});
|
|
822
826
|
llm.trackEngineUsage(label, result.usage);
|
|
823
827
|
|
|
@@ -852,7 +856,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
852
856
|
// Attempt 2: fresh session (include preamble for full context)
|
|
853
857
|
const freshPrompt = buildPrompt();
|
|
854
858
|
result = await llm.callLLM(freshPrompt, CC_STATIC_SYSTEM_PROMPT, {
|
|
855
|
-
timeout, label, model, maxTurns, allowedTools,
|
|
859
|
+
timeout, label, model, maxTurns, allowedTools, effort: ccEffort,
|
|
856
860
|
});
|
|
857
861
|
llm.trackEngineUsage(label, result.usage);
|
|
858
862
|
|
|
@@ -866,7 +870,7 @@ async function ccCall(message, { store = 'cc', sessionKey, extraContext, label =
|
|
|
866
870
|
console.log(`[${label}] Fresh call also failed (code=${result.code}, empty=${!result.text}), retrying once more...`);
|
|
867
871
|
await new Promise(r => setTimeout(r, 2000));
|
|
868
872
|
result = await llm.callLLM(freshPrompt, CC_STATIC_SYSTEM_PROMPT, {
|
|
869
|
-
timeout, label, model, maxTurns, allowedTools,
|
|
873
|
+
timeout, label, model, maxTurns, allowedTools, effort: ccEffort,
|
|
870
874
|
});
|
|
871
875
|
llm.trackEngineUsage(label, result.usage);
|
|
872
876
|
|
|
@@ -3374,10 +3378,12 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3374
3378
|
const prompt = preamble + '\n\n---\n\n' + body.message;
|
|
3375
3379
|
|
|
3376
3380
|
const { callLLMStreaming, trackEngineUsage: trackUsage } = require('./engine/llm');
|
|
3381
|
+
const streamModel = CONFIG.engine?.ccModel || shared.ENGINE_DEFAULTS.ccModel;
|
|
3382
|
+
const streamEffort = CONFIG.engine?.ccEffort || shared.ENGINE_DEFAULTS.ccEffort;
|
|
3377
3383
|
const llmPromise = callLLMStreaming(prompt, CC_STATIC_SYSTEM_PROMPT, {
|
|
3378
|
-
timeout: 900000, label: 'command-center', model:
|
|
3384
|
+
timeout: 900000, label: 'command-center', model: streamModel, maxTurns: 50,
|
|
3379
3385
|
allowedTools: 'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch',
|
|
3380
|
-
sessionId,
|
|
3386
|
+
sessionId, effort: streamEffort,
|
|
3381
3387
|
onChunk: (text) => {
|
|
3382
3388
|
try { res.write('data: ' + JSON.stringify({ type: 'chunk', text }) + '\n\n'); } catch {}
|
|
3383
3389
|
},
|
|
@@ -3582,6 +3588,15 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3582
3588
|
}
|
|
3583
3589
|
// String fields
|
|
3584
3590
|
if (e.worktreeRoot !== undefined) config.engine.worktreeRoot = String(e.worktreeRoot || D.worktreeRoot);
|
|
3591
|
+
// CC model/effort
|
|
3592
|
+
if (e.ccModel !== undefined) {
|
|
3593
|
+
const valid = ['sonnet', 'haiku', 'opus'];
|
|
3594
|
+
config.engine.ccModel = valid.includes(e.ccModel) ? e.ccModel : D.ccModel;
|
|
3595
|
+
}
|
|
3596
|
+
if (e.ccEffort !== undefined) {
|
|
3597
|
+
const valid = [null, 'low', 'medium', 'high'];
|
|
3598
|
+
config.engine.ccEffort = valid.includes(e.ccEffort) ? e.ccEffort : null;
|
|
3599
|
+
}
|
|
3585
3600
|
// Boolean fields
|
|
3586
3601
|
for (const key of ['autoApprovePlans', 'evalLoop', 'autoDecompose', 'allowTempAgents']) {
|
|
3587
3602
|
if (e[key] !== undefined) config.engine[key] = !!e[key];
|
package/engine/lifecycle.js
CHANGED
|
@@ -1019,6 +1019,12 @@ function updateMetrics(agentId, dispatchItem, result, taskUsage, prsCreatedCount
|
|
|
1019
1019
|
m.lastTask = dispatchItem.task;
|
|
1020
1020
|
m.lastCompleted = ts();
|
|
1021
1021
|
if (model) m.model = model;
|
|
1022
|
+
// Track runtime (wall-clock duration from dispatch start to completion)
|
|
1023
|
+
const runtimeMs = (dispatchItem.started_at && dispatchItem.completed_at)
|
|
1024
|
+
? new Date(dispatchItem.completed_at).getTime() - new Date(dispatchItem.started_at).getTime()
|
|
1025
|
+
: 0;
|
|
1026
|
+
if (runtimeMs > 0) m.totalRuntimeMs = (m.totalRuntimeMs || 0) + runtimeMs;
|
|
1027
|
+
|
|
1022
1028
|
if (result === DISPATCH_RESULT.SUCCESS) {
|
|
1023
1029
|
m.tasksCompleted++;
|
|
1024
1030
|
if (prsCreatedCount > 0) m.prsCreated = (m.prsCreated || 0) + prsCreatedCount;
|
|
@@ -1036,9 +1042,10 @@ function updateMetrics(agentId, dispatchItem, result, taskUsage, prsCreatedCount
|
|
|
1036
1042
|
}
|
|
1037
1043
|
const today = dateStamp();
|
|
1038
1044
|
if (!metrics._daily) metrics._daily = {};
|
|
1039
|
-
if (!metrics._daily[today]) metrics._daily[today] = { costUsd: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, tasks: 0 };
|
|
1045
|
+
if (!metrics._daily[today]) metrics._daily[today] = { costUsd: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, tasks: 0, runtimeMs: 0 };
|
|
1040
1046
|
const daily = metrics._daily[today];
|
|
1041
1047
|
daily.tasks++;
|
|
1048
|
+
if (runtimeMs > 0) daily.runtimeMs = (daily.runtimeMs || 0) + runtimeMs;
|
|
1042
1049
|
if (taskUsage) {
|
|
1043
1050
|
daily.costUsd += taskUsage.costUsd || 0;
|
|
1044
1051
|
daily.inputTokens += taskUsage.inputTokens || 0;
|
package/engine/llm.js
CHANGED
|
@@ -44,7 +44,7 @@ function trackEngineUsage(category, usage) {
|
|
|
44
44
|
|
|
45
45
|
// ── Core LLM Call ───────────────────────────────────────────────────────────
|
|
46
46
|
|
|
47
|
-
function callLLM(promptText, sysPromptText, { timeout = 120000, label = 'llm', model = 'sonnet', maxTurns = 1, allowedTools = '', sessionId = null } = {}) {
|
|
47
|
+
function callLLM(promptText, sysPromptText, { timeout = 120000, label = 'llm', model = 'sonnet', maxTurns = 1, allowedTools = '', sessionId = null, effort = null } = {}) {
|
|
48
48
|
return new Promise((resolve) => {
|
|
49
49
|
const id = uid();
|
|
50
50
|
const tmpDir = path.join(ENGINE_DIR, 'tmp');
|
|
@@ -61,6 +61,7 @@ function callLLM(promptText, sysPromptText, { timeout = 120000, label = 'llm', m
|
|
|
61
61
|
'--verbose',
|
|
62
62
|
];
|
|
63
63
|
if (allowedTools) args.push('--allowedTools', allowedTools);
|
|
64
|
+
if (effort) args.push('--effort', effort);
|
|
64
65
|
args.push('--permission-mode', 'bypassPermissions');
|
|
65
66
|
|
|
66
67
|
if (sessionId) args.push('--resume', sessionId);
|
|
@@ -113,7 +114,7 @@ function isResumeSessionStillValid(result) {
|
|
|
113
114
|
* Returns the same result object as callLLM when the process completes.
|
|
114
115
|
* onChunk(text) is called for each assistant text block as it arrives.
|
|
115
116
|
*/
|
|
116
|
-
function callLLMStreaming(promptText, sysPromptText, { timeout = 120000, label = 'llm', model = 'sonnet', maxTurns = 1, allowedTools = '', sessionId = null, onChunk = () => {}, onToolUse = null } = {}) {
|
|
117
|
+
function callLLMStreaming(promptText, sysPromptText, { timeout = 120000, label = 'llm', model = 'sonnet', maxTurns = 1, allowedTools = '', sessionId = null, onChunk = () => {}, onToolUse = null, effort = null } = {}) {
|
|
117
118
|
let _abort = null;
|
|
118
119
|
const promise = new Promise((resolve) => {
|
|
119
120
|
const id = uid();
|
|
@@ -131,6 +132,7 @@ function callLLMStreaming(promptText, sysPromptText, { timeout = 120000, label =
|
|
|
131
132
|
'--verbose',
|
|
132
133
|
];
|
|
133
134
|
if (allowedTools) args.push('--allowedTools', allowedTools);
|
|
135
|
+
if (effort) args.push('--effort', effort);
|
|
134
136
|
args.push('--permission-mode', 'bypassPermissions');
|
|
135
137
|
if (sessionId) args.push('--resume', sessionId);
|
|
136
138
|
|
package/engine/shared.js
CHANGED
|
@@ -529,6 +529,8 @@ const ENGINE_DEFAULTS = {
|
|
|
529
529
|
lockRetries: 2, // retry lock acquisition this many times after initial timeout (total attempts = 1 + lockRetries)
|
|
530
530
|
lockRetryBackoffMs: 500, // base backoff between lock retries (doubles each attempt: 500ms, 1s, 2s, ...)
|
|
531
531
|
maxBuildFixAttempts: 3, // max consecutive auto-fix dispatch cycles per PR before escalation to human
|
|
532
|
+
ccModel: 'sonnet', // model for Command Center and doc-chat (sonnet, haiku, opus)
|
|
533
|
+
ccEffort: null, // effort level for CC/doc-chat (null, 'low', 'medium', 'high')
|
|
532
534
|
};
|
|
533
535
|
|
|
534
536
|
// ─── Status & Type Constants ─────────────────────────────────────────────────
|
|
@@ -572,6 +574,7 @@ const DEFAULT_AGENT_METRICS = {
|
|
|
572
574
|
reviewsDone: 0,
|
|
573
575
|
lastTask: null, lastCompleted: null,
|
|
574
576
|
totalCostUsd: 0, totalInputTokens: 0, totalOutputTokens: 0, totalCacheRead: 0,
|
|
577
|
+
totalRuntimeMs: 0, // cumulative agent runtime across all tasks
|
|
575
578
|
};
|
|
576
579
|
|
|
577
580
|
const DEFAULT_AGENTS = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.568",
|
|
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"
|