@adhdev/daemon-standalone 0.9.82-rc.553 → 0.9.82-rc.555
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/index.js
CHANGED
|
@@ -30217,10 +30217,10 @@ var require_dist3 = __commonJS({
|
|
|
30217
30217
|
}
|
|
30218
30218
|
function getDaemonBuildInfo() {
|
|
30219
30219
|
if (cached2) return cached2;
|
|
30220
|
-
const commit = readInjected(true ? "
|
|
30221
|
-
const commitShort = readInjected(true ? "
|
|
30222
|
-
const version2 = readInjected(true ? "0.9.82-rc.
|
|
30223
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
30220
|
+
const commit = readInjected(true ? "45a19b2d93612559899332379b6953a02a4b4ccb" : void 0) ?? "unknown";
|
|
30221
|
+
const commitShort = readInjected(true ? "45a19b2d" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
30222
|
+
const version2 = readInjected(true ? "0.9.82-rc.555" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
30223
|
+
const builtAt = readInjected(true ? "2026-07-17T08:35:04.385Z" : void 0);
|
|
30224
30224
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
30225
30225
|
return cached2;
|
|
30226
30226
|
}
|
|
@@ -73511,6 +73511,12 @@ ${body}
|
|
|
73511
73511
|
const pos = this.screen.getCursorPosition();
|
|
73512
73512
|
return { row: pos.row, col: pos.col };
|
|
73513
73513
|
}
|
|
73514
|
+
/** Current terminal geometry (columns × rows). Tracked here rather than
|
|
73515
|
+
* read off the screen buffer so a resize is reflected immediately, before
|
|
73516
|
+
* the next repaint. Consumed by the mesh_read_terminal viewport read. */
|
|
73517
|
+
getScreenSize() {
|
|
73518
|
+
return { cols: this.cols, rows: this.rows };
|
|
73519
|
+
}
|
|
73514
73520
|
send_keys(text) {
|
|
73515
73521
|
this.recordEvent("input", capPreview(escapeControl(text)), text.length);
|
|
73516
73522
|
this.pty?.write(text);
|
|
@@ -73843,6 +73849,9 @@ ${body}
|
|
|
73843
73849
|
getScreen() {
|
|
73844
73850
|
return this.adapter.snapshot();
|
|
73845
73851
|
}
|
|
73852
|
+
getScreenSize() {
|
|
73853
|
+
return this.adapter.getScreenSize();
|
|
73854
|
+
}
|
|
73846
73855
|
/** Scrollback-inclusive screen as line array — used only for modal/button
|
|
73847
73856
|
* content extraction so a tall prompt's off-screen anchors stay matchable.
|
|
73848
73857
|
* Falls back to the viewport snapshot if scrollback read is unavailable. */
|
|
@@ -75869,6 +75878,8 @@ ${body}
|
|
|
75869
75878
|
return out;
|
|
75870
75879
|
}
|
|
75871
75880
|
var fs20 = __toESM2(require("fs"));
|
|
75881
|
+
var import_node_crypto4 = require("crypto");
|
|
75882
|
+
init_provider_cli_shared();
|
|
75872
75883
|
init_logger();
|
|
75873
75884
|
function stripAnsi3(text) {
|
|
75874
75885
|
return String(text || "").replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
@@ -76047,6 +76058,105 @@ ${body}
|
|
|
76047
76058
|
isReady() {
|
|
76048
76059
|
return this.spawned && !this.exited;
|
|
76049
76060
|
}
|
|
76061
|
+
// Process liveness for the MESH-STALL-WATCH watchdog (checkMeshWorkerStall).
|
|
76062
|
+
// The spec path drives the child through the transport/driver rather than a
|
|
76063
|
+
// directly-held ptyProcess handle, so liveness is tracked by the spawned/exited
|
|
76064
|
+
// lifecycle flags — the same pair isReady() uses. A spawned, not-yet-exited
|
|
76065
|
+
// session is alive. ProviderCliAdapter exposes the equivalent via `ptyProcess !== null`.
|
|
76066
|
+
isAlive() {
|
|
76067
|
+
return this.spawned && !this.exited;
|
|
76068
|
+
}
|
|
76069
|
+
// MESH-READ-TERMINAL / MESH-SEND-KEYS byte caps — same envelope as
|
|
76070
|
+
// ProviderCliAdapter (32KiB default view, 64KiB absolute hard cap). Bytes,
|
|
76071
|
+
// not chars: a multi-byte-glyph screen can exceed an MCP payload cap while
|
|
76072
|
+
// the char count still looks safe.
|
|
76073
|
+
static TERMINAL_SNAPSHOT_DEFAULT_MAX_BYTES = 32 * 1024;
|
|
76074
|
+
static TERMINAL_SNAPSHOT_ABSOLUTE_MAX_BYTES = 64 * 1024;
|
|
76075
|
+
/**
|
|
76076
|
+
* MESH-READ-TERMINAL (feature 2: RAW terminal read). Least-privilege read
|
|
76077
|
+
* of the CURRENT rendered viewport for mesh_read_terminal on the spec path
|
|
76078
|
+
* (claude-cli / antigravity / codex-cli — the native-source providers that
|
|
76079
|
+
* route through SpecCliAdapter). Mirrors ProviderCliAdapter.getTerminalScreenSnapshot:
|
|
76080
|
+
* - returns ONLY the driver's current viewport snapshot, the cursor
|
|
76081
|
+
* position and the terminal geometry — NO scrollback, NO parser/FSM
|
|
76082
|
+
* state, NO debug buffers;
|
|
76083
|
+
* - the payload is byte-bounded (UTF-8) with bottom-tail preservation so a
|
|
76084
|
+
* screen of multi-byte glyphs can never exceed the MCP payload cap;
|
|
76085
|
+
* - `hash` is over the FULL untruncated viewport so a caller can detect a
|
|
76086
|
+
* screen change across polls even when the returned text was truncated.
|
|
76087
|
+
*
|
|
76088
|
+
* SECURITY: the raw viewport can carry tokens / command args / env / user
|
|
76089
|
+
* data. Callers MUST gate this on mesh ownership and MUST NOT log the text.
|
|
76090
|
+
*/
|
|
76091
|
+
getTerminalScreenSnapshot(maxBytes = _SpecCliAdapter.TERMINAL_SNAPSHOT_DEFAULT_MAX_BYTES) {
|
|
76092
|
+
const cap = Math.min(
|
|
76093
|
+
_SpecCliAdapter.TERMINAL_SNAPSHOT_ABSOLUTE_MAX_BYTES,
|
|
76094
|
+
Math.max(1024, Math.floor(maxBytes) || _SpecCliAdapter.TERMINAL_SNAPSHOT_DEFAULT_MAX_BYTES)
|
|
76095
|
+
);
|
|
76096
|
+
let rawViewport = "";
|
|
76097
|
+
try {
|
|
76098
|
+
rawViewport = this.driver.snapshot() || "";
|
|
76099
|
+
} catch {
|
|
76100
|
+
rawViewport = "";
|
|
76101
|
+
}
|
|
76102
|
+
let cursor = { row: 0, col: 0 };
|
|
76103
|
+
try {
|
|
76104
|
+
cursor = this.driver.getCursorPosition();
|
|
76105
|
+
} catch {
|
|
76106
|
+
}
|
|
76107
|
+
let size = { cols: 0, rows: 0 };
|
|
76108
|
+
try {
|
|
76109
|
+
size = this.driver.getScreenSize?.() ?? size;
|
|
76110
|
+
} catch {
|
|
76111
|
+
}
|
|
76112
|
+
const truncation = truncateToByteTailByLine(rawViewport, cap);
|
|
76113
|
+
const hash2 = (0, import_node_crypto4.createHash)("sha256").update(rawViewport, "utf8").digest("hex").slice(0, 16);
|
|
76114
|
+
return {
|
|
76115
|
+
text: truncation.text,
|
|
76116
|
+
cursor: { col: cursor.col, row: cursor.row },
|
|
76117
|
+
cols: size.cols,
|
|
76118
|
+
rows: size.rows,
|
|
76119
|
+
truncated: truncation.truncated,
|
|
76120
|
+
originalBytes: truncation.originalBytes,
|
|
76121
|
+
returnedBytes: truncation.returnedBytes,
|
|
76122
|
+
hash: hash2
|
|
76123
|
+
};
|
|
76124
|
+
}
|
|
76125
|
+
/**
|
|
76126
|
+
* MESH-SEND-KEYS (feature 3: key injection). Inject a STRUCTURED key
|
|
76127
|
+
* sequence into the spec-driven PTY for mesh_send_keys. Mirrors
|
|
76128
|
+
* ProviderCliAdapter.injectKeys' modal fail-closed guard, then writes the
|
|
76129
|
+
* whole encoded sequence in ONE pty_write dispatch (text+ENTER is a single
|
|
76130
|
+
* contiguous string, so a submit key can never be separated from the text
|
|
76131
|
+
* it submits).
|
|
76132
|
+
*
|
|
76133
|
+
* The spec path drives the child through the FsmDriver, not a directly-held
|
|
76134
|
+
* ptyProcess — there is no adapter-level echo-gate/submit-retry FIFO to race
|
|
76135
|
+
* against here (the driver serializes its own writes), so the only guard is
|
|
76136
|
+
* the modal fail-closed: a NON-destructive injection into an actionable
|
|
76137
|
+
* approval modal is refused (use mesh_approve) unless explicitly overridden.
|
|
76138
|
+
* A destructive ESC/CTRL_C dismisses rather than confirms, so it is allowed
|
|
76139
|
+
* past this gate (the tool layer owns the destructive double-gate + audit).
|
|
76140
|
+
* This method NEVER logs the literal text — only key enums / byte length.
|
|
76141
|
+
*/
|
|
76142
|
+
async injectKeys(items, opts = {}) {
|
|
76143
|
+
if (!this.spawned || this.exited) throw new Error(`${this.cliName} is not running`);
|
|
76144
|
+
const encoded = encodeMeshSendKeys(items);
|
|
76145
|
+
const modalActive = this.latestState?.status === "approval";
|
|
76146
|
+
if (modalActive && !encoded.hasDestructive && !opts.allowModalOverride) {
|
|
76147
|
+
LOG2.warn("SpecAdapter", `[${this.cliType}] send_keys refused (actionable_modal): keys=${encoded.keys.join(",")} \u2014 use mesh_approve`);
|
|
76148
|
+
return { ok: false, refused: "actionable_modal", keys: encoded.keys, hasDestructive: encoded.hasDestructive };
|
|
76149
|
+
}
|
|
76150
|
+
this.driver.dispatch({ kind: "pty_write", data: encoded.sequence });
|
|
76151
|
+
LOG2.info("SpecAdapter", `[${this.cliType}] send_keys injected keys=${encoded.keys.join(",") || "(text-only)"} bytes=${Buffer.byteLength(encoded.sequence, "utf8")} destructive=${encoded.hasDestructive}`);
|
|
76152
|
+
return {
|
|
76153
|
+
ok: true,
|
|
76154
|
+
keys: encoded.keys,
|
|
76155
|
+
hasDestructive: encoded.hasDestructive,
|
|
76156
|
+
submits: encoded.submits,
|
|
76157
|
+
bytes: Buffer.byteLength(encoded.sequence, "utf8")
|
|
76158
|
+
};
|
|
76159
|
+
}
|
|
76050
76160
|
setOnStatusChange(cb) {
|
|
76051
76161
|
this.statusCallback = cb;
|
|
76052
76162
|
}
|
|
@@ -78290,6 +78400,25 @@ ${body}
|
|
|
78290
78400
|
}
|
|
78291
78401
|
return "";
|
|
78292
78402
|
}
|
|
78403
|
+
/**
|
|
78404
|
+
* NOTIF Defect-B: the final assistant summary this instance ALREADY parsed and
|
|
78405
|
+
* cached for the current turn (lastCompletionSummary), if any. The evidence
|
|
78406
|
+
* probe (completionFinalAssistantEvidence) is a POINT-SAMPLE: on a native-source
|
|
78407
|
+
* provider (antigravity) the parsed screen and the native transcript can both
|
|
78408
|
+
* momentarily yield no in-turn final assistant at the exact instant the
|
|
78409
|
+
* completion gate fires — source='unavailable', missingEvidence=true — even
|
|
78410
|
+
* though a prior poll already read the real answer off native-history and cached
|
|
78411
|
+
* it here (the same value mesh_read_chat.summary shows). Consulting the cache at
|
|
78412
|
+
* emit time lets that already-secured summary count as evidence, so the completion
|
|
78413
|
+
* notification carries the answer instead of completion_diagnostic=missing_final_assistant
|
|
78414
|
+
* with an empty summary. Returns '' when the cache is empty or was reset by the
|
|
78415
|
+
* next turn (see lastCompletionSummary = null on onTurnStarted).
|
|
78416
|
+
*/
|
|
78417
|
+
cachedCompletionSummaryContent() {
|
|
78418
|
+
const cached3 = this.lastCompletionSummary;
|
|
78419
|
+
const content = typeof cached3?.content === "string" ? cached3.content.trim() : "";
|
|
78420
|
+
return content;
|
|
78421
|
+
}
|
|
78293
78422
|
completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
|
|
78294
78423
|
const turnClosed = !this.hasAdapterPendingResponse();
|
|
78295
78424
|
if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
|
|
@@ -78353,12 +78482,19 @@ ${body}
|
|
|
78353
78482
|
const lastVisibleRole = typeof lastVisible?.role === "string" ? lastVisible.role.trim().toLowerCase() : null;
|
|
78354
78483
|
const lastVisibleKind = typeof lastVisible?.kind === "string" ? lastVisible.kind : null;
|
|
78355
78484
|
const lastVisibleContentLength = lastVisible ? flattenContent(lastVisible.content).trim().length : 0;
|
|
78485
|
+
const cachedSummary = evidence.present ? "" : this.cachedCompletionSummaryContent();
|
|
78486
|
+
const creditedFromCache = !evidence.present && cachedSummary.length > 0;
|
|
78487
|
+
const finalAssistantPresent = evidence.present || creditedFromCache;
|
|
78488
|
+
const finalAssistantEvidenceSource = evidence.present ? evidence.source : creditedFromCache ? "cached-summary" : evidence.source;
|
|
78489
|
+
const clearMissingBlock = creditedFromCache && args.blockReason === "missing_final_assistant";
|
|
78490
|
+
const effectiveBlockReason = clearMissingBlock ? void 0 : args.blockReason;
|
|
78356
78491
|
return {
|
|
78357
78492
|
providerType: this.type,
|
|
78358
78493
|
sessionId: this.instanceId,
|
|
78359
78494
|
providerSessionId: this.providerSessionId || null,
|
|
78360
78495
|
workspace: this.workingDir,
|
|
78361
|
-
blockReason:
|
|
78496
|
+
...effectiveBlockReason ? { blockReason: effectiveBlockReason } : {},
|
|
78497
|
+
...clearMissingBlock ? { originalBlockReason: args.blockReason } : {},
|
|
78362
78498
|
emittedAfterFinalizationTimeout: args.emittedAfterFinalizationTimeout,
|
|
78363
78499
|
waitedMs: args.waitedMs,
|
|
78364
78500
|
maxWaitMs: COMPLETED_FINALIZATION_MAX_WAIT_MS,
|
|
@@ -78366,8 +78502,9 @@ ${body}
|
|
|
78366
78502
|
latestVisibleStatus: args.latestVisibleStatus,
|
|
78367
78503
|
parsedStatus: typeof parsed?.status === "string" ? parsed.status : parseError ? "parse_error" : "unknown",
|
|
78368
78504
|
parseError: parseError || void 0,
|
|
78369
|
-
finalAssistantPresent
|
|
78370
|
-
|
|
78505
|
+
finalAssistantPresent,
|
|
78506
|
+
finalAssistantFromCachedSummary: !evidence.present && cachedSummary.length > 0,
|
|
78507
|
+
finalAssistantEvidenceSource,
|
|
78371
78508
|
visibleMessageCount: visibleMessages.length,
|
|
78372
78509
|
lastVisibleRole,
|
|
78373
78510
|
lastVisibleKind,
|
|
@@ -78561,6 +78698,7 @@ ${body}
|
|
|
78561
78698
|
*/
|
|
78562
78699
|
getTerminalScreenSnapshot(maxBytes) {
|
|
78563
78700
|
if (!this.isMeshWorkerSession()) return null;
|
|
78701
|
+
if (typeof this.adapter.getTerminalScreenSnapshot !== "function") return null;
|
|
78564
78702
|
return this.adapter.getTerminalScreenSnapshot(maxBytes);
|
|
78565
78703
|
}
|
|
78566
78704
|
/**
|
|
@@ -78579,6 +78717,9 @@ ${body}
|
|
|
78579
78717
|
if (!this.isMeshWorkerSession()) {
|
|
78580
78718
|
return { ok: false, refused: "not_mesh_worker", keys: [], hasDestructive: false };
|
|
78581
78719
|
}
|
|
78720
|
+
if (typeof this.adapter.injectKeys !== "function") {
|
|
78721
|
+
return { ok: false, refused: "unsupported", keys: [], hasDestructive: false };
|
|
78722
|
+
}
|
|
78582
78723
|
return this.adapter.injectKeys(items, opts);
|
|
78583
78724
|
}
|
|
78584
78725
|
/**
|
|
@@ -78611,7 +78752,7 @@ ${body}
|
|
|
78611
78752
|
this.meshStallEmittedForAnchor = false;
|
|
78612
78753
|
return;
|
|
78613
78754
|
}
|
|
78614
|
-
if (!this.adapter.isAlive()) {
|
|
78755
|
+
if (typeof this.adapter.isAlive === "function" && !this.adapter.isAlive()) {
|
|
78615
78756
|
this.meshStallAnchorAt = -1;
|
|
78616
78757
|
this.meshStallEmittedForAnchor = false;
|
|
78617
78758
|
return;
|
|
@@ -78968,7 +79109,13 @@ ${body}
|
|
|
78968
79109
|
// delegated session's inbox preview blank — or, for a LOCAL worktree session,
|
|
78969
79110
|
// stuck on the dispatched user task. If the parser DID surface assistant text,
|
|
78970
79111
|
// prefer it; only fall back to '' when no assistant summary can be derived.
|
|
78971
|
-
|
|
79112
|
+
// NOTIF Defect-B: completionFinalSummary is a point-sample of native-history/
|
|
79113
|
+
// screen at THIS instant; on a native-source provider (antigravity) it can be
|
|
79114
|
+
// empty at the forced-emit instant even though a prior poll already cached the
|
|
79115
|
+
// real answer (lastCompletionSummary). Fall back to the cache so the notification
|
|
79116
|
+
// carries the summary that mesh_read_chat.summary already shows — consistent with
|
|
79117
|
+
// completionDiagnostic.finalAssistantPresent being credited from the same cache.
|
|
79118
|
+
finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages, pending.turnStartedAt) || this.cachedCompletionSummaryContent() || (blockReason.startsWith("parsed_status:") ? "" : void 0),
|
|
78972
79119
|
completionDiagnostic
|
|
78973
79120
|
});
|
|
78974
79121
|
this.completedDebouncePending = null;
|
|
@@ -90584,6 +90731,150 @@ ${e?.stderr || ""}`
|
|
|
90584
90731
|
};
|
|
90585
90732
|
}
|
|
90586
90733
|
}
|
|
90734
|
+
async function classifyPatchEquivalenceFailure(repoRoot, baseHead, branchHead, summary, options = {}) {
|
|
90735
|
+
const targetBaseRef = options.targetBaseRef || baseHead;
|
|
90736
|
+
const autoPublish = options.autoPublishSubmoduleMainCommits;
|
|
90737
|
+
const evidence = {
|
|
90738
|
+
baseHead,
|
|
90739
|
+
branchHead,
|
|
90740
|
+
mergeBase: summary.mergeBase,
|
|
90741
|
+
expectedPatchId: summary.expectedPatchId,
|
|
90742
|
+
actualPatchId: summary.actualPatchId,
|
|
90743
|
+
patchIdEqual: !!summary.expectedPatchId && summary.expectedPatchId === summary.actualPatchId,
|
|
90744
|
+
...autoPublish !== void 0 ? { autoPublishSubmoduleMainCommits: autoPublish } : {}
|
|
90745
|
+
};
|
|
90746
|
+
try {
|
|
90747
|
+
const git = (args) => (0, import_node_child_process6.execFileSync)(GIT2, args, {
|
|
90748
|
+
cwd: repoRoot,
|
|
90749
|
+
encoding: "utf8",
|
|
90750
|
+
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
|
|
90751
|
+
windowsHide: true
|
|
90752
|
+
});
|
|
90753
|
+
const gitOk = (args) => {
|
|
90754
|
+
try {
|
|
90755
|
+
git(args);
|
|
90756
|
+
return true;
|
|
90757
|
+
} catch {
|
|
90758
|
+
return false;
|
|
90759
|
+
}
|
|
90760
|
+
};
|
|
90761
|
+
let ahead = 0;
|
|
90762
|
+
let behind = 0;
|
|
90763
|
+
try {
|
|
90764
|
+
const out = git(["rev-list", "--left-right", "--count", `${targetBaseRef}...${branchHead}`]).trim();
|
|
90765
|
+
const [left, right] = out.split(/\s+/).map((n) => Number.parseInt(n, 10));
|
|
90766
|
+
behind = Number.isFinite(left) ? left : 0;
|
|
90767
|
+
ahead = Number.isFinite(right) ? right : 0;
|
|
90768
|
+
} catch {
|
|
90769
|
+
}
|
|
90770
|
+
evidence.ahead = ahead;
|
|
90771
|
+
evidence.behind = behind;
|
|
90772
|
+
const baseIsAncestor = gitOk(["merge-base", "--is-ancestor", targetBaseRef, branchHead]);
|
|
90773
|
+
evidence.baseDiverged = !baseIsAncestor;
|
|
90774
|
+
let diffStat = "";
|
|
90775
|
+
try {
|
|
90776
|
+
if (summary.mergedTree) {
|
|
90777
|
+
diffStat = git(["diff", "--stat", baseHead, summary.mergedTree]).trim().split("\n").filter(Boolean).slice(-1)[0] || "";
|
|
90778
|
+
} else {
|
|
90779
|
+
diffStat = git(["diff", "--stat", baseHead, branchHead]).trim().split("\n").filter(Boolean).slice(-1)[0] || "";
|
|
90780
|
+
}
|
|
90781
|
+
} catch {
|
|
90782
|
+
}
|
|
90783
|
+
if (diffStat) evidence.diffStat = diffStat;
|
|
90784
|
+
const submoduleGitlinks = [];
|
|
90785
|
+
try {
|
|
90786
|
+
const nameStatus = git(["diff", "--name-only", "--diff-filter=d", baseHead, branchHead]).trim();
|
|
90787
|
+
const changedPaths = nameStatus ? nameStatus.split("\n").map((p) => p.trim()).filter(Boolean) : [];
|
|
90788
|
+
for (const p of changedPaths) {
|
|
90789
|
+
let baseCommit;
|
|
90790
|
+
let branchCommit;
|
|
90791
|
+
try {
|
|
90792
|
+
const baseLs = git(["ls-tree", baseHead, "--", p]).trim();
|
|
90793
|
+
const branchLs = git(["ls-tree", branchHead, "--", p]).trim();
|
|
90794
|
+
const isGitlink = /(^|\s)160000\s/.test(baseLs) || /(^|\s)160000\s/.test(branchLs);
|
|
90795
|
+
if (!isGitlink) continue;
|
|
90796
|
+
baseCommit = baseLs.split(/\s+/)[2];
|
|
90797
|
+
branchCommit = branchLs.split(/\s+/)[2];
|
|
90798
|
+
} catch {
|
|
90799
|
+
continue;
|
|
90800
|
+
}
|
|
90801
|
+
const submoduleRepo = (0, import_path14.join)(repoRoot, p);
|
|
90802
|
+
let fastForward;
|
|
90803
|
+
let reachableFromOriginMain;
|
|
90804
|
+
if (branchCommit) {
|
|
90805
|
+
if (baseCommit) {
|
|
90806
|
+
fastForward = execGitOk(submoduleRepo, ["merge-base", "--is-ancestor", baseCommit, branchCommit]);
|
|
90807
|
+
}
|
|
90808
|
+
reachableFromOriginMain = execGitOk(submoduleRepo, ["merge-base", "--is-ancestor", branchCommit, "refs/remotes/origin/main"]);
|
|
90809
|
+
}
|
|
90810
|
+
submoduleGitlinks.push({ path: p, baseCommit, branchCommit, fastForward, reachableFromOriginMain });
|
|
90811
|
+
}
|
|
90812
|
+
} catch {
|
|
90813
|
+
}
|
|
90814
|
+
if (submoduleGitlinks.length) evidence.submoduleGitlinks = submoduleGitlinks;
|
|
90815
|
+
const gitlinkFf = summary.gitlinkTrivialFastForward;
|
|
90816
|
+
const noResidualDiff = !evidence.diffStat && (!summary.actualPatchId || summary.actualPatchId === "");
|
|
90817
|
+
if (ahead === 0 && behind === 0 && noResidualDiff) {
|
|
90818
|
+
return {
|
|
90819
|
+
detailedReason: "already_converged",
|
|
90820
|
+
detailedReasonDescription: "Branch is already identical to the target base (ahead 0, behind 0, no residual diff); the merge would be a no-op.",
|
|
90821
|
+
recommendedAction: "Treat as already converged \u2014 no merge needed. Verify with `git range-diff` / patch-id, then mark the branch merged (or clean up the worktree).",
|
|
90822
|
+
evidence
|
|
90823
|
+
};
|
|
90824
|
+
}
|
|
90825
|
+
const unreachable = submoduleGitlinks.filter((g) => g.reachableFromOriginMain === false);
|
|
90826
|
+
if (unreachable.length > 0) {
|
|
90827
|
+
const paths = unreachable.map((g) => g.path).join(", ");
|
|
90828
|
+
return {
|
|
90829
|
+
detailedReason: "submodule_unreachable",
|
|
90830
|
+
detailedReasonDescription: `Submodule gitlink commit(s) not reachable from submodule origin/main (publish needed): ${paths}.`,
|
|
90831
|
+
recommendedAction: `Publish the submodule commit(s) to submodule origin/main, then retry mesh_refine_node (policy allowAutoPublishSubmoduleMainCommits=${autoPublish === void 0 ? "unknown" : autoPublish}).`,
|
|
90832
|
+
evidence
|
|
90833
|
+
};
|
|
90834
|
+
}
|
|
90835
|
+
const changedGitlinks = submoduleGitlinks.length > 0;
|
|
90836
|
+
const allGitlinksFf = changedGitlinks && submoduleGitlinks.every((g) => g.fastForward === true);
|
|
90837
|
+
const gateSawUnresolvedGitlinkFf = gitlinkFf?.resolved === false && Array.isArray(gitlinkFf.gitlinks) && gitlinkFf.gitlinks.some((g) => g.fastForward);
|
|
90838
|
+
if (baseIsAncestor && (evidence.patchIdEqual || allGitlinksFf || gateSawUnresolvedGitlinkFf)) {
|
|
90839
|
+
return {
|
|
90840
|
+
detailedReason: "trivial_ff_misjudgment",
|
|
90841
|
+
detailedReasonDescription: "HEAD descends the target base and the patch content matches; the block is a submodule gitlink trivial fast-forward that merge-tree refused, not a real divergence.",
|
|
90842
|
+
recommendedAction: "Converge via the strict fast-forward-only bypass (verify HEAD descends origin/main and patch-id equality, then merge --ff-only) instead of the refine gate.",
|
|
90843
|
+
evidence
|
|
90844
|
+
};
|
|
90845
|
+
}
|
|
90846
|
+
if (!baseIsAncestor) {
|
|
90847
|
+
return {
|
|
90848
|
+
detailedReason: "base_divergence",
|
|
90849
|
+
detailedReasonDescription: `Worktree base has diverged from ${targetBaseRef} (HEAD is not a descendant; ahead ${ahead}, behind ${behind}).`,
|
|
90850
|
+
recommendedAction: `Rebase the branch onto ${targetBaseRef}, then retry mesh_refine_node.`,
|
|
90851
|
+
evidence
|
|
90852
|
+
};
|
|
90853
|
+
}
|
|
90854
|
+
return {
|
|
90855
|
+
detailedReason: "actual_patch_diff",
|
|
90856
|
+
detailedReasonDescription: "The merge introduces content not equivalent to the branch's cumulative patch (expected tree vs actual merge diff differ).",
|
|
90857
|
+
recommendedAction: "Manual review required \u2014 inspect the residual diff; the branch content is not patch-equivalent to a clean merge onto the base.",
|
|
90858
|
+
evidence
|
|
90859
|
+
};
|
|
90860
|
+
} catch (e) {
|
|
90861
|
+
evidence.classifierError = e?.message || String(e);
|
|
90862
|
+
return {
|
|
90863
|
+
detailedReason: "unclassified",
|
|
90864
|
+
detailedReasonDescription: "Patch-equivalence sub-cause could not be classified (git inspection failed); see classifierError.",
|
|
90865
|
+
recommendedAction: "Inspect the refineStages and patchEquivalence summary manually to determine the cause.",
|
|
90866
|
+
evidence
|
|
90867
|
+
};
|
|
90868
|
+
}
|
|
90869
|
+
}
|
|
90870
|
+
function execGitOk(cwd, args) {
|
|
90871
|
+
try {
|
|
90872
|
+
(0, import_node_child_process6.execFileSync)(GIT2, args, { cwd, encoding: "utf8", maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES, windowsHide: true });
|
|
90873
|
+
return true;
|
|
90874
|
+
} catch {
|
|
90875
|
+
return false;
|
|
90876
|
+
}
|
|
90877
|
+
}
|
|
90587
90878
|
async function checkWorktreeChangesPatchEquivalentInRef(repoRoot, ref, worktreeHead) {
|
|
90588
90879
|
const startedAt = Date.now();
|
|
90589
90880
|
try {
|
|
@@ -91901,9 +92192,23 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
91901
92192
|
error: submoduleHintPatchEquivalence.error,
|
|
91902
92193
|
actionableHint: submoduleHintPatchEquivalence.actionableHint
|
|
91903
92194
|
});
|
|
92195
|
+
const classification = await classifyPatchEquivalenceFailure(
|
|
92196
|
+
repoRoot,
|
|
92197
|
+
baseHead,
|
|
92198
|
+
ctx.branchHead,
|
|
92199
|
+
submoduleHintPatchEquivalence,
|
|
92200
|
+
{
|
|
92201
|
+
targetBaseRef: baseHead,
|
|
92202
|
+
autoPublishSubmoduleMainCommits: resolveRefineryAutoPublishSubmoduleMainCommits(ctx.mesh, node.workspace).enabled
|
|
92203
|
+
}
|
|
92204
|
+
);
|
|
91904
92205
|
return { kind: "terminal", result: {
|
|
91905
92206
|
success: false,
|
|
91906
92207
|
code: "patch_equivalence_failed",
|
|
92208
|
+
detailedReason: classification.detailedReason,
|
|
92209
|
+
detailedReasonDescription: classification.detailedReasonDescription,
|
|
92210
|
+
recommendedAction: classification.recommendedAction,
|
|
92211
|
+
evidence: classification.evidence,
|
|
91907
92212
|
convergenceStatus: "blocked_review",
|
|
91908
92213
|
error: "Refinery patch-equivalence preflight failed (submodule gitlink conflict); merge/refine was not attempted.",
|
|
91909
92214
|
branch,
|
|
@@ -92043,7 +92348,7 @@ ${tail}` : ""
|
|
|
92043
92348
|
return { kind: "continue", ctx };
|
|
92044
92349
|
}
|
|
92045
92350
|
async function refinePatchEquivalenceStage(self, ctx) {
|
|
92046
|
-
const { meshId, nodeId, args, repoRoot, baseHead, branch, baseBranch, validationSummary, refineStages } = ctx;
|
|
92351
|
+
const { meshId, nodeId, args, repoRoot, baseHead, branch, baseBranch, mesh, node, validationSummary, refineStages } = ctx;
|
|
92047
92352
|
const branchHead = ctx.branchHead;
|
|
92048
92353
|
const patchEquivalenceStarted = Date.now();
|
|
92049
92354
|
const patchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
@@ -92057,9 +92362,27 @@ ${tail}` : ""
|
|
|
92057
92362
|
if (!patchEquivalence.equivalent) {
|
|
92058
92363
|
const alreadyMergedViaOtherPath = !patchEquivalence.actualPatchId && !!patchEquivalence.expectedPatchId;
|
|
92059
92364
|
if (!alreadyMergedViaOtherPath) {
|
|
92365
|
+
const classification = await classifyPatchEquivalenceFailure(
|
|
92366
|
+
repoRoot,
|
|
92367
|
+
baseHead,
|
|
92368
|
+
branchHead,
|
|
92369
|
+
patchEquivalence,
|
|
92370
|
+
{
|
|
92371
|
+
targetBaseRef: baseHead,
|
|
92372
|
+
autoPublishSubmoduleMainCommits: resolveRefineryAutoPublishSubmoduleMainCommits(mesh, node.workspace).enabled
|
|
92373
|
+
}
|
|
92374
|
+
);
|
|
92375
|
+
recordMeshRefineStage(refineStages, "patch_equivalence_classification", "failed", patchEquivalenceStarted, {
|
|
92376
|
+
detailedReason: classification.detailedReason,
|
|
92377
|
+
recommendedAction: classification.recommendedAction
|
|
92378
|
+
});
|
|
92060
92379
|
return { kind: "terminal", result: {
|
|
92061
92380
|
success: false,
|
|
92062
92381
|
code: "patch_equivalence_failed",
|
|
92382
|
+
detailedReason: classification.detailedReason,
|
|
92383
|
+
detailedReasonDescription: classification.detailedReasonDescription,
|
|
92384
|
+
recommendedAction: classification.recommendedAction,
|
|
92385
|
+
evidence: classification.evidence,
|
|
92063
92386
|
convergenceStatus: "blocked_review",
|
|
92064
92387
|
error: "Refinery patch-equivalence preflight failed; merge/refine was not attempted.",
|
|
92065
92388
|
branch,
|
|
@@ -93166,6 +93489,10 @@ ${e?.stderr || ""}`;
|
|
|
93166
93489
|
};
|
|
93167
93490
|
if (typeof result.error === "string") ctx.error = result.error;
|
|
93168
93491
|
if (typeof result.blockedReason === "string") ctx.blockedReason = result.blockedReason;
|
|
93492
|
+
if (typeof result.detailedReason === "string") ctx.detailedReason = result.detailedReason;
|
|
93493
|
+
if (typeof result.detailedReasonDescription === "string") ctx.detailedReasonDescription = result.detailedReasonDescription;
|
|
93494
|
+
if (typeof result.recommendedAction === "string") ctx.recommendedAction = result.recommendedAction;
|
|
93495
|
+
if (result.evidence && typeof result.evidence === "object") ctx.evidence = result.evidence;
|
|
93169
93496
|
if (stage === "patch_equivalence" && result.patchEquivalence) {
|
|
93170
93497
|
const pe = result.patchEquivalence;
|
|
93171
93498
|
ctx.details = {
|
|
@@ -93173,7 +93500,10 @@ ${e?.stderr || ""}`;
|
|
|
93173
93500
|
actualPatchId: pe.actualPatchId,
|
|
93174
93501
|
status: pe.status,
|
|
93175
93502
|
actionableHint: pe.actionableHint,
|
|
93176
|
-
error: pe.error
|
|
93503
|
+
error: pe.error,
|
|
93504
|
+
...typeof result.detailedReason === "string" ? { detailedReason: result.detailedReason } : {},
|
|
93505
|
+
...typeof result.recommendedAction === "string" ? { recommendedAction: result.recommendedAction } : {},
|
|
93506
|
+
...result.evidence && typeof result.evidence === "object" ? { evidence: result.evidence } : {}
|
|
93177
93507
|
};
|
|
93178
93508
|
}
|
|
93179
93509
|
if (stage === "submodule_reachability" && Array.isArray(result.unreachableSubmoduleCommits)) {
|