@yemi33/minions 0.1.2060 → 0.1.2061

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.
@@ -69,24 +69,18 @@ function renderAgents(agents) {
69
69
  `).join('');
70
70
  }
71
71
 
72
- async function openAgentDetail(id) {
73
- const agent = agentData.find(a => a.id === id);
74
- if (!agent) return;
75
- currentAgentId = id;
76
- currentTab = (agent.status === 'working') ? 'live' : 'thought-process';
77
-
78
- // SEC-03 Phase A: Build the detail header via DOM + textContent instead of innerHTML.
79
- // Emoji, name and role are all user-controlled fields; routing them through textContent
80
- // guarantees no HTML interpretation even if the escape function were ever bypassed.
72
+ // Re-render the detail-panel header (name + emoji + runtime/model chips +
73
+ // status badge + lastAction + blocking-tool / resultSummary blocks) from a
74
+ // slim agentData record. Extracted so renderAgents can call it every poll
75
+ // tick to keep an open detail panel live without re-fetching the expensive
76
+ // /api/agent/<id> charter/history/output payload. SEC-03 Phase A rules
77
+ // still apply: emoji/name/role go through textContent.
78
+ function _renderAgentDetailHeader(agent) {
81
79
  const nameEl = document.getElementById('detail-agent-name');
80
+ if (!nameEl) return;
82
81
  const emojiSpan = document.createElement('span');
83
82
  emojiSpan.style.fontSize = '22px';
84
83
  emojiSpan.textContent = agent.emoji || '';
85
- // Runtime tag \u2014 uses the inline-SVG logo from the same RUNTIME_TAGS map the
86
- // card uses, so the visual is consistent. The container's user-controlled
87
- // text fields stay on the textContent path; the SVG is a hardcoded literal
88
- // from RUNTIME_TAGS keyed by the runtime string (server-controlled, finite
89
- // set), so injecting via innerHTML on the icon-only span is safe.
90
84
  const runtimeMeta = RUNTIME_TAGS[agent.runtime];
91
85
  const runtimeSpan = document.createElement('span');
92
86
  runtimeSpan.title = 'Runtime: ' + (runtimeMeta?.label || agent.runtime || 'unknown');
@@ -100,9 +94,6 @@ async function openAgentDetail(id) {
100
94
  runtimeSpan.style.cssText += ';font-size:10px;font-weight:600;letter-spacing:0.4px;text-transform:uppercase;padding:2px 6px;border:1px solid var(--muted);border-radius:3px;color:var(--muted)';
101
95
  runtimeSpan.textContent = agent.runtime || 'unknown';
102
96
  }
103
- // W-mpmwxk4y00053271 — mirror the model chip the card shows so the detail
104
- // header stays in sync. textContent path keeps the model string from being
105
- // interpreted as HTML.
106
97
  const modelSpan = (agent.model && typeof agent.model === 'string')
107
98
  ? (() => {
108
99
  const s = document.createElement('span');
@@ -121,12 +112,27 @@ async function openAgentDetail(id) {
121
112
  nameEl.replaceChildren(...children);
122
113
 
123
114
  const badgeClass = agent.status;
124
- // eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; status is an internal bounded enum and all user data is wrapped in escapeHtml()/renderMd() (fields: lastAction, blocking tool, resultSummary)
125
- document.getElementById('detail-status-line').innerHTML =
126
- '<span class="status-badge ' + badgeClass + '">' + agent.status.toUpperCase() + '</span> ' +
127
- '<span style="color:var(--muted)">' + escapeHtml(agent.lastAction) + '</span>' +
128
- (agent._blockingToolCall ? '<div style="margin-top:4px;padding:4px 8px;background:rgba(130,160,210,0.13);border:1px solid rgba(130,160,210,0.3);border-radius:4px;font-size:11px;color:var(--muted)">&#x23F3; Blocking tool call (' + escapeHtml(agent._blockingToolCall.tool) + ') &mdash; silent ' + Math.round(agent._blockingToolCall.silentMs/60000) + 'min, timeout in ' + Math.round(agent._blockingToolCall.remainingMs/60000) + 'min</div>' : '') +
129
- (agent.resultSummary ? '<div style="margin-top:4px;font-size:11px;color:var(--text);line-height:1.4">' + renderMd(agent.resultSummary.slice(0, 300)) + '</div>' : '');
115
+ const statusEl = document.getElementById('detail-status-line');
116
+ if (statusEl) {
117
+ // eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; status is an internal bounded enum and all user data is wrapped in escapeHtml()/renderMd() (fields: lastAction, blocking tool, resultSummary)
118
+ statusEl.innerHTML =
119
+ '<span class="status-badge ' + badgeClass + '">' + agent.status.toUpperCase() + '</span> ' +
120
+ '<span style="color:var(--muted)">' + escapeHtml(agent.lastAction) + '</span>' +
121
+ (agent._blockingToolCall ? '<div style="margin-top:4px;padding:4px 8px;background:rgba(130,160,210,0.13);border:1px solid rgba(130,160,210,0.3);border-radius:4px;font-size:11px;color:var(--muted)">&#x23F3; Blocking tool call (' + escapeHtml(agent._blockingToolCall.tool) + ') &mdash; silent ' + Math.round(agent._blockingToolCall.silentMs/60000) + 'min, timeout in ' + Math.round(agent._blockingToolCall.remainingMs/60000) + 'min</div>' : '') +
122
+ (agent.resultSummary ? '<div style="margin-top:4px;font-size:11px;color:var(--text);line-height:1.4">' + renderMd(agent.resultSummary.slice(0, 300)) + '</div>' : '');
123
+ }
124
+ }
125
+
126
+ async function openAgentDetail(id) {
127
+ const agent = agentData.find(a => a.id === id);
128
+ if (!agent) return;
129
+ currentAgentId = id;
130
+ currentTab = (agent.status === 'working') ? 'live' : 'thought-process';
131
+
132
+ // SEC-03 Phase A: Build the detail header via DOM + textContent instead of innerHTML.
133
+ // Emoji, name and role are all user-controlled fields; routing them through textContent
134
+ // guarantees no HTML interpretation even if the escape function were ever bypassed.
135
+ _renderAgentDetailHeader(agent);
130
136
 
131
137
  // Show panel immediately with loading state — don't wait for API
132
138
  document.getElementById('detail-content').innerHTML = '<div style="padding:24px;text-align:center;color:var(--muted)">Loading...</div>';
@@ -161,7 +167,12 @@ function _tickAgentRuntimes() {
161
167
  el.textContent = 'Running: ' + (hr > 0 ? hr + 'h ' : '') + min + 'm ' + sec + 's';
162
168
  });
163
169
  }
164
- // Start ticker after each render if working agents exist
170
+ // Start ticker after each render if working agents exist, and refresh the
171
+ // open agent-detail panel header so status / lastAction / blocking-tool /
172
+ // resultSummary track the latest /api/status slice without re-fetching the
173
+ // expensive /api/agent/<id> charter/history/output payload. The body tabs
174
+ // (thought-process, live, output, etc.) are loaded once on open via
175
+ // openAgentDetail's safeFetch — they stay as-is until the user reopens.
165
176
  var _origRenderAgents = renderAgents;
166
177
  renderAgents = function(agents) {
167
178
  _origRenderAgents(agents);
@@ -170,6 +181,10 @@ renderAgents = function(agents) {
170
181
  _tickAgentRuntimes();
171
182
  _agentRuntimeTimer = setInterval(_tickAgentRuntimes, 1000);
172
183
  }
184
+ if (currentAgentId) {
185
+ var open = agents.find(function(a) { return a.id === currentAgentId; });
186
+ if (open) _renderAgentDetailHeader(open);
187
+ }
173
188
  };
174
189
 
175
190
  window.MinionsAgents = { renderAgents, openAgentDetail };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2060",
3
+ "version": "0.1.2061",
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"