@yemi33/minions 0.1.942 → 0.1.943
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 +7 -4
- package/dashboard/js/command-center.js +9 -3
- package/dashboard/js/detail-panel.js +1 -1
- package/dashboard/js/live-stream.js +4 -66
- package/dashboard/js/render-utils.js +174 -0
- package/dashboard/js/render-work-items.js +2 -2
- package/dashboard.js +15 -3
- package/engine/ado.js +7 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.943 (2026-04-14)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
6
|
- make ADO poll frequency configurable and ungate reconcilePrs
|
|
7
|
+
- update render-work-items.js output viewer to use renderAgentOutput
|
|
8
|
+
- update detail-panel.js Output Log tab to use renderAgentOutput
|
|
9
|
+
- Update command-center.js to capture tool inputs and use formatted summaries
|
|
7
10
|
- certificate-based auth for Teams integration (#1027)
|
|
11
|
+
- Create render-utils.js with shared formatting helpers
|
|
8
12
|
- add adoPollEnabled/ghPollEnabled engine settings
|
|
9
13
|
- doc-chat abort kills LLM process + queued messages auto-process
|
|
10
14
|
- add /api/work-items/reopen endpoint and reopen-work-item CC action (#982)
|
|
@@ -19,10 +23,9 @@
|
|
|
19
23
|
- fix pending-rebases.json race condition with file locking (#964)
|
|
20
24
|
- add missing resolveWorkItemPath import in engine.js (#963)
|
|
21
25
|
- bump plan-to-prd max turns from 20 to 35
|
|
22
|
-
- add weekly dead code & deprecated cleanup pipeline
|
|
23
|
-
- add red dot notification on CC tab when response completes (#934) (#946)
|
|
24
26
|
|
|
25
27
|
### Fixes
|
|
28
|
+
- use refs/pull/{id}/merge for build status scoping
|
|
26
29
|
- use repositoryId+reasonFilter for ADO build query
|
|
27
30
|
- deduplicate work item creation by title
|
|
28
31
|
- ADO build query uses repositoryId+pullRequest instead of branchName
|
|
@@ -42,9 +45,9 @@
|
|
|
42
45
|
- prioritize error_max_turns over permission-blocked in classifyFailure (#1001)
|
|
43
46
|
- optimistic archive hides both PRD and linked source plan simultaneously
|
|
44
47
|
- align CC tab unread dot to same position as working dot
|
|
45
|
-
- pipeline artifact links use pushModalBack instead of setTimeout race (#993)
|
|
46
48
|
|
|
47
49
|
### Other
|
|
50
|
+
- refactor: Make renderLiveChatMessage a thin wrapper over renderAgentOutput
|
|
48
51
|
- test(preflight): add unit tests for findClaudeBinary, runPreflight, printPreflight, checkOrExit (#953)
|
|
49
52
|
- test(meeting): add unit tests for key functions (#957)
|
|
50
53
|
- [E2E] Architecture meeting Tier 1+2: 6 correctness bugs + 2 nested lock violations + log buffering (#972)
|
|
@@ -148,7 +148,11 @@ function ccSwitchTab(id) {
|
|
|
148
148
|
var tools = tab._toolsUsed || [];
|
|
149
149
|
if (tools.length > 0) {
|
|
150
150
|
html += '<div style="margin-bottom:6px">';
|
|
151
|
-
tools.forEach(function(t) {
|
|
151
|
+
tools.forEach(function(t) {
|
|
152
|
+
var name = typeof t === 'string' ? t : t.name;
|
|
153
|
+
var input = typeof t === 'string' ? {} : (t.input || {});
|
|
154
|
+
html += '<div style="color:var(--muted);font-size:10px;font-family:monospace"><span style="flex-shrink:0">●</span> ' + formatToolSummary(name, input) + '</div>';
|
|
155
|
+
});
|
|
152
156
|
html += '</div>';
|
|
153
157
|
}
|
|
154
158
|
var text = tab._streamedText || '';
|
|
@@ -512,7 +516,9 @@ async function _ccDoSend(message, skipUserMsg, forceTabId) {
|
|
|
512
516
|
if (toolsUsed.length > 0) {
|
|
513
517
|
html += '<div style="margin-bottom:6px">';
|
|
514
518
|
toolsUsed.forEach(function(t) {
|
|
515
|
-
|
|
519
|
+
var name = typeof t === 'string' ? t : t.name;
|
|
520
|
+
var input = typeof t === 'string' ? {} : (t.input || {});
|
|
521
|
+
html += '<div style="color:var(--muted);font-size:10px;font-family:monospace"><span style="flex-shrink:0">●</span> ' + formatToolSummary(name, input) + '</div>';
|
|
516
522
|
});
|
|
517
523
|
html += '</div>';
|
|
518
524
|
}
|
|
@@ -572,7 +578,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId) {
|
|
|
572
578
|
if (activeTab) activeTab._streamedText = streamedText;
|
|
573
579
|
updateStreamDiv();
|
|
574
580
|
} else if (evt.type === 'tool') {
|
|
575
|
-
toolsUsed.push(evt.name);
|
|
581
|
+
toolsUsed.push({ name: evt.name, input: evt.input || {} });
|
|
576
582
|
if (activeTab) activeTab._toolsUsed = toolsUsed.slice();
|
|
577
583
|
updateStreamDiv();
|
|
578
584
|
if (msgs.scrollHeight - msgs.scrollTop - msgs.clientHeight < 150) msgs.scrollTop = msgs.scrollHeight;
|
|
@@ -141,7 +141,7 @@ function renderDetailContent(detail, tab) {
|
|
|
141
141
|
html += '<h4>Task History</h4><div class="section">' + renderMd(detail.history || 'No history yet.') + '</div>';
|
|
142
142
|
el.innerHTML = html;
|
|
143
143
|
} else if (tab === 'output') {
|
|
144
|
-
el.innerHTML = '<div class="section">' +
|
|
144
|
+
el.innerHTML = '<div class="section">' + (detail.outputLog ? renderAgentOutput(detail.outputLog) : 'No output log. The coordinator will save agent output here when tasks complete.') + '</div>';
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -22,70 +22,8 @@ function _updateRuntimeCounter() {
|
|
|
22
22
|
function renderLiveChatMessage(raw) {
|
|
23
23
|
const el = document.getElementById('live-messages');
|
|
24
24
|
if (!el) return;
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
function renderJsonObj(obj) {
|
|
28
|
-
if (obj.type === 'assistant' && obj.message?.content) {
|
|
29
|
-
for (const block of obj.message.content) {
|
|
30
|
-
if (block.type === 'thinking') {
|
|
31
|
-
fragments.push('<div style="font-size:10px;color:var(--muted);padding:2px 8px;font-style:italic">\u{1F4AD} Thinking...</div>');
|
|
32
|
-
}
|
|
33
|
-
if (block.type === 'text' && block.text) {
|
|
34
|
-
fragments.push('<div style="background:var(--surface2);padding:8px 12px;border-radius:12px 12px 12px 2px;max-width:90%;margin:4px 0;font-size:12px;word-break:break-word">' + renderMd(block.text) + '</div>');
|
|
35
|
-
}
|
|
36
|
-
if (block.type === 'tool_use') {
|
|
37
|
-
fragments.push('<div style="background:var(--surface);border:1px solid var(--border);padding:4px 8px;border-radius:4px;margin:2px 0;font-size:10px;color:var(--muted);cursor:pointer" onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display===\'none\'?\'block\':\'none\'">' +
|
|
38
|
-
'\u{1F527} ' + escHtml(block.name || 'tool') + '</div>' +
|
|
39
|
-
'<div style="display:none;background:var(--bg);padding:4px 8px;border-radius:4px;margin:0 0 4px;font-size:10px;font-family:monospace;white-space:pre-wrap;max-height:200px;overflow-y:auto;color:var(--muted)">' + escHtml(JSON.stringify(block.input || {}, null, 2).slice(0, 500)) + '</div>');
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (obj.type === 'tool_result' || (obj.type === 'user' && obj.message?.content?.[0]?.type === 'tool_result')) {
|
|
44
|
-
const content = obj.message?.content?.[0]?.content || obj.content || '';
|
|
45
|
-
const text = typeof content === 'string' ? content : JSON.stringify(content);
|
|
46
|
-
if (text.length > 10) {
|
|
47
|
-
fragments.push('<div style="background:var(--bg);border-left:2px solid var(--border);padding:2px 8px;margin:0 0 2px 16px;font-size:9px;font-family:monospace;color:var(--muted);max-height:100px;overflow-y:auto;white-space:pre-wrap;cursor:pointer" onclick="this.style.maxHeight=this.style.maxHeight===\'100px\'?\'none\':\'100px\'">' + escHtml(text.slice(0, 1000)) + (text.length > 1000 ? '...' : '') + '</div>');
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (obj.type === 'result') {
|
|
51
|
-
fragments.push('<div style="background:rgba(63,185,80,0.1);border:1px solid var(--green);padding:8px 12px;border-radius:8px;margin:8px 0;font-size:12px;color:var(--green)">\u2713 Task complete</div>');
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const lines = raw.split('\n');
|
|
56
|
-
for (const line of lines) {
|
|
57
|
-
const trimmed = line.trim();
|
|
58
|
-
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
59
|
-
|
|
60
|
-
if (trimmed.startsWith('[human-steering]')) {
|
|
61
|
-
const msg = trimmed.replace('[human-steering] ', '');
|
|
62
|
-
fragments.push('<div style="align-self:flex-end;background:var(--blue);color:#fff;padding:6px 12px;border-radius:12px 12px 2px 12px;max-width:80%;margin:4px 0;font-size:12px">' + escHtml(msg) +
|
|
63
|
-
'<div style="font-size:9px;opacity:0.7;margin-top:2px">\u2713 Queued</div></div>');
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (trimmed.startsWith('[heartbeat]')) continue;
|
|
67
|
-
if (trimmed.startsWith('[steering-failed]')) {
|
|
68
|
-
const msg = trimmed.replace('[steering-failed] ', '');
|
|
69
|
-
fragments.push('<div style="background:rgba(248,81,73,0.1);border:1px solid var(--red);color:var(--red);padding:6px 12px;border-radius:8px;margin:4px 0;font-size:11px">\u26A0 ' + escHtml(msg) + '</div>');
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (trimmed.startsWith('[')) {
|
|
73
|
-
try {
|
|
74
|
-
const arr = JSON.parse(trimmed);
|
|
75
|
-
if (Array.isArray(arr)) { for (const obj of arr) renderJsonObj(obj); continue; }
|
|
76
|
-
} catch { /* fall through */ }
|
|
77
|
-
}
|
|
78
|
-
if (trimmed.startsWith('{')) {
|
|
79
|
-
try { renderJsonObj(JSON.parse(trimmed)); continue; } catch { /* fall through */ }
|
|
80
|
-
}
|
|
81
|
-
if (trimmed.startsWith('[stderr]')) {
|
|
82
|
-
fragments.push('<div style="font-size:9px;color:var(--red);font-family:monospace;padding:1px 4px">' + escHtml(trimmed) + '</div>');
|
|
83
|
-
} else {
|
|
84
|
-
fragments.push('<div style="font-size:10px;color:var(--muted);font-family:monospace;padding:1px 4px">' + escHtml(trimmed) + '</div>');
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (fragments.length > 0) el.innerHTML += fragments.join('');
|
|
25
|
+
const html = renderAgentOutput(raw);
|
|
26
|
+
if (html) el.insertAdjacentHTML('beforeend', html);
|
|
89
27
|
|
|
90
28
|
// Auto-scroll
|
|
91
29
|
if (el.scrollHeight - el.scrollTop - el.clientHeight < 150) {
|
|
@@ -162,8 +100,8 @@ async function sendSteering() {
|
|
|
162
100
|
// Immediate feedback — show the message right away
|
|
163
101
|
const el = document.getElementById('live-messages');
|
|
164
102
|
if (el) {
|
|
165
|
-
el.
|
|
166
|
-
'<div id="steer-pending" style="font-size:9px;opacity:0.7;margin-top:2px">\u2197 Sending...</div></div>';
|
|
103
|
+
el.insertAdjacentHTML('beforeend', '<div style="align-self:flex-end;background:var(--blue);color:#fff;padding:6px 12px;border-radius:12px 12px 2px 12px;max-width:80%;margin:4px 0;font-size:12px">' + escHtml(message) +
|
|
104
|
+
'<div id="steer-pending" style="font-size:9px;opacity:0.7;margin-top:2px">\u2197 Sending...</div></div>');
|
|
167
105
|
el.scrollTop = el.scrollHeight;
|
|
168
106
|
}
|
|
169
107
|
showToast('cmd-toast', 'Steering message sent to ' + currentAgentId, true);
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// dashboard/js/render-utils.js — Shared formatting helpers for agent output rendering
|
|
2
|
+
// Depends on: escHtml() and renderMd() from utils.js (loaded before this file)
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a one-line human-readable summary for a Claude tool call.
|
|
6
|
+
* @param {string} name - Tool name (e.g. 'Bash', 'Read', 'Edit')
|
|
7
|
+
* @param {object} input - Tool input object
|
|
8
|
+
* @returns {string} Human-readable summary (HTML-escaped)
|
|
9
|
+
*/
|
|
10
|
+
function formatToolSummary(name, input) {
|
|
11
|
+
var inp = input || {};
|
|
12
|
+
switch (name) {
|
|
13
|
+
case 'Bash': {
|
|
14
|
+
var cmd = String(inp.command || '');
|
|
15
|
+
if (cmd.length > 80) cmd = cmd.slice(0, 77) + '...';
|
|
16
|
+
return '$ ' + escHtml(cmd);
|
|
17
|
+
}
|
|
18
|
+
case 'Read':
|
|
19
|
+
return 'Reading ' + escHtml(inp.file_path || '');
|
|
20
|
+
case 'Edit':
|
|
21
|
+
return 'Editing ' + escHtml(inp.file_path || '');
|
|
22
|
+
case 'Write':
|
|
23
|
+
return 'Writing ' + escHtml(inp.file_path || '');
|
|
24
|
+
case 'Grep': {
|
|
25
|
+
var pat = escHtml(inp.pattern || '');
|
|
26
|
+
var gPath = escHtml(inp.path || '.');
|
|
27
|
+
return 'Searching `' + pat + '` in ' + gPath;
|
|
28
|
+
}
|
|
29
|
+
case 'Glob':
|
|
30
|
+
return 'Glob ' + escHtml(inp.pattern || '');
|
|
31
|
+
case 'Agent':
|
|
32
|
+
return 'Spawning agent: ' + escHtml(inp.description || '');
|
|
33
|
+
case 'WebFetch':
|
|
34
|
+
return 'Fetch ' + escHtml(inp.url || '');
|
|
35
|
+
case 'WebSearch':
|
|
36
|
+
return 'Search "' + escHtml(inp.query || '') + '"';
|
|
37
|
+
case 'TodoWrite': {
|
|
38
|
+
var items = Array.isArray(inp.todos) ? inp.todos : [];
|
|
39
|
+
return 'Update todos (' + items.length + ' items)';
|
|
40
|
+
}
|
|
41
|
+
default: {
|
|
42
|
+
var keys = Object.keys(inp);
|
|
43
|
+
if (keys.length === 0) return escHtml(name) + '()';
|
|
44
|
+
var firstKey = keys[0];
|
|
45
|
+
var firstVal = String(inp[firstKey] || '');
|
|
46
|
+
if (firstVal.length > 40) firstVal = firstVal.slice(0, 37) + '...';
|
|
47
|
+
return escHtml(name) + '(' + escHtml(firstKey) + ': ' + escHtml(firstVal) + ')';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Internal helper: renders a single parsed JSON object into an HTML fragment.
|
|
54
|
+
* @param {object} obj - Parsed JSON object from agent JSONL output
|
|
55
|
+
* @returns {string} HTML fragment
|
|
56
|
+
*/
|
|
57
|
+
function _renderJsonObj(obj) {
|
|
58
|
+
var parts = [];
|
|
59
|
+
|
|
60
|
+
if (obj.type === 'assistant' && obj.message && obj.message.content) {
|
|
61
|
+
var content = obj.message.content;
|
|
62
|
+
for (var i = 0; i < content.length; i++) {
|
|
63
|
+
var block = content[i];
|
|
64
|
+
if (block.type === 'thinking') {
|
|
65
|
+
parts.push('<div style="font-size:10px;color:var(--muted);padding:2px 8px;font-style:italic">\u{1F4AD} Thinking...</div>');
|
|
66
|
+
}
|
|
67
|
+
if (block.type === 'text' && block.text) {
|
|
68
|
+
parts.push('<div style="display:flex;align-items:baseline;gap:6px;margin:4px 0">' +
|
|
69
|
+
'<span style="color:var(--muted);font-size:10px;flex-shrink:0">●</span>' +
|
|
70
|
+
'<div style="background:var(--surface2);padding:8px 12px;border-radius:12px 12px 12px 2px;max-width:90%;font-size:12px;word-break:break-word">' + renderMd(block.text) + '</div>' +
|
|
71
|
+
'</div>');
|
|
72
|
+
}
|
|
73
|
+
if (block.type === 'tool_use') {
|
|
74
|
+
var summary = formatToolSummary(block.name || 'tool', block.input || {});
|
|
75
|
+
var rawJson = escHtml(JSON.stringify(block.input || {}, null, 2).slice(0, 500));
|
|
76
|
+
parts.push(
|
|
77
|
+
'<div style="display:flex;align-items:center;gap:4px;margin:2px 0;font-size:10px;color:var(--muted);font-family:monospace">' +
|
|
78
|
+
'<span style="flex-shrink:0">●</span>' +
|
|
79
|
+
'<span>' + summary + '</span>' +
|
|
80
|
+
'<span style="cursor:pointer;opacity:0.6;margin-left:4px" onclick="var t=this.parentElement.nextElementSibling;t.style.display=t.style.display===\'none\'?\'block\':\'none\';this.textContent=t.style.display===\'none\'?\'[+]\':\'[-]\'">[+]</span>' +
|
|
81
|
+
'</div>' +
|
|
82
|
+
'<div style="display:none;background:var(--bg);padding:4px 8px;border-radius:4px;margin:0 0 4px 16px;font-size:10px;font-family:monospace;white-space:pre-wrap;max-height:200px;overflow-y:auto;color:var(--muted)">' + rawJson + '</div>'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (obj.type === 'tool_result' || (obj.type === 'user' && obj.message && obj.message.content && obj.message.content[0] && obj.message.content[0].type === 'tool_result')) {
|
|
89
|
+
var tc = (obj.message && obj.message.content && obj.message.content[0] && obj.message.content[0].content) || obj.content || '';
|
|
90
|
+
var text = typeof tc === 'string' ? tc : JSON.stringify(tc);
|
|
91
|
+
if (text.length > 10) {
|
|
92
|
+
var truncated = text.length > 3000;
|
|
93
|
+
var displayText = truncated ? text.slice(0, 3000) + '...' : text;
|
|
94
|
+
parts.push('<div style="background:var(--surface);border-left:2px solid var(--border);padding:2px 8px;margin:0 0 2px 16px;font-size:9px;font-family:monospace;color:var(--muted);max-height:160px;overflow-y:auto;white-space:pre-wrap;cursor:pointer" onclick="this.style.maxHeight=this.style.maxHeight===\'160px\'?\'none\':\'160px\'">' + escHtml(displayText) + '</div>');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (obj.type === 'result') {
|
|
99
|
+
parts.push('<div style="background:rgba(63,185,80,0.1);border:1px solid var(--green);padding:8px 12px;border-radius:8px;margin:8px 0;font-size:12px;color:var(--green)">\u2713 Task complete</div>');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return parts.join('');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Takes raw JSONL agent output text and returns an HTML string with formatted rendering.
|
|
107
|
+
* Parses JSON objects, tool summaries with ● prefix and [+] toggles for raw JSON,
|
|
108
|
+
* tool results with surface tint and 160px max-height, assistant text with ● prefix
|
|
109
|
+
* and markdown rendering, steering bubbles with ❯ prefix, completion indicator,
|
|
110
|
+
* stderr lines, and heartbeat filtering.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} text - Raw JSONL agent output
|
|
113
|
+
* @returns {string} HTML string
|
|
114
|
+
*/
|
|
115
|
+
function renderAgentOutput(text) {
|
|
116
|
+
if (!text) return '';
|
|
117
|
+
var fragments = [];
|
|
118
|
+
var lines = text.split('\n');
|
|
119
|
+
|
|
120
|
+
for (var i = 0; i < lines.length; i++) {
|
|
121
|
+
var trimmed = lines[i].trim();
|
|
122
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
123
|
+
|
|
124
|
+
// Heartbeat — filter out
|
|
125
|
+
if (trimmed.startsWith('[heartbeat]')) continue;
|
|
126
|
+
|
|
127
|
+
// Human steering
|
|
128
|
+
if (trimmed.startsWith('[human-steering]')) {
|
|
129
|
+
var msg = trimmed.replace('[human-steering] ', '');
|
|
130
|
+
fragments.push('<div style="align-self:flex-end;background:var(--blue);color:#fff;padding:6px 12px;border-radius:12px 12px 2px 12px;max-width:80%;margin:4px 0;font-size:12px">' +
|
|
131
|
+
'<span style="margin-right:4px">❯</span>' + escHtml(msg) +
|
|
132
|
+
'<div style="font-size:9px;opacity:0.7;margin-top:2px">\u2713 Queued</div></div>');
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Steering failed
|
|
137
|
+
if (trimmed.startsWith('[steering-failed]')) {
|
|
138
|
+
var failMsg = trimmed.replace('[steering-failed] ', '');
|
|
139
|
+
fragments.push('<div style="background:rgba(248,81,73,0.1);border:1px solid var(--red);color:var(--red);padding:6px 12px;border-radius:8px;margin:4px 0;font-size:11px">\u26A0 ' + escHtml(failMsg) + '</div>');
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// JSON array line
|
|
144
|
+
if (trimmed.startsWith('[')) {
|
|
145
|
+
try {
|
|
146
|
+
var arr = JSON.parse(trimmed);
|
|
147
|
+
if (Array.isArray(arr)) {
|
|
148
|
+
for (var j = 0; j < arr.length; j++) fragments.push(_renderJsonObj(arr[j]));
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
} catch (e) { /* fall through */ }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// JSON object line
|
|
155
|
+
if (trimmed.startsWith('{')) {
|
|
156
|
+
try {
|
|
157
|
+
fragments.push(_renderJsonObj(JSON.parse(trimmed)));
|
|
158
|
+
continue;
|
|
159
|
+
} catch (e) { /* fall through */ }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Stderr
|
|
163
|
+
if (trimmed.startsWith('[stderr]')) {
|
|
164
|
+
fragments.push('<div style="font-size:9px;color:var(--red);font-family:monospace;padding:1px 4px">' + escHtml(trimmed) + '</div>');
|
|
165
|
+
} else {
|
|
166
|
+
// Plain text fallback
|
|
167
|
+
fragments.push('<div style="font-size:10px;color:var(--muted);font-family:monospace;padding:1px 4px">' + escHtml(trimmed) + '</div>');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return fragments.join('');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
window.MinionsRenderUtils = { formatToolSummary, renderAgentOutput };
|
|
@@ -509,12 +509,12 @@ function viewAgentOutput(logPath) {
|
|
|
509
509
|
document.getElementById('modal-title').textContent = 'Agent Output';
|
|
510
510
|
document.getElementById('modal-body').innerHTML = '<p style="color:var(--muted)">Loading...</p>';
|
|
511
511
|
document.getElementById('modal-body').style.fontFamily = 'Consolas, monospace';
|
|
512
|
-
document.getElementById('modal-body').style.whiteSpace = '
|
|
512
|
+
document.getElementById('modal-body').style.whiteSpace = 'normal';
|
|
513
513
|
document.getElementById('modal').classList.add('open');
|
|
514
514
|
fetch('/api/agent-output?file=' + encodeURIComponent(logPath))
|
|
515
515
|
.then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); })
|
|
516
516
|
.then(function(content) {
|
|
517
|
-
document.getElementById('modal-body').
|
|
517
|
+
document.getElementById('modal-body').innerHTML = renderAgentOutput(content);
|
|
518
518
|
})
|
|
519
519
|
.catch(function() {
|
|
520
520
|
document.getElementById('modal-body').innerHTML = '<p style="color:var(--red)">Failed to load output.</p>';
|
package/dashboard.js
CHANGED
|
@@ -85,7 +85,7 @@ function buildDashboardHtml() {
|
|
|
85
85
|
|
|
86
86
|
// Assemble JS modules (order matters: utils → state → renderers → commands → refresh)
|
|
87
87
|
const jsFiles = [
|
|
88
|
-
'utils', 'state', 'detail-panel', 'live-stream',
|
|
88
|
+
'utils', 'state', 'render-utils', 'detail-panel', 'live-stream',
|
|
89
89
|
'render-agents', 'render-dispatch', 'render-work-items', 'render-prd',
|
|
90
90
|
'render-prs', 'render-plans', 'render-inbox', 'render-kb', 'render-skills',
|
|
91
91
|
'render-other', 'render-schedules', 'render-pipelines', 'render-meetings', 'render-pinned',
|
|
@@ -3448,6 +3448,18 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3448
3448
|
} catch (e) { ccInFlightTabs.delete(tabId); return jsonReply(res, 500, { error: e.message }); }
|
|
3449
3449
|
}
|
|
3450
3450
|
|
|
3451
|
+
/** Build a lightweight input object for SSE tool events — keeps only the fields formatToolSummary needs, with truncated string values. */
|
|
3452
|
+
function _lightToolInput(input) {
|
|
3453
|
+
if (!input || typeof input !== 'object') return {};
|
|
3454
|
+
const out = {};
|
|
3455
|
+
for (const [k, v] of Object.entries(input)) {
|
|
3456
|
+
if (Array.isArray(v)) { out[k] = v; }
|
|
3457
|
+
else if (typeof v === 'string') { out[k] = v.length > 200 ? v.slice(0, 197) + '...' : v; }
|
|
3458
|
+
else { out[k] = v; }
|
|
3459
|
+
}
|
|
3460
|
+
return out;
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3451
3463
|
async function handleCommandCenterStream(req, res) {
|
|
3452
3464
|
if (checkRateLimit('command-center', 10)) { res.statusCode = 429; res.end('Rate limited'); return; }
|
|
3453
3465
|
try {
|
|
@@ -3503,7 +3515,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3503
3515
|
try { res.write('data: ' + JSON.stringify({ type: 'chunk', text: display }) + '\n\n'); } catch {}
|
|
3504
3516
|
},
|
|
3505
3517
|
onToolUse: (name, input) => {
|
|
3506
|
-
try { res.write('data: ' + JSON.stringify({ type: 'tool', name, input:
|
|
3518
|
+
try { res.write('data: ' + JSON.stringify({ type: 'tool', name, input: _lightToolInput(input) }) + '\n\n'); } catch {}
|
|
3507
3519
|
}
|
|
3508
3520
|
});
|
|
3509
3521
|
_ccStreamAbort = llmPromise.abort;
|
|
@@ -3526,7 +3538,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3526
3538
|
try { res.write('data: ' + JSON.stringify({ type: 'chunk', text: display }) + '\n\n'); } catch {}
|
|
3527
3539
|
},
|
|
3528
3540
|
onToolUse: (name, input) => {
|
|
3529
|
-
try { res.write('data: ' + JSON.stringify({ type: 'tool', name, input:
|
|
3541
|
+
try { res.write('data: ' + JSON.stringify({ type: 'tool', name, input: _lightToolInput(input) }) + '\n\n'); } catch {}
|
|
3530
3542
|
}
|
|
3531
3543
|
});
|
|
3532
3544
|
_ccStreamAbort = retryPromise.abort;
|
package/engine/ado.js
CHANGED
|
@@ -372,19 +372,19 @@ async function pollPrStatus(config) {
|
|
|
372
372
|
if (newStatus !== PR_STATUS.ACTIVE) return updated;
|
|
373
373
|
|
|
374
374
|
// Query builds API directly — status checks (/statuses) are unreliable (stale codecoverage postbacks)
|
|
375
|
-
// Use the merge
|
|
376
|
-
const
|
|
375
|
+
// Use refs/pull/{id}/merge — the synthetic merge ref ADO creates for each PR — to scope builds exactly
|
|
376
|
+
const prNumber = pr.prNumber;
|
|
377
377
|
let buildStatus = 'none';
|
|
378
378
|
let buildFailReason = '';
|
|
379
379
|
let buildStatuses = []; // for error log fetching
|
|
380
380
|
|
|
381
|
-
if (
|
|
381
|
+
if (prNumber) {
|
|
382
382
|
try {
|
|
383
|
-
//
|
|
384
|
-
const
|
|
383
|
+
// branchName=refs/pull/{id}/merge targets exactly this PR's merge-commit builds (server-side filter, no client matching needed)
|
|
384
|
+
const mergeRef = encodeURIComponent(`refs/pull/${prNumber}/merge`);
|
|
385
|
+
const buildsUrl = `${orgBase}/${project.adoProject}/_apis/build/builds?branchName=${mergeRef}&repositoryId=${project.repositoryId}&repositoryType=TfsGit&$top=10&api-version=7.1`;
|
|
385
386
|
const buildsData = await adoFetch(buildsUrl, token);
|
|
386
|
-
|
|
387
|
-
const prBuilds = (buildsData?.value || []).filter(b => b.sourceVersion === mergeCommitId);
|
|
387
|
+
const prBuilds = buildsData?.value || [];
|
|
388
388
|
|
|
389
389
|
if (prBuilds.length > 0) {
|
|
390
390
|
// partiallySucceeded = warnings, not failures — counts as passing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.943",
|
|
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"
|