@yemi33/minions 0.1.2390 → 0.1.2391

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.
@@ -33,6 +33,38 @@ function _isCurrentModelLoad(scope, key, token) {
33
33
  return _modelLoadEpochs[scope][key] === token;
34
34
  }
35
35
 
36
+ // Per-open dedup cache for /api/runtimes/<name>/models payloads.
37
+ // initRuntimeFleetUI() fans out one model fetch per agent PLUS default + CC.
38
+ // When the whole fleet shares a runtime (the common case) those are
39
+ // byte-identical requests, and each one makes the server reloadConfig() from
40
+ // disk + re-run model discovery. Collapsing them to one request per distinct
41
+ // runtime per Settings open removes the N+2 → 1 duplicate round-trips (measured
42
+ // 10 → 1 for an 8-agent single-runtime fleet). forceRefresh (the ↻ button)
43
+ // bypasses the cache and repopulates it with the fresh list. Transient
44
+ // failures are NOT cached so a blip can't poison every agent cell for the open.
45
+ let _modelFetchCache = new Map();
46
+ function _resetModelFetchCache() { _modelFetchCache = new Map(); }
47
+ function _fetchRuntimeModelsPayload(runtimeName, forceRefresh) {
48
+ if (!runtimeName) return Promise.resolve({ models: null });
49
+ if (!forceRefresh) {
50
+ const cached = _modelFetchCache.get(runtimeName);
51
+ if (cached) return cached;
52
+ }
53
+ const suffix = forceRefresh ? '/models/refresh' : '/models';
54
+ const p = fetch('/api/runtimes/' + encodeURIComponent(runtimeName) + suffix, forceRefresh ? { method: 'POST' } : undefined)
55
+ .then(res => (res.ok ? res.json() : null))
56
+ .catch(() => null)
57
+ .then(payload => {
58
+ if (!payload || !Array.isArray(payload.models)) {
59
+ if (_modelFetchCache.get(runtimeName) === p) _modelFetchCache.delete(runtimeName);
60
+ return payload || { models: null };
61
+ }
62
+ return payload;
63
+ });
64
+ _modelFetchCache.set(runtimeName, p);
65
+ return p;
66
+ }
67
+
36
68
  async function openSettings() {
37
69
  document.getElementById('modal-title').textContent = 'Settings';
38
70
  document.getElementById('modal-body').innerHTML = '<p style="color:var(--muted)">Loading...</p>';
@@ -719,8 +751,16 @@ async function openSettings() {
719
751
  // hides unmatched rows so operators can see results without tab-hopping.
720
752
  const searchInput = document.getElementById('settings-search');
721
753
  if (searchInput) {
754
+ // Debounce the filter: _applySettingsSearch runs several full-DOM
755
+ // querySelectorAll scans and reads textContent of every [data-search] row
756
+ // (a forced reflow). Firing that per-keystroke made typing janky on the
757
+ // large Settings DOM. The field itself stays instant; only the filter
758
+ // computation is coalesced to the last keystroke in a ~120ms window.
759
+ let _searchDebounce = null;
722
760
  searchInput.addEventListener('input', function() {
723
- _applySettingsSearch(searchInput.value || '');
761
+ const val = searchInput.value || '';
762
+ if (_searchDebounce) clearTimeout(_searchDebounce);
763
+ _searchDebounce = setTimeout(function() { _applySettingsSearch(val); }, 120);
724
764
  });
725
765
  }
726
766
 
@@ -822,6 +862,9 @@ async function initRuntimeFleetUI(engineCfg, agentsCfg) {
822
862
  const ccCliSelect = document.getElementById('set-ccCli');
823
863
  if (!cliSelect || !ccCliSelect) return;
824
864
 
865
+ // Fresh cache per Settings open so a config change between opens is picked up.
866
+ _resetModelFetchCache();
867
+
825
868
  // Fetch the registry; render an empty fallback on failure so the rest of the
826
869
  // settings panel still works.
827
870
  let runtimes = [];
@@ -917,11 +960,7 @@ async function loadModelsForRuntime(runtimeName, inputId, currentValue, forceRef
917
960
  return;
918
961
  }
919
962
  let payload = { models: null };
920
- try {
921
- const suffix = forceRefresh ? '/models/refresh' : '/models';
922
- const res = await fetch('/api/runtimes/' + encodeURIComponent(runtimeName) + suffix, forceRefresh ? { method: 'POST' } : undefined);
923
- if (res.ok) payload = await res.json();
924
- } catch { /* fall through to free-text */ }
963
+ payload = await _fetchRuntimeModelsPayload(runtimeName, forceRefresh);
925
964
 
926
965
  if (!_isCurrentModelLoad('runtime', inputId, token)) return;
927
966
  const models = Array.isArray(payload.models) ? payload.models : null;
@@ -962,10 +1001,7 @@ async function loadModelsForAgent(agentId, runtimeName, currentValue) {
962
1001
  return;
963
1002
  }
964
1003
  let payload = { models: null };
965
- try {
966
- const res = await fetch('/api/runtimes/' + encodeURIComponent(runtimeName) + '/models');
967
- if (res.ok) payload = await res.json();
968
- } catch { /* fall through to free-text */ }
1004
+ payload = await _fetchRuntimeModelsPayload(runtimeName, false);
969
1005
 
970
1006
  if (!_isCurrentModelLoad('agent', agentId, token)) return;
971
1007
  const models = Array.isArray(payload.models) ? payload.models : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2390",
3
+ "version": "0.1.2391",
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"