@yemi33/minions 0.1.2424 → 0.1.2426
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/bin/install-internal-minions.js +1209 -0
- package/bin/minions.js +75 -9
- package/dashboard/docs/typography.md +27 -12
- package/dashboard/js/command-center.js +13 -11
- package/dashboard/js/live-stream.js +1 -1
- package/dashboard/js/memory-search.js +388 -43
- package/dashboard/js/modal-qa.js +5 -5
- package/dashboard/js/qa.js +39 -26
- package/dashboard/js/refresh.js +37 -18
- package/dashboard/js/render-agents.js +14 -18
- package/dashboard/js/render-dispatch.js +14 -74
- package/dashboard/js/render-inbox.js +3 -3
- package/dashboard/js/render-meetings.js +8 -8
- package/dashboard/js/render-other.js +45 -20
- package/dashboard/js/render-pipelines.js +6 -6
- package/dashboard/js/render-plans.js +14 -14
- package/dashboard/js/render-prd.js +46 -46
- package/dashboard/js/render-prs.js +9 -65
- package/dashboard/js/render-schedules.js +5 -5
- package/dashboard/js/render-skills.js +6 -6
- package/dashboard/js/render-utils.js +4 -4
- package/dashboard/js/render-watches.js +4 -16
- package/dashboard/js/render-work-items.js +95 -107
- package/dashboard/js/settings.js +175 -74
- package/dashboard/layout.html +3 -2
- package/dashboard/pages/inbox.html +1 -1
- package/dashboard/pages/work.html +1 -1
- package/dashboard/shared/model-display.js +9 -0
- package/dashboard/shared/watches-source.js +43 -0
- package/dashboard/shared/welcome-popup.js +259 -0
- package/dashboard/slim/body.html +6 -6
- package/dashboard/slim/js/chat.js +4 -2
- package/dashboard/slim/js/history.js +10 -3
- package/dashboard/slim/js/modals-tiles.js +85 -16
- package/dashboard/slim/js/status.js +12 -16
- package/dashboard/slim/styles.css +24 -4
- package/dashboard/styles.css +62 -27
- package/dashboard-build.js +8 -2
- package/dashboard.js +3854 -2514
- package/docs/README.md +7 -5
- package/docs/api-errors.md +144 -0
- package/docs/auto-discovery.md +83 -61
- package/docs/capture-demos.js +173 -30
- package/docs/command-center.md +5 -1
- package/docs/completion-reports.md +127 -11
- package/docs/constants.md +15 -3
- package/docs/constellation-style-telemetry.md +2 -2
- package/docs/copilot-cli-schema.md +32 -22
- package/docs/cross-repo-plans.md +30 -30
- package/docs/demo/memory-system.html +1 -1
- package/docs/deprecated.json +132 -18
- package/docs/diagnostics-memory.md +8 -4
- package/docs/human-vs-automated.md +2 -2
- package/docs/index.html +9 -2
- package/docs/internal-install.md +212 -0
- package/docs/kb-pr3223-cascade-archiving.md +16 -0
- package/docs/kb-pr696-merge-conflict-docs.md +23 -0
- package/docs/kb-sweep.md +2 -2
- package/docs/keep-processes.md +8 -1
- package/docs/live-checkout-mode.md +1 -1
- package/docs/managed-spawn.md +21 -3
- package/docs/named-agents.md +3 -2
- package/docs/onboarding.md +26 -0
- package/docs/plan-lifecycle.md +32 -32
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
- package/docs/qa-runbook-lifecycle.md +67 -51
- package/docs/qa-runbooks.md +2 -2
- package/docs/runtime-adapters.md +101 -50
- package/docs/security.md +28 -0
- package/docs/self-improvement.md +47 -13
- package/docs/skills.md +29 -0
- package/docs/slim-ux/concepts.md +2 -2
- package/docs/specs/agent-configurability.md +43 -41
- package/docs/specs/agent-rename.md +13 -13
- package/docs/team-memory.md +283 -14
- package/docs/tutorials/01-install-and-connect.md +23 -0
- package/docs/tutorials/03-plan-driven-feature.md +3 -3
- package/docs/tutorials/04-runtimes-and-harness.md +18 -21
- package/docs/tutorials/08-operations-and-recovery.md +3 -2
- package/docs/tutorials/README.md +4 -3
- package/docs/visual-evidence-ci.md +103 -0
- package/docs/watches.md +35 -30
- package/docs/worktree-lifecycle.md +151 -13
- package/engine/acp-transport.js +1 -0
- package/engine/ado.js +80 -33
- package/engine/agent-api-validation.js +571 -0
- package/engine/agent-worker-pool.js +7 -6
- package/engine/api-contracts/agent-content.js +761 -0
- package/engine/api-contracts/cc-ops.js +219 -0
- package/engine/api-contracts/config-runtime.js +419 -0
- package/engine/api-contracts/core.js +138 -0
- package/engine/api-contracts/index.js +506 -0
- package/engine/api-contracts/orchestration.js +602 -0
- package/engine/api-contracts/pull-requests.js +323 -0
- package/engine/api-contracts/qa-process.js +764 -0
- package/engine/api-contracts/work-plan-prd.js +494 -0
- package/engine/api-validation.js +543 -0
- package/engine/cc-api-validation.js +586 -0
- package/engine/cc-worker-pool.js +9 -1
- package/engine/cleanup.js +393 -271
- package/engine/cli.js +65 -7
- package/engine/comment-classifier.js +70 -5
- package/engine/consolidation.js +123 -5
- package/engine/content-api-validation.js +606 -0
- package/engine/create-pr-worktree.js +22 -1
- package/engine/db/migrations/027-review-learning-lifecycle.js +162 -0
- package/engine/dispatch.js +33 -0
- package/engine/distribution.js +189 -0
- package/engine/execution-model.js +59 -0
- package/engine/features.js +12 -0
- package/engine/github.js +67 -25
- package/engine/harness-context.js +313 -0
- package/engine/inbox-store.js +1 -1
- package/engine/kb-sweep.js +161 -38
- package/engine/keep-process-sweep.js +43 -3
- package/engine/lifecycle.js +1066 -165
- package/engine/llm.js +4 -0
- package/engine/managed-spawn.js +463 -48
- package/engine/meeting.js +197 -34
- package/engine/memory-retrieval.js +280 -0
- package/engine/memory-store.js +821 -38
- package/engine/model-discovery.js +17 -3
- package/engine/pipeline.js +802 -14
- package/engine/plan-prd-validation.js +745 -0
- package/engine/playbook.js +206 -17
- package/engine/pooled-agent-process.js +6 -1
- package/engine/pr-action.js +1 -1
- package/engine/pr-issue-validation.js +653 -0
- package/engine/pr-resolve.js +75 -13
- package/engine/prd-store.js +39 -6
- package/engine/preflight.js +7 -2
- package/engine/process-utils.js +79 -31
- package/engine/projects.js +198 -12
- package/engine/promotion.js +371 -0
- package/engine/qa-from-prd.js +3 -0
- package/engine/qa-process-validation.js +572 -0
- package/engine/qa-runbooks.js +41 -37
- package/engine/qa-runners/maestro.js +11 -7
- package/engine/qa-runners/playwright.js +37 -14
- package/engine/qa-runners.js +9 -10
- package/engine/qa-runs.js +124 -11
- package/engine/qa-sessions.js +139 -40
- package/engine/quarantine-refs.js +81 -11
- package/engine/queries.js +163 -9
- package/engine/review-learning-backfill.js +482 -0
- package/engine/review-learning.js +1236 -0
- package/engine/runtimes/claude.js +60 -2
- package/engine/runtimes/codex.js +57 -16
- package/engine/runtimes/contract.js +21 -0
- package/engine/runtimes/copilot.js +51 -3
- package/engine/runtimes/index.js +1 -0
- package/engine/scheduler.js +201 -32
- package/engine/settings-validation.js +909 -0
- package/engine/shared.js +705 -208
- package/engine/spawn-agent.js +48 -21
- package/engine/steering-constraints.js +31 -0
- package/engine/steering-store.js +10 -2
- package/engine/steering.js +14 -2
- package/engine/timeout.js +404 -111
- package/engine/untrusted-fence.js +24 -10
- package/engine/watch-actions.js +7 -2
- package/engine/watches.js +353 -51
- package/engine/work-item-validation.js +561 -0
- package/engine/work-items-store.js +61 -1
- package/engine/worktree-gc.js +656 -37
- package/engine/worktree-preflight.js +154 -0
- package/engine.js +2068 -342
- package/package.json +1 -1
- package/playbooks/_pr-description-audit.md +88 -41
- package/playbooks/build-fix-complex.md +3 -3
- package/playbooks/docs.md +2 -2
- package/playbooks/fix.md +18 -18
- package/playbooks/implement.md +18 -18
- package/playbooks/qa-session-draft.md +10 -12
- package/playbooks/qa-session-execute.md +16 -12
- package/playbooks/qa-validate.md +8 -7
- package/playbooks/shared-rules.md +45 -0
- package/playbooks/test.md +4 -0
- package/prompts/cc-system.md +1 -1
- package/skills/check-self-authored-review-comment/SKILL.md +2 -2
package/dashboard/js/refresh.js
CHANGED
|
@@ -136,7 +136,8 @@ function _detectPageChanges(data) {
|
|
|
136
136
|
// reload path below — RENDER_VERSIONS handles the within-process case.
|
|
137
137
|
const RENDER_VERSIONS = {
|
|
138
138
|
// Bumped 2→3 for client-ticked relative last-action timestamps.
|
|
139
|
-
|
|
139
|
+
// Bumped 3→4 to show the runtime-reported model when no model is pinned.
|
|
140
|
+
agents: 4,
|
|
140
141
|
prdProgress: 2,
|
|
141
142
|
prdPrs: 1,
|
|
142
143
|
inbox: 2,
|
|
@@ -164,13 +165,25 @@ const RENDER_VERSIONS = {
|
|
|
164
165
|
// Bumped 10→11: (prior).
|
|
165
166
|
// Bumped 11→12: WI detail modal surfaces the cancellation reason + cancelledAt
|
|
166
167
|
// for CANCELLED items (W-mrtdet41000h935a).
|
|
167
|
-
|
|
168
|
+
// Bumped 12→13: pending branch holds name the operator checkout and branch.
|
|
169
|
+
// Bumped 13→15: trailing action-button cell right-aligned so the delete ✕ aligns
|
|
170
|
+
// across rows (jumps over main's independent 13→14 workItems bump).
|
|
171
|
+
// Bumped 15→16: Archive row-action button outline now uses border-color
|
|
172
|
+
// var(--muted) to match its muted icon (visible outline, family convention).
|
|
173
|
+
// Bumped 16→17: merge of this branch's needs-attention renderer change with
|
|
174
|
+
// main's independent workItems→16 bump — both touch the row render, so force
|
|
175
|
+
// a client reload past both.
|
|
176
|
+
// Bumped 17→18: WI detail modal shows the effective model (persisted dispatch
|
|
177
|
+
// evidence, else the current-config projection) — W-ms0ytfdd000d5a26.
|
|
178
|
+
workItems: 18,
|
|
168
179
|
skills: 1,
|
|
169
180
|
commands: 1,
|
|
170
181
|
mcpServers: 1,
|
|
171
182
|
// Bumped to 2 by W-mqptgmcn000k5c90: renderHarnessDiagnostics now stamps each
|
|
172
183
|
// <details> with a stable data-hkey and the render is gated behind _changed().
|
|
173
|
-
|
|
184
|
+
// Bumped 2→3 (W-ms0plsji000h0f9d): compact card padding/margins + Suppressed
|
|
185
|
+
// assets now defaults collapsed, so force a client re-render past cached state.
|
|
186
|
+
harnessDiag: 3,
|
|
174
187
|
schedules: 1,
|
|
175
188
|
watches: 3,
|
|
176
189
|
meetings: 1,
|
|
@@ -185,7 +198,9 @@ const RENDER_VERSIONS = {
|
|
|
185
198
|
// Bumped to 4 by W-mqrdavob (Agents table: per-agent charter editor column).
|
|
186
199
|
// Bumped to 5 by W-mr0qs0vw (Worker Pool: CC session idle-timeout field).
|
|
187
200
|
// Bumped to 6 for the fleet ACP pool opt-in default and warning copy.
|
|
188
|
-
|
|
201
|
+
// Bumped to 7 on origin/main.
|
|
202
|
+
// Bumped to 8 for the Auto-fix pane tile-grid revamp (W-mryyzhug000454b7).
|
|
203
|
+
settings: 8,
|
|
189
204
|
};
|
|
190
205
|
const _sectionCache = {};
|
|
191
206
|
const _lastValueByKey = {};
|
|
@@ -267,15 +282,15 @@ function _condFetchJson(url) {
|
|
|
267
282
|
function _formatCcPowerLabel(autoMode) {
|
|
268
283
|
var runtime = autoMode && autoMode.ccCli ? String(autoMode.ccCli) : 'claude';
|
|
269
284
|
var runtimeLabel = runtime.charAt(0).toUpperCase() + runtime.slice(1);
|
|
270
|
-
var model = autoMode && autoMode.
|
|
271
|
-
return 'Ask anything, dispatch work, manage plans — powered by ' + runtimeLabel +
|
|
285
|
+
var model = MinionsModelDisplay.label(autoMode && (autoMode.ccModelDisplay || autoMode.ccModel));
|
|
286
|
+
return 'Ask anything, dispatch work, manage plans — powered by ' + runtimeLabel + ' (' + model + ')';
|
|
272
287
|
}
|
|
273
288
|
|
|
274
289
|
function _formatCcDrawerLabel(autoMode) {
|
|
275
290
|
var runtime = autoMode && autoMode.ccCli ? String(autoMode.ccCli) : 'claude';
|
|
276
291
|
var runtimeLabel = runtime.charAt(0).toUpperCase() + runtime.slice(1);
|
|
277
|
-
var model = autoMode && autoMode.
|
|
278
|
-
return runtimeLabel +
|
|
292
|
+
var model = MinionsModelDisplay.label(autoMode && (autoMode.ccModelDisplay || autoMode.ccModel));
|
|
293
|
+
return runtimeLabel + ' (' + model + ')-powered. Full minions context. Enter to send, Shift+Enter for newline.';
|
|
279
294
|
}
|
|
280
295
|
|
|
281
296
|
// W-mpnc4u8c001d9d6c / W-mpodheao0006a37a — #engine-quick-stats "Next tick in"
|
|
@@ -500,7 +515,7 @@ function _processStatusUpdate(data, opts) {
|
|
|
500
515
|
const autoEl = document.getElementById('auto-approve-badge');
|
|
501
516
|
// eslint-disable-next-line no-unsanitized/property -- reason: composed from compile-time constants (no user data flows in)
|
|
502
517
|
if (autoEl) autoEl.innerHTML = data.autoMode?.approvePlans
|
|
503
|
-
? '<span style="font-size:var(--text-
|
|
518
|
+
? '<span style="font-size:var(--text-sm);font-weight:600;padding:1px 6px;border-radius:3px;background:rgba(63,185,80,0.15);color:var(--green);border:1px solid rgba(63,185,80,0.3)">AUTO-APPROVE</span>'
|
|
504
519
|
: '';
|
|
505
520
|
const ccLabelEl = document.getElementById('cmd-powered-by');
|
|
506
521
|
if (ccLabelEl) ccLabelEl.textContent = _formatCcPowerLabel(data.autoMode);
|
|
@@ -824,14 +839,13 @@ function _processStatusUpdate(data, opts) {
|
|
|
824
839
|
// prunePrdRequeueState moved into the /api/work-items .then handler so it
|
|
825
840
|
// operates on the fresh fetched list rather than the synchronously-empty
|
|
826
841
|
// window._lastWorkItems. (Review finding #5.)
|
|
827
|
-
// Engine log (last 50 entries) comes from /
|
|
828
|
-
// client-side .slice(-50) to mirror queries.js getEngineLog (issue #2949).
|
|
842
|
+
// Engine log (last 50 entries) comes from the SQL-backed /api/engine-log.
|
|
829
843
|
// Scroll-preservation opts (preserveEngineLogScroll) are threaded through
|
|
830
844
|
// to renderEngineLog so the log pane keeps its scroll position across
|
|
831
845
|
// refreshes (W-mpp75wkg000b3018).
|
|
832
846
|
_safeRender('engineLog', function() {
|
|
833
847
|
const logOpts = { preserveEngineLogScroll: opts.preserveEngineLogScroll };
|
|
834
|
-
Promise.resolve(
|
|
848
|
+
Promise.resolve(fetchEngineLog())
|
|
835
849
|
.then(function (fresh) {
|
|
836
850
|
const list = fresh && fresh.length ? fresh : (window._lastEngineLog || []);
|
|
837
851
|
window._lastEngineLog = list;
|
|
@@ -995,17 +1009,22 @@ function _processStatusUpdate(data, opts) {
|
|
|
995
1009
|
if (Array.isArray(window._lastSchedules)) _safeRender('schedules', function() { renderSchedules(window._lastSchedules); });
|
|
996
1010
|
});
|
|
997
1011
|
});
|
|
998
|
-
// Watches
|
|
1012
|
+
// Watches come from GET /api/watches — the canonical SQL-backed read
|
|
1013
|
+
// surface (engine/watches-store.js). The retired engine/watches.json mirror
|
|
1014
|
+
// is never written, so /state/ has nothing to serve.
|
|
999
1015
|
_safeRender('watches', function() {
|
|
1000
|
-
// Seq guard — watches
|
|
1001
|
-
// (
|
|
1016
|
+
// Seq guard — the watches table is mutated every 3 ticks by
|
|
1017
|
+
// engine/watches.js (watch-fire / pause), so a late response from a prior
|
|
1018
|
+
// tick must not clobber fresher state. (Round-4 #10.)
|
|
1002
1019
|
const seq = (window._refreshSeq = (window._refreshSeq || 0) + 1);
|
|
1003
1020
|
window._lastRequestedSeq = window._lastRequestedSeq || {};
|
|
1004
1021
|
window._lastRequestedSeq.watches = seq;
|
|
1005
|
-
|
|
1022
|
+
MinionsWatchesSource.fetchWatches()
|
|
1006
1023
|
.then(function (fresh) {
|
|
1007
1024
|
if (seq < (window._lastRequestedSeq.watches || 0)) return;
|
|
1008
|
-
|
|
1025
|
+
// null = request failed / unusable payload → keep the prior slice.
|
|
1026
|
+
// A genuine empty array renders as empty (the list really is empty).
|
|
1027
|
+
const list = Array.isArray(fresh) ? fresh : (window._lastWatches || []);
|
|
1009
1028
|
window._lastWatches = list;
|
|
1010
1029
|
_safeRender('watches', function() { renderWatches(list); });
|
|
1011
1030
|
})
|
|
@@ -1446,7 +1465,7 @@ async function refresh(opts) {
|
|
|
1446
1465
|
refresh();
|
|
1447
1466
|
_syncPinsFromServer(); // Load server-side pins on startup
|
|
1448
1467
|
// Gate the "Try new Slim UX" promo button behind the slim-ux-promo feature
|
|
1449
|
-
// flag (
|
|
1468
|
+
// flag (on by default). The button ships hidden in layout.html; reveal it
|
|
1450
1469
|
// only when the flag is on. The classic HTML (and its window.MINIONS_FEATURES
|
|
1451
1470
|
// bootstrap) is built once at dashboard startup, so a runtime flag toggle
|
|
1452
1471
|
// wouldn't be reflected there — read the LIVE flag state from /api/features
|
|
@@ -32,16 +32,16 @@ function _runtimeTagHtml(runtime) {
|
|
|
32
32
|
}
|
|
33
33
|
// Unknown runtime — fall back to a small text pill so the user still sees something
|
|
34
34
|
const fallback = runtime || 'unknown';
|
|
35
|
-
return '<span class="agent-runtime-tag" title="Runtime: ' + escapeHtml(fallback) + '" style="font-size:var(--text-
|
|
35
|
+
return '<span class="agent-runtime-tag" title="Runtime: ' + escapeHtml(fallback) + '" style="font-size:var(--text-sm);font-weight:600;letter-spacing:0.4px;text-transform:uppercase;padding:1px 5px;margin-left:6px;border:1px solid var(--muted);border-radius:3px;color:var(--muted);background:transparent">' + escapeHtml(fallback) + '</span>';
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// W-mpmwxk4y00053271 — compact text chip showing the resolved model next to
|
|
39
|
-
// the runtime tag.
|
|
40
|
-
//
|
|
39
|
+
// the runtime tag. For unpinned agents, the API supplies the concrete model
|
|
40
|
+
// reported by the latest matching runtime session. Caleb feedback:
|
|
41
41
|
// "The model is really critical piece of information to me as a user."
|
|
42
42
|
function _modelChipHtml(model) {
|
|
43
|
-
|
|
44
|
-
return '<span class="agent-model-tag" title="Model: ' + escapeHtml(
|
|
43
|
+
const label = MinionsModelDisplay.label(model);
|
|
44
|
+
return '<span class="agent-model-tag" title="Model: ' + escapeHtml(label) + '" style="font-size:var(--text-sm);font-weight:600;letter-spacing:0.4px;padding:1px 5px;margin-left:4px;border:1px solid var(--muted);border-radius:3px;color:var(--muted);background:transparent">' + escapeHtml(label) + '</span>';
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function _agentHasTimedAction(agent) {
|
|
@@ -80,15 +80,15 @@ function renderAgents(agents) {
|
|
|
80
80
|
grid.innerHTML = agents.map(a => `
|
|
81
81
|
<div class="agent-card ${statusColor(a.status)}" data-agent-id="${escapeHtml(a.id)}" onclick="if(shouldIgnoreSelectionClick(event))return;openAgentDetail(this.dataset.agentId)">
|
|
82
82
|
<div class="agent-card-header">
|
|
83
|
-
<span class="agent-name"><span class="agent-emoji">${escapeHtml(a.emoji)}</span>${escapeHtml(a.name)}${_runtimeTagHtml(a.runtime)}${_modelChipHtml(a.model)}</span>
|
|
83
|
+
<span class="agent-name"><span class="agent-emoji">${escapeHtml(a.emoji)}</span>${escapeHtml(a.name)}${_runtimeTagHtml(a.runtime)}${_modelChipHtml(a.displayModel || a.model)}</span>
|
|
84
84
|
<span class="status-badge ${escapeHtml(a.status)}">${escapeHtml(a.status)}</span>
|
|
85
85
|
</div>
|
|
86
86
|
<div class="agent-role">${escapeHtml(a.role)}</div>
|
|
87
87
|
${_agentActionHtml(a)}
|
|
88
88
|
${(function() {
|
|
89
89
|
var s = a.started_at, c = a.completed_at;
|
|
90
|
-
if (s && c) { var d = new Date(c) - new Date(s); if (d > 0) { var sec = Math.floor(d/1000)%60, min = Math.floor(d/60000)%60, hr = Math.floor(d/3600000); return '<div style="font-size:var(--text-
|
|
91
|
-
if (s && a.status === 'working') return '<div class="agent-runtime-tick" data-started="' + s + '" style="font-size:var(--text-
|
|
90
|
+
if (s && c) { var d = new Date(c) - new Date(s); if (d > 0) { var sec = Math.floor(d/1000)%60, min = Math.floor(d/60000)%60, hr = Math.floor(d/3600000); return '<div style="font-size:var(--text-sm);color:var(--muted)">Last run: ' + (hr > 0 ? hr + 'h ' : '') + min + 'm ' + sec + 's</div>'; } }
|
|
91
|
+
if (s && a.status === 'working') return '<div class="agent-runtime-tick" data-started="' + s + '" style="font-size:var(--text-sm);color:var(--yellow)"></div>';
|
|
92
92
|
return '';
|
|
93
93
|
})()}
|
|
94
94
|
${a._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:var(--text-sm);color:var(--muted)">⏳ Blocking tool call (${escapeHtml(a._blockingToolCall.tool)}) — silent ${Math.round(a._blockingToolCall.silentMs/60000)}min, timeout in ${Math.round(a._blockingToolCall.remainingMs/60000)}min</div>` : ''}
|
|
@@ -151,21 +151,17 @@ async function openAgentDetail(id) {
|
|
|
151
151
|
// W-mpmwxk4y00053271 — mirror the model chip the card shows so the detail
|
|
152
152
|
// header stays in sync. textContent path keeps the model string from being
|
|
153
153
|
// interpreted as HTML.
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
s.textContent = agent.model;
|
|
160
|
-
return s;
|
|
161
|
-
})()
|
|
162
|
-
: null;
|
|
154
|
+
const modelLabel = MinionsModelDisplay.label(agent.displayModel || agent.model);
|
|
155
|
+
const modelSpan = document.createElement('span');
|
|
156
|
+
modelSpan.title = 'Model: ' + modelLabel;
|
|
157
|
+
modelSpan.style.cssText = 'display:inline-block;margin-left:6px;font-size:var(--text-sm);font-weight:600;letter-spacing:0.4px;padding:2px 6px;border:1px solid var(--muted);border-radius:3px;color:var(--muted)';
|
|
158
|
+
modelSpan.textContent = modelLabel;
|
|
163
159
|
const children = [
|
|
164
160
|
emojiSpan,
|
|
165
161
|
document.createTextNode(' ' + (agent.name || '') + ' \u2014 ' + (agent.role || '')),
|
|
166
162
|
runtimeSpan,
|
|
167
163
|
];
|
|
168
|
-
|
|
164
|
+
children.push(modelSpan);
|
|
169
165
|
nameEl.replaceChildren(...children);
|
|
170
166
|
|
|
171
167
|
const badgeClass = agent.status;
|
|
@@ -5,64 +5,9 @@ const LOG_PER_PAGE = 50;
|
|
|
5
5
|
let _completedPage = 0;
|
|
6
6
|
let _logPage = 0;
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// poll regardless of /api/status outer-cache state (issue #2949).
|
|
12
|
-
//
|
|
13
|
-
// completedTotal isn't on disk — it's metrics-derived (sum across
|
|
14
|
-
// agents.tasksCompleted + agents.tasksErrored). The renderer already falls
|
|
15
|
-
// back to completed.length when completedTotal is missing, so we just
|
|
16
|
-
// overlay the cached value from the previous /api/status snapshot for
|
|
17
|
-
// continuity. Engine-side _withCompletionReportSummary enrichment (which
|
|
18
|
-
// reads per-dispatch sidecar files) is preserved on the cached snapshot
|
|
19
|
-
// too, so the modal "view report" links keep working — we only overlay the
|
|
20
|
-
// fresh status/timestamp fields onto the cached row when ids match.
|
|
21
|
-
async function fetchDispatchFromDisk(cachedDispatch) {
|
|
22
|
-
try {
|
|
23
|
-
const r = await fetch('/state/engine/dispatch.json');
|
|
24
|
-
if (!r.ok) return null;
|
|
25
|
-
const fresh = await r.json();
|
|
26
|
-
if (!fresh || typeof fresh !== 'object') return null;
|
|
27
|
-
// Cap completed to last 20 to match engine/queries.js getDispatchQueue
|
|
28
|
-
// — render-dispatch only paginates within this window, and dispatch.json
|
|
29
|
-
// on disk is already capped to 100 by the engine, so this is the same
|
|
30
|
-
// shape the renderer has always seen.
|
|
31
|
-
if (Array.isArray(fresh.completed) && fresh.completed.length > 20) {
|
|
32
|
-
fresh.completed = fresh.completed.slice(-20);
|
|
33
|
-
}
|
|
34
|
-
// Overlay completion-report summaries from the cached snapshot keyed by
|
|
35
|
-
// dispatch id. They live in sidecar files the client can't read, but
|
|
36
|
-
// they're slow to change so the cached overlay is fine.
|
|
37
|
-
if (cachedDispatch) {
|
|
38
|
-
const cachedById = new Map();
|
|
39
|
-
for (const list of ['pending', 'active', 'completed']) {
|
|
40
|
-
for (const d of (cachedDispatch[list] || [])) {
|
|
41
|
-
if (d && d.id) cachedById.set(d.id, d);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
for (const list of ['pending', 'active', 'completed']) {
|
|
45
|
-
if (!Array.isArray(fresh[list])) continue;
|
|
46
|
-
fresh[list] = fresh[list].map((d) => {
|
|
47
|
-
if (!d || !d.id) return d;
|
|
48
|
-
const cached = cachedById.get(d.id);
|
|
49
|
-
if (!cached) return d;
|
|
50
|
-
const merged = Object.assign({}, d);
|
|
51
|
-
if (cached._completionReport !== undefined && merged._completionReport === undefined) {
|
|
52
|
-
merged._completionReport = cached._completionReport;
|
|
53
|
-
}
|
|
54
|
-
return merged;
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
if (fresh.completedTotal == null && cachedDispatch.completedTotal != null) {
|
|
58
|
-
fresh.completedTotal = cachedDispatch.completedTotal;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return fresh;
|
|
62
|
-
} catch {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
8
|
+
// Dispatch state is served fresh by GET /api/dispatch (see refresh.js) — the
|
|
9
|
+
// engine/dispatch.json mirror it used to read was retired by the SQL-only
|
|
10
|
+
// cutover, and completion-report sidecars are loaded server-side now.
|
|
66
11
|
|
|
67
12
|
function _completedPrev() { if (_completedPage > 0) { _completedPage--; refresh({ preserveCompletedScroll: false }); } }
|
|
68
13
|
function _completedNext() { _completedPage++; refresh({ preserveCompletedScroll: false }); } // clamped in renderDispatch
|
|
@@ -382,7 +327,7 @@ function renderDispatch(dispatch, opts) {
|
|
|
382
327
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: dispatch type, agent, task, skip reason)
|
|
383
328
|
pendingEl.innerHTML = '<div style="font-size:var(--text-base);color:var(--yellow);margin:8px 0 6px;font-weight:600">PENDING</div><div class="dispatch-list">' +
|
|
384
329
|
dispatch.pending.map(d => dispatchItemHtml(d,
|
|
385
|
-
d.skipReason ? '<span style="font-size:var(--text-
|
|
330
|
+
d.skipReason ? '<span style="font-size:var(--text-sm);color:var(--muted);margin-left:6px" title="' + escHtml(d.skipReason) + '">' + escHtml(d.skipReason.replace(/_/g, ' ')) + '</span>' : ''
|
|
386
331
|
)).join('') + '</div>';
|
|
387
332
|
} else {
|
|
388
333
|
pendingEl.innerHTML = '';
|
|
@@ -434,19 +379,14 @@ function renderDispatch(dispatch, opts) {
|
|
|
434
379
|
restoreDashboardScrollState(completedEl, completedScroll);
|
|
435
380
|
}
|
|
436
381
|
|
|
437
|
-
// Pull the engine log
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
// after the disk fetch. ETag/304 keeps the steady state cheap; a new log
|
|
441
|
-
// line forces a fresh download (file is ~500 KB at high engine traffic, but
|
|
442
|
-
// it changes only on engine writes — not on every 4 s poll).
|
|
443
|
-
async function fetchEngineLogFromDisk() {
|
|
382
|
+
// Pull the latest engine log entries from the SQL-backed API. Event-version
|
|
383
|
+
// ETags keep steady-state polls cheap while each new log batch invalidates it.
|
|
384
|
+
async function fetchEngineLog() {
|
|
444
385
|
try {
|
|
445
|
-
const r = await fetch('/
|
|
386
|
+
const r = await fetch('/api/engine-log');
|
|
446
387
|
if (!r.ok) return null;
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
return arr.slice(-50);
|
|
388
|
+
const entries = await r.json();
|
|
389
|
+
return Array.isArray(entries) ? entries.slice(-50) : [];
|
|
450
390
|
} catch {
|
|
451
391
|
return null;
|
|
452
392
|
}
|
|
@@ -548,7 +488,7 @@ function renderVersionBanner(version) {
|
|
|
548
488
|
|
|
549
489
|
const v = version.dashboardRunning || version.running || version.disk || '?';
|
|
550
490
|
const commitLabel = version.dashboardRunningCommit ? ' (' + version.dashboardRunningCommit + ')' : '';
|
|
551
|
-
const warnStyle = 'font-size:var(--text-
|
|
491
|
+
const warnStyle = 'font-size:var(--text-sm);padding:2px 8px;background:rgba(210,153,34,0.15);border:1px solid rgba(210,153,34,0.3);border-radius:4px;color:var(--yellow);cursor:help';
|
|
552
492
|
|
|
553
493
|
// During the post-restart grace window the old engine's reported codeVersion
|
|
554
494
|
// can still be in the cached payload — silently swallow the engineStale flag
|
|
@@ -570,11 +510,11 @@ function renderVersionBanner(version) {
|
|
|
570
510
|
el.textContent = '\u26A0 Dashboard running v' + (version.dashboardRunning || '?') + ' — disk has v' + (version.disk || '?') + '. Run: minions restart';
|
|
571
511
|
el.title = 'The dashboard process is running older code. Run: minions restart';
|
|
572
512
|
} else if (version.updateAvailable) {
|
|
573
|
-
el.style.cssText = 'font-size:var(--text-
|
|
574
|
-
el.textContent = 'v' + v + commitLabel + ' — v' + version.latest + ' available. Run: minions update or npm install -g @yemi33/minions@latest';
|
|
513
|
+
el.style.cssText = 'font-size:var(--text-sm);padding:2px 8px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:4px;color:var(--green);cursor:help';
|
|
514
|
+
el.textContent = 'v' + v + commitLabel + ' — v' + version.latest + ' available. Run: minions update or npm install -g ' + (version.packageName || '@yemi33/minions') + '@latest';
|
|
575
515
|
el.title = 'A newer version is available on npm. Run minions update to upgrade and restart.';
|
|
576
516
|
} else {
|
|
577
|
-
el.style.cssText = 'font-size:var(--text-
|
|
517
|
+
el.style.cssText = 'font-size:var(--text-sm);color:var(--muted)';
|
|
578
518
|
el.textContent = 'v' + v + commitLabel;
|
|
579
519
|
el.title = 'Minions v' + v + (version.latest ? ' (latest)' : '');
|
|
580
520
|
}
|
|
@@ -140,9 +140,9 @@ function renderInbox(inbox, opts) {
|
|
|
140
140
|
<div class="inbox-preview" onclick="openModal(${idx})" style="cursor:pointer">${escapeHtml(item.content.slice(0,200))}</div>
|
|
141
141
|
<div style="display:flex;gap:6px;margin-top:6px;align-items:center">
|
|
142
142
|
${pinButton(pk, pinned, 'inbox')}
|
|
143
|
-
<button class="pr-pager-btn" style="font-size:var(--text-
|
|
144
|
-
<button class="pr-pager-btn" style="font-size:var(--text-
|
|
145
|
-
<button class="pr-pager-btn" style="font-size:var(--text-
|
|
143
|
+
<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px" data-inbox-name="${escapeHtml(item.name)}" onclick="event.stopPropagation();promoteToKB(this.dataset.inboxName)">Add to Knowledge Base</button>
|
|
144
|
+
<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px" data-inbox-name="${escapeHtml(item.name)}" onclick="event.stopPropagation();openInboxInExplorer(this.dataset.inboxName)">Open in Explorer</button>
|
|
145
|
+
<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--red)" data-inbox-name="${escapeHtml(item.name)}" onclick="event.stopPropagation();deleteInboxItem(this.dataset.inboxName)">Delete</button>
|
|
146
146
|
</div>
|
|
147
147
|
</div>`;
|
|
148
148
|
}).join('');
|
|
@@ -121,8 +121,8 @@ function renderMeetings(meetings, opts) {
|
|
|
121
121
|
'<div style="display:flex;align-items:center;gap:8px">' +
|
|
122
122
|
'<span style="color:' + statusColor + ';font-size:var(--text-base);font-weight:600">' + statusLabel + '</span>' +
|
|
123
123
|
(m.status === 'archived'
|
|
124
|
-
? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
125
|
-
: (m.status === 'completed' ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
124
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px" onclick="event.stopPropagation();_unarchiveMeeting(\'' + escHtml(m.id) + '\')">Unarchive</button>'
|
|
125
|
+
: (m.status === 'completed' ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px" onclick="event.stopPropagation();_archiveMeeting(\'' + escHtml(m.id) + '\')">Archive</button>' : '')) +
|
|
126
126
|
'<button class="btn-destructive" onclick="event.stopPropagation();_deleteMeeting(\'' + escHtml(m.id) + '\')">Delete</button>' +
|
|
127
127
|
'</div>' +
|
|
128
128
|
'</div>' +
|
|
@@ -275,28 +275,28 @@ function _renderMeetingDetail(m) {
|
|
|
275
275
|
// Actions
|
|
276
276
|
if (m.status === 'archived') {
|
|
277
277
|
html += '<div style="display:flex;gap:8px;border-top:1px solid var(--border);padding-top:8px">' +
|
|
278
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
278
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px" onclick="_unarchiveMeeting(\'' + escHtml(m.id) + '\')">Unarchive</button>' +
|
|
279
279
|
'<button class="btn-destructive btn-destructive-md" onclick="_deleteMeeting(\'' + escHtml(m.id) + '\')">Delete</button>' +
|
|
280
280
|
'</div>';
|
|
281
281
|
} else if (m.status === 'completed') {
|
|
282
282
|
const linkedPlan = _findLinkedPlan(m);
|
|
283
283
|
html += '<div style="display:flex;gap:8px;flex-wrap:wrap;border-top:1px solid var(--border);padding-top:8px">' +
|
|
284
284
|
(linkedPlan
|
|
285
|
-
? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
285
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--blue);border-color:var(--blue)" onclick="_viewPlanWithBack(\'' + escHtml(linkedPlan.file) + '\',\'' + escHtml(m.id) + '\')">View Plan</button>' +
|
|
286
286
|
'<button class="btn-action btn-action-md" onclick="_createPlanFromMeeting(\'' + escHtml(m.id) + '\',this)">New Plan</button>'
|
|
287
287
|
: '<button class="btn-action btn-action-md" onclick="_createPlanFromMeeting(\'' + escHtml(m.id) + '\',this)">Create Plan from Meeting</button>') +
|
|
288
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
288
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px" onclick="_archiveMeeting(\'' + escHtml(m.id) + '\')">Archive</button>' +
|
|
289
289
|
'<button class="btn-destructive btn-destructive-md" onclick="_deleteMeeting(\'' + escHtml(m.id) + '\')">Delete</button>' +
|
|
290
290
|
'</div>' +
|
|
291
291
|
'<div id="meeting-plan-toast" class="cmd-toast cmd-toast-inline" style="margin-top:6px"></div>' +
|
|
292
|
-
'<div style="font-size:var(--text-
|
|
292
|
+
'<div style="font-size:var(--text-sm);color:var(--muted);margin-top:4px">Use the Q&A below to discuss action items' + (linkedPlan ? '' : ', then create a plan to execute them') + '.</div>';
|
|
293
293
|
} else {
|
|
294
294
|
html += '<div style="display:flex;gap:8px;border-top:1px solid var(--border);padding-top:8px">' +
|
|
295
295
|
'<input id="meeting-note-input" type="text" placeholder="Add context for all agents..." style="flex:1;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md)" onkeydown="if(event.key===\'Enter\')_submitMeetingNote(\'' + escHtml(m.id) + '\')">' +
|
|
296
296
|
'<button onclick="_submitMeetingNote(\'' + escHtml(m.id) + '\',this)" style="padding:6px 12px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-size:var(--text-base)">Add Note</button>' +
|
|
297
297
|
'</div>' +
|
|
298
298
|
'<div style="display:flex;gap:8px;margin-top:4px">' +
|
|
299
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
299
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--yellow);border-color:var(--yellow)" onclick="_advanceMeeting(\'' + escHtml(m.id) + '\',this)">Skip to Next Round</button>' +
|
|
300
300
|
'<button class="btn-destructive btn-destructive-md" onclick="_endMeeting(\'' + escHtml(m.id) + '\',this)">End Meeting</button>' +
|
|
301
301
|
'<button class="btn-destructive btn-destructive-md" onclick="_deleteMeeting(\'' + escHtml(m.id) + '\')">Delete</button>' +
|
|
302
302
|
'</div>';
|
|
@@ -498,7 +498,7 @@ function _viewPlanWithBack(file, meetingId) {
|
|
|
498
498
|
if (title && !title.querySelector('.mtg-back-btn')) {
|
|
499
499
|
const back = document.createElement('button');
|
|
500
500
|
back.className = 'pr-pager-btn mtg-back-btn';
|
|
501
|
-
back.style.cssText = 'font-size:var(--text-
|
|
501
|
+
back.style.cssText = 'font-size:var(--text-sm);padding:2px 8px;margin-right:8px;vertical-align:middle';
|
|
502
502
|
back.textContent = '\u2190 Back to Meeting';
|
|
503
503
|
back.onclick = function() { openMeetingDetail(meetingId); };
|
|
504
504
|
title.prepend(back);
|
|
@@ -690,6 +690,20 @@ function _aggregateEngineUsageForTokenTile(engine) {
|
|
|
690
690
|
return { cost, input, output, cache, calls };
|
|
691
691
|
}
|
|
692
692
|
|
|
693
|
+
function _formatTrackedCost(value, available) {
|
|
694
|
+
if (available === false) return 'N/A';
|
|
695
|
+
return '$' + (Number(value) || 0).toFixed(2);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function _usageExists(m) {
|
|
699
|
+
return !!((m.totalCostUsd || 0)
|
|
700
|
+
|| (m.totalInputTokens || 0)
|
|
701
|
+
|| (m.totalOutputTokens || 0)
|
|
702
|
+
|| (m.totalCacheRead || 0)
|
|
703
|
+
|| (m.tasksCompleted || 0)
|
|
704
|
+
|| (m.tasksErrored || 0));
|
|
705
|
+
}
|
|
706
|
+
|
|
693
707
|
function renderTokenUsage(metrics) {
|
|
694
708
|
const el = document.getElementById('token-usage-content');
|
|
695
709
|
const agents = Object.entries(metrics).filter(([k]) => !k.startsWith('_'));
|
|
@@ -723,10 +737,11 @@ function renderTokenUsage(metrics) {
|
|
|
723
737
|
|
|
724
738
|
const fmtTokens = (n) => n >= 1000000 ? (n / 1000000).toFixed(1) + 'M' : n >= 1000 ? (n / 1000).toFixed(0) + 'K' : String(n);
|
|
725
739
|
const fmtCost = (n) => '$' + n.toFixed(2);
|
|
740
|
+
const hasUnreportedAgentCost = agents.some(([, m]) => m.costTracking === false && _usageExists(m));
|
|
726
741
|
|
|
727
742
|
// Summary tiles
|
|
728
743
|
let html = '<div class="token-tiles">';
|
|
729
|
-
html += '<div class="token-tile"><div class="token-tile-label">
|
|
744
|
+
html += '<div class="token-tile"><div class="token-tile-label">Reported Cost</div><div class="token-tile-value">' + fmtCost(totalCost) + '</div><div class="token-tile-sub">excludes runtimes without USD telemetry</div></div>';
|
|
730
745
|
html += '<div class="token-tile"><div class="token-tile-label">Input Tokens</div><div class="token-tile-value">' + fmtTokens(totalInput) + '</div></div>';
|
|
731
746
|
html += '<div class="token-tile"><div class="token-tile-label">Output Tokens</div><div class="token-tile-value">' + fmtTokens(totalOutput) + '</div></div>';
|
|
732
747
|
html += '<div class="token-tile"><div class="token-tile-label">Cache Reads</div><div class="token-tile-value">' + fmtTokens(totalCache) + '</div></div>';
|
|
@@ -735,7 +750,8 @@ function renderTokenUsage(metrics) {
|
|
|
735
750
|
const today = new Date().toISOString().slice(0, 10);
|
|
736
751
|
const todayData = daily[today];
|
|
737
752
|
if (todayData) {
|
|
738
|
-
|
|
753
|
+
const todayCostAvailable = (todayData.costUsd || 0) > 0 || !hasUnreportedAgentCost;
|
|
754
|
+
html += '<div class="token-tile"><div class="token-tile-label">Today</div><div class="token-tile-value">' + _formatTrackedCost(todayData.costUsd, todayCostAvailable) + '</div><div class="token-tile-sub">' + (todayData.tasks || 0) + ' tasks' + (todayCostAvailable ? '' : ' · USD telemetry unavailable') + '</div></div>';
|
|
739
755
|
}
|
|
740
756
|
html += '</div>';
|
|
741
757
|
|
|
@@ -743,12 +759,13 @@ function renderTokenUsage(metrics) {
|
|
|
743
759
|
const days = Object.keys(daily).sort().slice(-14);
|
|
744
760
|
if (days.length > 1) {
|
|
745
761
|
const maxCost = Math.max(...days.map(d => daily[d].costUsd || 0), 0.01);
|
|
746
|
-
html += '<div style="font-size:var(--text-sm);color:var(--muted);margin:8px 0 4px">Daily Cost (last ' + days.length + ' days)</div>';
|
|
762
|
+
html += '<div style="font-size:var(--text-sm);color:var(--muted);margin:8px 0 4px">Daily Reported Cost (last ' + days.length + ' days)</div>';
|
|
747
763
|
html += '<div class="token-chart">';
|
|
748
764
|
for (const day of days) {
|
|
749
765
|
const d = daily[day];
|
|
766
|
+
const costAvailable = (d.costUsd || 0) > 0 || !hasUnreportedAgentCost;
|
|
750
767
|
const pct = Math.max(((d.costUsd || 0) / maxCost) * 100, 2);
|
|
751
|
-
html += '<div class="token-bar" style="height:' + pct + '%"><div class="token-bar-tip">' + day.slice(5) + ': ' +
|
|
768
|
+
html += '<div class="token-bar" style="height:' + pct + '%"><div class="token-bar-tip">' + day.slice(5) + ': ' + _formatTrackedCost(d.costUsd, costAvailable) + ' / ' + (d.tasks || 0) + ' tasks</div></div>';
|
|
752
769
|
}
|
|
753
770
|
html += '</div>';
|
|
754
771
|
html += '<div class="token-chart-labels">';
|
|
@@ -763,7 +780,7 @@ function renderTokenUsage(metrics) {
|
|
|
763
780
|
var tempAgents = agents.filter(function(a) { return a[0].startsWith('temp-'); });
|
|
764
781
|
var tokenRows = permAgents.slice();
|
|
765
782
|
if (tempAgents.length > 0) {
|
|
766
|
-
var tempMerged = { totalCostUsd: 0, totalInputTokens: 0, totalOutputTokens: 0, totalCacheRead: 0, tasksCompleted: 0, tasksErrored: 0, model: '' };
|
|
783
|
+
var tempMerged = { totalCostUsd: 0, totalInputTokens: 0, totalOutputTokens: 0, totalCacheRead: 0, tasksCompleted: 0, tasksErrored: 0, model: '', costTracking: true };
|
|
767
784
|
for (var ti = 0; ti < tempAgents.length; ti++) {
|
|
768
785
|
var tm = tempAgents[ti][1];
|
|
769
786
|
tempMerged.totalCostUsd += tm.totalCostUsd || 0;
|
|
@@ -772,22 +789,24 @@ function renderTokenUsage(metrics) {
|
|
|
772
789
|
tempMerged.totalCacheRead += tm.totalCacheRead || 0;
|
|
773
790
|
tempMerged.tasksCompleted += tm.tasksCompleted || 0;
|
|
774
791
|
tempMerged.tasksErrored += tm.tasksErrored || 0;
|
|
792
|
+
if (tm.costTracking === false) tempMerged.costTracking = false;
|
|
775
793
|
if (!tempMerged.model && tm.model) tempMerged.model = tm.model;
|
|
776
794
|
}
|
|
777
795
|
tokenRows.push(['Temp Agents (' + tempAgents.length + ')', tempMerged]);
|
|
778
796
|
}
|
|
779
|
-
var agentsWithUsage = tokenRows.filter(function(a) { return (a[1]
|
|
797
|
+
var agentsWithUsage = tokenRows.filter(function(a) { return _usageExists(a[1]); });
|
|
780
798
|
if (agentsWithUsage.length > 0) {
|
|
781
799
|
html += '<div style="font-size:var(--text-sm);color:var(--muted);margin:12px 0 4px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Agent Usage</div>';
|
|
782
800
|
html += '<table class="token-agent-table"><thead><tr><th style="width:20%">Agent</th><th style="width:10%">Model</th><th style="width:14%">Cost</th><th style="width:14%">Input</th><th style="width:14%">Output</th><th style="width:14%">Cache</th><th style="width:14%">$/task</th></tr></thead><tbody>';
|
|
783
801
|
for (const [id, m] of agentsWithUsage.sort((a, b) => (b[1].totalCostUsd || 0) - (a[1].totalCostUsd || 0))) {
|
|
784
802
|
const tasks = (m.tasksCompleted || 0) + (m.tasksErrored || 0);
|
|
785
|
-
const
|
|
803
|
+
const costAvailable = m.costTracking !== false;
|
|
804
|
+
const perTask = tasks > 0 ? _formatTrackedCost((m.totalCostUsd || 0) / tasks, costAvailable) : '-';
|
|
786
805
|
const modelLabel = (m.model || '').replace(/^claude-/, '').replace(/\[.*\]$/, '') || '-';
|
|
787
806
|
html += '<tr>' +
|
|
788
807
|
'<td style="font-weight:600">' + escHtml(id) + '</td>' +
|
|
789
808
|
'<td style="color:var(--muted);font-size:var(--text-sm)">' + escHtml(modelLabel) + '</td>' +
|
|
790
|
-
'<td>' +
|
|
809
|
+
'<td>' + _formatTrackedCost(m.totalCostUsd, costAvailable) + '</td>' +
|
|
791
810
|
'<td>' + fmtTokens(m.totalInputTokens || 0) + '</td>' +
|
|
792
811
|
'<td>' + fmtTokens(m.totalOutputTokens || 0) + '</td>' +
|
|
793
812
|
'<td>' + fmtTokens(m.totalCacheRead || 0) + '</td>' +
|
|
@@ -804,11 +823,12 @@ function renderTokenUsage(metrics) {
|
|
|
804
823
|
html += '<div style="font-size:var(--text-sm);color:var(--muted);margin:12px 0 4px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Engine Usage</div>';
|
|
805
824
|
html += '<table class="token-agent-table"><thead><tr><th style="width:20%">Operation</th><th style="width:10%">Calls</th><th style="width:14%">Cost</th><th style="width:14%">Input</th><th style="width:14%">Output</th><th style="width:14%">Cache</th><th style="width:14%">$/call</th></tr></thead><tbody>';
|
|
806
825
|
for (const [cat, e] of engineEntries.sort((a, b) => (b[1].costUsd || 0) - (a[1].costUsd || 0))) {
|
|
807
|
-
const
|
|
826
|
+
const costAvailable = (e.costUsd || 0) > 0;
|
|
827
|
+
const perCall = (e.calls || 0) > 0 ? _formatTrackedCost((e.costUsd || 0) / e.calls, costAvailable) : '-';
|
|
808
828
|
html += '<tr>' +
|
|
809
829
|
'<td style="font-weight:600">' + escHtml(labels[cat] || cat) + '</td>' +
|
|
810
830
|
'<td>' + (e.calls || 0) + '</td>' +
|
|
811
|
-
'<td>' +
|
|
831
|
+
'<td>' + _formatTrackedCost(e.costUsd, costAvailable) + '</td>' +
|
|
812
832
|
'<td>' + fmtTokens(e.inputTokens || 0) + '</td>' +
|
|
813
833
|
'<td>' + fmtTokens(e.outputTokens || 0) + '</td>' +
|
|
814
834
|
'<td>' + fmtTokens(e.cacheRead || 0) + '</td>' +
|
|
@@ -868,13 +888,13 @@ async function _runProjectScan() {
|
|
|
868
888
|
html += '<div style="max-height:400px;overflow-y:auto;display:flex;flex-direction:column;gap:4px">';
|
|
869
889
|
repos.forEach(function(r, i) {
|
|
870
890
|
var linked = r.linked;
|
|
871
|
-
var hostBadge = '<span style="font-size:var(--text-
|
|
891
|
+
var hostBadge = '<span style="font-size:var(--text-sm);padding:1px 5px;border-radius:3px;background:' +
|
|
872
892
|
(r.host === 'GitHub' ? 'rgba(88,166,255,0.15);color:var(--blue)' : r.host === 'ADO' ? 'rgba(188,140,255,0.15);color:var(--purple)' : 'var(--surface2);color:var(--muted)') +
|
|
873
893
|
'">' + escHtml(r.host) + '</span>';
|
|
874
894
|
html += '<label style="display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;cursor:' + (linked ? 'default' : 'pointer') + ';opacity:' + (linked ? '0.5' : '1') + '">' +
|
|
875
895
|
'<input type="checkbox" data-scan-idx="' + i + '" ' + (linked ? 'disabled checked' : '') + ' style="accent-color:var(--blue);width:16px;height:16px">' +
|
|
876
896
|
'<div style="flex:1;min-width:0">' +
|
|
877
|
-
'<div style="font-weight:600;font-size:var(--text-md)">' + escHtml(r.name) + ' ' + hostBadge + (linked ? ' <span style="font-size:var(--text-
|
|
897
|
+
'<div style="font-weight:600;font-size:var(--text-md)">' + escHtml(r.name) + ' ' + hostBadge + (linked ? ' <span style="font-size:var(--text-sm);color:var(--green)">linked</span>' : '') + '</div>' +
|
|
878
898
|
'<div style="font-size:var(--text-sm);color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escHtml(r.path) + '</div>' +
|
|
879
899
|
(r.description ? '<div style="font-size:var(--text-sm);color:var(--muted)">' + escHtml(r.description) + '</div>' : '') +
|
|
880
900
|
'</div>' +
|
|
@@ -982,7 +1002,7 @@ function _scopeBadge(scope, projectName, pluginName) {
|
|
|
982
1002
|
else if (scope === 'claude-code' || scope === 'copilot' || scope === 'codex') {
|
|
983
1003
|
label = 'user:' + scope;
|
|
984
1004
|
}
|
|
985
|
-
return '<span style="font-size:var(--text-
|
|
1005
|
+
return '<span style="font-size:var(--text-sm);color:' + color + ';margin-left:6px;font-weight:500">[' + escHtml(label) + ']</span>';
|
|
986
1006
|
}
|
|
987
1007
|
|
|
988
1008
|
function renderCommands(commands) {
|
|
@@ -1028,7 +1048,7 @@ function _harnessRowsBlock(label, rows) {
|
|
|
1028
1048
|
}
|
|
1029
1049
|
const body = rows.map(r => {
|
|
1030
1050
|
const marker = r.exists ? '<span style="color:var(--green)">✓</span>' : '<span style="color:var(--orange)">⚠</span>';
|
|
1031
|
-
const missing = r.exists ? '' : ' <span style="color:var(--orange);font-size:var(--text-
|
|
1051
|
+
const missing = r.exists ? '' : ' <span style="color:var(--orange);font-size:var(--text-sm)">(missing on disk)</span>';
|
|
1032
1052
|
return '<div style="font-family:monospace;font-size:var(--text-sm);margin-left:18px;display:flex;gap:8px;align-items:baseline">' +
|
|
1033
1053
|
marker + ' <span>' + escHtml(r.path) + '</span> <span style="color:var(--muted)">[' + escHtml(r.scope) + ']</span>' + missing +
|
|
1034
1054
|
'</div>';
|
|
@@ -1043,12 +1063,16 @@ function renderHarnessDiagnostics(diag) {
|
|
|
1043
1063
|
el.innerHTML = '<p class="empty">Harness diagnostics unavailable.</p>';
|
|
1044
1064
|
return;
|
|
1045
1065
|
}
|
|
1066
|
+
// Shared compact card style for every collapsed disclosure row so the section
|
|
1067
|
+
// reads as a tight stack instead of tall bordered boxes (W-ms0plsji000h0f9d).
|
|
1068
|
+
const cardStyle = 'margin:3px 0;border:1px solid var(--border);border-radius:6px;padding:4px 10px;background:var(--surface2)';
|
|
1069
|
+
const summaryStyle = 'font-weight:600;cursor:pointer;font-size:var(--text-sm)';
|
|
1046
1070
|
const parts = [];
|
|
1047
|
-
parts.push('<div style="font-size:var(--text-sm);color:var(--muted);margin-bottom:
|
|
1071
|
+
parts.push('<div style="font-size:var(--text-sm);color:var(--muted);margin-bottom:4px">Fleet-default runtime: <code style="color:var(--blue)">' + escHtml(diag.fleetDefaultCli || '?') + '</code>. Adapter inventory only: each CLI performs native discovery from its cwd and user configuration.</div>');
|
|
1048
1072
|
|
|
1049
1073
|
// Per-runtime adapter rows
|
|
1050
1074
|
for (const r of diag.runtimes || []) {
|
|
1051
|
-
parts.push('<details data-hkey="rt:' + escHtml(r.name) + '" style="
|
|
1075
|
+
parts.push('<details data-hkey="rt:' + escHtml(r.name) + '" style="' + cardStyle + '"><summary style="' + summaryStyle + '">Runtime: ' + escHtml(r.name) + '</summary>' +
|
|
1052
1076
|
_harnessRowsBlock('User asset dirs (adapter inventory)', r.userAssetDirs) +
|
|
1053
1077
|
_harnessRowsBlock('Skill roots (CLI native discovery)', r.skillRoots) +
|
|
1054
1078
|
_harnessRowsBlock('Skill write targets', r.skillWriteTargets) +
|
|
@@ -1057,10 +1081,11 @@ function renderHarnessDiagnostics(diag) {
|
|
|
1057
1081
|
'</details>');
|
|
1058
1082
|
}
|
|
1059
1083
|
|
|
1060
|
-
// Suppressed assets
|
|
1084
|
+
// Suppressed assets — default collapsed like the other cards; operator toggles
|
|
1085
|
+
// are still preserved across refreshes via the data-hkey open-state snapshot.
|
|
1061
1086
|
const suppressed = diag.suppressed || [];
|
|
1062
1087
|
if (suppressed.length > 0) {
|
|
1063
|
-
parts.push('<details data-hkey="suppressed"
|
|
1088
|
+
parts.push('<details data-hkey="suppressed" style="' + cardStyle + '"><summary style="' + summaryStyle + '">Suppressed assets <span style="color:var(--muted);font-weight:400">(' + suppressed.length + ')</span></summary>' +
|
|
1064
1089
|
suppressed.map(s =>
|
|
1065
1090
|
'<div style="margin:6px 0 0 12px"><code style="color:var(--orange)">' + escHtml(s.flag) + '</code> <span style="color:var(--muted);font-size:var(--text-sm)">(' + escHtml(String(s.value)) + ')</span>' +
|
|
1066
1091
|
'<div style="font-size:var(--text-sm);color:var(--muted);margin-left:18px">' + escHtml(s.effect || '') + '</div></div>'
|
|
@@ -1071,7 +1096,7 @@ function renderHarnessDiagnostics(diag) {
|
|
|
1071
1096
|
// Missing dirs summary (aggregate)
|
|
1072
1097
|
const missing = diag.missingDirs || [];
|
|
1073
1098
|
if (missing.length > 0) {
|
|
1074
|
-
parts.push('<details data-hkey="missingDirs" style="
|
|
1099
|
+
parts.push('<details data-hkey="missingDirs" style="' + cardStyle + '"><summary style="' + summaryStyle + '">Missing on-disk dirs <span style="color:var(--muted);font-weight:400">(' + missing.length + ')</span></summary>' +
|
|
1075
1100
|
'<div style="font-size:var(--text-sm);color:var(--muted);margin:4px 0 6px 12px">Adapters declare these paths but they do not exist on this host. Warnings, not failures — create them if the runtime should look there.</div>' +
|
|
1076
1101
|
missing.map(m =>
|
|
1077
1102
|
'<div style="font-family:monospace;font-size:var(--text-sm);margin-left:18px"><span style="color:var(--orange)">⚠</span> ' + escHtml(m.path) + ' <span style="color:var(--muted)">[' + escHtml(m.scope) + ' · ' + escHtml(m.kind) + ' · ' + escHtml(m.runtime) + ']</span></div>'
|
|
@@ -1097,7 +1122,7 @@ function renderHarnessDiagnostics(diag) {
|
|
|
1097
1122
|
}
|
|
1098
1123
|
}
|
|
1099
1124
|
|
|
1100
|
-
window.MinionsOther = { renderProjects, optimisticallyAddProject, projectChipRemove, renderMcpServers, renderCommands, renderHarnessDiagnostics, renderMetrics, renderLlmPerf, renderTokenUsage, _aggregateEngineUsageForTokenTile, openScanProjectsModal };
|
|
1125
|
+
window.MinionsOther = { renderProjects, optimisticallyAddProject, projectChipRemove, renderMcpServers, renderCommands, renderHarnessDiagnostics, renderMetrics, renderLlmPerf, renderTokenUsage, _aggregateEngineUsageForTokenTile, _formatTrackedCost, openScanProjectsModal };
|
|
1101
1126
|
|
|
1102
1127
|
// ─── keep_processes panel (W-mp68q6ke0010de68) ─────────────────────────────
|
|
1103
1128
|
// Polls /api/keep-processes every refresh (engine page only) and renders a
|