@adhdev/daemon-core 0.9.82-rc.259 → 0.9.82-rc.260
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/cli-adapters/cli-state-engine.d.ts +20 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +9 -0
- package/dist/commands/router.d.ts +14 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +467 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +457 -45
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-refine-batch.d.ts +68 -0
- package/dist/mesh/mesh-refine-status.d.ts +36 -0
- package/dist/mesh/mesh-work-queue.d.ts +26 -0
- package/package.json +1 -1
- package/src/cli-adapters/cli-state-engine.ts +56 -3
- package/src/cli-adapters/provider-cli-adapter.ts +1 -0
- package/src/cli-adapters/provider-cli-shared.ts +9 -0
- package/src/commands/router.ts +255 -0
- package/src/index.ts +3 -3
- package/src/mesh/mesh-refine-batch.ts +197 -0
- package/src/mesh/mesh-refine-status.ts +87 -0
- package/src/mesh/mesh-work-queue.ts +62 -0
- package/src/providers/cli-provider-instance.ts +11 -0
package/dist/index.mjs
CHANGED
|
@@ -2657,6 +2657,7 @@ __export(mesh_work_queue_exports, {
|
|
|
2657
2657
|
nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
|
|
2658
2658
|
normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
|
|
2659
2659
|
normalizeMeshTaskMode: () => normalizeMeshTaskMode,
|
|
2660
|
+
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
2660
2661
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
2661
2662
|
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
2662
2663
|
requeueTask: () => requeueTask,
|
|
@@ -2822,6 +2823,37 @@ function enqueueTask(meshId, message, opts) {
|
|
|
2822
2823
|
return entry;
|
|
2823
2824
|
});
|
|
2824
2825
|
}
|
|
2826
|
+
function recordDirectDispatchTask(meshId, message, opts) {
|
|
2827
|
+
const missionId = typeof opts.missionId === "string" ? opts.missionId.trim() : "";
|
|
2828
|
+
if (!missionId) return null;
|
|
2829
|
+
const taskId = typeof opts.id === "string" ? opts.id.trim() : "";
|
|
2830
|
+
if (!taskId) return null;
|
|
2831
|
+
const modeValidation = validateMeshTaskModeRequest(opts.taskMode, message);
|
|
2832
|
+
if (!modeValidation.valid) {
|
|
2833
|
+
throw new Error(`live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`);
|
|
2834
|
+
}
|
|
2835
|
+
const now = opts.dispatchedAt && opts.dispatchedAt.trim() ? opts.dispatchedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
2836
|
+
return withQueueLock(meshId, () => {
|
|
2837
|
+
if (MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId)) {
|
|
2838
|
+
return null;
|
|
2839
|
+
}
|
|
2840
|
+
const entry = {
|
|
2841
|
+
id: taskId,
|
|
2842
|
+
meshId,
|
|
2843
|
+
message,
|
|
2844
|
+
status: "assigned",
|
|
2845
|
+
...modeValidation.taskMode ? { taskMode: modeValidation.taskMode } : {},
|
|
2846
|
+
missionId,
|
|
2847
|
+
...opts.assignedNodeId ? { targetNodeId: opts.assignedNodeId, assignedNodeId: opts.assignedNodeId } : {},
|
|
2848
|
+
...opts.assignedSessionId ? { targetSessionId: opts.assignedSessionId, assignedSessionId: opts.assignedSessionId } : {},
|
|
2849
|
+
dispatchTimestamp: now,
|
|
2850
|
+
createdAt: now,
|
|
2851
|
+
updatedAt: now
|
|
2852
|
+
};
|
|
2853
|
+
MeshRuntimeStore.getInstance().insertQueueEntry(entry);
|
|
2854
|
+
return entry;
|
|
2855
|
+
});
|
|
2856
|
+
}
|
|
2825
2857
|
function getQueue(meshId, opts) {
|
|
2826
2858
|
return MeshRuntimeStore.getInstance().getQueueEntries(meshId, opts?.status?.length ? opts.status : void 0);
|
|
2827
2859
|
}
|
|
@@ -4401,9 +4433,48 @@ function buildMeshAsyncRefineJobs(args) {
|
|
|
4401
4433
|
return (Number.isFinite(bTime) ? bTime : 0) - (Number.isFinite(aTime) ? aTime : 0);
|
|
4402
4434
|
});
|
|
4403
4435
|
}
|
|
4436
|
+
function jobActivityTime(job) {
|
|
4437
|
+
const raw = job.lastUpdatedAt || job.completedAt || job.startedAt || "";
|
|
4438
|
+
const t = new Date(raw).getTime();
|
|
4439
|
+
return Number.isFinite(t) ? t : 0;
|
|
4440
|
+
}
|
|
4441
|
+
function summarizeMeshAsyncRefineJobs(jobs) {
|
|
4442
|
+
const activeJobs = [];
|
|
4443
|
+
const terminalJobs = [];
|
|
4444
|
+
for (const job of jobs) {
|
|
4445
|
+
if (TERMINAL_REFINE_STATUSES.has(job.status)) terminalJobs.push(job);
|
|
4446
|
+
else activeJobs.push(job);
|
|
4447
|
+
}
|
|
4448
|
+
let newest = 0;
|
|
4449
|
+
for (const job of jobs) newest = Math.max(newest, jobActivityTime(job));
|
|
4450
|
+
const cutoff = newest - STALE_TERMINAL_REFINE_WINDOW_MS;
|
|
4451
|
+
const terminalByRecency = [...terminalJobs].sort(
|
|
4452
|
+
(a, b) => jobActivityTime(b) - jobActivityTime(a)
|
|
4453
|
+
);
|
|
4454
|
+
const freshTerminal = terminalByRecency.filter((job) => jobActivityTime(job) >= cutoff).slice(0, RECENT_TERMINAL_REFINE_CAP);
|
|
4455
|
+
const freshTerminalIds = new Set(freshTerminal.map((job) => job.jobId));
|
|
4456
|
+
const byStatus = {};
|
|
4457
|
+
for (const job of activeJobs) {
|
|
4458
|
+
byStatus[job.status] = (byStatus[job.status] ?? 0) + 1;
|
|
4459
|
+
}
|
|
4460
|
+
for (const job of freshTerminal) {
|
|
4461
|
+
byStatus[job.status] = (byStatus[job.status] ?? 0) + 1;
|
|
4462
|
+
}
|
|
4463
|
+
const staleTerminal = terminalJobs.length - freshTerminalIds.size;
|
|
4464
|
+
return {
|
|
4465
|
+
total: activeJobs.length + freshTerminal.length,
|
|
4466
|
+
byStatus,
|
|
4467
|
+
staleTerminal,
|
|
4468
|
+
activeJobs
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4471
|
+
var TERMINAL_REFINE_STATUSES, STALE_TERMINAL_REFINE_WINDOW_MS, RECENT_TERMINAL_REFINE_CAP;
|
|
4404
4472
|
var init_mesh_refine_status = __esm({
|
|
4405
4473
|
"src/mesh/mesh-refine-status.ts"() {
|
|
4406
4474
|
"use strict";
|
|
4475
|
+
TERMINAL_REFINE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed"]);
|
|
4476
|
+
STALE_TERMINAL_REFINE_WINDOW_MS = 6 * 60 * 60 * 1e3;
|
|
4477
|
+
RECENT_TERMINAL_REFINE_CAP = 8;
|
|
4407
4478
|
}
|
|
4408
4479
|
});
|
|
4409
4480
|
|
|
@@ -10403,6 +10474,26 @@ var init_cli_state_engine = __esm({
|
|
|
10403
10474
|
// ── Approval ─────────────────────────────────────
|
|
10404
10475
|
lastApprovalResolvedAt = 0;
|
|
10405
10476
|
lastResolvedModalMessage = "";
|
|
10477
|
+
/**
|
|
10478
|
+
* Monotonic counter bumped every time the FSM *enters* waiting_approval
|
|
10479
|
+
* with a freshly captured modal (see `applyWaitingApproval`). It is the
|
|
10480
|
+
* single discriminator between "the same approval re-observed across TUI
|
|
10481
|
+
* paint flaps" and "a genuinely new, distinct approval".
|
|
10482
|
+
*
|
|
10483
|
+
* The message-equality cooldown below (`lastResolvedModalMessage`) cannot
|
|
10484
|
+
* tell these apart on its own: claude-cli routinely presents consecutive
|
|
10485
|
+
* approvals whose modal message text is identical (e.g. two back-to-back
|
|
10486
|
+
* Bash-command prompts). When that second approval arrived inside
|
|
10487
|
+
* `approvalCooldown`, the message-equality guard silently swallowed the
|
|
10488
|
+
* key write and the approval stuck forever — fatal under auto-approval.
|
|
10489
|
+
*
|
|
10490
|
+
* `approvalEntrySeq` increments on every fresh entry; `lastResolvedEntrySeq`
|
|
10491
|
+
* records which entry the cooldown belongs to. We only short-circuit the
|
|
10492
|
+
* write when we are still resolving *that same* entry — a new entry (new
|
|
10493
|
+
* seq) is always a real, distinct approval and must be written.
|
|
10494
|
+
*/
|
|
10495
|
+
approvalEntrySeq = 0;
|
|
10496
|
+
lastResolvedEntrySeq = -1;
|
|
10406
10497
|
/**
|
|
10407
10498
|
* When the engine previously held a modal but the latest parse failed
|
|
10408
10499
|
* to extract one, we record the timestamp here and only drop the modal
|
|
@@ -10508,6 +10599,7 @@ var init_cli_state_engine = __esm({
|
|
|
10508
10599
|
if (parsed?.status === "waiting_approval" && parsedModal) {
|
|
10509
10600
|
modal = parsedModal;
|
|
10510
10601
|
this.activeModal = parsedModal;
|
|
10602
|
+
this.approvalEntrySeq++;
|
|
10511
10603
|
if (this.currentStatus !== "waiting_approval") {
|
|
10512
10604
|
this.setStatus("waiting_approval", "resolve_modal_parse");
|
|
10513
10605
|
this.callbacks.onStatusChange();
|
|
@@ -10521,12 +10613,14 @@ var init_cli_state_engine = __esm({
|
|
|
10521
10613
|
if (!modal || !buttonsValid) return;
|
|
10522
10614
|
const currentModalMessage = typeof modal?.message === "string" ? modal.message.trim() : "";
|
|
10523
10615
|
const inCooldown = !!this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
10524
|
-
|
|
10616
|
+
const sameEntryReResolve = this.approvalEntrySeq === this.lastResolvedEntrySeq;
|
|
10617
|
+
if (inCooldown && sameEntryReResolve && currentModalMessage === this.lastResolvedModalMessage) return;
|
|
10525
10618
|
this.clearIdleFinishCandidate("resolve_modal");
|
|
10526
|
-
this.recordTrace("resolve_modal", { buttonIndex, activeModal: modal });
|
|
10619
|
+
this.recordTrace("resolve_modal", { buttonIndex, activeModal: modal, approvalEntrySeq: this.approvalEntrySeq });
|
|
10527
10620
|
this.activeModal = null;
|
|
10528
10621
|
this.lastApprovalResolvedAt = Date.now();
|
|
10529
10622
|
this.lastResolvedModalMessage = currentModalMessage;
|
|
10623
|
+
this.lastResolvedEntrySeq = this.approvalEntrySeq;
|
|
10530
10624
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
10531
10625
|
if (this.approvalExitTimeout) {
|
|
10532
10626
|
clearTimeout(this.approvalExitTimeout);
|
|
@@ -10883,7 +10977,7 @@ var init_cli_state_engine = __esm({
|
|
|
10883
10977
|
this.callbacks.onStatusChange();
|
|
10884
10978
|
return;
|
|
10885
10979
|
}
|
|
10886
|
-
if (!inCooldown) {
|
|
10980
|
+
if (!inCooldown || modal) {
|
|
10887
10981
|
if (!modal) {
|
|
10888
10982
|
LOG.warn("CLI", `[${this.provider.type}] detectStatus=waiting_approval but parseApproval returned null; ignoring`);
|
|
10889
10983
|
if (this.currentStatus === "waiting_approval" && this.activeModal) {
|
|
@@ -10906,6 +11000,7 @@ var init_cli_state_engine = __esm({
|
|
|
10906
11000
|
const nextBtnCount = Array.isArray(modal.buttons) ? modal.buttons.length : 0;
|
|
10907
11001
|
if (!prev || prevBtnCount !== nextBtnCount) {
|
|
10908
11002
|
this.activeModal = modal;
|
|
11003
|
+
this.approvalEntrySeq++;
|
|
10909
11004
|
this.callbacks.onStatusChange();
|
|
10910
11005
|
}
|
|
10911
11006
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
@@ -12100,6 +12195,7 @@ ${lastSnapshot}`;
|
|
|
12100
12195
|
messages: [],
|
|
12101
12196
|
workingDir: this.workingDir,
|
|
12102
12197
|
activeModal: effectiveModal,
|
|
12198
|
+
approvalEntrySeq: this.engine.approvalEntrySeq,
|
|
12103
12199
|
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
12104
12200
|
pendingOutboundMessages: this.pendingOutboundQueue.map((message) => ({
|
|
12105
12201
|
id: message.id,
|
|
@@ -16174,7 +16270,7 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
|
|
|
16174
16270
|
if (!validation.valid) {
|
|
16175
16271
|
return { status: "failed", required, configSource: loaded.path || loaded.source, configSourceType: "invalid", error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")), commandsRun: [] };
|
|
16176
16272
|
}
|
|
16177
|
-
const
|
|
16273
|
+
const execFileAsync4 = promisify3(execFile3);
|
|
16178
16274
|
const state = {
|
|
16179
16275
|
status: "running",
|
|
16180
16276
|
required,
|
|
@@ -16200,7 +16296,7 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
|
|
|
16200
16296
|
const startedAt = Date.now();
|
|
16201
16297
|
state.lastCommand = command.displayCommand;
|
|
16202
16298
|
try {
|
|
16203
|
-
const result = await
|
|
16299
|
+
const result = await execFileAsync4(command.command, command.args, {
|
|
16204
16300
|
cwd,
|
|
16205
16301
|
encoding: "utf8",
|
|
16206
16302
|
timeout: command.timeoutMs || DEFAULT_TIMEOUT_MS2,
|
|
@@ -31314,7 +31410,9 @@ var CliProviderInstance = class {
|
|
|
31314
31410
|
if (buttonIndex < 0) {
|
|
31315
31411
|
return autoApproveActive;
|
|
31316
31412
|
}
|
|
31413
|
+
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
|
|
31317
31414
|
const signature = [
|
|
31415
|
+
approvalEntrySeq,
|
|
31318
31416
|
typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
31319
31417
|
buttons.join("|"),
|
|
31320
31418
|
buttonIndex
|
|
@@ -36991,8 +37089,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
36991
37089
|
}
|
|
36992
37090
|
const https = __require("https");
|
|
36993
37091
|
const { exec: exec7 } = __require("child_process");
|
|
36994
|
-
const { promisify:
|
|
36995
|
-
const execAsync5 =
|
|
37092
|
+
const { promisify: promisify8 } = __require("util");
|
|
37093
|
+
const execAsync5 = promisify8(exec7);
|
|
36996
37094
|
const metaPath = path30.join(this.upstreamDir, _ProviderLoader.META_FILE);
|
|
36997
37095
|
let prevEtag = "";
|
|
36998
37096
|
let prevTimestamp = 0;
|
|
@@ -38272,6 +38370,93 @@ init_mesh_routing();
|
|
|
38272
38370
|
init_mesh_host_ownership();
|
|
38273
38371
|
init_mesh_fast_forward();
|
|
38274
38372
|
|
|
38373
|
+
// src/mesh/mesh-refine-batch.ts
|
|
38374
|
+
import { execFile as execFile4 } from "child_process";
|
|
38375
|
+
import { promisify as promisify6 } from "util";
|
|
38376
|
+
var execFileAsync3 = promisify6(execFile4);
|
|
38377
|
+
var MAX_CHANGED_FILES2 = 500;
|
|
38378
|
+
function topLevel(path39) {
|
|
38379
|
+
const slash = path39.indexOf("/");
|
|
38380
|
+
return slash === -1 ? path39 : path39.slice(0, slash);
|
|
38381
|
+
}
|
|
38382
|
+
async function analyzeMeshRefineNodeChangeArea(args) {
|
|
38383
|
+
const { nodeId, workspace, branch, baseRef, branchRef, diffCwd, submodulePaths } = args;
|
|
38384
|
+
const base = {
|
|
38385
|
+
nodeId,
|
|
38386
|
+
workspace,
|
|
38387
|
+
branch,
|
|
38388
|
+
changedTopLevelPaths: [],
|
|
38389
|
+
changedFiles: [],
|
|
38390
|
+
touchedSubmodulePaths: [],
|
|
38391
|
+
touchesSubmodule: false,
|
|
38392
|
+
aheadCount: 0
|
|
38393
|
+
};
|
|
38394
|
+
try {
|
|
38395
|
+
let mergeBase = baseRef;
|
|
38396
|
+
try {
|
|
38397
|
+
const { stdout } = await execFileAsync3("git", ["merge-base", baseRef, branchRef], { cwd: diffCwd, encoding: "utf8" });
|
|
38398
|
+
const resolved = stdout.trim();
|
|
38399
|
+
if (resolved) mergeBase = resolved;
|
|
38400
|
+
} catch {
|
|
38401
|
+
}
|
|
38402
|
+
const { stdout: countStdout } = await execFileAsync3(
|
|
38403
|
+
"git",
|
|
38404
|
+
["rev-list", "--count", `${mergeBase}..${branchRef}`],
|
|
38405
|
+
{ cwd: diffCwd, encoding: "utf8" }
|
|
38406
|
+
);
|
|
38407
|
+
base.aheadCount = Number.parseInt(countStdout.trim(), 10) || 0;
|
|
38408
|
+
const { stdout: nameStdout } = await execFileAsync3(
|
|
38409
|
+
"git",
|
|
38410
|
+
["diff", "--name-only", `${mergeBase}..${branchRef}`],
|
|
38411
|
+
{ cwd: diffCwd, encoding: "utf8" }
|
|
38412
|
+
);
|
|
38413
|
+
const files = nameStdout.split("\n").map((line) => line.trim()).filter(Boolean).slice(0, MAX_CHANGED_FILES2);
|
|
38414
|
+
base.changedFiles = files;
|
|
38415
|
+
const topSet = /* @__PURE__ */ new Set();
|
|
38416
|
+
const submoduleSet = /* @__PURE__ */ new Set();
|
|
38417
|
+
for (const file of files) {
|
|
38418
|
+
const top = topLevel(file);
|
|
38419
|
+
topSet.add(top);
|
|
38420
|
+
if (submodulePaths.has(file) || submodulePaths.has(top)) {
|
|
38421
|
+
submoduleSet.add(submodulePaths.has(file) ? file : top);
|
|
38422
|
+
}
|
|
38423
|
+
}
|
|
38424
|
+
base.changedTopLevelPaths = [...topSet].sort();
|
|
38425
|
+
base.touchedSubmodulePaths = [...submoduleSet].sort();
|
|
38426
|
+
base.touchesSubmodule = submoduleSet.size > 0;
|
|
38427
|
+
return base;
|
|
38428
|
+
} catch (e) {
|
|
38429
|
+
base.error = e?.message || String(e);
|
|
38430
|
+
return base;
|
|
38431
|
+
}
|
|
38432
|
+
}
|
|
38433
|
+
function orderMeshRefineBatchNodes(changeAreas) {
|
|
38434
|
+
const areaById = {};
|
|
38435
|
+
for (const area of changeAreas) areaById[area.nodeId] = area;
|
|
38436
|
+
const ranked = [...changeAreas].sort((a, b) => {
|
|
38437
|
+
const aSub = a.touchesSubmodule ? 1 : 0;
|
|
38438
|
+
const bSub = b.touchesSubmodule ? 1 : 0;
|
|
38439
|
+
if (aSub !== bSub) return aSub - bSub;
|
|
38440
|
+
const aBreadth = a.changedTopLevelPaths.length;
|
|
38441
|
+
const bBreadth = b.changedTopLevelPaths.length;
|
|
38442
|
+
if (aBreadth !== bBreadth) return aBreadth - bBreadth;
|
|
38443
|
+
return a.nodeId.localeCompare(b.nodeId);
|
|
38444
|
+
});
|
|
38445
|
+
const rationale = [];
|
|
38446
|
+
const nonSub = ranked.filter((a) => !a.touchesSubmodule).map((a) => a.nodeId);
|
|
38447
|
+
const sub = ranked.filter((a) => a.touchesSubmodule).map((a) => a.nodeId);
|
|
38448
|
+
if (nonSub.length) {
|
|
38449
|
+
rationale.push(`Non-submodule nodes first (no submodule-main advance, conflict-free ordering): ${nonSub.join(", ")}`);
|
|
38450
|
+
}
|
|
38451
|
+
if (sub.length) {
|
|
38452
|
+
rationale.push(`Submodule-touching nodes last, serialized (each merge advances submodule main, forcing rebase of the next): ${sub.join(", ")}`);
|
|
38453
|
+
}
|
|
38454
|
+
for (const area of ranked) {
|
|
38455
|
+
if (area.error) rationale.push(`Node ${area.nodeId}: change-area analysis degraded (${area.error}); placed with neutral priority.`);
|
|
38456
|
+
}
|
|
38457
|
+
return { order: ranked.map((a) => a.nodeId), changeAreas: areaById, rationale };
|
|
38458
|
+
}
|
|
38459
|
+
|
|
38275
38460
|
// src/mesh/preview-freshness.ts
|
|
38276
38461
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
38277
38462
|
import { existsSync as existsSync31, readFileSync as readFileSync24 } from "fs";
|
|
@@ -40263,10 +40448,10 @@ async function alignRefinerySubmodulesAfterMerge(repoRoot, previousBaseHead, cur
|
|
|
40263
40448
|
}
|
|
40264
40449
|
const commandArgs = ["submodule", "update", "--init", "--recursive", "--", ...updatePaths];
|
|
40265
40450
|
try {
|
|
40266
|
-
const { execFile:
|
|
40267
|
-
const { promisify:
|
|
40268
|
-
const
|
|
40269
|
-
const result = await
|
|
40451
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
40452
|
+
const { promisify: promisify8 } = await import("util");
|
|
40453
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
40454
|
+
const result = await execFileAsync4("git", commandArgs, {
|
|
40270
40455
|
cwd: repoRoot,
|
|
40271
40456
|
encoding: "utf8",
|
|
40272
40457
|
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
|
|
@@ -40309,11 +40494,11 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
|
|
|
40309
40494
|
const startedAt = Date.now();
|
|
40310
40495
|
const entries = [];
|
|
40311
40496
|
try {
|
|
40312
|
-
const { execFile:
|
|
40313
|
-
const { promisify:
|
|
40314
|
-
const
|
|
40497
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
40498
|
+
const { promisify: promisify8 } = await import("util");
|
|
40499
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
40315
40500
|
const runGit3 = async (cwd, args) => {
|
|
40316
|
-
const { stdout } = await
|
|
40501
|
+
const { stdout } = await execFileAsync4("git", args, {
|
|
40317
40502
|
cwd,
|
|
40318
40503
|
encoding: "utf8",
|
|
40319
40504
|
timeout: 3e4,
|
|
@@ -40328,7 +40513,7 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
|
|
|
40328
40513
|
};
|
|
40329
40514
|
const publishCommitToRemoteMain = async (submodulePath, commit, branch = "main") => {
|
|
40330
40515
|
const refspec = `${commit}:refs/heads/${branch}`;
|
|
40331
|
-
const { stdout, stderr } = await
|
|
40516
|
+
const { stdout, stderr } = await execFileAsync4("git", ["push", "origin", refspec], {
|
|
40332
40517
|
cwd: submodulePath,
|
|
40333
40518
|
encoding: "utf8",
|
|
40334
40519
|
timeout: 3e4,
|
|
@@ -40513,9 +40698,9 @@ function buildMeshRefineValidationPlan(mesh, workspace) {
|
|
|
40513
40698
|
};
|
|
40514
40699
|
}
|
|
40515
40700
|
async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
40516
|
-
const { execFile:
|
|
40517
|
-
const { promisify:
|
|
40518
|
-
const
|
|
40701
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
40702
|
+
const { promisify: promisify8 } = await import("util");
|
|
40703
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
40519
40704
|
const selection = resolveMeshRefineValidationPlan(mesh, workspace);
|
|
40520
40705
|
const summary = {
|
|
40521
40706
|
status: "skipped",
|
|
@@ -40609,7 +40794,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
40609
40794
|
const cwd = candidate.cwd ? pathResolve2(workspace, candidate.cwd) : workspace;
|
|
40610
40795
|
const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
|
|
40611
40796
|
try {
|
|
40612
|
-
const result = await
|
|
40797
|
+
const result = await execFileAsync4(candidate.command, candidate.args, {
|
|
40613
40798
|
cwd,
|
|
40614
40799
|
encoding: "utf8",
|
|
40615
40800
|
timeout,
|
|
@@ -40651,7 +40836,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
40651
40836
|
return summary;
|
|
40652
40837
|
}
|
|
40653
40838
|
try {
|
|
40654
|
-
const result = await
|
|
40839
|
+
const result = await execFileAsync4(candidate.command, candidate.args, {
|
|
40655
40840
|
cwd,
|
|
40656
40841
|
encoding: "utf8",
|
|
40657
40842
|
timeout,
|
|
@@ -41219,19 +41404,19 @@ var DaemonCommandRouter = class {
|
|
|
41219
41404
|
const isSubmoduleGuard = /working trees containing submodules cannot be moved or removed/i.test(message);
|
|
41220
41405
|
const submoduleForceBlocked = isSubmoduleGuard && !forceFallbackConvergence.allow;
|
|
41221
41406
|
if (isSubmoduleGuard && forceFallbackConvergence.allow) {
|
|
41222
|
-
const { execFile:
|
|
41223
|
-
const { promisify:
|
|
41224
|
-
const
|
|
41407
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
41408
|
+
const { promisify: promisify8 } = await import("util");
|
|
41409
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
41225
41410
|
const GIT_TIMEOUT_CLEANUP = 3e4;
|
|
41226
41411
|
const GIT_MAX_BUFFER_CLEANUP = 4 * 1024 * 1024;
|
|
41227
41412
|
try {
|
|
41228
|
-
await
|
|
41413
|
+
await execFileAsync4("git", ["-C", workspace, "submodule", "deinit", "--all", "-f"], {
|
|
41229
41414
|
encoding: "utf8",
|
|
41230
41415
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
41231
41416
|
maxBuffer: GIT_MAX_BUFFER_CLEANUP,
|
|
41232
41417
|
windowsHide: true
|
|
41233
41418
|
});
|
|
41234
|
-
await
|
|
41419
|
+
await execFileAsync4("git", ["worktree", "remove", "--force", workspace], {
|
|
41235
41420
|
cwd: repoRoot,
|
|
41236
41421
|
encoding: "utf8",
|
|
41237
41422
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
@@ -41250,7 +41435,7 @@ var DaemonCommandRouter = class {
|
|
|
41250
41435
|
} catch (deinitError) {
|
|
41251
41436
|
try {
|
|
41252
41437
|
fs23.rmSync(workspace, { recursive: true, force: true });
|
|
41253
|
-
await
|
|
41438
|
+
await execFileAsync4("git", ["worktree", "prune"], {
|
|
41254
41439
|
cwd: repoRoot,
|
|
41255
41440
|
encoding: "utf8",
|
|
41256
41441
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
@@ -41294,11 +41479,11 @@ var DaemonCommandRouter = class {
|
|
|
41294
41479
|
if (refinedConvergence === "merged_pushed" || refinedConvergence === "merged_to_main") {
|
|
41295
41480
|
return { allow: true, status: refinedConvergence, source: "node_refine_state" };
|
|
41296
41481
|
}
|
|
41297
|
-
const { execFile:
|
|
41298
|
-
const { promisify:
|
|
41299
|
-
const
|
|
41482
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
41483
|
+
const { promisify: promisify8 } = await import("util");
|
|
41484
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
41300
41485
|
const runGit3 = async (gitArgs, cwd) => {
|
|
41301
|
-
const { stdout } = await
|
|
41486
|
+
const { stdout } = await execFileAsync4("git", gitArgs, {
|
|
41302
41487
|
cwd,
|
|
41303
41488
|
encoding: "utf8",
|
|
41304
41489
|
timeout: 3e4,
|
|
@@ -41770,30 +41955,30 @@ var DaemonCommandRouter = class {
|
|
|
41770
41955
|
const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
|
|
41771
41956
|
const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
|
|
41772
41957
|
if (!repoRoot) return { success: false, error: "Source node repoRoot not found", refineStages };
|
|
41773
|
-
const { execFile:
|
|
41774
|
-
const { promisify:
|
|
41775
|
-
const
|
|
41958
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
41959
|
+
const { promisify: promisify8 } = await import("util");
|
|
41960
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
41776
41961
|
const resolveStarted = Date.now();
|
|
41777
|
-
const { stdout: branchStdout } = await
|
|
41962
|
+
const { stdout: branchStdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
41778
41963
|
const branch = branchStdout.trim();
|
|
41779
41964
|
if (!branch) return { success: false, error: "Could not determine branch of the worktree node", refineStages };
|
|
41780
|
-
const { stdout: baseBranchStdout } = await
|
|
41965
|
+
const { stdout: baseBranchStdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
41781
41966
|
const baseBranch = baseBranchStdout.trim();
|
|
41782
41967
|
let fetchWarning;
|
|
41783
41968
|
try {
|
|
41784
|
-
await
|
|
41969
|
+
await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
41785
41970
|
} catch (e) {
|
|
41786
41971
|
fetchWarning = `git fetch origin ${baseBranch} failed (proceeding with local HEAD): ${e?.message}`;
|
|
41787
41972
|
}
|
|
41788
41973
|
let baseHeadRaw;
|
|
41789
41974
|
try {
|
|
41790
|
-
const { stdout } = await
|
|
41975
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
|
|
41791
41976
|
baseHeadRaw = stdout.trim();
|
|
41792
41977
|
} catch {
|
|
41793
|
-
const { stdout: localHead } = await
|
|
41978
|
+
const { stdout: localHead } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
41794
41979
|
baseHeadRaw = localHead.trim();
|
|
41795
41980
|
}
|
|
41796
|
-
const { stdout: branchHeadStdout } = await
|
|
41981
|
+
const { stdout: branchHeadStdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
41797
41982
|
const baseHead = baseHeadRaw;
|
|
41798
41983
|
let branchHead = branchHeadStdout.trim();
|
|
41799
41984
|
recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, { branch, baseBranch, baseHead, branchHead, ...fetchWarning ? { fetchWarning } : {} });
|
|
@@ -41895,7 +42080,7 @@ ${tail}` : ""
|
|
|
41895
42080
|
cwd: node.workspace,
|
|
41896
42081
|
stdio: ["ignore", "pipe", "pipe"]
|
|
41897
42082
|
});
|
|
41898
|
-
const { stdout: rebasedHeadStdout } = await
|
|
42083
|
+
const { stdout: rebasedHeadStdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: node.workspace, encoding: "utf8" });
|
|
41899
42084
|
branchHead = rebasedHeadStdout.trim();
|
|
41900
42085
|
const rebasedPatchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
41901
42086
|
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", rebasedPatchEquivalence.status, autoRebaseStarted, {
|
|
@@ -42139,7 +42324,7 @@ ${tail}` : ""
|
|
|
42139
42324
|
let mergeResult;
|
|
42140
42325
|
const mergeStarted = Date.now();
|
|
42141
42326
|
try {
|
|
42142
|
-
const result = await
|
|
42327
|
+
const result = await execFileAsync4("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
42143
42328
|
mergeResult = {
|
|
42144
42329
|
stdout: truncateValidationOutput(result.stdout),
|
|
42145
42330
|
stderr: truncateValidationOutput(result.stderr),
|
|
@@ -42273,7 +42458,7 @@ ${tail}` : ""
|
|
|
42273
42458
|
if (!requireApprovalForPush) {
|
|
42274
42459
|
const pushStarted = Date.now();
|
|
42275
42460
|
try {
|
|
42276
|
-
await
|
|
42461
|
+
await execFileAsync4("git", ["push", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
42277
42462
|
pushResult = { pushed: true, remote: "origin", branch: baseBranch, durationMs: Date.now() - pushStarted };
|
|
42278
42463
|
recordMeshRefineStage(refineStages, "push", "passed", pushStarted, pushResult);
|
|
42279
42464
|
finalBranchConvergenceState.status = "merged_pushed";
|
|
@@ -42314,6 +42499,223 @@ ${tail}` : ""
|
|
|
42314
42499
|
return { success: false, error: e.message, refineStages };
|
|
42315
42500
|
}
|
|
42316
42501
|
}
|
|
42502
|
+
/**
|
|
42503
|
+
* Batch refinery: converge multiple sibling worktree nodes onto the base branch
|
|
42504
|
+
* in one sequential pipeline, absorbing the rebase + patch-equivalence churn that
|
|
42505
|
+
* arises when several siblings touch the same submodule.
|
|
42506
|
+
*
|
|
42507
|
+
* Reuses executeMeshRefineNodeSynchronously per node — every node goes through the
|
|
42508
|
+
* exact same validation / patch-equivalence / submodule-reachability / merge / cleanup
|
|
42509
|
+
* gates, including its built-in auto-rebase onto fresh origin/<base>. Because each
|
|
42510
|
+
* node fetches origin/<base> at the start of its own refine, a node merged earlier in
|
|
42511
|
+
* the batch advances the base, and the next node's refine auto-rebases onto it before
|
|
42512
|
+
* re-running patch-equivalence. No force-push, no reset — conflicting nodes are
|
|
42513
|
+
* isolated as blocked_review while the rest of the batch proceeds.
|
|
42514
|
+
*/
|
|
42515
|
+
async batchRefineMeshNodes(meshId, requestedNodeIds, args) {
|
|
42516
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
42517
|
+
const mesh = meshRecord?.mesh;
|
|
42518
|
+
if (!mesh) return { success: false, error: `Mesh '${meshId}' not found` };
|
|
42519
|
+
const allNodes = Array.isArray(mesh.nodes) ? mesh.nodes : [];
|
|
42520
|
+
const isConvergeable = (n) => n?.isLocalWorktree && typeof n.workspace === "string" && n.workspace;
|
|
42521
|
+
let targetNodes;
|
|
42522
|
+
if (Array.isArray(requestedNodeIds) && requestedNodeIds.length > 0) {
|
|
42523
|
+
targetNodes = [];
|
|
42524
|
+
const missing = [];
|
|
42525
|
+
const nonWorktree = [];
|
|
42526
|
+
for (const nodeId of requestedNodeIds) {
|
|
42527
|
+
const node = allNodes.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
42528
|
+
if (!node) {
|
|
42529
|
+
missing.push(nodeId);
|
|
42530
|
+
continue;
|
|
42531
|
+
}
|
|
42532
|
+
if (!isConvergeable(node)) {
|
|
42533
|
+
nonWorktree.push(nodeId);
|
|
42534
|
+
continue;
|
|
42535
|
+
}
|
|
42536
|
+
targetNodes.push(node);
|
|
42537
|
+
}
|
|
42538
|
+
if (missing.length || nonWorktree.length) {
|
|
42539
|
+
return {
|
|
42540
|
+
success: false,
|
|
42541
|
+
error: "One or more requested nodes are not convergeable local worktree nodes.",
|
|
42542
|
+
...missing.length ? { missingNodeIds: missing } : {},
|
|
42543
|
+
...nonWorktree.length ? { nonWorktreeNodeIds: nonWorktree } : {}
|
|
42544
|
+
};
|
|
42545
|
+
}
|
|
42546
|
+
} else {
|
|
42547
|
+
targetNodes = allNodes.filter(isConvergeable);
|
|
42548
|
+
}
|
|
42549
|
+
if (targetNodes.length === 0) {
|
|
42550
|
+
return { success: true, batch: true, dryRun: args?.dryRun !== false, nodeCount: 0, order: [], results: [], note: "No convergeable local worktree nodes found." };
|
|
42551
|
+
}
|
|
42552
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
42553
|
+
const { promisify: promisify8 } = await import("util");
|
|
42554
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
42555
|
+
const resolveRepoRootFor = (node) => {
|
|
42556
|
+
const sourceNode = node.clonedFromNodeId ? allNodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : allNodes.find((n) => !n.isLocalWorktree);
|
|
42557
|
+
return sourceNode?.repoRoot || sourceNode?.workspace;
|
|
42558
|
+
};
|
|
42559
|
+
const repoRootBaseRef = /* @__PURE__ */ new Map();
|
|
42560
|
+
const submodulePathsByRepoRoot = /* @__PURE__ */ new Map();
|
|
42561
|
+
const resolveBaseRef = async (repoRoot) => {
|
|
42562
|
+
const cached = repoRootBaseRef.get(repoRoot);
|
|
42563
|
+
if (cached) return cached;
|
|
42564
|
+
let baseBranch = "main";
|
|
42565
|
+
try {
|
|
42566
|
+
const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
42567
|
+
if (stdout.trim()) baseBranch = stdout.trim();
|
|
42568
|
+
} catch {
|
|
42569
|
+
}
|
|
42570
|
+
let baseRef = "HEAD";
|
|
42571
|
+
try {
|
|
42572
|
+
await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
42573
|
+
} catch {
|
|
42574
|
+
}
|
|
42575
|
+
try {
|
|
42576
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
|
|
42577
|
+
baseRef = stdout.trim();
|
|
42578
|
+
} catch {
|
|
42579
|
+
try {
|
|
42580
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
42581
|
+
baseRef = stdout.trim();
|
|
42582
|
+
} catch {
|
|
42583
|
+
}
|
|
42584
|
+
}
|
|
42585
|
+
repoRootBaseRef.set(repoRoot, baseRef);
|
|
42586
|
+
return baseRef;
|
|
42587
|
+
};
|
|
42588
|
+
const changeAreas = [];
|
|
42589
|
+
for (const node of targetNodes) {
|
|
42590
|
+
const repoRoot = resolveRepoRootFor(node);
|
|
42591
|
+
let branch = typeof node.worktreeBranch === "string" ? node.worktreeBranch : "";
|
|
42592
|
+
try {
|
|
42593
|
+
const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
42594
|
+
if (stdout.trim()) branch = stdout.trim();
|
|
42595
|
+
} catch {
|
|
42596
|
+
}
|
|
42597
|
+
if (!repoRoot || !branch) {
|
|
42598
|
+
changeAreas.push({
|
|
42599
|
+
nodeId: node.id,
|
|
42600
|
+
workspace: node.workspace,
|
|
42601
|
+
branch: branch || "(unknown)",
|
|
42602
|
+
changedTopLevelPaths: [],
|
|
42603
|
+
changedFiles: [],
|
|
42604
|
+
touchedSubmodulePaths: [],
|
|
42605
|
+
touchesSubmodule: false,
|
|
42606
|
+
aheadCount: 0,
|
|
42607
|
+
error: !repoRoot ? "source repoRoot not found" : "branch not resolved"
|
|
42608
|
+
});
|
|
42609
|
+
continue;
|
|
42610
|
+
}
|
|
42611
|
+
if (!submodulePathsByRepoRoot.has(repoRoot)) {
|
|
42612
|
+
let subPaths = /* @__PURE__ */ new Set();
|
|
42613
|
+
try {
|
|
42614
|
+
const { stdout } = await execFileAsync4("git", ["config", "--file", ".gitmodules", "--get-regexp", "path"], { cwd: repoRoot, encoding: "utf8" });
|
|
42615
|
+
for (const line of stdout.split("\n")) {
|
|
42616
|
+
const trimmed = line.trim();
|
|
42617
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
42618
|
+
if (spaceIdx === -1) continue;
|
|
42619
|
+
const value = trimmed.slice(spaceIdx + 1).trim();
|
|
42620
|
+
if (value) subPaths.add(value);
|
|
42621
|
+
}
|
|
42622
|
+
} catch {
|
|
42623
|
+
subPaths = /* @__PURE__ */ new Set();
|
|
42624
|
+
}
|
|
42625
|
+
submodulePathsByRepoRoot.set(repoRoot, subPaths);
|
|
42626
|
+
}
|
|
42627
|
+
const baseRef = await resolveBaseRef(repoRoot);
|
|
42628
|
+
let branchRef = branch;
|
|
42629
|
+
try {
|
|
42630
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
42631
|
+
branchRef = stdout.trim() || branch;
|
|
42632
|
+
} catch {
|
|
42633
|
+
}
|
|
42634
|
+
changeAreas.push(await analyzeMeshRefineNodeChangeArea({
|
|
42635
|
+
nodeId: node.id,
|
|
42636
|
+
workspace: node.workspace,
|
|
42637
|
+
branch,
|
|
42638
|
+
baseRef,
|
|
42639
|
+
branchRef,
|
|
42640
|
+
diffCwd: node.workspace,
|
|
42641
|
+
submodulePaths: submodulePathsByRepoRoot.get(repoRoot)
|
|
42642
|
+
}));
|
|
42643
|
+
}
|
|
42644
|
+
const ordering = orderMeshRefineBatchNodes(changeAreas);
|
|
42645
|
+
const orderedNodes = ordering.order.map((nodeId) => targetNodes.find((n) => n.id === nodeId || n.nodeId === nodeId)).filter((n) => !!n);
|
|
42646
|
+
const dryRun = args?.dryRun !== false && args?.execute !== true;
|
|
42647
|
+
if (dryRun) {
|
|
42648
|
+
return {
|
|
42649
|
+
success: true,
|
|
42650
|
+
batch: true,
|
|
42651
|
+
dryRun: true,
|
|
42652
|
+
nodeCount: orderedNodes.length,
|
|
42653
|
+
order: ordering.order,
|
|
42654
|
+
orderingRationale: ordering.rationale,
|
|
42655
|
+
changeAreas: ordering.changeAreas,
|
|
42656
|
+
plan: orderedNodes.map((node) => ({
|
|
42657
|
+
nodeId: node.id,
|
|
42658
|
+
workspace: node.workspace,
|
|
42659
|
+
validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
|
|
42660
|
+
mergeWillRun: false
|
|
42661
|
+
})),
|
|
42662
|
+
note: "Dry-run: no validation, rebase, or merge was executed. Re-run with execute=true to converge nodes in this order."
|
|
42663
|
+
};
|
|
42664
|
+
}
|
|
42665
|
+
const results = [];
|
|
42666
|
+
for (const node of orderedNodes) {
|
|
42667
|
+
let result;
|
|
42668
|
+
try {
|
|
42669
|
+
result = await this.executeMeshRefineNodeSynchronously(meshId, node.id, args);
|
|
42670
|
+
} catch (e) {
|
|
42671
|
+
result = { success: false, error: e?.message || String(e) };
|
|
42672
|
+
}
|
|
42673
|
+
const code = typeof result.code === "string" ? result.code : "";
|
|
42674
|
+
let convergence;
|
|
42675
|
+
if (code === "already_merged" && result.alreadyMergedViaOtherPath) {
|
|
42676
|
+
convergence = "skipped_patch_equivalent";
|
|
42677
|
+
} else if (result.success === true) {
|
|
42678
|
+
convergence = "merged_to_main";
|
|
42679
|
+
} else if (code === "merge_failed") {
|
|
42680
|
+
convergence = "not_mergeable";
|
|
42681
|
+
} else {
|
|
42682
|
+
convergence = "blocked_review";
|
|
42683
|
+
}
|
|
42684
|
+
const fbcs = result.finalBranchConvergenceState && typeof result.finalBranchConvergenceState === "object" ? result.finalBranchConvergenceState : void 0;
|
|
42685
|
+
const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s) => s.status === "failed").map((s) => s.stage).filter(Boolean).pop() : void 0;
|
|
42686
|
+
results.push({
|
|
42687
|
+
nodeId: node.id,
|
|
42688
|
+
workspace: node.workspace,
|
|
42689
|
+
convergence,
|
|
42690
|
+
...code ? { code } : {},
|
|
42691
|
+
...typeof result.blockedReason === "string" ? { reason: result.blockedReason } : {},
|
|
42692
|
+
...stage ? { stage } : {},
|
|
42693
|
+
...typeof result.error === "string" ? { error: result.error } : {},
|
|
42694
|
+
...fbcs ? { finalBranchConvergenceState: fbcs } : {}
|
|
42695
|
+
});
|
|
42696
|
+
}
|
|
42697
|
+
const summary = {
|
|
42698
|
+
merged: results.filter((r) => r.convergence === "merged_to_main").length,
|
|
42699
|
+
skipped: results.filter((r) => r.convergence === "skipped_patch_equivalent").length,
|
|
42700
|
+
blocked: results.filter((r) => r.convergence === "blocked_review").length,
|
|
42701
|
+
notMergeable: results.filter((r) => r.convergence === "not_mergeable").length
|
|
42702
|
+
};
|
|
42703
|
+
const allConverged = summary.blocked === 0 && summary.notMergeable === 0;
|
|
42704
|
+
return {
|
|
42705
|
+
success: true,
|
|
42706
|
+
batch: true,
|
|
42707
|
+
dryRun: false,
|
|
42708
|
+
nodeCount: orderedNodes.length,
|
|
42709
|
+
order: ordering.order,
|
|
42710
|
+
orderingRationale: ordering.rationale,
|
|
42711
|
+
summary,
|
|
42712
|
+
allConverged,
|
|
42713
|
+
results,
|
|
42714
|
+
...allConverged ? {} : {
|
|
42715
|
+
nextStep: "Resolve blocked_review / not_mergeable nodes manually (see per-node code/stage/error), then re-run mesh_refine_batch for the remaining nodes."
|
|
42716
|
+
}
|
|
42717
|
+
};
|
|
42718
|
+
}
|
|
42317
42719
|
async finishMeshRefineJob(handle, args) {
|
|
42318
42720
|
const key = this.buildRefineJobKey(handle.meshId, handle.targetNodeId);
|
|
42319
42721
|
let result;
|
|
@@ -43857,6 +44259,12 @@ ${tail}` : ""
|
|
|
43857
44259
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
43858
44260
|
return this.startMeshRefineJob(meshId, nodeId, args);
|
|
43859
44261
|
}
|
|
44262
|
+
case "batch_refine_mesh_nodes": {
|
|
44263
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
44264
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
44265
|
+
const requestedNodeIds = Array.isArray(args?.nodeIds) ? args.nodeIds.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim()) : void 0;
|
|
44266
|
+
return this.batchRefineMeshNodes(meshId, requestedNodeIds, args);
|
|
44267
|
+
}
|
|
43860
44268
|
case "remove_mesh_node": {
|
|
43861
44269
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
43862
44270
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
@@ -52984,7 +53392,7 @@ function shouldAutoRestoreHostedSessionsOnStartup(env = process.env) {
|
|
|
52984
53392
|
|
|
52985
53393
|
// src/installer.ts
|
|
52986
53394
|
import { exec as exec6 } from "child_process";
|
|
52987
|
-
import { promisify as
|
|
53395
|
+
import { promisify as promisify7 } from "util";
|
|
52988
53396
|
var EXTENSION_CATALOG = [
|
|
52989
53397
|
// AI Agent extensions
|
|
52990
53398
|
{
|
|
@@ -53071,7 +53479,7 @@ var EXTENSION_CATALOG = [
|
|
|
53071
53479
|
apiKeyName: "OpenAI/Anthropic API key"
|
|
53072
53480
|
}
|
|
53073
53481
|
];
|
|
53074
|
-
var execAsync4 =
|
|
53482
|
+
var execAsync4 = promisify7(exec6);
|
|
53075
53483
|
async function isExtensionInstalled(ide, marketplaceId) {
|
|
53076
53484
|
if (!ide.cliCommand) return false;
|
|
53077
53485
|
try {
|
|
@@ -53819,7 +54227,9 @@ export {
|
|
|
53819
54227
|
ProviderCliAdapter,
|
|
53820
54228
|
ProviderInstanceManager,
|
|
53821
54229
|
ProviderLoader,
|
|
54230
|
+
RECENT_TERMINAL_REFINE_CAP,
|
|
53822
54231
|
RawTerminalAttachment,
|
|
54232
|
+
STALE_TERMINAL_REFINE_WINDOW_MS,
|
|
53823
54233
|
STANDALONE_CDP_SCAN_INTERVAL_MS,
|
|
53824
54234
|
SessionHostPtyTransportFactory,
|
|
53825
54235
|
TerminalAdapter,
|
|
@@ -54027,6 +54437,7 @@ export {
|
|
|
54027
54437
|
reconcileDirectDispatchCompletionFromTranscript,
|
|
54028
54438
|
recordCompletionConflict,
|
|
54029
54439
|
recordDebugTrace,
|
|
54440
|
+
recordDirectDispatchTask,
|
|
54030
54441
|
recordMeshToolCall,
|
|
54031
54442
|
registerExtensionProviders,
|
|
54032
54443
|
registerMeshCoordinator,
|
|
@@ -54063,6 +54474,7 @@ export {
|
|
|
54063
54474
|
startLocalIpcServer,
|
|
54064
54475
|
suggestMeshRefineConfig,
|
|
54065
54476
|
summarizeGitStatus,
|
|
54477
|
+
summarizeMeshAsyncRefineJobs,
|
|
54066
54478
|
summarizeMeshMission,
|
|
54067
54479
|
summarizeMissionTasks,
|
|
54068
54480
|
triggerMeshQueue,
|