@yemi33/minions 0.1.1645 → 0.1.1646
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 +12 -0
- package/README.md +1 -1
- package/dashboard/js/command-center.js +0 -4
- package/dashboard.js +0 -15
- package/docs/auto-discovery.md +1 -1
- package/docs/pr-review-fix-loop.md +21 -17
- package/engine/cli.js +34 -8
- package/engine/copilot-models.json +1 -1
- package/engine/dispatch.js +33 -0
- package/engine/lifecycle.js +60 -10
- package/engine/shared.js +1 -3
- package/package.json +1 -1
- package/prompts/cc-system.md +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1646 (2026-05-01)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- scope syncPrsFromOutput inbox fallback to empty stdout
|
|
7
|
+
- execAsync no longer unref's child, fixes silent test exit
|
|
8
|
+
|
|
9
|
+
### Other
|
|
10
|
+
- Prevent CLI dispatch from orphaning agents
|
|
11
|
+
- Harden eval loop gating audit
|
|
12
|
+
- chore: gitignore agent runtime state and stray repo-root artifacts
|
|
13
|
+
- revert: remove CC hallucination detector — too many false positives
|
|
14
|
+
|
|
3
15
|
## 0.1.1645 (2026-04-30)
|
|
4
16
|
|
|
5
17
|
### Other
|
package/README.md
CHANGED
|
@@ -545,7 +545,7 @@ Engine behavior is controlled via `config.json`. Key settings:
|
|
|
545
545
|
| `autoDecompose` | true | Auto-decompose `implement:large` items into sub-tasks before dispatch |
|
|
546
546
|
| `autoApprovePlans` | false | Auto-approve PRDs without waiting for human approval |
|
|
547
547
|
| `evalLoop` | true | Auto-dispatch review → fix cycles after implementation completes |
|
|
548
|
-
| `evalMaxIterations` | 3 | Max review → fix cycles before
|
|
548
|
+
| `evalMaxIterations` | 3 | Max minion review → fix cycles before pausing minion review automation; human feedback, build fixes, and conflict fixes continue |
|
|
549
549
|
| `evalMaxCost` | null | USD ceiling per work item across all eval iterations (null = no limit) |
|
|
550
550
|
| `meetingRoundTimeout` | 600000 (10min) | Timeout per meeting round before auto-advance |
|
|
551
551
|
| `ccModel` | `sonnet` | Model for Command Center and doc-chat (sonnet/haiku/opus) |
|
|
@@ -706,10 +706,6 @@ async function _ccDoSend(message, skipUserMsg, forceTabId) {
|
|
|
706
706
|
// Surface as an inline warning so the user knows actions were dropped
|
|
707
707
|
// (was previously silent — appeared as "actions failed" with no signal).
|
|
708
708
|
addMsg('system', '<div style="padding:6px 12px;font-size:11px;color:var(--red);background:var(--surface2);border-radius:6px;margin:4px 0">⚠️ Actions block emitted but JSON could not be parsed — no actions were executed. Resend or rephrase. (' + escHtml(String(evt.actionParseError).slice(0, 200)) + ')</div>', false, activeTabId);
|
|
709
|
-
} else if (evt.hallucinationWarning) {
|
|
710
|
-
// CC said it dispatched/queued/assigned something but emitted no actions block —
|
|
711
|
-
// surface the false-claim guard so the user knows nothing actually ran.
|
|
712
|
-
addMsg('system', '<div style="padding:6px 12px;font-size:11px;color:var(--orange);background:var(--surface2);border-radius:6px;margin:4px 0">⚠️ ' + escHtml(evt.hallucinationWarning) + '</div>', false, activeTabId);
|
|
713
709
|
}
|
|
714
710
|
} else if (evt.type === 'error') {
|
|
715
711
|
terminalEventSeen = true;
|
package/dashboard.js
CHANGED
|
@@ -1345,16 +1345,6 @@ function _ccValidateAction(action) {
|
|
|
1345
1345
|
}
|
|
1346
1346
|
}
|
|
1347
1347
|
|
|
1348
|
-
// Hallucination guard: detect prose like "I dispatched ..." when no ===ACTIONS=== block was emitted.
|
|
1349
|
-
// The regex is intentionally narrow — we only want affirmative claims about completed work, not
|
|
1350
|
-
// hypotheticals like "I would dispatch this" or "consider dispatching X".
|
|
1351
|
-
function _detectClaimedActionWithoutBlock(displayText, actions) {
|
|
1352
|
-
if (Array.isArray(actions) && actions.length > 0) return null; // there are actions, no false claim
|
|
1353
|
-
const triggers = /\b(dispatched|enqueued|queued|created (?:a |the )?work item|assigned (?:this |it )?(?:to|for)|spun up|kicked off|i'?ll dispatch|i (?:have )?(?:just )?dispatched)\b/i;
|
|
1354
|
-
if (!triggers.test(displayText || '')) return null;
|
|
1355
|
-
return 'CC described an action ("dispatched", "assigned", etc.) but no ===ACTIONS=== block was emitted. No work was actually queued. Resend or rephrase the request.';
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
1348
|
async function executeCCActions(actions) {
|
|
1359
1349
|
const results = [];
|
|
1360
1350
|
for (const action of actions) {
|
|
@@ -4864,8 +4854,6 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
4864
4854
|
const { _actionParseError, ...parsedReply } = parsed;
|
|
4865
4855
|
const reply = { ...parsedReply, sessionId: ccSession.sessionId, newSession: !wasResume };
|
|
4866
4856
|
if (_actionParseError) reply.actionParseError = _actionParseError;
|
|
4867
|
-
const hallucinationWarning = _detectClaimedActionWithoutBlock(parsed.text, parsed.actions);
|
|
4868
|
-
if (hallucinationWarning) reply.hallucinationWarning = hallucinationWarning;
|
|
4869
4857
|
if (sessionReset) reply.sessionReset = true;
|
|
4870
4858
|
return jsonReply(res, 200, reply);
|
|
4871
4859
|
} finally {
|
|
@@ -5149,8 +5137,6 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5149
5137
|
// Issue #1834: surface action JSON parse failures so the UI can warn
|
|
5150
5138
|
// instead of silently dropping. Client renders this as a small notice.
|
|
5151
5139
|
if (_actionParseError) donePayload.actionParseError = _actionParseError;
|
|
5152
|
-
const hallucinationWarning = _detectClaimedActionWithoutBlock(displayText, actions);
|
|
5153
|
-
if (hallucinationWarning) donePayload.hallucinationWarning = hallucinationWarning;
|
|
5154
5140
|
if (sessionReset) donePayload.sessionReset = true;
|
|
5155
5141
|
liveState.donePayload = donePayload;
|
|
5156
5142
|
if (liveState.writer) liveState.writer(donePayload);
|
|
@@ -6550,7 +6536,6 @@ module.exports = {
|
|
|
6550
6536
|
_resolveSkillReadPath,
|
|
6551
6537
|
DOC_CHAT_DOCUMENT_DELIMITER,
|
|
6552
6538
|
_ccValidateAction,
|
|
6553
|
-
_detectClaimedActionWithoutBlock,
|
|
6554
6539
|
executeCCActions,
|
|
6555
6540
|
};
|
|
6556
6541
|
|
package/docs/auto-discovery.md
CHANGED
|
@@ -37,7 +37,7 @@ Before scanning, the engine materializes plans and specs into project work items
|
|
|
37
37
|
| `_mergeConflict: true` | Route to author for conflict resolution | `fix` |
|
|
38
38
|
Skips PRs where `status !== "active"`.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Inside `discoverFromPrs()`, `evalLoop` / `evalMaxIterations` only gate the minion review loop: initial minion reviews, minion re-reviews, and minion review-feedback fixes. Human-feedback fixes are evaluated outside that gate, build failures use the separate `maxBuildFixAttempts` cap, and merge conflicts use the separate `autoFixConflicts` gate. Conflict fixes are additionally gated by `!fixDispatched`, so an earlier successful human/review/build fix dispatch in the same PR discovery pass suppresses the conflict fix until a later pass.
|
|
41
41
|
|
|
42
42
|
### Source 2: PRD Gap Analysis (via `materializePlansAsWorkItems`)
|
|
43
43
|
|
|
@@ -23,30 +23,34 @@ How the engine manages the lifecycle of a PR from creation through review, fix,
|
|
|
23
23
|
|
|
24
24
|
## 4. Fix dispatch trigger order
|
|
25
25
|
|
|
26
|
-
`discoverFromPrs()` evaluates PR
|
|
26
|
+
`discoverFromPrs()` evaluates PR review/fix triggers in a fixed order during each discovery pass:
|
|
27
27
|
|
|
28
|
-
1.
|
|
29
|
-
2. Human feedback (`humanFeedback.pendingFix` or coalesced feedback)
|
|
30
|
-
3.
|
|
31
|
-
4.
|
|
28
|
+
1. Initial minion review (`reviewStatus === 'pending'`)
|
|
29
|
+
2. Human feedback (`humanFeedback.pendingFix` or coalesced feedback)
|
|
30
|
+
3. Minion re-review (`reviewStatus === 'waiting'` after a fix)
|
|
31
|
+
4. Minion review feedback (`changes-requested`)
|
|
32
|
+
5. Build failure (`buildStatus === 'failing'`)
|
|
33
|
+
6. Merge conflict (`_mergeConflict`)
|
|
32
34
|
|
|
33
|
-
When multiple problems coexist, earlier triggers get the first chance to enqueue work. The local `fixDispatched` flag is declared before the fix triggers
|
|
35
|
+
When multiple problems coexist, earlier triggers get the first chance to enqueue work. The local `fixDispatched` flag is declared before the fix triggers and set after human-feedback, review-feedback, and build-failure dispatches. Conflict fixes run last and explicitly require `!fixDispatched`, so any earlier successful fix dispatch suppresses the conflict fix for that PR in the same discovery pass. Build fixes are evaluated after human and minion review feedback, but the build-fix condition itself is not gated by `!fixDispatched`.
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
`evalMaxIterations` only applies to the minion review loop: initial minion review, minion re-review, and minion review-feedback fixes. It does not gate human-feedback fixes, build-failure fixes, or merge-conflict fixes.
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
- Routes to PR author via `_author_` routing token
|
|
39
|
-
- `review_note` = reviewer's feedback
|
|
40
|
-
- Sets `fixDispatched = true` — prevents human-feedback and conflict fixes from also firing this pass
|
|
41
|
-
- **Review-loop escalation**: after `evalMaxIterations` review→fix cycles (default 3), `_evalEscalated` is set on the PR and *only this trigger plus review/re-review* stop. Triggers B (human comments), C (build failures), and the merge-conflict fix path keep running. The dashboard PR row distinguishes the two states with separate badges (review badge `review-escalated` vs. build badge `build-escalated`).
|
|
39
|
+
### A. Human comments (`humanFeedback.pendingFix`)
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
- Gate: `pendingFix || coalescedFeedback` + `!awaitingReReview` + `!fixDispatched`
|
|
41
|
+
- Gate: `pendingFix || coalescedFeedback` + not already dispatched/on cooldown
|
|
46
42
|
- Agent comments filtered out via `/\bMinions\s*\(/i` regex on comment body
|
|
47
43
|
- Coalesces multiple comments arriving during cooldown into single fix
|
|
48
44
|
- Routes to author
|
|
49
|
-
- Not gated by `_evalEscalated` — humans can always force more fixes via PR comments even after the review loop escalates.
|
|
45
|
+
- Not gated by `_evalEscalated` — humans can always force more fixes via PR comments even after the minion review loop escalates.
|
|
46
|
+
|
|
47
|
+
### B. Review feedback (`changes-requested`)
|
|
48
|
+
|
|
49
|
+
- Gate: `reviewStatus === 'changes-requested'` + `!awaitingReReview` + `!evalEscalated` + not dispatched + not on cooldown
|
|
50
|
+
- Routes to PR author via `_author_` routing token
|
|
51
|
+
- `review_note` = reviewer's feedback
|
|
52
|
+
- Sets `fixDispatched = true` — prevents the later conflict fix from also firing this pass
|
|
53
|
+
- **Review-loop escalation**: after `evalMaxIterations` review→fix cycles (default 3), `_evalEscalated` is set on the PR and *only this trigger plus minion review/re-review* stop. Triggers A (human comments), C (build failures), and D (merge conflicts) keep running. The dashboard PR row distinguishes the two states with separate badges (review badge `review-escalated` vs. build badge `build-escalated`).
|
|
50
54
|
|
|
51
55
|
### C. Build failures (`buildStatus === 'failing'`)
|
|
52
56
|
|
|
@@ -90,7 +94,7 @@ When multiple problems coexist, earlier triggers get the first chance to enqueue
|
|
|
90
94
|
| Scenario | Guard |
|
|
91
95
|
|---|---|
|
|
92
96
|
| Simultaneous review + fix | `activePrIds` — skip PR if any dispatch in-flight |
|
|
93
|
-
| Duplicate fix (review +
|
|
97
|
+
| Duplicate fix (human/review/build + conflict) | `fixDispatched` flag — conflict triggers skip after earlier fix dispatches in the same PR pass |
|
|
94
98
|
| Branch write conflict | `isBranchActive()` mutex |
|
|
95
99
|
| Fix while awaiting re-review | `awaitingReReview` (waiting + fixedAt) |
|
|
96
100
|
| Build fix before CI runs | `_buildFixPushedAt` grace period (10min) |
|
package/engine/cli.js
CHANGED
|
@@ -21,6 +21,26 @@ function engine() {
|
|
|
21
21
|
let _dispatchModule = null;
|
|
22
22
|
function dispatchModule() { if (!_dispatchModule) _dispatchModule = require('./dispatch'); return _dispatchModule; }
|
|
23
23
|
|
|
24
|
+
function isEngineProcessAlive(control) {
|
|
25
|
+
if (!control?.pid) return false;
|
|
26
|
+
if (control.pid === process.pid) return true;
|
|
27
|
+
try {
|
|
28
|
+
if (process.platform === 'win32') {
|
|
29
|
+
const { execSync } = require('child_process');
|
|
30
|
+
const out = execSync(`tasklist /FI "PID eq ${control.pid}" /NH`, {
|
|
31
|
+
encoding: 'utf8',
|
|
32
|
+
windowsHide: true,
|
|
33
|
+
timeout: 3000,
|
|
34
|
+
});
|
|
35
|
+
return new RegExp(`\\b${control.pid}\\b`).test(out) && out.toLowerCase().includes('node');
|
|
36
|
+
}
|
|
37
|
+
process.kill(control.pid, 0);
|
|
38
|
+
return true;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
24
44
|
function handleCommand(cmd, args) {
|
|
25
45
|
if (!cmd) {
|
|
26
46
|
return commands.start();
|
|
@@ -910,16 +930,22 @@ const commands = {
|
|
|
910
930
|
},
|
|
911
931
|
|
|
912
932
|
dispatch() {
|
|
913
|
-
const e = engine();
|
|
914
|
-
console.log('Forcing dispatch cycle...');
|
|
915
933
|
const control = getControl();
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
safeWrite(CONTROL_PATH, { ...control, state: prevState });
|
|
934
|
+
if (control.state === 'running' && isEngineProcessAlive(control)) {
|
|
935
|
+
safeWrite(CONTROL_PATH, { ...control, _wakeupAt: Date.now() });
|
|
936
|
+
console.log(`Dispatch wakeup requested from running engine (PID ${control.pid}).`);
|
|
937
|
+
return;
|
|
921
938
|
}
|
|
922
|
-
|
|
939
|
+
|
|
940
|
+
const activeCount = (getDispatch().active || []).length;
|
|
941
|
+
if (activeCount > 0) {
|
|
942
|
+
console.log(`Engine is not running, but ${activeCount} dispatch(es) are active.`);
|
|
943
|
+
console.log('Refusing to run a local dispatch tick because it cannot track live agent processes.');
|
|
944
|
+
console.log('Start the engine to re-attach or recover: node engine.js start');
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
console.log('Engine is not running. Start it to dispatch work: node engine.js start');
|
|
923
949
|
},
|
|
924
950
|
|
|
925
951
|
spawn(agentId, ...promptParts) {
|
package/engine/dispatch.js
CHANGED
|
@@ -200,6 +200,36 @@ function readLiveWorkItem(meta) {
|
|
|
200
200
|
return Array.isArray(items) ? items.find(i => i.id === itemId) || null : null;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
function writeFailedAgentReport(item, reason, resultSummary, failureClass) {
|
|
204
|
+
if (!item?.id) {
|
|
205
|
+
log('warn', 'Cannot write failed agent report without dispatch id');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const agentId = item.agent || 'engine';
|
|
209
|
+
const itemId = item.meta?.item?.id || '';
|
|
210
|
+
const title = item.meta?.item?.title || item.task || item.id;
|
|
211
|
+
const metadata = {
|
|
212
|
+
dispatchId: item.id,
|
|
213
|
+
sourceItem: itemId || null,
|
|
214
|
+
result: DISPATCH_RESULT.ERROR,
|
|
215
|
+
failureClass: failureClass || null,
|
|
216
|
+
};
|
|
217
|
+
const content = [
|
|
218
|
+
`# Agent Failed: ${title}`,
|
|
219
|
+
'',
|
|
220
|
+
`**Agent:** ${agentId}`,
|
|
221
|
+
`**Dispatch:** \`${item.id}\``,
|
|
222
|
+
itemId ? `**Work Item:** \`${itemId}\`` : '',
|
|
223
|
+
`**Type:** ${item.type || 'unknown'}`,
|
|
224
|
+
`**Result:** ${DISPATCH_RESULT.ERROR}`,
|
|
225
|
+
failureClass ? `**Failure Class:** ${failureClass}` : '',
|
|
226
|
+
reason ? `**Reason:** ${reason}` : '',
|
|
227
|
+
'',
|
|
228
|
+
resultSummary ? `## Summary\n${resultSummary}` : '## Summary\n(no agent summary captured)',
|
|
229
|
+
].filter(Boolean).join('\n');
|
|
230
|
+
shared.writeToInbox(agentId, `agent-failure-${item.id}`, content, null, metadata);
|
|
231
|
+
}
|
|
232
|
+
|
|
203
233
|
// ─── Complete Dispatch ───────────────────────────────────────────────────────
|
|
204
234
|
|
|
205
235
|
function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', resultSummary = '', opts = {}) {
|
|
@@ -238,6 +268,9 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
|
|
|
238
268
|
|
|
239
269
|
if (item) {
|
|
240
270
|
log('info', `Completed dispatch: ${id} (${result}${reason ? ': ' + reason : ''})`);
|
|
271
|
+
if (result === DISPATCH_RESULT.ERROR) {
|
|
272
|
+
writeFailedAgentReport(item, reason, resultSummary, failureClass);
|
|
273
|
+
}
|
|
241
274
|
|
|
242
275
|
// Update source work item status on failure + auto-retry with backoff
|
|
243
276
|
const retryableFailure = isRetryableFailureReason(reason, failureClass);
|
package/engine/lifecycle.js
CHANGED
|
@@ -778,16 +778,22 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
778
778
|
}
|
|
779
779
|
} catch {}
|
|
780
780
|
|
|
781
|
-
// Accept inbox fallback
|
|
782
|
-
//
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
781
|
+
// Accept inbox fallback ONLY when the agent's stdout is empty (rotated/lost).
|
|
782
|
+
// The inbox note is the durable artifact for the "gh pr create ran in a sibling
|
|
783
|
+
// dispatch whose stdout was rotated" case. When stdout has actual content (even
|
|
784
|
+
// without PR evidence — e.g. the agent ran gh issue view but didn't create a PR),
|
|
785
|
+
// we must NOT pull in PR URLs from leftover inbox files of prior dispatches —
|
|
786
|
+
// those would falsely attribute unrelated PRs to this run.
|
|
787
|
+
if (!output || !String(output).trim()) {
|
|
788
|
+
const today = dateStamp();
|
|
789
|
+
const inboxFiles = getInboxFiles().filter(f => f.includes(agentId) && f.includes(today));
|
|
790
|
+
for (const f of inboxFiles) {
|
|
791
|
+
const content = safeRead(path.join(INBOX_DIR, f));
|
|
792
|
+
if (!content) continue;
|
|
793
|
+
const prHeaderPattern = /(?:^|\n)\s*\*{0,2}(?:PR|Pull\s+Request|E2E\s+PR)\s+(?:created|opened|submitted)\*{0,2}\s*[:\-]\s*([^\n]+)/gi;
|
|
794
|
+
while ((match = prHeaderPattern.exec(content)) !== null) {
|
|
795
|
+
addPrUrlEvidence(match[1]);
|
|
796
|
+
}
|
|
791
797
|
}
|
|
792
798
|
}
|
|
793
799
|
|
|
@@ -1688,6 +1694,44 @@ function parseStructuredCompletion(stdout, runtimeName) {
|
|
|
1688
1694
|
return result;
|
|
1689
1695
|
}
|
|
1690
1696
|
|
|
1697
|
+
function normalizeCompletionStatus(status) {
|
|
1698
|
+
return String(status || '').trim().toLowerCase().replace(/[\s_]+/g, '-');
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
function writeNonCleanAgentReport(dispatchItem, agentId, outcome, structuredCompletion, resultSummary, exitCode) {
|
|
1702
|
+
if (!dispatchItem?.id || !outcome) {
|
|
1703
|
+
log('warn', 'Cannot write non-clean agent report without dispatch id and outcome');
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
const itemId = dispatchItem.meta?.item?.id || '';
|
|
1707
|
+
const title = dispatchItem.meta?.item?.title || dispatchItem.task || dispatchItem.id;
|
|
1708
|
+
const metadata = {
|
|
1709
|
+
dispatchId: dispatchItem.id,
|
|
1710
|
+
sourceItem: itemId || null,
|
|
1711
|
+
result: outcome,
|
|
1712
|
+
completionStatus: structuredCompletion?.status || null,
|
|
1713
|
+
};
|
|
1714
|
+
const structuredLines = structuredCompletion
|
|
1715
|
+
? Object.entries(structuredCompletion).map(([key, value]) => `- ${key}: ${value}`).join('\n')
|
|
1716
|
+
: '- none';
|
|
1717
|
+
const content = [
|
|
1718
|
+
`# Agent ${outcome === 'partial' ? 'Partially Completed' : 'Reported Failure'}: ${title}`,
|
|
1719
|
+
'',
|
|
1720
|
+
`**Agent:** ${agentId}`,
|
|
1721
|
+
`**Dispatch:** \`${dispatchItem.id}\``,
|
|
1722
|
+
itemId ? `**Work Item:** \`${itemId}\`` : '',
|
|
1723
|
+
`**Type:** ${dispatchItem.type || 'unknown'}`,
|
|
1724
|
+
`**Exit Code:** ${exitCode}`,
|
|
1725
|
+
`**Outcome:** ${outcome}`,
|
|
1726
|
+
'',
|
|
1727
|
+
`## Structured Completion`,
|
|
1728
|
+
structuredLines,
|
|
1729
|
+
'',
|
|
1730
|
+
resultSummary ? `## Summary\n${resultSummary}` : '## Summary\n(no agent summary captured)',
|
|
1731
|
+
].filter(Boolean).join('\n');
|
|
1732
|
+
shared.writeToInbox(agentId || 'engine', `agent-${outcome}-${dispatchItem.id}`, content, null, metadata);
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1691
1735
|
/**
|
|
1692
1736
|
* Handle decomposition result — parse sub-items from agent output and create child work items.
|
|
1693
1737
|
* Called from runPostCompletionHooks when type === 'decompose'.
|
|
@@ -1827,6 +1871,12 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
|
|
|
1827
1871
|
}
|
|
1828
1872
|
const effectiveSuccess = isSuccess || autoRecovered;
|
|
1829
1873
|
|
|
1874
|
+
const completionStatus = normalizeCompletionStatus(structuredCompletion?.status);
|
|
1875
|
+
if (completionStatus.startsWith('partial') || autoRecovered || (completionStatus.startsWith('fail') && isSuccess)) {
|
|
1876
|
+
const outcome = completionStatus.startsWith('fail') ? 'failure' : 'partial';
|
|
1877
|
+
writeNonCleanAgentReport(dispatchItem, agentId, outcome, structuredCompletion, resultSummary, code);
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1830
1880
|
// Handle decomposition results — create sub-items from decompose agent output
|
|
1831
1881
|
let skipDoneStatus = false;
|
|
1832
1882
|
if (type === WORK_TYPE.DECOMPOSE && effectiveSuccess && meta?.item?.id) {
|
package/engine/shared.js
CHANGED
|
@@ -563,7 +563,7 @@ function execSilent(cmd, opts = {}) {
|
|
|
563
563
|
function execAsync(cmd, opts = {}) {
|
|
564
564
|
const { timeout, ...rest } = opts;
|
|
565
565
|
return new Promise((resolve, reject) => {
|
|
566
|
-
|
|
566
|
+
_cbExec(cmd, { windowsHide: true, encoding: 'utf8', ...rest, timeout: timeout || 30000 }, (err, stdout, stderr) => {
|
|
567
567
|
if (err) {
|
|
568
568
|
err.stderr = stderr;
|
|
569
569
|
err.stdout = stdout;
|
|
@@ -571,8 +571,6 @@ function execAsync(cmd, opts = {}) {
|
|
|
571
571
|
}
|
|
572
572
|
resolve(stdout);
|
|
573
573
|
});
|
|
574
|
-
// Safety: ensure child is killed if parent process exits
|
|
575
|
-
child.unref && child.unref();
|
|
576
574
|
});
|
|
577
575
|
}
|
|
578
576
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1646",
|
|
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"
|
package/prompts/cc-system.md
CHANGED
|
@@ -79,8 +79,6 @@ I'll dispatch dallas to fix that bug.
|
|
|
79
79
|
- `note`: `title` and `content` (or `description`) REQUIRED.
|
|
80
80
|
- `knowledge`: `title`, `content`, and `category` REQUIRED. Valid categories: architecture, conventions, project-notes, build-reports, reviews.
|
|
81
81
|
|
|
82
|
-
If you describe an action in prose ("I'll dispatch this..."), you MUST emit a matching `===ACTIONS===` block. The server detects prose claims without action blocks and surfaces a warning to the user — i.e., your false claim becomes visible. Either dispatch or don't promise to.
|
|
83
|
-
|
|
84
82
|
Core action types:
|
|
85
83
|
- **dispatch**: title (REQUIRED), workType, priority (low/medium/high), agents[] or agent (optional — both shapes accepted), project (REQUIRED when multi-project), description
|
|
86
84
|
workTypes: `explore` (research/report only, NO PR), `ask` (answer/report, NO PR), `implement` (new code, PR REQUIRED), `fix` (bug fix, PR REQUIRED), `review` (code review, NO PR), `test` (tests, PR if new), `verify` (merge/build/maintenance, NO PR)
|