@runfusion/fusion 0.74.0-beta.2 → 0.74.0-beta.3
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 +847 -655
- package/dist/child-process-worker.js +414 -231
- package/dist/client/.vite/manifest.json +268 -268
- package/dist/client/assets/{AgentDetailView-DH4Cn5iG.js → AgentDetailView-D0J_GDct.js} +1 -1
- package/dist/client/assets/{AgentPermissionPolicyEditor-BycC7HhP.js → AgentPermissionPolicyEditor-BBAwprvO.js} +1 -1
- package/dist/client/assets/{AgentsView-Dkij8Sm8.js → AgentsView-DTY5DIav.js} +3 -3
- package/dist/client/assets/{ChatView-DAsZY1We.js → ChatView-CRoW2tRJ.js} +1 -1
- package/dist/client/assets/{CommandCenter-jTzBo-pg.js → CommandCenter-BieyKvKb.js} +1 -1
- package/dist/client/assets/{DevServerView-CxlacYJp.js → DevServerView-CLR1LmhK.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-DsCk3Nsw.js → DirectoryPicker-B-olNAvT.js} +1 -1
- package/dist/client/assets/{DocumentsView-8EI6KNBY.js → DocumentsView-CG_-pnXU.js} +1 -1
- package/dist/client/assets/{EvalsView-kYnaIk11.js → EvalsView-Xp0zCUlr.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-ce6DROKD.js → ExperimentalAgentOnboardingModal-z8m5V5yv.js} +1 -1
- package/dist/client/assets/{GoalsView-BVlTtw-u.js → GoalsView-Bd-4g949.js} +1 -1
- package/dist/client/assets/{InsightsView-BKQeROkb.js → InsightsView-tCgg66wJ.js} +1 -1
- package/dist/client/assets/{MemoryView-DCzTnxsV.js → MemoryView-Dqgsd_0E.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-C94HwDz5.js → PiExtensionsManager-GzGoWgb5.js} +1 -1
- package/dist/client/assets/{PluginManager-C3DegklW.js → PluginManager-BWdqEqr9.js} +1 -1
- package/dist/client/assets/{PullRequestView-CrE89UL7.js → PullRequestView-CQq1G1kY.js} +1 -1
- package/dist/client/assets/{ReportModal-BD1ZUDdV.js → ReportModal-C-3rBFhw.js} +1 -1
- package/dist/client/assets/{ResearchView-eMiRw1EM.js → ResearchView-CS1HwDg_.js} +1 -1
- package/dist/client/assets/{SecretsView-6qK8Idb7.js → SecretsView-C6RP9Rkw.js} +1 -1
- package/dist/client/assets/{SessionTerminal-DtFLyUaK.js → SessionTerminal-C9nCSefk.js} +1 -1
- package/dist/client/assets/SettingsModal-C1oEklYy.css +1 -0
- package/dist/client/assets/{SettingsModal-BkOSlfyr.js → SettingsModal-D3DCvCfP.js} +1 -1
- package/dist/client/assets/{SettingsModal-DSq-_qwo.js → SettingsModal-D4mjT8hw.js} +3 -3
- package/dist/client/assets/{SettingsTextareaRow-Btyx5kaR.js → SettingsTextareaRow-zBVXQbcC.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-Blhx-dRt.js → SetupWizardModal-C9r1dd-h.js} +2 -2
- package/dist/client/assets/{SkillsView-DGE5kYc3.js → SkillsView-DFyUBWd8.js} +1 -1
- package/dist/client/assets/{TodoView-YSGI7ATd.js → TodoView-BC8f952m.js} +1 -1
- package/dist/client/assets/{WorkflowNodeEditor-CZiEThu2.js → WorkflowNodeEditor-UTX757WH.js} +1 -1
- package/dist/client/assets/{agent-import-generation-DQeNZMAm.js → agent-import-generation-DN0q1xEo.js} +1 -1
- package/dist/client/assets/{app-DJIiVHdA.js → app-BP57l_5h.js} +1 -1
- package/dist/client/assets/{app-hQw_x2KJ.js → app-D4frL0Ka.js} +1 -1
- package/dist/client/assets/{app-DaikOPcV.js → app-niWDkeV8.js} +3 -3
- package/dist/client/assets/{app-Dn1u7zJ9.js → app-sLOwmHAE.js} +1 -1
- package/dist/client/assets/{app-DMZ_RusZ.js → app-vRWwM-aD.js} +1 -1
- package/dist/client/assets/{app-DxTbBEMH.js → app-zPyey_fg.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-DhaJNKa4.js → architectureDiagram-3BPJPVTR-D-nqJ95-.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-pKpg7gJU.js → blockDiagram-GPEHLZMM--OlkXlyS.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-DVPXKJnr.js → c4Diagram-AAUBKEIU-CH7PaWgs.js} +1 -1
- package/dist/client/assets/channel-FJNMzlkv.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-CHt90T5z.js → chunk-2J33WTMH-CpHDwqg2.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-DsWFYy0a.js → chunk-4BX2VUAB-BdW4tdnc.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-D8XiXEo8.js → chunk-55IACEB6-b3tBFiuH.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-DlUYBn_Y.js → chunk-727SXJPM-DifknEtv.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-BSdPOr9q.js → chunk-AQP2D5EJ-DHIZNZ4Z.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-B9IAeBld.js → chunk-FMBD7UC4-BLRLYW2y.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-CmxnYQrR.js → chunk-ND2GUHAM-Dh8f06Ld.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-Dxp6Po_j.js → chunk-QZHKN3VN-CpzUiemt.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BOenhjVP.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BOenhjVP.js +1 -0
- package/dist/client/assets/common-13IRj_PO.js +1 -0
- package/dist/client/assets/common-BBynATth.js +1 -0
- package/dist/client/assets/common-BG-vw0tL.js +1 -0
- package/dist/client/assets/common-DSo46rnH.js +1 -0
- package/dist/client/assets/common-DbOCE-0i.js +1 -0
- package/dist/client/assets/common-DnXF4Jmj.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BNl3bhpS.js → cose-bilkent-S5V4N54A-CE2RJ_k0.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-DFs7WCcz.js → dagre-BM42HDAG-CCs_xlYw.js} +1 -1
- package/dist/client/assets/{dashboard-view-vc8kgLMs.js → dashboard-view-D5qv61fP.js} +1 -1
- package/dist/client/assets/{dashboard-view-ZGxLEL9r.js → dashboard-view-Eg7Jyvnm.js} +1 -1
- package/dist/client/assets/{dashboard-view-DnoTcnIo.js → dashboard-view-Z6H-k0uR.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-C2PVozvu.js → diagram-2AECGRRQ-CF60Q-de.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-DFJ5b9H8.js → diagram-5GNKFQAL-DDcGMlua.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-D335CW-c.js → diagram-KO2AKTUF-YgvJ4DQf.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DSfhroAW.js → diagram-LMA3HP47-Bi-j9nGw.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-8iGveM-Z.js → diagram-OG6HWLK6-DEmMqnwc.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-Xc56MEEi.js → erDiagram-TEJ5UH35-DwmZwhiW.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-DjCU9WbD.js → flowDiagram-I6XJVG4X-B5ITa_HJ.js} +1 -1
- package/dist/client/assets/{folder-open-BRrB44UX.js → folder-open-BVxA7YtM.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-CXQE1bea.js → ganttDiagram-6RSMTGT7-BgAPwh9w.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII--mrSABLC.js → gitGraphDiagram-PVQCEYII-DTtGzZFj.js} +1 -1
- package/dist/client/assets/{index-C2xsZoNG.js → index-idkERMsE.js} +289 -289
- package/dist/client/assets/{infoDiagram-5YYISTIA-CTS_nljP.js → infoDiagram-5YYISTIA-CkSnTje1.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CpV5x3gk.js → ishikawaDiagram-YF4QCWOH-jn5VNpMh.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-BK2QCMnt.js → journeyDiagram-JHISSGLW-BpaqoXaI.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-B4srpspY.js → kanban-definition-UN3LZRKU-CpfjJoBN.js} +1 -1
- package/dist/client/assets/{mermaid.core-B8DVAt21.js → mermaid.core-DZNGPQzF.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-CsmmiPpA.js → mindmap-definition-RKZ34NQL-BdbX1EJa.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-BahLDqf4.js → pieDiagram-4H26LBE5-Xn0322tJ.js} +1 -1
- package/dist/client/assets/{puzzle-BFPQsuIa.js → puzzle-CPx1RP2g.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Bo3u0wTU.js → quadrantDiagram-W4KKPZXB-OOHqqw5y.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-DYkYsz7d.js → requirementDiagram-4Y6WPE33-Dvj6zvaB.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BCbh0BSY.js → sankeyDiagram-5OEKKPKP-DEkEqthK.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-C2AP9GlE.js → sequenceDiagram-3UESZ5HK-BPc9ncPj.js} +1 -1
- package/dist/client/assets/{shield-alert-tlZOi02c.js → shield-alert-baoKRyU6.js} +1 -1
- package/dist/client/assets/{standing-instructions-template-C0Rzv9Mr.js → standing-instructions-template-CkqsGC1L.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CARkH0ug.js → stateDiagram-AJRCARHV-DywhjHIx.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-C2He043y.js → stateDiagram-v2-BHNVJYJU-D7t7x4uT.js} +1 -1
- package/dist/client/assets/{timeline-definition-PNZ67QCA-MvbYneEM.js → timeline-definition-PNZ67QCA-BH3CKgrg.js} +1 -1
- package/dist/client/assets/{upload-DWK0F17Z.js → upload-DtIvOGwM.js} +1 -1
- package/dist/client/assets/{users-1AkDfM14.js → users-DcxsxPlj.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-DOjzzLqc.js → vennDiagram-CIIHVFJN-CCDllBgJ.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-Di70vhu2.js → wardley-L42UT6IY-C43hFLXh.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-nK6ARhDv.js → wardleyDiagram-YWT4CUSO-__f9rI8w.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-C7_y76B7.js → xychartDiagram-2RQKCTM6-DY-fpQ_x.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/version.json +1 -1
- package/dist/extension.js +551 -368
- package/package.json +1 -1
- package/skill/fusion/references/engine-tools.md +1 -1
- package/dist/client/assets/SettingsModal-BTfZlghk.css +0 -1
- package/dist/client/assets/channel-xFBfK0sX.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BFBVD968.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BFBVD968.js +0 -1
- package/dist/client/assets/common-BDp2y6CF.js +0 -1
- package/dist/client/assets/common-BnEo5fLN.js +0 -1
- package/dist/client/assets/common-Bzr_Y8h4.js +0 -1
- package/dist/client/assets/common-CvwyFzfv.js +0 -1
- package/dist/client/assets/common-CztCXRkz.js +0 -1
- package/dist/client/assets/common-DPe2mbb7.js +0 -1
|
@@ -5603,7 +5603,7 @@ var init_sql = __esm({
|
|
|
5603
5603
|
return new SQL([new StringChunk(str)]);
|
|
5604
5604
|
}
|
|
5605
5605
|
sql2.raw = raw;
|
|
5606
|
-
function
|
|
5606
|
+
function join85(chunks, separator) {
|
|
5607
5607
|
const result = [];
|
|
5608
5608
|
for (const [i, chunk] of chunks.entries()) {
|
|
5609
5609
|
if (i > 0 && separator !== void 0) {
|
|
@@ -5613,7 +5613,7 @@ var init_sql = __esm({
|
|
|
5613
5613
|
}
|
|
5614
5614
|
return new SQL(result);
|
|
5615
5615
|
}
|
|
5616
|
-
sql2.join =
|
|
5616
|
+
sql2.join = join85;
|
|
5617
5617
|
function identifier(value) {
|
|
5618
5618
|
return new Name(value);
|
|
5619
5619
|
}
|
|
@@ -10261,7 +10261,7 @@ var init_select2 = __esm({
|
|
|
10261
10261
|
const baseTableName = this.tableName;
|
|
10262
10262
|
const tableName = getTableLikeName(table);
|
|
10263
10263
|
for (const item of extractUsedTable(table)) this.usedTables.add(item);
|
|
10264
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
10264
|
+
if (typeof tableName === "string" && this.config.joins?.some((join85) => join85.alias === tableName)) {
|
|
10265
10265
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
10266
10266
|
}
|
|
10267
10267
|
if (!this.isPartialSelect) {
|
|
@@ -11796,7 +11796,7 @@ var init_update = __esm({
|
|
|
11796
11796
|
createJoin(joinType) {
|
|
11797
11797
|
return (table, on) => {
|
|
11798
11798
|
const tableName = getTableLikeName(table);
|
|
11799
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
11799
|
+
if (typeof tableName === "string" && this.config.joins.some((join85) => join85.alias === tableName)) {
|
|
11800
11800
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
11801
11801
|
}
|
|
11802
11802
|
if (typeof on === "function") {
|
|
@@ -11892,10 +11892,10 @@ var init_update = __esm({
|
|
|
11892
11892
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
11893
11893
|
fields[tableName] = fromFields;
|
|
11894
11894
|
}
|
|
11895
|
-
for (const
|
|
11896
|
-
const tableName2 = getTableLikeName(
|
|
11897
|
-
if (typeof tableName2 === "string" && !is(
|
|
11898
|
-
const fromFields = this.getTableLikeFields(
|
|
11895
|
+
for (const join85 of this.config.joins) {
|
|
11896
|
+
const tableName2 = getTableLikeName(join85.table);
|
|
11897
|
+
if (typeof tableName2 === "string" && !is(join85.table, SQL)) {
|
|
11898
|
+
const fromFields = this.getTableLikeFields(join85.table);
|
|
11899
11899
|
fields[tableName2] = fromFields;
|
|
11900
11900
|
}
|
|
11901
11901
|
}
|
|
@@ -20499,26 +20499,26 @@ function validateParallelism(nodes2, outgoing, nodesById) {
|
|
|
20499
20499
|
}
|
|
20500
20500
|
const joinsReached = /* @__PURE__ */ new Set();
|
|
20501
20501
|
for (const edge of branchEdges) {
|
|
20502
|
-
const
|
|
20503
|
-
if (!
|
|
20502
|
+
const join86 = walkBranchToJoin(edge.to, split.id, outgoing, nodesById);
|
|
20503
|
+
if (!join86) {
|
|
20504
20504
|
throw new WorkflowIrError(`split '${split.id}' has a branch with no reachable matching join`);
|
|
20505
20505
|
}
|
|
20506
|
-
joinsReached.add(
|
|
20506
|
+
joinsReached.add(join86);
|
|
20507
20507
|
}
|
|
20508
20508
|
if (joinsReached.size !== 1) {
|
|
20509
20509
|
throw new WorkflowIrError(`split '${split.id}' branches converge on more than one join`);
|
|
20510
20510
|
}
|
|
20511
20511
|
const joinId = [...joinsReached][0];
|
|
20512
|
-
const
|
|
20513
|
-
const mode =
|
|
20512
|
+
const join85 = nodesById.get(joinId);
|
|
20513
|
+
const mode = join85.config?.mode;
|
|
20514
20514
|
if (mode && typeof mode === "object" && "quorum" in mode) {
|
|
20515
20515
|
const n = mode.quorum;
|
|
20516
20516
|
if (typeof n !== "number" || !Number.isInteger(n) || n < 1) {
|
|
20517
|
-
throw new WorkflowIrError(`join '${
|
|
20517
|
+
throw new WorkflowIrError(`join '${join85.id}' quorum must be a positive integer`);
|
|
20518
20518
|
}
|
|
20519
20519
|
if (n > branchEdges.length) {
|
|
20520
20520
|
throw new WorkflowIrError(
|
|
20521
|
-
`join '${
|
|
20521
|
+
`join '${join85.id}' quorum(${n}) exceeds the split's ${branchEdges.length} branches`
|
|
20522
20522
|
);
|
|
20523
20523
|
}
|
|
20524
20524
|
}
|
|
@@ -24074,14 +24074,14 @@ async function detectWorkspaceRepos(dir, runner = runGitCommand, timeout = DEFAU
|
|
|
24074
24074
|
return [];
|
|
24075
24075
|
}
|
|
24076
24076
|
const { stat: stat10 } = await import("node:fs/promises");
|
|
24077
|
-
const { join:
|
|
24077
|
+
const { join: join85 } = await import("node:path");
|
|
24078
24078
|
const found = [];
|
|
24079
24079
|
const EXCLUDED_ENTRIES = /* @__PURE__ */ new Set(["node_modules", ".fusion", ".git", ".pi"]);
|
|
24080
24080
|
for (const entry of entries) {
|
|
24081
24081
|
if (EXCLUDED_ENTRIES.has(entry)) continue;
|
|
24082
|
-
const childDir =
|
|
24082
|
+
const childDir = join85(dir, entry);
|
|
24083
24083
|
try {
|
|
24084
|
-
const s3 = await stat10(
|
|
24084
|
+
const s3 = await stat10(join85(childDir, ".git"));
|
|
24085
24085
|
if (!s3.isDirectory() && !s3.isFile()) continue;
|
|
24086
24086
|
} catch {
|
|
24087
24087
|
continue;
|
|
@@ -24094,9 +24094,9 @@ async function detectWorkspaceRepos(dir, runner = runGitCommand, timeout = DEFAU
|
|
|
24094
24094
|
}
|
|
24095
24095
|
async function isWorkspaceModeExplicitlyDisabled(projectPath) {
|
|
24096
24096
|
try {
|
|
24097
|
-
const { readFile:
|
|
24098
|
-
const { join:
|
|
24099
|
-
const raw = await
|
|
24097
|
+
const { readFile: readFile41 } = await import("node:fs/promises");
|
|
24098
|
+
const { join: join85 } = await import("node:path");
|
|
24099
|
+
const raw = await readFile41(join85(projectPath, ".fusion", "config.json"), "utf-8");
|
|
24100
24100
|
const config3 = JSON.parse(raw);
|
|
24101
24101
|
return config3.settings?.workspaceMode === false;
|
|
24102
24102
|
} catch {
|
|
@@ -24104,12 +24104,12 @@ async function isWorkspaceModeExplicitlyDisabled(projectPath) {
|
|
|
24104
24104
|
}
|
|
24105
24105
|
}
|
|
24106
24106
|
async function setWorkspaceModeInConfig(projectPath, value) {
|
|
24107
|
-
const { readFile:
|
|
24108
|
-
const { join:
|
|
24109
|
-
const configPath =
|
|
24107
|
+
const { readFile: readFile41, writeFile: writeFile31, mkdir: mkdir29 } = await import("node:fs/promises");
|
|
24108
|
+
const { join: join85 } = await import("node:path");
|
|
24109
|
+
const configPath = join85(projectPath, ".fusion", "config.json");
|
|
24110
24110
|
let config3 = {};
|
|
24111
24111
|
try {
|
|
24112
|
-
config3 = JSON.parse(await
|
|
24112
|
+
config3 = JSON.parse(await readFile41(configPath, "utf-8"));
|
|
24113
24113
|
} catch (err) {
|
|
24114
24114
|
if (err?.code !== "ENOENT") throw err;
|
|
24115
24115
|
}
|
|
@@ -24118,7 +24118,7 @@ async function setWorkspaceModeInConfig(projectPath, value) {
|
|
|
24118
24118
|
}
|
|
24119
24119
|
const settings = config3.settings;
|
|
24120
24120
|
settings.workspaceMode = value;
|
|
24121
|
-
await mkdir29(
|
|
24121
|
+
await mkdir29(join85(projectPath, ".fusion"), { recursive: true });
|
|
24122
24122
|
await writeFile31(configPath, JSON.stringify(config3, null, 2), "utf-8");
|
|
24123
24123
|
}
|
|
24124
24124
|
function isInRootRelativePath(entry, pathMod) {
|
|
@@ -24131,12 +24131,12 @@ function isInRootRelativePath(entry, pathMod) {
|
|
|
24131
24131
|
return true;
|
|
24132
24132
|
}
|
|
24133
24133
|
async function loadWorkspaceConfig(rootDir) {
|
|
24134
|
-
const { readFile:
|
|
24134
|
+
const { readFile: readFile41 } = await import("node:fs/promises");
|
|
24135
24135
|
const pathMod = await import("node:path");
|
|
24136
|
-
const { join:
|
|
24137
|
-
const configPath =
|
|
24136
|
+
const { join: join85 } = pathMod;
|
|
24137
|
+
const configPath = join85(rootDir, ".fusion", WORKSPACE_CONFIG_FILENAME);
|
|
24138
24138
|
try {
|
|
24139
|
-
const raw = await
|
|
24139
|
+
const raw = await readFile41(configPath, "utf-8");
|
|
24140
24140
|
const parsed = JSON.parse(raw);
|
|
24141
24141
|
if (parsed !== null && typeof parsed === "object" && "repos" in parsed && Array.isArray(parsed.repos) && parsed.repos.every((r) => typeof r === "string")) {
|
|
24142
24142
|
const rawRepos = parsed.repos;
|
|
@@ -24150,11 +24150,11 @@ async function loadWorkspaceConfig(rootDir) {
|
|
|
24150
24150
|
}
|
|
24151
24151
|
async function saveWorkspaceConfig(rootDir, config3) {
|
|
24152
24152
|
const { mkdir: mkdir29, writeFile: writeFile31 } = await import("node:fs/promises");
|
|
24153
|
-
const { join:
|
|
24154
|
-
const fusionDir =
|
|
24153
|
+
const { join: join85 } = await import("node:path");
|
|
24154
|
+
const fusionDir = join85(rootDir, ".fusion");
|
|
24155
24155
|
await mkdir29(fusionDir, { recursive: true });
|
|
24156
24156
|
await writeFile31(
|
|
24157
|
-
|
|
24157
|
+
join85(fusionDir, WORKSPACE_CONFIG_FILENAME),
|
|
24158
24158
|
JSON.stringify(config3, null, 2),
|
|
24159
24159
|
"utf-8"
|
|
24160
24160
|
);
|
|
@@ -38983,11 +38983,11 @@ async function rekeyFallbackProjectPartition(db, fallbackProjectId, registeredPr
|
|
|
38983
38983
|
const parentKey = `${fk.parentSchema}.${fk.parentTable}`;
|
|
38984
38984
|
if (!targetKeys.has(parentKey)) continue;
|
|
38985
38985
|
const childTarget = targetKeys.has(`${fk.childSchema}.${fk.childTable}`);
|
|
38986
|
-
const
|
|
38986
|
+
const join85 = fk.childColumns.map((column, index2) => `child.${quoteIdentifier(column)} IS NOT DISTINCT FROM parent.${quoteIdentifier(fk.parentColumns[index2])}`).join(" AND ");
|
|
38987
38987
|
const registeredChildScope = childTarget ? ` AND child.${quoteIdentifier("project_id")} = ${quoteLiteral(registeredProjectId)}` : "";
|
|
38988
38988
|
const unreplaced = await tx.execute(sql.raw(`SELECT EXISTS (
|
|
38989
38989
|
SELECT 1 FROM ${relationName({ schema: fk.childSchema, table: fk.childTable })} child
|
|
38990
|
-
JOIN ${relationName({ schema: fk.parentSchema, table: fk.parentTable })} parent ON ${
|
|
38990
|
+
JOIN ${relationName({ schema: fk.parentSchema, table: fk.parentTable })} parent ON ${join85}
|
|
38991
38991
|
JOIN fusion_rekey_conflict_candidates parent_candidate
|
|
38992
38992
|
ON parent_candidate.schema_name = ${quoteLiteral(fk.parentSchema)}
|
|
38993
38993
|
AND parent_candidate.table_name = ${quoteLiteral(fk.parentTable)}
|
|
@@ -49824,8 +49824,8 @@ var require_CronFileParser = __commonJS({
|
|
|
49824
49824
|
* @throws If file cannot be read
|
|
49825
49825
|
*/
|
|
49826
49826
|
static async parseFile(filePath) {
|
|
49827
|
-
const { readFile:
|
|
49828
|
-
const data = await
|
|
49827
|
+
const { readFile: readFile41 } = await Promise.resolve().then(() => __importStar(__require("fs/promises")));
|
|
49828
|
+
const data = await readFile41(filePath, "utf8");
|
|
49829
49829
|
return _CronFileParser.#parseContent(data);
|
|
49830
49830
|
}
|
|
49831
49831
|
/**
|
|
@@ -136300,10 +136300,10 @@ async function runHoldReleaseSweep(store, deps) {
|
|
|
136300
136300
|
}
|
|
136301
136301
|
return result;
|
|
136302
136302
|
}
|
|
136303
|
-
async function issueRelease(store, deps, task, target, ir2) {
|
|
136303
|
+
async function issueRelease(store, deps, task, target, ir2, options = {}) {
|
|
136304
136304
|
const targetColumn = findColumn2(ir2, target);
|
|
136305
136305
|
const targetIsProcessing = targetColumn ? resolveColumnFlags(targetColumn).countsTowardWip === true : false;
|
|
136306
|
-
if (targetIsProcessing && await isUnplannedForExecution(store, task, ir2)) {
|
|
136306
|
+
if (targetIsProcessing && !options.allowUnplanned && await isUnplannedForExecution(store, task, ir2)) {
|
|
136307
136307
|
schedulerLog.log(`Hold release for ${task.id} blocked \u2014 card is unplanned and cannot enter processing column ${target}`);
|
|
136308
136308
|
return false;
|
|
136309
136309
|
}
|
|
@@ -136345,7 +136345,7 @@ async function issueRelease(store, deps, task, target, ir2) {
|
|
|
136345
136345
|
return false;
|
|
136346
136346
|
}
|
|
136347
136347
|
}
|
|
136348
|
-
async function promoteHeldTask(store, taskId, deps = {}) {
|
|
136348
|
+
async function promoteHeldTask(store, taskId, deps = {}, options = {}) {
|
|
136349
136349
|
const task = await store.getTask(taskId);
|
|
136350
136350
|
if (!task) return { released: false, rejection: "task-not-found" };
|
|
136351
136351
|
const ir2 = await resolveWorkflowIrForTask(store, taskId);
|
|
@@ -136356,17 +136356,50 @@ async function promoteHeldTask(store, taskId, deps = {}) {
|
|
|
136356
136356
|
if (!target) return { released: false, rejection: "no-release-target" };
|
|
136357
136357
|
const targetColumn = findColumn2(ir2, target);
|
|
136358
136358
|
const targetIsProcessing = targetColumn ? resolveColumnFlags(targetColumn).countsTowardWip === true : false;
|
|
136359
|
-
|
|
136359
|
+
const unplanned = targetIsProcessing && await isUnplannedForExecution(store, task, ir2);
|
|
136360
|
+
if (unplanned && options.force !== true) {
|
|
136360
136361
|
return { released: false, rejection: "unplanned-for-execution", toColumn: target };
|
|
136361
136362
|
}
|
|
136363
|
+
let promoted = task;
|
|
136364
|
+
if (unplanned) {
|
|
136365
|
+
if (task.status === "needs-replan" || task.status === "plan-review-unavailable") {
|
|
136366
|
+
try {
|
|
136367
|
+
await store.updateTask(task.id, { status: null });
|
|
136368
|
+
promoted = { ...task, status: void 0 };
|
|
136369
|
+
} catch (error2) {
|
|
136370
|
+
schedulerLog.warn(
|
|
136371
|
+
`Force-promote for ${task.id} could not clear replan status: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
136372
|
+
);
|
|
136373
|
+
}
|
|
136374
|
+
}
|
|
136375
|
+
if (typeof store.recordRunAuditEvent === "function") {
|
|
136376
|
+
try {
|
|
136377
|
+
await store.recordRunAuditEvent({
|
|
136378
|
+
domain: "database",
|
|
136379
|
+
mutationType: "task:promote-forced-unplanned",
|
|
136380
|
+
target: task.id,
|
|
136381
|
+
taskId: task.id,
|
|
136382
|
+
agentId: "system",
|
|
136383
|
+
runId: `promote-force-${task.id}`,
|
|
136384
|
+
metadata: { fromColumn: task.column, toColumn: target, priorStatus: task.status ?? null }
|
|
136385
|
+
});
|
|
136386
|
+
} catch {
|
|
136387
|
+
}
|
|
136388
|
+
}
|
|
136389
|
+
schedulerLog.log(`Force-promote for ${task.id} bypassing unplanned gate into ${target} (operator override)`);
|
|
136390
|
+
}
|
|
136362
136391
|
const released = await issueRelease(
|
|
136363
136392
|
store,
|
|
136364
136393
|
{ now: () => Date.now(), reserveSlot: deps.reserveSlot, allocateWorktree: deps.allocateWorktree },
|
|
136365
|
-
|
|
136394
|
+
promoted,
|
|
136366
136395
|
target,
|
|
136367
|
-
ir2
|
|
136396
|
+
ir2,
|
|
136397
|
+
{ allowUnplanned: unplanned }
|
|
136368
136398
|
);
|
|
136369
|
-
|
|
136399
|
+
if (!released) {
|
|
136400
|
+
return { released: false, rejection: "capacity-exhausted-or-no-slot" };
|
|
136401
|
+
}
|
|
136402
|
+
return { released: true, toColumn: target, forcedUnplanned: unplanned || void 0 };
|
|
136370
136403
|
}
|
|
136371
136404
|
async function releaseHeldTaskByEvent(store, taskId, eventTag, deps = {}) {
|
|
136372
136405
|
const task = await store.getTask(taskId);
|
|
@@ -137290,8 +137323,8 @@ var require_graceful_fs = __commonJS({
|
|
|
137290
137323
|
fs10.createReadStream = createReadStream;
|
|
137291
137324
|
fs10.createWriteStream = createWriteStream;
|
|
137292
137325
|
var fs$readFile = fs10.readFile;
|
|
137293
|
-
fs10.readFile =
|
|
137294
|
-
function
|
|
137326
|
+
fs10.readFile = readFile41;
|
|
137327
|
+
function readFile41(path5, options, cb) {
|
|
137295
137328
|
if (typeof options === "function")
|
|
137296
137329
|
cb = options, options = null;
|
|
137297
137330
|
return go$readFile(path5, options, cb);
|
|
@@ -169790,6 +169823,86 @@ var init_research_step_runner = __esm({
|
|
|
169790
169823
|
}
|
|
169791
169824
|
});
|
|
169792
169825
|
|
|
169826
|
+
// ../engine/src/plan-artifact-writeback.ts
|
|
169827
|
+
import { readFile as readFile33 } from "node:fs/promises";
|
|
169828
|
+
import { join as join66 } from "node:path";
|
|
169829
|
+
function relativePromptPath(taskId) {
|
|
169830
|
+
return `.fusion/tasks/${taskId}/PROMPT.md`;
|
|
169831
|
+
}
|
|
169832
|
+
async function readIfPresent(path5) {
|
|
169833
|
+
try {
|
|
169834
|
+
return await readFile33(path5, "utf-8");
|
|
169835
|
+
} catch {
|
|
169836
|
+
return null;
|
|
169837
|
+
}
|
|
169838
|
+
}
|
|
169839
|
+
async function reconcileWorktreePlanArtifact(options) {
|
|
169840
|
+
const { store, taskId, rootDir, planningCwd, logger: logger3 } = options;
|
|
169841
|
+
const relPath = relativePromptPath(taskId);
|
|
169842
|
+
const rootContent = await readIfPresent(join66(rootDir, relPath));
|
|
169843
|
+
if (planningCwd === rootDir) {
|
|
169844
|
+
return { outcome: "not-worktree", content: rootContent ?? void 0 };
|
|
169845
|
+
}
|
|
169846
|
+
const worktreeContent = await readIfPresent(join66(planningCwd, relPath));
|
|
169847
|
+
if (worktreeContent === null) {
|
|
169848
|
+
return { outcome: "no-worktree-artifact", content: rootContent ?? void 0 };
|
|
169849
|
+
}
|
|
169850
|
+
if (worktreeContent.trim().length === 0) {
|
|
169851
|
+
return { outcome: "worktree-artifact-empty", content: rootContent ?? void 0 };
|
|
169852
|
+
}
|
|
169853
|
+
if (rootContent === worktreeContent) {
|
|
169854
|
+
return { outcome: "already-authoritative", content: rootContent };
|
|
169855
|
+
}
|
|
169856
|
+
try {
|
|
169857
|
+
await store.updateTask(taskId, { prompt: worktreeContent });
|
|
169858
|
+
logger3?.log?.(
|
|
169859
|
+
`${taskId}: recovered a worktree-local PROMPT.md into the project .fusion folder (${planningCwd})`
|
|
169860
|
+
);
|
|
169861
|
+
return { outcome: "recovered", content: worktreeContent };
|
|
169862
|
+
} catch (error2) {
|
|
169863
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
169864
|
+
logger3?.warn?.(
|
|
169865
|
+
`${taskId}: failed to copy the worktree-local PROMPT.md back into the project .fusion folder: ${message}`
|
|
169866
|
+
);
|
|
169867
|
+
return { outcome: "recovery-failed", content: rootContent ?? void 0 };
|
|
169868
|
+
}
|
|
169869
|
+
}
|
|
169870
|
+
async function mirrorPlanToProjectDb(store, taskId, content, options = {}) {
|
|
169871
|
+
if (content.trim().length === 0) return false;
|
|
169872
|
+
if (typeof store.upsertTaskDocument !== "function") return false;
|
|
169873
|
+
try {
|
|
169874
|
+
if (typeof store.getTaskDocument === "function") {
|
|
169875
|
+
const existing = await store.getTaskDocument(taskId, PLAN_DOCUMENT_KEY);
|
|
169876
|
+
if (typeof existing?.content === "string" && existing.content === content) return false;
|
|
169877
|
+
}
|
|
169878
|
+
await store.upsertTaskDocument(taskId, {
|
|
169879
|
+
key: PLAN_DOCUMENT_KEY,
|
|
169880
|
+
content,
|
|
169881
|
+
author: options.author ?? "agent"
|
|
169882
|
+
});
|
|
169883
|
+
return true;
|
|
169884
|
+
} catch (error2) {
|
|
169885
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
169886
|
+
options.logger?.warn?.(`${taskId}: failed to mirror the plan into the project database: ${message}`);
|
|
169887
|
+
return false;
|
|
169888
|
+
}
|
|
169889
|
+
}
|
|
169890
|
+
async function persistPlanArtifact(options) {
|
|
169891
|
+
const result = await reconcileWorktreePlanArtifact(options);
|
|
169892
|
+
const mirrored = result.content ? await mirrorPlanToProjectDb(options.store, options.taskId, result.content, {
|
|
169893
|
+
author: options.author,
|
|
169894
|
+
logger: options.logger
|
|
169895
|
+
}) : false;
|
|
169896
|
+
return { ...result, mirrored };
|
|
169897
|
+
}
|
|
169898
|
+
var PLAN_DOCUMENT_KEY;
|
|
169899
|
+
var init_plan_artifact_writeback = __esm({
|
|
169900
|
+
"../engine/src/plan-artifact-writeback.ts"() {
|
|
169901
|
+
"use strict";
|
|
169902
|
+
PLAN_DOCUMENT_KEY = "plan";
|
|
169903
|
+
}
|
|
169904
|
+
});
|
|
169905
|
+
|
|
169793
169906
|
// ../engine/src/auto-recovery-handlers/message-delivery.ts
|
|
169794
169907
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
169795
169908
|
var baseLog, MessageDeliveryAutoRecoveryHandler;
|
|
@@ -170817,9 +170930,9 @@ function assertInRootRepoRelPath(repoRelPath, sep12, isAbsolute22, normalize8) {
|
|
|
170817
170930
|
async function acquireWorkspaceRepoWorktree(opts) {
|
|
170818
170931
|
const { repoRelPath, workspaceRootDir, task, store, settings, logger: logger3, secretsStore, audit, runContext, runConfiguredCommand: runConfiguredCommand2, taskEnv } = opts;
|
|
170819
170932
|
const registry3 = opts.registry ?? activeSessionRegistry;
|
|
170820
|
-
const { join:
|
|
170933
|
+
const { join: join85, isAbsolute: isAbsolute22, normalize: normalize8, sep: sep12 } = await import("node:path");
|
|
170821
170934
|
assertInRootRepoRelPath(repoRelPath, sep12, isAbsolute22, normalize8);
|
|
170822
|
-
const repoAbsPath =
|
|
170935
|
+
const repoAbsPath = join85(workspaceRootDir, repoRelPath);
|
|
170823
170936
|
const existing = task.workspaceWorktrees?.[repoRelPath];
|
|
170824
170937
|
if (existing) {
|
|
170825
170938
|
let live = existsSync52(existing.worktreePath);
|
|
@@ -171008,11 +171121,11 @@ var init_worktree_acquisition = __esm({
|
|
|
171008
171121
|
});
|
|
171009
171122
|
|
|
171010
171123
|
// ../engine/src/agent-tools.ts
|
|
171011
|
-
import { appendFile as appendFile3, mkdir as mkdir26, readFile as
|
|
171124
|
+
import { appendFile as appendFile3, mkdir as mkdir26, readFile as readFile34, readdir as readdir9, realpath, stat as stat9, writeFile as writeFile25 } from "node:fs/promises";
|
|
171012
171125
|
import { existsSync as existsSync53 } from "node:fs";
|
|
171013
171126
|
import { createHash as createHash16, randomUUID as randomUUID45 } from "node:crypto";
|
|
171014
171127
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
171015
|
-
import { extname as extname3, isAbsolute as isAbsolute13, join as
|
|
171128
|
+
import { extname as extname3, isAbsolute as isAbsolute13, join as join67, relative as relative11, resolve as resolve32, sep as sep9 } from "node:path";
|
|
171016
171129
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
171017
171130
|
function sanitizeAgentMemoryId(agentId) {
|
|
171018
171131
|
return agentId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
@@ -171024,16 +171137,16 @@ function agentDreamsDisplayPath(agentId) {
|
|
|
171024
171137
|
return `${AGENT_MEMORY_ROOT3}/${sanitizeAgentMemoryId(agentId)}/${AGENT_DREAMS_FILENAME2}`;
|
|
171025
171138
|
}
|
|
171026
171139
|
function agentMemoryDirectory(rootDir, agentId) {
|
|
171027
|
-
return
|
|
171140
|
+
return join67(rootDir, AGENT_MEMORY_ROOT3, sanitizeAgentMemoryId(agentId));
|
|
171028
171141
|
}
|
|
171029
171142
|
function agentMemoryFilePath(rootDir, agentId) {
|
|
171030
|
-
return
|
|
171143
|
+
return join67(agentMemoryDirectory(rootDir, agentId), AGENT_MEMORY_FILENAME2);
|
|
171031
171144
|
}
|
|
171032
171145
|
function agentDreamsFilePath(rootDir, agentId) {
|
|
171033
|
-
return
|
|
171146
|
+
return join67(agentMemoryDirectory(rootDir, agentId), AGENT_DREAMS_FILENAME2);
|
|
171034
171147
|
}
|
|
171035
171148
|
function agentDailyFilePath(rootDir, agentId, date5 = /* @__PURE__ */ new Date()) {
|
|
171036
|
-
return
|
|
171149
|
+
return join67(agentMemoryDirectory(rootDir, agentId), `${date5.toISOString().slice(0, 10)}.md`);
|
|
171037
171150
|
}
|
|
171038
171151
|
async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
|
|
171039
171152
|
const safeRoot = typeof rootDir === "string" ? rootDir.trim() : "";
|
|
@@ -171047,7 +171160,7 @@ async function readAgentMemoryWorkspaceLongTerm(rootDir, agentId) {
|
|
|
171047
171160
|
if (!fileStat.isFile()) {
|
|
171048
171161
|
return "";
|
|
171049
171162
|
}
|
|
171050
|
-
const content = await
|
|
171163
|
+
const content = await readFile34(filePath, "utf-8");
|
|
171051
171164
|
return typeof content === "string" ? content.trim() : "";
|
|
171052
171165
|
} catch {
|
|
171053
171166
|
return "";
|
|
@@ -171126,7 +171239,7 @@ async function listAgentMemoryFiles2(rootDir, agentMemory) {
|
|
|
171126
171239
|
}
|
|
171127
171240
|
for (const entry of entries) {
|
|
171128
171241
|
if (!DAILY_AGENT_MEMORY_RE2.test(entry)) continue;
|
|
171129
|
-
const absPath =
|
|
171242
|
+
const absPath = join67(dir, entry);
|
|
171130
171243
|
const fileStat = await stat9(absPath);
|
|
171131
171244
|
if (fileStat.isFile()) {
|
|
171132
171245
|
files.push({
|
|
@@ -171149,7 +171262,7 @@ async function searchAgentMemoryFile(rootDir, agentMemory, query, limit) {
|
|
|
171149
171262
|
}
|
|
171150
171263
|
const results = [];
|
|
171151
171264
|
for (const file of await listAgentMemoryFiles2(rootDir, agentMemory)) {
|
|
171152
|
-
const content = await
|
|
171265
|
+
const content = await readFile34(file.absPath, "utf-8");
|
|
171153
171266
|
const lines = content.split("\n");
|
|
171154
171267
|
for (let index2 = 0; index2 < lines.length; index2 += 8) {
|
|
171155
171268
|
const chunk = lines.slice(index2, index2 + 12).join("\n").trim();
|
|
@@ -171295,7 +171408,7 @@ function resolveAgentMemoryPath(rootDir, agentId, path5) {
|
|
|
171295
171408
|
return null;
|
|
171296
171409
|
}
|
|
171297
171410
|
return {
|
|
171298
|
-
absPath:
|
|
171411
|
+
absPath: join67(agentMemoryDirectory(rootDir, agentId), filename),
|
|
171299
171412
|
displayPath: `${prefix}${filename}`
|
|
171300
171413
|
};
|
|
171301
171414
|
}
|
|
@@ -171305,7 +171418,7 @@ async function getAgentMemoryWindow(rootDir, agentMemory, path5, startLine = 1,
|
|
|
171305
171418
|
return null;
|
|
171306
171419
|
}
|
|
171307
171420
|
await syncAgentMemoryFile(rootDir, agentMemory);
|
|
171308
|
-
const content = await
|
|
171421
|
+
const content = await readFile34(resolved.absPath, "utf-8");
|
|
171309
171422
|
const lines = content.split("\n");
|
|
171310
171423
|
const start = Math.max(1, Math.floor(startLine));
|
|
171311
171424
|
const count2 = Math.max(1, Math.min(Math.floor(lineCount), 200));
|
|
@@ -171955,6 +172068,9 @@ function createTaskPromptWriteTool(store, taskId, runContext) {
|
|
|
171955
172068
|
if (persisted?.prompt !== params.content) {
|
|
171956
172069
|
throw new Error("authoritative PROMPT.md read-back did not match the requested content; persistence could not be verified");
|
|
171957
172070
|
}
|
|
172071
|
+
await mirrorPlanToProjectDb(store, taskId, params.content, {
|
|
172072
|
+
author: runContext?.agentId ?? "agent"
|
|
172073
|
+
});
|
|
171958
172074
|
return {
|
|
171959
172075
|
content: [{ type: "text", text: `Updated PROMPT.md for ${taskId}.` }],
|
|
171960
172076
|
details: {}
|
|
@@ -172169,7 +172285,7 @@ async function readArtifactFileFromPath(params, baseDir) {
|
|
|
172169
172285
|
if (!mimeType) {
|
|
172170
172286
|
throw new Error(`Could not infer a MIME type from ${resolvedPath}; pass mimeType explicitly.`);
|
|
172171
172287
|
}
|
|
172172
|
-
const data = await
|
|
172288
|
+
const data = await readFile34(canonicalPath);
|
|
172173
172289
|
if (params.type === "image") {
|
|
172174
172290
|
if (!mimeType.startsWith("image/")) {
|
|
172175
172291
|
throw new Error(`image artifacts require an image/* mimeType, got ${mimeType}.`);
|
|
@@ -172512,25 +172628,33 @@ function createTaskPromoteTool(store, currentTaskId) {
|
|
|
172512
172628
|
return {
|
|
172513
172629
|
name: "fn_task_promote",
|
|
172514
172630
|
label: "Promote Held Task",
|
|
172515
|
-
description: "Manually promote a held task out of its hold column, releasing it regardless of the hold's release kind (the explicit operator action a 'manual' hold waits for). Defaults to the current task. Returns the destination column, or a rejection reason when the task is not held or the destination is full.",
|
|
172631
|
+
description: "Manually promote a held task out of its hold column, releasing it regardless of the hold's release kind (the explicit operator action a 'manual' hold waits for). Defaults to the current task. Returns the destination column, or a rejection reason when the task is not held or the destination is full. Pass force:true to start execution even when planning or plan review is still outstanding (that waives the plan gate and cancels the pending replan; capacity still applies).",
|
|
172516
172632
|
parameters: taskPromoteParams,
|
|
172517
172633
|
execute: async (_id, params) => {
|
|
172518
172634
|
const taskId = params.task_id?.trim() || currentTaskId;
|
|
172635
|
+
const force = params.force === true;
|
|
172519
172636
|
try {
|
|
172520
|
-
const outcome = await promoteHeldTask(store, taskId);
|
|
172637
|
+
const outcome = await promoteHeldTask(store, taskId, {}, { force });
|
|
172521
172638
|
if (outcome.released) {
|
|
172639
|
+
const forcedNote = outcome.forcedUnplanned ? " Forced past the outstanding planning/plan review \u2014 the pending replan was cancelled." : "";
|
|
172522
172640
|
return {
|
|
172523
172641
|
content: [{
|
|
172524
172642
|
type: "text",
|
|
172525
|
-
text: `Promoted ${taskId} to column '${outcome.toColumn}'
|
|
172643
|
+
text: `Promoted ${taskId} to column '${outcome.toColumn}'.${forcedNote}`
|
|
172526
172644
|
}],
|
|
172527
|
-
details: {
|
|
172645
|
+
details: {
|
|
172646
|
+
taskId,
|
|
172647
|
+
released: true,
|
|
172648
|
+
toColumn: outcome.toColumn,
|
|
172649
|
+
forcedUnplanned: outcome.forcedUnplanned === true
|
|
172650
|
+
}
|
|
172528
172651
|
};
|
|
172529
172652
|
}
|
|
172653
|
+
const forceHint = outcome.rejection === "unplanned-for-execution" ? " Pass force:true to start execution anyway." : "";
|
|
172530
172654
|
return {
|
|
172531
172655
|
content: [{
|
|
172532
172656
|
type: "text",
|
|
172533
|
-
text: `ERROR: Could not promote ${taskId}: ${outcome.rejection ?? "unknown"}
|
|
172657
|
+
text: `ERROR: Could not promote ${taskId}: ${outcome.rejection ?? "unknown"}.${forceHint}`
|
|
172534
172658
|
}],
|
|
172535
172659
|
details: { taskId, released: false, rejection: outcome.rejection },
|
|
172536
172660
|
isError: true
|
|
@@ -174713,6 +174837,7 @@ var init_agent_tools = __esm({
|
|
|
174713
174837
|
init_provider_registry();
|
|
174714
174838
|
init_research_step_runner();
|
|
174715
174839
|
init_logger();
|
|
174840
|
+
init_plan_artifact_writeback();
|
|
174716
174841
|
init_web_fetch();
|
|
174717
174842
|
init_agent_action_gate();
|
|
174718
174843
|
init_message_delivery();
|
|
@@ -174892,6 +175017,19 @@ var init_agent_tools = __esm({
|
|
|
174892
175017
|
taskPromoteParams = Type2.Object({
|
|
174893
175018
|
task_id: Type2.Optional(
|
|
174894
175019
|
Type2.String({ description: "Held task to promote. Defaults to the current task." })
|
|
175020
|
+
),
|
|
175021
|
+
/*
|
|
175022
|
+
FNXC:WorkflowScheduling 2026-07-25-05:40:
|
|
175023
|
+
Agent-native parity with the dashboard's force-promote override: an agent that
|
|
175024
|
+
has read the card and judged the pending replan / Plan Review not worth waiting
|
|
175025
|
+
for can start execution anyway. Opt-in per call and never defaulted on — the
|
|
175026
|
+
automatic surfaces (hold-release sweep, webhook release) still cannot force, so
|
|
175027
|
+
FN-7648 holds for everything that is not an explicit promote request.
|
|
175028
|
+
*/
|
|
175029
|
+
force: Type2.Optional(
|
|
175030
|
+
Type2.Boolean({
|
|
175031
|
+
description: "Start execution even when the task is still waiting on planning or plan review (rejection 'unplanned-for-execution'). Waives ONLY that gate \u2014 hold membership and downstream capacity still apply \u2014 and cancels the pending replan. Default false."
|
|
175032
|
+
})
|
|
174895
175033
|
)
|
|
174896
175034
|
});
|
|
174897
175035
|
workflowCreateParams = Type2.Object({
|
|
@@ -175276,8 +175414,8 @@ var init_agent_tools = __esm({
|
|
|
175276
175414
|
});
|
|
175277
175415
|
|
|
175278
175416
|
// ../engine/src/agent-memory-index.ts
|
|
175279
|
-
import { readFile as
|
|
175280
|
-
import { join as
|
|
175417
|
+
import { readFile as readFile35 } from "node:fs/promises";
|
|
175418
|
+
import { join as join68 } from "node:path";
|
|
175281
175419
|
function clampUtf8(input, maxBytes) {
|
|
175282
175420
|
if (Buffer.byteLength(input, "utf8") <= maxBytes) return input;
|
|
175283
175421
|
let out = "";
|
|
@@ -175309,7 +175447,7 @@ function parseHeadings(content) {
|
|
|
175309
175447
|
}
|
|
175310
175448
|
async function buildFileSection(sectionHeader, displayPath, fsPath) {
|
|
175311
175449
|
try {
|
|
175312
|
-
const content = await
|
|
175450
|
+
const content = await readFile35(fsPath, "utf-8");
|
|
175313
175451
|
const headings = parseHeadings(content);
|
|
175314
175452
|
const lines = [sectionHeader, `- ${displayPath}`];
|
|
175315
175453
|
for (const entry of headings) {
|
|
@@ -175325,9 +175463,9 @@ async function buildFileSection(sectionHeader, displayPath, fsPath) {
|
|
|
175325
175463
|
async function buildMemoryIndex({ rootDir, agentId }) {
|
|
175326
175464
|
const safeAgentId = agentId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
175327
175465
|
const agentDisplayPath = `.fusion/agent-memory/${safeAgentId}/MEMORY.md`;
|
|
175328
|
-
const agentFsPath =
|
|
175466
|
+
const agentFsPath = join68(rootDir, ".fusion", "agent-memory", safeAgentId, "MEMORY.md");
|
|
175329
175467
|
const projectDisplayPath = ".fusion/memory/MEMORY.md";
|
|
175330
|
-
const projectFsPath =
|
|
175468
|
+
const projectFsPath = join68(rootDir, ".fusion", "memory", "MEMORY.md");
|
|
175331
175469
|
const [agentSection, projectSection] = await Promise.all([
|
|
175332
175470
|
buildFileSection("## Agent Memory Index (use fn_memory_search / fn_memory_get to read)", agentDisplayPath, agentFsPath),
|
|
175333
175471
|
buildFileSection("## Project Memory Index", projectDisplayPath, projectFsPath)
|
|
@@ -175346,7 +175484,7 @@ var init_agent_memory_index = __esm({
|
|
|
175346
175484
|
});
|
|
175347
175485
|
|
|
175348
175486
|
// ../engine/src/agent-instructions.ts
|
|
175349
|
-
import { readFile as
|
|
175487
|
+
import { readFile as readFile36, writeFile as writeFile26, mkdir as mkdir27, access as access8 } from "node:fs/promises";
|
|
175350
175488
|
import { isAbsolute as isAbsolute14, resolve as resolve33, relative as relative12, normalize as normalize7, sep as sep10, dirname as dirname28 } from "node:path";
|
|
175351
175489
|
function trimAndClamp(value, maxLength, label, agentId) {
|
|
175352
175490
|
const trimmed = value.trim();
|
|
@@ -175407,7 +175545,7 @@ async function resolveAgentHeartbeatProcedure(agent, rootDir) {
|
|
|
175407
175545
|
return null;
|
|
175408
175546
|
}
|
|
175409
175547
|
try {
|
|
175410
|
-
const content = await
|
|
175548
|
+
const content = await readFile36(filePath, "utf-8");
|
|
175411
175549
|
const normalized = trimAndClamp(
|
|
175412
175550
|
content,
|
|
175413
175551
|
MAX_INSTRUCTIONS_TEXT_LENGTH2,
|
|
@@ -175533,7 +175671,7 @@ async function resolveAgentInstructions(agent, rootDir, ratingSummary, inclusion
|
|
|
175533
175671
|
const filePath = resolveValidatedInstructionsPath(agent.instructionsPath, rootDir, agent.id);
|
|
175534
175672
|
if (filePath) {
|
|
175535
175673
|
try {
|
|
175536
|
-
const content = await
|
|
175674
|
+
const content = await readFile36(filePath, "utf-8");
|
|
175537
175675
|
const normalizedContent = trimAndClamp(
|
|
175538
175676
|
content,
|
|
175539
175677
|
MAX_INSTRUCTIONS_TEXT_LENGTH2,
|
|
@@ -177366,10 +177504,10 @@ var init_verification_utils = __esm({
|
|
|
177366
177504
|
import { exec as exec19 } from "node:child_process";
|
|
177367
177505
|
import { createHash as createHash17 } from "node:crypto";
|
|
177368
177506
|
import { existsSync as existsSync54, readFileSync as readFileSync18, writeFileSync as writeFileSync7 } from "node:fs";
|
|
177369
|
-
import { join as
|
|
177507
|
+
import { join as join69 } from "node:path";
|
|
177370
177508
|
import { promisify as promisify24 } from "node:util";
|
|
177371
177509
|
function hasInstallState(rootDir) {
|
|
177372
|
-
return existsSync54(
|
|
177510
|
+
return existsSync54(join69(rootDir, "node_modules")) || existsSync54(join69(rootDir, ".pnp.cjs"));
|
|
177373
177511
|
}
|
|
177374
177512
|
function getConfiguredWorktreeInitCommand(settings) {
|
|
177375
177513
|
const trimmed = settings?.worktreeInitCommand?.trim();
|
|
@@ -177378,17 +177516,17 @@ function getConfiguredWorktreeInitCommand(settings) {
|
|
|
177378
177516
|
function getDependencySyncCommand(rootDir, settings) {
|
|
177379
177517
|
const configuredCommand = getConfiguredWorktreeInitCommand(settings);
|
|
177380
177518
|
if (configuredCommand) return configuredCommand;
|
|
177381
|
-
if (existsSync54(
|
|
177382
|
-
if (existsSync54(
|
|
177383
|
-
if (existsSync54(
|
|
177384
|
-
if (existsSync54(
|
|
177519
|
+
if (existsSync54(join69(rootDir, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
|
|
177520
|
+
if (existsSync54(join69(rootDir, "package-lock.json"))) return "npm install";
|
|
177521
|
+
if (existsSync54(join69(rootDir, "yarn.lock"))) return "yarn install --frozen-lockfile";
|
|
177522
|
+
if (existsSync54(join69(rootDir, "bun.lock")) || existsSync54(join69(rootDir, "bun.lockb"))) {
|
|
177385
177523
|
return "bun install --frozen-lockfile";
|
|
177386
177524
|
}
|
|
177387
177525
|
return null;
|
|
177388
177526
|
}
|
|
177389
177527
|
function computeLockfileHash(rootDir) {
|
|
177390
177528
|
for (const name2 of LOCKFILE_CANDIDATES) {
|
|
177391
|
-
const p2 =
|
|
177529
|
+
const p2 = join69(rootDir, name2);
|
|
177392
177530
|
if (existsSync54(p2)) {
|
|
177393
177531
|
try {
|
|
177394
177532
|
return createHash17("sha256").update(readFileSync18(p2)).digest("hex");
|
|
@@ -177401,7 +177539,7 @@ function computeLockfileHash(rootDir) {
|
|
|
177401
177539
|
}
|
|
177402
177540
|
function readInstallMarker(rootDir) {
|
|
177403
177541
|
try {
|
|
177404
|
-
const value = readFileSync18(
|
|
177542
|
+
const value = readFileSync18(join69(rootDir, INSTALL_MARKER_RELPATH), "utf-8").trim();
|
|
177405
177543
|
return value || null;
|
|
177406
177544
|
} catch {
|
|
177407
177545
|
return null;
|
|
@@ -177409,7 +177547,7 @@ function readInstallMarker(rootDir) {
|
|
|
177409
177547
|
}
|
|
177410
177548
|
function writeInstallMarker(rootDir, hash) {
|
|
177411
177549
|
try {
|
|
177412
|
-
writeFileSync7(
|
|
177550
|
+
writeFileSync7(join69(rootDir, INSTALL_MARKER_RELPATH), hash);
|
|
177413
177551
|
} catch {
|
|
177414
177552
|
}
|
|
177415
177553
|
}
|
|
@@ -177500,7 +177638,7 @@ var init_merge_dependency_sync = __esm({
|
|
|
177500
177638
|
"../engine/src/merge-dependency-sync.ts"() {
|
|
177501
177639
|
"use strict";
|
|
177502
177640
|
execAsync17 = promisify24(exec19);
|
|
177503
|
-
INSTALL_MARKER_RELPATH =
|
|
177641
|
+
INSTALL_MARKER_RELPATH = join69("node_modules", ".fusion-install-marker");
|
|
177504
177642
|
LOCKFILE_CANDIDATES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb", "bun.lock"];
|
|
177505
177643
|
INSTALL_TIMEOUT_MS = 3e5;
|
|
177506
177644
|
}
|
|
@@ -178121,7 +178259,7 @@ var init_merger_glob = __esm({
|
|
|
178121
178259
|
|
|
178122
178260
|
// ../engine/src/merger-workspace-test-commands.ts
|
|
178123
178261
|
import { existsSync as existsSync55, readdirSync as readdirSync9, readFileSync as readFileSync19 } from "node:fs";
|
|
178124
|
-
import { basename as basename18, dirname as dirname31, join as
|
|
178262
|
+
import { basename as basename18, dirname as dirname31, join as join70 } from "node:path";
|
|
178125
178263
|
import { execSync as execSync3 } from "node:child_process";
|
|
178126
178264
|
function quoteArg(value) {
|
|
178127
178265
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
@@ -178152,14 +178290,14 @@ function resolveWorkspacePackageRoots(rootDir, globs) {
|
|
|
178152
178290
|
for (const glob of globs) {
|
|
178153
178291
|
const starIdx = glob.indexOf("*");
|
|
178154
178292
|
if (starIdx === -1) {
|
|
178155
|
-
const candidate =
|
|
178156
|
-
if (existsSync55(
|
|
178293
|
+
const candidate = join70(rootDir, glob);
|
|
178294
|
+
if (existsSync55(join70(candidate, "package.json"))) {
|
|
178157
178295
|
roots.push(glob);
|
|
178158
178296
|
}
|
|
178159
178297
|
continue;
|
|
178160
178298
|
}
|
|
178161
178299
|
const prefix = glob.slice(0, starIdx);
|
|
178162
|
-
const parentDir =
|
|
178300
|
+
const parentDir = join70(rootDir, prefix.replace(/\/$/, ""));
|
|
178163
178301
|
let entries;
|
|
178164
178302
|
try {
|
|
178165
178303
|
entries = readdirSync9(parentDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -178168,8 +178306,8 @@ function resolveWorkspacePackageRoots(rootDir, globs) {
|
|
|
178168
178306
|
}
|
|
178169
178307
|
for (const entry of entries) {
|
|
178170
178308
|
const relPath = `${prefix.replace(/\/$/, "")}/${entry}`;
|
|
178171
|
-
const absPath =
|
|
178172
|
-
if (existsSync55(
|
|
178309
|
+
const absPath = join70(rootDir, relPath);
|
|
178310
|
+
if (existsSync55(join70(absPath, "package.json"))) {
|
|
178173
178311
|
roots.push(relPath);
|
|
178174
178312
|
}
|
|
178175
178313
|
}
|
|
@@ -178192,7 +178330,7 @@ function mapChangedFilesToPackageNames(changedFiles, packageRoots, rootDir) {
|
|
|
178192
178330
|
}
|
|
178193
178331
|
if (bestRoot !== null) {
|
|
178194
178332
|
try {
|
|
178195
|
-
const pkgJsonPath =
|
|
178333
|
+
const pkgJsonPath = join70(rootDir, bestRoot, "package.json");
|
|
178196
178334
|
const raw = readFileSync19(pkgJsonPath, "utf-8");
|
|
178197
178335
|
const parsed = JSON.parse(raw);
|
|
178198
178336
|
if (parsed.name) {
|
|
@@ -178209,7 +178347,7 @@ function packageNamesForFiles(rootDir, files) {
|
|
|
178209
178347
|
if (files.length === 0) return [];
|
|
178210
178348
|
let workspaceContent;
|
|
178211
178349
|
try {
|
|
178212
|
-
workspaceContent = readFileSync19(
|
|
178350
|
+
workspaceContent = readFileSync19(join70(rootDir, "pnpm-workspace.yaml"), "utf-8");
|
|
178213
178351
|
} catch {
|
|
178214
178352
|
return [];
|
|
178215
178353
|
}
|
|
@@ -178220,7 +178358,7 @@ function packageNamesForFiles(rootDir, files) {
|
|
|
178220
178358
|
return mapChangedFilesToPackageNames(files, packageRoots, rootDir);
|
|
178221
178359
|
}
|
|
178222
178360
|
function deriveScopedPnpmTestCommand(rootDir, baseBranch, branch) {
|
|
178223
|
-
const workspacePath =
|
|
178361
|
+
const workspacePath = join70(rootDir, "pnpm-workspace.yaml");
|
|
178224
178362
|
let workspaceContent;
|
|
178225
178363
|
try {
|
|
178226
178364
|
workspaceContent = readFileSync19(workspacePath, "utf-8");
|
|
@@ -178258,7 +178396,7 @@ function deriveScopedPnpmTestCommand(rootDir, baseBranch, branch) {
|
|
|
178258
178396
|
function deriveFileScopedPnpmTestCommand(rootDir, baseBranch, branch) {
|
|
178259
178397
|
let workspaceContent;
|
|
178260
178398
|
try {
|
|
178261
|
-
workspaceContent = readFileSync19(
|
|
178399
|
+
workspaceContent = readFileSync19(join70(rootDir, "pnpm-workspace.yaml"), "utf-8");
|
|
178262
178400
|
} catch {
|
|
178263
178401
|
return null;
|
|
178264
178402
|
}
|
|
@@ -178300,7 +178438,7 @@ function deriveFileScopedPnpmTestCommand(rootDir, baseBranch, branch) {
|
|
|
178300
178438
|
];
|
|
178301
178439
|
for (const candidate of candidates) {
|
|
178302
178440
|
const normalized = candidate.startsWith("./") ? candidate.slice(2) : candidate;
|
|
178303
|
-
if (existsSync55(
|
|
178441
|
+
if (existsSync55(join70(rootDir, normalized))) {
|
|
178304
178442
|
resolvedTests.add(normalized);
|
|
178305
178443
|
}
|
|
178306
178444
|
}
|
|
@@ -178328,7 +178466,7 @@ function deriveFileScopedPnpmTestCommand(rootDir, baseBranch, branch) {
|
|
|
178328
178466
|
let name2 = bestRoot;
|
|
178329
178467
|
try {
|
|
178330
178468
|
const parsed = JSON.parse(
|
|
178331
|
-
readFileSync19(
|
|
178469
|
+
readFileSync19(join70(rootDir, bestRoot, "package.json"), "utf-8")
|
|
178332
178470
|
);
|
|
178333
178471
|
if (parsed.name) name2 = parsed.name;
|
|
178334
178472
|
} catch {
|
|
@@ -178372,8 +178510,8 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
178372
178510
|
};
|
|
178373
178511
|
}
|
|
178374
178512
|
const buildSource = explicitBuildCommand?.trim() ? "explicit" : void 0;
|
|
178375
|
-
if (existsSync55(
|
|
178376
|
-
if (existsSync55(
|
|
178513
|
+
if (existsSync55(join70(rootDir, "pnpm-lock.yaml"))) {
|
|
178514
|
+
if (existsSync55(join70(rootDir, "pnpm-workspace.yaml"))) {
|
|
178377
178515
|
if (baseBranch?.trim() && branch?.trim()) {
|
|
178378
178516
|
try {
|
|
178379
178517
|
const scoped = deriveScopedPnpmTestCommand(rootDir, baseBranch.trim(), branch.trim());
|
|
@@ -178392,13 +178530,13 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
178392
178530
|
}
|
|
178393
178531
|
return { command: "pnpm test", testSource: "inferred", buildSource };
|
|
178394
178532
|
}
|
|
178395
|
-
if (existsSync55(
|
|
178533
|
+
if (existsSync55(join70(rootDir, "yarn.lock"))) {
|
|
178396
178534
|
return { command: "yarn test", testSource: "inferred", buildSource };
|
|
178397
178535
|
}
|
|
178398
|
-
if (existsSync55(
|
|
178536
|
+
if (existsSync55(join70(rootDir, "bun.lock")) || existsSync55(join70(rootDir, "bun.lockb"))) {
|
|
178399
178537
|
return { command: "bun test", testSource: "inferred", buildSource };
|
|
178400
178538
|
}
|
|
178401
|
-
if (existsSync55(
|
|
178539
|
+
if (existsSync55(join70(rootDir, "package-lock.json"))) {
|
|
178402
178540
|
return { command: "npm test", testSource: "inferred", buildSource };
|
|
178403
178541
|
}
|
|
178404
178542
|
return null;
|
|
@@ -181390,7 +181528,7 @@ var init_merger_ref_update_advance = __esm({
|
|
|
181390
181528
|
import { execFile as execFile21, spawn as spawn7 } from "node:child_process";
|
|
181391
181529
|
import { promisify as promisify39 } from "node:util";
|
|
181392
181530
|
import { copyFileSync, existsSync as existsSync56, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync2, readFileSync as readFileSync20, rmSync as rmSync7, writeFileSync as writeFileSync8 } from "node:fs";
|
|
181393
|
-
import { dirname as dirname32, join as
|
|
181531
|
+
import { dirname as dirname32, join as join71 } from "node:path";
|
|
181394
181532
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
181395
181533
|
async function runGit4(args, cwd, timeoutMs) {
|
|
181396
181534
|
const result = await execFileAsync14("git", ["-c", "core.quotePath=false", ...args], {
|
|
@@ -181476,9 +181614,9 @@ async function syncWorktreeToHead(input) {
|
|
|
181476
181614
|
if (mode === "ff-only") {
|
|
181477
181615
|
return { kind: "skipped-dirty", fromSha: previousSha, reason: "ff-only-mode-requires-clean-tree", dirtyFiles, untrackedFiles };
|
|
181478
181616
|
}
|
|
181479
|
-
const stageDir = mkdtempSync2(
|
|
181480
|
-
const patchPath =
|
|
181481
|
-
const untrackedDir =
|
|
181617
|
+
const stageDir = mkdtempSync2(join71(tmpdir7(), "fusion-worktree-sync-"));
|
|
181618
|
+
const patchPath = join71(stageDir, "edits.patch");
|
|
181619
|
+
const untrackedDir = join71(stageDir, "untracked");
|
|
181482
181620
|
let preserveStageDir = false;
|
|
181483
181621
|
try {
|
|
181484
181622
|
mkdirSync9(untrackedDir, { recursive: true });
|
|
@@ -181498,8 +181636,8 @@ async function syncWorktreeToHead(input) {
|
|
|
181498
181636
|
}
|
|
181499
181637
|
}
|
|
181500
181638
|
for (const rel of untrackedFiles) {
|
|
181501
|
-
const src =
|
|
181502
|
-
const dst =
|
|
181639
|
+
const src = join71(worktreePath, rel);
|
|
181640
|
+
const dst = join71(untrackedDir, rel);
|
|
181503
181641
|
try {
|
|
181504
181642
|
mkdirSync9(dirname32(dst), { recursive: true });
|
|
181505
181643
|
copyFileSync(src, dst);
|
|
@@ -181562,13 +181700,13 @@ async function syncWorktreeToHead(input) {
|
|
|
181562
181700
|
const restored = [];
|
|
181563
181701
|
const untrackedSkippedAsTracked = [];
|
|
181564
181702
|
for (const rel of untrackedFiles) {
|
|
181565
|
-
const src =
|
|
181703
|
+
const src = join71(untrackedDir, rel);
|
|
181566
181704
|
if (!existsSync56(src)) continue;
|
|
181567
181705
|
if (trackedAtHead.has(rel)) {
|
|
181568
181706
|
untrackedSkippedAsTracked.push(rel);
|
|
181569
181707
|
continue;
|
|
181570
181708
|
}
|
|
181571
|
-
const dst =
|
|
181709
|
+
const dst = join71(worktreePath, rel);
|
|
181572
181710
|
try {
|
|
181573
181711
|
mkdirSync9(dirname32(dst), { recursive: true });
|
|
181574
181712
|
const data = readFileSync20(src);
|
|
@@ -181637,8 +181775,8 @@ var init_worktree_ref_sync = __esm({
|
|
|
181637
181775
|
|
|
181638
181776
|
// ../engine/src/merger-scope-auto-widen.ts
|
|
181639
181777
|
import { exec as exec28 } from "node:child_process";
|
|
181640
|
-
import { readFile as
|
|
181641
|
-
import { join as
|
|
181778
|
+
import { readFile as readFile37, writeFile as writeFile27 } from "node:fs/promises";
|
|
181779
|
+
import { join as join72 } from "node:path";
|
|
181642
181780
|
import { promisify as promisify40 } from "node:util";
|
|
181643
181781
|
function quoteArg4(value) {
|
|
181644
181782
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -181777,8 +181915,8 @@ function parseScopeEntries(section) {
|
|
|
181777
181915
|
async function appendAutoWidenedScopeToPrompt(params) {
|
|
181778
181916
|
const { store, taskId, files } = params;
|
|
181779
181917
|
if (files.length === 0) return [];
|
|
181780
|
-
const promptPath =
|
|
181781
|
-
const prompt = await
|
|
181918
|
+
const promptPath = join72(store.getTaskDir(taskId), "PROMPT.md");
|
|
181919
|
+
const prompt = await readFile37(promptPath, "utf-8");
|
|
181782
181920
|
const { before, section, after } = splitPromptSections(prompt);
|
|
181783
181921
|
const existing = parseScopeEntries(section);
|
|
181784
181922
|
const toAdd = files.filter((file) => !existing.has(file));
|
|
@@ -181934,7 +182072,7 @@ import * as childProcess4 from "node:child_process";
|
|
|
181934
182072
|
import { promisify as promisify41 } from "node:util";
|
|
181935
182073
|
import { existsSync as existsSync57, readFileSync as readFileSync21, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3, renameSync as renameSync5 } from "node:fs";
|
|
181936
182074
|
import { createHash as createHash19 } from "node:crypto";
|
|
181937
|
-
import { join as
|
|
182075
|
+
import { join as join73, resolve as resolve36 } from "node:path";
|
|
181938
182076
|
import { hostname as hostname2 } from "node:os";
|
|
181939
182077
|
import { Type as Type3 } from "typebox";
|
|
181940
182078
|
function mergerCommitEnv() {
|
|
@@ -182247,7 +182385,7 @@ async function runDeterministicVerification(store, rootDir, taskId, testCommand,
|
|
|
182247
182385
|
const deterministicVerificationMessage = "Running deterministic merge verification" + (hasTestCommand ? ` (test${testSourceDisplayLabel}: ${normalizedTestCommand})` : "") + (hasBuildCommand ? ` (build${buildSource === "inferred" ? " [inferred]" : ""}: ${normalizedBuildCommand})` : "");
|
|
182248
182386
|
await store.logEntry(taskId, deterministicVerificationMessage);
|
|
182249
182387
|
await store.appendAgentLog(taskId, deterministicVerificationMessage, "status", void 0, "merger");
|
|
182250
|
-
const bootstrapScriptPath =
|
|
182388
|
+
const bootstrapScriptPath = join73(rootDir, "scripts/ensure-test-artifacts.mjs");
|
|
182251
182389
|
if (hasTestCommand || hasBuildCommand) {
|
|
182252
182390
|
if (!existsSync57(bootstrapScriptPath)) {
|
|
182253
182391
|
const bootstrapMissingMessage = `${taskId}: [verification:bootstrap] script missing at scripts/ensure-test-artifacts.mjs \u2014 skipping preamble`;
|
|
@@ -182746,7 +182884,7 @@ async function stashTreesEqual(rootDir, aSha, bSha) {
|
|
|
182746
182884
|
}
|
|
182747
182885
|
function writeActiveMergerStatus(rootDir, taskId) {
|
|
182748
182886
|
try {
|
|
182749
|
-
const statusPath =
|
|
182887
|
+
const statusPath = join73(rootDir, ".git", ACTIVE_MERGER_STATUS_FILENAME);
|
|
182750
182888
|
const payload = {
|
|
182751
182889
|
taskId,
|
|
182752
182890
|
pid: process.pid,
|
|
@@ -182775,7 +182913,7 @@ function clearActiveMergerStatus(statusPath, taskId) {
|
|
|
182775
182913
|
}
|
|
182776
182914
|
function readActiveMergerStatus(rootDir) {
|
|
182777
182915
|
try {
|
|
182778
|
-
const statusPath =
|
|
182916
|
+
const statusPath = join73(rootDir, ".git", ACTIVE_MERGER_STATUS_FILENAME);
|
|
182779
182917
|
if (!existsSync57(statusPath)) return null;
|
|
182780
182918
|
const raw = readFileSync21(statusPath, "utf-8");
|
|
182781
182919
|
const parsed = JSON.parse(raw);
|
|
@@ -183348,7 +183486,7 @@ async function findFilesWithConflictMarkers(rootDir, files) {
|
|
|
183348
183486
|
const stillConflicted = [];
|
|
183349
183487
|
for (const file of files) {
|
|
183350
183488
|
try {
|
|
183351
|
-
const fullPath =
|
|
183489
|
+
const fullPath = join73(rootDir, file);
|
|
183352
183490
|
if (!existsSync57(fullPath)) continue;
|
|
183353
183491
|
const { stdout } = await execAsync27(
|
|
183354
183492
|
`git grep -l -e "^<<<<<<< " -e "^=======$" -e "^>>>>>>> " --no-index -- ${quoteArg2(fullPath)}`,
|
|
@@ -183375,7 +183513,7 @@ async function tryRecoverHardFailApply(params) {
|
|
|
183375
183513
|
if (!patchText.trim()) {
|
|
183376
183514
|
mergerLog.warn(`${taskId}: autostash ${sha.slice(0, 7)} produced empty patch; cannot 3-way recover`);
|
|
183377
183515
|
} else {
|
|
183378
|
-
const patchPath =
|
|
183516
|
+
const patchPath = join73(rootDir, ".git", `fusion-autostash-${sha.slice(0, 7)}.patch`);
|
|
183379
183517
|
writeFileSync9(patchPath, patchText, "utf-8");
|
|
183380
183518
|
try {
|
|
183381
183519
|
await execAsync27(`git apply --3way --whitespace=nowarn ${quoteArg2(patchPath)}`, { cwd: rootDir });
|
|
@@ -184863,13 +185001,13 @@ async function isCherryPickInProgress(rootDir) {
|
|
|
184863
185001
|
cwd: rootDir,
|
|
184864
185002
|
encoding: "utf-8"
|
|
184865
185003
|
});
|
|
184866
|
-
const cherryPickHeadPath =
|
|
185004
|
+
const cherryPickHeadPath = join73(rootDir, cherryPickHeadPathOut.trim());
|
|
184867
185005
|
if (existsSync57(cherryPickHeadPath)) return true;
|
|
184868
185006
|
const { stdout: sequencerPathOut } = await execAsync27("git rev-parse --git-path sequencer", {
|
|
184869
185007
|
cwd: rootDir,
|
|
184870
185008
|
encoding: "utf-8"
|
|
184871
185009
|
});
|
|
184872
|
-
const sequencerPath =
|
|
185010
|
+
const sequencerPath = join73(rootDir, sequencerPathOut.trim());
|
|
184873
185011
|
return existsSync57(sequencerPath);
|
|
184874
185012
|
} catch {
|
|
184875
185013
|
return false;
|
|
@@ -189911,7 +190049,7 @@ var agent_reflection_exports = {};
|
|
|
189911
190049
|
__export(agent_reflection_exports, {
|
|
189912
190050
|
AgentReflectionService: () => AgentReflectionService
|
|
189913
190051
|
});
|
|
189914
|
-
import { readFile as
|
|
190052
|
+
import { readFile as readFile40 } from "node:fs/promises";
|
|
189915
190053
|
import { isAbsolute as isAbsolute20, resolve as resolve38 } from "node:path";
|
|
189916
190054
|
var reflectionLog, REFLECTION_SYSTEM_PROMPT, DEFAULT_OUTCOME_LIMIT, AgentReflectionService;
|
|
189917
190055
|
var init_agent_reflection = __esm({
|
|
@@ -190470,7 +190608,7 @@ Rules:
|
|
|
190470
190608
|
if (agent.instructionsPath?.trim()) {
|
|
190471
190609
|
const resolvedPath = isAbsolute20(agent.instructionsPath) ? agent.instructionsPath : resolve38(this.rootDir, agent.instructionsPath);
|
|
190472
190610
|
try {
|
|
190473
|
-
const content = await
|
|
190611
|
+
const content = await readFile40(resolvedPath, "utf-8");
|
|
190474
190612
|
if (content.trim()) {
|
|
190475
190613
|
pieces.push(content.trim());
|
|
190476
190614
|
}
|
|
@@ -194184,9 +194322,9 @@ var Scheduler = class {
|
|
|
194184
194322
|
import { exec as exec37, execFile as execFile26, execSync as execSync7 } from "node:child_process";
|
|
194185
194323
|
import { promisify as promisify52 } from "node:util";
|
|
194186
194324
|
import { setImmediate as setImmediateCb3 } from "node:timers";
|
|
194187
|
-
import { basename as basename19, delimiter as delimiter2, isAbsolute as isAbsolute18, join as
|
|
194325
|
+
import { basename as basename19, delimiter as delimiter2, isAbsolute as isAbsolute18, join as join79, relative as relative16, resolve as resolvePath3 } from "node:path";
|
|
194188
194326
|
import { existsSync as existsSync65, lstatSync as lstatSync3, realpathSync as realpathSync9 } from "node:fs";
|
|
194189
|
-
import { readFile as
|
|
194327
|
+
import { readFile as readFile38, rm as rm12, writeFile as writeFile29 } from "node:fs/promises";
|
|
194190
194328
|
init_src2();
|
|
194191
194329
|
|
|
194192
194330
|
// ../engine/src/auto-merge-finalization.ts
|
|
@@ -194637,16 +194775,16 @@ async function persistBranchState(persistence, state) {
|
|
|
194637
194775
|
);
|
|
194638
194776
|
}
|
|
194639
194777
|
}
|
|
194640
|
-
function resolveJoinConfig(
|
|
194641
|
-
const rawMode =
|
|
194778
|
+
function resolveJoinConfig(join85) {
|
|
194779
|
+
const rawMode = join85.config?.mode;
|
|
194642
194780
|
let mode = "all";
|
|
194643
194781
|
if (rawMode === "all" || rawMode === "any") mode = rawMode;
|
|
194644
194782
|
else if (rawMode && typeof rawMode === "object" && "quorum" in rawMode) {
|
|
194645
194783
|
const n = rawMode.quorum;
|
|
194646
194784
|
if (typeof n === "number" && Number.isInteger(n) && n > 0) mode = { quorum: n };
|
|
194647
|
-
else throw new WorkflowIrError(`join '${
|
|
194785
|
+
else throw new WorkflowIrError(`join '${join85.id}' quorum must be a positive integer`);
|
|
194648
194786
|
}
|
|
194649
|
-
const rawFail =
|
|
194787
|
+
const rawFail = join85.config?.onBranchFailure;
|
|
194650
194788
|
const onBranchFailure = rawFail === "collect" ? "collect" : "fail-fast";
|
|
194651
194789
|
return { mode, onBranchFailure };
|
|
194652
194790
|
}
|
|
@@ -194662,9 +194800,9 @@ async function runSplitJoin(split, env) {
|
|
|
194662
194800
|
if (branchEdges.length === 0) {
|
|
194663
194801
|
throw new WorkflowIrError(`split '${split.id}' has no traversable branches`);
|
|
194664
194802
|
}
|
|
194665
|
-
const
|
|
194666
|
-
if (!
|
|
194667
|
-
const joinConfig = resolveJoinConfig(env.nodeMap.get(
|
|
194803
|
+
const join85 = findMatchingJoin(branchEdges[0].to, env);
|
|
194804
|
+
if (!join85) throw new WorkflowIrError(`split '${split.id}' has no reachable matching join`);
|
|
194805
|
+
const joinConfig = resolveJoinConfig(env.nodeMap.get(join85));
|
|
194668
194806
|
const controller = new AbortController();
|
|
194669
194807
|
const branchCount = branchEdges.length;
|
|
194670
194808
|
const required2 = requiredCompletions(joinConfig.mode, branchCount);
|
|
@@ -194700,7 +194838,7 @@ async function runSplitJoin(split, env) {
|
|
|
194700
194838
|
};
|
|
194701
194839
|
const branchPromises = branchEdges.map((edge) => {
|
|
194702
194840
|
const branchId = edge.to;
|
|
194703
|
-
return walkBranch(branchId,
|
|
194841
|
+
return walkBranch(branchId, join85, env, controller.signal, visitedNodeIds).then((result) => {
|
|
194704
194842
|
branchOutcomes.push({ branchId, outcome: result.outcome, nodeId: result.lastNodeId });
|
|
194705
194843
|
if (result.outcome === "success") succeeded += 1;
|
|
194706
194844
|
else failed += 1;
|
|
@@ -194718,7 +194856,7 @@ async function runSplitJoin(split, env) {
|
|
|
194718
194856
|
});
|
|
194719
194857
|
const outcome = await joinReached;
|
|
194720
194858
|
await Promise.allSettled(branchPromises);
|
|
194721
|
-
return { joinNodeId:
|
|
194859
|
+
return { joinNodeId: join85, outcome, branchOutcomes, visitedNodeIds };
|
|
194722
194860
|
}
|
|
194723
194861
|
async function walkBranch(startNodeId, joinId, env, signal, visitedNodeIds) {
|
|
194724
194862
|
let currentId = startNodeId;
|
|
@@ -198101,11 +198239,11 @@ init_active_session_registry();
|
|
|
198101
198239
|
// ../engine/src/cli-agent/task-session.ts
|
|
198102
198240
|
import { mkdtemp as mkdtemp3, rm as rm10 } from "node:fs/promises";
|
|
198103
198241
|
import { tmpdir as tmpdir8 } from "node:os";
|
|
198104
|
-
import { join as
|
|
198242
|
+
import { join as join75 } from "node:path";
|
|
198105
198243
|
|
|
198106
198244
|
// ../engine/src/cli-agent/hook-scripts.ts
|
|
198107
198245
|
import { mkdir as mkdir28, rm as rm9, writeFile as writeFile28 } from "node:fs/promises";
|
|
198108
|
-
import { join as
|
|
198246
|
+
import { join as join74 } from "node:path";
|
|
198109
198247
|
var HOOK_SCRIPT_NAMES = {
|
|
198110
198248
|
/** Main hook script: POSTs stdin payload (with the hook event name) to the route. */
|
|
198111
198249
|
hook: "fusion-hook.sh",
|
|
@@ -198187,8 +198325,8 @@ exit 0
|
|
|
198187
198325
|
async function writeSessionHookScripts(opts) {
|
|
198188
198326
|
const { sessionId, token, endpointUrl, dir } = opts;
|
|
198189
198327
|
await mkdir28(dir, { recursive: true, mode: 448 });
|
|
198190
|
-
const hookScriptPath =
|
|
198191
|
-
const notifyScriptPath =
|
|
198328
|
+
const hookScriptPath = join74(dir, HOOK_SCRIPT_NAMES.hook);
|
|
198329
|
+
const notifyScriptPath = join74(dir, HOOK_SCRIPT_NAMES.notify);
|
|
198192
198330
|
await writeFile28(hookScriptPath, buildHookScriptContent({ sessionId, token, endpointUrl }), {
|
|
198193
198331
|
mode: 448
|
|
198194
198332
|
});
|
|
@@ -198364,9 +198502,9 @@ var CliTaskSession = class _CliTaskSession {
|
|
|
198364
198502
|
isApproved: opts.isAutonomyApproved ?? (() => false)
|
|
198365
198503
|
});
|
|
198366
198504
|
const root = opts.hookDirRoot ?? tmpdir8();
|
|
198367
|
-
const hookDir = await mkdtemp3(
|
|
198368
|
-
const hookScriptPath =
|
|
198369
|
-
const settingsPath =
|
|
198505
|
+
const hookDir = await mkdtemp3(join75(root, "fusion-cli-hooks-"));
|
|
198506
|
+
const hookScriptPath = join75(hookDir, HOOK_SCRIPT_NAMES.hook);
|
|
198507
|
+
const settingsPath = join75(hookDir, "settings.json");
|
|
198370
198508
|
const cliAgentSettings = opts.config.cliAgentSettings ?? null;
|
|
198371
198509
|
const operatorEnvAdditions = (cliAgentSettings?.envAdditions ?? []).filter(
|
|
198372
198510
|
(k2) => !/^FUSION_/i.test(k2)
|
|
@@ -198619,7 +198757,7 @@ function killLiveTaskSessions(taskId, manager, store) {
|
|
|
198619
198757
|
// ../engine/src/pty-native.ts
|
|
198620
198758
|
import * as fs7 from "node:fs";
|
|
198621
198759
|
import { createRequire as createRequire7 } from "node:module";
|
|
198622
|
-
import { join as
|
|
198760
|
+
import { join as join76, dirname as dirname33 } from "node:path";
|
|
198623
198761
|
var isBunBinary = typeof Bun !== "undefined" && !!Bun.embeddedFiles;
|
|
198624
198762
|
var ptyModule = null;
|
|
198625
198763
|
var ptyLoadError = null;
|
|
@@ -198633,12 +198771,12 @@ function findInstalledNodePtyNativeDir() {
|
|
|
198633
198771
|
try {
|
|
198634
198772
|
const packageJsonPath = require4.resolve("node-pty/package.json");
|
|
198635
198773
|
const pkgRoot = dirname33(packageJsonPath);
|
|
198636
|
-
const releaseDir =
|
|
198637
|
-
if (fs7.existsSync(
|
|
198774
|
+
const releaseDir = join76(pkgRoot, "build", "Release");
|
|
198775
|
+
if (fs7.existsSync(join76(releaseDir, "pty.node"))) {
|
|
198638
198776
|
return releaseDir;
|
|
198639
198777
|
}
|
|
198640
|
-
const prebuildDir =
|
|
198641
|
-
if (fs7.existsSync(
|
|
198778
|
+
const prebuildDir = join76(pkgRoot, "prebuilds", getNativePrebuildName());
|
|
198779
|
+
if (fs7.existsSync(join76(prebuildDir, "pty.node"))) {
|
|
198642
198780
|
return prebuildDir;
|
|
198643
198781
|
}
|
|
198644
198782
|
return null;
|
|
@@ -198649,14 +198787,14 @@ function findInstalledNodePtyNativeDir() {
|
|
|
198649
198787
|
function findStagedNativeDir() {
|
|
198650
198788
|
const prebuildName = getNativePrebuildName();
|
|
198651
198789
|
if (process.env.FUSION_RUNTIME_DIR) {
|
|
198652
|
-
const envPath =
|
|
198653
|
-
if (fs7.existsSync(
|
|
198790
|
+
const envPath = join76(process.env.FUSION_RUNTIME_DIR, prebuildName);
|
|
198791
|
+
if (fs7.existsSync(join76(envPath, "pty.node"))) {
|
|
198654
198792
|
return envPath;
|
|
198655
198793
|
}
|
|
198656
198794
|
}
|
|
198657
198795
|
const execDir = dirname33(process.execPath);
|
|
198658
|
-
const nextToBinary =
|
|
198659
|
-
if (fs7.existsSync(
|
|
198796
|
+
const nextToBinary = join76(execDir, "runtime", prebuildName);
|
|
198797
|
+
if (fs7.existsSync(join76(nextToBinary, "pty.node"))) {
|
|
198660
198798
|
return nextToBinary;
|
|
198661
198799
|
}
|
|
198662
198800
|
return null;
|
|
@@ -198679,8 +198817,8 @@ function ensureNodePtyNativePermissions() {
|
|
|
198679
198817
|
candidateDirs.add(installedNativeDir);
|
|
198680
198818
|
}
|
|
198681
198819
|
for (const nativeDir of candidateDirs) {
|
|
198682
|
-
const helperPath =
|
|
198683
|
-
const nativeModulePath =
|
|
198820
|
+
const helperPath = join76(nativeDir, "spawn-helper");
|
|
198821
|
+
const nativeModulePath = join76(nativeDir, "pty.node");
|
|
198684
198822
|
try {
|
|
198685
198823
|
fs7.chmodSync(helperPath, 493);
|
|
198686
198824
|
} catch {
|
|
@@ -198710,7 +198848,7 @@ async function loadPtyModule() {
|
|
|
198710
198848
|
process.env.NODE_PTY_SPAWN_HELPER_DIR = nativeDir;
|
|
198711
198849
|
}
|
|
198712
198850
|
process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir;
|
|
198713
|
-
const nativePath =
|
|
198851
|
+
const nativePath = join76(nativeDir, "pty.node");
|
|
198714
198852
|
if (fs7.existsSync(nativePath)) {
|
|
198715
198853
|
try {
|
|
198716
198854
|
const nativeModule = { exports: {} };
|
|
@@ -201830,7 +201968,7 @@ import { promisify as promisify49 } from "node:util";
|
|
|
201830
201968
|
import { setImmediate as setImmediateCb2 } from "node:timers";
|
|
201831
201969
|
import { existsSync as existsSync63, mkdirSync as mkdirSync10, readdirSync as readdirSync10, readFileSync as readFileSync22, realpathSync as realpathSync8, rmSync as rmSync8, statSync as statSync12, writeFileSync as writeFileSync10 } from "node:fs";
|
|
201832
201970
|
import { tmpdir as tmpdir9 } from "node:os";
|
|
201833
|
-
import { isAbsolute as isAbsolute15, join as
|
|
201971
|
+
import { isAbsolute as isAbsolute15, join as join77, relative as relative13, resolve as resolve37 } from "node:path";
|
|
201834
201972
|
init_transient_error_detector();
|
|
201835
201973
|
|
|
201836
201974
|
// ../engine/src/auto-claim-snapshot.ts
|
|
@@ -207253,9 +207391,9 @@ async function preserveWorktreeChanges(repoDir, worktreePath, taskId) {
|
|
|
207253
207391
|
return null;
|
|
207254
207392
|
}
|
|
207255
207393
|
const diff = (await execAsync32("git diff HEAD --binary", { cwd: worktreePath, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 })).stdout;
|
|
207256
|
-
const recoveryDir =
|
|
207394
|
+
const recoveryDir = join77(repoDir, ".fusion", "recovery");
|
|
207257
207395
|
mkdirSync10(recoveryDir, { recursive: true });
|
|
207258
|
-
const patchPath =
|
|
207396
|
+
const patchPath = join77(recoveryDir, `${taskId.toLowerCase()}-${formatRecoveryTimestamp()}.patch`);
|
|
207259
207397
|
writeFileSync10(patchPath, diff, "utf-8");
|
|
207260
207398
|
return patchPath;
|
|
207261
207399
|
} catch (error2) {
|
|
@@ -208621,6 +208759,35 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
208621
208759
|
}
|
|
208622
208760
|
return false;
|
|
208623
208761
|
}
|
|
208762
|
+
/**
|
|
208763
|
+
* FNXC:WorkflowRecovery 2026-07-25-09:40:
|
|
208764
|
+
* Single shared decision for "is this branch tip's foreign `Fusion-Task-Id`/`Fusion-Task-Lineage` trailer real
|
|
208765
|
+
* evidence of cross-task contamination, or just an inherited start point?"
|
|
208766
|
+
*
|
|
208767
|
+
* A task branch cut from the integration branch points at whatever landed last. When the task has not committed
|
|
208768
|
+
* anything of its own (planning aborted, spec revise, operator move back to `todo`), the branch tip IS that
|
|
208769
|
+
* previous task's commit — a foreign trailer with zero foreign intent. FN-1406 hit exactly this: `fusion/fn-1406`
|
|
208770
|
+
* was cut from `main` at FN-1401's landed commit, planning ended without a commit, and the reclaim sweep logged
|
|
208771
|
+
* `already-merged rejected ... owner=FN-1401 reason=foreign-task-tip` every pass instead of clearing the stale
|
|
208772
|
+
* worktree/branch metadata.
|
|
208773
|
+
*
|
|
208774
|
+
* Merge-base-to-tip diff proof separates the two: no unique diff means the branch carries no task content, so the
|
|
208775
|
+
* foreign trailer is inherited and the caller may treat the row as stale metadata. The one exception is when the
|
|
208776
|
+
* base ALREADY has an explicit commit for this task — then the branch should have been sitting on the task's own
|
|
208777
|
+
* landed commit, and a foreign tip is genuine misbinding worth rejecting.
|
|
208778
|
+
*
|
|
208779
|
+
* Callers: already-merged recovery (`branchTipForeignOwnership`), branch-misbound recovery
|
|
208780
|
+
* (`isBranchTipMisboundToTask`), and the self-owned-branch reclaim sweep's `tip-already-merged` arm. Keeping the
|
|
208781
|
+
* three on one helper is the point — the reclaim arm drifted without the diff proof and that was the bug.
|
|
208782
|
+
*/
|
|
208783
|
+
async foreignTipRejection(input) {
|
|
208784
|
+
const { taskId, lineageId, branchTip, baseBranch, ownership } = input;
|
|
208785
|
+
if (ownership.rejectionReason !== "foreign-task" && ownership.rejectionReason !== "foreign-lineage") return null;
|
|
208786
|
+
const hasNoUniqueDiff = await this.branchHasNoUniqueDiff(branchTip, baseBranch).catch(() => false);
|
|
208787
|
+
const baseAlreadyHasCurrentTask = hasNoUniqueDiff ? await this.baseHasExplicitTaskOwnership(taskId, lineageId, baseBranch).catch(() => false) : false;
|
|
208788
|
+
if (hasNoUniqueDiff && !baseAlreadyHasCurrentTask) return null;
|
|
208789
|
+
return ownership.rejectionReason === "foreign-task" ? { reason: "foreign-task-tip", owner: ownership.ownerTaskId } : { reason: "foreign-lineage-tip", owner: ownership.ownerLineageId };
|
|
208790
|
+
}
|
|
208624
208791
|
async rejectForeignAlreadyMergedCandidate(input) {
|
|
208625
208792
|
const { task, candidateSha, candidateOwner, taskBranch, baseBranch, reason, phase } = input;
|
|
208626
208793
|
await this.store.logEntry(
|
|
@@ -208662,20 +208829,14 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
208662
208829
|
}
|
|
208663
208830
|
const sha = stdout.trim();
|
|
208664
208831
|
if (!sha) return null;
|
|
208665
|
-
const hasNoUniqueDiff = await this.branchHasNoUniqueDiff(sha, baseBranch).catch(() => false);
|
|
208666
208832
|
let ownership;
|
|
208667
208833
|
try {
|
|
208668
208834
|
ownership = await this.readCommitTaskOwnership(sha, taskId, lineageId);
|
|
208669
208835
|
} catch {
|
|
208670
208836
|
return { sha, reason: "ownership-unverifiable" };
|
|
208671
208837
|
}
|
|
208672
|
-
const
|
|
208673
|
-
if (
|
|
208674
|
-
return { sha, owner: ownership.ownerTaskId, reason: "foreign-task-tip" };
|
|
208675
|
-
}
|
|
208676
|
-
if ((!hasNoUniqueDiff || baseAlreadyHasCurrentTask) && ownership.rejectionReason === "foreign-lineage") {
|
|
208677
|
-
return { sha, owner: ownership.ownerLineageId, reason: "foreign-lineage-tip" };
|
|
208678
|
-
}
|
|
208838
|
+
const rejection = await this.foreignTipRejection({ taskId, lineageId, branchTip: sha, baseBranch, ownership });
|
|
208839
|
+
if (rejection) return { sha, owner: rejection.owner, reason: rejection.reason };
|
|
208679
208840
|
return null;
|
|
208680
208841
|
}
|
|
208681
208842
|
async resolveSelfHealingMergeTarget(task, settings, phase) {
|
|
@@ -209891,14 +210052,21 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
209891
210052
|
if (!ownership) {
|
|
209892
210053
|
continue;
|
|
209893
210054
|
}
|
|
209894
|
-
|
|
210055
|
+
const foreignRejection = await this.foreignTipRejection({
|
|
210056
|
+
taskId: task.id,
|
|
210057
|
+
lineageId: task.lineageId,
|
|
210058
|
+
branchTip: inspection.tipSha,
|
|
210059
|
+
baseBranch: inspection.integrationRef,
|
|
210060
|
+
ownership
|
|
210061
|
+
});
|
|
210062
|
+
if (foreignRejection) {
|
|
209895
210063
|
await this.rejectForeignAlreadyMergedCandidate({
|
|
209896
210064
|
task,
|
|
209897
210065
|
candidateSha: inspection.tipSha,
|
|
209898
|
-
candidateOwner:
|
|
210066
|
+
candidateOwner: foreignRejection.owner,
|
|
209899
210067
|
taskBranch: branchName,
|
|
209900
210068
|
baseBranch: inspection.integrationRef,
|
|
209901
|
-
reason:
|
|
210069
|
+
reason: foreignRejection.reason,
|
|
209902
210070
|
phase: "tip-already-merged"
|
|
209903
210071
|
});
|
|
209904
210072
|
continue;
|
|
@@ -213838,7 +214006,7 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
213838
214006
|
const unrecoverableRepos = [];
|
|
213839
214007
|
for (const repoRel of repoKeys) {
|
|
213840
214008
|
const entry = workspaceWorktrees[repoRel];
|
|
213841
|
-
const repoRootDir =
|
|
214009
|
+
const repoRootDir = join77(this.options.rootDir, repoRel);
|
|
213842
214010
|
let integrationBranch;
|
|
213843
214011
|
try {
|
|
213844
214012
|
integrationBranch = await resolveIntegrationBranch(
|
|
@@ -214132,7 +214300,7 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
214132
214300
|
if ((this.orphanWorktreeRemovalFailures.get(worktreePath) ?? 0) >= MAX_STARVATION_DROPS) {
|
|
214133
214301
|
continue;
|
|
214134
214302
|
}
|
|
214135
|
-
const repoRootDir =
|
|
214303
|
+
const repoRootDir = join77(this.options.rootDir, repoRel);
|
|
214136
214304
|
let success = false;
|
|
214137
214305
|
let reason = "removed";
|
|
214138
214306
|
try {
|
|
@@ -215053,7 +215221,7 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
215053
215221
|
if (testWorkerRoot) {
|
|
215054
215222
|
try {
|
|
215055
215223
|
for (const entry of readdirSync10(testWorkerRoot)) {
|
|
215056
|
-
if (entry.startsWith("redir-")) roots.push(
|
|
215224
|
+
if (entry.startsWith("redir-")) roots.push(join77(testWorkerRoot, entry));
|
|
215057
215225
|
}
|
|
215058
215226
|
} catch {
|
|
215059
215227
|
}
|
|
@@ -215067,7 +215235,7 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
215067
215235
|
} catch {
|
|
215068
215236
|
continue;
|
|
215069
215237
|
}
|
|
215070
|
-
for (const entry of entries) paths2.push(
|
|
215238
|
+
for (const entry of entries) paths2.push(join77(root, entry));
|
|
215071
215239
|
}
|
|
215072
215240
|
return paths2;
|
|
215073
215241
|
}
|
|
@@ -215303,14 +215471,10 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
215303
215471
|
maxBuffer: 1024 * 1024
|
|
215304
215472
|
});
|
|
215305
215473
|
const branchTip = tipOut.trim();
|
|
215306
|
-
const hasNoUniqueDiff = await this.branchHasNoUniqueDiff(branchTip, baseBranch).catch(() => false);
|
|
215307
215474
|
const ownership = await this.readCommitTaskOwnership(branchTip, taskId, lineageId);
|
|
215308
|
-
const
|
|
215309
|
-
if (
|
|
215310
|
-
return { misbound: false, branchTip, landed: null, rejection
|
|
215311
|
-
}
|
|
215312
|
-
if ((!hasNoUniqueDiff || baseAlreadyHasCurrentTask) && ownership.rejectionReason === "foreign-lineage") {
|
|
215313
|
-
return { misbound: false, branchTip, landed: null, rejection: { reason: "foreign-lineage-tip", owner: ownership.ownerLineageId } };
|
|
215475
|
+
const rejection = await this.foreignTipRejection({ taskId, lineageId, branchTip, baseBranch, ownership });
|
|
215476
|
+
if (rejection) {
|
|
215477
|
+
return { misbound: false, branchTip, landed: null, rejection };
|
|
215314
215478
|
}
|
|
215315
215479
|
const hasTaskId = ownership.ownerTaskId === taskId;
|
|
215316
215480
|
const hasLineage = lineageId ? ownership.ownerLineageId === lineageId : false;
|
|
@@ -216972,7 +217136,7 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
216972
217136
|
let processedMarkers = 0;
|
|
216973
217137
|
for (const task of candidates) {
|
|
216974
217138
|
try {
|
|
216975
|
-
const promptPath =
|
|
217139
|
+
const promptPath = join77(this.options.rootDir, ".fusion", "tasks", task.id, "PROMPT.md");
|
|
216976
217140
|
if (!existsSync63(promptPath)) {
|
|
216977
217141
|
continue;
|
|
216978
217142
|
}
|
|
@@ -217359,7 +217523,7 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
217359
217523
|
if (!existsSync63(worktreesDir)) return 0;
|
|
217360
217524
|
let dirs;
|
|
217361
217525
|
try {
|
|
217362
|
-
dirs = readdirSync10(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !isAiMergeContainerDir(e.name)).map((e) =>
|
|
217526
|
+
dirs = readdirSync10(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !isAiMergeContainerDir(e.name)).map((e) => join77(worktreesDir, e.name));
|
|
217363
217527
|
} catch (err) {
|
|
217364
217528
|
log28.warn(`Failed to read .worktrees/ for unregistered orphan reap: ${err instanceof Error ? err.message : String(err)}`);
|
|
217365
217529
|
return 0;
|
|
@@ -217429,7 +217593,7 @@ var SelfHealingManager = class _SelfHealingManager {
|
|
|
217429
217593
|
}
|
|
217430
217594
|
if (entries.length === 0) continue;
|
|
217431
217595
|
for (const entry of entries) {
|
|
217432
|
-
const path5 =
|
|
217596
|
+
const path5 = join77(tempRoot, entry);
|
|
217433
217597
|
let canonicalPath = path5;
|
|
217434
217598
|
let cleanupReason = "stale";
|
|
217435
217599
|
try {
|
|
@@ -217821,7 +217985,7 @@ init_src2();
|
|
|
217821
217985
|
init_logger();
|
|
217822
217986
|
init_verification_concurrency();
|
|
217823
217987
|
import { existsSync as existsSync64, readdirSync as readdirSync11, readFileSync as readFileSync23 } from "node:fs";
|
|
217824
|
-
import { isAbsolute as isAbsolute16, join as
|
|
217988
|
+
import { isAbsolute as isAbsolute16, join as join78, relative as relative14 } from "node:path";
|
|
217825
217989
|
import { Type as Type5 } from "@earendil-works/pi-ai";
|
|
217826
217990
|
var MAX_OUTPUT_BYTES = 200 * 1024;
|
|
217827
217991
|
var QUIET_HEARTBEAT_INTERVAL_MS = 6e4;
|
|
@@ -217946,7 +218110,7 @@ function findWorkspacePackageDir(rootDir, packageName) {
|
|
|
217946
218110
|
for (const candidate of candidates) {
|
|
217947
218111
|
if (!candidate) continue;
|
|
217948
218112
|
try {
|
|
217949
|
-
const pkg = JSON.parse(readFileSync23(
|
|
218113
|
+
const pkg = JSON.parse(readFileSync23(join78(rootDir, candidate, "package.json"), "utf8"));
|
|
217950
218114
|
if (pkg.name === packageName) {
|
|
217951
218115
|
packageDirCache.set(cacheKey, candidate);
|
|
217952
218116
|
return candidate;
|
|
@@ -217961,7 +218125,7 @@ function findWorkspacePackageDir(rootDir, packageName) {
|
|
|
217961
218125
|
while (queue.length > 0) {
|
|
217962
218126
|
const current = queue.shift();
|
|
217963
218127
|
if (!current) break;
|
|
217964
|
-
const abs =
|
|
218128
|
+
const abs = join78(rootDir, current.dir);
|
|
217965
218129
|
let entries;
|
|
217966
218130
|
try {
|
|
217967
218131
|
entries = readdirSync11(abs, { withFileTypes: true });
|
|
@@ -217971,9 +218135,9 @@ function findWorkspacePackageDir(rootDir, packageName) {
|
|
|
217971
218135
|
for (const entry of entries) {
|
|
217972
218136
|
if (!entry.isDirectory()) continue;
|
|
217973
218137
|
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
217974
|
-
const child =
|
|
218138
|
+
const child = join78(current.dir, entry.name);
|
|
217975
218139
|
try {
|
|
217976
|
-
const pkg = JSON.parse(readFileSync23(
|
|
218140
|
+
const pkg = JSON.parse(readFileSync23(join78(rootDir, child, "package.json"), "utf8"));
|
|
217977
218141
|
if (pkg.name === packageName) {
|
|
217978
218142
|
packageDirCache.set(cacheKey, child);
|
|
217979
218143
|
return child;
|
|
@@ -217993,7 +218157,7 @@ function toPackageRelativeFilter(token, rootDir, packageDir) {
|
|
|
217993
218157
|
return normalized.slice(normalizedPackageDir.length + 1);
|
|
217994
218158
|
}
|
|
217995
218159
|
if (isAbsolute16(token)) {
|
|
217996
|
-
const rel = relative14(
|
|
218160
|
+
const rel = relative14(join78(rootDir, packageDir), token).replace(/\\/g, "/");
|
|
217997
218161
|
if (!rel.startsWith("../") && rel !== "..") return rel;
|
|
217998
218162
|
}
|
|
217999
218163
|
return token;
|
|
@@ -218281,7 +218445,7 @@ function createRunVerificationTool(opts) {
|
|
|
218281
218445
|
if (params.cwd && isAbsolute16(params.cwd)) {
|
|
218282
218446
|
resolvedCwd = params.cwd;
|
|
218283
218447
|
} else if (params.cwd) {
|
|
218284
|
-
resolvedCwd =
|
|
218448
|
+
resolvedCwd = join78(worktreePath, params.cwd);
|
|
218285
218449
|
} else {
|
|
218286
218450
|
resolvedCwd = worktreePath;
|
|
218287
218451
|
}
|
|
@@ -218314,7 +218478,7 @@ function createRunVerificationTool(opts) {
|
|
|
218314
218478
|
warnings.push(...normalized.warnings);
|
|
218315
218479
|
}
|
|
218316
218480
|
if (effectiveCommand.trimStart().startsWith("pnpm --filter")) {
|
|
218317
|
-
const modulesYaml =
|
|
218481
|
+
const modulesYaml = join78(rootDir, "node_modules", ".modules.yaml");
|
|
218318
218482
|
if (!existsSync64(modulesYaml)) {
|
|
218319
218483
|
const installCmd = "pnpm install --prefer-offline";
|
|
218320
218484
|
const msg = `node_modules/.modules.yaml not found in workspace root \u2014 auto-prepending \`${installCmd}\` before running the command.`;
|
|
@@ -218604,7 +218768,7 @@ function isWorkflowStepSkillDiscoverable(skillName, additionalSkillPaths, ceSkil
|
|
|
218604
218768
|
return false;
|
|
218605
218769
|
}
|
|
218606
218770
|
return (additionalSkillPaths ?? []).some(
|
|
218607
|
-
(skillPath) => basename19(skillPath) === bareSkillName && existsSync65(
|
|
218771
|
+
(skillPath) => basename19(skillPath) === bareSkillName && existsSync65(join79(skillPath, "SKILL.md")) || existsSync65(join79(skillPath, bareSkillName, "SKILL.md"))
|
|
218608
218772
|
);
|
|
218609
218773
|
}
|
|
218610
218774
|
var yieldEventLoop3 = () => new Promise((resolve40) => setImmediateCb3(resolve40));
|
|
@@ -218932,7 +219096,7 @@ function normalizeWorktreePath(pathValue) {
|
|
|
218932
219096
|
}
|
|
218933
219097
|
async function extractPersistedSessionWorktreePath(sessionFile, rootDir, settings) {
|
|
218934
219098
|
try {
|
|
218935
|
-
const content = await
|
|
219099
|
+
const content = await readFile38(sessionFile, "utf-8");
|
|
218936
219100
|
const matches = content.match(buildSessionWorktreePathRegex(rootDir, settings)) ?? [];
|
|
218937
219101
|
if (matches.length === 0) return null;
|
|
218938
219102
|
const normalizedCounts = /* @__PURE__ */ new Map();
|
|
@@ -222007,7 +222171,7 @@ ${info.feedback}`, revisionKey),
|
|
|
222007
222171
|
const authoritativeAgentLayer = this.store.getAsyncLayer();
|
|
222008
222172
|
if (!this.authoritativeAssignedAgentStore || authoritativeAgentLayer && !this.authoritativeAssignedAgentStore.backendMode) {
|
|
222009
222173
|
this.authoritativeAssignedAgentStore = new AgentStore({
|
|
222010
|
-
rootDir:
|
|
222174
|
+
rootDir: join79(this.rootDir, ".fusion"),
|
|
222011
222175
|
taskStore: this.store,
|
|
222012
222176
|
...authoritativeAgentLayer ? { asyncLayer: authoritativeAgentLayer } : {}
|
|
222013
222177
|
});
|
|
@@ -226467,7 +226631,7 @@ Graph value: ${failureValue}` : ""}`, budget.key),
|
|
|
226467
226631
|
const activeMergeStatuses = /* @__PURE__ */ new Set(["merging", "merging-pr", "merging-fix"]);
|
|
226468
226632
|
const isActiveTask = activeColumns.has(task.column) || activeMergeStatuses.has(task.status ?? "");
|
|
226469
226633
|
if (!isActiveTask) {
|
|
226470
|
-
const tasksDir =
|
|
226634
|
+
const tasksDir = join79(this.store.getFusionDir(), "tasks");
|
|
226471
226635
|
const promptPath = getPromptPath(tasksDir, task.id);
|
|
226472
226636
|
const staleness = await evaluateSpecStaleness({ settings, promptPath, task });
|
|
226473
226637
|
if (staleness.isStale) {
|
|
@@ -230328,10 +230492,10 @@ Please fix the issues so the verification can pass on the next attempt.`,
|
|
|
230328
230492
|
* The section is replaced entirely to avoid accumulation of old feedback.
|
|
230329
230493
|
*/
|
|
230330
230494
|
async injectWorkflowStepFailureInstructions(task, failureFeedback, stepName, retry) {
|
|
230331
|
-
const promptPath =
|
|
230495
|
+
const promptPath = join79(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
|
|
230332
230496
|
let content;
|
|
230333
230497
|
try {
|
|
230334
|
-
content = await
|
|
230498
|
+
content = await readFile38(promptPath, "utf-8");
|
|
230335
230499
|
} catch {
|
|
230336
230500
|
executorLog.warn(`${task.id}: PROMPT.md not found at ${promptPath}, skipping workflow failure injection`);
|
|
230337
230501
|
return;
|
|
@@ -241504,7 +241668,7 @@ init_effective_settings();
|
|
|
241504
241668
|
|
|
241505
241669
|
// ../engine/src/spec-validation/task-document-references.ts
|
|
241506
241670
|
import { access as access9 } from "node:fs/promises";
|
|
241507
|
-
import { join as
|
|
241671
|
+
import { join as join80 } from "node:path";
|
|
241508
241672
|
var TASK_PATH_REGEX = /\.fusion\/tasks\/[A-Z0-9]+(?:-[A-Z0-9]+)*\/[^\s`'"\]]+/g;
|
|
241509
241673
|
function normalizePathToken(token) {
|
|
241510
241674
|
return token.replace(/^[`([{"']+/, "").replace(/[`),.;:!?\]}'"]+$/, "");
|
|
@@ -241593,7 +241757,7 @@ async function detectDanglingTaskDocReferences(promptContent, opts) {
|
|
|
241593
241757
|
}
|
|
241594
241758
|
const dangling = [];
|
|
241595
241759
|
for (const [path5, sections] of references.entries()) {
|
|
241596
|
-
const absPath =
|
|
241760
|
+
const absPath = join80(opts.rootDir, path5);
|
|
241597
241761
|
if (await existsImpl(absPath)) continue;
|
|
241598
241762
|
dangling.push({ path: path5, sections: Array.from(sections).sort() });
|
|
241599
241763
|
}
|
|
@@ -241616,14 +241780,15 @@ init_agent_logger();
|
|
|
241616
241780
|
init_agent_instructions();
|
|
241617
241781
|
init_fallback_model_observer();
|
|
241618
241782
|
init_logger();
|
|
241783
|
+
init_plan_artifact_writeback();
|
|
241619
241784
|
init_mcp_resolution();
|
|
241620
241785
|
init_usage_limit_detector();
|
|
241621
241786
|
init_transient_error_detector();
|
|
241622
241787
|
init_rate_limit_retry();
|
|
241623
241788
|
init_agent_tools();
|
|
241624
241789
|
import { exec as exec41 } from "node:child_process";
|
|
241625
|
-
import { readFile as
|
|
241626
|
-
import { join as
|
|
241790
|
+
import { readFile as readFile39, writeFile as writeFile30, rm as rm13 } from "node:fs/promises";
|
|
241791
|
+
import { join as join81 } from "node:path";
|
|
241627
241792
|
import { promisify as promisify56 } from "node:util";
|
|
241628
241793
|
|
|
241629
241794
|
// ../engine/src/triage-preflight.ts
|
|
@@ -242299,8 +242464,8 @@ var TriageProcessor = class _TriageProcessor {
|
|
|
242299
242464
|
}
|
|
242300
242465
|
const settings = await mergeEffectiveSettings(this.store, task, await this.store.getSettings());
|
|
242301
242466
|
const approvalRequired = resolvePlanApprovalRequired(settings);
|
|
242302
|
-
const promptPath =
|
|
242303
|
-
const written = await
|
|
242467
|
+
const promptPath = join81(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md");
|
|
242468
|
+
const written = await readFile39(promptPath, "utf-8").catch((err) => {
|
|
242304
242469
|
const msg = err instanceof Error ? err.message : String(err);
|
|
242305
242470
|
planLog.warn(`${task.id}: failed to read PROMPT.md during planning recovery (${promptPath}): ${msg}`);
|
|
242306
242471
|
return "";
|
|
@@ -242338,8 +242503,8 @@ var TriageProcessor = class _TriageProcessor {
|
|
|
242338
242503
|
return true;
|
|
242339
242504
|
}
|
|
242340
242505
|
async readNonEmptyPromptDraft(taskId, context) {
|
|
242341
|
-
const promptPath =
|
|
242342
|
-
const written = await
|
|
242506
|
+
const promptPath = join81(this.rootDir, ".fusion", "tasks", taskId, "PROMPT.md");
|
|
242507
|
+
const written = await readFile39(promptPath, "utf-8").catch((err) => {
|
|
242343
242508
|
const msg = err instanceof Error ? err.message : String(err);
|
|
242344
242509
|
planLog.warn(`${taskId}: failed to read PROMPT.md during ${context} (${promptPath}): ${msg}`);
|
|
242345
242510
|
return "";
|
|
@@ -242503,8 +242668,8 @@ var TriageProcessor = class _TriageProcessor {
|
|
|
242503
242668
|
continue;
|
|
242504
242669
|
}
|
|
242505
242670
|
try {
|
|
242506
|
-
const promptPath =
|
|
242507
|
-
const content = await
|
|
242671
|
+
const promptPath = join81(this.rootDir, ".fusion", "tasks", todoTask.id, "PROMPT.md");
|
|
242672
|
+
const content = await readFile39(promptPath, "utf-8");
|
|
242508
242673
|
if (isUnplannedSeedPrompt(content, todoTask.id, todoTask.title, todoTask.description)) {
|
|
242509
242674
|
eligibleTodoTasks.push(todoTask);
|
|
242510
242675
|
}
|
|
@@ -242715,7 +242880,7 @@ var TriageProcessor = class _TriageProcessor {
|
|
|
242715
242880
|
const detail = await this.store.getTask(task.id);
|
|
242716
242881
|
const currentTask = detail ?? task;
|
|
242717
242882
|
const settings = await mergeEffectiveSettings(this.store, currentTask, await this.store.getSettings());
|
|
242718
|
-
const promptPath =
|
|
242883
|
+
const promptPath = relativePromptPath(task.id);
|
|
242719
242884
|
const isFast = task.executionMode === "fast";
|
|
242720
242885
|
const leanPlanning = settings.leanPlanning === true || isFast;
|
|
242721
242886
|
const agentWork = async () => {
|
|
@@ -243051,8 +243216,22 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
|
|
|
243051
243216
|
}
|
|
243052
243217
|
return;
|
|
243053
243218
|
}
|
|
243054
|
-
const
|
|
243055
|
-
|
|
243219
|
+
const planPersistence = await persistPlanArtifact({
|
|
243220
|
+
store: this.store,
|
|
243221
|
+
taskId: task.id,
|
|
243222
|
+
rootDir: this.rootDir,
|
|
243223
|
+
planningCwd,
|
|
243224
|
+
author: "triage",
|
|
243225
|
+
logger: { log: (m2) => planLog.log(m2), warn: (m2) => planLog.warn(m2) }
|
|
243226
|
+
});
|
|
243227
|
+
if (planPersistence.outcome === "recovered") {
|
|
243228
|
+
await this.store.logEntry(
|
|
243229
|
+
task.id,
|
|
243230
|
+
"Recovered the plan written inside the task worktree into the project .fusion folder"
|
|
243231
|
+
).catch(() => void 0);
|
|
243232
|
+
}
|
|
243233
|
+
const written = await readFile39(
|
|
243234
|
+
join81(this.rootDir, promptPath),
|
|
243056
243235
|
"utf-8"
|
|
243057
243236
|
).catch((err) => {
|
|
243058
243237
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -243669,7 +243848,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
243669
243848
|
if (isNearDuplicateCanonicalInactive(canonicalTask ?? void 0)) {
|
|
243670
243849
|
if (canClearInactiveMarker) {
|
|
243671
243850
|
if (!await this.runIfStillPlanningUnderTaskLock(task, async () => {
|
|
243672
|
-
await rm13(
|
|
243851
|
+
await rm13(join81(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md"), { force: true });
|
|
243673
243852
|
})) return;
|
|
243674
243853
|
await this.updatePlanningStateIfStillCurrent(task, { paused: false, pausedReason: null, status: null });
|
|
243675
243854
|
}
|
|
@@ -243680,7 +243859,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
243680
243859
|
if (resolution === "prompt" && keepAcknowledged) {
|
|
243681
243860
|
if (canClearInactiveMarker) {
|
|
243682
243861
|
if (!await this.runIfStillPlanningUnderTaskLock(task, async () => {
|
|
243683
|
-
await rm13(
|
|
243862
|
+
await rm13(join81(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md"), { force: true });
|
|
243684
243863
|
})) return;
|
|
243685
243864
|
await this.updatePlanningStateIfStillCurrent(task, {
|
|
243686
243865
|
paused: false,
|
|
@@ -243721,7 +243900,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
243721
243900
|
return;
|
|
243722
243901
|
}
|
|
243723
243902
|
if (!await this.runIfStillPlanningUnderTaskLock(task, async () => {
|
|
243724
|
-
await rm13(
|
|
243903
|
+
await rm13(join81(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md"), { force: true });
|
|
243725
243904
|
})) return;
|
|
243726
243905
|
if (!await this.updatePlanningStateIfStillCurrent(task, {
|
|
243727
243906
|
paused: false,
|
|
@@ -243786,7 +243965,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
243786
243965
|
let nextPrompt = applyOriginalDescription(written, originalDescription);
|
|
243787
243966
|
nextPrompt = applyFrontendUxCriteria(nextPrompt, parsedFileScope);
|
|
243788
243967
|
if (nextPrompt !== written) {
|
|
243789
|
-
const promptPath =
|
|
243968
|
+
const promptPath = join81(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md");
|
|
243790
243969
|
try {
|
|
243791
243970
|
if (!await this.runIfStillPlanningUnderTaskLock(task, async () => {
|
|
243792
243971
|
await writeFile30(promptPath, nextPrompt, "utf-8");
|
|
@@ -243798,6 +243977,10 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
243798
243977
|
}
|
|
243799
243978
|
}
|
|
243800
243979
|
}
|
|
243980
|
+
await mirrorPlanToProjectDb(this.store, task.id, written, {
|
|
243981
|
+
author: "triage",
|
|
243982
|
+
logger: { warn: (m2) => planLog.warn(m2) }
|
|
243983
|
+
});
|
|
243801
243984
|
let taskIntentSignature = {
|
|
243802
243985
|
routePaths: [],
|
|
243803
243986
|
filePaths: [],
|
|
@@ -244074,10 +244257,10 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
|
|
|
244074
244257
|
if (!attachments || attachments.length === 0) {
|
|
244075
244258
|
return { attachmentContents, imageContents };
|
|
244076
244259
|
}
|
|
244077
|
-
const { readFile:
|
|
244078
|
-
const { join:
|
|
244260
|
+
const { readFile: readFile41 } = await import("node:fs/promises");
|
|
244261
|
+
const { join: join85 } = await import("node:path");
|
|
244079
244262
|
for (const att of attachments) {
|
|
244080
|
-
const filePath =
|
|
244263
|
+
const filePath = join85(
|
|
244081
244264
|
rootDir,
|
|
244082
244265
|
".fusion",
|
|
244083
244266
|
"tasks",
|
|
@@ -244087,7 +244270,7 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
|
|
|
244087
244270
|
);
|
|
244088
244271
|
try {
|
|
244089
244272
|
if (IMAGE_MIME_TYPES.has(att.mimeType)) {
|
|
244090
|
-
const data = await
|
|
244273
|
+
const data = await readFile41(filePath);
|
|
244091
244274
|
const detectedMimeType = detectImageMimeFromBytes(data);
|
|
244092
244275
|
const imageMimeType = detectedMimeType ?? att.mimeType;
|
|
244093
244276
|
if (detectedMimeType && detectedMimeType !== att.mimeType) {
|
|
@@ -244104,7 +244287,7 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
|
|
|
244104
244287
|
text: null
|
|
244105
244288
|
});
|
|
244106
244289
|
} else {
|
|
244107
|
-
const data = await
|
|
244290
|
+
const data = await readFile41(filePath, "utf-8");
|
|
244108
244291
|
const text2 = data.length > TEXT_INLINE_LIMIT ? data.slice(0, TEXT_INLINE_LIMIT) + "\n... (truncated at 50KB)" : data;
|
|
244109
244292
|
attachmentContents.push({
|
|
244110
244293
|
originalName: att.originalName,
|
|
@@ -247464,7 +247647,7 @@ function extractAdvisorAssistantText(session) {
|
|
|
247464
247647
|
// ../engine/src/overseer-watchdog.ts
|
|
247465
247648
|
init_logger();
|
|
247466
247649
|
import { existsSync as existsSync67, readFileSync as readFileSync24 } from "node:fs";
|
|
247467
|
-
import { dirname as dirname34, join as
|
|
247650
|
+
import { dirname as dirname34, join as join82, relative as relative17, resolve as resolve39, sep as sep11 } from "node:path";
|
|
247468
247651
|
import { homedir as homedir11 } from "node:os";
|
|
247469
247652
|
var log32 = createLogger("overseer-watchdog");
|
|
247470
247653
|
var WATCHDOG_FILENAMES = ["OVERSEER.md", "WATCHDOG.md"];
|
|
@@ -247495,7 +247678,7 @@ function discoverOverseerWatchdogFiles(options) {
|
|
|
247495
247678
|
};
|
|
247496
247679
|
if (agentDir) {
|
|
247497
247680
|
for (const name2 of WATCHDOG_FILENAMES) {
|
|
247498
|
-
tryAdd(
|
|
247681
|
+
tryAdd(join82(agentDir, name2), "user", 999);
|
|
247499
247682
|
}
|
|
247500
247683
|
}
|
|
247501
247684
|
let current = cwd;
|
|
@@ -247503,9 +247686,9 @@ function discoverOverseerWatchdogFiles(options) {
|
|
|
247503
247686
|
for (let hop = 0; hop < 64; hop++) {
|
|
247504
247687
|
const depth = relative17(cwd, current) === "" ? 0 : relative17(cwd, current).split(sep11).filter(Boolean).length;
|
|
247505
247688
|
for (const name2 of WATCHDOG_FILENAMES) {
|
|
247506
|
-
tryAdd(
|
|
247507
|
-
tryAdd(
|
|
247508
|
-
tryAdd(
|
|
247689
|
+
tryAdd(join82(current, name2), "project", depth);
|
|
247690
|
+
tryAdd(join82(current, ".fusion", name2), "project", depth);
|
|
247691
|
+
tryAdd(join82(current, ".omp", name2), "project", depth);
|
|
247509
247692
|
}
|
|
247510
247693
|
if (current === stopAt) break;
|
|
247511
247694
|
const parent = dirname34(current);
|
|
@@ -248470,7 +248653,7 @@ import { promisify as promisify58 } from "node:util";
|
|
|
248470
248653
|
import { realpathSync as realpathSync11, readdirSync as readdirSync13 } from "node:fs";
|
|
248471
248654
|
import { mkdtemp as mkdtemp4 } from "node:fs/promises";
|
|
248472
248655
|
import { tmpdir as tmpdir11 } from "node:os";
|
|
248473
|
-
import { join as
|
|
248656
|
+
import { join as join84 } from "node:path";
|
|
248474
248657
|
|
|
248475
248658
|
// ../engine/src/overseer-noop-finalize-veto.ts
|
|
248476
248659
|
init_src2();
|
|
@@ -248564,7 +248747,7 @@ import { execFile as execFile28 } from "node:child_process";
|
|
|
248564
248747
|
import { appendFileSync as appendFileSync2, existsSync as existsSync68, mkdirSync as mkdirSync11, readdirSync as readdirSync12, readFileSync as readFileSync25, realpathSync as realpathSync10, rmSync as rmSync9, statSync as statSync13 } from "node:fs";
|
|
248565
248748
|
import { rm as rm14 } from "node:fs/promises";
|
|
248566
248749
|
import { tmpdir as tmpdir10 } from "node:os";
|
|
248567
|
-
import { isAbsolute as isAbsolute21, join as
|
|
248750
|
+
import { isAbsolute as isAbsolute21, join as join83, relative as relative18 } from "node:path";
|
|
248568
248751
|
import { promisify as promisify57 } from "node:util";
|
|
248569
248752
|
init_worktree_paths();
|
|
248570
248753
|
var execFileAsync21 = promisify57(execFile28);
|
|
@@ -248597,7 +248780,7 @@ function isBenignAbsentWorktreeError(err) {
|
|
|
248597
248780
|
return /is not a working tree|No such file or directory|spawn\s+.*\bENOENT\b/i.test(description);
|
|
248598
248781
|
}
|
|
248599
248782
|
function ensureAiMergeRootIgnored(projectRootDir, settings) {
|
|
248600
|
-
const excludePath =
|
|
248783
|
+
const excludePath = join83(projectRootDir, ".git", "info", "exclude");
|
|
248601
248784
|
if (!existsSync68(excludePath)) return;
|
|
248602
248785
|
try {
|
|
248603
248786
|
const current = readFileSync25(excludePath, "utf-8");
|
|
@@ -248629,7 +248812,7 @@ function getAiMergeTempSearchRoots(projectRootDir, settings) {
|
|
|
248629
248812
|
if (testWorkerRoot) {
|
|
248630
248813
|
try {
|
|
248631
248814
|
for (const entry of readdirSync12(testWorkerRoot)) {
|
|
248632
|
-
if (entry.startsWith("redir-")) roots.push(
|
|
248815
|
+
if (entry.startsWith("redir-")) roots.push(join83(testWorkerRoot, entry));
|
|
248633
248816
|
}
|
|
248634
248817
|
} catch {
|
|
248635
248818
|
}
|
|
@@ -248652,7 +248835,7 @@ async function pruneExistingAiMergeWorktrees(taskId, projectRootDir, audit, log3
|
|
|
248652
248835
|
continue;
|
|
248653
248836
|
}
|
|
248654
248837
|
for (const entry of entries) {
|
|
248655
|
-
const candidatePath =
|
|
248838
|
+
const candidatePath = join83(tempRoot, entry);
|
|
248656
248839
|
let canonicalPath = candidatePath;
|
|
248657
248840
|
try {
|
|
248658
248841
|
canonicalPath = realpathSync10(candidatePath);
|
|
@@ -249128,7 +249311,7 @@ function listAiMergeWorktreeCandidates(taskId, projectRootDir, settings) {
|
|
|
249128
249311
|
if (testWorkerRoot) {
|
|
249129
249312
|
try {
|
|
249130
249313
|
for (const entry of readdirSync13(testWorkerRoot)) {
|
|
249131
|
-
if (entry.startsWith("redir-")) roots.push(
|
|
249314
|
+
if (entry.startsWith("redir-")) roots.push(join84(testWorkerRoot, entry));
|
|
249132
249315
|
}
|
|
249133
249316
|
} catch {
|
|
249134
249317
|
}
|
|
@@ -249141,7 +249324,7 @@ function listAiMergeWorktreeCandidates(taskId, projectRootDir, settings) {
|
|
|
249141
249324
|
} catch {
|
|
249142
249325
|
continue;
|
|
249143
249326
|
}
|
|
249144
|
-
for (const entry of entries) candidates.push(
|
|
249327
|
+
for (const entry of entries) candidates.push(join84(root, entry));
|
|
249145
249328
|
}
|
|
249146
249329
|
return candidates;
|
|
249147
249330
|
}
|
|
@@ -249485,7 +249668,7 @@ async function landOneRepo(repoRootDir, branch, integrationBranch, ctx) {
|
|
|
249485
249668
|
registeredMergePaths.add(pathToRegister);
|
|
249486
249669
|
};
|
|
249487
249670
|
try {
|
|
249488
|
-
mergeRoot = await mkdtemp4(
|
|
249671
|
+
mergeRoot = await mkdtemp4(join84(resolveAiMergeRoot(repoRootDir, settings), `fusion-ai-merge-${taskId.toLowerCase()}-`));
|
|
249489
249672
|
registerMergeRoot(mergeRoot);
|
|
249490
249673
|
await git5(["worktree", "add", "--detach", mergeRoot, tipSha], repoRootDir);
|
|
249491
249674
|
worktreeAdded = true;
|
|
@@ -250002,7 +250185,7 @@ async function landWorkspaceTask(store, task, workspaceRootDir, options = {}, de
|
|
|
250002
250185
|
for (const repoRel of repoKeys) {
|
|
250003
250186
|
throwIfAborted2(options.signal, taskId);
|
|
250004
250187
|
const entry = workspaceWorktrees[repoRel];
|
|
250005
|
-
const repoRootDir =
|
|
250188
|
+
const repoRootDir = join84(workspaceRootDir, repoRel);
|
|
250006
250189
|
let integrationBranch;
|
|
250007
250190
|
try {
|
|
250008
250191
|
integrationBranch = await resolveIntegrationBranch(
|
|
@@ -250319,7 +250502,7 @@ async function pushAfterMergeToRemote(input) {
|
|
|
250319
250502
|
let worktreeAdded = false;
|
|
250320
250503
|
const registeredPaths = /* @__PURE__ */ new Set();
|
|
250321
250504
|
try {
|
|
250322
|
-
pushRoot = await mkdtemp4(
|
|
250505
|
+
pushRoot = await mkdtemp4(join84(resolveAiMergeRoot(projectRootDir, settings), `fusion-ai-merge-push-${taskId.toLowerCase()}-`));
|
|
250323
250506
|
for (const p2 of [pushRoot]) {
|
|
250324
250507
|
activeSessionRegistry.registerPath(p2, { taskId, kind: "ai-merge", ownerKey: `ai-merge-push:${taskId}` });
|
|
250325
250508
|
registeredPaths.add(p2);
|