@yemi33/minions 0.1.2390 → 0.1.2392

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.
@@ -155,7 +155,7 @@ const RENDER_VERSIONS = {
155
155
  // chip row into its own non-interactive "Branch" field (W-mr2qjr3b0002522f).
156
156
  // Bumped 8→9 for paused pre-dispatch evaluation guidance.
157
157
  // Bumped 9→10: row actions now preserve work-item source scope when IDs collide.
158
- workItems: 10,
158
+ workItems: 11,
159
159
  skills: 1,
160
160
  commands: 1,
161
161
  mcpServers: 1,
@@ -179,13 +179,9 @@ function wiRow(item) {
179
179
  : (item.branchStrategy === 'shared-branch' && item.status === 'done')
180
180
  ? '<span style="font-size:var(--text-xs);color:var(--muted)" title="Part of shared branch — aggregate PR created at verify stage">shared branch</span>'
181
181
  : '<span style="color:var(--muted)">—</span>';
182
- // W-mqrdn6qq Non-retryable failures surface their red failReason snippet in
183
- // the PR column (not the Agent column). The engine prefixes failReason with
184
- // "Non-retryable failure:" and stamps item._failureClass on non-retryable
185
- // demotion (engine/dispatch.js writeDispatchResult / force-demote path).
186
- var isNonRetryableFail = !!item._failureClass
187
- || /^Non-retryable failure:/i.test(item.failReason || '')
188
- || /^Project "[^"]+" not found\./i.test(item.failReason || '');
182
+ // W-mqrdn6qq / W-mrkqc0pl ALL failure/error snippets (retryable AND
183
+ // non-retryable) surface their red failReason snippet in the PR column,
184
+ // never the Agent column.
189
185
  var failSnippet = item.failReason
190
186
  ? '<span style="display:block;font-size:var(--text-xs);color:var(--red)" title="' + escapeHtml(item.failReason) + '">' + escapeHtml(item.failReason.slice(0, 30)) + '</span>'
191
187
  : '';
@@ -234,9 +230,8 @@ function wiRow(item) {
234
230
  (item.completedAgents && item.completedAgents.length > 0
235
231
  ? '<span class="pr-agent">' + escapeHtml(item.completedAgents.join(', ')) + '</span>'
236
232
  : '<span class="pr-agent">' + escapeHtml(item.dispatched_to || item.agent || '—') + '</span>') +
237
- (failSnippet && !isNonRetryableFail ? failSnippet : '') +
238
233
  '</td>' +
239
- '<td>' + prLink + (failSnippet && isNonRetryableFail ? failSnippet : '') + '</td>' +
234
+ '<td>' + prLink + (failSnippet ? failSnippet : '') + '</td>' +
240
235
  '<td><span class="pr-date">' + escapeHtml((item.created || '').slice(0, 16).replace('T', ' ')) + '</span></td>' +
241
236
  '<td style="white-space:nowrap;font-size:var(--text-xs);color:var(--muted)">' +
242
237
  (function() {
@@ -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;
@@ -1,32 +1,32 @@
1
1
  [
2
2
  {
3
3
  "id": "agent-config-skills-field",
4
- "description": "Legacy per-agent descriptive-metadata array `agents.<id>.skills` in config.json, renamed to `agents.<id>.expertise` to remove the name collision with executable runtime/harness skills (SKILL.md). The field is metadata only (capability tags like `architecture`, `bug-fixes`); nothing in the dispatch path reads it for behavior. A read-compat shim honors the old key so operator configs still carrying `skills` (and no `expertise`) keep working.",
4
+ "description": "Legacy per-agent descriptive-metadata array `agents.<id>.skills` in config.json, renamed to `agents.<id>.expertise` to remove the name collision with executable runtime/harness skills (SKILL.md). The tags appear in the agent identity prompt and provide a soft preference to `pickReReviewAgentHints` when selecting a re-reviewer; they are not hard routing constraints. A read-compat shim honors the old key so operator configs still carrying `skills` (and no `expertise`) keep working.",
5
5
  "code": [
6
6
  {
7
7
  "file": "engine/playbook.js",
8
- "lines": "950",
8
+ "lines": "1094",
9
9
  "note": "buildSystemPrompt reads `agent.expertise ?? agent.skills ?? []` for the `Expertise:` identity line"
10
10
  },
11
11
  {
12
12
  "file": "engine/lifecycle.js",
13
- "lines": "4620-4621",
13
+ "lines": "5317-5321",
14
14
  "note": "pickReReviewAgentHints reads `agent.expertise` with an `agent.skills` array fallback"
15
15
  },
16
16
  {
17
17
  "file": "engine/queries.js",
18
- "lines": "731",
18
+ "lines": "830-832",
19
19
  "note": "getAgents normalizes `expertise: a.expertise ?? a.skills ?? []` so the dashboard/settings UI always receives `expertise`"
20
20
  },
21
21
  {
22
22
  "file": "dashboard.js",
23
- "lines": "10708-10716",
23
+ "lines": "11245-11252",
24
24
  "note": "settings POST accepts a legacy `updates.skills` key, persists as `config.agents[id].expertise`, and deletes the old `skills` key"
25
25
  }
26
26
  ],
27
27
  "removalGate": "Telemetry / a config sweep across all known engines must show no persisted `config.agents.<id>.skills` key (only `expertise`) for >=30 consecutive days, confirming every operator config has been re-saved through the dashboard (which drops the legacy key) or hand-migrated.",
28
28
  "targetRemovalDate": "2026-09-17",
29
- "notes": "Safe to remove on or after 2026-09-17 (~3 release windows) once the removal gate clears. Removal scope: drop the `?? agent.skills` / `?? a.skills` fallbacks in engine/playbook.js:950, engine/lifecycle.js:4620-4621, and engine/queries.js:731; drop the `updates.skills` legacy branch + `delete config.agents[id].skills` in dashboard.js:10708-10716; and update docs/named-agents.md to stop mentioning the legacy key. Does NOT touch the unrelated executable-skills system (queries.js harnesses, runtime adapters, dashboard renderSkills, SKILL.md tooling)."
29
+ "notes": "Safe to remove on or after 2026-09-17 (~3 release windows) once the removal gate clears. Removal scope: drop the `?? agent.skills` / `?? a.skills` fallbacks in engine/playbook.js:1094, engine/lifecycle.js:5317-5319, and engine/queries.js:832; drop the `updates.skills` legacy branch + `delete config.agents[id].skills` in dashboard.js:11245-11252; and update docs/named-agents.md to stop mentioning the legacy key. Does NOT touch the unrelated executable-skills system (queries.js harnesses, runtime adapters, dashboard renderSkills, SKILL.md tooling)."
30
30
  },
31
31
  {
32
32
  "id": "config-poll-key-migration",