@yemi33/minions 0.1.2009 → 0.1.2011
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/settings.js +4 -2
- package/dashboard.js +11 -0
- package/engine/dispatch.js +7 -0
- package/engine/lifecycle.js +11 -0
- package/engine/playbook.js +11 -1
- package/engine/runtimes/claude.js +11 -0
- package/engine/runtimes/copilot.js +9 -0
- package/engine/shared.js +3 -1
- package/engine.js +82 -2
- package/package.json +1 -1
package/dashboard/js/settings.js
CHANGED
|
@@ -249,8 +249,9 @@ async function openSettings() {
|
|
|
249
249
|
'<div style="display:flex;flex-direction:column;gap:6px;margin-top:8px">' +
|
|
250
250
|
settingsToggle('Claude bare mode', 'set-claudeBareMode', !!e.claudeBareMode, '--bare suppresses CLAUDE.md auto-discovery; pair with explicit ccSystemPrompt or context will be lost') +
|
|
251
251
|
'</div>' +
|
|
252
|
-
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:8px">' +
|
|
253
|
-
settingsField('Claude fallback model', 'set-claudeFallbackModel', e.claudeFallbackModel || '', '', '
|
|
252
|
+
'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-top:8px">' +
|
|
253
|
+
settingsField('Claude fallback model', 'set-claudeFallbackModel', e.claudeFallbackModel || '', 'e.g. sonnet', 'Passed via Claude --fallback-model. The Claude CLI swaps to this only on rate-limit (429). Used together with engine.copilotFallbackModel for the engine-level MODEL_UNAVAILABLE (overloaded/503) retry — see CLAUDE.md.') +
|
|
254
|
+
settingsField('Copilot fallback model', 'set-copilotFallbackModel', e.copilotFallbackModel || '', 'e.g. gpt-5.4', 'Copilot has no --fallback-model flag. On a MODEL_UNAVAILABLE (overloaded/503) retry, the engine OVERRIDES --model with this value (Copilot only).') +
|
|
254
255
|
settingsField('Max budget (USD)', 'set-maxBudgetUsd', e.maxBudgetUsd != null ? String(e.maxBudgetUsd) : '', '', 'Fleet ceiling for --max-budget-usd. 0 is a valid cap (read-only / dry-run). Empty = no cap. Claude only.') +
|
|
255
256
|
'</div>' +
|
|
256
257
|
'<div style="display:flex;flex-direction:column;gap:6px;margin-top:8px">' +
|
|
@@ -627,6 +628,7 @@ async function saveSettings() {
|
|
|
627
628
|
ccEffort: document.getElementById('set-ccEffort').value || null,
|
|
628
629
|
claudeBareMode: !!document.getElementById('set-claudeBareMode')?.checked,
|
|
629
630
|
claudeFallbackModel: (document.getElementById('set-claudeFallbackModel')?.value ?? '').trim(),
|
|
631
|
+
copilotFallbackModel: (document.getElementById('set-copilotFallbackModel')?.value ?? '').trim(),
|
|
630
632
|
copilotDisableBuiltinMcps: !!document.getElementById('set-copilotDisableBuiltinMcps')?.checked,
|
|
631
633
|
copilotSuppressAgentsMd: !!document.getElementById('set-copilotSuppressAgentsMd')?.checked,
|
|
632
634
|
copilotStreamMode: document.getElementById('set-copilotStreamMode')?.value || 'on',
|
package/dashboard.js
CHANGED
|
@@ -8308,6 +8308,17 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
8308
8308
|
if (_isClear(e.claudeFallbackModel)) _deleteEngineConfig('claudeFallbackModel');
|
|
8309
8309
|
else _setEngineConfig('claudeFallbackModel', String(e.claudeFallbackModel));
|
|
8310
8310
|
}
|
|
8311
|
+
// W-mpg6isvy000xca4d — Copilot fallback model. Mirrors the
|
|
8312
|
+
// claudeFallbackModel handler above. Empty / null clears; any string
|
|
8313
|
+
// value is stored verbatim. The dispatch retry path (engine.js
|
|
8314
|
+
// spawnAgent) reads engine.copilotFallbackModel when the previous
|
|
8315
|
+
// failure was FAILURE_CLASS.MODEL_UNAVAILABLE and the runtime has
|
|
8316
|
+
// capabilities.fallbackModel === false (Copilot has no --fallback-model
|
|
8317
|
+
// flag, so we override --model directly).
|
|
8318
|
+
if (e.copilotFallbackModel !== undefined) {
|
|
8319
|
+
if (_isClear(e.copilotFallbackModel)) _deleteEngineConfig('copilotFallbackModel');
|
|
8320
|
+
else _setEngineConfig('copilotFallbackModel', String(e.copilotFallbackModel));
|
|
8321
|
+
}
|
|
8311
8322
|
if (e.copilotStreamMode !== undefined) {
|
|
8312
8323
|
const valid = ['on', 'off'];
|
|
8313
8324
|
if (_isClear(e.copilotStreamMode)) _deleteEngineConfig('copilotStreamMode');
|
package/engine/dispatch.js
CHANGED
|
@@ -654,6 +654,12 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
|
|
|
654
654
|
wi.status = WI_STATUS.PENDING;
|
|
655
655
|
wi._lastRetryReason = reason || '';
|
|
656
656
|
wi._lastRetryAt = ts();
|
|
657
|
+
// W-mpg6isvy000xca4d — surface the previous failure class so the
|
|
658
|
+
// next spawn can swap in a runtime-appropriate fallback model
|
|
659
|
+
// when the previous attempt was bounced for MODEL_UNAVAILABLE
|
|
660
|
+
// (overloaded_error / 503). Empty string clears any stale
|
|
661
|
+
// value from an earlier failure cycle.
|
|
662
|
+
wi._lastFailureClass = failureClass || '';
|
|
657
663
|
delete wi.failReason;
|
|
658
664
|
delete wi.failedAt;
|
|
659
665
|
delete wi.dispatched_at;
|
|
@@ -683,6 +689,7 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
|
|
|
683
689
|
[FAILURE_CLASS.INVALID_MANAGED_SPAWN]: 'managed-spawn.json failed validation (bad schema, workdir, or allowlist — see inbox alert)',
|
|
684
690
|
[FAILURE_CLASS.MANAGED_SPAWN_HEALTHCHECK_FAILED]: 'managed-spawn spec(s) failed healthcheck within timeout (failing PIDs killed; surviving siblings stay alive)',
|
|
685
691
|
[FAILURE_CLASS.INJECTION_FLAGGED]: 'agent flagged a prompt-injection attempt in spliced untrusted content — human review of the listed sources required before re-dispatch',
|
|
692
|
+
[FAILURE_CLASS.MODEL_UNAVAILABLE]: 'requested model returned overloaded/503 — fallback model swapped in for retry',
|
|
686
693
|
[FAILURE_CLASS.UNKNOWN]: 'unknown error',
|
|
687
694
|
};
|
|
688
695
|
const classLabel = failureClass ? (CLASS_LABELS[failureClass] || failureClass) : '';
|
package/engine/lifecycle.js
CHANGED
|
@@ -1963,8 +1963,19 @@ function recordPrNoOpFixAttempt(target, cause, source, dispatchItem, branchChang
|
|
|
1963
1963
|
).slice(0, 500);
|
|
1964
1964
|
target._lastDispatchByCause = target._lastDispatchByCause
|
|
1965
1965
|
&& typeof target._lastDispatchByCause === 'object' ? target._lastDispatchByCause : {};
|
|
1966
|
+
// W-mpg6wptq0011cc68: distinguish indeterminate noops (detection could not
|
|
1967
|
+
// prove the branch advanced — fetch failed, worktree gone, missing baseline)
|
|
1968
|
+
// from confirmed noops (remote head was verified to equal beforeHead). The
|
|
1969
|
+
// same-head guard in engine.js MUST NOT permanently suppress on indeterminate
|
|
1970
|
+
// records, otherwise a single failed detection locks out future fix
|
|
1971
|
+
// dispatches on that head+comment until the SHA moves — and the SHA cannot
|
|
1972
|
+
// move unless the suppressed fix runs. The `_noOpFixes[cause].count` +
|
|
1973
|
+
// `prNoOpFixPauseAttempts` (default 3) safety valve still applies; only the
|
|
1974
|
+
// same-head guard is relaxed.
|
|
1975
|
+
const indeterminate = !!branchChange && branchChange.changed === null;
|
|
1966
1976
|
target._lastDispatchByCause[cause] = {
|
|
1967
1977
|
outcome: 'noop',
|
|
1978
|
+
indeterminate,
|
|
1968
1979
|
headSha,
|
|
1969
1980
|
reason: reasonText,
|
|
1970
1981
|
dispatchedAt: now,
|
package/engine/playbook.js
CHANGED
|
@@ -1032,7 +1032,17 @@ function selectPlaybook(workType, item) {
|
|
|
1032
1032
|
|
|
1033
1033
|
function buildPrDispatch(agentId, config, project, pr, type, extraVars, taskLabel, meta) {
|
|
1034
1034
|
const dispatchId = `${agentId || 'unassigned'}-${type}-${shared.uid()}`;
|
|
1035
|
-
|
|
1035
|
+
// W-mpg6wptq0011cc68: PR-context dispatches (review/fix/build-and-test) all
|
|
1036
|
+
// operate on the PR's branch, so default `branch_name` to `pr_branch` here
|
|
1037
|
+
// so shared-rules.md's `{{branch_name}}` reference resolves. Without this,
|
|
1038
|
+
// every PR fix/review render emits a recurring `unresolved template
|
|
1039
|
+
// variables: branch_name` warn. Callers may still override explicitly.
|
|
1040
|
+
const vars = {
|
|
1041
|
+
...buildBaseVars(agentId, config, project),
|
|
1042
|
+
branch_name: extraVars?.pr_branch || '',
|
|
1043
|
+
...extraVars,
|
|
1044
|
+
task_id: dispatchId,
|
|
1045
|
+
};
|
|
1036
1046
|
const playbookName = type === 'test' ? 'build-and-test' : (type === 'review' ? 'review' : 'fix');
|
|
1037
1047
|
const prompt = renderPlaybook(playbookName, vars);
|
|
1038
1048
|
if (!prompt) return null;
|
|
@@ -412,6 +412,7 @@ function usesSystemPromptFile({ isResume } = {}) {
|
|
|
412
412
|
function _runtimeFailureClass(code) {
|
|
413
413
|
if (code === 'auth-failure' || code === 'budget-exceeded') return FAILURE_CLASS.PERMISSION_BLOCKED;
|
|
414
414
|
if (code === 'context-limit') return FAILURE_CLASS.OUT_OF_CONTEXT;
|
|
415
|
+
if (code === 'model-unavailable') return FAILURE_CLASS.MODEL_UNAVAILABLE;
|
|
415
416
|
if (code === 'crash') return FAILURE_CLASS.SPAWN_ERROR;
|
|
416
417
|
return null;
|
|
417
418
|
}
|
|
@@ -552,6 +553,16 @@ function parseError(rawOutput) {
|
|
|
552
553
|
if (/budget.*exceed|max.budget.usd.*reach|cost.*limit.*exceed/i.test(lower)) {
|
|
553
554
|
return { message: 'Claude budget cap exceeded — check your Claude account spending limit.', code: 'budget-exceeded', retriable: false };
|
|
554
555
|
}
|
|
556
|
+
// W-mpg6isvy000xca4d — Anthropic overload / 503 / service-unavailable. Claude's
|
|
557
|
+
// own `--fallback-model` only fires on 429 (rate-limit); these failure modes
|
|
558
|
+
// hang the agent until the 5h timeout. Classify as MODEL_UNAVAILABLE so the
|
|
559
|
+
// dispatch loop retries with the runtime-appropriate fallback model. Match
|
|
560
|
+
// before the crash branch — Anthropic's overloaded responses can include
|
|
561
|
+
// "internal error" / "panic"-style phrasing that would otherwise be misread
|
|
562
|
+
// as a CLI crash.
|
|
563
|
+
if (/overloaded_error|service[_ ]unavailable|model.*(?:unavailable|overloaded)|\b503\b|temporarily unavailable/i.test(text)) {
|
|
564
|
+
return { message: 'Claude model temporarily unavailable (overloaded / 503)', code: 'model-unavailable', retriable: true };
|
|
565
|
+
}
|
|
555
566
|
if (/internal error|panic|segmentation fault|claude.*crashed|fatal: claude/i.test(lower)) {
|
|
556
567
|
return { message: 'Claude CLI crashed unexpectedly. Try again.', code: 'crash', retriable: true };
|
|
557
568
|
}
|
|
@@ -602,6 +602,7 @@ function usesSystemPromptFile() {
|
|
|
602
602
|
function _runtimeFailureClass(code) {
|
|
603
603
|
if (code === 'auth-failure' || code === 'budget-exceeded') return FAILURE_CLASS.PERMISSION_BLOCKED;
|
|
604
604
|
if (code === 'unknown-model') return FAILURE_CLASS.CONFIG_ERROR;
|
|
605
|
+
if (code === 'model-unavailable') return FAILURE_CLASS.MODEL_UNAVAILABLE;
|
|
605
606
|
if (code === 'rate-limit') return FAILURE_CLASS.NETWORK_ERROR;
|
|
606
607
|
if (code === 'crash') return FAILURE_CLASS.SPAWN_ERROR;
|
|
607
608
|
return null;
|
|
@@ -844,6 +845,14 @@ function parseError(rawOutput) {
|
|
|
844
845
|
if (hasExplicitAuthFailure || hasAuthStatusCode) {
|
|
845
846
|
return { message: text, code: 'auth-failure', retriable: false };
|
|
846
847
|
}
|
|
848
|
+
// W-mpg6isvy000xca4d — Copilot has no --fallback-model flag; classify
|
|
849
|
+
// overloaded / 503 / service_unavailable as MODEL_UNAVAILABLE so the engine
|
|
850
|
+
// retry can OVERRIDE --model with engine.copilotFallbackModel. Match before
|
|
851
|
+
// rate-limit so 503/overload never gets misread as a 429 (which would
|
|
852
|
+
// bucket into NETWORK_ERROR and re-spawn against the same broken model).
|
|
853
|
+
if (/overloaded_error|service[_ ]unavailable|model.*(?:unavailable|overloaded)|\b503\b|temporarily unavailable/i.test(text)) {
|
|
854
|
+
return { message: text, code: 'model-unavailable', retriable: true };
|
|
855
|
+
}
|
|
847
856
|
if (/rate limit|too many requests|\b429\b/i.test(text)) {
|
|
848
857
|
return { message: text, code: 'rate-limit', retriable: true };
|
|
849
858
|
}
|
package/engine/shared.js
CHANGED
|
@@ -1764,7 +1764,8 @@ const ENGINE_DEFAULTS = {
|
|
|
1764
1764
|
ccCli: undefined, // CC/doc-chat CLI override; undefined = inherit defaultCli (independent of agent path)
|
|
1765
1765
|
ccModel: undefined, // CC/doc-chat model override; undefined = inherit defaultModel
|
|
1766
1766
|
claudeBareMode: false, // Claude --bare: suppress CLAUDE.md auto-discovery (per-agent override: agents.<id>.bareMode)
|
|
1767
|
-
claudeFallbackModel: undefined,// Claude --fallback-model on rate-limit
|
|
1767
|
+
claudeFallbackModel: undefined,// Claude --fallback-model — Claude CLI honors this on rate-limit (429) only; engine retry on FAILURE_CLASS.MODEL_UNAVAILABLE keeps the flag passed so the CLI can swap internally
|
|
1768
|
+
copilotFallbackModel: undefined,// W-mpg6isvy000xca4d: Copilot has no --fallback-model flag; engine retry on FAILURE_CLASS.MODEL_UNAVAILABLE OVERRIDES --model directly with this value (separate knob from claudeFallbackModel because model namespaces differ across runtimes)
|
|
1768
1769
|
copilotDisableBuiltinMcps: true, // Copilot --disable-builtin-mcps: keep github-mcp-server out so it can't bypass pull-requests.json tracking
|
|
1769
1770
|
copilotSuppressAgentsMd: true, // Copilot --no-custom-instructions: stop AGENTS.md auto-load from fighting Minions playbook prompts
|
|
1770
1771
|
copilotStreamMode: 'on', // Copilot --stream <on|off>: 'on' streams assistant.message_delta events live; 'off' batches them
|
|
@@ -2606,6 +2607,7 @@ const FAILURE_CLASS = {
|
|
|
2606
2607
|
INVALID_MANAGED_SPAWN: 'invalid-managed-spawn', // P-7a3b1c92: agents/<id>/managed-spawn.json failed validator (bad schema, broken workdir, executable/env not on allowlist, healthcheck shape wrong). Engine refuses to spawn any spec — agent must fix file; never retryable as-is.
|
|
2607
2608
|
MANAGED_SPAWN_HEALTHCHECK_FAILED: 'managed-spawn-healthcheck-failed', // P-7a3b1c92: at least one managed-spawn spec was spawned but failed its healthcheck within timeout_s. Engine killed the failing PIDs; siblings stay alive. Dispatch ERROR with the failing spec name + log tail surfaced in the inbox alert.
|
|
2608
2609
|
INJECTION_FLAGGED: 'injection-flagged', // F5 (W-mpeklod3000we69c): the agent set `securityFlags.injectionAttempt:true` in its completion report after spotting a prompt-injection attempt inside an <UNTRUSTED-INPUT> fence. Engine writes a security inbox note + stamps `_securityFlag` on the WI and treats the dispatch as non-retryable so a human can review the source before the agent re-runs.
|
|
2610
|
+
MODEL_UNAVAILABLE: 'model-unavailable', // W-mpg6isvy000xca4d: requested model returned overloaded_error / 503 / service_unavailable. Retriable — engine swaps in the runtime-appropriate fallback model on next spawn (Claude leans on --fallback-model already plumbed; Copilot overrides --model with engine.copilotFallbackModel).
|
|
2609
2611
|
UNKNOWN: 'unknown', // Unclassified failure
|
|
2610
2612
|
};
|
|
2611
2613
|
const ESCALATION_POLICY = {
|
package/engine.js
CHANGED
|
@@ -148,6 +148,12 @@ const { renderPlaybook, validatePlaybookVars, PLAYBOOK_REQUIRED_VARS,
|
|
|
148
148
|
buildBaseVars, buildPrDispatch, resolveTaskContext,
|
|
149
149
|
getRepoHost, getRepoHostLabel, getRepoHostToolRule } = require('./engine/playbook');
|
|
150
150
|
|
|
151
|
+
// Per-slug GitHub PAT resolution — mirrors getAdoToken/MINIONS_ADO_TOKEN.
|
|
152
|
+
// Used at agent spawn time to inject GH_TOKEN for GitHub projects so child
|
|
153
|
+
// `gh`/`git push` calls authenticate as the right account without falling
|
|
154
|
+
// through to an interactive `gh auth login` device-code flow.
|
|
155
|
+
const ghToken = require('./engine/gh-token');
|
|
156
|
+
|
|
151
157
|
// sanitizeBranch imported from shared.js
|
|
152
158
|
|
|
153
159
|
// ─── Lifecycle (extracted to engine/lifecycle.js) ────────────────────────────
|
|
@@ -1681,6 +1687,27 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1681
1687
|
const resolvedMaxBudget = shared.resolveAgentMaxBudget(agentConfig, engineConfig);
|
|
1682
1688
|
const resolvedBare = shared.resolveAgentBareMode(agentConfig, engineConfig);
|
|
1683
1689
|
|
|
1690
|
+
// W-mpg6isvy000xca4d — On retry after FAILURE_CLASS.MODEL_UNAVAILABLE, swap
|
|
1691
|
+
// to the runtime-appropriate fallback model. Two paths gated on
|
|
1692
|
+
// `runtime.capabilities.fallbackModel` (no `runtime.name === ...` branches):
|
|
1693
|
+
// - Capability TRUE (Claude): the CLI's own --fallback-model handles
|
|
1694
|
+
// the swap on 429. We keep `engineConfig.claudeFallbackModel` plumbed
|
|
1695
|
+
// unconditionally via the fallbackModel opt below; no model override
|
|
1696
|
+
// needed at the engine layer.
|
|
1697
|
+
// - Capability FALSE (Copilot): no --fallback-model flag exists, so we
|
|
1698
|
+
// OVERRIDE the --model arg directly with engine.copilotFallbackModel
|
|
1699
|
+
// for this retry attempt only. The work item's _lastFailureClass is
|
|
1700
|
+
// written by dispatch.js's retry block; the next normal dispatch loop
|
|
1701
|
+
// pick-up re-reads it through meta.item.
|
|
1702
|
+
let effectiveModel = resolvedModel;
|
|
1703
|
+
const prevFailureClass = meta?.item?._lastFailureClass || null;
|
|
1704
|
+
if (prevFailureClass === FAILURE_CLASS.MODEL_UNAVAILABLE
|
|
1705
|
+
&& runtime.capabilities?.fallbackModel === false
|
|
1706
|
+
&& engineConfig.copilotFallbackModel) {
|
|
1707
|
+
effectiveModel = engineConfig.copilotFallbackModel;
|
|
1708
|
+
log('info', `MODEL_UNAVAILABLE retry: ${runtimeName} ${id} — overriding --model to ${effectiveModel}`);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1684
1711
|
const requestedEffort = engineConfig.agentEffort || null;
|
|
1685
1712
|
|
|
1686
1713
|
let cachedSessionId = null;
|
|
@@ -1698,7 +1725,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1698
1725
|
}
|
|
1699
1726
|
|
|
1700
1727
|
const args = _buildAgentSpawnFlags(runtime, {
|
|
1701
|
-
model:
|
|
1728
|
+
model: effectiveModel,
|
|
1702
1729
|
maxTurns: _maxTurnsForType(type, engineConfig),
|
|
1703
1730
|
allowedTools: claudeConfig.allowedTools,
|
|
1704
1731
|
effort: requestedEffort,
|
|
@@ -1735,6 +1762,17 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1735
1762
|
childEnv.MINIONS_KEEP_PROCESSES_SKIP_WORKDIR_CHECK = '1';
|
|
1736
1763
|
}
|
|
1737
1764
|
|
|
1765
|
+
// W-mpg54mi2000n7b7e — suppress Git's interactive credential prompts and
|
|
1766
|
+
// Git Credential Manager's GUI dialog for every agent spawn. Without these,
|
|
1767
|
+
// a child `git push` against a private repo whose cached PAT expired pops a
|
|
1768
|
+
// Windows credential window the agent can never dismiss, hanging the
|
|
1769
|
+
// dispatch until the 5h agentTimeout wall-clock kill. Mirrors
|
|
1770
|
+
// shared.gitEnv() which already sets these for the engine's own git ops.
|
|
1771
|
+
// These vars are cheap and only take effect when git is invoked, so we set
|
|
1772
|
+
// them unconditionally regardless of repo host.
|
|
1773
|
+
childEnv.GIT_TERMINAL_PROMPT = '0';
|
|
1774
|
+
childEnv.GCM_INTERACTIVE = 'never';
|
|
1775
|
+
|
|
1738
1776
|
if (getRepoHost(project) === 'ado') {
|
|
1739
1777
|
// Inject cached ADO token so ADO agents skip re-authentication (#998).
|
|
1740
1778
|
// getAdoToken() returns cached token (30-min TTL) or null — never blocks on browser auth.
|
|
@@ -1742,6 +1780,19 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
1742
1780
|
const adoToken = await getAdoToken();
|
|
1743
1781
|
if (adoToken) childEnv.MINIONS_ADO_TOKEN = adoToken;
|
|
1744
1782
|
} catch { /* non-fatal — agent can still authenticate on its own */ }
|
|
1783
|
+
} else if (getRepoHost(project) === 'github') {
|
|
1784
|
+
// W-mpg54mi2000n7b7e — inject a per-slug GitHub PAT so child `gh`/`git`
|
|
1785
|
+
// calls authenticate as the right account without any `gh auth login`
|
|
1786
|
+
// interactive flow. Resolution honors config.engine.ghAccounts via
|
|
1787
|
+
// engine/gh-token.js (exact owner → owner-glob → fleet default → null).
|
|
1788
|
+
// Mirrors the MINIONS_ADO_TOKEN injection above for ADO projects.
|
|
1789
|
+
try {
|
|
1790
|
+
const slug = shared.getProjectOrg(project) && project?.repoName
|
|
1791
|
+
? `${shared.getProjectOrg(project)}/${project.repoName}`
|
|
1792
|
+
: null;
|
|
1793
|
+
const ghTok = slug ? ghToken.resolveTokenForSlug(slug) : null;
|
|
1794
|
+
if (ghTok) childEnv.GH_TOKEN = ghTok;
|
|
1795
|
+
} catch { /* non-fatal — agent can still authenticate on its own */ }
|
|
1745
1796
|
}
|
|
1746
1797
|
|
|
1747
1798
|
// Spawn via wrapper script — node directly (no bash intermediary)
|
|
@@ -2017,7 +2068,7 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
2017
2068
|
}
|
|
2018
2069
|
|
|
2019
2070
|
const resumeArgs = _buildAgentSpawnFlags(runtime, {
|
|
2020
|
-
model:
|
|
2071
|
+
model: effectiveModel,
|
|
2021
2072
|
maxTurns: engineConfig?.maxTurns || ENGINE_DEFAULTS.maxTurns,
|
|
2022
2073
|
allowedTools: claudeConfig?.allowedTools,
|
|
2023
2074
|
sessionId: steerSessionId,
|
|
@@ -2048,12 +2099,28 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
2048
2099
|
if (completionNonce) childEnv.MINIONS_COMPLETION_NONCE = completionNonce;
|
|
2049
2100
|
childEnv.MINIONS_LIVE_OUTPUT_PATH = liveOutputPath;
|
|
2050
2101
|
childEnv.MINIONS_REPO_HOST = getRepoHost(project);
|
|
2102
|
+
// W-mpg54mi2000n7b7e — same Git non-interactive guards as the initial
|
|
2103
|
+
// spawn path. Steering-resumed agents are equally susceptible to GCM
|
|
2104
|
+
// credential dialogs on `git push` against stale PATs.
|
|
2105
|
+
childEnv.GIT_TERMINAL_PROMPT = '0';
|
|
2106
|
+
childEnv.GCM_INTERACTIVE = 'never';
|
|
2051
2107
|
if (getRepoHost(project) === 'ado') {
|
|
2052
2108
|
// Inject cached ADO token for steering session too (#998)
|
|
2053
2109
|
try {
|
|
2054
2110
|
const adoToken = await getAdoToken();
|
|
2055
2111
|
if (adoToken) childEnv.MINIONS_ADO_TOKEN = adoToken;
|
|
2056
2112
|
} catch { /* non-fatal */ }
|
|
2113
|
+
} else if (getRepoHost(project) === 'github') {
|
|
2114
|
+
// W-mpg54mi2000n7b7e — same per-slug GH_TOKEN injection as the
|
|
2115
|
+
// initial spawn path so steering-resumed GitHub agents don't fall
|
|
2116
|
+
// through to an interactive `gh auth login` device-code flow.
|
|
2117
|
+
try {
|
|
2118
|
+
const slug = shared.getProjectOrg(project) && project?.repoName
|
|
2119
|
+
? `${shared.getProjectOrg(project)}/${project.repoName}`
|
|
2120
|
+
: null;
|
|
2121
|
+
const ghTok = slug ? ghToken.resolveTokenForSlug(slug) : null;
|
|
2122
|
+
if (ghTok) childEnv.GH_TOKEN = ghTok;
|
|
2123
|
+
} catch { /* non-fatal */ }
|
|
2057
2124
|
}
|
|
2058
2125
|
// W-mp6k7ywi000fa33c — propagate keep_processes workdir-check override across steering resume.
|
|
2059
2126
|
if (dispatchItem.meta?.item?.meta?.keep_processes_skip_workdir_check
|
|
@@ -4185,6 +4252,13 @@ async function discoverFromPrs(config, project) {
|
|
|
4185
4252
|
// queued). Skip only the human-feedback dispatch path; leave
|
|
4186
4253
|
// `fixDispatched=false` so downstream causes are still evaluated.
|
|
4187
4254
|
const skipHumanFeedback = !!(lastHumanDispatch?.outcome === 'noop'
|
|
4255
|
+
// W-mpg6wptq0011cc68: indeterminate noops (detection could not verify
|
|
4256
|
+
// branch advance — fetch failed, worktree gone) must NOT permanently
|
|
4257
|
+
// suppress re-dispatch. lifecycle.js:2043-2048 explicitly comments
|
|
4258
|
+
// that a future tick with working detection must be free to re-fire.
|
|
4259
|
+
// The `_noOpFixes` count + `prNoOpFixPauseAttempts` (default 3) safety
|
|
4260
|
+
// valve still triggers pause if detection keeps failing.
|
|
4261
|
+
&& !lastHumanDispatch.indeterminate
|
|
4188
4262
|
&& lastHumanDispatch.headSha
|
|
4189
4263
|
&& currentHeadSha
|
|
4190
4264
|
&& lastHumanDispatch.headSha === currentHeadSha
|
|
@@ -4369,6 +4443,12 @@ async function discoverFromPrs(config, project) {
|
|
|
4369
4443
|
// below — symmetric to the human-feedback bug. Skip only the build-fix
|
|
4370
4444
|
// dispatch path; downstream merge-conflict resolution must still run.
|
|
4371
4445
|
const skipBuildFix = !!(lastBuildDispatch?.outcome === 'noop'
|
|
4446
|
+
// W-mpg6wptq0011cc68: symmetric protection — indeterminate noops here
|
|
4447
|
+
// (detection couldn't verify branch advance) must NOT permanently
|
|
4448
|
+
// suppress build-fix either. Same rationale as the human-feedback
|
|
4449
|
+
// guard above; the per-cause `_noOpFixes` count + pause-after-N valve
|
|
4450
|
+
// still applies.
|
|
4451
|
+
&& !lastBuildDispatch.indeterminate
|
|
4372
4452
|
&& lastBuildDispatch.headSha
|
|
4373
4453
|
&& currentHeadSha
|
|
4374
4454
|
&& lastBuildDispatch.headSha === currentHeadSha);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2011",
|
|
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"
|