@yemi33/minions 0.1.445 → 0.1.447

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/CHANGELOG.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.445 (2026-04-06)
3
+ ## 0.1.447 (2026-04-06)
4
4
 
5
5
  ### Features
6
6
  - notification dot on Notes & KB sidebar when sweep completes/fails
7
7
 
8
8
  ### Fixes
9
+ - prevent double-dispatch — dedup by work item ID and dispatchKey
10
+ - pipeline agent is optional — set only when user explicitly requests
9
11
  - remove hardcoded agent assignments from all pipeline JSONs
10
12
  - pipeline tasks don't hardcode agent — any available agent picks up work
11
13
 
package/dashboard.js CHANGED
@@ -513,7 +513,7 @@ Available action types:
513
513
  - **delete-schedule**: Delete a scheduled task. Fields: id.
514
514
  - **create-meeting**: Start a team meeting. Fields: title (short meeting name), agenda (detailed text — what agents should investigate/debate, numbered items work best), agents (array of agent IDs), rounds (optional, default 3), project (optional).
515
515
  - **set-config**: Update engine settings. Fields: setting (setting name), value (new value). Valid settings: autoApprovePlans (bool), autoDecompose (bool), allowTempAgents (bool), maxConcurrent (number), maxTurns (number). Example: { "type": "set-config", "setting": "autoApprovePlans", "value": true }
516
- - **edit-pipeline**: Update an existing pipeline. Fields: id (pipeline ID), title (optional), stages (optional, JSON array — do NOT set "agent" on stages; the engine routes to any available agent automatically), trigger (optional, { cron: "minute hour dow" } or null for manual).
516
+ - **edit-pipeline**: Update an existing pipeline. Fields: id (pipeline ID), title (optional), stages (optional, JSON array — omit "agent" on stages unless the user specifically requests one; the engine routes to any available agent by default), trigger (optional, { cron: "minute hour dow" } or null for manual).
517
517
  - **unpin**: Remove a pinned note. Fields: title (exact title of the pinned note to remove)
518
518
  - **archive-plan**: Archive a completed/paused plan. Fields: file (PRD .json or plan .md filename)
519
519
  - **reject-plan**: Reject a plan. Fields: file (PRD .json filename), reason (optional)
@@ -37,11 +37,30 @@ function mutateDispatch(mutator) {
37
37
  function addToDispatch(item) {
38
38
  item.id = item.id || `${item.agent}-${item.type}-${shared.uid()}`;
39
39
  item.created_at = ts();
40
+ let added = false;
40
41
  mutateDispatch((dispatch) => {
42
+ // Dedup: skip if same work item ID is already pending or active
43
+ const wiId = item.meta?.item?.id;
44
+ if (wiId) {
45
+ const existing = [...dispatch.pending, ...(dispatch.active || [])].find(d => d.meta?.item?.id === wiId);
46
+ if (existing) {
47
+ log('info', `Dedup: skipping ${item.id} — work item ${wiId} already in ${existing.id}`);
48
+ return dispatch;
49
+ }
50
+ }
51
+ // Also dedup by dispatchKey
52
+ if (item.meta?.dispatchKey) {
53
+ const existing = [...dispatch.pending, ...(dispatch.active || [])].find(d => d.meta?.dispatchKey === item.meta.dispatchKey);
54
+ if (existing) {
55
+ log('info', `Dedup: skipping ${item.id} — dispatchKey ${item.meta.dispatchKey} already in ${existing.id}`);
56
+ return dispatch;
57
+ }
58
+ }
41
59
  dispatch.pending.push(item);
60
+ added = true;
42
61
  return dispatch;
43
62
  });
44
- log('info', `Queued dispatch: ${item.id} (${item.type} → ${item.agent})`);
63
+ if (added) log('info', `Queued dispatch: ${item.id} (${item.type} → ${item.agent})`);
45
64
  return item.id;
46
65
  }
47
66
 
@@ -156,7 +156,7 @@ async function executeStage(stage, run, pipeline, config) {
156
156
 
157
157
  function executeTaskStage(stage, stageState, run, config) {
158
158
  // Create work item(s) for the task
159
- const items = stage.items || [{ title: stage.title, description: stage.description || '', type: stage.taskType || 'explore' }];
159
+ const items = stage.items || [{ title: stage.title, description: stage.description || '', type: stage.taskType || 'explore', agent: stage.agent }];
160
160
  const count = stage.count || items.length;
161
161
  const wiPath = path.join(__dirname, '..', 'work-items.json');
162
162
  const workItems = safeJson(wiPath) || [];
@@ -172,7 +172,8 @@ function executeTaskStage(stage, stageState, run, config) {
172
172
  description: item.description || stage.description || '',
173
173
  type: item.type || stage.taskType || 'explore',
174
174
  priority: item.priority || stage.priority || 'medium',
175
- // No hardcoded agent — let engine routing assign any available agent
175
+ // Only set agent if explicitly specified otherwise engine routing assigns any available agent
176
+ ...(item.agent || stage.agent ? { agent: item.agent || stage.agent } : {}),
176
177
  status: WI_STATUS.PENDING,
177
178
  created: ts(),
178
179
  createdBy: 'pipeline:' + run.pipelineId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.445",
3
+ "version": "0.1.447",
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"