@yemi33/minions 0.1.2249 → 0.1.2250
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/render-prs.js +10 -4
- package/engine/ado-git-auth.js +2 -1
- package/engine/ado.js +14 -8
- package/engine/dispatch.js +6 -2
- package/engine/github.js +19 -1
- package/engine/lifecycle.js +2 -1
- package/engine/shared.js +39 -11
- package/engine.js +1 -1
- package/package.json +1 -1
|
@@ -59,9 +59,13 @@ async function fetchPullRequestsFromDisk(projects) {
|
|
|
59
59
|
return out;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// Terminal statuses — follow-up WIs in these states are historical, not active.
|
|
63
|
+
var _WI_TERMINAL = new Set(['done', 'cancelled', 'failed', 'decomposed', 'in-pr', 'implemented', 'complete']);
|
|
64
|
+
|
|
62
65
|
function _countPrFollowups(pr) {
|
|
63
|
-
// PR follow-up chip (W-mpej3cox00099466) — counts WIs
|
|
64
|
-
// meta.pr_followup.parent_pr_url or parent_pr_id matches this PR.
|
|
66
|
+
// PR follow-up chip (W-mpej3cox00099466) — counts ACTIVE (non-terminal) WIs
|
|
67
|
+
// whose meta.pr_followup.parent_pr_url or parent_pr_id matches this PR.
|
|
68
|
+
// Terminal WIs (done/cancelled/failed) are excluded to avoid stale noise.
|
|
65
69
|
if (!pr) return 0;
|
|
66
70
|
var wis = (window._lastWorkItems) || [];
|
|
67
71
|
if (!wis.length) return 0;
|
|
@@ -69,7 +73,9 @@ function _countPrFollowups(pr) {
|
|
|
69
73
|
var prId = pr.id || '';
|
|
70
74
|
var n = 0;
|
|
71
75
|
for (var i = 0; i < wis.length; i++) {
|
|
72
|
-
var
|
|
76
|
+
var wi = wis[i];
|
|
77
|
+
if (!wi || _WI_TERMINAL.has(wi.status)) continue;
|
|
78
|
+
var f = wi.meta && wi.meta.pr_followup;
|
|
73
79
|
if (!f) continue;
|
|
74
80
|
if (prUrl && f.parent_pr_url === prUrl) { n++; continue; }
|
|
75
81
|
if (prId && f.parent_pr_id === prId) { n++; }
|
|
@@ -114,7 +120,7 @@ function prRow(pr) {
|
|
|
114
120
|
: '';
|
|
115
121
|
var followupCount = _countPrFollowups(pr);
|
|
116
122
|
var followupChip = followupCount > 0
|
|
117
|
-
? ' <span class="pr-badge draft" style="font-size:var(--text-xs)" title="' + followupCount + ' follow-up work item(s)
|
|
123
|
+
? ' <span class="pr-badge draft" style="font-size:var(--text-xs)" title="' + followupCount + ' active follow-up work item(s) in progress from comments on this PR">+' + followupCount + ' follow-up' + (followupCount === 1 ? '' : 's') + '</span>'
|
|
118
124
|
: '';
|
|
119
125
|
// Issue #2969 — paused-cause chip(s). Prefer the API-enriched _pausedCauses
|
|
120
126
|
// field; fall back to deriving from _noOpFixes for the /state/ disk-fetch
|
package/engine/ado-git-auth.js
CHANGED
|
@@ -210,7 +210,8 @@ function isAdoAuthFailure(err) {
|
|
|
210
210
|
|
|
211
211
|
function _redactBearer(s) {
|
|
212
212
|
if (typeof s !== 'string') return s;
|
|
213
|
-
|
|
213
|
+
// W-mqrb8okm — include +/= so base64-padded ADO tokens are fully redacted
|
|
214
|
+
return s.replace(/Authorization:\s*Bearer\s+[A-Za-z0-9+/=._\-]+/gi, 'Authorization: Bearer [REDACTED]');
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
function _redactErrorInPlace(err) {
|
package/engine/ado.js
CHANGED
|
@@ -1207,22 +1207,28 @@ async function forEachActivePr(config, token, callback) {
|
|
|
1207
1207
|
// 3. The repo-host filter is explicit (repoHost === 'ado') so a future
|
|
1208
1208
|
// central poll that loops over both forks doesn't accidentally treat
|
|
1209
1209
|
// github PRs as ADO records.
|
|
1210
|
-
// 4. Already-configured PRs are skipped
|
|
1211
|
-
//
|
|
1212
|
-
// project
|
|
1210
|
+
// 4. Already-configured PRs are skipped only when they are actually present
|
|
1211
|
+
// in that project's per-project file, so the project loop will really see
|
|
1212
|
+
// them. Blindly skipping all project-compatible IDs caused a permanent
|
|
1213
|
+
// orphan when the record lived in the central file (#328).
|
|
1213
1214
|
const centralPath = path.join(shared.MINIONS_DIR, 'pull-requests.json');
|
|
1214
1215
|
const centralPrs = shared.safeJsonArr(centralPath);
|
|
1215
1216
|
const configuredAdoProjects = projects.filter(p => !isGitHubProject(p) && p.adoOrg && p.adoProject);
|
|
1216
|
-
const isConfiguredAdoCanonical = (canonicalId) => {
|
|
1217
|
-
if (!canonicalId) return false;
|
|
1218
|
-
return configuredAdoProjects.some(p => shared.isPrCompatibleWithProject(p, { id: canonicalId }, ''));
|
|
1219
|
-
};
|
|
1220
1217
|
|
|
1221
1218
|
const activeCentral = centralPrs.filter(pr => {
|
|
1222
1219
|
if (!shared.PR_POLLABLE_STATUSES.has(pr.status)) return false;
|
|
1223
1220
|
if (String(pr.repoHost || 'ado').toLowerCase() !== 'ado') return false;
|
|
1224
1221
|
if (!parseCanonicalAdoPrId(pr.id, 'central ADO PR poll')) return false;
|
|
1225
|
-
|
|
1222
|
+
// Only defer to the project loop when the PR is ACTUALLY present in that
|
|
1223
|
+
// project's per-project file. Unconditionally skipping all project-compatible
|
|
1224
|
+
// PRs caused a permanent orphan: the project loop never read the central file,
|
|
1225
|
+
// so a PR stored centrally but compatible with a configured project was never
|
|
1226
|
+
// polled (fix for #328).
|
|
1227
|
+
const owningProject = configuredAdoProjects.find(p => shared.isPrCompatibleWithProject(p, { id: pr.id }, ''));
|
|
1228
|
+
if (owningProject) {
|
|
1229
|
+
const projectPrs = shared.safeJsonArr(shared.projectPrPath(owningProject));
|
|
1230
|
+
if (projectPrs.some(p => p.id === pr.id)) return false; // present in per-project file → project loop handles it
|
|
1231
|
+
}
|
|
1226
1232
|
return true;
|
|
1227
1233
|
});
|
|
1228
1234
|
|
package/engine/dispatch.js
CHANGED
|
@@ -602,6 +602,9 @@ function writeFailedAgentReport(item, reason, resultSummary, failureClass) {
|
|
|
602
602
|
log('warn', 'Cannot write failed agent report without dispatch id');
|
|
603
603
|
return;
|
|
604
604
|
}
|
|
605
|
+
// W-mqrb8okm — redact bearer tokens before writing to inbox notes
|
|
606
|
+
const safeReason = reason ? shared.redactSecrets(reason) : reason;
|
|
607
|
+
const safeSummary = resultSummary ? shared.redactSecrets(resultSummary) : resultSummary;
|
|
605
608
|
const agentId = item.agent || 'engine';
|
|
606
609
|
const itemId = item.meta?.item?.id || '';
|
|
607
610
|
const title = item.meta?.item?.title || item.task || item.id;
|
|
@@ -620,9 +623,9 @@ function writeFailedAgentReport(item, reason, resultSummary, failureClass) {
|
|
|
620
623
|
`**Type:** ${item.type || 'unknown'}`,
|
|
621
624
|
`**Result:** ${DISPATCH_RESULT.ERROR}`,
|
|
622
625
|
failureClass ? `**Failure Class:** ${failureClass}` : '',
|
|
623
|
-
|
|
626
|
+
safeReason ? `**Reason:** ${safeReason}` : '',
|
|
624
627
|
'',
|
|
625
|
-
|
|
628
|
+
safeSummary ? `## Summary\n${safeSummary}` : '## Summary\n(no agent summary captured)',
|
|
626
629
|
].filter(Boolean).join('\n');
|
|
627
630
|
shared.writeToInbox(agentId, `agent-failure-${item.id}`, content, null, metadata);
|
|
628
631
|
}
|
|
@@ -1164,4 +1167,5 @@ module.exports = {
|
|
|
1164
1167
|
isCompletedWorkItemForFailure,
|
|
1165
1168
|
_isPrdSourcedAndVetted,
|
|
1166
1169
|
NON_MUTATING_DISPATCH_TYPES,
|
|
1170
|
+
writeFailedAgentReport,
|
|
1167
1171
|
};
|
package/engine/github.js
CHANGED
|
@@ -594,7 +594,25 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
594
594
|
// Also poll manually-linked PRs from central pull-requests.json (extract slug from URL)
|
|
595
595
|
const centralPath = path.join(MINIONS_DIR, 'pull-requests.json');
|
|
596
596
|
const centralPrs = safeJsonArr(centralPath);
|
|
597
|
-
|
|
597
|
+
// Build a slug→project map for configured GitHub projects so we can detect
|
|
598
|
+
// PRs that are actually owned by the project loop (present in per-project file).
|
|
599
|
+
const configuredGhProjectsBySlug = new Map(projects.map(p => [getRepoSlug(p), p]));
|
|
600
|
+
const activeCentral = centralPrs.filter(pr => {
|
|
601
|
+
if (!PR_POLLABLE_STATUSES.has(pr.status)) return false;
|
|
602
|
+
if (!pr.url) return false;
|
|
603
|
+
// Symmetric fix for #328: only defer to the project loop when the PR is
|
|
604
|
+
// ACTUALLY present in that project's per-project file. If it's absent there,
|
|
605
|
+
// the project loop will never see it — the central branch must poll it.
|
|
606
|
+
const ghMatch = pr.url.match(/github\.com\/([^/]+\/[^/]+)\/pull\/\d+/);
|
|
607
|
+
if (ghMatch) {
|
|
608
|
+
const owningProject = configuredGhProjectsBySlug.get(ghMatch[1]);
|
|
609
|
+
if (owningProject) {
|
|
610
|
+
const projectPrs = safeJsonArr(projectPrPath(owningProject));
|
|
611
|
+
if (projectPrs.some(p => p.id === pr.id)) return false; // present → project loop handles it
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return true;
|
|
615
|
+
});
|
|
598
616
|
|
|
599
617
|
// W-mp5trwh60008386d: probe each unique slug in the central list ONCE before iterating PRs.
|
|
600
618
|
// Without this gate, central PRs would inherit the same per-PR 404 trapdoor that project-local
|
package/engine/lifecycle.js
CHANGED
|
@@ -720,7 +720,8 @@ function updateWorkItemStatus(meta, status, reason) {
|
|
|
720
720
|
delete target._noopReason;
|
|
721
721
|
}
|
|
722
722
|
} else if (status === WI_STATUS.FAILED) {
|
|
723
|
-
|
|
723
|
+
// W-mqrb8okm — redact bearer tokens from reason before persisting as failReason
|
|
724
|
+
if (reason) target.failReason = shared.redactSecrets(reason);
|
|
724
725
|
target.failedAt = ts();
|
|
725
726
|
}
|
|
726
727
|
}
|
package/engine/shared.js
CHANGED
|
@@ -403,6 +403,19 @@ function _redactString(s, options = {}) {
|
|
|
403
403
|
.replace(_JWT_RE, '[REDACTED_JWT]');
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
+
// W-mqrb8okm — Redact bearer tokens from git error.message / stdout / stderr
|
|
407
|
+
// in-place. Called from shellSafeGit / shellSafeGitSync catch paths so that ADO
|
|
408
|
+
// -c http.extraHeader=Authorization: Bearer TOKEN args embedded in the
|
|
409
|
+
// "Command failed: git ..." error message are sanitised before the error
|
|
410
|
+
// propagates to callers (who may store it as failReason / inbox notes).
|
|
411
|
+
function _redactGitError(err) {
|
|
412
|
+
if (!err || typeof err !== 'object') return err;
|
|
413
|
+
if (err.message) err.message = _redactString(err.message);
|
|
414
|
+
if (typeof err.stdout === 'string') err.stdout = _redactString(err.stdout);
|
|
415
|
+
if (typeof err.stderr === 'string') err.stderr = _redactString(err.stderr);
|
|
416
|
+
return err;
|
|
417
|
+
}
|
|
418
|
+
|
|
406
419
|
function redactSecrets(value, options = {}) {
|
|
407
420
|
if (value == null) return value;
|
|
408
421
|
if (typeof value === 'string') return _redactString(value, options);
|
|
@@ -2342,17 +2355,27 @@ function shellSafeGit(args, opts = {}) {
|
|
|
2342
2355
|
// can inject per-invocation `-c key=value` flags (e.g. ADO bearer-token
|
|
2343
2356
|
// auth header) without rewriting every shellSafeGit call site. Strip the
|
|
2344
2357
|
// key before delegating so it never reaches Node's execFile options.
|
|
2345
|
-
|
|
2358
|
+
// W-mqrb8okm — `_execFileAsync` injection point for unit tests; production
|
|
2359
|
+
// callers leave it unset so the module-level promisified execFile is used.
|
|
2360
|
+
const { timeout, gitExtraArgs, _execFileAsync: _execFileOverride, ...rest } = opts;
|
|
2346
2361
|
const finalArgs = (Array.isArray(gitExtraArgs) && gitExtraArgs.length > 0)
|
|
2347
2362
|
? [...gitExtraArgs, ...args]
|
|
2348
2363
|
: args;
|
|
2349
|
-
|
|
2364
|
+
const execFn = _execFileOverride || _execFileAsync;
|
|
2365
|
+
return execFn('git', finalArgs, {
|
|
2350
2366
|
windowsHide: true,
|
|
2351
2367
|
encoding: 'utf8',
|
|
2352
2368
|
shell: false,
|
|
2353
2369
|
...rest,
|
|
2354
2370
|
timeout: timeout || 30000,
|
|
2355
|
-
}).then(({ stdout }) => stdout)
|
|
2371
|
+
}).then(({ stdout }) => stdout).catch(err => {
|
|
2372
|
+
// W-mqrb8okm — Redact bearer tokens from git errors before they propagate.
|
|
2373
|
+
// The "Command failed: git ..." message produced by Node's execFile includes
|
|
2374
|
+
// every argv element, so -c http.extraHeader=Authorization: Bearer TOKEN
|
|
2375
|
+
// would end up verbatim in failReason / inbox notes without this guard.
|
|
2376
|
+
_redactGitError(err);
|
|
2377
|
+
throw err;
|
|
2378
|
+
});
|
|
2356
2379
|
}
|
|
2357
2380
|
|
|
2358
2381
|
// Sync argv-form helper for callers that aren't async (e.g. plan
|
|
@@ -2368,14 +2391,19 @@ function shellSafeGitSync(args, opts = {}) {
|
|
|
2368
2391
|
? [...gitExtraArgs, ...args]
|
|
2369
2392
|
: args;
|
|
2370
2393
|
const { execFileSync: _execFileSync } = require('child_process');
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2394
|
+
try {
|
|
2395
|
+
return _execFileSync('git', finalArgs, {
|
|
2396
|
+
windowsHide: true,
|
|
2397
|
+
encoding: 'utf8',
|
|
2398
|
+
shell: false,
|
|
2399
|
+
stdio: 'pipe',
|
|
2400
|
+
...rest,
|
|
2401
|
+
timeout: timeout || 30000,
|
|
2402
|
+
});
|
|
2403
|
+
} catch (err) {
|
|
2404
|
+
// W-mqrb8okm — same bearer-token redaction as the async variant.
|
|
2405
|
+
throw _redactGitError(err);
|
|
2406
|
+
}
|
|
2379
2407
|
}
|
|
2380
2408
|
|
|
2381
2409
|
/**
|
package/engine.js
CHANGED
|
@@ -8422,7 +8422,7 @@ function discoverCentralWorkItems(config) {
|
|
|
8422
8422
|
item_id: item.id,
|
|
8423
8423
|
item_name: item.title || item.id,
|
|
8424
8424
|
item_priority: item.priority || 'medium',
|
|
8425
|
-
item_description: item.description || '',
|
|
8425
|
+
item_description: shared.redactPromptDescription(item.description || ''),
|
|
8426
8426
|
work_type: workType,
|
|
8427
8427
|
additional_context: item.prompt ? `## Additional Context\n\n${item.prompt}` : '',
|
|
8428
8428
|
scope_section: buildProjectContext(dispatchProjects, assignedProject, true, agent.name, agent.role),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2250",
|
|
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"
|