@yemi33/minions 0.1.1859 → 0.1.1861
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 +10 -0
- package/engine/lifecycle.js +55 -4
- package/engine/timeout.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1861 (2026-05-10)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- surface phantom-completion as the failReason instead of "no PR detected"
|
|
7
|
+
|
|
8
|
+
## 0.1.1860 (2026-05-10)
|
|
9
|
+
|
|
10
|
+
### Fixes
|
|
11
|
+
- treat exit-0 + empty-result as phantom completion, retry instead of hard-failing PR contract (#2335)
|
|
12
|
+
|
|
3
13
|
## 0.1.1859 (2026-05-10)
|
|
4
14
|
|
|
5
15
|
### Features
|
package/engine/lifecycle.js
CHANGED
|
@@ -1098,6 +1098,21 @@ function _outputContainsPrUrl(output) {
|
|
|
1098
1098
|
return prUrlPattern.test(output);
|
|
1099
1099
|
}
|
|
1100
1100
|
|
|
1101
|
+
// Detects the phantom-completion signature in the raw runtime output: the
|
|
1102
|
+
// runtime's terminating `{"type":"result"}` event never landed. When this is
|
|
1103
|
+
// true and the PR-attachment contract is about to hard-fail for "no PR
|
|
1104
|
+
// detected," it's far more accurate to say "runtime crashed before emitting a
|
|
1105
|
+
// result event" — the agent didn't fail silently, the runtime CLI did. Used by
|
|
1106
|
+
// enforcePrAttachmentContract to pick a truthful failReason.
|
|
1107
|
+
function _outputHasRuntimeResultEvent(output) {
|
|
1108
|
+
if (!output || typeof output !== 'string') return false;
|
|
1109
|
+
// Both Claude and Copilot emit a top-level `{"type":"result"…}` JSONL line as
|
|
1110
|
+
// the conversation terminator. A literal substring match is enough; the
|
|
1111
|
+
// captured streams are JSONL so the brace pattern can't legitimately appear
|
|
1112
|
+
// mid-field-value without the prefix.
|
|
1113
|
+
return /"type":\s*"result"/.test(output);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1101
1116
|
function markMissingPrAttachment(meta, agentId, reason, resultSummary, severity) {
|
|
1102
1117
|
const noPrWiPath = resolveWorkItemPath(meta);
|
|
1103
1118
|
const isHard = severity !== 'soft';
|
|
@@ -1239,9 +1254,20 @@ async function enforcePrAttachmentContract(type, meta, agentId, config, resultSu
|
|
|
1239
1254
|
// was designed to catch) from "agent claimed a PR but engine couldn't attach
|
|
1240
1255
|
// it canonically" (soft — verification gap, not a failure).
|
|
1241
1256
|
const severity = _outputContainsPrUrl(output) ? 'soft' : 'hard';
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1257
|
+
// Hard-fail messaging: if the runtime never emitted its terminating result
|
|
1258
|
+
// event, the failure is a phantom completion (runtime CLI crashed), not the
|
|
1259
|
+
// agent silently skipping work. Surface that truthfully so operators don't
|
|
1260
|
+
// chase "the agent didn't open a PR" when the real cause is "the runtime
|
|
1261
|
+
// process died mid-conversation." Soft cases keep the original wording —
|
|
1262
|
+
// they imply the runtime DID finish and a URL is in the stream.
|
|
1263
|
+
let reason;
|
|
1264
|
+
if (severity === 'hard') {
|
|
1265
|
+
reason = _outputHasRuntimeResultEvent(output)
|
|
1266
|
+
? `${meta.item.id} completed but no PR URL was detected in the agent's output. Expected a PR — verify the agent didn't fail silently. (Branch: ${meta.branch || '(none)'}, agent: ${agentId})`
|
|
1267
|
+
: `${meta.item.id} runtime exited without emitting a terminating result event — likely a phantom completion (the runtime CLI crashed mid-conversation, not a true agent failure). No PR was created. (Branch: ${meta.branch || '(none)'}, agent: ${agentId})`;
|
|
1268
|
+
} else {
|
|
1269
|
+
reason = `${meta.item.id} completed and a PR URL was found in the agent's output, but it couldn't be canonically attached. The work likely succeeded — verify by checking the PR list. (Branch: ${meta.branch || '(none)'}, agent: ${agentId})`;
|
|
1270
|
+
}
|
|
1245
1271
|
markMissingPrAttachment(meta, agentId, reason, resultSummary, severity);
|
|
1246
1272
|
log(severity === 'hard' ? 'warn' : 'info', reason);
|
|
1247
1273
|
return { reason, itemId: meta.item.id, severity };
|
|
@@ -2472,7 +2498,7 @@ const NON_TERMINAL_COMPLETION_STATUSES = new Set([
|
|
|
2472
2498
|
'failed', 'failure', 'error',
|
|
2473
2499
|
]);
|
|
2474
2500
|
|
|
2475
|
-
function detectNonTerminalResultSummary(_resultSummary, structuredCompletion, completionReport) {
|
|
2501
|
+
function detectNonTerminalResultSummary(_resultSummary, structuredCompletion, completionReport, opts) {
|
|
2476
2502
|
const candidates = [completionReport?.status, structuredCompletion?.status];
|
|
2477
2503
|
for (const status of candidates) {
|
|
2478
2504
|
const norm = normalizeCompletionStatus(status);
|
|
@@ -2487,6 +2513,31 @@ function detectNonTerminalResultSummary(_resultSummary, structuredCompletion, co
|
|
|
2487
2513
|
};
|
|
2488
2514
|
}
|
|
2489
2515
|
}
|
|
2516
|
+
|
|
2517
|
+
// Phantom completion (opt-in via opts.detectPhantom): process exited 0 but
|
|
2518
|
+
// the runtime never emitted any signal of what it actually did — no result-
|
|
2519
|
+
// event prose, no fenced ```completion block, no on-disk completion report.
|
|
2520
|
+
// This is the signature of a runtime CLI that crashed/aborted between session
|
|
2521
|
+
// start and result emission (token expiry, MCP daemon drop, sandbox crash).
|
|
2522
|
+
//
|
|
2523
|
+
// Before PR #2266 these silently fell through the slow stale-orphan path and
|
|
2524
|
+
// were retried. After #2266 the [process-exit] sentinel always lands, so
|
|
2525
|
+
// completeFromOutput now classifies them as SUCCESS — and without this branch
|
|
2526
|
+
// the PR-attachment contract hard-fails them with no retry.
|
|
2527
|
+
//
|
|
2528
|
+
// Only enabled at call sites where there is no other success signal (e.g.
|
|
2529
|
+
// engine/timeout.js completeFromOutput). runPostCompletionHooks has richer
|
|
2530
|
+
// signals (PRs created, plan-to-prd PRD file existence, autoRecover) and
|
|
2531
|
+
// must not be demoted by this heuristic.
|
|
2532
|
+
if (opts && opts.detectPhantom) {
|
|
2533
|
+
const summaryEmpty = !_resultSummary || !String(_resultSummary).trim();
|
|
2534
|
+
if (summaryEmpty && !structuredCompletion && !completionReport) {
|
|
2535
|
+
return {
|
|
2536
|
+
phrase: 'phantom-completion',
|
|
2537
|
+
reason: 'Phantom completion: process exited cleanly but the runtime emitted no result event, no structured completion, and no completion report — likely a runtime crash before result emission',
|
|
2538
|
+
};
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2490
2541
|
return null;
|
|
2491
2542
|
}
|
|
2492
2543
|
|
package/engine/timeout.js
CHANGED
|
@@ -307,7 +307,7 @@ function checkTimeouts(config) {
|
|
|
307
307
|
outputResultSummary = parseAgentOutput(fullLogForHooks, runtimeName).resultSummary || '';
|
|
308
308
|
const gateSummary = outputResultSummary || (!fullLogForHooks.includes('"type":') ? fullLogForHooks : '');
|
|
309
309
|
completionDetection = isSuccess
|
|
310
|
-
? detectNonTerminalResultSummary(gateSummary, parseStructuredCompletion(fullLogForHooks, runtimeName), parseCompletionReportFile(item))
|
|
310
|
+
? detectNonTerminalResultSummary(gateSummary, parseStructuredCompletion(fullLogForHooks, runtimeName), parseCompletionReportFile(item), { detectPhantom: true })
|
|
311
311
|
: null;
|
|
312
312
|
} catch (e) { log('warn', 'completion summary gate: ' + e.message); }
|
|
313
313
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1861",
|
|
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"
|