@yemi33/minions 0.1.2229 → 0.1.2230

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/dashboard.js CHANGED
@@ -6965,9 +6965,28 @@ const server = http.createServer(async (req, res) => {
6965
6965
  // Replay-on-connect: if a terminal event for this agent landed within
6966
6966
  // the ring's TTL, push it immediately so a viewer who opens the live
6967
6967
  // view AFTER completion still sees the verdict.
6968
+ //
6969
+ // W-mqprx2px00076668 — but a STALE terminal failure must not be replayed
6970
+ // as the active blocking banner when the agent has since started a newer
6971
+ // dispatch (e.g. a successful retry of the same work item). Gate the
6972
+ // replay on the agent's live dispatch state: suppress the failure banner
6973
+ // when the agent is actively working a different (newer) dispatch than the
6974
+ // one that produced the event. History is preserved — the raw event still
6975
+ // lives in the JSONL ring and the WI/event history; only the prominent
6976
+ // active-agent banner is gated.
6968
6977
  try {
6969
6978
  const recent = dispatchEvents.getLastTerminalForAgent(agentId);
6970
- if (recent) sendTerminalEvent(recent);
6979
+ if (recent) {
6980
+ let agentStatus = null;
6981
+ try { agentStatus = queries.getAgentStatus(agentId); } catch { agentStatus = null; }
6982
+ if (dispatchEvents.isStaleTerminalForActiveDispatch(recent, agentStatus)) {
6983
+ // Mark as already-handled so a later file-rotation rewind in the
6984
+ // terminal watcher cannot re-broadcast this suppressed stale banner.
6985
+ if (recent.dispatchId) _sentTerminalDispatchIds.add(recent.dispatchId);
6986
+ } else {
6987
+ sendTerminalEvent(recent);
6988
+ }
6989
+ }
6971
6990
  } catch { /* best-effort replay */ }
6972
6991
 
6973
6992
  const eventsFile = dispatchEvents._eventsFile();
@@ -141,6 +141,45 @@ function getLastTerminalForAgent(agentId) {
141
141
  return null;
142
142
  }
143
143
 
144
+ /**
145
+ * W-mqprx2px00076668 — gate a terminal-FAILURE banner against the agent's
146
+ * live dispatch state. A stale terminal failure (result !== 'success', e.g.
147
+ * `error` / `timeout`) MUST NOT be surfaced as the active blocking banner
148
+ * once the SAME agent is actively working a NEWER dispatch than the one that
149
+ * produced the event. The active-dispatch state wins over any older terminal
150
+ * error whose `dispatchId` is not the agent's current dispatch.
151
+ *
152
+ * This is a GENERIC rule across all failureClasses — keyed solely on "does
153
+ * this terminal event belong to the agent's CURRENT dispatch?" — not a
154
+ * special-case for any one failureClass. Pure (no I/O): the caller passes the
155
+ * agent's status snapshot so the predicate stays unit-testable and reusable
156
+ * by both the dashboard replay-on-connect path and the client renderer.
157
+ *
158
+ * Returns true when the event should be suppressed as the active banner.
159
+ * Does NOT delete history — the raw event still lives in the JSONL ring and
160
+ * the WI/event history; only the prominent active-agent banner is gated.
161
+ *
162
+ * @param {object} event - dispatch.terminal event ({ result, dispatchId, ... }).
163
+ * @param {object} agentStatus - agent status snapshot, e.g. the
164
+ * `{ status, dispatch_id }` shape returned by queries.getAgentStatus().
165
+ * @returns {boolean}
166
+ */
167
+ function isStaleTerminalForActiveDispatch(event, agentStatus) {
168
+ if (!event || !event.dispatchId) return false;
169
+ // Success banners never imply the run is blocked — leave them to the
170
+ // dispatch.started clear path. Only failure/timeout banners are gated.
171
+ if (event.result === 'success') return false;
172
+ // No active work → keep the failure banner. An agent that is genuinely
173
+ // idle (or in the post-completion `error`/`done` window) after a terminal
174
+ // failure STILL shows its banner; don't over-suppress.
175
+ if (!agentStatus || agentStatus.status !== 'working') return false;
176
+ const current = agentStatus.dispatch_id || null;
177
+ if (!current) return false;
178
+ // Agent is actively working a DIFFERENT (newer) dispatch than the one that
179
+ // produced this terminal failure → suppress it as the active banner.
180
+ return current !== event.dispatchId;
181
+ }
182
+
144
183
 
145
184
  /**
146
185
  * Build the terminal event payload from a freshly-completed dispatch entry +
@@ -237,6 +276,7 @@ module.exports = {
237
276
  onDispatchTerminal,
238
277
  readRecentEvents,
239
278
  getLastTerminalForAgent,
279
+ isStaleTerminalForActiveDispatch,
240
280
  buildTerminalEvent,
241
281
  // dispatch.started (W-mq1m1wo5000nd78d)
242
282
  emitDispatchStarted,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2229",
3
+ "version": "0.1.2230",
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"