@runfusion/fusion 0.77.0-beta.5 → 0.77.0-beta.6
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/bin.js +433 -102
- package/dist/child-process-worker.js +352 -81
- package/dist/client/.vite/manifest.json +261 -261
- package/dist/client/assets/{AgentDetailView-CSK_Rnlk.js → AgentDetailView-oyY72uSo.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-kG44Adcm.js → AgentPermissionPolicyEditor-C3Qo9p1M.js} +1 -1
- package/dist/client/assets/{AgentsView-ClKwKWnU.js → AgentsView-M2MhTIGW.js} +3 -3
- package/dist/client/assets/{ChatView-DoOqgkPX.js → ChatView-zKrUSqiQ.js} +1 -1
- package/dist/client/assets/{CommandCenter-CJxvPbgn.js → CommandCenter-CpcAt6jt.js} +27 -27
- package/dist/client/assets/{DevServerView-C4nyLONX.js → DevServerView-CjTneoG7.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BIvXWIB5.js → DirectoryPicker-BY6V5HLV.js} +1 -1
- package/dist/client/assets/{DocumentsView-DH_aZysM.js → DocumentsView-RDK56-qg.js} +1 -1
- package/dist/client/assets/{EvalsView-klRK-GJQ.js → EvalsView-DKK4ceUW.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-CgBmukIm.js → ExperimentalAgentOnboardingModal-DiBPZVwC.js} +1 -1
- package/dist/client/assets/{GoalsView-ysh3X2x6.js → GoalsView-DPj59B3l.js} +1 -1
- package/dist/client/assets/{InsightsView-BbMjCu7J.js → InsightsView-QLJNkKJp.js} +1 -1
- package/dist/client/assets/{MemoryView-B3IgA8Bx.js → MemoryView-CDCS-1ig.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-CZlIw7C3.js → PiExtensionsManager-DktXXrmF.js} +1 -1
- package/dist/client/assets/{PluginManager-vy0YZ2Nx.js → PluginManager-CQ3iEFMq.js} +1 -1
- package/dist/client/assets/{PullRequestView-DhhsJV8I.js → PullRequestView-CkObT6e3.js} +1 -1
- package/dist/client/assets/{ReportModal-CQMn4i1y.js → ReportModal-B5LTdDgi.js} +1 -1
- package/dist/client/assets/{ResearchView-CVwfs4l3.js → ResearchView-CryFrV8l.js} +1 -1
- package/dist/client/assets/{SecretsView-CrJfHle9.js → SecretsView-9YcNW_fo.js} +1 -1
- package/dist/client/assets/{SessionTerminal-BRI8W1-G.js → SessionTerminal-BWsA9puD.js} +1 -1
- package/dist/client/assets/{SettingsModal-h1A_aFqD.js → SettingsModal-CL6aiXiI.js} +4 -4
- package/dist/client/assets/{SettingsModal-B6U5Umru.js → SettingsModal-llkoaJev.js} +1 -1
- package/dist/client/assets/{SettingsTextareaRow-O7-u9uMC.js → SettingsTextareaRow-B_Legu-A.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-nXfJp8cA.js → SetupWizardModal-DLA4UAGp.js} +2 -2
- package/dist/client/assets/{SkillsView-CoWAjvWU.js → SkillsView-CSeauwIw.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-BFMM3RF_.js → WorkflowNodeEditor-CGgMiiWy.js} +1 -1
- package/dist/client/assets/{agent-import-generation-BqDmtTur.js → agent-import-generation-CttglSmZ.js} +1 -1
- package/dist/client/assets/{agentsOrgChartLayout-C4cLqlV6.js → agentsOrgChartLayout-f-9mwRbD.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-CdxcwdRe.js → architectureDiagram-3BPJPVTR-DA_Hm_di.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-CfQrRfHR.js → blockDiagram-GPEHLZMM-JF7K8CcV.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-Cx7-2SOm.js → c4Diagram-AAUBKEIU-DN-wklCP.js} +1 -1
- package/dist/client/assets/channel-CwFfFSI9.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-D8jvgP3M.js → chunk-2J33WTMH-ChtjU8Cu.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-B5tBx_s_.js → chunk-4BX2VUAB-dJL0n3nn.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-C-F-HQ5Z.js → chunk-55IACEB6-Bfg-QB9A.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-BP8Ln-8C.js → chunk-727SXJPM-BHPPwYUS.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DMnTmRmn.js → chunk-AQP2D5EJ-DIIpPUQN.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-D_YdfnNc.js → chunk-FMBD7UC4-D230R32b.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-CULXyuXq.js → chunk-ND2GUHAM-0Lhp5CRv.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CT1BWEJL.js → chunk-QZHKN3VN-BxbHBfKJ.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-C8dZsxxX.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C8dZsxxX.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-M-3DXa6e.js → cose-bilkent-S5V4N54A-3vEwhfYz.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-Bsiqvroz.js → dagre-BM42HDAG-Br44UsdY.js} +1 -1
- package/dist/client/assets/{dashboard-view-Bo_gFjdO.js → dashboard-view-B0OM3KYC.js} +1 -1
- package/dist/client/assets/{dashboard-view-CnFFG3cW.js → dashboard-view-BldxxYfz.js} +1 -1
- package/dist/client/assets/{dashboard-view-DAHn20ph.js → dashboard-view-CcPcELPx.js} +1 -1
- package/dist/client/assets/{dashboard-view-BEBh_Ei5.js → dashboard-view-_EglxMDl.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-CZiHoXo5.js → diagram-2AECGRRQ-CLNxbHUP.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-BoJVz4Or.js → diagram-5GNKFQAL-D3cNAeQH.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-DivGQuAL.js → diagram-KO2AKTUF-DkX7h6to.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DkMK7OJp.js → diagram-LMA3HP47-6iQIiYUr.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-DFFvRcpW.js → diagram-OG6HWLK6-Caje3229.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35--217vrom.js → erDiagram-TEJ5UH35-BkU-1HB1.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-CQgWO3xf.js → flowDiagram-I6XJVG4X-BRx3V6RE.js} +1 -1
- package/dist/client/assets/{folder-open-DR6cRcNR.js → folder-open-BaSOBfk2.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-BjDMUrS3.js → ganttDiagram-6RSMTGT7-D9f1kH3t.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-gKUcDnln.js → gitGraphDiagram-PVQCEYII-CQwmZcRd.js} +1 -1
- package/dist/client/assets/{index-oxnX59eO.js → index-B8PcG8AJ.js} +298 -298
- package/dist/client/assets/{infoDiagram-5YYISTIA-BA91T8S5.js → infoDiagram-5YYISTIA-DYWBkGjG.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-0BY5xe-c.js → ishikawaDiagram-YF4QCWOH-Df0Nzvh4.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-B_OwenbQ.js → journeyDiagram-JHISSGLW-bSvrZSyZ.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-GLabUTKp.js → kanban-definition-UN3LZRKU-yHJ101M8.js} +1 -1
- package/dist/client/assets/{mermaid.core-BXo4rxYI.js → mermaid.core-Bc_0VLJw.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-DZl6LPoE.js → mindmap-definition-RKZ34NQL-DWRnMguM.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-DtHq-4py.js → pieDiagram-4H26LBE5-Ds-DEOqr.js} +1 -1
- package/dist/client/assets/{puzzle-OVm-I1L4.js → puzzle-CdYF81IT.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BdjXhd5d.js → quadrantDiagram-W4KKPZXB-Bz-j2fkf.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-BoFVjELN.js → requirementDiagram-4Y6WPE33-BaQ_PaZ-.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-B0qyJh-P.js → sankeyDiagram-5OEKKPKP-pUCz8V4-.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-ByY62Eqp.js → sequenceDiagram-3UESZ5HK-CFwUMspe.js} +1 -1
- package/dist/client/assets/{shield-alert-CDy69fl7.js → shield-alert-4qeU3_Rm.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-BsnnRlwm.js → standing-instructions-template-Crocrob7.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CwJ-_Phi.js → stateDiagram-AJRCARHV-CHZwo-vd.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-Cfvhq8mV.js → stateDiagram-v2-BHNVJYJU-BTvKaIZR.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-Co8VfGN9.js → timeline-definition-PNZ67QCA-Cn3XIoz2.js} +1 -1
- package/dist/client/assets/{upload-CkqDbEeE.js → upload--GqImz6h.js} +1 -1
- package/dist/client/assets/{useDiscoveredSkillsCache-BHUasRiZ.js → useDiscoveredSkillsCache-BEHgTFai.js} +1 -1
- package/dist/client/assets/{users-B8DkpbXD.js → users-B0h1q4x6.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-XSJteQrL.js → vennDiagram-CIIHVFJN-CS0FNVU_.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-Btc0FRfF.js → wardley-L42UT6IY-DdYkfF5d.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO--IcDNmrf.js → wardleyDiagram-YWT4CUSO-B_rFmPCh.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-CZW93G6X.js → xychartDiagram-2RQKCTM6-CCoaCgmc.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +381 -87
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugin-sdk/index.d.ts +9 -0
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
- package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-quality/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-todos/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/channel-B4WN1jC_.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CKE8RSOv.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CKE8RSOv.js +0 -1
package/dist/bin.js
CHANGED
|
@@ -269,10 +269,16 @@ function resolveEntryPointBranchAssignment(input) {
|
|
|
269
269
|
};
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
|
-
var isValidTaskBranchName;
|
|
272
|
+
var BranchWriteProvenanceError, isValidTaskBranchName;
|
|
273
273
|
var init_branch_assignment = __esm({
|
|
274
274
|
"../core/src/branch/branch-assignment.ts"() {
|
|
275
275
|
"use strict";
|
|
276
|
+
BranchWriteProvenanceError = class extends Error {
|
|
277
|
+
constructor() {
|
|
278
|
+
super("branchWriteOrigin is required when branch is provided");
|
|
279
|
+
this.name = "BranchWriteProvenanceError";
|
|
280
|
+
}
|
|
281
|
+
};
|
|
276
282
|
isValidTaskBranchName = isValidBranchGroupBranchName;
|
|
277
283
|
}
|
|
278
284
|
});
|
|
@@ -4650,8 +4656,8 @@ var init_research_settings = __esm({
|
|
|
4650
4656
|
function resolveResearchFindingId(finding) {
|
|
4651
4657
|
const explicitId = typeof finding.id === "string" ? finding.id.trim() : "";
|
|
4652
4658
|
if (explicitId) return explicitId;
|
|
4653
|
-
const
|
|
4654
|
-
const canonical = [
|
|
4659
|
+
const normalize12 = (value2) => value2.trim().replace(/\s+/g, " ").toLowerCase();
|
|
4660
|
+
const canonical = [normalize12(finding.heading ?? ""), normalize12(finding.content ?? ""), ...[...finding.sources ?? []].map(normalize12).filter(Boolean).sort()].join("\n");
|
|
4655
4661
|
let hash3 = 2166136261;
|
|
4656
4662
|
for (let index3 = 0; index3 < canonical.length; index3 += 1) {
|
|
4657
4663
|
hash3 ^= canonical.charCodeAt(index3);
|
|
@@ -36105,17 +36111,17 @@ var init_agent_store = __esm({
|
|
|
36105
36111
|
* @returns Matching agent when unambiguous; otherwise null
|
|
36106
36112
|
*/
|
|
36107
36113
|
async resolveAgent(shortname) {
|
|
36108
|
-
const
|
|
36114
|
+
const normalize12 = (value2) => value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
36109
36115
|
const all = await this.listAgents();
|
|
36110
36116
|
const exact = all.find((agent) => agent.id === shortname);
|
|
36111
36117
|
if (exact) {
|
|
36112
36118
|
return exact;
|
|
36113
36119
|
}
|
|
36114
|
-
const normalizedTarget =
|
|
36120
|
+
const normalizedTarget = normalize12(shortname);
|
|
36115
36121
|
if (!normalizedTarget) {
|
|
36116
36122
|
return null;
|
|
36117
36123
|
}
|
|
36118
|
-
const matches = all.filter((agent) =>
|
|
36124
|
+
const matches = all.filter((agent) => normalize12(agent.name) === normalizedTarget);
|
|
36119
36125
|
return matches.length === 1 ? matches[0] : null;
|
|
36120
36126
|
}
|
|
36121
36127
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -45357,11 +45363,11 @@ function withWindowsNativeBinPath(environment, nativeRoot, platform6 = process.p
|
|
|
45357
45363
|
if (platform6 !== "win32") return environment;
|
|
45358
45364
|
const pathKey = Object.keys(environment).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
45359
45365
|
const bin = nativeRoot.replace(/[\\/]+$/, "") + "\\bin";
|
|
45360
|
-
const
|
|
45366
|
+
const normalize12 = (value2) => value2.replace(/\//g, "\\").toLowerCase();
|
|
45361
45367
|
const inherited = environment[pathKey] ?? "";
|
|
45362
45368
|
const segments = inherited.split(";").filter(Boolean);
|
|
45363
|
-
const normalizedBin =
|
|
45364
|
-
const deduplicated = segments.filter((segment) =>
|
|
45369
|
+
const normalizedBin = normalize12(bin);
|
|
45370
|
+
const deduplicated = segments.filter((segment) => normalize12(segment) !== normalizedBin);
|
|
45365
45371
|
return { ...environment, [pathKey]: [bin, ...deduplicated].join(";") };
|
|
45366
45372
|
}
|
|
45367
45373
|
function readPostgresPid(dataDir) {
|
|
@@ -67310,14 +67316,14 @@ function resolveGlobalDir(dir2) {
|
|
|
67310
67316
|
const isFusionDirInsideRepo = basename8(dir2) === ".fusion" && existsSync15(join19(dirname12(dir2), ".git"));
|
|
67311
67317
|
if (isFusionDirInsideRepo) {
|
|
67312
67318
|
const homeGlobalDir = resolveGlobalDirForHome(getHomeDir2());
|
|
67313
|
-
const
|
|
67319
|
+
const normalize12 = (p2) => {
|
|
67314
67320
|
try {
|
|
67315
67321
|
return realpathSync3.native(p2);
|
|
67316
67322
|
} catch {
|
|
67317
67323
|
return resolve14(p2);
|
|
67318
67324
|
}
|
|
67319
67325
|
};
|
|
67320
|
-
if (
|
|
67326
|
+
if (normalize12(dir2) !== normalize12(homeGlobalDir)) {
|
|
67321
67327
|
throw new Error(
|
|
67322
67328
|
`resolveGlobalDir(): refusing project-local '.fusion' directory '${dir2}' for the central/global store. This would create a stray per-project central database seeded with default global settings and silently reset them. Pass the resolved global dir (or omit the argument so it defaults to ~/.fusion); see TaskStore.getGlobalSettingsDir(). If this really is your intended global dir (e.g. a version-controlled dotfiles repo), set FUSION_ALLOW_PROJECT_LOCAL_GLOBAL_DIR=true to override.`
|
|
67323
67329
|
);
|
|
@@ -88044,15 +88050,15 @@ async function appendRecall(layer, input) {
|
|
|
88044
88050
|
async function mergeRecallGraphNodeIds(layer, recordId, addGraphNodeIds) {
|
|
88045
88051
|
const pid = projectId(layer);
|
|
88046
88052
|
const table = project_exports.memoryRecallRecords;
|
|
88047
|
-
const
|
|
88053
|
+
const normalize12 = (ids) => [...new Set(ids.map((id) => id.trim()).filter(Boolean))].sort();
|
|
88048
88054
|
return layer.transactionImmediate(async (tx) => {
|
|
88049
88055
|
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${recallGraphNodeIdsLockKey(pid, recordId)}))`);
|
|
88050
88056
|
const rows = await tx.select().from(table).where(and(eq(table.projectId, pid), eq(table.id, recordId))).limit(1);
|
|
88051
88057
|
await recallGraphNodeIdsTestHooks?.afterRowRead?.();
|
|
88052
88058
|
const row = rows[0];
|
|
88053
88059
|
if (!row) return { status: "missing" };
|
|
88054
|
-
const stored =
|
|
88055
|
-
const next =
|
|
88060
|
+
const stored = normalize12(row.graphNodeIds);
|
|
88061
|
+
const next = normalize12([...stored, ...addGraphNodeIds]);
|
|
88056
88062
|
if (stored.length === next.length && stored.every((value2, index3) => value2 === next[index3])) return { status: "unchanged" };
|
|
88057
88063
|
await tx.update(table).set({ graphNodeIds: next, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and(eq(table.projectId, pid), eq(table.id, recordId)));
|
|
88058
88064
|
return { status: "updated" };
|
|
@@ -92455,7 +92461,7 @@ import { randomUUID as randomUUID31 } from "node:crypto";
|
|
|
92455
92461
|
function normalizeCreateBranchProvenance(input) {
|
|
92456
92462
|
if (input.branch === void 0) return input;
|
|
92457
92463
|
if (input.branchWriteOrigin !== "operator" && input.branchWriteOrigin !== "engine") {
|
|
92458
|
-
throw new
|
|
92464
|
+
throw new BranchWriteProvenanceError();
|
|
92459
92465
|
}
|
|
92460
92466
|
validateTaskBranchName(input.branch);
|
|
92461
92467
|
if (input.branchWriteOrigin === "operator") {
|
|
@@ -94672,7 +94678,7 @@ async function updateTaskRepositoryScopeImpl(store, id, requestedScope) {
|
|
|
94672
94678
|
...replacement,
|
|
94673
94679
|
repositories: nextRepositories,
|
|
94674
94680
|
revision: Math.max((current.repositoryScope?.revision ?? 0) + 1, replacement.revision ?? 0),
|
|
94675
|
-
...scopeChanged ? { reviewEvidence: void 0 } : {}
|
|
94681
|
+
...scopeChanged ? { reviewEvidence: void 0, reviewRemediation: void 0 } : {}
|
|
94676
94682
|
};
|
|
94677
94683
|
const [updatedRow] = await tx.update(project_exports.tasks).set({
|
|
94678
94684
|
repositoryScope: normalized2 ?? null,
|
|
@@ -94688,6 +94694,35 @@ async function updateTaskRepositoryScopeImpl(store, id, requestedScope) {
|
|
|
94688
94694
|
return outcome;
|
|
94689
94695
|
}));
|
|
94690
94696
|
}
|
|
94697
|
+
async function updateWorkspaceReviewStateImpl(store, id, expectedScopeRevision, reviewRemediation) {
|
|
94698
|
+
return store.withTaskLock(id, async () => {
|
|
94699
|
+
const layer = store.asyncLayer;
|
|
94700
|
+
const outcome = await layer.transactionImmediate(async (tx) => {
|
|
94701
|
+
await acquireTaskAdvisoryXactLock(tx, layer.projectId, id);
|
|
94702
|
+
const row = await readTaskRowInTransaction(tx, id, { includeDeleted: true }, layer.projectId);
|
|
94703
|
+
if (!row) throw new TaskNotFoundError(id);
|
|
94704
|
+
if (row.deletedAt) throw new TaskDeletedError(id, row.deletedAt);
|
|
94705
|
+
const current = store.rowToTask(store.pgRowToTaskRow(row));
|
|
94706
|
+
if (current.repositoryScope?.revision !== expectedScopeRevision) return { task: current, updated: false };
|
|
94707
|
+
const repositoryScope = {
|
|
94708
|
+
...current.repositoryScope,
|
|
94709
|
+
...reviewRemediation ? { reviewRemediation } : { reviewRemediation: void 0 }
|
|
94710
|
+
};
|
|
94711
|
+
const [updatedRow] = await tx.update(project_exports.tasks).set({
|
|
94712
|
+
repositoryScope,
|
|
94713
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
94714
|
+
}).where(and(eq(project_exports.tasks.id, id), taskProjectScope(layer))).returning();
|
|
94715
|
+
if (!updatedRow) throw new TaskNotFoundError(id);
|
|
94716
|
+
return { task: store.rowToTask(store.pgRowToTaskRow(updatedRow)), updated: true };
|
|
94717
|
+
});
|
|
94718
|
+
if (outcome.updated) {
|
|
94719
|
+
await store.writeTaskJsonFile(store.taskDir(id), outcome.task);
|
|
94720
|
+
if (store.isWatching) store.taskCache.set(id, { ...outcome.task });
|
|
94721
|
+
store.emitTaskLifecycleEventSafely("task:updated", [outcome.task]);
|
|
94722
|
+
}
|
|
94723
|
+
return outcome;
|
|
94724
|
+
});
|
|
94725
|
+
}
|
|
94691
94726
|
async function resolveTaskWedgeNotificationEpisodeImpl(store, id, episodeId) {
|
|
94692
94727
|
const layer = store.asyncLayer;
|
|
94693
94728
|
const row = await resolveActiveTaskWedgeEpisodeRow(layer, id, episodeId, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -103491,7 +103526,7 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
|
|
|
103491
103526
|
if (updates.recommendations !== void 0) assertValidRecommendations(updates.recommendations);
|
|
103492
103527
|
if (updates.branch !== void 0) {
|
|
103493
103528
|
if (updates.branchWriteOrigin !== "operator" && updates.branchWriteOrigin !== "engine") {
|
|
103494
|
-
throw new
|
|
103529
|
+
throw new BranchWriteProvenanceError();
|
|
103495
103530
|
}
|
|
103496
103531
|
if (updates.branch !== null) validateTaskBranchName(updates.branch);
|
|
103497
103532
|
}
|
|
@@ -103580,7 +103615,7 @@ async function updateTaskUnlockedImpl(store, id, updates, runContext) {
|
|
|
103580
103615
|
task.repositoryScope = void 0;
|
|
103581
103616
|
} else if (updates.repositoryScope !== void 0) {
|
|
103582
103617
|
const scopeRevisionChanged = task.repositoryScope?.revision !== updates.repositoryScope.revision;
|
|
103583
|
-
task.repositoryScope = scopeRevisionChanged ? { ...updates.repositoryScope, reviewEvidence: void 0 } : updates.repositoryScope;
|
|
103618
|
+
task.repositoryScope = scopeRevisionChanged ? { ...updates.repositoryScope, reviewEvidence: void 0, reviewRemediation: void 0 } : updates.repositoryScope;
|
|
103584
103619
|
if (scopeRevisionChanged) {
|
|
103585
103620
|
task.workflowStepResults = invalidateSupersededRepositoryScopeReviews(
|
|
103586
103621
|
task.workflowStepResults,
|
|
@@ -106080,6 +106115,9 @@ var init_store = __esm({
|
|
|
106080
106115
|
async mutateTaskRepositoryScope(id, mutation) {
|
|
106081
106116
|
return updateTaskRepositoryScopeImpl(this, id, mutation);
|
|
106082
106117
|
}
|
|
106118
|
+
async updateWorkspaceReviewState(id, expectedScopeRevision, reviewRemediation) {
|
|
106119
|
+
return updateWorkspaceReviewStateImpl(this, id, expectedScopeRevision, reviewRemediation);
|
|
106120
|
+
}
|
|
106083
106121
|
async resolveTaskWedgeNotificationEpisode(id, episodeId) {
|
|
106084
106122
|
return resolveTaskWedgeNotificationEpisodeImpl(this, id, episodeId);
|
|
106085
106123
|
}
|
|
@@ -136087,6 +136125,7 @@ __export(src_exports, {
|
|
|
136087
136125
|
BUNDLED_PLUGIN_IDS: () => BUNDLED_PLUGIN_IDS,
|
|
136088
136126
|
BackupManager: () => BackupManager,
|
|
136089
136127
|
BackwardCompat: () => BackwardCompat,
|
|
136128
|
+
BranchWriteProvenanceError: () => BranchWriteProvenanceError,
|
|
136090
136129
|
CENTRAL_BACKUP_SCHEMAS: () => CENTRAL_BACKUP_SCHEMAS,
|
|
136091
136130
|
CLAUDE_SONNET_5_MODEL_ID: () => CLAUDE_SONNET_5_MODEL_ID,
|
|
136092
136131
|
CLEAN_COMPLETION_MARKERS: () => CLEAN_COMPLETION_MARKERS,
|
|
@@ -158737,8 +158776,8 @@ var require_resolve = __commonJS({
|
|
|
158737
158776
|
}
|
|
158738
158777
|
return count2;
|
|
158739
158778
|
}
|
|
158740
|
-
function getFullPath(resolver, id = "",
|
|
158741
|
-
if (
|
|
158779
|
+
function getFullPath(resolver, id = "", normalize12) {
|
|
158780
|
+
if (normalize12 !== false)
|
|
158742
158781
|
id = normalizeId2(id);
|
|
158743
158782
|
const p2 = resolver.parse(id);
|
|
158744
158783
|
return _getFullPath(resolver, p2);
|
|
@@ -160078,7 +160117,7 @@ var require_fast_uri = __commonJS({
|
|
|
160078
160117
|
"use strict";
|
|
160079
160118
|
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
|
|
160080
160119
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
160081
|
-
function
|
|
160120
|
+
function normalize12(uri, options) {
|
|
160082
160121
|
if (typeof uri === "string") {
|
|
160083
160122
|
uri = /** @type {T} */
|
|
160084
160123
|
serialize(parse4(uri, options), options);
|
|
@@ -160314,7 +160353,7 @@ var require_fast_uri = __commonJS({
|
|
|
160314
160353
|
}
|
|
160315
160354
|
var fastUri = {
|
|
160316
160355
|
SCHEMES,
|
|
160317
|
-
normalize:
|
|
160356
|
+
normalize: normalize12,
|
|
160318
160357
|
resolve: resolve114,
|
|
160319
160358
|
resolveComponent,
|
|
160320
160359
|
equal,
|
|
@@ -175735,6 +175774,39 @@ var init_worktrunk_failure_handler = __esm({
|
|
|
175735
175774
|
}
|
|
175736
175775
|
});
|
|
175737
175776
|
|
|
175777
|
+
// ../engine/src/executor/workspace-review-remediation.ts
|
|
175778
|
+
function normalize8(value2) {
|
|
175779
|
+
return (value2 ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
175780
|
+
}
|
|
175781
|
+
function resolveWorkspaceReviewRemediationRepository(task, declaredRepositories) {
|
|
175782
|
+
const scope = task.repositoryScope;
|
|
175783
|
+
const remediation2 = scope?.reviewRemediation;
|
|
175784
|
+
if (!remediation2) return void 0;
|
|
175785
|
+
if (scope?.state !== "confirmed" || scope.revision !== remediation2.scopeRevision) {
|
|
175786
|
+
throw new Error(`Workspace Code Review remediation target is stale for ${task.id}`);
|
|
175787
|
+
}
|
|
175788
|
+
if (!declaredRepositories.includes(remediation2.repository)) {
|
|
175789
|
+
throw new Error(`Workspace Code Review remediation repository ${remediation2.repository} is not declared for ${task.id}`);
|
|
175790
|
+
}
|
|
175791
|
+
return remediation2.repository;
|
|
175792
|
+
}
|
|
175793
|
+
function deriveWorkspaceReviewRemediation(result2) {
|
|
175794
|
+
if (typeof result2.repositoryScopeRevision !== "number") return void 0;
|
|
175795
|
+
const blocking = (result2.repositoryReviewOutcomes ?? []).filter((outcome) => outcome.status === "REVIEWED" && (outcome.verdict === "REVISE" || outcome.verdict === "RETHINK")).sort((left, right) => left.repository.localeCompare(right.repository))[0];
|
|
175796
|
+
if (!blocking) return void 0;
|
|
175797
|
+
const findings = (blocking.findings ?? []).map((finding) => `${finding.id}:${normalize8(finding.title)}:${normalize8(finding.body)}`).sort().join("|");
|
|
175798
|
+
return {
|
|
175799
|
+
scopeRevision: result2.repositoryScopeRevision,
|
|
175800
|
+
repository: blocking.repository,
|
|
175801
|
+
inputSignature: `${result2.workflowStepId}\0${blocking.repository}\0${blocking.fingerprint ?? ""}\0${blocking.verdict}\0${findings}`
|
|
175802
|
+
};
|
|
175803
|
+
}
|
|
175804
|
+
var init_workspace_review_remediation = __esm({
|
|
175805
|
+
"../engine/src/executor/workspace-review-remediation.ts"() {
|
|
175806
|
+
"use strict";
|
|
175807
|
+
}
|
|
175808
|
+
});
|
|
175809
|
+
|
|
175738
175810
|
// ../engine/src/worktree/worktree-copy-files.ts
|
|
175739
175811
|
import { constants as fsConstants3 } from "node:fs";
|
|
175740
175812
|
import { access as access7, copyFile as copyFile2, lstat as lstat2, mkdir as mkdir26 } from "node:fs/promises";
|
|
@@ -176121,14 +176193,15 @@ function invalidateWorkspaceConfigCache(owner) {
|
|
|
176121
176193
|
workspaceConfigEpochs.set(owner, (workspaceConfigEpochs.get(owner) ?? 0) + 1);
|
|
176122
176194
|
}
|
|
176123
176195
|
async function normalizeWorkspaceTaskRouting(store, taskId) {
|
|
176124
|
-
const
|
|
176125
|
-
if (typeof
|
|
176196
|
+
const normalize12 = store.normalizeWorkspaceTaskWorktreeMetadata;
|
|
176197
|
+
if (typeof normalize12 === "function") return normalize12.call(store, taskId);
|
|
176126
176198
|
let task = await store.getTask(taskId);
|
|
176127
176199
|
if (task.worktree || task.branch || task.executionStartBranch || task.baseCommitSha) {
|
|
176128
176200
|
if (typeof store.updateTask !== "function") return task;
|
|
176129
176201
|
await store.updateTask(taskId, {
|
|
176130
176202
|
worktree: null,
|
|
176131
176203
|
branch: null,
|
|
176204
|
+
branchWriteOrigin: "engine",
|
|
176132
176205
|
executionStartBranch: null,
|
|
176133
176206
|
baseCommitSha: null,
|
|
176134
176207
|
sessionFile: null
|
|
@@ -176330,11 +176403,15 @@ async function ensureWorkspaceGroupOwnership(workspaceContext, settings) {
|
|
|
176330
176403
|
async function acquireTaskWorktree(opts) {
|
|
176331
176404
|
const { task, rootDir, store, settings, pool, logger: logger3, audit, runContext, createWorktree: createWorktree2, runConfiguredCommand: runConfiguredCommand2, runInitCommand, taskEnv, secretsStore, workspaceContext } = opts;
|
|
176332
176405
|
const persistWorktreeAssignment = async (patch) => {
|
|
176406
|
+
const provenancePatch = patch.branch === void 0 ? patch : {
|
|
176407
|
+
...patch,
|
|
176408
|
+
branchWriteOrigin: patch.branchWriteOrigin ?? (classifyTaskBranchOrigin(task, patch.branch ?? void 0) === "operator-supplied" ? "operator" : "engine")
|
|
176409
|
+
};
|
|
176333
176410
|
if (!opts.suppressSingularWorktreePersist) {
|
|
176334
|
-
await store.updateTask(task.id,
|
|
176411
|
+
await store.updateTask(task.id, provenancePatch);
|
|
176335
176412
|
return;
|
|
176336
176413
|
}
|
|
176337
|
-
const { worktree: _worktree, branch: _branch, ...nonSingularPatch } =
|
|
176414
|
+
const { worktree: _worktree, branch: _branch, branchWriteOrigin: _origin, ...nonSingularPatch } = provenancePatch;
|
|
176338
176415
|
if (Object.keys(nonSingularPatch).length > 0) {
|
|
176339
176416
|
await store.updateTask(task.id, nonSingularPatch);
|
|
176340
176417
|
}
|
|
@@ -176571,7 +176648,22 @@ async function acquireTaskWorktree(opts) {
|
|
|
176571
176648
|
}
|
|
176572
176649
|
worktreePath = created2.path;
|
|
176573
176650
|
branch = created2.branch;
|
|
176574
|
-
|
|
176651
|
+
try {
|
|
176652
|
+
await persistWorktreeAssignment({ worktree: created2.path, branch: created2.branch });
|
|
176653
|
+
} catch (error3) {
|
|
176654
|
+
try {
|
|
176655
|
+
await removeWorktree({
|
|
176656
|
+
worktreePath: created2.path,
|
|
176657
|
+
rootDir,
|
|
176658
|
+
settings,
|
|
176659
|
+
taskId: task.id,
|
|
176660
|
+
reason: RemovalReason.ExecutorDispose
|
|
176661
|
+
});
|
|
176662
|
+
} catch (cleanupError) {
|
|
176663
|
+
logger3?.warn(`${task.id}: failed to compensate unpersisted worktree ${created2.path}: ${formatError(cleanupError)}`);
|
|
176664
|
+
}
|
|
176665
|
+
throw error3;
|
|
176666
|
+
}
|
|
176575
176667
|
await audit?.git({ type: "worktree:create", target: created2.path, metadata: { branch: created2.branch, source: logOrigin === "return-guard" ? "acquire-return-guard" : void 0 } });
|
|
176576
176668
|
await audit?.git({ type: "branch:create", target: created2.branch });
|
|
176577
176669
|
if (created2.branch !== branchName) {
|
|
@@ -176813,6 +176905,17 @@ async function acquireTaskWorktree(opts) {
|
|
|
176813
176905
|
if (pinned) {
|
|
176814
176906
|
return acquirePinnedWorktree();
|
|
176815
176907
|
}
|
|
176908
|
+
if (!workspaceContext && !task.worktree && !(pool && settings.recycleWorktrees)) {
|
|
176909
|
+
const registeredMatches = (await getRegisteredWorktreeBranches(rootDir)).filter((entry) => entry.branch === branchName);
|
|
176910
|
+
if (registeredMatches.length === 1) {
|
|
176911
|
+
const registeredPath = registeredMatches[0].worktreePath;
|
|
176912
|
+
const classification = await classifyTaskWorktree(rootDir, registeredPath);
|
|
176913
|
+
if (classification.ok) {
|
|
176914
|
+
await persistWorktreeAssignment({ worktree: registeredPath, branch: branchName });
|
|
176915
|
+
return reuseWarmWorktree(registeredPath, branchName, "existing");
|
|
176916
|
+
}
|
|
176917
|
+
}
|
|
176918
|
+
}
|
|
176816
176919
|
if (task.worktree && isResume) {
|
|
176817
176920
|
if (logger3?.debug) logger3.debug(`Reusing existing worktree: ${worktreePath}`);
|
|
176818
176921
|
const cleanup = await removeDesktopBuildArtifacts(worktreePath, logger3);
|
|
@@ -176848,6 +176951,7 @@ async function acquireTaskWorktree(opts) {
|
|
|
176848
176951
|
}
|
|
176849
176952
|
}
|
|
176850
176953
|
if (pooled) {
|
|
176954
|
+
let poolAssignmentPersistenceStarted = false;
|
|
176851
176955
|
try {
|
|
176852
176956
|
const preparedRaw = await pool.prepareForTask(pooled, branchName, freshStartPoint, {
|
|
176853
176957
|
allowSiblingBranchRename,
|
|
@@ -176905,7 +177009,9 @@ async function acquireTaskWorktree(opts) {
|
|
|
176905
177009
|
});
|
|
176906
177010
|
acquiredFromPool = true;
|
|
176907
177011
|
logger3?.log(`Acquired worktree from pool: ${worktreePath}`);
|
|
177012
|
+
poolAssignmentPersistenceStarted = true;
|
|
176908
177013
|
await persistWorktreeAssignment({ worktree: worktreePath, branch });
|
|
177014
|
+
poolAssignmentPersistenceStarted = false;
|
|
176909
177015
|
await audit?.git({ type: "worktree:reuse", target: worktreePath, metadata: { branch, reclaimed: prepared.reclaimed } });
|
|
176910
177016
|
if (prepared.reclaimed) {
|
|
176911
177017
|
await store.logEntry(task.id, `Acquired reclaimed worktree from pool: ${worktreePath} (${prepared.strandedCommitCount ?? 0} commits preserved)`, void 0, runContext);
|
|
@@ -176959,6 +177065,10 @@ async function acquireTaskWorktree(opts) {
|
|
|
176959
177065
|
});
|
|
176960
177066
|
}
|
|
176961
177067
|
} catch (poolErr) {
|
|
177068
|
+
if (poolAssignmentPersistenceStarted) {
|
|
177069
|
+
pool.release(pooled, task.id);
|
|
177070
|
+
throw poolErr;
|
|
177071
|
+
}
|
|
176962
177072
|
if (poolErr instanceof WorktreeBaseRefreshError) {
|
|
176963
177073
|
await persistWorktreeAssignment({ worktree: null, branch: null, branchWriteOrigin: "engine", sessionFile: null });
|
|
176964
177074
|
pool.release(pooled, task.id);
|
|
@@ -177270,7 +177380,11 @@ async function acquireWorkspaceTaskWorktrees(opts) {
|
|
|
177270
177380
|
current = await opts.store.getTask(opts.task.id);
|
|
177271
177381
|
}
|
|
177272
177382
|
const entries2 = current.workspaceWorktrees ?? {};
|
|
177273
|
-
const
|
|
177383
|
+
const remediationRepository = resolveWorkspaceReviewRemediationRepository(current, repoRelPaths);
|
|
177384
|
+
if (opts.remediationRepository !== remediationRepository) {
|
|
177385
|
+
throw new Error(`Workspace Code Review remediation target changed during acquisition for ${opts.task.id}`);
|
|
177386
|
+
}
|
|
177387
|
+
const coordinatorWorktreePath = remediationRepository ? entries2[remediationRepository]?.worktreePath : repoRelPaths.map((repoRelPath) => entries2[repoRelPath]?.worktreePath).find((path17) => typeof path17 === "string" && path17.length > 0);
|
|
177274
177388
|
if (!coordinatorWorktreePath) {
|
|
177275
177389
|
throw new Error(`Workspace task ${opts.task.id} did not acquire a declared repository worktree`);
|
|
177276
177390
|
}
|
|
@@ -177291,6 +177405,7 @@ var init_worktree_acquisition = __esm({
|
|
|
177291
177405
|
init_worktree_backend();
|
|
177292
177406
|
init_worktrunk_installer();
|
|
177293
177407
|
init_worktrunk_failure_handler();
|
|
177408
|
+
init_workspace_review_remediation();
|
|
177294
177409
|
init_secrets_env_writer();
|
|
177295
177410
|
init_worktree_desktop_artifacts();
|
|
177296
177411
|
init_worktree_hooks();
|
|
@@ -192124,7 +192239,7 @@ var init_agent_memory_index = __esm({
|
|
|
192124
192239
|
// ../engine/src/agents/agent-instructions.ts
|
|
192125
192240
|
import { readFile as readFile42, writeFile as writeFile28, mkdir as mkdir29, access as access9 } from "node:fs/promises";
|
|
192126
192241
|
import { constants as fsConstants4 } from "node:fs";
|
|
192127
|
-
import { isAbsolute as isAbsolute16, resolve as resolve41, relative as relative15, normalize as
|
|
192242
|
+
import { isAbsolute as isAbsolute16, resolve as resolve41, relative as relative15, normalize as normalize9, sep as sep12, dirname as dirname32 } from "node:path";
|
|
192128
192243
|
function trimAndClamp(value2, maxLength, label, agentId) {
|
|
192129
192244
|
const trimmed = value2.trim();
|
|
192130
192245
|
if (!trimmed) {
|
|
@@ -192158,7 +192273,7 @@ function resolveValidatedMarkdownPath(rawPath, rootDir, agentId, fieldLabel) {
|
|
|
192158
192273
|
log26.warn(`${fieldLabel} must be project-relative for agent ${agentId}: ${trimmed}`);
|
|
192159
192274
|
return null;
|
|
192160
192275
|
}
|
|
192161
|
-
const normalized2 =
|
|
192276
|
+
const normalized2 = normalize9(trimmed);
|
|
192162
192277
|
if (isPathTraversal2(normalized2)) {
|
|
192163
192278
|
log26.warn(`${fieldLabel} traversal is not allowed for agent ${agentId}: ${trimmed}`);
|
|
192164
192279
|
return null;
|
|
@@ -200921,8 +201036,8 @@ async function autoRecoverWorktreeSessionStartFailure(store, task, opts) {
|
|
|
200921
201036
|
}
|
|
200922
201037
|
const workspaceTask = configuredWorkspace || isWorkspaceTask(task);
|
|
200923
201038
|
if (workspaceTask) {
|
|
200924
|
-
const
|
|
200925
|
-
if (typeof
|
|
201039
|
+
const normalize12 = store.normalizeWorkspaceTaskWorktreeMetadata;
|
|
201040
|
+
if (typeof normalize12 === "function") task = await normalize12.call(store, task.id);
|
|
200926
201041
|
}
|
|
200927
201042
|
const resetRetryBudget = opts.resetRetryBudgetOnStaleMetadataClear === true;
|
|
200928
201043
|
const staleMetadataClearRecoveryRetryCount = opts.staleMetadataClearRecoveryRetryCount;
|
|
@@ -200977,7 +201092,13 @@ async function autoRecoverWorktreeSessionStartFailure(store, task, opts) {
|
|
|
200977
201092
|
worktreeSessionRetryCount: nextCount,
|
|
200978
201093
|
...nextStaleMetadataClearRecoveryCount === void 0 ? {} : { recoveryRetryCount: nextStaleMetadataClearRecoveryCount },
|
|
200979
201094
|
worktree: clearWorktreeMetadata ? null : staleWorktree,
|
|
200980
|
-
|
|
201095
|
+
/*
|
|
201096
|
+
* FNXC:BranchNaming 2026-08-21-09:36:
|
|
201097
|
+
* Session recovery often changes only worktree/session metadata. Do not reassert an unchanged
|
|
201098
|
+
* branch with engine provenance: that would erase a matching operator override and later make
|
|
201099
|
+
* its branch eligible for engine cleanup. A real canonical clear remains an explicit engine write.
|
|
201100
|
+
*/
|
|
201101
|
+
...(task.branch ?? null) === nextBranch ? {} : { branch: nextBranch, branchWriteOrigin: "engine" },
|
|
200981
201102
|
sessionFile: null
|
|
200982
201103
|
});
|
|
200983
201104
|
await opts.auditor?.database({
|
|
@@ -210706,7 +210827,8 @@ async function tryEarlyEmptyOwnDiffFinalize(input) {
|
|
|
210706
210827
|
try {
|
|
210707
210828
|
await store.updateTask(taskId, {
|
|
210708
210829
|
worktree: worktreeRemoved ? null : task.worktree,
|
|
210709
|
-
branch: branchDeleted ? null : task.branch
|
|
210830
|
+
branch: branchDeleted ? null : task.branch,
|
|
210831
|
+
...branchDeleted ? { branchWriteOrigin: "engine" } : {}
|
|
210710
210832
|
});
|
|
210711
210833
|
if (worktreeRemoved) task.worktree = void 0;
|
|
210712
210834
|
if (branchDeleted) task.branch = void 0;
|
|
@@ -211066,7 +211188,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
211066
211188
|
rootDir,
|
|
211067
211189
|
integrationBranch: mergeTarget.branch
|
|
211068
211190
|
});
|
|
211069
|
-
await store.updateTask(taskId, { worktree: reusableMatch.path, branch: reusableMatch.branch });
|
|
211191
|
+
await store.updateTask(taskId, { worktree: reusableMatch.path, branch: reusableMatch.branch, branchWriteOrigin: "engine" });
|
|
211070
211192
|
await emitReuseHandoffAuditEvent(
|
|
211071
211193
|
"merge:reuse-fallback-reused-existing-registration",
|
|
211072
211194
|
{
|
|
@@ -218626,7 +218748,7 @@ var init_self_healing = __esm({
|
|
|
218626
218748
|
} catch {
|
|
218627
218749
|
}
|
|
218628
218750
|
}
|
|
218629
|
-
const patch = preservedWorktree ? { branch: selected.branch } : { branch: selected.branch, worktree: null };
|
|
218751
|
+
const patch = preservedWorktree ? { branch: selected.branch, branchWriteOrigin: "engine" } : { branch: selected.branch, branchWriteOrigin: "engine", worktree: null };
|
|
218630
218752
|
if (!task.baseCommitSha) {
|
|
218631
218753
|
const derivedBaseCommit = (await execAsync18(
|
|
218632
218754
|
`git merge-base ${shellQuote2(integrationBase)} ${shellQuote2(selected.branch)}`,
|
|
@@ -222303,6 +222425,12 @@ var init_self_healing = __esm({
|
|
|
222303
222425
|
await this.emitWorkspacePartialLandNoAction(task, "merge-pending", liveness.livePaths);
|
|
222304
222426
|
continue;
|
|
222305
222427
|
}
|
|
222428
|
+
const latestTask = await this.store.getTask(task.id).catch(() => null);
|
|
222429
|
+
const blockerTask = latestTask?.status === "failed" && latestTask.error?.startsWith("Workspace partial land:") ? { ...latestTask, status: null, error: void 0 } : latestTask;
|
|
222430
|
+
if (!blockerTask || getTaskMergeBlocker(blockerTask, { skipColumnIdentityCheck: true }) !== void 0) {
|
|
222431
|
+
await this.emitWorkspacePartialLandNoAction(task, "merge-blocked", []);
|
|
222432
|
+
continue;
|
|
222433
|
+
}
|
|
222306
222434
|
const workspaceWorktrees = task.workspaceWorktrees ?? {};
|
|
222307
222435
|
const explicitScope = task.repositoryScope?.state === "confirmed" ? task.repositoryScope.repositories : void 0;
|
|
222308
222436
|
if (!explicitScope) {
|
|
@@ -222310,8 +222438,12 @@ var init_self_healing = __esm({
|
|
|
222310
222438
|
continue;
|
|
222311
222439
|
}
|
|
222312
222440
|
const repoKeys = Object.keys(workspaceWorktrees).filter(
|
|
222313
|
-
(repoRel) => explicitScope.includes(repoRel) && (task.modifiedFiles ?? []).some((file) => file.startsWith(`${repoRel}/`))
|
|
222441
|
+
(repoRel) => explicitScope.includes(repoRel) && ((task.modifiedFiles ?? []).some((file) => file.startsWith(`${repoRel}/`)) || Boolean(workspaceWorktrees[repoRel]?.landedSha))
|
|
222314
222442
|
);
|
|
222443
|
+
if (repoKeys.length === 0) {
|
|
222444
|
+
await this.emitWorkspacePartialLandNoAction(task, "empty-obligations", []);
|
|
222445
|
+
continue;
|
|
222446
|
+
}
|
|
222315
222447
|
const landedRepos = [];
|
|
222316
222448
|
const unlandedRepos = [];
|
|
222317
222449
|
const unrecoverableRepos = [];
|
|
@@ -222368,13 +222500,12 @@ var init_self_healing = __esm({
|
|
|
222368
222500
|
phase: "reconcile-workspace-partial-land"
|
|
222369
222501
|
});
|
|
222370
222502
|
if (evidenceUnavailableRepos.length > 0) {
|
|
222371
|
-
const defers = (
|
|
222372
|
-
this.
|
|
222503
|
+
const defers = (task.mergeTransientRetryCount ?? 0) + 1;
|
|
222504
|
+
await this.store.updateTask(task.id, { mergeTransientRetryCount: defers });
|
|
222373
222505
|
if (defers >= MAX_STARVATION_DROPS) {
|
|
222374
222506
|
const error3 = `Workspace partial-land evidence unavailable: branch state could not be read after ${MAX_STARVATION_DROPS} sweeps for sub-repo(s) ${evidenceUnavailableRepos.join(", ")} \u2014 manual intervention required.`;
|
|
222375
222507
|
await this.store.updateTask(task.id, { status: "failed", error: error3 });
|
|
222376
222508
|
await this.store.logEntry(task.id, error3);
|
|
222377
|
-
this.workspacePartialLandEvidenceDefers.delete(task.id);
|
|
222378
222509
|
await auditor.database({
|
|
222379
222510
|
type: "task:reconcile-workspace-partial-land",
|
|
222380
222511
|
target: task.id,
|
|
@@ -222387,7 +222518,6 @@ var init_self_healing = __esm({
|
|
|
222387
222518
|
}
|
|
222388
222519
|
continue;
|
|
222389
222520
|
}
|
|
222390
|
-
this.workspacePartialLandEvidenceDefers.delete(task.id);
|
|
222391
222521
|
if (unrecoverableRepos.length > 0) {
|
|
222392
222522
|
const missingBranches = unrecoverableRepos.map((repoRel) => workspaceWorktrees[repoRel]?.branch ?? resolveTaskWorkingBranch(task)).join(", ");
|
|
222393
222523
|
const error3 = `Workspace partial-land unrecoverable: sub-repo(s) ${unrecoverableRepos.join(", ")} have no branch (${missingBranches}) and no landedSha \u2014 manual intervention required.`;
|
|
@@ -222415,7 +222545,7 @@ var init_self_healing = __esm({
|
|
|
222415
222545
|
landedRepos,
|
|
222416
222546
|
unlandedRepos: [],
|
|
222417
222547
|
reason: "all-landed-not-finalized",
|
|
222418
|
-
successLog: "
|
|
222548
|
+
successLog: "Workspace merge recovery scheduled: all sub-repositories have proven landing evidence; awaiting finalize-once result"
|
|
222419
222549
|
});
|
|
222420
222550
|
recovered++;
|
|
222421
222551
|
continue;
|
|
@@ -222424,7 +222554,7 @@ var init_self_healing = __esm({
|
|
|
222424
222554
|
landedRepos,
|
|
222425
222555
|
unlandedRepos,
|
|
222426
222556
|
reason: landedRepos.length > 0 ? "partial-land" : "zero-land",
|
|
222427
|
-
successLog: `
|
|
222557
|
+
successLog: `Workspace merge recovery scheduled: ${landedRepos.length} landed, ${unlandedRepos.length} pending`
|
|
222428
222558
|
});
|
|
222429
222559
|
recovered++;
|
|
222430
222560
|
} catch (err) {
|
|
@@ -222489,7 +222619,8 @@ var init_self_healing = __esm({
|
|
|
222489
222619
|
}).catch(() => void 0);
|
|
222490
222620
|
return false;
|
|
222491
222621
|
}
|
|
222492
|
-
const drops = (
|
|
222622
|
+
const drops = (task.mergeTransientRetryCount ?? 0) + 1;
|
|
222623
|
+
await this.store.updateTask(task.id, { mergeTransientRetryCount: drops });
|
|
222493
222624
|
this.workspacePartialLandDrops.set(task.id, drops);
|
|
222494
222625
|
log34.warn(`reconcileWorkspacePartialLands: enqueue dropped for ${task.id} (${drops}/${MAX_STARVATION_DROPS}); merge queue rejected re-enqueue`);
|
|
222495
222626
|
if (drops >= MAX_STARVATION_DROPS) {
|
|
@@ -232085,7 +232216,7 @@ function recoverPassedPlanReviewFromLatestLog(task) {
|
|
|
232085
232216
|
completedAt: latest.timestamp
|
|
232086
232217
|
};
|
|
232087
232218
|
}
|
|
232088
|
-
var PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE, PLAN_REVIEW_LEASE_HELD_VALUE, SESSION_CONTENTION_HOLD_VALUE, WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY, WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY, WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY, WORKFLOW_REVIEW_KIND_CONTEXT_KEY, WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND, WORKFLOW_DRIFT_PARK_CONTEXT_KEY, WorkflowGraphSuspended, MERGE_REGION_KINDS, WorkflowGraphExecutor;
|
|
232219
|
+
var PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE, BRANCH_WRITE_PROVENANCE_FAILURE_VALUE, PLAN_REVIEW_LEASE_HELD_VALUE, SESSION_CONTENTION_HOLD_VALUE, WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY, WORKFLOW_INTERRUPTED_NODE_ABORT_KIND_CONTEXT_KEY, WORKFLOW_OPTIONAL_GROUP_CONTEXT_KEY, WORKFLOW_REVIEW_KIND_CONTEXT_KEY, WORKFLOW_NODE_ENGINE_PAUSE_ABORT_KIND, WORKFLOW_DRIFT_PARK_CONTEXT_KEY, WorkflowGraphSuspended, MERGE_REGION_KINDS, WorkflowGraphExecutor;
|
|
232089
232220
|
var init_workflow_graph_executor = __esm({
|
|
232090
232221
|
"../engine/src/workflows/workflow-graph-executor.ts"() {
|
|
232091
232222
|
"use strict";
|
|
@@ -232098,8 +232229,10 @@ var init_workflow_graph_executor = __esm({
|
|
|
232098
232229
|
init_workflow_graph_foreach();
|
|
232099
232230
|
init_workflow_graph_loop();
|
|
232100
232231
|
init_workflow_node_execution_needs();
|
|
232232
|
+
init_src2();
|
|
232101
232233
|
init_worktree_acquisition();
|
|
232102
232234
|
PLAN_REVIEW_PROVIDER_FAILURE_HOLD_VALUE = "plan-review-provider-failure-hold";
|
|
232235
|
+
BRANCH_WRITE_PROVENANCE_FAILURE_VALUE = "branch-write-provenance-failure";
|
|
232103
232236
|
PLAN_REVIEW_LEASE_HELD_VALUE = "plan-review-lease-held";
|
|
232104
232237
|
SESSION_CONTENTION_HOLD_VALUE = "session-contention-hold";
|
|
232105
232238
|
WORKFLOW_INTERRUPTED_NODE_ID_CONTEXT_KEY = "workflow:interruptedNodeId";
|
|
@@ -233016,7 +233149,7 @@ var init_workflow_graph_executor = __esm({
|
|
|
233016
233149
|
const failureResult = {
|
|
233017
233150
|
outcome: "failure",
|
|
233018
233151
|
// FNXC:SessionContention 2026-07-25-21:30: contention is a retryable hold, not an exception.
|
|
233019
|
-
value: isSessionContentionError(lastErrorText) ? SESSION_CONTENTION_HOLD_VALUE : "exception",
|
|
233152
|
+
value: lastError instanceof BranchWriteProvenanceError ? BRANCH_WRITE_PROVENANCE_FAILURE_VALUE : isSessionContentionError(lastErrorText) ? SESSION_CONTENTION_HOLD_VALUE : "exception",
|
|
233020
233153
|
contextPatch: {
|
|
233021
233154
|
[`node:${node.id}:error`]: lastErrorText
|
|
233022
233155
|
}
|
|
@@ -237104,6 +237237,7 @@ async function handleBranchConflict(deps, task, error3) {
|
|
|
237104
237237
|
status: "failed",
|
|
237105
237238
|
error: conflictMessage,
|
|
237106
237239
|
branch: error3.branchName,
|
|
237240
|
+
branchWriteOrigin: "engine",
|
|
237107
237241
|
worktree: error3.conflictingWorktreePath,
|
|
237108
237242
|
paused: true,
|
|
237109
237243
|
pausedReason: "branch-conflict-unrecoverable"
|
|
@@ -237159,8 +237293,8 @@ async function recoverMissingWorktreeSessionStartFailure(deps, task, worktreePat
|
|
|
237159
237293
|
}
|
|
237160
237294
|
let recoveryTask = task;
|
|
237161
237295
|
if (workspaceTask) {
|
|
237162
|
-
const
|
|
237163
|
-
if (typeof
|
|
237296
|
+
const normalize12 = deps.store.normalizeWorkspaceTaskWorktreeMetadata;
|
|
237297
|
+
if (typeof normalize12 === "function") recoveryTask = await normalize12.call(deps.store, task.id);
|
|
237164
237298
|
}
|
|
237165
237299
|
if (!workspaceTask && isInsideWorktreesDir(deps.rootDir, staleWorktreePath)) {
|
|
237166
237300
|
try {
|
|
@@ -237578,8 +237712,21 @@ async function detectWorkspaceMainCheckoutWork(deps, task, repos, declaredScope)
|
|
|
237578
237712
|
const { stdout } = await execAsync41("git log -n 200 --format=%H%x1f%ct%x1f%B%x1e HEAD", { ...probeOptions, cwd: checkout });
|
|
237579
237713
|
const attributed = new RegExp(`(?:${task.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}|Fusion-Task-Id:\\s*${task.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`, "i");
|
|
237580
237714
|
for (const commit of parseCommits(stdout)) {
|
|
237581
|
-
const
|
|
237582
|
-
|
|
237715
|
+
const entry = workspaceWorktrees[repo];
|
|
237716
|
+
const recordedLanding = entry?.landedSha === commit.sha || task.mergeDetails?.workspaceLandedShas?.[repo] === commit.sha || task.mergeDetails?.commitSha === commit.sha;
|
|
237717
|
+
let reachableFromBaseline = false;
|
|
237718
|
+
if (entry?.baseCommitSha) {
|
|
237719
|
+
try {
|
|
237720
|
+
await execAsync41(`git merge-base --is-ancestor ${commit.sha} ${entry.baseCommitSha}`, { ...probeOptions, cwd: checkout });
|
|
237721
|
+
reachableFromBaseline = true;
|
|
237722
|
+
} catch {
|
|
237723
|
+
}
|
|
237724
|
+
}
|
|
237725
|
+
if (attributed.test(commit.body) && !recordedLanding && !reachableFromBaseline && anchor !== null && commit.committedAt >= anchor) {
|
|
237726
|
+
violations.push({ repo, files: [], commits: [commit.sha], evidence: "task-attributed-commit" });
|
|
237727
|
+
} else if (!recordedLanding && !reachableFromBaseline && anchor !== null && commit.committedAt >= anchor) {
|
|
237728
|
+
warnings.push({ repo, files: [], commits: [commit.sha], reason: "pre-existing-dirt" });
|
|
237729
|
+
}
|
|
237583
237730
|
}
|
|
237584
237731
|
} catch {
|
|
237585
237732
|
warnings.push({ repo, files: [], commits: [], reason: "commit-scan-unavailable" });
|
|
@@ -237659,7 +237806,7 @@ async function verifyWorktreeInvariants(deps, task, worktreePathOverride, allowR
|
|
|
237659
237806
|
reason: "main_checkout_edit",
|
|
237660
237807
|
repo: firstMainCheckoutViolation.repo,
|
|
237661
237808
|
observed: `${firstMainCheckoutViolation.evidence}: ${observed}`,
|
|
237662
|
-
expected: `move task work into the acquired fusion/${task.id} worktree for ${firstMainCheckoutViolation.repo} (acquire it first if needed), restore its main checkout, then
|
|
237809
|
+
expected: `move task work into the acquired fusion/${task.id} worktree for ${firstMainCheckoutViolation.repo} (acquire it first if needed), restore its main checkout, then ask an operator to retry fn_task_done`
|
|
237663
237810
|
};
|
|
237664
237811
|
}
|
|
237665
237812
|
const zeroAcquire = classifyWorkspaceZeroAcquire(task, {
|
|
@@ -239142,7 +239289,7 @@ var init_workflow_step_failure_injection = __esm({
|
|
|
239142
239289
|
});
|
|
239143
239290
|
|
|
239144
239291
|
// ../engine/src/executor/send-task-back-for-fix.ts
|
|
239145
|
-
async function sendTaskBackForFix(deps, task, worktreePath, failureFeedback, stepName, reason, preserveResumeState = true, mergeVerificationFailure = false, retryPresentation, findings) {
|
|
239292
|
+
async function sendTaskBackForFix(deps, task, worktreePath, failureFeedback, stepName, reason, preserveResumeState = true, mergeVerificationFailure = false, retryPresentation, findings, persistWorktreePath) {
|
|
239146
239293
|
const taskId = task.id;
|
|
239147
239294
|
deps.clearCompletedTaskWatchdog(taskId);
|
|
239148
239295
|
const { task: authoritativeRemediationTask, route: externalExecutionRoute } = await resolveAuthoritativeExternalExecutionRoute(deps.store, task);
|
|
@@ -239185,7 +239332,7 @@ Please fix the issues so the verification can pass on the next attempt.`,
|
|
|
239185
239332
|
remediationWorktreePath,
|
|
239186
239333
|
`${taskId}: sent back to in-progress for remediation`,
|
|
239187
239334
|
preserveResumeState,
|
|
239188
|
-
!externalExecutionRoute.configured
|
|
239335
|
+
persistWorktreePath ?? !externalExecutionRoute.configured
|
|
239189
239336
|
);
|
|
239190
239337
|
}
|
|
239191
239338
|
var init_send_task_back_for_fix = __esm({
|
|
@@ -244281,6 +244428,7 @@ async function ensureGraphCustomNodeWorktree(deps, task, settings, nodeId, refre
|
|
|
244281
244428
|
deps.getRunContextFor(task.id)
|
|
244282
244429
|
);
|
|
244283
244430
|
if (workspaceConfig) {
|
|
244431
|
+
const remediationRepository = resolveWorkspaceReviewRemediationRepository(task, workspaceConfig.repos);
|
|
244284
244432
|
const workspace = await acquireWorkspaceTaskWorktrees({
|
|
244285
244433
|
workspaceConfig,
|
|
244286
244434
|
workspaceRootDir: deps.rootDir,
|
|
@@ -244305,7 +244453,8 @@ async function ensureGraphCustomNodeWorktree(deps, task, settings, nodeId, refre
|
|
|
244305
244453
|
return result2;
|
|
244306
244454
|
}),
|
|
244307
244455
|
taskEnv: process.env,
|
|
244308
|
-
addActiveWorktree: deps.addActiveWorktree
|
|
244456
|
+
addActiveWorktree: deps.addActiveWorktree,
|
|
244457
|
+
remediationRepository
|
|
244309
244458
|
});
|
|
244310
244459
|
deps.onStart?.(workspace.task, workspace.coordinatorWorktreePath);
|
|
244311
244460
|
executorLog.debug(`${task.id}: workflow node '${nodeId}' using workspace coordinator ${workspace.coordinatorWorktreePath}`);
|
|
@@ -244369,6 +244518,7 @@ var init_ensure_graph_custom_node_worktree = __esm({
|
|
|
244369
244518
|
init_worktree_git_refs();
|
|
244370
244519
|
init_task_predicates();
|
|
244371
244520
|
init_workspace_config_resolver();
|
|
244521
|
+
init_workspace_review_remediation();
|
|
244372
244522
|
}
|
|
244373
244523
|
});
|
|
244374
244524
|
|
|
@@ -244957,7 +245107,22 @@ ${info.feedback}`,
|
|
|
244957
245107
|
return false;
|
|
244958
245108
|
}
|
|
244959
245109
|
const revisionKey = optionalStepRevisionKey2(info.nodeId, info.stepName);
|
|
244960
|
-
|
|
245110
|
+
const reviewResult = (liveTask.workflowStepResults ?? []).find(
|
|
245111
|
+
(result2) => (result2.workflowStepId === info.nodeId || result2.workflowStepName === info.stepName) && result2.verdict === "REVISE"
|
|
245112
|
+
);
|
|
245113
|
+
const derivedRemediation = reviewResult ? deriveWorkspaceReviewRemediation(reviewResult) : void 0;
|
|
245114
|
+
const remediation2 = derivedRemediation && liveTask.workspaceWorktrees ? derivedRemediation : void 0;
|
|
245115
|
+
const priorRemediation = liveTask.repositoryScope?.reviewRemediation;
|
|
245116
|
+
const hasDurableRepeatedWorkspaceReview = remediation2 !== void 0 && priorRemediation?.scopeRevision === remediation2.scopeRevision && priorRemediation.repository === remediation2.repository && priorRemediation.inputSignature === remediation2.inputSignature;
|
|
245117
|
+
const updateWorkspaceReviewState = deps.store.updateWorkspaceReviewState;
|
|
245118
|
+
if (remediation2 && !hasDurableRepeatedWorkspaceReview && updateWorkspaceReviewState) {
|
|
245119
|
+
const persisted = await updateWorkspaceReviewState.call(deps.store, taskId, remediation2.scopeRevision, remediation2);
|
|
245120
|
+
if (!persisted.updated) {
|
|
245121
|
+
executorLog.warn(`${taskId}: workspace Code Review remediation was superseded by a repository scope change.`);
|
|
245122
|
+
return false;
|
|
245123
|
+
}
|
|
245124
|
+
}
|
|
245125
|
+
if (hasDurableRepeatedWorkspaceReview || (!remediation2 || !updateWorkspaceReviewState) && hasRepeatedUnchangedCodeReview(liveTask, info)) {
|
|
244961
245126
|
const runContext = deps.getRunContextFor(taskId);
|
|
244962
245127
|
await deps.store.logEntry(
|
|
244963
245128
|
taskId,
|
|
@@ -245005,9 +245170,18 @@ Feedback:
|
|
|
245005
245170
|
${info.feedback}`, revisionKey),
|
|
245006
245171
|
deps.getRunContextFor(taskId)
|
|
245007
245172
|
);
|
|
245008
|
-
|
|
245173
|
+
const remediationWorktreePath = remediation2 ? liveTask.workspaceWorktrees?.[remediation2.repository]?.worktreePath : liveTask.worktree;
|
|
245174
|
+
if (remediation2 && !remediationWorktreePath) {
|
|
245175
|
+
await deps.store.updateTask(taskId, {
|
|
245176
|
+
status: "awaiting-approval",
|
|
245177
|
+
awaitingApprovalReason: "code-review-non-convergence",
|
|
245178
|
+
error: `Workspace Code Review remediation target ${remediation2.repository} has no acquired worktree.`
|
|
245179
|
+
});
|
|
245180
|
+
return false;
|
|
245181
|
+
}
|
|
245182
|
+
const sendArgs = [
|
|
245009
245183
|
liveTask,
|
|
245010
|
-
|
|
245184
|
+
remediationWorktreePath ?? "",
|
|
245011
245185
|
info.feedback,
|
|
245012
245186
|
info.stepName,
|
|
245013
245187
|
`Pre-merge optional workflow step "${info.stepName}" requested revision`,
|
|
@@ -245015,7 +245189,12 @@ ${info.feedback}`, revisionKey),
|
|
|
245015
245189
|
false,
|
|
245016
245190
|
{ attempt: nextCount, max: budget.unbounded ? void 0 : budget.max },
|
|
245017
245191
|
info.findings
|
|
245018
|
-
|
|
245192
|
+
];
|
|
245193
|
+
if (remediation2) {
|
|
245194
|
+
await deps.sendTaskBackForFix(...sendArgs, false);
|
|
245195
|
+
} else {
|
|
245196
|
+
await deps.sendTaskBackForFix(...sendArgs);
|
|
245197
|
+
}
|
|
245019
245198
|
return true;
|
|
245020
245199
|
}
|
|
245021
245200
|
var init_request_pre_merge_optional_step_fix = __esm({
|
|
@@ -245030,6 +245209,7 @@ var init_request_pre_merge_optional_step_fix = __esm({
|
|
|
245030
245209
|
init_plan_review_feedback_history();
|
|
245031
245210
|
init_logger3();
|
|
245032
245211
|
init_emit_bounded_run_audit3();
|
|
245212
|
+
init_workspace_review_remediation();
|
|
245033
245213
|
}
|
|
245034
245214
|
});
|
|
245035
245215
|
|
|
@@ -245918,6 +246098,14 @@ function createTaskDoneTool(deps, taskId, worktreePath, promptContent, codeRevie
|
|
|
245918
246098
|
const refusalMessage = `fn_task_done refused: ${invariantCheck.reason} \u2014 observed=${invariantCheck.observed}, expected=${invariantCheck.expected}`;
|
|
245919
246099
|
await store.logEntry(taskId, refusalMessage, void 0, deps.getRunContextFor(task.id));
|
|
245920
246100
|
executorLog.error(`${taskId}: fn_task_done refused (${invariantCheck.reason}) \u2014 observed=${invariantCheck.observed}, expected=${invariantCheck.expected}`);
|
|
246101
|
+
if (invariantCheck.reason === "main_checkout_edit") {
|
|
246102
|
+
await store.updateTask(taskId, { status: "failed", error: refusalMessage });
|
|
246103
|
+
await store.logEntry(taskId, `${refusalMessage} \u2014 operator cleanup and Retry are required; executor retry budget preserved`, void 0, deps.getRunContextFor(task.id));
|
|
246104
|
+
return {
|
|
246105
|
+
content: [{ type: "text", text: refusalMessage }],
|
|
246106
|
+
details: { error: refusalMessage }
|
|
246107
|
+
};
|
|
246108
|
+
}
|
|
245921
246109
|
const priorRequeues = task.taskDoneRetryCount ?? 0;
|
|
245922
246110
|
const nextRequeueCount = priorRequeues + 1;
|
|
245923
246111
|
if (priorRequeues < MAX_TASK_DONE_REQUEUE_RETRIES) {
|
|
@@ -247750,7 +247938,9 @@ Repository scope (task-level; review this plan once): ${detail.repositoryScope?.
|
|
|
247750
247938
|
return {
|
|
247751
247939
|
repositoryScope: {
|
|
247752
247940
|
...currentScope,
|
|
247753
|
-
reviewEvidence: Object.fromEntries(Object.entries(review.repositoryDiffFingerprints).map(([repo, fingerprint]) => [repo, { fingerprint, approvedAt }]))
|
|
247941
|
+
reviewEvidence: Object.fromEntries(Object.entries(review.repositoryDiffFingerprints).map(([repo, fingerprint]) => [repo, { fingerprint, approvedAt }])),
|
|
247942
|
+
// FNXC:WorkspaceFinalization 2026-08-21-09:50: An APPROVE for this fenced scope ends its REVISE episode; retaining its target would misroute later acquisition.
|
|
247943
|
+
...currentScope.reviewRemediation?.scopeRevision === review.repositoryScopeRevision ? { reviewRemediation: void 0 } : {}
|
|
247754
247944
|
}
|
|
247755
247945
|
};
|
|
247756
247946
|
});
|
|
@@ -249155,7 +249345,8 @@ async function runGraphCustomNode(deps, node, nodeTask, settings, columnBinding,
|
|
|
249155
249345
|
executionTarget = await deps.ensureGraphCustomNodeWorktree(acquisitionTask, settings, node.id);
|
|
249156
249346
|
}
|
|
249157
249347
|
}
|
|
249158
|
-
const
|
|
249348
|
+
const remediationRepository = workspaceConfig ? resolveWorkspaceReviewRemediationRepository(executionTarget, workspaceConfig.repos) : void 0;
|
|
249349
|
+
const workspaceCoordinator = workspaceConfig ? remediationRepository ? executionTarget.workspaceWorktrees?.[remediationRepository]?.worktreePath : Object.keys(executionTarget.workspaceWorktrees ?? {}).filter((repoRelPath) => workspaceConfig.repos.includes(repoRelPath)).sort().map((repoRelPath) => executionTarget.workspaceWorktrees?.[repoRelPath]?.worktreePath).find((path17) => typeof path17 === "string" && path17.length > 0) : void 0;
|
|
249159
249350
|
if (!executionTarget.worktree && !workspaceCoordinator) {
|
|
249160
249351
|
return { outcome: "failure", value: "no-worktree-for-write-node" };
|
|
249161
249352
|
}
|
|
@@ -249315,7 +249506,9 @@ ${prompt}`;
|
|
|
249315
249506
|
return {
|
|
249316
249507
|
repositoryScope: {
|
|
249317
249508
|
...currentScope,
|
|
249318
|
-
reviewEvidence: Object.fromEntries(Object.entries(aggregate.repositoryDiffFingerprints).map(([repo, fingerprint]) => [repo, { fingerprint, approvedAt }]))
|
|
249509
|
+
reviewEvidence: Object.fromEntries(Object.entries(aggregate.repositoryDiffFingerprints).map(([repo, fingerprint]) => [repo, { fingerprint, approvedAt }])),
|
|
249510
|
+
// FNXC:WorkspaceFinalization 2026-08-21-09:50: A current-scope APPROVE clears the durable REVISE coordinator before graph completion can schedule another run.
|
|
249511
|
+
...currentScope.reviewRemediation?.scopeRevision === aggregate.repositoryScopeRevision ? { reviewRemediation: void 0 } : {}
|
|
249319
249512
|
}
|
|
249320
249513
|
};
|
|
249321
249514
|
});
|
|
@@ -249413,6 +249606,7 @@ var init_run_graph_custom_node = __esm({
|
|
|
249413
249606
|
"use strict";
|
|
249414
249607
|
init_src2();
|
|
249415
249608
|
init_logger3();
|
|
249609
|
+
init_workspace_review_remediation();
|
|
249416
249610
|
init_workflow_graph_executor();
|
|
249417
249611
|
init_workflow_node_execution_needs();
|
|
249418
249612
|
init_workflow_step_verdict();
|
|
@@ -249468,6 +249662,13 @@ async function handleGraphFailure(deps, task, result2) {
|
|
|
249468
249662
|
await deps.persistTokenUsage(task.id);
|
|
249469
249663
|
return;
|
|
249470
249664
|
}
|
|
249665
|
+
if (graphFailureValue(result2) === BRANCH_WRITE_PROVENANCE_FAILURE_VALUE) {
|
|
249666
|
+
const diagnostic = graphFailureErrorTexts(result2).find((message2) => message2.includes("branchWriteOrigin is required when branch is provided")) ?? "branchWriteOrigin is required when branch is provided";
|
|
249667
|
+
await deps.store.logEntry(task.id, diagnostic, void 0, deps.getRunContextFor(task.id));
|
|
249668
|
+
await deps.store.updateTask(task.id, { status: "failed", error: diagnostic }, deps.getRunContextFor(task.id));
|
|
249669
|
+
await deps.persistTokenUsage(task.id);
|
|
249670
|
+
return;
|
|
249671
|
+
}
|
|
249471
249672
|
if (isSessionContentionGraphFailure(result2)) {
|
|
249472
249673
|
await deps.holdForSessionContention(task, live, result2);
|
|
249473
249674
|
await deps.persistTokenUsage(task.id);
|
|
@@ -252205,6 +252406,7 @@ async function runImplementation(deps, task, graphCompletion, reportImplementati
|
|
|
252205
252406
|
deps.registerConfiguredCommandController(task.id, taskCommandAbortController);
|
|
252206
252407
|
let workspaceCoordinatorWorktree;
|
|
252207
252408
|
if (hasWorkspaceRepos && deps.workspaceConfig) {
|
|
252409
|
+
const remediationRepository = resolveWorkspaceReviewRemediationRepository(task, deps.workspaceConfig.repos);
|
|
252208
252410
|
const workspace = await acquireWorkspaceTaskWorktrees({
|
|
252209
252411
|
workspaceConfig: deps.workspaceConfig,
|
|
252210
252412
|
workspaceRootDir: deps.rootDir,
|
|
@@ -252229,7 +252431,8 @@ async function runImplementation(deps, task, graphCompletion, reportImplementati
|
|
|
252229
252431
|
return result2;
|
|
252230
252432
|
}),
|
|
252231
252433
|
taskEnv,
|
|
252232
|
-
addActiveWorktree: deps.addActiveWorktree
|
|
252434
|
+
addActiveWorktree: deps.addActiveWorktree,
|
|
252435
|
+
remediationRepository
|
|
252233
252436
|
});
|
|
252234
252437
|
task = workspace.task;
|
|
252235
252438
|
workspaceCoordinatorWorktree = workspace.coordinatorWorktreePath;
|
|
@@ -254789,6 +254992,7 @@ var init_run_implementation = __esm({
|
|
|
254789
254992
|
init_run_audit();
|
|
254790
254993
|
init_emit_bounded_run_audit3();
|
|
254791
254994
|
init_worktree_acquisition();
|
|
254995
|
+
init_workspace_review_remediation();
|
|
254792
254996
|
init_worktree_paths();
|
|
254793
254997
|
init_worktree_pool();
|
|
254794
254998
|
execAsync52 = promisify75(exec56);
|
|
@@ -263696,6 +263900,40 @@ var init_overseer_noop_finalize_veto = __esm({
|
|
|
263696
263900
|
}
|
|
263697
263901
|
});
|
|
263698
263902
|
|
|
263903
|
+
// ../engine/src/merge/workspace-merge-readiness.ts
|
|
263904
|
+
function resolveWorkspaceMergeReadiness(task, freshModifiedRepositories, netZeroBranchRepositories) {
|
|
263905
|
+
const scope = task.repositoryScope;
|
|
263906
|
+
if (scope?.state !== "confirmed") {
|
|
263907
|
+
return { kind: "blocked", reason: `Workspace repository scope is unresolved for ${task.id}; operator confirmation is required before landing` };
|
|
263908
|
+
}
|
|
263909
|
+
const entries2 = task.workspaceWorktrees ?? {};
|
|
263910
|
+
const declaredRepositories = scope.repositories.map((repository) => repository.trim());
|
|
263911
|
+
if (declaredRepositories.some((repository) => !repository) || new Set(declaredRepositories).size !== declaredRepositories.length) {
|
|
263912
|
+
return { kind: "blocked", reason: `Workspace repository scope has duplicate or invalid declarations for ${task.id}; operator repair is required` };
|
|
263913
|
+
}
|
|
263914
|
+
const worktreePaths = Object.values(entries2).map((entry) => entry.worktreePath?.trim()).filter((path17) => Boolean(path17));
|
|
263915
|
+
if (new Set(worktreePaths).size !== worktreePaths.length) {
|
|
263916
|
+
return { kind: "blocked", reason: `Workspace merge has duplicate worktree paths for ${task.id}; operator repair is required` };
|
|
263917
|
+
}
|
|
263918
|
+
const repositories = declaredRepositories.filter((repo) => Boolean(entries2[repo])).filter((repo) => freshModifiedRepositories.has(repo) || netZeroBranchRepositories.has(repo) || Boolean(entries2[repo]?.landedSha)).sort();
|
|
263919
|
+
const preservedFiles = [...new Set(task.modifiedFiles ?? [])].sort();
|
|
263920
|
+
if (repositories.length > 0) return { kind: "ready", repositories, preservedFiles };
|
|
263921
|
+
if (task.noCommitsExpected === true && scope.repositories.length === 0) {
|
|
263922
|
+
return { kind: "no-op", repositories: [], preservedFiles: [] };
|
|
263923
|
+
}
|
|
263924
|
+
const declared = [...declaredRepositories].sort();
|
|
263925
|
+
const knownEntries = declared.filter((repo) => entries2[repo] !== void 0);
|
|
263926
|
+
if (knownEntries.length === 0) {
|
|
263927
|
+
return { kind: "blocked", reason: `Workspace merge has no acquired repositories matching confirmed scope for ${task.id}; operator repair is required` };
|
|
263928
|
+
}
|
|
263929
|
+
return { kind: "blocked", reason: `Workspace merge has no evidenced landing obligations for ${task.id}; operator repair is required` };
|
|
263930
|
+
}
|
|
263931
|
+
var init_workspace_merge_readiness = __esm({
|
|
263932
|
+
"../engine/src/merge/workspace-merge-readiness.ts"() {
|
|
263933
|
+
"use strict";
|
|
263934
|
+
}
|
|
263935
|
+
});
|
|
263936
|
+
|
|
263699
263937
|
// ../engine/src/merge/workspace-fence-ref.ts
|
|
263700
263938
|
import { execFile as execFile32 } from "node:child_process";
|
|
263701
263939
|
import { createHash as createHash33 } from "node:crypto";
|
|
@@ -265460,8 +265698,21 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
265460
265698
|
const taskTitle = task.title?.trim() ? task.title.split("\n")[0] : void 0;
|
|
265461
265699
|
const liveMergeBoundaryTask = await store.getTask(taskId).catch(() => void 0);
|
|
265462
265700
|
const mergeBoundaryTask = liveMergeBoundaryTask?.workspaceWorktrees ? liveMergeBoundaryTask : task;
|
|
265701
|
+
const reviewColumns = /* @__PURE__ */ new Set(["in-review"]);
|
|
265702
|
+
const workflowIr = await resolveWorkflowIrForTask(store, taskId).catch(() => void 0);
|
|
265703
|
+
if (workflowIr) for (const column of resolveReviewColumns(workflowIr)) reviewColumns.add(column);
|
|
265704
|
+
const completeColumns = /* @__PURE__ */ new Set(["done"]);
|
|
265705
|
+
if (workflowIr) for (const column of resolveTerminalColumns(workflowIr)) completeColumns.add(column);
|
|
265706
|
+
if (!completeColumns.has(mergeBoundaryTask.column)) {
|
|
265707
|
+
const mergeBlocker = getTaskMergeBlocker(mergeBoundaryTask, {
|
|
265708
|
+
manual: options.manual === true,
|
|
265709
|
+
reviewColumns
|
|
265710
|
+
});
|
|
265711
|
+
if (mergeBlocker) throw new WorkspaceFinalizeBlockedError(taskId, mergeBlocker);
|
|
265712
|
+
}
|
|
265463
265713
|
const confirmedScope = mergeBoundaryTask.repositoryScope?.state === "confirmed" ? new Set(mergeBoundaryTask.repositoryScope.repositories) : void 0;
|
|
265464
265714
|
const mergeBoundaryModifiedFiles = [];
|
|
265715
|
+
const mergeBoundaryModifiedRepositories = /* @__PURE__ */ new Set();
|
|
265465
265716
|
const mergeBoundaryFingerprints = {};
|
|
265466
265717
|
const modifiedOutOfScopeRepositories = /* @__PURE__ */ new Set();
|
|
265467
265718
|
const netZeroBranchRepositories = /* @__PURE__ */ new Set();
|
|
@@ -265478,6 +265729,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
265478
265729
|
if (files.length > 0 && !confirmedScope?.has(repoRel)) {
|
|
265479
265730
|
modifiedOutOfScopeRepositories.add(repoRel);
|
|
265480
265731
|
} else if (confirmedScope?.has(repoRel)) {
|
|
265732
|
+
if (files.length > 0) mergeBoundaryModifiedRepositories.add(repoRel);
|
|
265481
265733
|
mergeBoundaryModifiedFiles.push(...files.map((file) => `${repoRel}/${file}`));
|
|
265482
265734
|
if (files.length === 0) {
|
|
265483
265735
|
const { stdout: aheadCount } = await execFileAsync28("git", ["rev-list", "--count", `HEAD..${entry.branch}`], { cwd: entry.worktreePath, encoding: "utf8" });
|
|
@@ -265493,19 +265745,26 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
265493
265745
|
}
|
|
265494
265746
|
const normalizedMergeBoundaryFiles = [...new Set(mergeBoundaryModifiedFiles)].sort();
|
|
265495
265747
|
const persistedReviewFiles = [...new Set(mergeBoundaryTask.modifiedFiles ?? [])].sort();
|
|
265496
|
-
const approvedReviewEvidence = mergeBoundaryTask.repositoryScope?.reviewEvidence
|
|
265497
|
-
const
|
|
265498
|
-
|
|
265748
|
+
const approvedReviewEvidence = mergeBoundaryTask.repositoryScope?.reviewEvidence;
|
|
265749
|
+
const requiresRepositoryReviewEvidence = approvedReviewEvidence !== void 0 || (mergeBoundaryTask.enabledWorkflowSteps ?? []).some((step) => /review/i.test(step));
|
|
265750
|
+
const missingOrStaleReviewApproval = requiresRepositoryReviewEvidence && Object.entries(mergeBoundaryFingerprints).some(([repoRel, fingerprint]) => approvedReviewEvidence?.[repoRel]?.fingerprint !== fingerprint);
|
|
265751
|
+
if (requiresRepositoryReviewEvidence && normalizedMergeBoundaryFiles.some((file) => !persistedReviewFiles.includes(file)) || missingOrStaleReviewApproval) {
|
|
265499
265752
|
throw new Error(`Workspace merge evidence changed after review for ${taskId}; return the task to Code Review before landing`);
|
|
265500
265753
|
}
|
|
265501
|
-
|
|
265502
|
-
|
|
265503
|
-
|
|
265504
|
-
|
|
265505
|
-
|
|
265506
|
-
|
|
265754
|
+
const readiness = resolveWorkspaceMergeReadiness(
|
|
265755
|
+
mergeBoundaryTask,
|
|
265756
|
+
mergeBoundaryModifiedRepositories,
|
|
265757
|
+
netZeroBranchRepositories
|
|
265758
|
+
);
|
|
265759
|
+
if (readiness.kind === "blocked") throw new Error(readiness.reason);
|
|
265760
|
+
if (readiness.kind === "no-op") {
|
|
265761
|
+
throw new Error(`Workspace task ${taskId} has explicit no-commits policy and cannot enter repository landing`);
|
|
265507
265762
|
}
|
|
265508
|
-
const
|
|
265763
|
+
const retainedModifiedFiles = [.../* @__PURE__ */ new Set([...normalizedMergeBoundaryFiles, ...readiness.preservedFiles])].sort();
|
|
265764
|
+
await store.updateTask(taskId, { modifiedFiles: retainedModifiedFiles });
|
|
265765
|
+
task = { ...mergeBoundaryTask, modifiedFiles: retainedModifiedFiles };
|
|
265766
|
+
const workspaceWorktrees = task.workspaceWorktrees ?? {};
|
|
265767
|
+
const repoKeys = readiness.repositories;
|
|
265509
265768
|
const repos = [];
|
|
265510
265769
|
let allLanded = true;
|
|
265511
265770
|
await setStatus("merging");
|
|
@@ -265534,7 +265793,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
265534
265793
|
};
|
|
265535
265794
|
} catch (error3) {
|
|
265536
265795
|
if (error3 instanceof WorkspaceFenceRefError) {
|
|
265537
|
-
throw new
|
|
265796
|
+
throw new WorkspaceMergeTechnicalError("dispatch-fence-publication", `Workspace merge dispatch fence publication failed for ${taskId}: ${error3.message}`);
|
|
265538
265797
|
}
|
|
265539
265798
|
throw error3;
|
|
265540
265799
|
}
|
|
@@ -265623,7 +265882,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
265623
265882
|
} catch (error3) {
|
|
265624
265883
|
if (durableLandLease) await store.releaseWorkspaceLease(durableLandLease).catch(() => void 0);
|
|
265625
265884
|
if (error3 instanceof WorkspaceRepoLandBusyError) throw error3;
|
|
265626
|
-
throw new
|
|
265885
|
+
throw new WorkspaceMergeTechnicalError("durable-lease", `Workspace repository lease unavailable for ${repoRel}: ${getErrorMessage4(error3)}`);
|
|
265627
265886
|
}
|
|
265628
265887
|
try {
|
|
265629
265888
|
activeSessionRegistry.registerPath(repoRootDir, {
|
|
@@ -265660,7 +265919,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
265660
265919
|
renewalTimer?.unref?.();
|
|
265661
265920
|
const landSignal = options.signal ? AbortSignal.any([options.signal, leaseAbort.signal]) : leaseAbort.signal;
|
|
265662
265921
|
const assertLeaseLive = () => {
|
|
265663
|
-
if (leaseLost || leaseAbort.signal.aborted) throw new
|
|
265922
|
+
if (leaseLost || leaseAbort.signal.aborted) throw new WorkspaceMergeTechnicalError("durable-lease", `Workspace repository lease renewal was lost for ${repoRel}`);
|
|
265664
265923
|
};
|
|
265665
265924
|
try {
|
|
265666
265925
|
const landResult = await landOneRepo(repoRootDir, entry.branch, integrationBranch, {
|
|
@@ -265745,11 +266004,11 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
265745
266004
|
repos.push({ repo: repoRel, repoRootDir, integrationBranch, branch: entry.branch, status: "empty" });
|
|
265746
266005
|
}
|
|
265747
266006
|
} catch (err) {
|
|
265748
|
-
if (leaseLost) throw new
|
|
266007
|
+
if (leaseLost) throw new WorkspaceMergeTechnicalError("durable-lease", `Workspace repository lease renewal was lost for ${repoRel}`);
|
|
265749
266008
|
if (isMergeAbortedError(err)) throw err;
|
|
265750
266009
|
if (err instanceof WorkspacePartialLandError) throw err;
|
|
265751
266010
|
if (err instanceof WorkspaceFenceRefError) {
|
|
265752
|
-
throw new
|
|
266011
|
+
throw new WorkspaceMergeTechnicalError("repository-fence-publication", `Workspace repository fence publication failed for ${repoRel}: ${err.message}`);
|
|
265753
266012
|
}
|
|
265754
266013
|
const message = getErrorMessage4(err);
|
|
265755
266014
|
await log44(`AI merge (workspace): sub-repo ${repoRel} land failed: ${message}`);
|
|
@@ -266308,7 +266567,7 @@ async function finalizeTask(store, taskId, result2, audit, log44, rootDir, fence
|
|
|
266308
266567
|
function throwIfAborted2(signal, taskId) {
|
|
266309
266568
|
assertMergeGenerationOwned(signal, taskId);
|
|
266310
266569
|
}
|
|
266311
|
-
var execFileAsync28, aiMergeLog, MAX_CONCURRENT_ADVANCE_RETRIES, MAX_BLOCKING_REVIEW_REASONS, AiMergeBlockedError, AiMergeReviewReconciliationInvalidatedError, LEGACY_COMPLETE_COLUMN, LEGACY_ARCHIVED_COLUMN, LEGACY_TERMINAL_COLUMNS3, LEGACY_REBOUND_COLUMN2, STRONG_LANDED_STRATEGIES, WORKSPACE_REPO_LAND_OWNER_KEY, WorkspaceRepoLandBusyError, WorkspacePartialLandError, WorkspaceFinalizeBlockedError, WorkspaceMergeDispatchSupersededError;
|
|
266570
|
+
var execFileAsync28, aiMergeLog, MAX_CONCURRENT_ADVANCE_RETRIES, MAX_BLOCKING_REVIEW_REASONS, AiMergeBlockedError, AiMergeReviewReconciliationInvalidatedError, LEGACY_COMPLETE_COLUMN, LEGACY_ARCHIVED_COLUMN, LEGACY_TERMINAL_COLUMNS3, LEGACY_REBOUND_COLUMN2, STRONG_LANDED_STRATEGIES, WORKSPACE_REPO_LAND_OWNER_KEY, WorkspaceRepoLandBusyError, WorkspaceMergeTechnicalError, WorkspacePartialLandError, WorkspaceFinalizeBlockedError, WorkspaceMergeDispatchSupersededError;
|
|
266312
266571
|
var init_merger_ai = __esm({
|
|
266313
266572
|
"../engine/src/merge/merger-ai.ts"() {
|
|
266314
266573
|
"use strict";
|
|
@@ -266340,6 +266599,7 @@ var init_merger_ai = __esm({
|
|
|
266340
266599
|
init_active_session_registry();
|
|
266341
266600
|
init_mcp_resolution();
|
|
266342
266601
|
init_workspace_land_predicate();
|
|
266602
|
+
init_workspace_merge_readiness();
|
|
266343
266603
|
init_workspace_land_failure();
|
|
266344
266604
|
init_workspace_fence_ref();
|
|
266345
266605
|
init_auto_merge_finalization();
|
|
@@ -266385,6 +266645,14 @@ var init_merger_ai = __esm({
|
|
|
266385
266645
|
}
|
|
266386
266646
|
retryable = true;
|
|
266387
266647
|
};
|
|
266648
|
+
WorkspaceMergeTechnicalError = class extends Error {
|
|
266649
|
+
constructor(kind, message) {
|
|
266650
|
+
super(message);
|
|
266651
|
+
this.kind = kind;
|
|
266652
|
+
this.name = "WorkspaceMergeTechnicalError";
|
|
266653
|
+
}
|
|
266654
|
+
retryable = true;
|
|
266655
|
+
};
|
|
266388
266656
|
WorkspacePartialLandError = class extends Error {
|
|
266389
266657
|
constructor(landedCount, failedRepos, message) {
|
|
266390
266658
|
super(message);
|
|
@@ -285578,6 +285846,15 @@ ${detail}`);
|
|
|
285578
285846
|
}
|
|
285579
285847
|
continue;
|
|
285580
285848
|
}
|
|
285849
|
+
if (err instanceof WorkspaceMergeTechnicalError) {
|
|
285850
|
+
await store.logEntry(taskId, `Workspace merge technical failure (${err.kind}): ${err.message}`, "WorkspaceMergeTechnicalFailure").catch(() => void 0);
|
|
285851
|
+
if (hasManualResolver) {
|
|
285852
|
+
this.rejectMergeResolvers(taskId, err);
|
|
285853
|
+
} else if (!await this.maybeRetryTransientMerge(store, taskId, await store.getTask(taskId).catch(() => null), err.message, true)) {
|
|
285854
|
+
await store.updateTask(taskId, { status: "failed", error: err.message }).catch(() => void 0);
|
|
285855
|
+
}
|
|
285856
|
+
continue;
|
|
285857
|
+
}
|
|
285581
285858
|
const isWorkspaceBusyError = err instanceof WorkspaceRepoLandBusyError || err instanceof WorkspaceMergeDispatchBusyError || err instanceof WorkspaceMergeDispatchSupersededError;
|
|
285582
285859
|
if (isWorkspaceBusyError && hasManualResolver) {
|
|
285583
285860
|
await store.logEntry(taskId, `Workspace sub-repo land busy (contention): ${errorMsg}`, "WorkspaceRepoLandBusy").catch(() => void 0);
|
|
@@ -285585,18 +285862,18 @@ ${detail}`);
|
|
|
285585
285862
|
continue;
|
|
285586
285863
|
}
|
|
285587
285864
|
if (isWorkspaceBusyError && !hasManualResolver) {
|
|
285588
|
-
const
|
|
285865
|
+
const liveTask = await store.getTask(taskId).catch(() => null);
|
|
285866
|
+
const busyCount = liveTask?.mergeTransientRetryCount ?? 0;
|
|
285589
285867
|
await store.logEntry(taskId, `Workspace sub-repo land busy (contention): ${errorMsg}`, "WorkspaceRepoLandBusy").catch(() => void 0);
|
|
285590
285868
|
if (busyCount < _ProjectEngine.WORKSPACE_BUSY_MAX_REENQUEUES) {
|
|
285591
|
-
|
|
285869
|
+
const nextCount = busyCount + 1;
|
|
285592
285870
|
const delayMs = Math.min(5e3 * Math.pow(2, busyCount), 6e4);
|
|
285593
|
-
await store.updateTask(taskId, { status: null }).catch(() => void 0);
|
|
285871
|
+
await store.updateTask(taskId, { status: null, mergeTransientRetryCount: nextCount }).catch(() => void 0);
|
|
285594
285872
|
runtimeLog.log(
|
|
285595
|
-
`Workspace land busy re-enqueue ${
|
|
285873
|
+
`Workspace land busy re-enqueue ${nextCount}/${_ProjectEngine.WORKSPACE_BUSY_MAX_REENQUEUES} for ${taskId} in ${delayMs / 1e3}s (durable transient retry)`
|
|
285596
285874
|
);
|
|
285597
285875
|
this.scheduleWorkspaceBusyReenqueue(taskId, delayMs);
|
|
285598
285876
|
} else {
|
|
285599
|
-
this.workspaceBusyReenqueues.delete(taskId);
|
|
285600
285877
|
await store.updateTask(taskId, { status: "failed", error: errorMsg }).catch(() => void 0);
|
|
285601
285878
|
runtimeLog.error(
|
|
285602
285879
|
`Auto-merge: ${taskId} workspace land busy ${_ProjectEngine.WORKSPACE_BUSY_MAX_REENQUEUES} times \u2014 parked as failed (sustained sub-repo lease contention)`
|
|
@@ -286925,7 +287202,7 @@ var init_engine_singleton_lock = __esm({
|
|
|
286925
287202
|
import { realpathSync as realpathSync16 } from "node:fs";
|
|
286926
287203
|
import { resolve as pathResolve } from "node:path";
|
|
286927
287204
|
function sameProjectRoot(a, b3) {
|
|
286928
|
-
const
|
|
287205
|
+
const normalize12 = (p2) => {
|
|
286929
287206
|
const resolved = pathResolve(p2);
|
|
286930
287207
|
try {
|
|
286931
287208
|
return realpathSync16(resolved);
|
|
@@ -286933,7 +287210,7 @@ function sameProjectRoot(a, b3) {
|
|
|
286933
287210
|
return resolved;
|
|
286934
287211
|
}
|
|
286935
287212
|
};
|
|
286936
|
-
return
|
|
287213
|
+
return normalize12(a) === normalize12(b3);
|
|
286937
287214
|
}
|
|
286938
287215
|
var DEFAULT_RECONCILIATION_INTERVAL_MS, ProjectEngineManager;
|
|
286939
287216
|
var init_project_engine_manager = __esm({
|
|
@@ -303626,7 +303903,7 @@ var init_gitlab = __esm({
|
|
|
303626
303903
|
if (response.status === 204) return void 0;
|
|
303627
303904
|
return await response.json();
|
|
303628
303905
|
}
|
|
303629
|
-
async listPaginated(path17, options,
|
|
303906
|
+
async listPaginated(path17, options, normalize12) {
|
|
303630
303907
|
const limit2 = clampLimit(options.limit);
|
|
303631
303908
|
const out = [];
|
|
303632
303909
|
let page = 1;
|
|
@@ -303637,7 +303914,7 @@ var init_gitlab = __esm({
|
|
|
303637
303914
|
appendListParams(params, options);
|
|
303638
303915
|
const rows = await this.request(`${path17}?${params.toString()}`);
|
|
303639
303916
|
if (!Array.isArray(rows) || rows.length === 0) break;
|
|
303640
|
-
out.push(...rows.map(
|
|
303917
|
+
out.push(...rows.map(normalize12));
|
|
303641
303918
|
if (rows.length < Number(params.get("per_page"))) break;
|
|
303642
303919
|
page += 1;
|
|
303643
303920
|
}
|
|
@@ -319584,6 +319861,7 @@ function registerPlanningSubtaskRoutes(ctx, deps) {
|
|
|
319584
319861
|
priority: isTaskPriority3(summary.priority) ? summary.priority : DEFAULT_TASK_PRIORITY,
|
|
319585
319862
|
...sourceContext ? { sourceIssue: sourceContext.sourceIssue, source: { sourceType: "github_import", sourceMetadata: sourceContext.sourceMetadata }, ...trackingDecision?.githubTracking ? { githubTracking: trackingDecision.githubTracking } : {} } : { source: { sourceType: "api" } },
|
|
319586
319863
|
branch: resolvedBranch,
|
|
319864
|
+
...resolvedBranch !== void 0 ? { branchWriteOrigin: "operator" } : {},
|
|
319587
319865
|
baseBranch: resolvedBaseBranch,
|
|
319588
319866
|
/*
|
|
319589
319867
|
FNXC:WorkflowSelection 2026-06-20-16:48:
|
|
@@ -351306,7 +351584,7 @@ var require_commonjs4 = __commonJS({
|
|
|
351306
351584
|
var TYPEMASK = 1023;
|
|
351307
351585
|
var entToType = (s3) => s3.isFile() ? IFREG : s3.isDirectory() ? IFDIR : s3.isSymbolicLink() ? IFLNK : s3.isCharacterDevice() ? IFCHR : s3.isBlockDevice() ? IFBLK : s3.isSocket() ? IFSOCK : s3.isFIFO() ? IFIFO : UNKNOWN;
|
|
351308
351586
|
var normalizeCache = /* @__PURE__ */ new Map();
|
|
351309
|
-
var
|
|
351587
|
+
var normalize12 = (s3) => {
|
|
351310
351588
|
const c = normalizeCache.get(s3);
|
|
351311
351589
|
if (c)
|
|
351312
351590
|
return c;
|
|
@@ -351319,7 +351597,7 @@ var require_commonjs4 = __commonJS({
|
|
|
351319
351597
|
const c = normalizeNocaseCache.get(s3);
|
|
351320
351598
|
if (c)
|
|
351321
351599
|
return c;
|
|
351322
|
-
const n =
|
|
351600
|
+
const n = normalize12(s3.toLowerCase());
|
|
351323
351601
|
normalizeNocaseCache.set(s3, n);
|
|
351324
351602
|
return n;
|
|
351325
351603
|
};
|
|
@@ -351488,7 +351766,7 @@ var require_commonjs4 = __commonJS({
|
|
|
351488
351766
|
*/
|
|
351489
351767
|
constructor(name2, type = UNKNOWN, root, roots, nocase, children, opts) {
|
|
351490
351768
|
this.name = name2;
|
|
351491
|
-
this.#matchName = nocase ? normalizeNocase(name2) :
|
|
351769
|
+
this.#matchName = nocase ? normalizeNocase(name2) : normalize12(name2);
|
|
351492
351770
|
this.#type = type & TYPEMASK;
|
|
351493
351771
|
this.nocase = nocase;
|
|
351494
351772
|
this.roots = roots;
|
|
@@ -351581,7 +351859,7 @@ var require_commonjs4 = __commonJS({
|
|
|
351581
351859
|
return this.parent || this;
|
|
351582
351860
|
}
|
|
351583
351861
|
const children = this.children();
|
|
351584
|
-
const name2 = this.nocase ? normalizeNocase(pathPart) :
|
|
351862
|
+
const name2 = this.nocase ? normalizeNocase(pathPart) : normalize12(pathPart);
|
|
351585
351863
|
for (const p2 of children) {
|
|
351586
351864
|
if (p2.#matchName === name2) {
|
|
351587
351865
|
return p2;
|
|
@@ -351826,7 +352104,7 @@ var require_commonjs4 = __commonJS({
|
|
|
351826
352104
|
* directly.
|
|
351827
352105
|
*/
|
|
351828
352106
|
isNamed(n) {
|
|
351829
|
-
return !this.nocase ? this.#matchName ===
|
|
352107
|
+
return !this.nocase ? this.#matchName === normalize12(n) : this.#matchName === normalizeNocase(n);
|
|
351830
352108
|
}
|
|
351831
352109
|
/**
|
|
351832
352110
|
* Return the Path object corresponding to the target of a symbolic link.
|
|
@@ -351965,7 +352243,7 @@ var require_commonjs4 = __commonJS({
|
|
|
351965
352243
|
#readdirMaybePromoteChild(e, c) {
|
|
351966
352244
|
for (let p2 = c.provisional; p2 < c.length; p2++) {
|
|
351967
352245
|
const pchild = c[p2];
|
|
351968
|
-
const name2 = this.nocase ? normalizeNocase(e.name) :
|
|
352246
|
+
const name2 = this.nocase ? normalizeNocase(e.name) : normalize12(e.name);
|
|
351969
352247
|
if (name2 !== pchild.#matchName) {
|
|
351970
352248
|
continue;
|
|
351971
352249
|
}
|
|
@@ -394484,6 +394762,22 @@ var init_update_install_coordinator = __esm({
|
|
|
394484
394762
|
pendingVersion;
|
|
394485
394763
|
pendingResult;
|
|
394486
394764
|
restartRequested = false;
|
|
394765
|
+
/**
|
|
394766
|
+
* FNXC:PendingUpdateInstall 2026-08-21-05:58:
|
|
394767
|
+
* A completed install belongs to the still-running old process, not a mounted
|
|
394768
|
+
* Settings dialog. Expose its target until process replacement so every route
|
|
394769
|
+
* can reject a second install and every dashboard remount can offer restart.
|
|
394770
|
+
*/
|
|
394771
|
+
getPendingInstall() {
|
|
394772
|
+
if (!this.pendingVersion || !this.pendingResult) return void 0;
|
|
394773
|
+
return {
|
|
394774
|
+
...this.pendingResult,
|
|
394775
|
+
latestVersion: this.pendingVersion,
|
|
394776
|
+
restartAttempted: this.restartRequested,
|
|
394777
|
+
restartScheduled: this.restartRequested,
|
|
394778
|
+
priorPid: this.restartRequested ? process.pid : void 0
|
|
394779
|
+
};
|
|
394780
|
+
}
|
|
394487
394781
|
async install(targetVersion, operation) {
|
|
394488
394782
|
if (this.pendingVersion && this.pendingResult) return this.pendingResult;
|
|
394489
394783
|
if (!this.inFlight) {
|
|
@@ -394523,8 +394817,25 @@ var init_register_update_check_routes = __esm({
|
|
|
394523
394817
|
const { router, store, rethrowAsApiError: rethrowAsApiError8 } = ctx;
|
|
394524
394818
|
const cliPackageVersion = getCliPackageVersion(import.meta.url);
|
|
394525
394819
|
const coordinator = ctx.options?.systemControl ? processUpdateInstallCoordinator : new UpdateInstallCoordinator();
|
|
394820
|
+
const pendingResponse = () => {
|
|
394821
|
+
const pendingInstall = coordinator.getPendingInstall();
|
|
394822
|
+
if (!pendingInstall) return void 0;
|
|
394823
|
+
return {
|
|
394824
|
+
currentVersion: pendingInstall.currentVersion,
|
|
394825
|
+
latestVersion: pendingInstall.latestVersion,
|
|
394826
|
+
// Keep the old version's availability signal compatible for legacy readers.
|
|
394827
|
+
updateAvailable: true,
|
|
394828
|
+
pendingInstall,
|
|
394829
|
+
lastChecked: Date.now()
|
|
394830
|
+
};
|
|
394831
|
+
};
|
|
394526
394832
|
router.get("/update-check", async (_req, res) => {
|
|
394527
394833
|
try {
|
|
394834
|
+
const pending = pendingResponse();
|
|
394835
|
+
if (pending) {
|
|
394836
|
+
res.json(pending);
|
|
394837
|
+
return;
|
|
394838
|
+
}
|
|
394528
394839
|
const globalSettings = await store.getGlobalSettingsStore().getSettings();
|
|
394529
394840
|
if (globalSettings.updateCheckEnabled === false) {
|
|
394530
394841
|
res.json({
|
|
@@ -394547,6 +394858,11 @@ var init_register_update_check_routes = __esm({
|
|
|
394547
394858
|
});
|
|
394548
394859
|
router.post("/update-check/refresh", async (_req, res) => {
|
|
394549
394860
|
try {
|
|
394861
|
+
const pending = pendingResponse();
|
|
394862
|
+
if (pending) {
|
|
394863
|
+
res.json(pending);
|
|
394864
|
+
return;
|
|
394865
|
+
}
|
|
394550
394866
|
const globalSettings = await store.getGlobalSettingsStore().getSettings();
|
|
394551
394867
|
const fusionDir = resolveGlobalDir();
|
|
394552
394868
|
await clearUpdateCheckCache(fusionDir);
|
|
@@ -394561,6 +394877,11 @@ var init_register_update_check_routes = __esm({
|
|
|
394561
394877
|
});
|
|
394562
394878
|
router.post("/update-check/install", async (_req, res) => {
|
|
394563
394879
|
try {
|
|
394880
|
+
const pending = coordinator.getPendingInstall();
|
|
394881
|
+
if (pending) {
|
|
394882
|
+
res.json(pending);
|
|
394883
|
+
return;
|
|
394884
|
+
}
|
|
394564
394885
|
const globalSettings = await store.getGlobalSettingsStore().getSettings();
|
|
394565
394886
|
const fusionDir = resolveGlobalDir();
|
|
394566
394887
|
const updateCheck = await performUpdateCheck(fusionDir, cliPackageVersion, {
|
|
@@ -394593,7 +394914,7 @@ var init_register_update_check_routes = __esm({
|
|
|
394593
394914
|
}
|
|
394594
394915
|
const restartAttempted = result2.updated && result2.outcome === "installed" && resolveUpdateAutomationSettings(latestSettings).autoRestartAfterUpdate;
|
|
394595
394916
|
const restartScheduled = restartAttempted ? coordinator.requestRestart(() => ctx.options?.systemControl?.requestRestart("update-install") === true) : false;
|
|
394596
|
-
res.json({ ...result2, restartAttempted, restartScheduled, priorPid: restartAttempted ? process.pid : void 0 });
|
|
394917
|
+
res.json(coordinator.getPendingInstall() ?? { ...result2, restartAttempted, restartScheduled, priorPid: restartAttempted ? process.pid : void 0 });
|
|
394597
394918
|
} catch (error3) {
|
|
394598
394919
|
rethrowAsApiError8(error3, "Failed to install update");
|
|
394599
394920
|
}
|
|
@@ -405763,7 +406084,7 @@ var init_badge_pubsub = __esm({
|
|
|
405763
406084
|
});
|
|
405764
406085
|
|
|
405765
406086
|
// ../dashboard/src/terminal-websocket-diagnostics.ts
|
|
405766
|
-
import { basename as basename37, normalize as
|
|
406087
|
+
import { basename as basename37, normalize as normalize10, sep as sep25 } from "node:path";
|
|
405767
406088
|
function toSessionTag(sessionId) {
|
|
405768
406089
|
const normalized2 = sessionId.trim();
|
|
405769
406090
|
if (normalized2.length <= 8) {
|
|
@@ -405772,7 +406093,7 @@ function toSessionTag(sessionId) {
|
|
|
405772
406093
|
return `${normalized2.slice(0, 4)}\u2026${normalized2.slice(-4)}#${normalized2.length}`;
|
|
405773
406094
|
}
|
|
405774
406095
|
function toRedactedPathHint(pathValue) {
|
|
405775
|
-
const normalized2 =
|
|
406096
|
+
const normalized2 = normalize10(pathValue);
|
|
405776
406097
|
const segments = normalized2.split(sep25).filter(Boolean);
|
|
405777
406098
|
if (segments.length === 0) {
|
|
405778
406099
|
return "<empty>";
|
|
@@ -406712,6 +407033,8 @@ async function runAutoUpdateCycle(deps) {
|
|
|
406712
407033
|
});
|
|
406713
407034
|
return "unsupervised";
|
|
406714
407035
|
}
|
|
407036
|
+
const coordinator = deps.coordinator ?? new UpdateInstallCoordinator();
|
|
407037
|
+
if (coordinator.getPendingInstall()) return "restart-waiting";
|
|
406715
407038
|
const fusionDir = deps.fusionDir ?? resolveGlobalDir();
|
|
406716
407039
|
const check3 = deps.checkForUpdate ?? performUpdateCheck;
|
|
406717
407040
|
const install = deps.installUpdate ?? performUpdateInstall;
|
|
@@ -406737,7 +407060,6 @@ async function runAutoUpdateCycle(deps) {
|
|
|
406737
407060
|
});
|
|
406738
407061
|
let installed;
|
|
406739
407062
|
try {
|
|
406740
|
-
const coordinator = deps.coordinator ?? new UpdateInstallCoordinator();
|
|
406741
407063
|
installed = await coordinator.install(result2.latestVersion, () => install(result2.currentVersion, result2.latestVersion, {
|
|
406742
407064
|
fusionDir,
|
|
406743
407065
|
installMethod: { sourceWorkspaceRoot: deps.sourceWorkspaceRoot }
|
|
@@ -406762,7 +407084,7 @@ async function runAutoUpdateCycle(deps) {
|
|
|
406762
407084
|
latestSettings = {};
|
|
406763
407085
|
}
|
|
406764
407086
|
if (!resolveUpdateAutomationSettings(latestSettings).autoRestartAfterUpdate) return "up-to-date";
|
|
406765
|
-
const scheduled =
|
|
407087
|
+
const scheduled = coordinator.requestRestart(() => deps.requestRestart("auto-update"));
|
|
406766
407088
|
if (!scheduled) {
|
|
406767
407089
|
deps.log.warn("Auto-update installed but restart was not scheduled", {
|
|
406768
407090
|
message: `v${installed.latestVersion} is installed; restart Fusion manually to run it.`
|
|
@@ -408596,9 +408918,17 @@ function createServer6(store, options) {
|
|
|
408596
408918
|
};
|
|
408597
408919
|
const jsonParser = express3.json({ verify: preserveRawBody });
|
|
408598
408920
|
const planningImageCaptureParser = express3.json({ limit: "5mb", verify: preserveRawBody });
|
|
408921
|
+
const chatMessageParser = express3.json({ limit: 2 * 1024 * 1024, verify: preserveRawBody });
|
|
408922
|
+
const fileSaveParser = express3.json({ limit: 6 * MAX_FILE_SIZE + 1024, verify: preserveRawBody });
|
|
408923
|
+
const isChatMessagePath = (path17) => /^\/api\/chat\/(?:sessions|rooms)\/[^/]+\/messages\/?$/.test(path17);
|
|
408924
|
+
const isTaskFileSavePath = (path17) => /^\/api\/tasks\/[^/]+\/files\/.+\/?$/.test(path17);
|
|
408925
|
+
const isWorkspaceFileSavePath = (path17) => {
|
|
408926
|
+
if (!/^\/api\/files\/.+/.test(path17) || /^\/api\/files\/mkdir\/?$/.test(path17)) return false;
|
|
408927
|
+
return !/^\/api\/files\/.+\/(?:copy|move|delete|rename)\/?$/.test(path17);
|
|
408928
|
+
};
|
|
408599
408929
|
app.use((req, res, next) => {
|
|
408600
408930
|
if (req.path === "/api/voice/transcribe" || req.path === "/api/voice/transcribe/") return next();
|
|
408601
|
-
const parser = req.path === "/api/planning/start-streaming" || req.path === "/api/planning/start-streaming/" ? planningImageCaptureParser : jsonParser;
|
|
408931
|
+
const parser = req.path === "/api/planning/start-streaming" || req.path === "/api/planning/start-streaming/" ? planningImageCaptureParser : req.method === "POST" && isChatMessagePath(req.path) ? chatMessageParser : req.method === "POST" && (isTaskFileSavePath(req.path) || isWorkspaceFileSavePath(req.path)) ? fileSaveParser : jsonParser;
|
|
408602
408932
|
return parser(req, res, (error3) => {
|
|
408603
408933
|
if (error3?.type === "entity.too.large") return res.status(413).json({ error: "payload-too-large" });
|
|
408604
408934
|
return next(error3);
|
|
@@ -410014,6 +410344,7 @@ var init_server2 = __esm({
|
|
|
410014
410344
|
init_api_error();
|
|
410015
410345
|
init_project_store_resolver();
|
|
410016
410346
|
init_chat_project_services();
|
|
410347
|
+
init_file_service();
|
|
410017
410348
|
init_terminal_viewport();
|
|
410018
410349
|
init_terminal_service();
|
|
410019
410350
|
init_terminal();
|
|
@@ -431037,7 +431368,7 @@ var init_mcp_memory_server = __esm({
|
|
|
431037
431368
|
|
|
431038
431369
|
// src/project-resolver.ts
|
|
431039
431370
|
import { existsSync as existsSync126, statSync as statSync21 } from "node:fs";
|
|
431040
|
-
import { basename as basename43, dirname as dirname77, join as join153, normalize as
|
|
431371
|
+
import { basename as basename43, dirname as dirname77, join as join153, normalize as normalize11, resolve as resolve97 } from "node:path";
|
|
431041
431372
|
import { createInterface as createInterface5 } from "node:readline/promises";
|
|
431042
431373
|
async function getCentralCore() {
|
|
431043
431374
|
if (!centralCoreInstance) {
|
|
@@ -431134,8 +431465,8 @@ Run \`fn project remove ` + match.name + "` to clean up the registry entry.",
|
|
|
431134
431465
|
const fusionDir = findKbDir(cwd);
|
|
431135
431466
|
if (fusionDir) {
|
|
431136
431467
|
const allProjects2 = await central.listProjects();
|
|
431137
|
-
const normalizedKbDir =
|
|
431138
|
-
const match = allProjects2.find((p2) =>
|
|
431468
|
+
const normalizedKbDir = normalize11(fusionDir);
|
|
431469
|
+
const match = allProjects2.find((p2) => normalize11(p2.path) === normalizedKbDir);
|
|
431139
431470
|
if (match) {
|
|
431140
431471
|
if (!existsSync126(match.path)) {
|
|
431141
431472
|
throw new ProjectResolutionError(
|