@yemi33/minions 0.1.1587 → 0.1.1589
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/bin/minions.js +5 -3
- package/dashboard/js/settings.js +216 -22
- package/dashboard.js +136 -8
- package/docs/copilot-cli-schema.md +637 -0
- package/docs/copilot-output-sample-claude.jsonl +72 -0
- package/docs/copilot-output-sample-default.jsonl +26 -0
- package/docs/copilot-output-sample-gpt4o.jsonl +23 -0
- package/engine/cli.js +250 -18
- package/engine/lifecycle.js +14 -9
- package/engine/llm.js +346 -94
- package/engine/model-discovery.js +167 -0
- package/engine/preflight.js +247 -19
- package/engine/runtimes/claude.js +413 -0
- package/engine/runtimes/copilot.js +566 -0
- package/engine/runtimes/index.js +61 -0
- package/engine/shared.js +299 -63
- package/engine/spawn-agent.js +265 -181
- package/engine.js +118 -31
- package/package.json +1 -1
package/engine/shared.js
CHANGED
|
@@ -641,72 +641,35 @@ function gitEnv() {
|
|
|
641
641
|
return { ...process.env, GIT_TERMINAL_PROMPT: '0', GCM_INTERACTIVE: 'never' };
|
|
642
642
|
}
|
|
643
643
|
|
|
644
|
-
// ──
|
|
644
|
+
// ── Stream-JSON Output Parsing (runtime-aware delegator) ────────────────────
|
|
645
645
|
|
|
646
646
|
/**
|
|
647
|
-
* Parse stream-json output from
|
|
648
|
-
*
|
|
647
|
+
* Parse stream-json output from a CLI runtime. Returns { text, usage, sessionId, model }.
|
|
648
|
+
*
|
|
649
|
+
* As of P-7e3a8b1c this is a thin delegator over the runtime adapter registry —
|
|
650
|
+
* the actual parsing logic lives in `engine/runtimes/<name>.parseOutput()`.
|
|
651
|
+
* Kept on `shared` for backward compat with all existing callers (llm.js,
|
|
652
|
+
* consolidation.js, lifecycle.js, meeting.js, timeout.js).
|
|
653
|
+
*
|
|
654
|
+
* Signatures supported:
|
|
655
|
+
* parseStreamJsonOutput(raw)
|
|
656
|
+
* parseStreamJsonOutput(raw, optsObj) ← legacy form (engine/llm.js still uses this)
|
|
657
|
+
* parseStreamJsonOutput(raw, runtimeName)
|
|
658
|
+
* parseStreamJsonOutput(raw, runtimeName, optsObj)
|
|
659
|
+
*
|
|
660
|
+
* `runtimeName` defaults to `'claude'`. Unknown runtime names throw via the
|
|
661
|
+
* registry — surfaces misconfiguration immediately at the parse site.
|
|
649
662
|
*/
|
|
650
|
-
function parseStreamJsonOutput(raw,
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
function extractResult(obj) {
|
|
657
|
-
if (obj.type !== 'result') return false;
|
|
658
|
-
// Slice from the tail, not the head — review VERDICTs, structured completion
|
|
659
|
-
// blocks, PR URLs, and agent conclusions all appear at the END of the output.
|
|
660
|
-
// Head-slicing truncated VERDICTs and caused review work items to be
|
|
661
|
-
// re-dispatched up to maxRetries times despite successful completion (#1234).
|
|
662
|
-
if (obj.result) text = maxTextLength ? obj.result.slice(-maxTextLength) : obj.result;
|
|
663
|
-
if (obj.session_id) sessionId = obj.session_id;
|
|
664
|
-
if (obj.total_cost_usd || obj.usage) {
|
|
665
|
-
usage = {
|
|
666
|
-
costUsd: obj.total_cost_usd || 0,
|
|
667
|
-
inputTokens: obj.usage?.input_tokens || 0,
|
|
668
|
-
outputTokens: obj.usage?.output_tokens || 0,
|
|
669
|
-
cacheRead: obj.usage?.cache_read_input_tokens || obj.usage?.cacheReadInputTokens || 0,
|
|
670
|
-
cacheCreation: obj.usage?.cache_creation_input_tokens || obj.usage?.cacheCreationInputTokens || 0,
|
|
671
|
-
durationMs: obj.duration_ms || 0,
|
|
672
|
-
numTurns: obj.num_turns || 0,
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
return true;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
const lines = raw.split('\n');
|
|
679
|
-
// Scan forward for model from init message (appears early in output)
|
|
680
|
-
for (let i = 0; i < Math.min(lines.length, 10); i++) {
|
|
681
|
-
const line = lines[i].trim();
|
|
682
|
-
if (!line || !line.startsWith('{')) continue;
|
|
683
|
-
try {
|
|
684
|
-
const obj = JSON.parse(line);
|
|
685
|
-
if (obj.type === 'system' && obj.subtype === 'init' && obj.model) { model = obj.model; break; }
|
|
686
|
-
} catch {}
|
|
687
|
-
}
|
|
688
|
-
// Scan backward for result (appears at end of output)
|
|
689
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
690
|
-
const line = lines[i].trim();
|
|
691
|
-
if (!line) continue;
|
|
692
|
-
// Handle JSON array format (--output-format json)
|
|
693
|
-
if (line.startsWith('[')) {
|
|
694
|
-
try {
|
|
695
|
-
const arr = JSON.parse(line);
|
|
696
|
-
for (let j = arr.length - 1; j >= 0; j--) {
|
|
697
|
-
if (extractResult(arr[j])) break;
|
|
698
|
-
}
|
|
699
|
-
if (text || usage) break;
|
|
700
|
-
} catch {}
|
|
701
|
-
}
|
|
702
|
-
// Handle newline-delimited format (--output-format stream-json)
|
|
703
|
-
if (line.startsWith('{')) {
|
|
704
|
-
try {
|
|
705
|
-
if (extractResult(JSON.parse(line))) break;
|
|
706
|
-
} catch {}
|
|
707
|
-
}
|
|
663
|
+
function parseStreamJsonOutput(raw, runtimeName, opts) {
|
|
664
|
+
// Backward-compat: callers passing `(raw, optsObject)` — second arg is opts, not name
|
|
665
|
+
if (runtimeName != null && typeof runtimeName === 'object') {
|
|
666
|
+
opts = runtimeName;
|
|
667
|
+
runtimeName = undefined;
|
|
708
668
|
}
|
|
709
|
-
|
|
669
|
+
// Lazy require to avoid a circular dep at module init (runtimes/claude.js
|
|
670
|
+
// doesn't import shared, but downstream adapters might).
|
|
671
|
+
const { resolveRuntime } = require('./runtimes');
|
|
672
|
+
return resolveRuntime(runtimeName).parseOutput(raw, opts || {});
|
|
710
673
|
}
|
|
711
674
|
|
|
712
675
|
// ── Knowledge Base ──────────────────────────────────────────────────────────
|
|
@@ -776,8 +739,25 @@ const ENGINE_DEFAULTS = {
|
|
|
776
739
|
prMergeMethod: 'squash', // merge method: squash, merge, rebase
|
|
777
740
|
ignoredCommentAuthors: [], // comments from these authors are auto-closed and never trigger fixes
|
|
778
741
|
agentBusyReassignMs: 600000, // 10min — reassign work item to another agent if preferred agent is busy beyond this threshold
|
|
779
|
-
ccModel: 'sonnet', // model for Command Center and doc-chat (sonnet, haiku, opus)
|
|
780
742
|
ccEffort: null, // effort level for CC/doc-chat (null, 'low', 'medium', 'high')
|
|
743
|
+
|
|
744
|
+
// ── Runtime fleet (P-3b8e5f1d) ──────────────────────────────────────────────
|
|
745
|
+
// Single source of truth for which CLI runtime + model every spawn uses.
|
|
746
|
+
// Engine code MUST go through the resolveAgent*/resolveCc* helpers below;
|
|
747
|
+
// never read these fields directly. New runtimes are added by registering
|
|
748
|
+
// an adapter in engine/runtimes/index.js — these defaults stay stable.
|
|
749
|
+
defaultCli: 'claude', // fleet-wide CLI runtime (must be a key in engine/runtimes/index.js)
|
|
750
|
+
defaultModel: undefined, // fleet-wide model; undefined = let the runtime adapter pick its own default
|
|
751
|
+
ccCli: undefined, // CC/doc-chat CLI override; undefined = inherit defaultCli (independent of agent path)
|
|
752
|
+
ccModel: undefined, // CC/doc-chat model override; undefined = inherit defaultModel
|
|
753
|
+
claudeBareMode: false, // Claude --bare: suppress CLAUDE.md auto-discovery (per-agent override: agents.<id>.bareMode)
|
|
754
|
+
claudeFallbackModel: undefined,// Claude --fallback-model on rate-limit / overload (Claude-only)
|
|
755
|
+
copilotDisableBuiltinMcps: true, // Copilot --disable-builtin-mcps: keep github-mcp-server out so it can't bypass pull-requests.json tracking
|
|
756
|
+
copilotSuppressAgentsMd: true, // Copilot --no-custom-instructions: stop AGENTS.md auto-load from fighting Minions playbook prompts
|
|
757
|
+
copilotStreamMode: 'on', // Copilot --stream <on|off>: 'on' streams assistant.message_delta events live; 'off' batches them
|
|
758
|
+
copilotReasoningSummaries: false, // Copilot --enable-reasoning-summaries (Anthropic-family models only)
|
|
759
|
+
maxBudgetUsd: undefined, // fleet USD ceiling for --max-budget-usd (per-agent override: agents.<id>.maxBudgetUsd). Honors 0 via ?? so a literal cap of $0 works
|
|
760
|
+
disableModelDiscovery: false, // skip runtime.listModels() REST calls fleet-wide (settings UI falls back to free-text)
|
|
781
761
|
heartbeatTimeouts: {}, // populated after WORK_TYPE is defined (below)
|
|
782
762
|
maxPendingContexts: 20, // cap pendingContexts arrays in cooldowns.json to prevent unbounded growth
|
|
783
763
|
maxPendingContextEntryBytes: 256 * 1024, // 256 KB — cap each pendingContexts entry to prevent huge PR comments from bloating cooldowns.json
|
|
@@ -797,6 +777,10 @@ const ENGINE_DEFAULTS = {
|
|
|
797
777
|
maxMeetingHumanNotesBytes: 2 * 1024, // cap human note bullet lists injected into meeting prompts
|
|
798
778
|
maxPipelineMeetingContextBytes: 16 * 1024, // cap aggregated meeting/dependency context for pipeline plan generation
|
|
799
779
|
notesArchiveMaxFiles: 2000, // keep notes/archive bounded during periodic cleanup
|
|
780
|
+
// Backward-compat: keep `engine.claude.*` field family deprecation tracker. Listed here so preflight
|
|
781
|
+
// knows which subkeys to flag as deprecated. Do not consume `claude.*` in new code — use the runtime
|
|
782
|
+
// adapter system (engine/runtimes/) and the resolveAgent*/resolveCc* helpers instead.
|
|
783
|
+
_deprecatedConfigClaudeFields: ['binary', 'outputFormat', 'allowedTools', 'maxTurns', 'effort', 'budgetCap'],
|
|
800
784
|
// Teams integration — config.teams shape: { enabled, appId, appPassword, certPath, privateKeyPath, tenantId, notifyEvents, ccMirror, inboxPollInterval }
|
|
801
785
|
// Auth modes: (1) appId + appPassword (client secret), or (2) appId + certPath + privateKeyPath + tenantId (certificate)
|
|
802
786
|
teams: {
|
|
@@ -812,6 +796,254 @@ const ENGINE_DEFAULTS = {
|
|
|
812
796
|
},
|
|
813
797
|
};
|
|
814
798
|
|
|
799
|
+
// ─── Runtime Fleet Resolution (P-3b8e5f1d) ──────────────────────────────────
|
|
800
|
+
//
|
|
801
|
+
// Six helpers that are the single source of truth for "which CLI runtime + model
|
|
802
|
+
// + budget + bare-mode applies to this spawn?". Engine code MUST go through
|
|
803
|
+
// these — never read `agent.cli`, `engine.defaultCli`, etc. directly. Future
|
|
804
|
+
// agents adding new resolution rules should extend these helpers, not bypass
|
|
805
|
+
// them.
|
|
806
|
+
//
|
|
807
|
+
// Independence rule: the agent path (`resolveAgent*`) and the CC path
|
|
808
|
+
// (`resolveCc*`) do not fall through to each other. CC overrides via
|
|
809
|
+
// `engine.ccCli` / `engine.ccModel` are CC-only. Per-agent overrides via
|
|
810
|
+
// `agents.<id>.cli` / `agents.<id>.model` are agent-only. A user who wants
|
|
811
|
+
// fleet-wide change sets `engine.defaultCli` / `engine.defaultModel`.
|
|
812
|
+
//
|
|
813
|
+
// Empty strings (`''`) are treated as "unset" so the dashboard's "Default
|
|
814
|
+
// (CLI chooses)" option (which submits an empty string) clears the override
|
|
815
|
+
// instead of pinning the runtime to nothing.
|
|
816
|
+
|
|
817
|
+
function _isMeaningful(v) {
|
|
818
|
+
return v !== undefined && v !== null && v !== '';
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Resolve the CLI runtime for a per-agent spawn. Priority:
|
|
823
|
+
* 1. `agent.cli` — per-agent override
|
|
824
|
+
* 2. `engine.defaultCli` — fleet default
|
|
825
|
+
* 3. `ENGINE_DEFAULTS.defaultCli` ('claude') — hardcoded fallback
|
|
826
|
+
*
|
|
827
|
+
* Does NOT fall through to `engine.ccCli`. CC and agents are independent paths.
|
|
828
|
+
*/
|
|
829
|
+
function resolveAgentCli(agent, engine) {
|
|
830
|
+
if (agent && _isMeaningful(agent.cli)) return String(agent.cli);
|
|
831
|
+
if (engine && _isMeaningful(engine.defaultCli)) return String(engine.defaultCli);
|
|
832
|
+
return ENGINE_DEFAULTS.defaultCli;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Resolve the CLI runtime for the Command Center / doc-chat. Priority:
|
|
837
|
+
* 1. `engine.ccCli` — CC-only override
|
|
838
|
+
* 2. `engine.defaultCli` — fleet default
|
|
839
|
+
* 3. `ENGINE_DEFAULTS.defaultCli` ('claude') — hardcoded fallback
|
|
840
|
+
*
|
|
841
|
+
* Does NOT inspect any agent overrides. CC has no notion of "which agent" —
|
|
842
|
+
* it's a fleet-wide singleton.
|
|
843
|
+
*/
|
|
844
|
+
function resolveCcCli(engine) {
|
|
845
|
+
if (engine && _isMeaningful(engine.ccCli)) return String(engine.ccCli);
|
|
846
|
+
if (engine && _isMeaningful(engine.defaultCli)) return String(engine.defaultCli);
|
|
847
|
+
return ENGINE_DEFAULTS.defaultCli;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Resolve the model for a per-agent spawn. Priority:
|
|
852
|
+
* 1. `agent.model` — per-agent override
|
|
853
|
+
* 2. `engine.defaultModel` — fleet default
|
|
854
|
+
* 3. `undefined` — let the runtime adapter pick its own default
|
|
855
|
+
*
|
|
856
|
+
* Returning `undefined` is intentional: it tells the adapter to omit the
|
|
857
|
+
* `--model` flag entirely so the underlying CLI uses whatever the user has
|
|
858
|
+
* configured globally (Claude defaults to its own preferred model, Copilot
|
|
859
|
+
* to the user's `~/.copilot/settings.json` model).
|
|
860
|
+
*/
|
|
861
|
+
function resolveAgentModel(agent, engine) {
|
|
862
|
+
if (agent && _isMeaningful(agent.model)) return String(agent.model);
|
|
863
|
+
if (engine && _isMeaningful(engine.defaultModel)) return String(engine.defaultModel);
|
|
864
|
+
return undefined;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* Resolve the model for the Command Center / doc-chat. Priority:
|
|
869
|
+
* 1. `engine.ccModel` — CC-only override
|
|
870
|
+
* 2. `engine.defaultModel` — fleet default (CC inherits this when ccModel unset)
|
|
871
|
+
* 3. `undefined` — let the runtime adapter pick
|
|
872
|
+
*/
|
|
873
|
+
function resolveCcModel(engine) {
|
|
874
|
+
if (engine && _isMeaningful(engine.ccModel)) return String(engine.ccModel);
|
|
875
|
+
if (engine && _isMeaningful(engine.defaultModel)) return String(engine.defaultModel);
|
|
876
|
+
return undefined;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Resolve the per-spawn USD budget cap. Priority:
|
|
881
|
+
* 1. `agent.maxBudgetUsd` — per-agent override
|
|
882
|
+
* 2. `engine.maxBudgetUsd` — fleet default
|
|
883
|
+
* 3. `undefined` — no cap
|
|
884
|
+
*
|
|
885
|
+
* Uses nullish coalescing so a literal `0` is honored as a valid cap (for
|
|
886
|
+
* read-only / dry-run agents) instead of being treated as "no cap" — the
|
|
887
|
+
* acceptance criteria are explicit about this.
|
|
888
|
+
*/
|
|
889
|
+
function resolveAgentMaxBudget(agent, engine) {
|
|
890
|
+
const a = agent ? agent.maxBudgetUsd : undefined;
|
|
891
|
+
if (a !== undefined && a !== null) {
|
|
892
|
+
const n = typeof a === 'number' ? a : Number(a);
|
|
893
|
+
if (!Number.isNaN(n)) return n;
|
|
894
|
+
}
|
|
895
|
+
const e = engine ? engine.maxBudgetUsd : undefined;
|
|
896
|
+
if (e !== undefined && e !== null) {
|
|
897
|
+
const n = typeof e === 'number' ? e : Number(e);
|
|
898
|
+
if (!Number.isNaN(n)) return n;
|
|
899
|
+
}
|
|
900
|
+
return undefined;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Resolve whether this agent should run in Claude `--bare` mode. Priority:
|
|
905
|
+
* 1. `agent.bareMode` — per-agent override (boolean)
|
|
906
|
+
* 2. `engine.claudeBareMode` — fleet default
|
|
907
|
+
* 3. `false` — hardcoded fallback
|
|
908
|
+
*
|
|
909
|
+
* Strict undefined/null check (not falsy) so a per-agent `false` correctly
|
|
910
|
+
* overrides an engine `true`.
|
|
911
|
+
*/
|
|
912
|
+
function resolveAgentBareMode(agent, engine) {
|
|
913
|
+
const a = agent ? agent.bareMode : undefined;
|
|
914
|
+
if (a !== undefined && a !== null) return !!a;
|
|
915
|
+
const e = engine ? engine.claudeBareMode : undefined;
|
|
916
|
+
if (e !== undefined && e !== null) return !!e;
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// ─── Legacy ccModel → defaultModel Migration ─────────────────────────────────
|
|
921
|
+
//
|
|
922
|
+
// Pre-P-3b8e5f1d, `engine.ccModel` was the single fleet-wide model knob (it
|
|
923
|
+
// was also used for agent dispatch via fall-through). The new architecture
|
|
924
|
+
// makes `defaultModel` the fleet knob and demotes `ccModel` to a CC-only
|
|
925
|
+
// override. To avoid breaking installs that have only `ccModel` configured,
|
|
926
|
+
// promote `ccModel` to act as `defaultModel` in memory at startup, log a
|
|
927
|
+
// deprecation notice once, and leave the on-disk config alone (so a future
|
|
928
|
+
// admin edit can decide whether to keep `ccModel` as override or remove it).
|
|
929
|
+
|
|
930
|
+
let _legacyCcModelMigrationLogged = false;
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* If `config.engine.ccModel` is set but `config.engine.defaultModel` is unset,
|
|
934
|
+
* mutate the in-memory `config.engine` so `defaultModel` mirrors `ccModel` and
|
|
935
|
+
* log a one-time deprecation notice. Does NOT write to disk.
|
|
936
|
+
*
|
|
937
|
+
* Returns `true` when the migration was applied (useful for tests).
|
|
938
|
+
*
|
|
939
|
+
* The dedup flag is module-scoped so the warning fires once per Node process
|
|
940
|
+
* even if multiple subsystems independently call this — e.g., engine startup
|
|
941
|
+
* + a settings-reset path + a hot-reload tick.
|
|
942
|
+
*/
|
|
943
|
+
function applyLegacyCcModelMigration(config, { logger = log } = {}) {
|
|
944
|
+
if (!config || !config.engine || typeof config.engine !== 'object') return false;
|
|
945
|
+
const e = config.engine;
|
|
946
|
+
if (_isMeaningful(e.defaultModel)) return false;
|
|
947
|
+
if (!_isMeaningful(e.ccModel)) return false;
|
|
948
|
+
e.defaultModel = e.ccModel;
|
|
949
|
+
if (!_legacyCcModelMigrationLogged) {
|
|
950
|
+
_legacyCcModelMigrationLogged = true;
|
|
951
|
+
try {
|
|
952
|
+
logger('warn', 'ccModel is now a CC-specific override; set defaultModel to apply fleet-wide');
|
|
953
|
+
} catch { /* logger may not be wired during tests — best-effort */ }
|
|
954
|
+
}
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/** Test helper: reset the dedup flag so repeated tests can re-trigger the log. */
|
|
959
|
+
function _resetLegacyCcModelMigrationFlag() {
|
|
960
|
+
_legacyCcModelMigrationLogged = false;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// ─── Runtime Config Preflight Warnings ──────────────────────────────────────
|
|
964
|
+
//
|
|
965
|
+
// Emit non-fatal warnings about runtime/CLI configuration drift. Consumed by
|
|
966
|
+
// engine/preflight.js (which converts the entries to `{ name, ok: 'warn',
|
|
967
|
+
// message }` shape) and surfaced via `minions doctor`.
|
|
968
|
+
//
|
|
969
|
+
// The function is pure: takes the config and the list of registered runtime
|
|
970
|
+
// names, returns warning objects. No FS, no console writes — preflight owns
|
|
971
|
+
// presentation.
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Inspect runtime fleet config and return warning entries for misconfiguration.
|
|
975
|
+
*
|
|
976
|
+
* Warnings emitted:
|
|
977
|
+
* - Unknown CLI: any `cli` value (per-agent, ccCli, defaultCli) not in
|
|
978
|
+
* `registeredRuntimes`. Each unknown value produces one entry.
|
|
979
|
+
* - Deprecated `config.claude.*` fields: presence of any field in
|
|
980
|
+
* `ENGINE_DEFAULTS._deprecatedConfigClaudeFields` under `config.claude`.
|
|
981
|
+
* - Bare-mode misconfig: `engine.claudeBareMode === true` paired with
|
|
982
|
+
* CC running on the Claude runtime (resolved via `resolveCcCli`) and no
|
|
983
|
+
* explicit `engine.ccSystemPrompt` configured. `--bare` strips
|
|
984
|
+
* CLAUDE.md auto-discovery, so users should know CC will lose project
|
|
985
|
+
* context unless they wire an explicit system prompt.
|
|
986
|
+
*
|
|
987
|
+
* Returns: `{ id, message }[]` — `id` is a stable kebab-case identifier so
|
|
988
|
+
* tests can assert specific warnings without matching message text.
|
|
989
|
+
*/
|
|
990
|
+
function runtimeConfigWarnings(config, registeredRuntimes) {
|
|
991
|
+
const warnings = [];
|
|
992
|
+
if (!config || typeof config !== 'object') return warnings;
|
|
993
|
+
const known = new Set(Array.isArray(registeredRuntimes) ? registeredRuntimes : []);
|
|
994
|
+
const engine = config.engine || {};
|
|
995
|
+
const agents = config.agents || {};
|
|
996
|
+
|
|
997
|
+
// 1. Unknown CLI values across the fleet.
|
|
998
|
+
const seen = new Set();
|
|
999
|
+
const checkCli = (label, value) => {
|
|
1000
|
+
if (!_isMeaningful(value)) return;
|
|
1001
|
+
const key = `${label}:${value}`;
|
|
1002
|
+
if (seen.has(key)) return;
|
|
1003
|
+
seen.add(key);
|
|
1004
|
+
if (known.size > 0 && !known.has(String(value))) {
|
|
1005
|
+
const knownList = Array.from(known).sort().join(', ');
|
|
1006
|
+
warnings.push({
|
|
1007
|
+
id: 'unknown-cli',
|
|
1008
|
+
message: `Unknown CLI runtime "${value}" (${label}). Registered runtimes: ${knownList}`,
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
checkCli('engine.defaultCli', engine.defaultCli);
|
|
1013
|
+
checkCli('engine.ccCli', engine.ccCli);
|
|
1014
|
+
for (const [agentId, agent] of Object.entries(agents)) {
|
|
1015
|
+
if (agent && typeof agent === 'object') checkCli(`agents.${agentId}.cli`, agent.cli);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// 2. Deprecated `config.claude.*` fields.
|
|
1019
|
+
const claude = config.claude;
|
|
1020
|
+
if (claude && typeof claude === 'object') {
|
|
1021
|
+
const deprecatedKeys = ENGINE_DEFAULTS._deprecatedConfigClaudeFields || [];
|
|
1022
|
+
const present = deprecatedKeys.filter(k => Object.prototype.hasOwnProperty.call(claude, k));
|
|
1023
|
+
if (present.length > 0) {
|
|
1024
|
+
warnings.push({
|
|
1025
|
+
id: 'deprecated-config-claude',
|
|
1026
|
+
message: `config.claude.{${present.join(',')}} is deprecated. Use the runtime adapter (engine/runtimes/) and resolveAgent*/resolveCc* helpers instead.`,
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// 3. Bare-mode misconfig: claudeBareMode + Claude as CC runtime + no explicit
|
|
1032
|
+
// CC system prompt. `--bare` suppresses CLAUDE.md auto-discovery; CC will
|
|
1033
|
+
// lose project context unless the user wires an explicit prompt.
|
|
1034
|
+
if (engine.claudeBareMode === true) {
|
|
1035
|
+
const ccCli = resolveCcCli(engine);
|
|
1036
|
+
if (ccCli === 'claude' && !_isMeaningful(engine.ccSystemPrompt)) {
|
|
1037
|
+
warnings.push({
|
|
1038
|
+
id: 'bare-mode-misconfig',
|
|
1039
|
+
message: 'engine.claudeBareMode is true but CC runs on Claude with no engine.ccSystemPrompt — CLAUDE.md auto-discovery is suppressed and CC will lose project context.',
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return warnings;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
815
1047
|
// ─── Status & Type Constants ─────────────────────────────────────────────────
|
|
816
1048
|
|
|
817
1049
|
const WI_STATUS = {
|
|
@@ -1834,6 +2066,10 @@ module.exports = {
|
|
|
1834
2066
|
KB_CATEGORIES,
|
|
1835
2067
|
classifyInboxItem,
|
|
1836
2068
|
ENGINE_DEFAULTS,
|
|
2069
|
+
resolveAgentCli, resolveCcCli, resolveAgentModel, resolveCcModel,
|
|
2070
|
+
resolveAgentMaxBudget, resolveAgentBareMode,
|
|
2071
|
+
applyLegacyCcModelMigration, _resetLegacyCcModelMigrationFlag,
|
|
2072
|
+
runtimeConfigWarnings,
|
|
1837
2073
|
WI_STATUS, DONE_STATUSES, PLAN_TERMINAL_STATUSES, WORK_TYPE, PLAN_STATUS, PRD_ITEM_STATUS, PRD_MATERIALIZABLE, PR_STATUS, PR_POLLABLE_STATUSES, DISPATCH_RESULT, trackReviewMetric, queuePlanToPrd,
|
|
1838
2074
|
WATCH_STATUS, WATCH_TARGET_TYPE, WATCH_CONDITION, WATCH_ABSOLUTE_CONDITIONS,
|
|
1839
2075
|
PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, AGENT_STATUS,
|