@yemi33/minions 0.1.2179 → 0.1.2181

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.
@@ -422,6 +422,22 @@ async function openSettings() {
422
422
  'Permission bypass is runtime-owned: Claude agents use <code>--dangerously-skip-permissions</code>; Copilot agents use <code>--autopilot --allow-all --no-ask-user</code>. There is no dashboard permission-mode setting.' +
423
423
  '</div>';
424
424
 
425
+ // Harness propagation knobs (PL-seamless-harness-invocation, P-2bd84e91).
426
+ // All three flags govern what the spawned agent inherits from the operator's
427
+ // local CLI harness (user-scope skills/commands/MCPs, project-local-on-main
428
+ // assets, Claude workspace .mcp.json pre-approval). See docs/harness-propagation.md.
429
+ const paneHarness =
430
+ '<h3>Harness Propagation</h3>' +
431
+ '<div class="settings-pane-sub">Controls what the spawned agent sees from your local CLI harness (skills, commands, MCPs). Defaults match the docs/harness-propagation.md contract — flip these only if you understand the tradeoff. The Tools tab\'s Harness diagnostics view shows the live propagated surface.</div>' +
432
+ '<div class="settings-stack" style="margin-bottom:12px">' +
433
+ settingsToggle('Propagate project-local harness on main', 'set-harnessPropagateProjectLocal', e.harnessPropagateProjectLocal !== false,
434
+ 'P-08b62d49: surface uncommitted <repo>/.claude/skills, <repo>/.copilot/commands, etc. into the worktree dispatch via extra --add-dir entries. Default ON closes the worktree-uncommitted footgun. Turn OFF to fall back to the legacy "only committed assets are visible" behavior.') +
435
+ settingsToggle('Claude: pre-approve workspace .mcp.json', 'set-claudePreApproveWorkspaceMcps', e.claudePreApproveWorkspaceMcps !== false,
436
+ 'P-7d31a06b: pre-warms ~/.claude.json projects.<worktree>.enabledMcpjsonServers on every Claude spawn so the first invocation in a brand-new worktree skips the "trust this server?" prompt — which would otherwise be invisible behind --dangerously-skip-permissions and silently drop workspace MCPs. Claude-only (no-op on Copilot/Codex). Best-effort: failures never block dispatch.') +
437
+ settingsToggle('Hermetic harness (drop user-scope assets)', 'set-hermeticHarness', !!e.hermeticHarness,
438
+ '⚠ P-49e1c8b7 fleet-wide opt-out: when ON, --add-dir is exactly [minionsDir] (user-scope skill/command/MCP roots dropped), project-local propagation skipped, and Claude workspace .mcp.json pre-approval skipped. Use when you want a known-empty harness surface (reproducible CI runs, debugging "works in my CLI" bugs). Per-agent override available via agent.hermeticHarness. Default OFF.') +
439
+ '</div>';
440
+
425
441
  const paneBudget =
426
442
  '<h3>Budget</h3>' +
427
443
  '<div class="settings-pane-sub">Fleet-wide spend ceiling. Per-agent monthly caps live in the Agents table on the Runtime &amp; Models tab.</div>' +
@@ -522,6 +538,7 @@ async function openSettings() {
522
538
  { id: 'worktree', label: 'Worker Pool & Worktrees', html: paneWorktree },
523
539
  { id: 'copilot', label: 'Copilot Tuning', html: paneCopilot },
524
540
  { id: 'claude', label: 'Claude Tuning', html: paneClaude },
541
+ { id: 'harness', label: 'Harness Propagation', html: paneHarness },
525
542
  { id: 'budget', label: 'Budget', html: paneBudget },
526
543
  { id: 'maxturns', label: 'Max Turns', html: paneMaxTurns },
527
544
  { id: 'features', label: 'Feature Flags', html: paneFeatures },
@@ -1025,6 +1042,12 @@ async function saveSettings() {
1025
1042
  copilotSuppressAgentsMd: !!document.getElementById('set-copilotSuppressAgentsMd')?.checked,
1026
1043
  copilotStreamMode: document.getElementById('set-copilotStreamMode')?.value || 'on',
1027
1044
  copilotReasoningSummaries: !!document.getElementById('set-copilotReasoningSummaries')?.checked,
1045
+ // Harness propagation knobs (P-2bd84e91). Booleans round-trip through
1046
+ // handleSettingsUpdate's auto boolean-fields loop because all three live
1047
+ // in ENGINE_DEFAULTS as booleans.
1048
+ harnessPropagateProjectLocal: !!document.getElementById('set-harnessPropagateProjectLocal')?.checked,
1049
+ claudePreApproveWorkspaceMcps: !!document.getElementById('set-claudePreApproveWorkspaceMcps')?.checked,
1050
+ hermeticHarness: !!document.getElementById('set-hermeticHarness')?.checked,
1028
1051
  maxBudgetUsd: (document.getElementById('set-maxBudgetUsd')?.value ?? '').trim(),
1029
1052
  disableModelDiscovery: !!document.getElementById('set-disableModelDiscovery')?.checked,
1030
1053
  qaDualWriteJson: !!document.getElementById('set-qaDualWriteJson')?.checked,
@@ -46,4 +46,11 @@
46
46
  <div id="memory-dashboard-sparkline" style="height:60px;width:100%;color:var(--fg)"></div>
47
47
  </div>
48
48
  </div>
49
+ <div id="memory-heap-snapshot-panel" style="margin-top:12px;padding:10px;border:1px solid var(--border);border-radius:4px;background:var(--surface2)">
50
+ <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap">
51
+ <button type="button" id="memory-heap-snapshot-btn" class="btn" style="cursor:pointer">Capture heap snapshot</button>
52
+ <span style="font-size:var(--text-sm);color:var(--muted)">Stalls engine + dashboard for several seconds, writes a 50–200 MB <code>.heapsnapshot</code> per process under <code>engine/diagnostics/</code> (load in Chrome DevTools → Memory → Load). Rate-limited to 1 / 60 s; only the 5 most-recent per process are kept.</span>
53
+ </div>
54
+ <div id="memory-heap-snapshot-result" style="margin-top:8px;font-family:monospace;font-size:var(--text-sm);color:var(--muted);white-space:pre-wrap"></div>
55
+ </div>
49
56
  </section>
@@ -2,7 +2,15 @@
2
2
  <h2>Minions Skills <span class="count" id="skills-count">0</span> <span style="font-size:var(--text-sm);color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">discovered from runtime native dirs, plugin installs, and configured project repos</span></h2>
3
3
  <div id="skills-list"><p class="empty">No skills yet. Agents create these when they discover repeatable workflows.</p></div>
4
4
  </section>
5
+ <section>
6
+ <h2>Slash Commands <span class="count" id="commands-count">0</span> <span style="font-size:var(--text-sm);color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">discovered from runtime native command dirs, plugin installs, and configured project repos</span></h2>
7
+ <div id="commands-list"><p class="empty">No slash commands discovered.</p></div>
8
+ </section>
5
9
  <section>
6
10
  <h2>MCP Servers <span class="count" id="mcp-count">0</span></h2>
7
11
  <div id="mcp-list"><p class="empty">No MCP servers synced.</p></div>
8
12
  </section>
13
+ <section>
14
+ <h2>Harness Propagation <span style="font-size:var(--text-sm);color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">what each runtime sees, what's attached via --add-dir, and which assets won't propagate to a fresh worktree</span></h2>
15
+ <div id="harness-diag"><p class="empty">Loading harness diagnostics…</p></div>
16
+ </section>
@@ -74,19 +74,51 @@ const SLIM_JS_ORDER = [
74
74
  'command-send', 'status', 'members', 'modals-tiles', 'history', 'pinned',
75
75
  ];
76
76
 
77
+ // Cache for the assembled slim source fragments (layout/css/body/js). Keyed on
78
+ // the input file mtimes so dev edits still take effect on the next request
79
+ // without a server restart, while production serves repeated requests without
80
+ // re-reading the ~14 fragment files each time. Phase 3 perf audit (P-a7b8c9d0)
81
+ // measured `buildSlimHtml()` at ~7.7 ms/req pre-cache; post-cache it's ~14
82
+ // cheap fs.statSync() calls + one string substitution per request (~sub-ms).
83
+ let _slimPartsCache = null;
84
+
85
+ function _slimSourcePaths(slimDir) {
86
+ return [
87
+ path.join(slimDir, 'layout.html'),
88
+ path.join(slimDir, 'styles.css'),
89
+ path.join(slimDir, 'body.html'),
90
+ ...SLIM_JS_ORDER.map(f => path.join(slimDir, 'js', f + '.js')),
91
+ ];
92
+ }
93
+
94
+ function _statMtimeOrZero(p) {
95
+ try { return fs.statSync(p).mtimeMs; } catch { return 0; }
96
+ }
97
+
77
98
  function buildSlimHtml(opts) {
78
99
  const slimDir = path.join(MINIONS_DIR, 'dashboard', 'slim');
79
100
  const layoutPath = path.join(slimDir, 'layout.html');
80
101
  if (!fs.existsSync(layoutPath)) {
81
102
  throw new Error(`Slim layout not found: ${layoutPath}. The dashboard/slim/ directory must exist.`);
82
103
  }
83
- const layout = safeRead(layoutPath);
84
- const css = safeRead(path.join(slimDir, 'styles.css'));
85
- const body = safeRead(path.join(slimDir, 'body.html'));
86
- // Join with a newline so a fragment that loses its trailing newline can't
87
- // glue two statements together (e.g. `}var foo`). Parts share one IIFE
88
- // scope, so order is load-bearing — see SLIM_JS_ORDER.
89
- const js = SLIM_JS_ORDER.map(f => safeRead(path.join(slimDir, 'js', f + '.js'))).join('\n');
104
+ const paths = _slimSourcePaths(slimDir);
105
+ const mtimes = paths.map(_statMtimeOrZero);
106
+ if (!_slimPartsCache
107
+ || _slimPartsCache.mtimes.length !== mtimes.length
108
+ || _slimPartsCache.mtimes.some((m, i) => m !== mtimes[i])) {
109
+ const [lp, cp, bp, ...jsPaths] = paths;
110
+ _slimPartsCache = {
111
+ mtimes,
112
+ layout: safeRead(lp),
113
+ css: safeRead(cp),
114
+ body: safeRead(bp),
115
+ // Join with a newline so a fragment that loses its trailing newline
116
+ // can't glue two statements together (e.g. `}var foo`). Parts share one
117
+ // IIFE scope, so order is load-bearing — see SLIM_JS_ORDER.
118
+ js: jsPaths.map(p => safeRead(p)).join('\n'),
119
+ };
120
+ }
121
+ const { layout, css, body, js } = _slimPartsCache;
90
122
 
91
123
  // Feature-flag bootstrap (window.MINIONS_FEATURES) injected at the top of the
92
124
  // slim IIFE. serveSlimUx passes the live flags as JSON via opts.featuresJson;
@@ -103,4 +135,8 @@ function buildSlimHtml(opts) {
103
135
  .replace('/* __JS__ */', () => js);
104
136
  }
105
137
 
106
- module.exports = { buildDashboardHtml, buildSlimHtml, SLIM_JS_ORDER };
138
+ // exported for testing — lets tests reset the module-scope cache so they can
139
+ // observe a cold first call without depending on test execution order.
140
+ function _resetSlimPartsCacheForTest() { _slimPartsCache = null; }
141
+
142
+ module.exports = { buildDashboardHtml, buildSlimHtml, SLIM_JS_ORDER, _resetSlimPartsCacheForTest };