@adhdev/daemon-core 0.9.82-rc.485 → 0.9.82-rc.487
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/dist/git/git-status.d.ts +23 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +343 -55
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +342 -55
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-queue-assignment.d.ts +4 -0
- package/dist/mesh/mesh-refine-gates.d.ts +29 -0
- package/dist/mesh/mesh-work-queue.d.ts +9 -0
- package/dist/repo-mesh-types.d.ts +18 -2
- package/dist/shared-types.d.ts +8 -0
- package/dist/status/snapshot.d.ts +1 -0
- package/package.json +3 -3
- package/src/commands/handler.ts +21 -1
- package/src/commands/high-family/mesh-coordinator-launch.ts +17 -1
- package/src/commands/high-family/mesh-status.ts +8 -0
- package/src/commands/med-family/cli-agent.ts +29 -0
- package/src/commands/router-refine.ts +27 -2
- package/src/git/git-status.ts +84 -29
- package/src/index.ts +1 -1
- package/src/mesh/coordinator-prompt.ts +4 -1
- package/src/mesh/mesh-queue-assignment.ts +164 -27
- package/src/mesh/mesh-refine-gates.ts +113 -7
- package/src/mesh/mesh-work-queue.ts +14 -0
- package/src/repo-mesh-types.ts +28 -3
- package/src/shared-types.ts +8 -0
- package/src/status/builders.ts +45 -2
- package/src/status/snapshot.ts +1 -1
package/dist/index.mjs
CHANGED
|
@@ -140,7 +140,8 @@ var init_repo_mesh_types = __esm({
|
|
|
140
140
|
"first_eligible",
|
|
141
141
|
"least_loaded",
|
|
142
142
|
"round_robin",
|
|
143
|
-
"priority_only"
|
|
143
|
+
"priority_only",
|
|
144
|
+
"fitness"
|
|
144
145
|
];
|
|
145
146
|
DEFAULT_MESH_SCHEDULING_STRATEGY = "first_eligible";
|
|
146
147
|
MESH_CONVERGE_REFINE_TAG = "converge=refine";
|
|
@@ -152,7 +153,11 @@ var init_repo_mesh_types = __esm({
|
|
|
152
153
|
allowAutoPublishSubmoduleMainCommits: false,
|
|
153
154
|
requireApprovalForDestructiveGit: true,
|
|
154
155
|
dirtyWorkspaceBehavior: "warn",
|
|
155
|
-
|
|
156
|
+
// Mesh-wide task cap is effectively unlimited by default: the real concurrency
|
|
157
|
+
// limits live per node / per capability slot (ORCHESTRATION_NODE_SLOTS.md), so a
|
|
158
|
+
// global ceiling is rarely meaningful. The UI hides this control; set it via the
|
|
159
|
+
// API only to impose a deliberate mesh-wide cap.
|
|
160
|
+
maxParallelTasks: 200,
|
|
156
161
|
// Coordinator-spawned worker sessions default to hidden so the dashboard is not
|
|
157
162
|
// flooded with mesh noise tabs/notifications. Users can still surface or unmute
|
|
158
163
|
// any specific session manually; that override is preserved per-device.
|
|
@@ -404,10 +409,10 @@ function readInjected(value) {
|
|
|
404
409
|
}
|
|
405
410
|
function getDaemonBuildInfo() {
|
|
406
411
|
if (cached) return cached;
|
|
407
|
-
const commit = readInjected(true ? "
|
|
408
|
-
const commitShort = readInjected(true ? "
|
|
409
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
410
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
412
|
+
const commit = readInjected(true ? "ef3ded0f5df148982ed222411ea08c6ab0fdb39b" : void 0) ?? "unknown";
|
|
413
|
+
const commitShort = readInjected(true ? "ef3ded0f" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
414
|
+
const version = readInjected(true ? "0.9.82-rc.487" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
415
|
+
const builtAt = readInjected(true ? "2026-07-10T01:53:17.254Z" : void 0);
|
|
411
416
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
412
417
|
return cached;
|
|
413
418
|
}
|
|
@@ -779,30 +784,41 @@ function isNonRuntimeRootFile(file, policy) {
|
|
|
779
784
|
}
|
|
780
785
|
return false;
|
|
781
786
|
}
|
|
787
|
+
function classifyChangedFileList(files, policy) {
|
|
788
|
+
if (files.length === 0) {
|
|
789
|
+
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
790
|
+
}
|
|
791
|
+
const pkgs = /* @__PURE__ */ new Set();
|
|
792
|
+
let sawRuntimeAmbiguousNonPackage = false;
|
|
793
|
+
for (const file of files) {
|
|
794
|
+
const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
|
|
795
|
+
if (!match) {
|
|
796
|
+
if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
pkgs.add(match[1]);
|
|
800
|
+
}
|
|
801
|
+
const affectedPackages = [...pkgs].sort();
|
|
802
|
+
const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
|
|
803
|
+
return { isDaemonAffecting: !allBenign, affectedPackages };
|
|
804
|
+
}
|
|
782
805
|
async function classifyDaemonBuildChange(repoPath, buildCommit, options, policy) {
|
|
783
806
|
try {
|
|
784
807
|
const diff = await runGit(repoPath, ["diff", "--name-only", `${buildCommit}..HEAD`], options);
|
|
785
808
|
const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
786
|
-
|
|
787
|
-
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
788
|
-
}
|
|
789
|
-
const pkgs = /* @__PURE__ */ new Set();
|
|
790
|
-
let sawRuntimeAmbiguousNonPackage = false;
|
|
791
|
-
for (const file of files) {
|
|
792
|
-
const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
|
|
793
|
-
if (!match) {
|
|
794
|
-
if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
|
|
795
|
-
continue;
|
|
796
|
-
}
|
|
797
|
-
pkgs.add(match[1]);
|
|
798
|
-
}
|
|
799
|
-
const affectedPackages = [...pkgs].sort();
|
|
800
|
-
const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
|
|
801
|
-
return { isDaemonAffecting: !allBenign, affectedPackages };
|
|
809
|
+
return classifyChangedFileList(files, policy);
|
|
802
810
|
} catch {
|
|
803
811
|
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
804
812
|
}
|
|
805
813
|
}
|
|
814
|
+
async function classifyChangedPackages(repoPath, fromRef, toRef, options = {}) {
|
|
815
|
+
const repo = await resolveGitRepository(repoPath, options);
|
|
816
|
+
const { config } = resolveChangeImpactConfigForRepo(repo.repoRoot, options);
|
|
817
|
+
const policy = resolveChangeImpactPolicy(config);
|
|
818
|
+
const diff = await runGit(repoPath, ["diff", "--name-only", `${fromRef}..${toRef}`], options);
|
|
819
|
+
const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
820
|
+
return classifyChangedFileList(files, policy);
|
|
821
|
+
}
|
|
806
822
|
function resolveChangeImpactConfigForRepo(repoRoot, options) {
|
|
807
823
|
if (options.changeImpactConfig === null) {
|
|
808
824
|
return { config: null, sourceKey: "forced-default" };
|
|
@@ -2792,6 +2808,74 @@ function normalizeDifficultyBrainMap(raw) {
|
|
|
2792
2808
|
}
|
|
2793
2809
|
return out;
|
|
2794
2810
|
}
|
|
2811
|
+
function normalizeNodeCapabilitySlot(raw) {
|
|
2812
|
+
const r = raw && typeof raw === "object" ? raw : {};
|
|
2813
|
+
const provider = typeof r.provider === "string" ? r.provider.trim() : "";
|
|
2814
|
+
if (!provider) return null;
|
|
2815
|
+
const model = typeof r.model === "string" ? r.model.trim() : "";
|
|
2816
|
+
const thinkingLevel = typeof r.thinkingLevel === "string" ? r.thinkingLevel.trim() : "";
|
|
2817
|
+
const difficulty = Array.isArray(r.difficulty) ? r.difficulty.filter(isMeshTaskDifficulty) : [];
|
|
2818
|
+
const capability = Array.isArray(r.capability) ? r.capability.filter((t) => typeof t === "string" && !!t.trim()).map((t) => t.trim()) : [];
|
|
2819
|
+
const maxParallelNum = Number(r.maxParallel);
|
|
2820
|
+
const maxParallel = Number.isFinite(maxParallelNum) && maxParallelNum > 0 ? Math.floor(maxParallelNum) : void 0;
|
|
2821
|
+
return {
|
|
2822
|
+
provider,
|
|
2823
|
+
...model ? { model } : {},
|
|
2824
|
+
...thinkingLevel ? { thinkingLevel } : {},
|
|
2825
|
+
...difficulty.length ? { difficulty } : {},
|
|
2826
|
+
...capability.length ? { capability } : {},
|
|
2827
|
+
...maxParallel !== void 0 ? { maxParallel } : {}
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
function normalizeNodeCapabilitySlots(raw) {
|
|
2831
|
+
if (!Array.isArray(raw)) return [];
|
|
2832
|
+
const out = [];
|
|
2833
|
+
for (const entry of raw) {
|
|
2834
|
+
const slot = normalizeNodeCapabilitySlot(entry);
|
|
2835
|
+
if (slot) out.push(slot);
|
|
2836
|
+
}
|
|
2837
|
+
return out;
|
|
2838
|
+
}
|
|
2839
|
+
function deriveSlotsFromLegacy(input) {
|
|
2840
|
+
const priority = Array.isArray(input.providerPriority) ? input.providerPriority.filter((p) => typeof p === "string" && !!p.trim()).map((p) => p.trim()) : [];
|
|
2841
|
+
if (priority.length === 0) return [];
|
|
2842
|
+
const roleCap = /* @__PURE__ */ new Map();
|
|
2843
|
+
for (const role of input.providerRoles || []) {
|
|
2844
|
+
if (role && typeof role.providerType === "string" && Number.isFinite(role.maxParallel)) {
|
|
2845
|
+
roleCap.set(role.providerType.trim(), Math.floor(Number(role.maxParallel)));
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
const brains = input.difficultyBrains || {};
|
|
2849
|
+
const byProvider = /* @__PURE__ */ new Map();
|
|
2850
|
+
const shared = [];
|
|
2851
|
+
for (const diff of MESH_TASK_DIFFICULTIES) {
|
|
2852
|
+
const b = brains[diff];
|
|
2853
|
+
if (!b) continue;
|
|
2854
|
+
const entry = { difficulty: diff, model: b.model, thinkingLevel: b.thinkingLevel };
|
|
2855
|
+
if (b.provider) {
|
|
2856
|
+
const list = byProvider.get(b.provider) ?? [];
|
|
2857
|
+
list.push(entry);
|
|
2858
|
+
byProvider.set(b.provider, list);
|
|
2859
|
+
} else {
|
|
2860
|
+
shared.push(entry);
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
return priority.map((provider) => {
|
|
2864
|
+
const specific = byProvider.get(provider) || [];
|
|
2865
|
+
const applied = specific.length ? specific : shared;
|
|
2866
|
+
const difficulty = applied.map((a) => a.difficulty);
|
|
2867
|
+
const model = applied.find((a) => a.model)?.model;
|
|
2868
|
+
const thinkingLevel = applied.find((a) => a.thinkingLevel)?.thinkingLevel;
|
|
2869
|
+
const maxParallel = roleCap.get(provider);
|
|
2870
|
+
return {
|
|
2871
|
+
provider,
|
|
2872
|
+
...model ? { model } : {},
|
|
2873
|
+
...thinkingLevel ? { thinkingLevel } : {},
|
|
2874
|
+
...difficulty.length ? { difficulty } : {},
|
|
2875
|
+
...maxParallel !== void 0 ? { maxParallel } : {}
|
|
2876
|
+
};
|
|
2877
|
+
});
|
|
2878
|
+
}
|
|
2795
2879
|
var DAEMON_ID_PREFIXES, MAGI_RAW_ANSWER_CAP, MESH_TASK_DIFFICULTIES, DEFAULT_DIFFICULTY_BRAINS, CANONICAL_MESH_TOOL_NAMES, CANONICAL_MESH_TOOL_COUNT;
|
|
2796
2880
|
var init_dist = __esm({
|
|
2797
2881
|
"../mesh-shared/dist/index.mjs"() {
|
|
@@ -2847,7 +2931,9 @@ var init_dist = __esm({
|
|
|
2847
2931
|
"mesh_magi_review",
|
|
2848
2932
|
"mesh_magi_collect",
|
|
2849
2933
|
"mesh_magi_kind_panel_set",
|
|
2850
|
-
"mesh_magi_kind_panel_list"
|
|
2934
|
+
"mesh_magi_kind_panel_list",
|
|
2935
|
+
"mesh_node_slots_set",
|
|
2936
|
+
"mesh_node_slots_list"
|
|
2851
2937
|
];
|
|
2852
2938
|
CANONICAL_MESH_TOOL_COUNT = CANONICAL_MESH_TOOL_NAMES.length;
|
|
2853
2939
|
}
|
|
@@ -3799,6 +3885,7 @@ function buildRulesSection(coordinatorCliType) {
|
|
|
3799
3885
|
- **Reuse idle sessions.** For follow-up, retry, commit/push, or cleanup on the same issue, send only the delta to the existing idle session. Start fresh only for independent work, provider mismatch, transcript contamination, or required worktree isolation.
|
|
3800
3886
|
- **Worktree affinity.** A worktree is a durable per-branch workspace; keep all of a branch's code_change/fix/review work on its worktree node by targeting \`required_tags: ["worktree=<branch>"]\` or \`target_node_id\`. Get the id/branch from the \`mesh_clone_node\` result or a live \`mesh_status\` \u2014 the Configured Nodes snapshot won't list a worktree cloned after launch. Untargeted same-branch follow-ups drift to the base node. Only \`convergence\` (merge/push) runs on the base, never pinned to the worktree.
|
|
3801
3887
|
- **Classify task difficulty to save tokens.** For each task you enqueue, judge its execution difficulty and pass \`difficulty\`: \`easy\` (extraction, renames, doc tweaks, trivial fixes), \`medium\` (ordinary feature/bugfix work), \`difficult\` (architecture, tricky debugging, multi-file refactors, subtle reasoning), or \`freeform\`. The mesh's per-difficulty brain preset then runs easy tasks on a cheaper model at low reasoning effort and hard tasks on a stronger model at high effort \u2014 real token savings on simple work. The current presets are shown in the "Brain presets" section below. You may still pass an explicit \`model\`/\`thinkingLevel\` to override the preset for one task.
|
|
3888
|
+
- **Retune node profiles when routing is a poor fit \u2014 but only with approval.** A node's capability slots (its provider/model/thinking + difficulty range + capability tags, seen via \`mesh_node_slots_list\`) are what task\u2192node fitness routing matches against. If you notice a persistent mismatch \u2014 e.g. every \`difficult\` task lands on a node whose only slot is a cheap model, or a capability a node clearly has isn't declared \u2014 you MAY propose a slot change with \`mesh_node_slots_set\` (write=false). That returns current-vs-proposed; present that diff to the user with a one-line reason and apply (write=true) ONLY after they approve. It is a WHOLESALE replacement of the node's slots, so include the slots you want to keep. Never rewrite a node's profile silently or without a clear routing reason.
|
|
3802
3889
|
- **Respect explicit provider requests.** Map: Hermes \u2192 \`hermes-cli\`, Claude/Claude Code \u2192 \`claude-cli\`, Codex \u2192 \`codex-cli\`, Gemini \u2192 \`gemini-cli\`, Antigravity \u2192 \`antigravity-cli\`. Never substitute the coordinator's own runtime.
|
|
3803
3890
|
- **Verify via git, not source.** Use \`mesh_git_status\` to confirm side effects. Treat agent summaries as self-reports, not verification.
|
|
3804
3891
|
- **Limit parallelism.** Start with 1\u20132 tasks; scale only on success. Never duplicate a session because \`mesh_read_chat\` shows no final message while tool/terminal activity is ongoing. This caps *concurrent* load \u2014 it does not mean serialize independent work: when a new, independent request arrives and there is headroom under \`maxParallelTasks\`, dispatch it right away rather than waiting for an in-flight task or a user nudge (read-only diagnosis especially, since it has no merge cost).
|
|
@@ -3876,7 +3963,9 @@ var init_coordinator_prompt = __esm({
|
|
|
3876
3963
|
| \`mesh_magi_review\` | Cross-verify a read-only investigation across a standing panel of independent mesh agents (different machines/providers) instead of a single worker |
|
|
3877
3964
|
| \`mesh_magi_collect\` | Collect + synthesize a previously dispatched MAGI fan-out by its consensus group id (async companion to mesh_magi_review wait:false) |
|
|
3878
3965
|
| \`mesh_magi_kind_panel_set\` | Bind a task_kind \u2192 MAGI kind-panel slots (the SOLE MAGI panel-resolution surface; machine-local, wholesale replacement \u2014 approve current-vs-new first) |
|
|
3879
|
-
| \`mesh_magi_kind_panel_list\` | List configured task_kind \u2192 MAGI kind-panel slot bindings (machine-local, read-only)
|
|
3966
|
+
| \`mesh_magi_kind_panel_list\` | List configured task_kind \u2192 MAGI kind-panel slot bindings (machine-local, read-only) |
|
|
3967
|
+
| \`mesh_node_slots_list\` | List a node's capability slots (its AI-tool profile: provider/model/thinking + difficulty range + capability tags), read-only |
|
|
3968
|
+
| \`mesh_node_slots_set\` | PROPOSE (dry-run) or APPLY a node's capability slots \u2014 how you autonomously retune a node's tool profile; WHOLESALE replacement, present current-vs-proposed and get user approval before write=true |`;
|
|
3880
3969
|
TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
|
|
3881
3970
|
|
|
3882
3971
|
Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
|
|
@@ -5824,6 +5913,7 @@ function enqueueTask(meshId, message, opts) {
|
|
|
5824
5913
|
const maxRetries = typeof opts?.maxRetries === "number" && Number.isFinite(opts.maxRetries) && opts.maxRetries >= 0 ? Math.floor(opts.maxRetries) : void 0;
|
|
5825
5914
|
let effectiveModel = typeof opts?.model === "string" && opts.model.trim() ? opts.model.trim() : void 0;
|
|
5826
5915
|
let effectiveThinkingLevel = typeof opts?.thinkingLevel === "string" && opts.thinkingLevel.trim() ? opts.thinkingLevel.trim() : void 0;
|
|
5916
|
+
const taskDifficulty = isMeshTaskDifficulty(opts?.difficulty) ? opts.difficulty : void 0;
|
|
5827
5917
|
if (isMeshTaskDifficulty(opts?.difficulty)) {
|
|
5828
5918
|
try {
|
|
5829
5919
|
const preset = getDifficultyBrains()[opts.difficulty];
|
|
@@ -5867,6 +5957,7 @@ function enqueueTask(meshId, message, opts) {
|
|
|
5867
5957
|
...typeof opts?.consensusGroupId === "string" && opts.consensusGroupId.trim() ? { consensusGroupId: opts.consensusGroupId.trim() } : {},
|
|
5868
5958
|
...effectiveModel ? { model: effectiveModel } : {},
|
|
5869
5959
|
...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {},
|
|
5960
|
+
...taskDifficulty ? { difficulty: taskDifficulty } : {},
|
|
5870
5961
|
...typeof opts?.sourceCoordinatorSessionId === "string" && opts.sourceCoordinatorSessionId.trim() ? { sourceCoordinatorSessionId: opts.sourceCoordinatorSessionId.trim() } : {},
|
|
5871
5962
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5872
5963
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -15865,8 +15956,16 @@ function nodeHasActiveAssignment(meshId, nodeId) {
|
|
|
15865
15956
|
function nodeActiveLoad(meshId, nodeId) {
|
|
15866
15957
|
return MeshRuntimeStore.getInstance().nodeActiveAssignmentCount(meshId, nodeId);
|
|
15867
15958
|
}
|
|
15959
|
+
function meshHasExplicitSlots(mesh) {
|
|
15960
|
+
const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
|
|
15961
|
+
return nodes.some((n) => normalizeNodeCapabilitySlots(n?.policy?.slots).length > 0);
|
|
15962
|
+
}
|
|
15868
15963
|
function resolveSchedulingStrategy(mesh) {
|
|
15869
|
-
|
|
15964
|
+
const raw = mesh?.policy?.schedulingStrategy;
|
|
15965
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
15966
|
+
return normalizeMeshSchedulingStrategy(raw);
|
|
15967
|
+
}
|
|
15968
|
+
return meshHasExplicitSlots(mesh) ? "fitness" : normalizeMeshSchedulingStrategy(void 0);
|
|
15870
15969
|
}
|
|
15871
15970
|
function buildSchedulingPool(localCandidates, remoteCandidates) {
|
|
15872
15971
|
const pool = [...localCandidates, ...remoteCandidates].map((c) => ({
|
|
@@ -15880,10 +15979,68 @@ function buildSchedulingPool(localCandidates, remoteCandidates) {
|
|
|
15880
15979
|
}));
|
|
15881
15980
|
return { pool, uniqueNodes };
|
|
15882
15981
|
}
|
|
15982
|
+
function resolveNodeCapabilitySlots(node) {
|
|
15983
|
+
const explicit = normalizeNodeCapabilitySlots(node?.policy?.slots);
|
|
15984
|
+
if (explicit.length) return explicit;
|
|
15985
|
+
let difficultyBrains;
|
|
15986
|
+
try {
|
|
15987
|
+
difficultyBrains = getDifficultyBrains();
|
|
15988
|
+
} catch {
|
|
15989
|
+
difficultyBrains = void 0;
|
|
15990
|
+
}
|
|
15991
|
+
return deriveSlotsFromLegacy({
|
|
15992
|
+
providerPriority: normalizeProviderPriority(node?.policy),
|
|
15993
|
+
providerRoles: Array.isArray(node?.policy?.providerRoles) ? node.policy.providerRoles : void 0,
|
|
15994
|
+
difficultyBrains
|
|
15995
|
+
});
|
|
15996
|
+
}
|
|
15997
|
+
function scoreSlotForTask(slot, task) {
|
|
15998
|
+
let score = 1;
|
|
15999
|
+
const diff = isMeshTaskDifficulty(task.difficulty) ? task.difficulty : void 0;
|
|
16000
|
+
if (diff) {
|
|
16001
|
+
if (slot.difficulty?.length) {
|
|
16002
|
+
score += slot.difficulty.includes(diff) ? 100 : 0;
|
|
16003
|
+
} else {
|
|
16004
|
+
score += 20;
|
|
16005
|
+
}
|
|
16006
|
+
}
|
|
16007
|
+
const req = task.requiredTags?.filter((t) => !!t) ?? [];
|
|
16008
|
+
if (req.length) {
|
|
16009
|
+
const cap = new Set(slot.capability ?? []);
|
|
16010
|
+
const covered = req.every((t) => cap.has(t));
|
|
16011
|
+
score += covered ? 30 : 0;
|
|
16012
|
+
}
|
|
16013
|
+
return score;
|
|
16014
|
+
}
|
|
16015
|
+
function bestSlotForTask(node, task) {
|
|
16016
|
+
const slots = resolveNodeCapabilitySlots(node);
|
|
16017
|
+
if (!slots.length) return null;
|
|
16018
|
+
let best = null;
|
|
16019
|
+
for (const slot of slots) {
|
|
16020
|
+
const score = scoreSlotForTask(slot, task);
|
|
16021
|
+
if (!best || score > best.score) best = { slot, score };
|
|
16022
|
+
}
|
|
16023
|
+
return best;
|
|
16024
|
+
}
|
|
16025
|
+
function nodeFitnessForTask(node, task) {
|
|
16026
|
+
return bestSlotForTask(node, task)?.score ?? 0;
|
|
16027
|
+
}
|
|
15883
16028
|
function orderEligibleNodes(meshId, strategy, nodes, opts) {
|
|
15884
16029
|
if (strategy === "first_eligible" || nodes.length <= 1) {
|
|
15885
16030
|
return nodes;
|
|
15886
16031
|
}
|
|
16032
|
+
if (strategy === "fitness" && opts?.task) {
|
|
16033
|
+
const task = opts.task;
|
|
16034
|
+
return [...nodes].sort((a, b) => {
|
|
16035
|
+
const fitDelta = nodeFitnessForTask(b.node, task) - nodeFitnessForTask(a.node, task);
|
|
16036
|
+
if (fitDelta !== 0) return fitDelta;
|
|
16037
|
+
const prioDelta = resolveNodeSchedulingPriority(b.node?.policy) - resolveNodeSchedulingPriority(a.node?.policy);
|
|
16038
|
+
if (prioDelta !== 0) return prioDelta;
|
|
16039
|
+
const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
|
|
16040
|
+
if (loadDelta !== 0) return loadDelta;
|
|
16041
|
+
return a.index - b.index;
|
|
16042
|
+
});
|
|
16043
|
+
}
|
|
15887
16044
|
const priorityOf = (n) => resolveNodeSchedulingPriority(n.node?.policy);
|
|
15888
16045
|
let rotation = 0;
|
|
15889
16046
|
if (strategy === "least_loaded" || strategy === "round_robin") {
|
|
@@ -16027,13 +16184,15 @@ function markAutoLaunch(meshId, taskId, args) {
|
|
|
16027
16184
|
retractActionableSkipIfPreviouslyNotified(meshId, taskId);
|
|
16028
16185
|
}
|
|
16029
16186
|
}
|
|
16030
|
-
async function resolveUsableProvider(components, nodeId, node, requiredTags) {
|
|
16031
|
-
const providerPriority = normalizeProviderPriority(node?.policy);
|
|
16032
|
-
if (!providerPriority.length) return { reason: "missing_provider_priority" };
|
|
16187
|
+
async function resolveUsableProvider(components, nodeId, node, requiredTags, task) {
|
|
16033
16188
|
const providerLoader = components.providerLoader;
|
|
16034
16189
|
if (!providerLoader) return { reason: "provider_loader_unavailable" };
|
|
16190
|
+
const slots = resolveNodeCapabilitySlots(node);
|
|
16191
|
+
if (!slots.length) return { reason: "missing_provider_priority" };
|
|
16192
|
+
const orderedSlots = task ? [...slots].sort((a, b) => scoreSlotForTask(b, task) - scoreSlotForTask(a, task)) : slots;
|
|
16035
16193
|
const failed = [];
|
|
16036
|
-
for (const
|
|
16194
|
+
for (const slot of orderedSlots) {
|
|
16195
|
+
const requestedType = slot.provider;
|
|
16037
16196
|
const normalizedType = typeof providerLoader.resolveAlias === "function" ? providerLoader.resolveAlias(requestedType) : requestedType;
|
|
16038
16197
|
if (requiredTags?.length && !nodeSatisfiesRequiredTags(requiredTags, buildMeshNodeCapabilityTags(node, normalizedType))) {
|
|
16039
16198
|
failed.push(`${requestedType}: required_tags_mismatch`);
|
|
@@ -16058,7 +16217,13 @@ async function resolveUsableProvider(components, nodeId, node, requiredTags) {
|
|
|
16058
16217
|
}], false);
|
|
16059
16218
|
}
|
|
16060
16219
|
components.onStatusChange?.();
|
|
16061
|
-
if (detected)
|
|
16220
|
+
if (detected) {
|
|
16221
|
+
return {
|
|
16222
|
+
providerType: normalizedType,
|
|
16223
|
+
...slot.model ? { model: slot.model } : {},
|
|
16224
|
+
...slot.thinkingLevel ? { thinkingLevel: slot.thinkingLevel } : {}
|
|
16225
|
+
};
|
|
16226
|
+
}
|
|
16062
16227
|
failed.push(`${requestedType}: not detected`);
|
|
16063
16228
|
}
|
|
16064
16229
|
return { reason: `provider_priority_unusable: ${failed.join("; ") || nodeId}` };
|
|
@@ -16186,7 +16351,9 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
16186
16351
|
meshId,
|
|
16187
16352
|
strategy,
|
|
16188
16353
|
candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
|
|
16189
|
-
|
|
16354
|
+
// Auto-launch drains one task at a time, so the task IS in scope here —
|
|
16355
|
+
// pass it through for the 'fitness' strategy's task→slot ranking.
|
|
16356
|
+
{ bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags } }
|
|
16190
16357
|
).map((c) => c.node);
|
|
16191
16358
|
for (const node of orderedCandidateNodes) {
|
|
16192
16359
|
const nodeId = readMeshNodeId(node);
|
|
@@ -16233,11 +16400,13 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
16233
16400
|
}
|
|
16234
16401
|
autoLaunchInProgress.add(launchKey);
|
|
16235
16402
|
try {
|
|
16236
|
-
const resolved = await resolveUsableProvider(components, nodeId, node, task.requiredTags);
|
|
16403
|
+
const resolved = await resolveUsableProvider(components, nodeId, node, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags });
|
|
16237
16404
|
if (!resolved.providerType) {
|
|
16238
16405
|
markAutoLaunch(meshId, task.id, { status: "skipped", reason: resolved.reason || "provider_unusable", nodeId });
|
|
16239
16406
|
continue;
|
|
16240
16407
|
}
|
|
16408
|
+
const effectiveModel = typeof task.model === "string" && task.model.trim() ? task.model.trim() : resolved.model;
|
|
16409
|
+
const effectiveThinkingLevel = typeof task.thinkingLevel === "string" && task.thinkingLevel.trim() ? task.thinkingLevel.trim() : resolved.thinkingLevel;
|
|
16241
16410
|
const providerCap = resolveProviderMaxParallel(node?.policy, resolved.providerType);
|
|
16242
16411
|
if (providerCap !== void 0 && activeProviderAssignedCount(meshId, nodeId, resolved.providerType) >= providerCap) {
|
|
16243
16412
|
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_provider_parallel_reached", nodeId, providerType: resolved.providerType });
|
|
@@ -16271,9 +16440,10 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
16271
16440
|
settings: remoteSettings,
|
|
16272
16441
|
// MAGI-KIND-PANEL model axis: forward the task's model override so the
|
|
16273
16442
|
// remote worker session launches with it (initialModel). Best-effort.
|
|
16274
|
-
|
|
16275
|
-
|
|
16276
|
-
|
|
16443
|
+
// Slot-aware: task override wins, else the matched slot's model.
|
|
16444
|
+
...effectiveModel ? { initialModel: effectiveModel } : {},
|
|
16445
|
+
// BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
|
|
16446
|
+
...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
|
|
16277
16447
|
});
|
|
16278
16448
|
} catch (e) {
|
|
16279
16449
|
markAutoLaunch(meshId, task.id, { status: "failed", reason: `remote_launch_dispatch_failed: ${e?.message || String(e)}`, nodeId, providerType: resolved.providerType });
|
|
@@ -16300,11 +16470,11 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
16300
16470
|
cliType: resolved.providerType,
|
|
16301
16471
|
dir: node.workspace,
|
|
16302
16472
|
settings: launchSettings,
|
|
16303
|
-
// MAGI-KIND-PANEL model axis: local launch forwards the
|
|
16304
|
-
// override as initialModel (CLI → modelLaunchArgs; ACP → setConfigOption).
|
|
16305
|
-
...
|
|
16306
|
-
// BRAIN-ROUTING thinking axis: forward the
|
|
16307
|
-
...
|
|
16473
|
+
// MAGI-KIND-PANEL model axis: local launch forwards the effective model
|
|
16474
|
+
// (task override, else matched slot) as initialModel (CLI → modelLaunchArgs; ACP → setConfigOption).
|
|
16475
|
+
...effectiveModel ? { initialModel: effectiveModel } : {},
|
|
16476
|
+
// BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
|
|
16477
|
+
...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
|
|
16308
16478
|
});
|
|
16309
16479
|
if (!launchResult?.success) {
|
|
16310
16480
|
const reason = launchResult?.error || "launch_cli_failed";
|
|
@@ -16439,7 +16609,7 @@ async function triggerMeshQueue(components, meshId) {
|
|
|
16439
16609
|
const aPrio = resolveNodeSchedulingPriority(a.node?.policy);
|
|
16440
16610
|
const bPrio = resolveNodeSchedulingPriority(b.node?.policy);
|
|
16441
16611
|
if (aPrio !== bPrio) return bPrio - aPrio;
|
|
16442
|
-
if (strategy === "least_loaded" || strategy === "round_robin") {
|
|
16612
|
+
if (strategy === "least_loaded" || strategy === "round_robin" || strategy === "fitness") {
|
|
16443
16613
|
const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
|
|
16444
16614
|
if (loadDelta !== 0) return loadDelta;
|
|
16445
16615
|
}
|
|
@@ -18433,6 +18603,22 @@ var init_provider_input_support = __esm({
|
|
|
18433
18603
|
});
|
|
18434
18604
|
|
|
18435
18605
|
// src/status/builders.ts
|
|
18606
|
+
function isCoordinatorSpawnedHiddenWorker(settings) {
|
|
18607
|
+
if (!settings) return false;
|
|
18608
|
+
return settings.launchedByCoordinator === true && typeof settings.meshNodeFor === "string" && settings.meshNodeFor.trim().length > 0 && settings.spawnedSessionVisibility === "hidden";
|
|
18609
|
+
}
|
|
18610
|
+
function resolveSurfaceHidden(settings) {
|
|
18611
|
+
if (!settings) return false;
|
|
18612
|
+
if (settings.userHidden === true) return true;
|
|
18613
|
+
if (settings.userHidden === false) return false;
|
|
18614
|
+
return settings.spawnedSessionVisibility === "hidden" || isCoordinatorSpawnedHiddenWorker(settings);
|
|
18615
|
+
}
|
|
18616
|
+
function resolveMuted(settings) {
|
|
18617
|
+
if (!settings) return false;
|
|
18618
|
+
if (settings.userMuted === true) return true;
|
|
18619
|
+
if (settings.userMuted === false) return false;
|
|
18620
|
+
return isCoordinatorSpawnedHiddenWorker(settings);
|
|
18621
|
+
}
|
|
18436
18622
|
function getActiveChatOptions(profile) {
|
|
18437
18623
|
if (profile === "full") return {};
|
|
18438
18624
|
return LIVE_STATUS_ACTIVE_CHAT_OPTIONS;
|
|
@@ -18638,7 +18824,8 @@ function buildCliSession(state, options) {
|
|
|
18638
18824
|
settings: state.settings,
|
|
18639
18825
|
...coordinator && { coordinator },
|
|
18640
18826
|
...meshQueueStats && { meshQueueStats },
|
|
18641
|
-
...state.settings
|
|
18827
|
+
...resolveSurfaceHidden(state.settings) && { surfaceHidden: true },
|
|
18828
|
+
...resolveMuted(state.settings) && { muted: true }
|
|
18642
18829
|
};
|
|
18643
18830
|
}
|
|
18644
18831
|
function buildAcpSession(state, options) {
|
|
@@ -18679,7 +18866,8 @@ function buildAcpSession(state, options) {
|
|
|
18679
18866
|
settings: state.settings,
|
|
18680
18867
|
...coordinator && { coordinator },
|
|
18681
18868
|
...meshQueueStats && { meshQueueStats },
|
|
18682
|
-
...state.settings
|
|
18869
|
+
...resolveSurfaceHidden(state.settings) && { surfaceHidden: true },
|
|
18870
|
+
...resolveMuted(state.settings) && { muted: true }
|
|
18683
18871
|
};
|
|
18684
18872
|
}
|
|
18685
18873
|
function buildSessionEntries(allStates, cdpManagers, options = {}) {
|
|
@@ -39078,11 +39266,15 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
39078
39266
|
if (!manifestPath) continue;
|
|
39079
39267
|
try {
|
|
39080
39268
|
const m = JSON.parse(fs41.readFileSync(manifestPath, "utf-8"));
|
|
39269
|
+
const modelOptions = Array.isArray(m.modelOptions) ? m.modelOptions.filter((x) => typeof x === "string" && !!x.trim()) : [];
|
|
39270
|
+
const thinkingLevelOptions = Array.isArray(m.thinkingLevelOptions) ? m.thinkingLevelOptions.filter((x) => typeof x === "string" && !!x.trim()) : [];
|
|
39081
39271
|
items.push({
|
|
39082
39272
|
type,
|
|
39083
39273
|
category,
|
|
39084
39274
|
version: typeof m.providerVersion === "string" ? m.providerVersion : "0.0.0",
|
|
39085
|
-
path: manifestPath
|
|
39275
|
+
path: manifestPath,
|
|
39276
|
+
...modelOptions.length ? { modelOptions } : {},
|
|
39277
|
+
...thinkingLevelOptions.length ? { thinkingLevelOptions } : {}
|
|
39086
39278
|
});
|
|
39087
39279
|
} catch {
|
|
39088
39280
|
}
|
|
@@ -49871,6 +50063,31 @@ var cliAgentHandlers = {
|
|
|
49871
50063
|
record_provider_pty: async (ctx, args) => {
|
|
49872
50064
|
return ctx.deps.cliManager.handleCliCommand("record_provider_pty", args);
|
|
49873
50065
|
},
|
|
50066
|
+
// Daemon-owned per-session user Mute/Hide. Replaces the old browser-local
|
|
50067
|
+
// localStorage layer: the user's manual hide/mute for a conversation is stored
|
|
50068
|
+
// in-memory on the live session's settings (userHidden / userMuted) and rides
|
|
50069
|
+
// the SAME status snapshot pipeline as the coordinator-policy surfaceHidden
|
|
50070
|
+
// flag, so every client of this daemon sees the same state. In-memory only —
|
|
50071
|
+
// resets on daemon restart (coordinator-spawned sessions re-derive their hidden
|
|
50072
|
+
// default from mesh policy on relaunch). Passing null/undefined for a field
|
|
50073
|
+
// leaves it unchanged; pass an explicit boolean to set, or false to clear an
|
|
50074
|
+
// earlier hide/mute (e.g. unmute a coordinator-spawned worker overrides the
|
|
50075
|
+
// policy default until restart).
|
|
50076
|
+
set_conversation_prefs: async (ctx, args) => {
|
|
50077
|
+
const sessionId = readStringValue(args?.sessionId, args?.targetSessionId, args?.instanceId);
|
|
50078
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
50079
|
+
const inst = ctx.deps.instanceManager.getInstance(sessionId);
|
|
50080
|
+
if (!inst || typeof inst.updateSettings !== "function") {
|
|
50081
|
+
return { success: false, error: "Session not found or does not support preferences" };
|
|
50082
|
+
}
|
|
50083
|
+
const patch = {};
|
|
50084
|
+
if (typeof args?.hidden === "boolean") patch.userHidden = args.hidden;
|
|
50085
|
+
if (typeof args?.muted === "boolean") patch.userMuted = args.muted;
|
|
50086
|
+
if (!Object.keys(patch).length) return { success: false, error: "Nothing to update (hidden and/or muted required)" };
|
|
50087
|
+
inst.updateSettings(patch);
|
|
50088
|
+
ctx.deps.onStatusChange?.();
|
|
50089
|
+
return { success: true, sessionId, ...patch };
|
|
50090
|
+
},
|
|
49874
50091
|
agent_command: async (ctx, args) => {
|
|
49875
50092
|
{
|
|
49876
50093
|
const dispatchSessionId = readStringValue(args?.targetSessionId, args?.sessionId, args?.instanceId);
|
|
@@ -56132,6 +56349,8 @@ var meshCoordinatorLaunchHandlers = {
|
|
|
56132
56349
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
56133
56350
|
let cliType = typeof args?.cliType === "string" ? args.cliType.trim() : "";
|
|
56134
56351
|
const extraSystemPrompt = typeof args?.extraSystemPrompt === "string" ? args.extraSystemPrompt.trim() : "";
|
|
56352
|
+
const initialModel = typeof args?.initialModel === "string" && args.initialModel.trim() ? args.initialModel.trim() : null;
|
|
56353
|
+
const initialThinkingLevel = typeof args?.initialThinkingLevel === "string" && args.initialThinkingLevel.trim() ? args.initialThinkingLevel.trim() : null;
|
|
56135
56354
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
56136
56355
|
try {
|
|
56137
56356
|
const { buildCoordinatorSystemPrompt: buildCoordinatorSystemPrompt2 } = await Promise.resolve().then(() => (init_coordinator_prompt(), coordinator_prompt_exports));
|
|
@@ -56402,7 +56621,9 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
56402
56621
|
dir: workspace,
|
|
56403
56622
|
cliArgs: cliCmdArgs.length > 0 ? cliCmdArgs : void 0,
|
|
56404
56623
|
env: Object.keys(cliCmdEnv).length > 0 ? cliCmdEnv : void 0,
|
|
56405
|
-
settings: { meshCoordinatorFor: meshId }
|
|
56624
|
+
settings: { meshCoordinatorFor: meshId },
|
|
56625
|
+
...initialModel ? { initialModel } : {},
|
|
56626
|
+
...initialThinkingLevel ? { initialThinkingLevel } : {}
|
|
56406
56627
|
});
|
|
56407
56628
|
if (cliCmdLaunch?.success && cliCmdContextFilePath) {
|
|
56408
56629
|
const stripPath = cliCmdContextFilePath;
|
|
@@ -56587,7 +56808,9 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
56587
56808
|
env: Object.keys(launchEnv).length > 0 ? launchEnv : void 0,
|
|
56588
56809
|
settings: {
|
|
56589
56810
|
meshCoordinatorFor: meshId
|
|
56590
|
-
}
|
|
56811
|
+
},
|
|
56812
|
+
...initialModel ? { initialModel } : {},
|
|
56813
|
+
...initialThinkingLevel ? { initialThinkingLevel } : {}
|
|
56591
56814
|
});
|
|
56592
56815
|
if (launchResult?.success && autoImportContextFilePath) {
|
|
56593
56816
|
const stripPath = autoImportContextFilePath;
|
|
@@ -56946,6 +57169,11 @@ var meshStatusHandlers = {
|
|
|
56946
57169
|
...node.reportedProviderVersions && typeof node.reportedProviderVersions === "object" ? { providerVersions: node.reportedProviderVersions } : {},
|
|
56947
57170
|
...typeof node.reportedDaemonBuildVersion === "string" && node.reportedDaemonBuildVersion ? { daemonBuildVersion: node.reportedDaemonBuildVersion } : {},
|
|
56948
57171
|
providerPriority,
|
|
57172
|
+
// ORCHESTRATION_NODE_SLOTS.md: surface the node's capability
|
|
57173
|
+
// slots so the dashboard slot editor can read them. Only
|
|
57174
|
+
// emitted when explicitly configured (derived-from-legacy
|
|
57175
|
+
// slots stay implicit — the editor shows the legacy fields).
|
|
57176
|
+
...Array.isArray(node.policy?.slots) && node.policy.slots.length ? { slots: normalizeNodeCapabilitySlots(node.policy.slots) } : {},
|
|
56949
57177
|
activeSessions: [],
|
|
56950
57178
|
activeSessionDetails: [],
|
|
56951
57179
|
launchReady: false
|
|
@@ -57587,6 +57815,7 @@ function orderMeshRefineBatchNodes(changeAreas) {
|
|
|
57587
57815
|
|
|
57588
57816
|
// src/commands/router-refine.ts
|
|
57589
57817
|
init_repo_mesh_types();
|
|
57818
|
+
init_git_status();
|
|
57590
57819
|
init_mesh_node_identity();
|
|
57591
57820
|
|
|
57592
57821
|
// src/mesh/mesh-refine-gates.ts
|
|
@@ -58527,6 +58756,41 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
58527
58756
|
if (fs31.existsSync(pathJoin2(cwd, "node_modules"))) return false;
|
|
58528
58757
|
return ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"].some((lock) => fs31.existsSync(pathJoin2(cwd, lock)));
|
|
58529
58758
|
};
|
|
58759
|
+
const needsNodeModules = (candidate, cwd) => isPackageManagerValidation(candidate) && dependenciesLikelyMissing(cwd);
|
|
58760
|
+
const isDaemonScopedCommand = (candidate) => {
|
|
58761
|
+
const haystack = [candidate.command, ...candidate.args || [], candidate.displayCommand || ""].join(" ").toLowerCase();
|
|
58762
|
+
if (candidate.category === "typecheck") return false;
|
|
58763
|
+
if (/\btypecheck\b/.test(haystack)) return false;
|
|
58764
|
+
if (/\bweb-core\b|\bweb-cloud\b|\bweb-standalone\b|\btest:web\b/.test(haystack)) return false;
|
|
58765
|
+
return /\bdaemon-core\b|\bdaemon-cloud\b|\btest:daemon\b|check-vendor-drift/.test(haystack);
|
|
58766
|
+
};
|
|
58767
|
+
const scopeUnaffectedDaemon = opts?.changeImpact?.isDaemonAffecting === false;
|
|
58768
|
+
const skippedDaemonCommands = [];
|
|
58769
|
+
const commandsToRun = [];
|
|
58770
|
+
for (const candidate of selection.commands) {
|
|
58771
|
+
if (scopeUnaffectedDaemon && isDaemonScopedCommand(candidate)) {
|
|
58772
|
+
skippedDaemonCommands.push(candidate.displayCommand);
|
|
58773
|
+
summary.commandsRun.push({
|
|
58774
|
+
command: candidate.command,
|
|
58775
|
+
args: candidate.args,
|
|
58776
|
+
displayCommand: candidate.displayCommand,
|
|
58777
|
+
category: candidate.category,
|
|
58778
|
+
source: candidate.source,
|
|
58779
|
+
passed: true,
|
|
58780
|
+
skipped: true,
|
|
58781
|
+
skipReason: "unaffected_daemon_scope"
|
|
58782
|
+
});
|
|
58783
|
+
continue;
|
|
58784
|
+
}
|
|
58785
|
+
commandsToRun.push(candidate);
|
|
58786
|
+
}
|
|
58787
|
+
if (opts?.changeImpact) {
|
|
58788
|
+
summary.changeImpact = {
|
|
58789
|
+
isDaemonAffecting: opts.changeImpact.isDaemonAffecting,
|
|
58790
|
+
affectedPackages: opts.changeImpact.affectedPackages,
|
|
58791
|
+
...skippedDaemonCommands.length ? { skippedDaemonCommands } : {}
|
|
58792
|
+
};
|
|
58793
|
+
}
|
|
58530
58794
|
if (runLegacyBootstrapCommands) {
|
|
58531
58795
|
summary.bootstrap = { stage: "legacy" };
|
|
58532
58796
|
for (const candidate of selection.bootstrapCommands) {
|
|
@@ -58561,23 +58825,22 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
58561
58825
|
}
|
|
58562
58826
|
}
|
|
58563
58827
|
}
|
|
58564
|
-
|
|
58828
|
+
let missingDepsBlocked = false;
|
|
58829
|
+
for (const candidate of commandsToRun) {
|
|
58565
58830
|
const startedAt = Date.now();
|
|
58566
58831
|
const cwd = candidate.cwd ? pathResolve2(workspace, candidate.cwd) : workspace;
|
|
58567
58832
|
const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
|
|
58568
58833
|
const bootstrapProvidedDependencies = summary.bootstrap?.stage === "cached" || summary.bootstrap?.stage === "ran" || summary.bootstrap?.stage === "legacy";
|
|
58569
|
-
if (!bootstrapProvidedDependencies &&
|
|
58834
|
+
if (!bootstrapProvidedDependencies && needsNodeModules(candidate, cwd)) {
|
|
58570
58835
|
summary.commandsRun.push(commandRecord(candidate, cwd, startedAt, {
|
|
58571
|
-
stderr: "Dependencies appear to be missing: package.json and a lockfile are present, but node_modules is absent. Configure validation.bootstrapCommands in repo mesh/refine config if Refinery should install/bootstrap before validation."
|
|
58836
|
+
stderr: "Dependencies appear to be missing: package.json and a lockfile are present, but node_modules is absent. Configure validation.bootstrapCommands (or .adhdev/worktree_bootstrap.json) in repo mesh/refine config if Refinery should install/bootstrap before validation."
|
|
58572
58837
|
}, false, {
|
|
58573
58838
|
exitCode: null,
|
|
58574
58839
|
skipped: true,
|
|
58575
58840
|
failureKind: "missing_dependencies"
|
|
58576
58841
|
}));
|
|
58577
|
-
|
|
58578
|
-
|
|
58579
|
-
summary.failureCode = "missing_dependencies";
|
|
58580
|
-
return summary;
|
|
58842
|
+
missingDepsBlocked = true;
|
|
58843
|
+
continue;
|
|
58581
58844
|
}
|
|
58582
58845
|
const resolvedCommand = resolveWin32Executable(candidate.command);
|
|
58583
58846
|
const spawn5 = buildWin32ExecFileSpawn(resolvedCommand, candidate.args);
|
|
@@ -58613,6 +58876,12 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
58613
58876
|
return summary;
|
|
58614
58877
|
}
|
|
58615
58878
|
}
|
|
58879
|
+
if (missingDepsBlocked) {
|
|
58880
|
+
summary.status = "failed";
|
|
58881
|
+
summary.failureKind = "missing_dependencies";
|
|
58882
|
+
summary.failureCode = "missing_dependencies";
|
|
58883
|
+
return summary;
|
|
58884
|
+
}
|
|
58616
58885
|
summary.status = "passed";
|
|
58617
58886
|
return summary;
|
|
58618
58887
|
}
|
|
@@ -58820,7 +59089,20 @@ async function refineResolveRefsStage(self, meshId, nodeId, args, refineStages)
|
|
|
58820
59089
|
const { stdout: branchHeadStdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
58821
59090
|
const baseHead = baseHeadRaw;
|
|
58822
59091
|
const branchHead = branchHeadStdout.trim();
|
|
58823
|
-
|
|
59092
|
+
let changeImpact;
|
|
59093
|
+
try {
|
|
59094
|
+
changeImpact = await classifyChangedPackages(node.workspace, baseHead, branchHead);
|
|
59095
|
+
} catch {
|
|
59096
|
+
changeImpact = void 0;
|
|
59097
|
+
}
|
|
59098
|
+
recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, {
|
|
59099
|
+
branch,
|
|
59100
|
+
baseBranch,
|
|
59101
|
+
baseHead,
|
|
59102
|
+
branchHead,
|
|
59103
|
+
...changeImpact ? { changeImpact } : {},
|
|
59104
|
+
...fetchWarning ? { fetchWarning } : {}
|
|
59105
|
+
});
|
|
58824
59106
|
return {
|
|
58825
59107
|
kind: "continue",
|
|
58826
59108
|
ctx: {
|
|
@@ -58837,6 +59119,7 @@ async function refineResolveRefsStage(self, meshId, nodeId, args, refineStages)
|
|
|
58837
59119
|
baseBranch,
|
|
58838
59120
|
baseHead,
|
|
58839
59121
|
branchHead,
|
|
59122
|
+
changeImpact,
|
|
58840
59123
|
validationSummary: void 0,
|
|
58841
59124
|
patchEquivalence: void 0,
|
|
58842
59125
|
submoduleReachability: void 0
|
|
@@ -58847,6 +59130,9 @@ async function refineValidationStage(self, ctx) {
|
|
|
58847
59130
|
const { mesh, node, branch, baseBranch, refineStages } = ctx;
|
|
58848
59131
|
const validationStarted = Date.now();
|
|
58849
59132
|
const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace, {
|
|
59133
|
+
// (a) Scope the validation command set by coarse change-impact (resolved
|
|
59134
|
+
// in resolve_refs). Undefined → gate runs the full command set (fail-open).
|
|
59135
|
+
changeImpact: ctx.changeImpact,
|
|
58850
59136
|
// M2-2: consume the node's persisted bootstrap state; persist re-runs.
|
|
58851
59137
|
persistedBootstrapState: node.worktreeBootstrap,
|
|
58852
59138
|
onBootstrapStateChange: (state) => {
|
|
@@ -58866,7 +59152,7 @@ async function refineValidationStage(self, ctx) {
|
|
|
58866
59152
|
if (validationSummary.status === "failed") {
|
|
58867
59153
|
const firstFailedCmd = Array.isArray(validationSummary.commandsRun) ? validationSummary.commandsRun.find((c) => c.success === false) : void 0;
|
|
58868
59154
|
const buildValidationFailedError = () => {
|
|
58869
|
-
const base = validationSummary.failureCode === "missing_dependencies" ? "Refinery validation dependencies are missing; merge/refine was not attempted.
|
|
59155
|
+
const base = validationSummary.failureCode === "missing_dependencies" ? "Refinery validation dependencies are missing for a change-affected package; merge/refine was not attempted. To make this self-service, either (1) configure .adhdev/worktree_bootstrap.json (or validation.bootstrapCommands in .adhdev/refine.json) so Refinery installs deps before validation, or (2) converge the branch via the documented manual fast-forward-only bypass (rebase onto the fetched base, verify strict ancestry, then push ff-only) instead of the refine gate." : validationSummary.failureCode === "dependency_bootstrap_failed" ? "Refinery dependency/bootstrap command failed; merge/refine was not attempted." : validationSummary.failureCode === "spawn_resolution_failed" ? validationSummary.spawnResolutionError || "Refinery validation command could not be spawned (executable not found); merge/refine was not attempted." : "Refinery validation gate failed; merge/refine was not attempted.";
|
|
58870
59156
|
if (!firstFailedCmd) return base;
|
|
58871
59157
|
const cmdName = typeof firstFailedCmd.displayCommand === "string" ? firstFailedCmd.displayCommand : typeof firstFailedCmd.command === "string" ? [firstFailedCmd.command, ...Array.isArray(firstFailedCmd.args) ? firstFailedCmd.args : []].join(" ").trim() : typeof firstFailedCmd.cmd === "string" ? firstFailedCmd.cmd : "";
|
|
58872
59158
|
const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s2) => typeof s2 === "string" && s2.length > 0).join("\n");
|
|
@@ -69819,6 +70105,7 @@ export {
|
|
|
69819
70105
|
appendRemoteLedgerEntries,
|
|
69820
70106
|
assertNoDependencyCycle,
|
|
69821
70107
|
buildAssistantChatMessage,
|
|
70108
|
+
buildAvailableProviders,
|
|
69822
70109
|
buildChatMessage,
|
|
69823
70110
|
buildChatMessageSignature,
|
|
69824
70111
|
buildChatTailDeliverySignature,
|