@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.js
CHANGED
|
@@ -2662,6 +2662,7 @@ __export(mesh_work_queue_exports, {
|
|
|
2662
2662
|
nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
|
|
2663
2663
|
normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
|
|
2664
2664
|
normalizeMeshTaskMode: () => normalizeMeshTaskMode,
|
|
2665
|
+
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
2665
2666
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
2666
2667
|
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
2667
2668
|
requeueTask: () => requeueTask,
|
|
@@ -2826,6 +2827,37 @@ function enqueueTask(meshId, message, opts) {
|
|
|
2826
2827
|
return entry;
|
|
2827
2828
|
});
|
|
2828
2829
|
}
|
|
2830
|
+
function recordDirectDispatchTask(meshId, message, opts) {
|
|
2831
|
+
const missionId = typeof opts.missionId === "string" ? opts.missionId.trim() : "";
|
|
2832
|
+
if (!missionId) return null;
|
|
2833
|
+
const taskId = typeof opts.id === "string" ? opts.id.trim() : "";
|
|
2834
|
+
if (!taskId) return null;
|
|
2835
|
+
const modeValidation = validateMeshTaskModeRequest(opts.taskMode, message);
|
|
2836
|
+
if (!modeValidation.valid) {
|
|
2837
|
+
throw new Error(`live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`);
|
|
2838
|
+
}
|
|
2839
|
+
const now = opts.dispatchedAt && opts.dispatchedAt.trim() ? opts.dispatchedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
2840
|
+
return withQueueLock(meshId, () => {
|
|
2841
|
+
if (MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId)) {
|
|
2842
|
+
return null;
|
|
2843
|
+
}
|
|
2844
|
+
const entry = {
|
|
2845
|
+
id: taskId,
|
|
2846
|
+
meshId,
|
|
2847
|
+
message,
|
|
2848
|
+
status: "assigned",
|
|
2849
|
+
...modeValidation.taskMode ? { taskMode: modeValidation.taskMode } : {},
|
|
2850
|
+
missionId,
|
|
2851
|
+
...opts.assignedNodeId ? { targetNodeId: opts.assignedNodeId, assignedNodeId: opts.assignedNodeId } : {},
|
|
2852
|
+
...opts.assignedSessionId ? { targetSessionId: opts.assignedSessionId, assignedSessionId: opts.assignedSessionId } : {},
|
|
2853
|
+
dispatchTimestamp: now,
|
|
2854
|
+
createdAt: now,
|
|
2855
|
+
updatedAt: now
|
|
2856
|
+
};
|
|
2857
|
+
MeshRuntimeStore.getInstance().insertQueueEntry(entry);
|
|
2858
|
+
return entry;
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2829
2861
|
function getQueue(meshId, opts) {
|
|
2830
2862
|
return MeshRuntimeStore.getInstance().getQueueEntries(meshId, opts?.status?.length ? opts.status : void 0);
|
|
2831
2863
|
}
|
|
@@ -4407,9 +4439,48 @@ function buildMeshAsyncRefineJobs(args) {
|
|
|
4407
4439
|
return (Number.isFinite(bTime) ? bTime : 0) - (Number.isFinite(aTime) ? aTime : 0);
|
|
4408
4440
|
});
|
|
4409
4441
|
}
|
|
4442
|
+
function jobActivityTime(job) {
|
|
4443
|
+
const raw = job.lastUpdatedAt || job.completedAt || job.startedAt || "";
|
|
4444
|
+
const t = new Date(raw).getTime();
|
|
4445
|
+
return Number.isFinite(t) ? t : 0;
|
|
4446
|
+
}
|
|
4447
|
+
function summarizeMeshAsyncRefineJobs(jobs) {
|
|
4448
|
+
const activeJobs = [];
|
|
4449
|
+
const terminalJobs = [];
|
|
4450
|
+
for (const job of jobs) {
|
|
4451
|
+
if (TERMINAL_REFINE_STATUSES.has(job.status)) terminalJobs.push(job);
|
|
4452
|
+
else activeJobs.push(job);
|
|
4453
|
+
}
|
|
4454
|
+
let newest = 0;
|
|
4455
|
+
for (const job of jobs) newest = Math.max(newest, jobActivityTime(job));
|
|
4456
|
+
const cutoff = newest - STALE_TERMINAL_REFINE_WINDOW_MS;
|
|
4457
|
+
const terminalByRecency = [...terminalJobs].sort(
|
|
4458
|
+
(a, b) => jobActivityTime(b) - jobActivityTime(a)
|
|
4459
|
+
);
|
|
4460
|
+
const freshTerminal = terminalByRecency.filter((job) => jobActivityTime(job) >= cutoff).slice(0, RECENT_TERMINAL_REFINE_CAP);
|
|
4461
|
+
const freshTerminalIds = new Set(freshTerminal.map((job) => job.jobId));
|
|
4462
|
+
const byStatus = {};
|
|
4463
|
+
for (const job of activeJobs) {
|
|
4464
|
+
byStatus[job.status] = (byStatus[job.status] ?? 0) + 1;
|
|
4465
|
+
}
|
|
4466
|
+
for (const job of freshTerminal) {
|
|
4467
|
+
byStatus[job.status] = (byStatus[job.status] ?? 0) + 1;
|
|
4468
|
+
}
|
|
4469
|
+
const staleTerminal = terminalJobs.length - freshTerminalIds.size;
|
|
4470
|
+
return {
|
|
4471
|
+
total: activeJobs.length + freshTerminal.length,
|
|
4472
|
+
byStatus,
|
|
4473
|
+
staleTerminal,
|
|
4474
|
+
activeJobs
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4477
|
+
var TERMINAL_REFINE_STATUSES, STALE_TERMINAL_REFINE_WINDOW_MS, RECENT_TERMINAL_REFINE_CAP;
|
|
4410
4478
|
var init_mesh_refine_status = __esm({
|
|
4411
4479
|
"src/mesh/mesh-refine-status.ts"() {
|
|
4412
4480
|
"use strict";
|
|
4481
|
+
TERMINAL_REFINE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed"]);
|
|
4482
|
+
STALE_TERMINAL_REFINE_WINDOW_MS = 6 * 60 * 60 * 1e3;
|
|
4483
|
+
RECENT_TERMINAL_REFINE_CAP = 8;
|
|
4413
4484
|
}
|
|
4414
4485
|
});
|
|
4415
4486
|
|
|
@@ -10407,6 +10478,26 @@ var init_cli_state_engine = __esm({
|
|
|
10407
10478
|
// ── Approval ─────────────────────────────────────
|
|
10408
10479
|
lastApprovalResolvedAt = 0;
|
|
10409
10480
|
lastResolvedModalMessage = "";
|
|
10481
|
+
/**
|
|
10482
|
+
* Monotonic counter bumped every time the FSM *enters* waiting_approval
|
|
10483
|
+
* with a freshly captured modal (see `applyWaitingApproval`). It is the
|
|
10484
|
+
* single discriminator between "the same approval re-observed across TUI
|
|
10485
|
+
* paint flaps" and "a genuinely new, distinct approval".
|
|
10486
|
+
*
|
|
10487
|
+
* The message-equality cooldown below (`lastResolvedModalMessage`) cannot
|
|
10488
|
+
* tell these apart on its own: claude-cli routinely presents consecutive
|
|
10489
|
+
* approvals whose modal message text is identical (e.g. two back-to-back
|
|
10490
|
+
* Bash-command prompts). When that second approval arrived inside
|
|
10491
|
+
* `approvalCooldown`, the message-equality guard silently swallowed the
|
|
10492
|
+
* key write and the approval stuck forever — fatal under auto-approval.
|
|
10493
|
+
*
|
|
10494
|
+
* `approvalEntrySeq` increments on every fresh entry; `lastResolvedEntrySeq`
|
|
10495
|
+
* records which entry the cooldown belongs to. We only short-circuit the
|
|
10496
|
+
* write when we are still resolving *that same* entry — a new entry (new
|
|
10497
|
+
* seq) is always a real, distinct approval and must be written.
|
|
10498
|
+
*/
|
|
10499
|
+
approvalEntrySeq = 0;
|
|
10500
|
+
lastResolvedEntrySeq = -1;
|
|
10410
10501
|
/**
|
|
10411
10502
|
* When the engine previously held a modal but the latest parse failed
|
|
10412
10503
|
* to extract one, we record the timestamp here and only drop the modal
|
|
@@ -10512,6 +10603,7 @@ var init_cli_state_engine = __esm({
|
|
|
10512
10603
|
if (parsed?.status === "waiting_approval" && parsedModal) {
|
|
10513
10604
|
modal = parsedModal;
|
|
10514
10605
|
this.activeModal = parsedModal;
|
|
10606
|
+
this.approvalEntrySeq++;
|
|
10515
10607
|
if (this.currentStatus !== "waiting_approval") {
|
|
10516
10608
|
this.setStatus("waiting_approval", "resolve_modal_parse");
|
|
10517
10609
|
this.callbacks.onStatusChange();
|
|
@@ -10525,12 +10617,14 @@ var init_cli_state_engine = __esm({
|
|
|
10525
10617
|
if (!modal || !buttonsValid) return;
|
|
10526
10618
|
const currentModalMessage = typeof modal?.message === "string" ? modal.message.trim() : "";
|
|
10527
10619
|
const inCooldown = !!this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
10528
|
-
|
|
10620
|
+
const sameEntryReResolve = this.approvalEntrySeq === this.lastResolvedEntrySeq;
|
|
10621
|
+
if (inCooldown && sameEntryReResolve && currentModalMessage === this.lastResolvedModalMessage) return;
|
|
10529
10622
|
this.clearIdleFinishCandidate("resolve_modal");
|
|
10530
|
-
this.recordTrace("resolve_modal", { buttonIndex, activeModal: modal });
|
|
10623
|
+
this.recordTrace("resolve_modal", { buttonIndex, activeModal: modal, approvalEntrySeq: this.approvalEntrySeq });
|
|
10531
10624
|
this.activeModal = null;
|
|
10532
10625
|
this.lastApprovalResolvedAt = Date.now();
|
|
10533
10626
|
this.lastResolvedModalMessage = currentModalMessage;
|
|
10627
|
+
this.lastResolvedEntrySeq = this.approvalEntrySeq;
|
|
10534
10628
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
10535
10629
|
if (this.approvalExitTimeout) {
|
|
10536
10630
|
clearTimeout(this.approvalExitTimeout);
|
|
@@ -10887,7 +10981,7 @@ var init_cli_state_engine = __esm({
|
|
|
10887
10981
|
this.callbacks.onStatusChange();
|
|
10888
10982
|
return;
|
|
10889
10983
|
}
|
|
10890
|
-
if (!inCooldown) {
|
|
10984
|
+
if (!inCooldown || modal) {
|
|
10891
10985
|
if (!modal) {
|
|
10892
10986
|
LOG.warn("CLI", `[${this.provider.type}] detectStatus=waiting_approval but parseApproval returned null; ignoring`);
|
|
10893
10987
|
if (this.currentStatus === "waiting_approval" && this.activeModal) {
|
|
@@ -10910,6 +11004,7 @@ var init_cli_state_engine = __esm({
|
|
|
10910
11004
|
const nextBtnCount = Array.isArray(modal.buttons) ? modal.buttons.length : 0;
|
|
10911
11005
|
if (!prev || prevBtnCount !== nextBtnCount) {
|
|
10912
11006
|
this.activeModal = modal;
|
|
11007
|
+
this.approvalEntrySeq++;
|
|
10913
11008
|
this.callbacks.onStatusChange();
|
|
10914
11009
|
}
|
|
10915
11010
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
@@ -12105,6 +12200,7 @@ ${lastSnapshot}`;
|
|
|
12105
12200
|
messages: [],
|
|
12106
12201
|
workingDir: this.workingDir,
|
|
12107
12202
|
activeModal: effectiveModal,
|
|
12203
|
+
approvalEntrySeq: this.engine.approvalEntrySeq,
|
|
12108
12204
|
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
12109
12205
|
pendingOutboundMessages: this.pendingOutboundQueue.map((message) => ({
|
|
12110
12206
|
id: message.id,
|
|
@@ -13998,7 +14094,9 @@ __export(index_exports, {
|
|
|
13998
14094
|
ProviderCliAdapter: () => ProviderCliAdapter,
|
|
13999
14095
|
ProviderInstanceManager: () => ProviderInstanceManager,
|
|
14000
14096
|
ProviderLoader: () => ProviderLoader,
|
|
14097
|
+
RECENT_TERMINAL_REFINE_CAP: () => RECENT_TERMINAL_REFINE_CAP,
|
|
14001
14098
|
RawTerminalAttachment: () => RawTerminalAttachment,
|
|
14099
|
+
STALE_TERMINAL_REFINE_WINDOW_MS: () => STALE_TERMINAL_REFINE_WINDOW_MS,
|
|
14002
14100
|
STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS,
|
|
14003
14101
|
SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory,
|
|
14004
14102
|
TerminalAdapter: () => TerminalAdapter,
|
|
@@ -14206,6 +14304,7 @@ __export(index_exports, {
|
|
|
14206
14304
|
reconcileDirectDispatchCompletionFromTranscript: () => reconcileDirectDispatchCompletionFromTranscript,
|
|
14207
14305
|
recordCompletionConflict: () => recordCompletionConflict,
|
|
14208
14306
|
recordDebugTrace: () => recordDebugTrace,
|
|
14307
|
+
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
14209
14308
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
14210
14309
|
registerExtensionProviders: () => registerExtensionProviders,
|
|
14211
14310
|
registerMeshCoordinator: () => registerMeshCoordinator,
|
|
@@ -14242,6 +14341,7 @@ __export(index_exports, {
|
|
|
14242
14341
|
startLocalIpcServer: () => startLocalIpcServer,
|
|
14243
14342
|
suggestMeshRefineConfig: () => suggestMeshRefineConfig,
|
|
14244
14343
|
summarizeGitStatus: () => summarizeGitStatus,
|
|
14344
|
+
summarizeMeshAsyncRefineJobs: () => summarizeMeshAsyncRefineJobs,
|
|
14245
14345
|
summarizeMeshMission: () => summarizeMeshMission,
|
|
14246
14346
|
summarizeMissionTasks: () => summarizeMissionTasks,
|
|
14247
14347
|
triggerMeshQueue: () => triggerMeshQueue,
|
|
@@ -16506,7 +16606,7 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
|
|
|
16506
16606
|
if (!validation.valid) {
|
|
16507
16607
|
return { status: "failed", required, configSource: loaded.path || loaded.source, configSourceType: "invalid", error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")), commandsRun: [] };
|
|
16508
16608
|
}
|
|
16509
|
-
const
|
|
16609
|
+
const execFileAsync4 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
|
|
16510
16610
|
const state = {
|
|
16511
16611
|
status: "running",
|
|
16512
16612
|
required,
|
|
@@ -16532,7 +16632,7 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
|
|
|
16532
16632
|
const startedAt = Date.now();
|
|
16533
16633
|
state.lastCommand = command.displayCommand;
|
|
16534
16634
|
try {
|
|
16535
|
-
const result = await
|
|
16635
|
+
const result = await execFileAsync4(command.command, command.args, {
|
|
16536
16636
|
cwd,
|
|
16537
16637
|
encoding: "utf8",
|
|
16538
16638
|
timeout: command.timeoutMs || DEFAULT_TIMEOUT_MS2,
|
|
@@ -31646,7 +31746,9 @@ var CliProviderInstance = class {
|
|
|
31646
31746
|
if (buttonIndex < 0) {
|
|
31647
31747
|
return autoApproveActive;
|
|
31648
31748
|
}
|
|
31749
|
+
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
|
|
31649
31750
|
const signature = [
|
|
31751
|
+
approvalEntrySeq,
|
|
31650
31752
|
typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
31651
31753
|
buttons.join("|"),
|
|
31652
31754
|
buttonIndex
|
|
@@ -37318,8 +37420,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
37318
37420
|
}
|
|
37319
37421
|
const https = require("https");
|
|
37320
37422
|
const { exec: exec7 } = require("child_process");
|
|
37321
|
-
const { promisify:
|
|
37322
|
-
const execAsync5 =
|
|
37423
|
+
const { promisify: promisify8 } = require("util");
|
|
37424
|
+
const execAsync5 = promisify8(exec7);
|
|
37323
37425
|
const metaPath = path30.join(this.upstreamDir, _ProviderLoader.META_FILE);
|
|
37324
37426
|
let prevEtag = "";
|
|
37325
37427
|
let prevTimestamp = 0;
|
|
@@ -38599,14 +38701,101 @@ init_mesh_routing();
|
|
|
38599
38701
|
init_mesh_host_ownership();
|
|
38600
38702
|
init_mesh_fast_forward();
|
|
38601
38703
|
|
|
38602
|
-
// src/mesh/
|
|
38704
|
+
// src/mesh/mesh-refine-batch.ts
|
|
38603
38705
|
var import_node_child_process4 = require("child_process");
|
|
38706
|
+
var import_node_util4 = require("util");
|
|
38707
|
+
var execFileAsync3 = (0, import_node_util4.promisify)(import_node_child_process4.execFile);
|
|
38708
|
+
var MAX_CHANGED_FILES2 = 500;
|
|
38709
|
+
function topLevel(path39) {
|
|
38710
|
+
const slash = path39.indexOf("/");
|
|
38711
|
+
return slash === -1 ? path39 : path39.slice(0, slash);
|
|
38712
|
+
}
|
|
38713
|
+
async function analyzeMeshRefineNodeChangeArea(args) {
|
|
38714
|
+
const { nodeId, workspace, branch, baseRef, branchRef, diffCwd, submodulePaths } = args;
|
|
38715
|
+
const base = {
|
|
38716
|
+
nodeId,
|
|
38717
|
+
workspace,
|
|
38718
|
+
branch,
|
|
38719
|
+
changedTopLevelPaths: [],
|
|
38720
|
+
changedFiles: [],
|
|
38721
|
+
touchedSubmodulePaths: [],
|
|
38722
|
+
touchesSubmodule: false,
|
|
38723
|
+
aheadCount: 0
|
|
38724
|
+
};
|
|
38725
|
+
try {
|
|
38726
|
+
let mergeBase = baseRef;
|
|
38727
|
+
try {
|
|
38728
|
+
const { stdout } = await execFileAsync3("git", ["merge-base", baseRef, branchRef], { cwd: diffCwd, encoding: "utf8" });
|
|
38729
|
+
const resolved = stdout.trim();
|
|
38730
|
+
if (resolved) mergeBase = resolved;
|
|
38731
|
+
} catch {
|
|
38732
|
+
}
|
|
38733
|
+
const { stdout: countStdout } = await execFileAsync3(
|
|
38734
|
+
"git",
|
|
38735
|
+
["rev-list", "--count", `${mergeBase}..${branchRef}`],
|
|
38736
|
+
{ cwd: diffCwd, encoding: "utf8" }
|
|
38737
|
+
);
|
|
38738
|
+
base.aheadCount = Number.parseInt(countStdout.trim(), 10) || 0;
|
|
38739
|
+
const { stdout: nameStdout } = await execFileAsync3(
|
|
38740
|
+
"git",
|
|
38741
|
+
["diff", "--name-only", `${mergeBase}..${branchRef}`],
|
|
38742
|
+
{ cwd: diffCwd, encoding: "utf8" }
|
|
38743
|
+
);
|
|
38744
|
+
const files = nameStdout.split("\n").map((line) => line.trim()).filter(Boolean).slice(0, MAX_CHANGED_FILES2);
|
|
38745
|
+
base.changedFiles = files;
|
|
38746
|
+
const topSet = /* @__PURE__ */ new Set();
|
|
38747
|
+
const submoduleSet = /* @__PURE__ */ new Set();
|
|
38748
|
+
for (const file of files) {
|
|
38749
|
+
const top = topLevel(file);
|
|
38750
|
+
topSet.add(top);
|
|
38751
|
+
if (submodulePaths.has(file) || submodulePaths.has(top)) {
|
|
38752
|
+
submoduleSet.add(submodulePaths.has(file) ? file : top);
|
|
38753
|
+
}
|
|
38754
|
+
}
|
|
38755
|
+
base.changedTopLevelPaths = [...topSet].sort();
|
|
38756
|
+
base.touchedSubmodulePaths = [...submoduleSet].sort();
|
|
38757
|
+
base.touchesSubmodule = submoduleSet.size > 0;
|
|
38758
|
+
return base;
|
|
38759
|
+
} catch (e) {
|
|
38760
|
+
base.error = e?.message || String(e);
|
|
38761
|
+
return base;
|
|
38762
|
+
}
|
|
38763
|
+
}
|
|
38764
|
+
function orderMeshRefineBatchNodes(changeAreas) {
|
|
38765
|
+
const areaById = {};
|
|
38766
|
+
for (const area of changeAreas) areaById[area.nodeId] = area;
|
|
38767
|
+
const ranked = [...changeAreas].sort((a, b) => {
|
|
38768
|
+
const aSub = a.touchesSubmodule ? 1 : 0;
|
|
38769
|
+
const bSub = b.touchesSubmodule ? 1 : 0;
|
|
38770
|
+
if (aSub !== bSub) return aSub - bSub;
|
|
38771
|
+
const aBreadth = a.changedTopLevelPaths.length;
|
|
38772
|
+
const bBreadth = b.changedTopLevelPaths.length;
|
|
38773
|
+
if (aBreadth !== bBreadth) return aBreadth - bBreadth;
|
|
38774
|
+
return a.nodeId.localeCompare(b.nodeId);
|
|
38775
|
+
});
|
|
38776
|
+
const rationale = [];
|
|
38777
|
+
const nonSub = ranked.filter((a) => !a.touchesSubmodule).map((a) => a.nodeId);
|
|
38778
|
+
const sub = ranked.filter((a) => a.touchesSubmodule).map((a) => a.nodeId);
|
|
38779
|
+
if (nonSub.length) {
|
|
38780
|
+
rationale.push(`Non-submodule nodes first (no submodule-main advance, conflict-free ordering): ${nonSub.join(", ")}`);
|
|
38781
|
+
}
|
|
38782
|
+
if (sub.length) {
|
|
38783
|
+
rationale.push(`Submodule-touching nodes last, serialized (each merge advances submodule main, forcing rebase of the next): ${sub.join(", ")}`);
|
|
38784
|
+
}
|
|
38785
|
+
for (const area of ranked) {
|
|
38786
|
+
if (area.error) rationale.push(`Node ${area.nodeId}: change-area analysis degraded (${area.error}); placed with neutral priority.`);
|
|
38787
|
+
}
|
|
38788
|
+
return { order: ranked.map((a) => a.nodeId), changeAreas: areaById, rationale };
|
|
38789
|
+
}
|
|
38790
|
+
|
|
38791
|
+
// src/mesh/preview-freshness.ts
|
|
38792
|
+
var import_node_child_process5 = require("child_process");
|
|
38604
38793
|
var import_node_fs4 = require("fs");
|
|
38605
38794
|
var import_node_path2 = require("path");
|
|
38606
38795
|
var PREVIEW_DEPLOY_RECORD = ".adhdev/preview-deploy.json";
|
|
38607
38796
|
function runGit2(repoRoot, args) {
|
|
38608
38797
|
try {
|
|
38609
|
-
return (0,
|
|
38798
|
+
return (0, import_node_child_process5.execFileSync)("git", args, {
|
|
38610
38799
|
cwd: repoRoot,
|
|
38611
38800
|
encoding: "utf8",
|
|
38612
38801
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -39374,7 +39563,7 @@ init_repo_mesh_types();
|
|
|
39374
39563
|
var import_os3 = require("os");
|
|
39375
39564
|
var import_path10 = require("path");
|
|
39376
39565
|
var fs23 = __toESM(require("fs"));
|
|
39377
|
-
var
|
|
39566
|
+
var import_node_child_process6 = require("child_process");
|
|
39378
39567
|
var CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
|
|
39379
39568
|
var CHANNEL_SERVER_URL = {
|
|
39380
39569
|
stable: "https://api.adhf.dev",
|
|
@@ -40532,7 +40721,7 @@ function buildPatchEquivalenceSubmoduleConflictHint(repoRoot, baseHead, branchHe
|
|
|
40532
40721
|
}
|
|
40533
40722
|
function readChangedGitlinkPaths(repoRoot, fromRef, toRef) {
|
|
40534
40723
|
try {
|
|
40535
|
-
const output = (0,
|
|
40724
|
+
const output = (0, import_node_child_process6.execFileSync)("git", ["diff", "--raw", "--no-abbrev", fromRef, toRef], {
|
|
40536
40725
|
cwd: repoRoot,
|
|
40537
40726
|
encoding: "utf8",
|
|
40538
40727
|
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
|
|
@@ -40556,7 +40745,7 @@ function readChangedGitlinkPaths(repoRoot, fromRef, toRef) {
|
|
|
40556
40745
|
}
|
|
40557
40746
|
function readTreeObject(repoRoot, ref, path39) {
|
|
40558
40747
|
try {
|
|
40559
|
-
const output = (0,
|
|
40748
|
+
const output = (0, import_node_child_process6.execFileSync)("git", ["ls-tree", ref, "--", path39], {
|
|
40560
40749
|
cwd: repoRoot,
|
|
40561
40750
|
encoding: "utf8",
|
|
40562
40751
|
maxBuffer: 1024 * 1024
|
|
@@ -40590,10 +40779,10 @@ async function alignRefinerySubmodulesAfterMerge(repoRoot, previousBaseHead, cur
|
|
|
40590
40779
|
}
|
|
40591
40780
|
const commandArgs = ["submodule", "update", "--init", "--recursive", "--", ...updatePaths];
|
|
40592
40781
|
try {
|
|
40593
|
-
const { execFile:
|
|
40594
|
-
const { promisify:
|
|
40595
|
-
const
|
|
40596
|
-
const result = await
|
|
40782
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
40783
|
+
const { promisify: promisify8 } = await import("util");
|
|
40784
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
40785
|
+
const result = await execFileAsync4("git", commandArgs, {
|
|
40597
40786
|
cwd: repoRoot,
|
|
40598
40787
|
encoding: "utf8",
|
|
40599
40788
|
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
|
|
@@ -40636,11 +40825,11 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
|
|
|
40636
40825
|
const startedAt = Date.now();
|
|
40637
40826
|
const entries = [];
|
|
40638
40827
|
try {
|
|
40639
|
-
const { execFile:
|
|
40640
|
-
const { promisify:
|
|
40641
|
-
const
|
|
40828
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
40829
|
+
const { promisify: promisify8 } = await import("util");
|
|
40830
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
40642
40831
|
const runGit3 = async (cwd, args) => {
|
|
40643
|
-
const { stdout } = await
|
|
40832
|
+
const { stdout } = await execFileAsync4("git", args, {
|
|
40644
40833
|
cwd,
|
|
40645
40834
|
encoding: "utf8",
|
|
40646
40835
|
timeout: 3e4,
|
|
@@ -40655,7 +40844,7 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
|
|
|
40655
40844
|
};
|
|
40656
40845
|
const publishCommitToRemoteMain = async (submodulePath, commit, branch = "main") => {
|
|
40657
40846
|
const refspec = `${commit}:refs/heads/${branch}`;
|
|
40658
|
-
const { stdout, stderr } = await
|
|
40847
|
+
const { stdout, stderr } = await execFileAsync4("git", ["push", "origin", refspec], {
|
|
40659
40848
|
cwd: submodulePath,
|
|
40660
40849
|
encoding: "utf8",
|
|
40661
40850
|
timeout: 3e4,
|
|
@@ -40840,9 +41029,9 @@ function buildMeshRefineValidationPlan(mesh, workspace) {
|
|
|
40840
41029
|
};
|
|
40841
41030
|
}
|
|
40842
41031
|
async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
40843
|
-
const { execFile:
|
|
40844
|
-
const { promisify:
|
|
40845
|
-
const
|
|
41032
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
41033
|
+
const { promisify: promisify8 } = await import("util");
|
|
41034
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
40846
41035
|
const selection = resolveMeshRefineValidationPlan(mesh, workspace);
|
|
40847
41036
|
const summary = {
|
|
40848
41037
|
status: "skipped",
|
|
@@ -40936,7 +41125,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
40936
41125
|
const cwd = candidate.cwd ? (0, import_path10.resolve)(workspace, candidate.cwd) : workspace;
|
|
40937
41126
|
const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
|
|
40938
41127
|
try {
|
|
40939
|
-
const result = await
|
|
41128
|
+
const result = await execFileAsync4(candidate.command, candidate.args, {
|
|
40940
41129
|
cwd,
|
|
40941
41130
|
encoding: "utf8",
|
|
40942
41131
|
timeout,
|
|
@@ -40978,7 +41167,7 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
40978
41167
|
return summary;
|
|
40979
41168
|
}
|
|
40980
41169
|
try {
|
|
40981
|
-
const result = await
|
|
41170
|
+
const result = await execFileAsync4(candidate.command, candidate.args, {
|
|
40982
41171
|
cwd,
|
|
40983
41172
|
encoding: "utf8",
|
|
40984
41173
|
timeout,
|
|
@@ -41546,19 +41735,19 @@ var DaemonCommandRouter = class {
|
|
|
41546
41735
|
const isSubmoduleGuard = /working trees containing submodules cannot be moved or removed/i.test(message);
|
|
41547
41736
|
const submoduleForceBlocked = isSubmoduleGuard && !forceFallbackConvergence.allow;
|
|
41548
41737
|
if (isSubmoduleGuard && forceFallbackConvergence.allow) {
|
|
41549
|
-
const { execFile:
|
|
41550
|
-
const { promisify:
|
|
41551
|
-
const
|
|
41738
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
41739
|
+
const { promisify: promisify8 } = await import("util");
|
|
41740
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
41552
41741
|
const GIT_TIMEOUT_CLEANUP = 3e4;
|
|
41553
41742
|
const GIT_MAX_BUFFER_CLEANUP = 4 * 1024 * 1024;
|
|
41554
41743
|
try {
|
|
41555
|
-
await
|
|
41744
|
+
await execFileAsync4("git", ["-C", workspace, "submodule", "deinit", "--all", "-f"], {
|
|
41556
41745
|
encoding: "utf8",
|
|
41557
41746
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
41558
41747
|
maxBuffer: GIT_MAX_BUFFER_CLEANUP,
|
|
41559
41748
|
windowsHide: true
|
|
41560
41749
|
});
|
|
41561
|
-
await
|
|
41750
|
+
await execFileAsync4("git", ["worktree", "remove", "--force", workspace], {
|
|
41562
41751
|
cwd: repoRoot,
|
|
41563
41752
|
encoding: "utf8",
|
|
41564
41753
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
@@ -41577,7 +41766,7 @@ var DaemonCommandRouter = class {
|
|
|
41577
41766
|
} catch (deinitError) {
|
|
41578
41767
|
try {
|
|
41579
41768
|
fs23.rmSync(workspace, { recursive: true, force: true });
|
|
41580
|
-
await
|
|
41769
|
+
await execFileAsync4("git", ["worktree", "prune"], {
|
|
41581
41770
|
cwd: repoRoot,
|
|
41582
41771
|
encoding: "utf8",
|
|
41583
41772
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
@@ -41621,11 +41810,11 @@ var DaemonCommandRouter = class {
|
|
|
41621
41810
|
if (refinedConvergence === "merged_pushed" || refinedConvergence === "merged_to_main") {
|
|
41622
41811
|
return { allow: true, status: refinedConvergence, source: "node_refine_state" };
|
|
41623
41812
|
}
|
|
41624
|
-
const { execFile:
|
|
41625
|
-
const { promisify:
|
|
41626
|
-
const
|
|
41813
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
41814
|
+
const { promisify: promisify8 } = await import("util");
|
|
41815
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
41627
41816
|
const runGit3 = async (gitArgs, cwd) => {
|
|
41628
|
-
const { stdout } = await
|
|
41817
|
+
const { stdout } = await execFileAsync4("git", gitArgs, {
|
|
41629
41818
|
cwd,
|
|
41630
41819
|
encoding: "utf8",
|
|
41631
41820
|
timeout: 3e4,
|
|
@@ -42097,30 +42286,30 @@ var DaemonCommandRouter = class {
|
|
|
42097
42286
|
const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
|
|
42098
42287
|
const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
|
|
42099
42288
|
if (!repoRoot) return { success: false, error: "Source node repoRoot not found", refineStages };
|
|
42100
|
-
const { execFile:
|
|
42101
|
-
const { promisify:
|
|
42102
|
-
const
|
|
42289
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
42290
|
+
const { promisify: promisify8 } = await import("util");
|
|
42291
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
42103
42292
|
const resolveStarted = Date.now();
|
|
42104
|
-
const { stdout: branchStdout } = await
|
|
42293
|
+
const { stdout: branchStdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
42105
42294
|
const branch = branchStdout.trim();
|
|
42106
42295
|
if (!branch) return { success: false, error: "Could not determine branch of the worktree node", refineStages };
|
|
42107
|
-
const { stdout: baseBranchStdout } = await
|
|
42296
|
+
const { stdout: baseBranchStdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
42108
42297
|
const baseBranch = baseBranchStdout.trim();
|
|
42109
42298
|
let fetchWarning;
|
|
42110
42299
|
try {
|
|
42111
|
-
await
|
|
42300
|
+
await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
42112
42301
|
} catch (e) {
|
|
42113
42302
|
fetchWarning = `git fetch origin ${baseBranch} failed (proceeding with local HEAD): ${e?.message}`;
|
|
42114
42303
|
}
|
|
42115
42304
|
let baseHeadRaw;
|
|
42116
42305
|
try {
|
|
42117
|
-
const { stdout } = await
|
|
42306
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
|
|
42118
42307
|
baseHeadRaw = stdout.trim();
|
|
42119
42308
|
} catch {
|
|
42120
|
-
const { stdout: localHead } = await
|
|
42309
|
+
const { stdout: localHead } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
42121
42310
|
baseHeadRaw = localHead.trim();
|
|
42122
42311
|
}
|
|
42123
|
-
const { stdout: branchHeadStdout } = await
|
|
42312
|
+
const { stdout: branchHeadStdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
42124
42313
|
const baseHead = baseHeadRaw;
|
|
42125
42314
|
let branchHead = branchHeadStdout.trim();
|
|
42126
42315
|
recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, { branch, baseBranch, baseHead, branchHead, ...fetchWarning ? { fetchWarning } : {} });
|
|
@@ -42208,7 +42397,7 @@ ${tail}` : ""
|
|
|
42208
42397
|
let didAutoRebase = false;
|
|
42209
42398
|
let isBehindBase = false;
|
|
42210
42399
|
try {
|
|
42211
|
-
(0,
|
|
42400
|
+
(0, import_node_child_process6.execFileSync)("git", ["merge-base", "--is-ancestor", branchHead, baseHead], {
|
|
42212
42401
|
cwd: node.workspace,
|
|
42213
42402
|
stdio: "ignore"
|
|
42214
42403
|
});
|
|
@@ -42218,11 +42407,11 @@ ${tail}` : ""
|
|
|
42218
42407
|
if (isBehindBase) {
|
|
42219
42408
|
const autoRebaseStarted = Date.now();
|
|
42220
42409
|
try {
|
|
42221
|
-
(0,
|
|
42410
|
+
(0, import_node_child_process6.execFileSync)("git", ["rebase", baseHead], {
|
|
42222
42411
|
cwd: node.workspace,
|
|
42223
42412
|
stdio: ["ignore", "pipe", "pipe"]
|
|
42224
42413
|
});
|
|
42225
|
-
const { stdout: rebasedHeadStdout } = await
|
|
42414
|
+
const { stdout: rebasedHeadStdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: node.workspace, encoding: "utf8" });
|
|
42226
42415
|
branchHead = rebasedHeadStdout.trim();
|
|
42227
42416
|
const rebasedPatchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
42228
42417
|
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", rebasedPatchEquivalence.status, autoRebaseStarted, {
|
|
@@ -42259,7 +42448,7 @@ ${tail}` : ""
|
|
|
42259
42448
|
}
|
|
42260
42449
|
} catch (rebaseErr) {
|
|
42261
42450
|
try {
|
|
42262
|
-
(0,
|
|
42451
|
+
(0, import_node_child_process6.execFileSync)("git", ["rebase", "--abort"], { cwd: node.workspace, stdio: "ignore" });
|
|
42263
42452
|
} catch {
|
|
42264
42453
|
}
|
|
42265
42454
|
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", "failed", autoRebaseStarted, {
|
|
@@ -42466,7 +42655,7 @@ ${tail}` : ""
|
|
|
42466
42655
|
let mergeResult;
|
|
42467
42656
|
const mergeStarted = Date.now();
|
|
42468
42657
|
try {
|
|
42469
|
-
const result = await
|
|
42658
|
+
const result = await execFileAsync4("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
42470
42659
|
mergeResult = {
|
|
42471
42660
|
stdout: truncateValidationOutput(result.stdout),
|
|
42472
42661
|
stderr: truncateValidationOutput(result.stderr),
|
|
@@ -42600,7 +42789,7 @@ ${tail}` : ""
|
|
|
42600
42789
|
if (!requireApprovalForPush) {
|
|
42601
42790
|
const pushStarted = Date.now();
|
|
42602
42791
|
try {
|
|
42603
|
-
await
|
|
42792
|
+
await execFileAsync4("git", ["push", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
42604
42793
|
pushResult = { pushed: true, remote: "origin", branch: baseBranch, durationMs: Date.now() - pushStarted };
|
|
42605
42794
|
recordMeshRefineStage(refineStages, "push", "passed", pushStarted, pushResult);
|
|
42606
42795
|
finalBranchConvergenceState.status = "merged_pushed";
|
|
@@ -42641,6 +42830,223 @@ ${tail}` : ""
|
|
|
42641
42830
|
return { success: false, error: e.message, refineStages };
|
|
42642
42831
|
}
|
|
42643
42832
|
}
|
|
42833
|
+
/**
|
|
42834
|
+
* Batch refinery: converge multiple sibling worktree nodes onto the base branch
|
|
42835
|
+
* in one sequential pipeline, absorbing the rebase + patch-equivalence churn that
|
|
42836
|
+
* arises when several siblings touch the same submodule.
|
|
42837
|
+
*
|
|
42838
|
+
* Reuses executeMeshRefineNodeSynchronously per node — every node goes through the
|
|
42839
|
+
* exact same validation / patch-equivalence / submodule-reachability / merge / cleanup
|
|
42840
|
+
* gates, including its built-in auto-rebase onto fresh origin/<base>. Because each
|
|
42841
|
+
* node fetches origin/<base> at the start of its own refine, a node merged earlier in
|
|
42842
|
+
* the batch advances the base, and the next node's refine auto-rebases onto it before
|
|
42843
|
+
* re-running patch-equivalence. No force-push, no reset — conflicting nodes are
|
|
42844
|
+
* isolated as blocked_review while the rest of the batch proceeds.
|
|
42845
|
+
*/
|
|
42846
|
+
async batchRefineMeshNodes(meshId, requestedNodeIds, args) {
|
|
42847
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
42848
|
+
const mesh = meshRecord?.mesh;
|
|
42849
|
+
if (!mesh) return { success: false, error: `Mesh '${meshId}' not found` };
|
|
42850
|
+
const allNodes = Array.isArray(mesh.nodes) ? mesh.nodes : [];
|
|
42851
|
+
const isConvergeable = (n) => n?.isLocalWorktree && typeof n.workspace === "string" && n.workspace;
|
|
42852
|
+
let targetNodes;
|
|
42853
|
+
if (Array.isArray(requestedNodeIds) && requestedNodeIds.length > 0) {
|
|
42854
|
+
targetNodes = [];
|
|
42855
|
+
const missing = [];
|
|
42856
|
+
const nonWorktree = [];
|
|
42857
|
+
for (const nodeId of requestedNodeIds) {
|
|
42858
|
+
const node = allNodes.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
42859
|
+
if (!node) {
|
|
42860
|
+
missing.push(nodeId);
|
|
42861
|
+
continue;
|
|
42862
|
+
}
|
|
42863
|
+
if (!isConvergeable(node)) {
|
|
42864
|
+
nonWorktree.push(nodeId);
|
|
42865
|
+
continue;
|
|
42866
|
+
}
|
|
42867
|
+
targetNodes.push(node);
|
|
42868
|
+
}
|
|
42869
|
+
if (missing.length || nonWorktree.length) {
|
|
42870
|
+
return {
|
|
42871
|
+
success: false,
|
|
42872
|
+
error: "One or more requested nodes are not convergeable local worktree nodes.",
|
|
42873
|
+
...missing.length ? { missingNodeIds: missing } : {},
|
|
42874
|
+
...nonWorktree.length ? { nonWorktreeNodeIds: nonWorktree } : {}
|
|
42875
|
+
};
|
|
42876
|
+
}
|
|
42877
|
+
} else {
|
|
42878
|
+
targetNodes = allNodes.filter(isConvergeable);
|
|
42879
|
+
}
|
|
42880
|
+
if (targetNodes.length === 0) {
|
|
42881
|
+
return { success: true, batch: true, dryRun: args?.dryRun !== false, nodeCount: 0, order: [], results: [], note: "No convergeable local worktree nodes found." };
|
|
42882
|
+
}
|
|
42883
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
42884
|
+
const { promisify: promisify8 } = await import("util");
|
|
42885
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
42886
|
+
const resolveRepoRootFor = (node) => {
|
|
42887
|
+
const sourceNode = node.clonedFromNodeId ? allNodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : allNodes.find((n) => !n.isLocalWorktree);
|
|
42888
|
+
return sourceNode?.repoRoot || sourceNode?.workspace;
|
|
42889
|
+
};
|
|
42890
|
+
const repoRootBaseRef = /* @__PURE__ */ new Map();
|
|
42891
|
+
const submodulePathsByRepoRoot = /* @__PURE__ */ new Map();
|
|
42892
|
+
const resolveBaseRef = async (repoRoot) => {
|
|
42893
|
+
const cached = repoRootBaseRef.get(repoRoot);
|
|
42894
|
+
if (cached) return cached;
|
|
42895
|
+
let baseBranch = "main";
|
|
42896
|
+
try {
|
|
42897
|
+
const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
42898
|
+
if (stdout.trim()) baseBranch = stdout.trim();
|
|
42899
|
+
} catch {
|
|
42900
|
+
}
|
|
42901
|
+
let baseRef = "HEAD";
|
|
42902
|
+
try {
|
|
42903
|
+
await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
42904
|
+
} catch {
|
|
42905
|
+
}
|
|
42906
|
+
try {
|
|
42907
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
|
|
42908
|
+
baseRef = stdout.trim();
|
|
42909
|
+
} catch {
|
|
42910
|
+
try {
|
|
42911
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
42912
|
+
baseRef = stdout.trim();
|
|
42913
|
+
} catch {
|
|
42914
|
+
}
|
|
42915
|
+
}
|
|
42916
|
+
repoRootBaseRef.set(repoRoot, baseRef);
|
|
42917
|
+
return baseRef;
|
|
42918
|
+
};
|
|
42919
|
+
const changeAreas = [];
|
|
42920
|
+
for (const node of targetNodes) {
|
|
42921
|
+
const repoRoot = resolveRepoRootFor(node);
|
|
42922
|
+
let branch = typeof node.worktreeBranch === "string" ? node.worktreeBranch : "";
|
|
42923
|
+
try {
|
|
42924
|
+
const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
42925
|
+
if (stdout.trim()) branch = stdout.trim();
|
|
42926
|
+
} catch {
|
|
42927
|
+
}
|
|
42928
|
+
if (!repoRoot || !branch) {
|
|
42929
|
+
changeAreas.push({
|
|
42930
|
+
nodeId: node.id,
|
|
42931
|
+
workspace: node.workspace,
|
|
42932
|
+
branch: branch || "(unknown)",
|
|
42933
|
+
changedTopLevelPaths: [],
|
|
42934
|
+
changedFiles: [],
|
|
42935
|
+
touchedSubmodulePaths: [],
|
|
42936
|
+
touchesSubmodule: false,
|
|
42937
|
+
aheadCount: 0,
|
|
42938
|
+
error: !repoRoot ? "source repoRoot not found" : "branch not resolved"
|
|
42939
|
+
});
|
|
42940
|
+
continue;
|
|
42941
|
+
}
|
|
42942
|
+
if (!submodulePathsByRepoRoot.has(repoRoot)) {
|
|
42943
|
+
let subPaths = /* @__PURE__ */ new Set();
|
|
42944
|
+
try {
|
|
42945
|
+
const { stdout } = await execFileAsync4("git", ["config", "--file", ".gitmodules", "--get-regexp", "path"], { cwd: repoRoot, encoding: "utf8" });
|
|
42946
|
+
for (const line of stdout.split("\n")) {
|
|
42947
|
+
const trimmed = line.trim();
|
|
42948
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
42949
|
+
if (spaceIdx === -1) continue;
|
|
42950
|
+
const value = trimmed.slice(spaceIdx + 1).trim();
|
|
42951
|
+
if (value) subPaths.add(value);
|
|
42952
|
+
}
|
|
42953
|
+
} catch {
|
|
42954
|
+
subPaths = /* @__PURE__ */ new Set();
|
|
42955
|
+
}
|
|
42956
|
+
submodulePathsByRepoRoot.set(repoRoot, subPaths);
|
|
42957
|
+
}
|
|
42958
|
+
const baseRef = await resolveBaseRef(repoRoot);
|
|
42959
|
+
let branchRef = branch;
|
|
42960
|
+
try {
|
|
42961
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
42962
|
+
branchRef = stdout.trim() || branch;
|
|
42963
|
+
} catch {
|
|
42964
|
+
}
|
|
42965
|
+
changeAreas.push(await analyzeMeshRefineNodeChangeArea({
|
|
42966
|
+
nodeId: node.id,
|
|
42967
|
+
workspace: node.workspace,
|
|
42968
|
+
branch,
|
|
42969
|
+
baseRef,
|
|
42970
|
+
branchRef,
|
|
42971
|
+
diffCwd: node.workspace,
|
|
42972
|
+
submodulePaths: submodulePathsByRepoRoot.get(repoRoot)
|
|
42973
|
+
}));
|
|
42974
|
+
}
|
|
42975
|
+
const ordering = orderMeshRefineBatchNodes(changeAreas);
|
|
42976
|
+
const orderedNodes = ordering.order.map((nodeId) => targetNodes.find((n) => n.id === nodeId || n.nodeId === nodeId)).filter((n) => !!n);
|
|
42977
|
+
const dryRun = args?.dryRun !== false && args?.execute !== true;
|
|
42978
|
+
if (dryRun) {
|
|
42979
|
+
return {
|
|
42980
|
+
success: true,
|
|
42981
|
+
batch: true,
|
|
42982
|
+
dryRun: true,
|
|
42983
|
+
nodeCount: orderedNodes.length,
|
|
42984
|
+
order: ordering.order,
|
|
42985
|
+
orderingRationale: ordering.rationale,
|
|
42986
|
+
changeAreas: ordering.changeAreas,
|
|
42987
|
+
plan: orderedNodes.map((node) => ({
|
|
42988
|
+
nodeId: node.id,
|
|
42989
|
+
workspace: node.workspace,
|
|
42990
|
+
validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
|
|
42991
|
+
mergeWillRun: false
|
|
42992
|
+
})),
|
|
42993
|
+
note: "Dry-run: no validation, rebase, or merge was executed. Re-run with execute=true to converge nodes in this order."
|
|
42994
|
+
};
|
|
42995
|
+
}
|
|
42996
|
+
const results = [];
|
|
42997
|
+
for (const node of orderedNodes) {
|
|
42998
|
+
let result;
|
|
42999
|
+
try {
|
|
43000
|
+
result = await this.executeMeshRefineNodeSynchronously(meshId, node.id, args);
|
|
43001
|
+
} catch (e) {
|
|
43002
|
+
result = { success: false, error: e?.message || String(e) };
|
|
43003
|
+
}
|
|
43004
|
+
const code = typeof result.code === "string" ? result.code : "";
|
|
43005
|
+
let convergence;
|
|
43006
|
+
if (code === "already_merged" && result.alreadyMergedViaOtherPath) {
|
|
43007
|
+
convergence = "skipped_patch_equivalent";
|
|
43008
|
+
} else if (result.success === true) {
|
|
43009
|
+
convergence = "merged_to_main";
|
|
43010
|
+
} else if (code === "merge_failed") {
|
|
43011
|
+
convergence = "not_mergeable";
|
|
43012
|
+
} else {
|
|
43013
|
+
convergence = "blocked_review";
|
|
43014
|
+
}
|
|
43015
|
+
const fbcs = result.finalBranchConvergenceState && typeof result.finalBranchConvergenceState === "object" ? result.finalBranchConvergenceState : void 0;
|
|
43016
|
+
const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s) => s.status === "failed").map((s) => s.stage).filter(Boolean).pop() : void 0;
|
|
43017
|
+
results.push({
|
|
43018
|
+
nodeId: node.id,
|
|
43019
|
+
workspace: node.workspace,
|
|
43020
|
+
convergence,
|
|
43021
|
+
...code ? { code } : {},
|
|
43022
|
+
...typeof result.blockedReason === "string" ? { reason: result.blockedReason } : {},
|
|
43023
|
+
...stage ? { stage } : {},
|
|
43024
|
+
...typeof result.error === "string" ? { error: result.error } : {},
|
|
43025
|
+
...fbcs ? { finalBranchConvergenceState: fbcs } : {}
|
|
43026
|
+
});
|
|
43027
|
+
}
|
|
43028
|
+
const summary = {
|
|
43029
|
+
merged: results.filter((r) => r.convergence === "merged_to_main").length,
|
|
43030
|
+
skipped: results.filter((r) => r.convergence === "skipped_patch_equivalent").length,
|
|
43031
|
+
blocked: results.filter((r) => r.convergence === "blocked_review").length,
|
|
43032
|
+
notMergeable: results.filter((r) => r.convergence === "not_mergeable").length
|
|
43033
|
+
};
|
|
43034
|
+
const allConverged = summary.blocked === 0 && summary.notMergeable === 0;
|
|
43035
|
+
return {
|
|
43036
|
+
success: true,
|
|
43037
|
+
batch: true,
|
|
43038
|
+
dryRun: false,
|
|
43039
|
+
nodeCount: orderedNodes.length,
|
|
43040
|
+
order: ordering.order,
|
|
43041
|
+
orderingRationale: ordering.rationale,
|
|
43042
|
+
summary,
|
|
43043
|
+
allConverged,
|
|
43044
|
+
results,
|
|
43045
|
+
...allConverged ? {} : {
|
|
43046
|
+
nextStep: "Resolve blocked_review / not_mergeable nodes manually (see per-node code/stage/error), then re-run mesh_refine_batch for the remaining nodes."
|
|
43047
|
+
}
|
|
43048
|
+
};
|
|
43049
|
+
}
|
|
42644
43050
|
async finishMeshRefineJob(handle, args) {
|
|
42645
43051
|
const key = this.buildRefineJobKey(handle.meshId, handle.targetNodeId);
|
|
42646
43052
|
let result;
|
|
@@ -44184,6 +44590,12 @@ ${tail}` : ""
|
|
|
44184
44590
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
44185
44591
|
return this.startMeshRefineJob(meshId, nodeId, args);
|
|
44186
44592
|
}
|
|
44593
|
+
case "batch_refine_mesh_nodes": {
|
|
44594
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
44595
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
44596
|
+
const requestedNodeIds = Array.isArray(args?.nodeIds) ? args.nodeIds.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim()) : void 0;
|
|
44597
|
+
return this.batchRefineMeshNodes(meshId, requestedNodeIds, args);
|
|
44598
|
+
}
|
|
44187
44599
|
case "remove_mesh_node": {
|
|
44188
44600
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
44189
44601
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
@@ -54140,7 +54552,9 @@ var V1_CONTRACT_VERSION = "1.0.0";
|
|
|
54140
54552
|
ProviderCliAdapter,
|
|
54141
54553
|
ProviderInstanceManager,
|
|
54142
54554
|
ProviderLoader,
|
|
54555
|
+
RECENT_TERMINAL_REFINE_CAP,
|
|
54143
54556
|
RawTerminalAttachment,
|
|
54557
|
+
STALE_TERMINAL_REFINE_WINDOW_MS,
|
|
54144
54558
|
STANDALONE_CDP_SCAN_INTERVAL_MS,
|
|
54145
54559
|
SessionHostPtyTransportFactory,
|
|
54146
54560
|
TerminalAdapter,
|
|
@@ -54348,6 +54762,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
|
|
|
54348
54762
|
reconcileDirectDispatchCompletionFromTranscript,
|
|
54349
54763
|
recordCompletionConflict,
|
|
54350
54764
|
recordDebugTrace,
|
|
54765
|
+
recordDirectDispatchTask,
|
|
54351
54766
|
recordMeshToolCall,
|
|
54352
54767
|
registerExtensionProviders,
|
|
54353
54768
|
registerMeshCoordinator,
|
|
@@ -54384,6 +54799,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
|
|
|
54384
54799
|
startLocalIpcServer,
|
|
54385
54800
|
suggestMeshRefineConfig,
|
|
54386
54801
|
summarizeGitStatus,
|
|
54802
|
+
summarizeMeshAsyncRefineJobs,
|
|
54387
54803
|
summarizeMeshMission,
|
|
54388
54804
|
summarizeMissionTasks,
|
|
54389
54805
|
triggerMeshQueue,
|