@runfusion/fusion 0.8.3 → 0.8.4
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 +1691 -831
- package/dist/client/assets/{AgentDetailView-O2adxiKD.js → AgentDetailView-C_DD03d6.js} +1 -1
- package/dist/client/assets/{AgentsView-CMsJN9J0.js → AgentsView-CsEX_Fsi.js} +3 -3
- package/dist/client/assets/ChatView-DhudD-jT.js +1 -0
- package/dist/client/assets/{DevServerView-C-CMoxz2.js → DevServerView-CNItMoga.js} +1 -1
- package/dist/client/assets/DirectoryPicker-BqcVbDZX.js +1 -0
- package/dist/client/assets/{DocumentsView-B_co_BXz.js → DocumentsView-zpHkjZdf.js} +1 -1
- package/dist/client/assets/{InsightsView-Egu71gmh.css → InsightsView-6LHF7OdE.css} +1 -1
- package/dist/client/assets/InsightsView-CgguV1au.js +11 -0
- package/dist/client/assets/{MemoryView-BcDbZcBz.js → MemoryView-BWXP8uGT.js} +1 -1
- package/dist/client/assets/{NodesView-DCfW16N1.js → NodesView-DP_O4ae0.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-BIhPGruz.js → PiExtensionsManager-DGEPbF0y.js} +3 -3
- package/dist/client/assets/{PluginManager-DOuZY2ZA.js → PluginManager-4TehPpcf.js} +1 -1
- package/dist/client/assets/{RoadmapsView-CkEjJ6G3.js → RoadmapsView-DNaToPqN.js} +2 -2
- package/dist/client/assets/SettingsModal-BluRnKGd.js +31 -0
- package/dist/client/assets/{SettingsModal-Cwjchghd.js → SettingsModal-C-RjolQ5.js} +1 -1
- package/dist/client/assets/SettingsModal-C7gPLBaR.css +1 -0
- package/dist/client/assets/{SetupWizardModal-DPtyqkGN.js → SetupWizardModal-BQoo_AvX.js} +1 -1
- package/dist/client/assets/{SkillsView-utmyFduT.js → SkillsView-AS8Cr_Md.js} +1 -1
- package/dist/client/assets/{TodoView-DyiPO0Fc.js → TodoView-nWOpOg3R.js} +2 -2
- package/dist/client/assets/{folder-open-mD33kGzX.js → folder-open-D0LfE0ZP.js} +1 -1
- package/dist/client/assets/index-D6xr8Oa2.css +1 -0
- package/dist/client/assets/index-heCcln3Z.js +656 -0
- package/dist/client/assets/{list-checks-NQXhquQA.js → list-checks-D1faMe1o.js} +1 -1
- package/dist/client/assets/{star-Bai3dakq.js → star-B_uA5YGG.js} +1 -1
- package/dist/client/assets/{upload-Bhl-n_mv.js → upload-DPQ3hWf0.js} +1 -1
- package/dist/client/assets/{users-tZ6VJnpO.js → users-CEcYlrZO.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/extension.js +1033 -372
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/pi-claude-cli/src/__tests__/control-handler.test.ts +39 -66
- package/dist/pi-claude-cli/src/__tests__/process-manager.test.ts +7 -9
- package/dist/pi-claude-cli/src/__tests__/provider.test.ts +73 -148
- package/dist/pi-claude-cli/src/__tests__/setup-test-isolation.test.ts +2 -0
- package/dist/pi-claude-cli/src/__tests__/setup-test-isolation.ts +8 -0
- package/dist/pi-claude-cli/src/control-handler.ts +22 -8
- package/dist/pi-claude-cli/src/process-manager.ts +10 -3
- package/dist/pi-claude-cli/src/prompt-builder.ts +2 -1
- package/dist/pi-claude-cli/src/provider.ts +10 -2
- package/package.json +1 -1
- package/dist/client/assets/ChatView-DoqdjDGh.js +0 -1
- package/dist/client/assets/DirectoryPicker-tAfuzQl7.js +0 -1
- package/dist/client/assets/InsightsView-CI-TP1Lp.js +0 -11
- package/dist/client/assets/SettingsModal-D5hLoLXp.css +0 -1
- package/dist/client/assets/SettingsModal-a6UiOZ_3.js +0 -31
- package/dist/client/assets/index-D2fXOwWF.css +0 -1
- package/dist/client/assets/index-DZBqmGBj.js +0 -656
package/dist/extension.js
CHANGED
|
@@ -2432,7 +2432,7 @@ var init_db = __esm({
|
|
|
2432
2432
|
"use strict";
|
|
2433
2433
|
init_sqlite_adapter();
|
|
2434
2434
|
init_types();
|
|
2435
|
-
SCHEMA_VERSION =
|
|
2435
|
+
SCHEMA_VERSION = 52;
|
|
2436
2436
|
SCHEMA_SQL = `
|
|
2437
2437
|
-- Tasks table with JSON columns for nested data
|
|
2438
2438
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
@@ -2949,7 +2949,12 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
2949
2949
|
if (!inMemory && !existsSync(fusionDir)) {
|
|
2950
2950
|
mkdirSync(fusionDir, { recursive: true });
|
|
2951
2951
|
}
|
|
2952
|
-
|
|
2952
|
+
try {
|
|
2953
|
+
this.db = new DatabaseSync(this.dbPath);
|
|
2954
|
+
} catch (error) {
|
|
2955
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2956
|
+
throw new Error(`Failed to open Fusion database at ${this.dbPath}: ${message}`);
|
|
2957
|
+
}
|
|
2953
2958
|
if (!inMemory) {
|
|
2954
2959
|
this.db.exec("PRAGMA journal_mode = WAL");
|
|
2955
2960
|
}
|
|
@@ -3972,6 +3977,11 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3972
3977
|
}
|
|
3973
3978
|
});
|
|
3974
3979
|
}
|
|
3980
|
+
if (version < 52) {
|
|
3981
|
+
this.applyMigration(52, () => {
|
|
3982
|
+
this.addColumnIfMissing("tasks", "mergeConflictBounceCount", "INTEGER DEFAULT 0");
|
|
3983
|
+
});
|
|
3984
|
+
}
|
|
3975
3985
|
}
|
|
3976
3986
|
/**
|
|
3977
3987
|
* Run a single migration step inside a transaction and bump the version.
|
|
@@ -6305,14 +6315,17 @@ import { homedir } from "node:os";
|
|
|
6305
6315
|
import { dirname, join as join4 } from "node:path";
|
|
6306
6316
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3, rename as rename2, chmod } from "node:fs/promises";
|
|
6307
6317
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, renameSync } from "node:fs";
|
|
6318
|
+
function getHomeDir() {
|
|
6319
|
+
return process.env.HOME || process.env.USERPROFILE || homedir();
|
|
6320
|
+
}
|
|
6308
6321
|
function legacyGlobalDir() {
|
|
6309
|
-
return join4(
|
|
6322
|
+
return join4(getHomeDir(), ".pi", "fusion");
|
|
6310
6323
|
}
|
|
6311
6324
|
function legacyGlobalDirOriginal() {
|
|
6312
|
-
return join4(
|
|
6325
|
+
return join4(getHomeDir(), ".pi", "kb");
|
|
6313
6326
|
}
|
|
6314
6327
|
function defaultGlobalDir() {
|
|
6315
|
-
return join4(
|
|
6328
|
+
return join4(getHomeDir(), ".fusion");
|
|
6316
6329
|
}
|
|
6317
6330
|
function resolveGlobalDir(dir) {
|
|
6318
6331
|
const hasExplicitDir = typeof dir === "string" && dir.length > 0;
|
|
@@ -6484,9 +6497,9 @@ var init_global_settings = __esm({
|
|
|
6484
6497
|
* Serialize operations via promise chain to prevent lost-update races.
|
|
6485
6498
|
*/
|
|
6486
6499
|
withLock(fn) {
|
|
6487
|
-
let
|
|
6500
|
+
let resolve17;
|
|
6488
6501
|
const next = new Promise((r) => {
|
|
6489
|
-
|
|
6502
|
+
resolve17 = r;
|
|
6490
6503
|
});
|
|
6491
6504
|
const prev = this.lock;
|
|
6492
6505
|
this.lock = next;
|
|
@@ -6494,7 +6507,7 @@ var init_global_settings = __esm({
|
|
|
6494
6507
|
try {
|
|
6495
6508
|
return await fn();
|
|
6496
6509
|
} finally {
|
|
6497
|
-
|
|
6510
|
+
resolve17();
|
|
6498
6511
|
}
|
|
6499
6512
|
});
|
|
6500
6513
|
}
|
|
@@ -12033,7 +12046,12 @@ CREATE INDEX IF NOT EXISTS idxSettingsSyncNode ON settingsSyncState(nodeId);
|
|
|
12033
12046
|
if (!existsSync6(this.globalDir)) {
|
|
12034
12047
|
mkdirSync4(this.globalDir, { recursive: true });
|
|
12035
12048
|
}
|
|
12036
|
-
|
|
12049
|
+
try {
|
|
12050
|
+
this.db = new DatabaseSync(this.dbPath);
|
|
12051
|
+
} catch (error) {
|
|
12052
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12053
|
+
throw new Error(`Failed to open Fusion central database at ${this.dbPath}: ${message}`);
|
|
12054
|
+
}
|
|
12037
12055
|
this.db.exec("PRAGMA journal_mode = WAL");
|
|
12038
12056
|
this.db.exec("PRAGMA busy_timeout = 5000");
|
|
12039
12057
|
this.db.exec("PRAGMA foreign_keys = ON");
|
|
@@ -17957,6 +17975,40 @@ var init_central_core = __esm({
|
|
|
17957
17975
|
}
|
|
17958
17976
|
});
|
|
17959
17977
|
|
|
17978
|
+
// ../core/src/sqlite-validation.ts
|
|
17979
|
+
import { existsSync as existsSync8, statSync as statSync2 } from "node:fs";
|
|
17980
|
+
function isValidSqliteDatabaseFile(dbPath) {
|
|
17981
|
+
if (!existsSync8(dbPath)) {
|
|
17982
|
+
return false;
|
|
17983
|
+
}
|
|
17984
|
+
try {
|
|
17985
|
+
if (!statSync2(dbPath).isFile()) {
|
|
17986
|
+
return false;
|
|
17987
|
+
}
|
|
17988
|
+
} catch {
|
|
17989
|
+
return false;
|
|
17990
|
+
}
|
|
17991
|
+
let db = null;
|
|
17992
|
+
try {
|
|
17993
|
+
db = new DatabaseSync(dbPath);
|
|
17994
|
+
db.prepare("PRAGMA schema_version").get();
|
|
17995
|
+
return true;
|
|
17996
|
+
} catch {
|
|
17997
|
+
return false;
|
|
17998
|
+
} finally {
|
|
17999
|
+
try {
|
|
18000
|
+
db?.close();
|
|
18001
|
+
} catch {
|
|
18002
|
+
}
|
|
18003
|
+
}
|
|
18004
|
+
}
|
|
18005
|
+
var init_sqlite_validation = __esm({
|
|
18006
|
+
"../core/src/sqlite-validation.ts"() {
|
|
18007
|
+
"use strict";
|
|
18008
|
+
init_sqlite_adapter();
|
|
18009
|
+
}
|
|
18010
|
+
});
|
|
18011
|
+
|
|
17960
18012
|
// ../core/src/migration.ts
|
|
17961
18013
|
var migration_exports = {};
|
|
17962
18014
|
__export(migration_exports, {
|
|
@@ -17965,26 +18017,25 @@ __export(migration_exports, {
|
|
|
17965
18017
|
MigrationCoordinator: () => MigrationCoordinator,
|
|
17966
18018
|
ProjectRequiredError: () => ProjectRequiredError
|
|
17967
18019
|
});
|
|
17968
|
-
import { existsSync as
|
|
18020
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
17969
18021
|
import { homedir as homedir2, tmpdir } from "node:os";
|
|
17970
18022
|
import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4, basename as basename3, dirname as dirname3 } from "node:path";
|
|
18023
|
+
function getHomeDir2() {
|
|
18024
|
+
return process.env.HOME || process.env.USERPROFILE || homedir2();
|
|
18025
|
+
}
|
|
17971
18026
|
function hasProjectDbFile(dir, folderName, dbName) {
|
|
17972
18027
|
const projectDir = join11(dir, folderName);
|
|
17973
18028
|
const dbPath = join11(projectDir, dbName);
|
|
17974
|
-
if (!
|
|
17975
|
-
|
|
17976
|
-
try {
|
|
17977
|
-
const stat8 = statSync2(dbPath);
|
|
17978
|
-
return stat8.isFile() && stat8.size > 0;
|
|
17979
|
-
} catch {
|
|
17980
|
-
return false;
|
|
17981
|
-
}
|
|
18029
|
+
if (!existsSync9(projectDir)) return false;
|
|
18030
|
+
return isValidSqliteDatabaseFile(dbPath);
|
|
17982
18031
|
}
|
|
17983
18032
|
var ProjectRequiredError, FirstRunDetector, MigrationCoordinator, BackwardCompat;
|
|
17984
18033
|
var init_migration = __esm({
|
|
17985
18034
|
"../core/src/migration.ts"() {
|
|
17986
18035
|
"use strict";
|
|
17987
18036
|
init_central_core();
|
|
18037
|
+
init_global_settings();
|
|
18038
|
+
init_sqlite_validation();
|
|
17988
18039
|
ProjectRequiredError = class extends Error {
|
|
17989
18040
|
constructor(message, availableProjects) {
|
|
17990
18041
|
super(message);
|
|
@@ -18045,7 +18096,7 @@ var init_migration = __esm({
|
|
|
18045
18096
|
*/
|
|
18046
18097
|
hasCentralDb() {
|
|
18047
18098
|
const centralDbPath = join11(this.globalDir, "fusion-central.db");
|
|
18048
|
-
return
|
|
18099
|
+
return existsSync9(centralDbPath);
|
|
18049
18100
|
}
|
|
18050
18101
|
/**
|
|
18051
18102
|
* Get the path to the central database.
|
|
@@ -18067,7 +18118,7 @@ var init_migration = __esm({
|
|
|
18067
18118
|
const projects = [];
|
|
18068
18119
|
const visited = /* @__PURE__ */ new Set();
|
|
18069
18120
|
let current = resolve4(startDir);
|
|
18070
|
-
const home =
|
|
18121
|
+
const home = getHomeDir2();
|
|
18071
18122
|
const root = dirname3(current) === current ? current : "/";
|
|
18072
18123
|
const systemTmp = resolve4(tmpdir());
|
|
18073
18124
|
while (current !== home && current !== root && current !== systemTmp) {
|
|
@@ -18099,7 +18150,7 @@ var init_migration = __esm({
|
|
|
18099
18150
|
* @returns Generated name
|
|
18100
18151
|
*/
|
|
18101
18152
|
async generateProjectName(projectPath) {
|
|
18102
|
-
if (!
|
|
18153
|
+
if (!existsSync9(join11(projectPath, ".git"))) {
|
|
18103
18154
|
return basename3(projectPath);
|
|
18104
18155
|
}
|
|
18105
18156
|
try {
|
|
@@ -18148,7 +18199,7 @@ var init_migration = __esm({
|
|
|
18148
18199
|
return hasProjectDbFile(dir, ".fusion", "fusion.db");
|
|
18149
18200
|
}
|
|
18150
18201
|
getDefaultGlobalDir() {
|
|
18151
|
-
return
|
|
18202
|
+
return resolveGlobalDir();
|
|
18152
18203
|
}
|
|
18153
18204
|
};
|
|
18154
18205
|
MigrationCoordinator = class {
|
|
@@ -27572,8 +27623,8 @@ var require_CronFileParser = __commonJS({
|
|
|
27572
27623
|
* @throws If file cannot be read
|
|
27573
27624
|
*/
|
|
27574
27625
|
static parseFileSync(filePath) {
|
|
27575
|
-
const { readFileSync:
|
|
27576
|
-
const data =
|
|
27626
|
+
const { readFileSync: readFileSync8 } = __require("fs");
|
|
27627
|
+
const data = readFileSync8(filePath, "utf8");
|
|
27577
27628
|
return _CronFileParser.#parseContent(data);
|
|
27578
27629
|
}
|
|
27579
27630
|
/**
|
|
@@ -27790,9 +27841,9 @@ var init_automation_store = __esm({
|
|
|
27790
27841
|
*/
|
|
27791
27842
|
withScheduleLock(id, fn) {
|
|
27792
27843
|
const prev = this.scheduleLocks.get(id) ?? Promise.resolve();
|
|
27793
|
-
let
|
|
27844
|
+
let resolve17;
|
|
27794
27845
|
const next = new Promise((r) => {
|
|
27795
|
-
|
|
27846
|
+
resolve17 = r;
|
|
27796
27847
|
});
|
|
27797
27848
|
this.scheduleLocks.set(id, next);
|
|
27798
27849
|
return prev.then(async () => {
|
|
@@ -27802,7 +27853,7 @@ var init_automation_store = __esm({
|
|
|
27802
27853
|
if (this.scheduleLocks.get(id) === next) {
|
|
27803
27854
|
this.scheduleLocks.delete(id);
|
|
27804
27855
|
}
|
|
27805
|
-
|
|
27856
|
+
resolve17();
|
|
27806
27857
|
}
|
|
27807
27858
|
});
|
|
27808
27859
|
}
|
|
@@ -28061,7 +28112,7 @@ __export(memory_dreams_exports, {
|
|
|
28061
28112
|
syncMemoryDreamsAutomation: () => syncMemoryDreamsAutomation
|
|
28062
28113
|
});
|
|
28063
28114
|
import { appendFile, mkdir as mkdir5, readFile as readFile5, readdir as readdir3, stat, writeFile as writeFile4 } from "node:fs/promises";
|
|
28064
|
-
import { existsSync as
|
|
28115
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
28065
28116
|
import { join as join13 } from "node:path";
|
|
28066
28117
|
function agentMemoryWorkspacePath(rootDir, agentId) {
|
|
28067
28118
|
const safeAgentId = agentId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
@@ -28080,7 +28131,7 @@ async function ensureAgentMemoryFiles(rootDir, agent, date = /* @__PURE__ */ new
|
|
|
28080
28131
|
const workspacePath = agentMemoryWorkspacePath(rootDir, agent.id);
|
|
28081
28132
|
await mkdir5(workspacePath, { recursive: true });
|
|
28082
28133
|
const longTermPath = agentMemoryLongTermPath(rootDir, agent.id);
|
|
28083
|
-
if (!
|
|
28134
|
+
if (!existsSync10(longTermPath)) {
|
|
28084
28135
|
const title = agent.name?.trim() ? `# Agent Memory: ${agent.name.trim()}` : "# Agent Memory";
|
|
28085
28136
|
await writeFile4(
|
|
28086
28137
|
longTermPath,
|
|
@@ -28094,11 +28145,11 @@ ${agent.memory?.trim() ?? ""}
|
|
|
28094
28145
|
);
|
|
28095
28146
|
}
|
|
28096
28147
|
const dreamsPath = agentMemoryDreamsPath(rootDir, agent.id);
|
|
28097
|
-
if (!
|
|
28148
|
+
if (!existsSync10(dreamsPath)) {
|
|
28098
28149
|
await writeFile4(dreamsPath, "# Agent Memory Dreams\n\n<!-- Synthesized patterns from this agent's daily notes. -->\n", "utf-8");
|
|
28099
28150
|
}
|
|
28100
28151
|
const dailyPath = agentDailyMemoryPath(rootDir, agent.id, date);
|
|
28101
|
-
if (!
|
|
28152
|
+
if (!existsSync10(dailyPath)) {
|
|
28102
28153
|
await writeFile4(dailyPath, `# Agent Daily Memory ${date.toISOString().slice(0, 10)}
|
|
28103
28154
|
|
|
28104
28155
|
<!-- Running observations for this agent. -->
|
|
@@ -28146,7 +28197,7 @@ function extractDreamProcessorResult(output) {
|
|
|
28146
28197
|
};
|
|
28147
28198
|
}
|
|
28148
28199
|
async function readIfExists(path2) {
|
|
28149
|
-
if (!
|
|
28200
|
+
if (!existsSync10(path2)) {
|
|
28150
28201
|
return "";
|
|
28151
28202
|
}
|
|
28152
28203
|
return readFile5(path2, "utf-8");
|
|
@@ -28188,7 +28239,7 @@ async function readAgentDailyNotes(rootDir, agentId, date) {
|
|
|
28188
28239
|
const workspacePath = agentMemoryWorkspacePath(rootDir, agentId);
|
|
28189
28240
|
const dateKey = date.toISOString().slice(0, 10);
|
|
28190
28241
|
const dailyPath = agentDailyMemoryPath(rootDir, agentId, date);
|
|
28191
|
-
if (
|
|
28242
|
+
if (existsSync10(dailyPath)) {
|
|
28192
28243
|
return readFile5(dailyPath, "utf-8");
|
|
28193
28244
|
}
|
|
28194
28245
|
const files = await readdir3(workspacePath).catch(() => []);
|
|
@@ -28383,7 +28434,7 @@ __export(memory_backend_exports, {
|
|
|
28383
28434
|
writeProjectMemoryFile: () => writeProjectMemoryFile
|
|
28384
28435
|
});
|
|
28385
28436
|
import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir6, access as access2, constants, readdir as readdir4, stat as stat2 } from "node:fs/promises";
|
|
28386
|
-
import { existsSync as
|
|
28437
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
28387
28438
|
import { basename as basename4, dirname as dirname4, isAbsolute as isAbsolute4, join as join14, normalize as normalize2, relative, resolve as resolve5, sep as sep2 } from "node:path";
|
|
28388
28439
|
import { createHash as createHash3 } from "node:crypto";
|
|
28389
28440
|
function shouldSkipBackgroundQmdRefresh() {
|
|
@@ -28468,18 +28519,18 @@ async function ensureOpenClawMemoryFiles(rootDir, date = /* @__PURE__ */ new Dat
|
|
|
28468
28519
|
await mkdir6(workspacePath, { recursive: true });
|
|
28469
28520
|
const longTermPath = memoryLongTermPath(rootDir);
|
|
28470
28521
|
let longTermCreated = false;
|
|
28471
|
-
if (!
|
|
28522
|
+
if (!existsSync11(longTermPath)) {
|
|
28472
28523
|
await writeFile5(longTermPath, getDefaultLongTermMemoryScaffold(), "utf-8");
|
|
28473
28524
|
longTermCreated = true;
|
|
28474
28525
|
}
|
|
28475
28526
|
const todayPath = dailyMemoryPath(rootDir, date);
|
|
28476
28527
|
let dailyCreated = false;
|
|
28477
|
-
if (!
|
|
28528
|
+
if (!existsSync11(todayPath)) {
|
|
28478
28529
|
await writeFile5(todayPath, getDefaultDailyMemoryScaffold(date), "utf-8");
|
|
28479
28530
|
dailyCreated = true;
|
|
28480
28531
|
}
|
|
28481
28532
|
const dreamsPath = memoryDreamsPath(rootDir);
|
|
28482
|
-
if (!
|
|
28533
|
+
if (!existsSync11(dreamsPath)) {
|
|
28483
28534
|
await writeFile5(dreamsPath, getDefaultDreamsScaffold(), "utf-8");
|
|
28484
28535
|
}
|
|
28485
28536
|
return { longTermCreated, dailyCreated };
|
|
@@ -28558,7 +28609,7 @@ async function listAgentMemoryFiles(rootDir, agentId, date = /* @__PURE__ */ new
|
|
|
28558
28609
|
const workspaceDisplayPath = relative(rootDir, workspacePath).replace(/\\/g, "/");
|
|
28559
28610
|
const files = [];
|
|
28560
28611
|
const longTermPath = join14(workspacePath, MEMORY_LONG_TERM_FILENAME);
|
|
28561
|
-
if (
|
|
28612
|
+
if (existsSync11(longTermPath)) {
|
|
28562
28613
|
files.push({
|
|
28563
28614
|
absPath: longTermPath,
|
|
28564
28615
|
displayPath: `${workspaceDisplayPath}/${MEMORY_LONG_TERM_FILENAME}`,
|
|
@@ -28566,14 +28617,14 @@ async function listAgentMemoryFiles(rootDir, agentId, date = /* @__PURE__ */ new
|
|
|
28566
28617
|
});
|
|
28567
28618
|
}
|
|
28568
28619
|
const dreamsPath = join14(workspacePath, MEMORY_DREAMS_FILENAME);
|
|
28569
|
-
if (
|
|
28620
|
+
if (existsSync11(dreamsPath)) {
|
|
28570
28621
|
files.push({
|
|
28571
28622
|
absPath: dreamsPath,
|
|
28572
28623
|
displayPath: `${workspaceDisplayPath}/${MEMORY_DREAMS_FILENAME}`,
|
|
28573
28624
|
fileName: MEMORY_DREAMS_FILENAME
|
|
28574
28625
|
});
|
|
28575
28626
|
}
|
|
28576
|
-
if (
|
|
28627
|
+
if (existsSync11(workspacePath)) {
|
|
28577
28628
|
for (const entry of await readdir4(workspacePath)) {
|
|
28578
28629
|
if (!DAILY_MEMORY_RE.test(entry)) continue;
|
|
28579
28630
|
const absPath = join14(workspacePath, entry);
|
|
@@ -28739,14 +28790,14 @@ async function listMemoryFiles(rootDir) {
|
|
|
28739
28790
|
const files = [];
|
|
28740
28791
|
const workspacePath = memoryWorkspacePath(rootDir);
|
|
28741
28792
|
const longTerm = memoryLongTermPath(rootDir);
|
|
28742
|
-
if (
|
|
28793
|
+
if (existsSync11(longTerm)) {
|
|
28743
28794
|
files.push({ absPath: longTerm, displayPath: `${MEMORY_WORKSPACE_PATH}/${MEMORY_LONG_TERM_FILENAME}` });
|
|
28744
28795
|
}
|
|
28745
28796
|
const dreams = memoryDreamsPath(rootDir);
|
|
28746
|
-
if (
|
|
28797
|
+
if (existsSync11(dreams)) {
|
|
28747
28798
|
files.push({ absPath: dreams, displayPath: `${MEMORY_WORKSPACE_PATH}/${MEMORY_DREAMS_FILENAME}` });
|
|
28748
28799
|
}
|
|
28749
|
-
if (
|
|
28800
|
+
if (existsSync11(workspacePath)) {
|
|
28750
28801
|
for (const entry of await readdir4(workspacePath)) {
|
|
28751
28802
|
if (!DAILY_MEMORY_RE.test(entry)) continue;
|
|
28752
28803
|
const absPath = join14(workspacePath, entry);
|
|
@@ -29112,7 +29163,7 @@ var init_memory_backend = __esm({
|
|
|
29112
29163
|
const filePath = this.getLongTermPath(rootDir);
|
|
29113
29164
|
const dir = join14(rootDir, MEMORY_WORKSPACE_PATH);
|
|
29114
29165
|
try {
|
|
29115
|
-
if (!
|
|
29166
|
+
if (!existsSync11(dir)) {
|
|
29116
29167
|
await mkdir6(dir, { recursive: true });
|
|
29117
29168
|
}
|
|
29118
29169
|
const tmpPath = filePath + ".tmp";
|
|
@@ -29255,7 +29306,7 @@ var init_memory_backend = __esm({
|
|
|
29255
29306
|
|
|
29256
29307
|
// ../core/src/project-memory.ts
|
|
29257
29308
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
29258
|
-
import { existsSync as
|
|
29309
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
29259
29310
|
function getDefaultMemoryScaffold() {
|
|
29260
29311
|
return `# Project Memory
|
|
29261
29312
|
|
|
@@ -29280,7 +29331,7 @@ function getDefaultMemoryScaffold() {
|
|
|
29280
29331
|
}
|
|
29281
29332
|
async function ensureMemoryFile(rootDir) {
|
|
29282
29333
|
const longTermPath = memoryLongTermPath(rootDir);
|
|
29283
|
-
if (
|
|
29334
|
+
if (existsSync12(longTermPath)) {
|
|
29284
29335
|
return false;
|
|
29285
29336
|
}
|
|
29286
29337
|
const { longTermCreated } = await ensureOpenClawMemoryFiles(rootDir);
|
|
@@ -29575,7 +29626,7 @@ This project has a memory system that stores durable project learnings.
|
|
|
29575
29626
|
}
|
|
29576
29627
|
async function readProjectMemory(rootDir) {
|
|
29577
29628
|
const longTermPath = memoryLongTermPath(rootDir);
|
|
29578
|
-
if (!
|
|
29629
|
+
if (!existsSync12(longTermPath)) {
|
|
29579
29630
|
return "";
|
|
29580
29631
|
}
|
|
29581
29632
|
return readFile7(longTermPath, "utf-8");
|
|
@@ -29590,7 +29641,7 @@ var init_project_memory = __esm({
|
|
|
29590
29641
|
// ../core/src/run-command.ts
|
|
29591
29642
|
import { spawn } from "node:child_process";
|
|
29592
29643
|
function runCommandAsync(command, options = {}) {
|
|
29593
|
-
return new Promise((
|
|
29644
|
+
return new Promise((resolve17) => {
|
|
29594
29645
|
const maxBuffer = options.maxBuffer ?? DEFAULT_MAX_BUFFER;
|
|
29595
29646
|
let stdout = "";
|
|
29596
29647
|
let stderr = "";
|
|
@@ -29649,7 +29700,7 @@ function runCommandAsync(command, options = {}) {
|
|
|
29649
29700
|
clearTimeout(forceKillTimer);
|
|
29650
29701
|
forceKillTimer = null;
|
|
29651
29702
|
}
|
|
29652
|
-
|
|
29703
|
+
resolve17({
|
|
29653
29704
|
stdout,
|
|
29654
29705
|
stderr,
|
|
29655
29706
|
exitCode: null,
|
|
@@ -29667,7 +29718,7 @@ function runCommandAsync(command, options = {}) {
|
|
|
29667
29718
|
}
|
|
29668
29719
|
signalProcessGroup("SIGTERM");
|
|
29669
29720
|
scheduleForceKill(NORMAL_CLEANUP_FORCE_KILL_DELAY_MS);
|
|
29670
|
-
|
|
29721
|
+
resolve17({
|
|
29671
29722
|
stdout,
|
|
29672
29723
|
stderr,
|
|
29673
29724
|
exitCode: code,
|
|
@@ -29740,15 +29791,15 @@ import { EventEmitter as EventEmitter12 } from "node:events";
|
|
|
29740
29791
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
29741
29792
|
import { mkdir as mkdir7, readdir as readdir5, readFile as readFile8, writeFile as writeFile6, rename as rename4, unlink as unlink3 } from "node:fs/promises";
|
|
29742
29793
|
import { join as join15 } from "node:path";
|
|
29743
|
-
import { existsSync as
|
|
29794
|
+
import { existsSync as existsSync13, watch } from "node:fs";
|
|
29744
29795
|
function assertSafeGitBranchName(name) {
|
|
29745
29796
|
if (!name || name.length > 255 || name.startsWith("-") || name.startsWith(".") || name.startsWith("/") || name.endsWith("/") || name.endsWith(".") || name.endsWith(".lock") || name.includes("..") || name.includes("@{") || !/^[A-Za-z0-9._/+-]+$/.test(name)) {
|
|
29746
29797
|
throw new Error(`Unsafe git branch name: ${JSON.stringify(name)}`);
|
|
29747
29798
|
}
|
|
29748
29799
|
}
|
|
29749
29800
|
function assertSafeAbsolutePath(path2) {
|
|
29750
|
-
const
|
|
29751
|
-
if (!path2 || path2.length > 4096 || !
|
|
29801
|
+
const isAbsolute13 = path2.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path2);
|
|
29802
|
+
if (!path2 || path2.length > 4096 || !isAbsolute13 || path2.startsWith("-") || // Reject shell metacharacters, quotes, control chars, and NULs.
|
|
29752
29803
|
/["'`$\n\r\t;&|<>()*?[\]{}\\\0]/.test(
|
|
29753
29804
|
path2.replace(/^[A-Za-z]:/, "")
|
|
29754
29805
|
// ignore the drive-letter colon on Windows
|
|
@@ -29972,7 +30023,7 @@ var init_store = __esm({
|
|
|
29972
30023
|
}
|
|
29973
30024
|
await this.migrateActiveArchivedTasksToArchiveDb();
|
|
29974
30025
|
await this.importLegacyAgentLogsOnce();
|
|
29975
|
-
if (!
|
|
30026
|
+
if (!existsSync13(this.configPath)) {
|
|
29976
30027
|
const config = await this.readConfig();
|
|
29977
30028
|
try {
|
|
29978
30029
|
await writeFile6(this.configPath, JSON.stringify(config, null, 2));
|
|
@@ -30034,6 +30085,7 @@ var init_store = __esm({
|
|
|
30034
30085
|
recoveryRetryCount: row.recoveryRetryCount ?? void 0,
|
|
30035
30086
|
taskDoneRetryCount: row.taskDoneRetryCount ?? void 0,
|
|
30036
30087
|
verificationFailureCount: row.verificationFailureCount ?? void 0,
|
|
30088
|
+
mergeConflictBounceCount: row.mergeConflictBounceCount ?? void 0,
|
|
30037
30089
|
nextRecoveryAt: row.nextRecoveryAt || void 0,
|
|
30038
30090
|
error: row.error || void 0,
|
|
30039
30091
|
summary: row.summary || void 0,
|
|
@@ -30181,7 +30233,7 @@ ${recentText}` : void 0
|
|
|
30181
30233
|
}
|
|
30182
30234
|
async readPromptForArchive(taskId) {
|
|
30183
30235
|
const promptPath = join15(this.taskDir(taskId), "PROMPT.md");
|
|
30184
|
-
if (!
|
|
30236
|
+
if (!existsSync13(promptPath)) {
|
|
30185
30237
|
return void 0;
|
|
30186
30238
|
}
|
|
30187
30239
|
return readFile8(promptPath, "utf-8");
|
|
@@ -30323,6 +30375,7 @@ ${recentText}` : void 0
|
|
|
30323
30375
|
"recoveryRetryCount",
|
|
30324
30376
|
"taskDoneRetryCount",
|
|
30325
30377
|
"verificationFailureCount",
|
|
30378
|
+
"mergeConflictBounceCount",
|
|
30326
30379
|
"nextRecoveryAt",
|
|
30327
30380
|
"error",
|
|
30328
30381
|
"summary",
|
|
@@ -30425,6 +30478,7 @@ ${outcome}`;
|
|
|
30425
30478
|
"recoveryRetryCount",
|
|
30426
30479
|
"taskDoneRetryCount",
|
|
30427
30480
|
"verificationFailureCount",
|
|
30481
|
+
"mergeConflictBounceCount",
|
|
30428
30482
|
"nextRecoveryAt",
|
|
30429
30483
|
"error",
|
|
30430
30484
|
"summary",
|
|
@@ -30495,7 +30549,7 @@ ${outcome}`;
|
|
|
30495
30549
|
id, title, description, priority, "column", status, size, reviewLevel, currentStep,
|
|
30496
30550
|
worktree, blockedBy, paused, baseBranch, branch, baseCommitSha, modelPresetId, modelProvider,
|
|
30497
30551
|
modelId, validatorModelProvider, validatorModelId, planningModelProvider, planningModelId, mergeRetries,
|
|
30498
|
-
workflowStepRetries, stuckKillCount, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, verificationFailureCount, nextRecoveryAt, error,
|
|
30552
|
+
workflowStepRetries, stuckKillCount, postReviewFixCount, recoveryRetryCount, taskDoneRetryCount, verificationFailureCount, mergeConflictBounceCount, nextRecoveryAt, error,
|
|
30499
30553
|
summary, thinkingLevel, executionMode, tokenUsageInputTokens, tokenUsageOutputTokens, tokenUsageCachedTokens,
|
|
30500
30554
|
tokenUsageTotalTokens, tokenUsageFirstUsedAt, tokenUsageLastUsedAt, createdAt, updatedAt, columnMovedAt,
|
|
30501
30555
|
dependencies, steps, log, attachments, steeringComments,
|
|
@@ -30503,7 +30557,7 @@ ${outcome}`;
|
|
|
30503
30557
|
sourceIssueProvider, sourceIssueRepository, sourceIssueExternalIssueId, sourceIssueNumber, sourceIssueUrl,
|
|
30504
30558
|
mergeDetails, breakIntoSubtasks, enabledWorkflowSteps, modifiedFiles, missionId, sliceId, assignedAgentId, assigneeUserId, nodeId, effectiveNodeId, effectiveNodeSource, checkedOutBy, checkedOutAt
|
|
30505
30559
|
) VALUES (
|
|
30506
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
30560
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
30507
30561
|
)
|
|
30508
30562
|
ON CONFLICT(id) DO UPDATE SET
|
|
30509
30563
|
title = excluded.title,
|
|
@@ -30534,6 +30588,7 @@ ${outcome}`;
|
|
|
30534
30588
|
recoveryRetryCount = excluded.recoveryRetryCount,
|
|
30535
30589
|
taskDoneRetryCount = excluded.taskDoneRetryCount,
|
|
30536
30590
|
verificationFailureCount = excluded.verificationFailureCount,
|
|
30591
|
+
mergeConflictBounceCount = excluded.mergeConflictBounceCount,
|
|
30537
30592
|
nextRecoveryAt = excluded.nextRecoveryAt,
|
|
30538
30593
|
error = excluded.error,
|
|
30539
30594
|
summary = excluded.summary,
|
|
@@ -30605,6 +30660,7 @@ ${outcome}`;
|
|
|
30605
30660
|
task.recoveryRetryCount ?? null,
|
|
30606
30661
|
task.taskDoneRetryCount ?? 0,
|
|
30607
30662
|
task.verificationFailureCount ?? 0,
|
|
30663
|
+
task.mergeConflictBounceCount ?? 0,
|
|
30608
30664
|
task.nextRecoveryAt ?? null,
|
|
30609
30665
|
task.error ?? null,
|
|
30610
30666
|
task.summary ?? null,
|
|
@@ -30817,9 +30873,9 @@ ${outcome}`;
|
|
|
30817
30873
|
* lost-update races on the nextId counter.
|
|
30818
30874
|
*/
|
|
30819
30875
|
withConfigLock(fn) {
|
|
30820
|
-
let
|
|
30876
|
+
let resolve17;
|
|
30821
30877
|
const next = new Promise((r) => {
|
|
30822
|
-
|
|
30878
|
+
resolve17 = r;
|
|
30823
30879
|
});
|
|
30824
30880
|
const prev = this.configLock;
|
|
30825
30881
|
this.configLock = next;
|
|
@@ -30827,7 +30883,7 @@ ${outcome}`;
|
|
|
30827
30883
|
try {
|
|
30828
30884
|
return await fn();
|
|
30829
30885
|
} finally {
|
|
30830
|
-
|
|
30886
|
+
resolve17();
|
|
30831
30887
|
}
|
|
30832
30888
|
});
|
|
30833
30889
|
}
|
|
@@ -30837,9 +30893,9 @@ ${outcome}`;
|
|
|
30837
30893
|
*/
|
|
30838
30894
|
withTaskLock(id, fn) {
|
|
30839
30895
|
const prev = this.taskLocks.get(id) ?? Promise.resolve();
|
|
30840
|
-
let
|
|
30896
|
+
let resolve17;
|
|
30841
30897
|
const next = new Promise((r) => {
|
|
30842
|
-
|
|
30898
|
+
resolve17 = r;
|
|
30843
30899
|
});
|
|
30844
30900
|
this.taskLocks.set(id, next);
|
|
30845
30901
|
return prev.then(async () => {
|
|
@@ -30849,7 +30905,7 @@ ${outcome}`;
|
|
|
30849
30905
|
if (this.taskLocks.get(id) === next) {
|
|
30850
30906
|
this.taskLocks.delete(id);
|
|
30851
30907
|
}
|
|
30852
|
-
|
|
30908
|
+
resolve17();
|
|
30853
30909
|
}
|
|
30854
30910
|
});
|
|
30855
30911
|
}
|
|
@@ -31567,7 +31623,7 @@ ${newTask.description}
|
|
|
31567
31623
|
for (const attachment of sourceTask.attachments) {
|
|
31568
31624
|
const sourcePath = join15(sourceAttachDir, attachment.filename);
|
|
31569
31625
|
const targetPath = join15(targetAttachDir, attachment.filename);
|
|
31570
|
-
if (
|
|
31626
|
+
if (existsSync13(sourcePath)) {
|
|
31571
31627
|
const content = await readFile8(sourcePath);
|
|
31572
31628
|
await writeFile6(targetPath, content);
|
|
31573
31629
|
}
|
|
@@ -31599,7 +31655,7 @@ ${newTask.description}
|
|
|
31599
31655
|
}
|
|
31600
31656
|
let prompt = "";
|
|
31601
31657
|
const promptPath = join15(this.taskDir(id), "PROMPT.md");
|
|
31602
|
-
if (
|
|
31658
|
+
if (existsSync13(promptPath)) {
|
|
31603
31659
|
prompt = await readFile8(promptPath, "utf-8");
|
|
31604
31660
|
}
|
|
31605
31661
|
return { ...task, prompt };
|
|
@@ -32002,6 +32058,11 @@ ${newTask.description}
|
|
|
32002
32058
|
} else if (updates.verificationFailureCount !== void 0) {
|
|
32003
32059
|
task.verificationFailureCount = updates.verificationFailureCount;
|
|
32004
32060
|
}
|
|
32061
|
+
if (updates.mergeConflictBounceCount === null) {
|
|
32062
|
+
task.mergeConflictBounceCount = void 0;
|
|
32063
|
+
} else if (updates.mergeConflictBounceCount !== void 0) {
|
|
32064
|
+
task.mergeConflictBounceCount = updates.mergeConflictBounceCount;
|
|
32065
|
+
}
|
|
32005
32066
|
if (updates.nextRecoveryAt === null) {
|
|
32006
32067
|
task.nextRecoveryAt = void 0;
|
|
32007
32068
|
} else if (updates.nextRecoveryAt !== void 0) {
|
|
@@ -32121,7 +32182,7 @@ ${newTask.description}
|
|
|
32121
32182
|
}
|
|
32122
32183
|
if (updates.prompt === void 0 && (updates.title !== void 0 || updates.description !== void 0)) {
|
|
32123
32184
|
const promptPath = join15(dir, "PROMPT.md");
|
|
32124
|
-
if (
|
|
32185
|
+
if (existsSync13(promptPath)) {
|
|
32125
32186
|
const existingPrompt = await readFile8(promptPath, "utf-8");
|
|
32126
32187
|
let newPrompt;
|
|
32127
32188
|
if (task.column === "triage") {
|
|
@@ -32422,7 +32483,7 @@ ${task.description}
|
|
|
32422
32483
|
async parseStepsFromPrompt(id) {
|
|
32423
32484
|
const dir = this.taskDir(id);
|
|
32424
32485
|
const promptPath = join15(dir, "PROMPT.md");
|
|
32425
|
-
if (!
|
|
32486
|
+
if (!existsSync13(promptPath)) return [];
|
|
32426
32487
|
const content = await readFile8(promptPath, "utf-8");
|
|
32427
32488
|
const steps = [];
|
|
32428
32489
|
const stepRegex = /^###\s+Step\s+\d+[^:]*:\s*(.+)$/gm;
|
|
@@ -32445,7 +32506,7 @@ ${task.description}
|
|
|
32445
32506
|
async parseDependenciesFromPrompt(id) {
|
|
32446
32507
|
const dir = this.taskDir(id);
|
|
32447
32508
|
const promptPath = join15(dir, "PROMPT.md");
|
|
32448
|
-
if (!
|
|
32509
|
+
if (!existsSync13(promptPath)) return [];
|
|
32449
32510
|
const content = await readFile8(promptPath, "utf-8");
|
|
32450
32511
|
const headingMatch = content.match(/^##\s+Dependencies\s*$/m);
|
|
32451
32512
|
if (!headingMatch) return [];
|
|
@@ -32469,7 +32530,7 @@ ${task.description}
|
|
|
32469
32530
|
async parseFileScopeFromPrompt(id) {
|
|
32470
32531
|
const dir = this.taskDir(id);
|
|
32471
32532
|
const promptPath = join15(dir, "PROMPT.md");
|
|
32472
|
-
if (!
|
|
32533
|
+
if (!existsSync13(promptPath)) return [];
|
|
32473
32534
|
const content = await readFile8(promptPath, "utf-8");
|
|
32474
32535
|
const headingMatch = content.match(/^##\s+File\s+Scope\s*$/m);
|
|
32475
32536
|
if (!headingMatch) return [];
|
|
@@ -32506,7 +32567,7 @@ ${task.description}
|
|
|
32506
32567
|
const rewrittenDependents = this.rewriteDependentsAndDeleteTask(id, dependentIds);
|
|
32507
32568
|
if (this.isWatching) this.taskCache.delete(id);
|
|
32508
32569
|
const dir = this.taskDir(id);
|
|
32509
|
-
if (
|
|
32570
|
+
if (existsSync13(dir)) {
|
|
32510
32571
|
const { rm: rm4 } = await import("node:fs/promises");
|
|
32511
32572
|
await rm4(dir, { recursive: true });
|
|
32512
32573
|
}
|
|
@@ -32698,7 +32759,7 @@ ${task.description}
|
|
|
32698
32759
|
};
|
|
32699
32760
|
const worktreePath2 = task.worktree;
|
|
32700
32761
|
const changed = this.clearDoneTransientFields(task);
|
|
32701
|
-
if (worktreePath2 &&
|
|
32762
|
+
if (worktreePath2 && existsSync13(worktreePath2)) {
|
|
32702
32763
|
assertSafeAbsolutePath(worktreePath2);
|
|
32703
32764
|
const removeWorktree = await this.runGitCommand(`git worktree remove "${worktreePath2}" --force`, 12e4);
|
|
32704
32765
|
if (removeWorktree.exitCode === 0) {
|
|
@@ -32765,7 +32826,7 @@ ${task.description}
|
|
|
32765
32826
|
# resolve conflicts, then: fn task move ${id} done`
|
|
32766
32827
|
);
|
|
32767
32828
|
}
|
|
32768
|
-
if (worktreePath &&
|
|
32829
|
+
if (worktreePath && existsSync13(worktreePath)) {
|
|
32769
32830
|
assertSafeAbsolutePath(worktreePath);
|
|
32770
32831
|
const removeWorktree = await this.runGitCommand(`git worktree remove "${worktreePath}" --force`, 12e4);
|
|
32771
32832
|
if (removeWorktree.exitCode === 0) {
|
|
@@ -33004,7 +33065,7 @@ ${task.description}
|
|
|
33004
33065
|
}
|
|
33005
33066
|
}
|
|
33006
33067
|
}
|
|
33007
|
-
await new Promise((
|
|
33068
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
33008
33069
|
const selectClause = this.getTaskSelectClause(true);
|
|
33009
33070
|
const changedRows = this.lastPollTime ? this.db.prepare(`SELECT ${selectClause} FROM tasks WHERE updatedAt > ? OR columnMovedAt > ?`).all(this.lastPollTime, this.lastPollTime) : this.db.prepare(`SELECT ${selectClause} FROM tasks`).all();
|
|
33010
33071
|
this.lastPollTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -33024,7 +33085,7 @@ ${task.description}
|
|
|
33024
33085
|
this.emit("task:updated", task);
|
|
33025
33086
|
}
|
|
33026
33087
|
if (i > 0 && i % 50 === 0) {
|
|
33027
|
-
await new Promise((
|
|
33088
|
+
await new Promise((resolve17) => setImmediate(resolve17));
|
|
33028
33089
|
}
|
|
33029
33090
|
}
|
|
33030
33091
|
const elapsed = Date.now() - startTime;
|
|
@@ -33716,7 +33777,7 @@ ${task.description}
|
|
|
33716
33777
|
return rows.map((row) => this.mapAgentLogRow(row));
|
|
33717
33778
|
}
|
|
33718
33779
|
async importLegacyAgentLogs() {
|
|
33719
|
-
if (!
|
|
33780
|
+
if (!existsSync13(this.tasksDir)) return 0;
|
|
33720
33781
|
const entries = await readdir5(this.tasksDir, { withFileTypes: true });
|
|
33721
33782
|
let imported = 0;
|
|
33722
33783
|
const insertStmt = this.db.prepare(`
|
|
@@ -33726,7 +33787,7 @@ ${task.description}
|
|
|
33726
33787
|
for (const entry of entries) {
|
|
33727
33788
|
if (!entry.isDirectory()) continue;
|
|
33728
33789
|
const logPath = join15(this.tasksDir, entry.name, "agent.log");
|
|
33729
|
-
if (!
|
|
33790
|
+
if (!existsSync13(logPath)) continue;
|
|
33730
33791
|
try {
|
|
33731
33792
|
const content = await readFile8(logPath, "utf-8");
|
|
33732
33793
|
for (const line of content.split("\n")) {
|
|
@@ -33830,7 +33891,7 @@ ${task.description}
|
|
|
33830
33891
|
const cleanedUpIds = [];
|
|
33831
33892
|
for (const task of archivedTasks) {
|
|
33832
33893
|
const dir = this.taskDir(task.id);
|
|
33833
|
-
if (!
|
|
33894
|
+
if (!existsSync13(dir)) {
|
|
33834
33895
|
continue;
|
|
33835
33896
|
}
|
|
33836
33897
|
const entry = await this.taskToArchiveEntry(task, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -34564,17 +34625,17 @@ var init_daemon_token = __esm({
|
|
|
34564
34625
|
});
|
|
34565
34626
|
|
|
34566
34627
|
// ../core/src/pi-extensions.ts
|
|
34567
|
-
import { existsSync as
|
|
34628
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync2, statSync as statSync3, writeFileSync } from "node:fs";
|
|
34568
34629
|
import { homedir as homedir3 } from "node:os";
|
|
34569
|
-
import { basename as basename5, join as join16, relative as relative2, resolve as resolve6, sep as sep3 } from "node:path";
|
|
34570
|
-
function
|
|
34630
|
+
import { basename as basename5, isAbsolute as isAbsolute5, join as join16, relative as relative2, resolve as resolve6, sep as sep3, win32 } from "node:path";
|
|
34631
|
+
function getHomeDir3(home) {
|
|
34571
34632
|
return home ?? process.env.HOME ?? process.env.USERPROFILE ?? homedir3();
|
|
34572
34633
|
}
|
|
34573
34634
|
function getFusionAgentDir(home) {
|
|
34574
|
-
return join16(
|
|
34635
|
+
return join16(getHomeDir3(home), ".fusion", "agent");
|
|
34575
34636
|
}
|
|
34576
34637
|
function getLegacyPiAgentDir(home) {
|
|
34577
|
-
return join16(
|
|
34638
|
+
return join16(getHomeDir3(home), ".pi", "agent");
|
|
34578
34639
|
}
|
|
34579
34640
|
function getFusionAgentSettingsPath(home) {
|
|
34580
34641
|
return join16(getFusionAgentDir(home), "settings.json");
|
|
@@ -34582,7 +34643,7 @@ function getFusionAgentSettingsPath(home) {
|
|
|
34582
34643
|
function resolvePiExtensionProjectRoot(cwd) {
|
|
34583
34644
|
let current = resolve6(cwd);
|
|
34584
34645
|
while (true) {
|
|
34585
|
-
if (
|
|
34646
|
+
if (existsSync14(join16(current, ".fusion"))) {
|
|
34586
34647
|
return current;
|
|
34587
34648
|
}
|
|
34588
34649
|
const parent = resolve6(current, "..");
|
|
@@ -34619,21 +34680,21 @@ function readPiManifest(packageJsonPath) {
|
|
|
34619
34680
|
}
|
|
34620
34681
|
function resolveExtensionEntries(dir) {
|
|
34621
34682
|
const packageJsonPath = join16(dir, "package.json");
|
|
34622
|
-
if (
|
|
34683
|
+
if (existsSync14(packageJsonPath)) {
|
|
34623
34684
|
const manifest = readPiManifest(packageJsonPath);
|
|
34624
34685
|
if (manifest?.extensions?.length) {
|
|
34625
|
-
const entries = manifest.extensions.map((entry) => resolve6(dir, entry)).filter((entry) =>
|
|
34686
|
+
const entries = manifest.extensions.map((entry) => resolve6(dir, entry)).filter((entry) => existsSync14(entry));
|
|
34626
34687
|
if (entries.length > 0) return entries;
|
|
34627
34688
|
}
|
|
34628
34689
|
}
|
|
34629
34690
|
const indexTs = join16(dir, "index.ts");
|
|
34630
|
-
if (
|
|
34691
|
+
if (existsSync14(indexTs)) return [indexTs];
|
|
34631
34692
|
const indexJs = join16(dir, "index.js");
|
|
34632
|
-
if (
|
|
34693
|
+
if (existsSync14(indexJs)) return [indexJs];
|
|
34633
34694
|
return null;
|
|
34634
34695
|
}
|
|
34635
34696
|
function discoverExtensionsInDir(dir, cwd, home) {
|
|
34636
|
-
if (!
|
|
34697
|
+
if (!existsSync14(dir)) return [];
|
|
34637
34698
|
const discovered = [];
|
|
34638
34699
|
try {
|
|
34639
34700
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
@@ -34753,10 +34814,24 @@ function reconcileClaudeCliPaths(paths, vendoredPath) {
|
|
|
34753
34814
|
}
|
|
34754
34815
|
return filtered;
|
|
34755
34816
|
}
|
|
34817
|
+
function getDisplayPathWithinRoot(root, targetPath) {
|
|
34818
|
+
const usesWindowsPaths = /^[A-Za-z]:[\\/]/.test(root) || /^[A-Za-z]:[\\/]/.test(targetPath) || root.includes("\\") || targetPath.includes("\\");
|
|
34819
|
+
const pathApi = usesWindowsPaths ? win32 : { relative: relative2, isAbsolute: isAbsolute5, sep: sep3 };
|
|
34820
|
+
const rel = pathApi.relative(root, targetPath);
|
|
34821
|
+
if (rel === "") {
|
|
34822
|
+
return "";
|
|
34823
|
+
}
|
|
34824
|
+
if (!rel || rel === ".." || rel.startsWith(`..${pathApi.sep}`) || pathApi.isAbsolute(rel)) {
|
|
34825
|
+
return null;
|
|
34826
|
+
}
|
|
34827
|
+
return rel.split(pathApi.sep).join("/");
|
|
34828
|
+
}
|
|
34756
34829
|
function formatPiExtensionSource(source, extensionPath, cwd, home) {
|
|
34757
|
-
const homeDir =
|
|
34830
|
+
const homeDir = getHomeDir3(home);
|
|
34758
34831
|
const projectRoot = resolvePiExtensionProjectRoot(cwd);
|
|
34759
|
-
const
|
|
34832
|
+
const relativeToHome = getDisplayPathWithinRoot(homeDir, extensionPath);
|
|
34833
|
+
const relativeToProject = getDisplayPathWithinRoot(projectRoot, extensionPath);
|
|
34834
|
+
const relativePath = relativeToHome !== null ? relativeToHome.length > 0 ? `~/${relativeToHome}` : "~" : relativeToProject !== null ? relativeToProject || "." : extensionPath;
|
|
34760
34835
|
return `${source}: ${relativePath}`;
|
|
34761
34836
|
}
|
|
34762
34837
|
var FUSION_DISABLED_EXTENSIONS_KEY;
|
|
@@ -34850,7 +34925,7 @@ function runGh(args, cwd) {
|
|
|
34850
34925
|
}
|
|
34851
34926
|
function runGhAsync(args, cwdOrOptions) {
|
|
34852
34927
|
const { cwd, signal: externalSignal, timeoutMs = DEFAULT_GH_TIMEOUT_MS } = normalizeRunGhOptions(cwdOrOptions);
|
|
34853
|
-
return new Promise((
|
|
34928
|
+
return new Promise((resolve17, reject) => {
|
|
34854
34929
|
if (externalSignal?.aborted) {
|
|
34855
34930
|
reject(makeGhError(`gh command aborted: ${describeAbortReason(externalSignal.reason)}`, "ABORT_ERR"));
|
|
34856
34931
|
return;
|
|
@@ -34901,7 +34976,7 @@ function runGhAsync(args, cwdOrOptions) {
|
|
|
34901
34976
|
ghError.stderr = stderr ?? "";
|
|
34902
34977
|
reject(ghError);
|
|
34903
34978
|
} else {
|
|
34904
|
-
|
|
34979
|
+
resolve17(stdout ?? "");
|
|
34905
34980
|
}
|
|
34906
34981
|
}
|
|
34907
34982
|
);
|
|
@@ -35189,9 +35264,9 @@ var init_routine_store = __esm({
|
|
|
35189
35264
|
*/
|
|
35190
35265
|
withRoutineLock(id, fn) {
|
|
35191
35266
|
const prev = this.routineLocks.get(id) ?? Promise.resolve();
|
|
35192
|
-
let
|
|
35267
|
+
let resolve17;
|
|
35193
35268
|
const next = new Promise((r) => {
|
|
35194
|
-
|
|
35269
|
+
resolve17 = r;
|
|
35195
35270
|
});
|
|
35196
35271
|
this.routineLocks.set(id, next);
|
|
35197
35272
|
return prev.then(async () => {
|
|
@@ -35201,7 +35276,7 @@ var init_routine_store = __esm({
|
|
|
35201
35276
|
if (this.routineLocks.get(id) === next) {
|
|
35202
35277
|
this.routineLocks.delete(id);
|
|
35203
35278
|
}
|
|
35204
|
-
|
|
35279
|
+
resolve17();
|
|
35205
35280
|
}
|
|
35206
35281
|
});
|
|
35207
35282
|
}
|
|
@@ -35534,7 +35609,7 @@ var init_dispatcher = __esm({
|
|
|
35534
35609
|
});
|
|
35535
35610
|
|
|
35536
35611
|
// ../core/src/plugin-loader.ts
|
|
35537
|
-
import { basename as basename6, dirname as dirname5, extname, isAbsolute as
|
|
35612
|
+
import { basename as basename6, dirname as dirname5, extname, isAbsolute as isAbsolute6, resolve as resolve7 } from "node:path";
|
|
35538
35613
|
import { copyFile, rm } from "node:fs/promises";
|
|
35539
35614
|
import { pathToFileURL } from "node:url";
|
|
35540
35615
|
import { EventEmitter as EventEmitter14 } from "node:events";
|
|
@@ -35673,7 +35748,7 @@ var init_plugin_loader = __esm({
|
|
|
35673
35748
|
}
|
|
35674
35749
|
}
|
|
35675
35750
|
resolvePluginPath(path2) {
|
|
35676
|
-
if (
|
|
35751
|
+
if (isAbsolute6(path2)) {
|
|
35677
35752
|
return path2;
|
|
35678
35753
|
}
|
|
35679
35754
|
if (path2.startsWith("@") || path2.includes("/")) {
|
|
@@ -35800,13 +35875,13 @@ var init_plugin_loader = __esm({
|
|
|
35800
35875
|
* Execute a promise with a timeout.
|
|
35801
35876
|
*/
|
|
35802
35877
|
withTimeout(promise, ms, timeoutMessage) {
|
|
35803
|
-
return new Promise((
|
|
35878
|
+
return new Promise((resolve17, reject) => {
|
|
35804
35879
|
const timer = setTimeout(() => {
|
|
35805
35880
|
reject(new Error(timeoutMessage));
|
|
35806
35881
|
}, ms);
|
|
35807
35882
|
promise.then((result) => {
|
|
35808
35883
|
clearTimeout(timer);
|
|
35809
|
-
|
|
35884
|
+
resolve17(result);
|
|
35810
35885
|
}).catch((err) => {
|
|
35811
35886
|
clearTimeout(timer);
|
|
35812
35887
|
reject(err);
|
|
@@ -36092,7 +36167,7 @@ var init_plugin_loader = __esm({
|
|
|
36092
36167
|
|
|
36093
36168
|
// ../core/src/backup.ts
|
|
36094
36169
|
import { cp, mkdir as mkdir8, readdir as readdir6, stat as stat3, unlink as unlink4 } from "node:fs/promises";
|
|
36095
|
-
import { existsSync as
|
|
36170
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
36096
36171
|
import { join as join17 } from "node:path";
|
|
36097
36172
|
function generateBackupFilename() {
|
|
36098
36173
|
return `fusion-${formatTimestamp(/* @__PURE__ */ new Date())}.db`;
|
|
@@ -36276,7 +36351,7 @@ var init_backup = __esm({
|
|
|
36276
36351
|
let filename = generateBackupFilename();
|
|
36277
36352
|
let targetPath = join17(backupDirPath, filename);
|
|
36278
36353
|
let counter = 1;
|
|
36279
|
-
while (
|
|
36354
|
+
while (existsSync15(targetPath)) {
|
|
36280
36355
|
const baseName = filename.replace(/\.db$/, "");
|
|
36281
36356
|
filename = `${baseName}-${counter}.db`;
|
|
36282
36357
|
targetPath = join17(backupDirPath, filename);
|
|
@@ -36653,10 +36728,88 @@ async function summarizeTitle(description, rootDir, provider, modelId) {
|
|
|
36653
36728
|
}
|
|
36654
36729
|
}
|
|
36655
36730
|
}
|
|
36731
|
+
async function summarizeCommitBody(diffStat, rootDir, provider, modelId, opts) {
|
|
36732
|
+
const trimmedStat = (diffStat ?? "").trim();
|
|
36733
|
+
if (trimmedStat.length === 0) {
|
|
36734
|
+
return null;
|
|
36735
|
+
}
|
|
36736
|
+
const truncatedStat = trimmedStat.length > MAX_COMMIT_BODY_INPUT_LENGTH ? trimmedStat.slice(0, MAX_COMMIT_BODY_INPUT_LENGTH) + "\n\u2026(truncated)" : trimmedStat;
|
|
36737
|
+
const userPromptParts = [];
|
|
36738
|
+
if (opts?.branch) userPromptParts.push(`Branch: ${opts.branch}`);
|
|
36739
|
+
if (opts?.taskId) userPromptParts.push(`Task: ${opts.taskId}`);
|
|
36740
|
+
if (userPromptParts.length > 0) userPromptParts.push("");
|
|
36741
|
+
userPromptParts.push("Files changed (`git diff --stat`):");
|
|
36742
|
+
userPromptParts.push(truncatedStat);
|
|
36743
|
+
userPromptParts.push("");
|
|
36744
|
+
userPromptParts.push("Write the commit body now.");
|
|
36745
|
+
const userPrompt = userPromptParts.join("\n");
|
|
36746
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_COMMIT_BODY_TIMEOUT_MS;
|
|
36747
|
+
const aborter = new AbortController();
|
|
36748
|
+
const timer = setTimeout(() => aborter.abort(), timeoutMs);
|
|
36749
|
+
if (opts?.signal) {
|
|
36750
|
+
if (opts.signal.aborted) aborter.abort();
|
|
36751
|
+
else opts.signal.addEventListener("abort", () => aborter.abort(), { once: true });
|
|
36752
|
+
}
|
|
36753
|
+
let session;
|
|
36754
|
+
try {
|
|
36755
|
+
const createFnAgent5 = await getFnAgent();
|
|
36756
|
+
if (!createFnAgent5) {
|
|
36757
|
+
if (DEBUG) console.log("[ai-summarize] AI engine not available for commit body");
|
|
36758
|
+
return null;
|
|
36759
|
+
}
|
|
36760
|
+
const agentOptions = {
|
|
36761
|
+
cwd: rootDir,
|
|
36762
|
+
systemPrompt: COMMIT_BODY_SYSTEM_PROMPT,
|
|
36763
|
+
tools: "readonly"
|
|
36764
|
+
};
|
|
36765
|
+
if (provider && modelId) {
|
|
36766
|
+
agentOptions.defaultProvider = provider;
|
|
36767
|
+
agentOptions.defaultModelId = modelId;
|
|
36768
|
+
}
|
|
36769
|
+
const agentResult = await createFnAgent5(agentOptions);
|
|
36770
|
+
if (!agentResult?.session) return null;
|
|
36771
|
+
session = agentResult.session;
|
|
36772
|
+
await session.prompt(userPrompt);
|
|
36773
|
+
if (aborter.signal.aborted) return null;
|
|
36774
|
+
if (session.state?.error) {
|
|
36775
|
+
if (DEBUG) console.log(`[ai-summarize] Commit-body session error: ${session.state.error}`);
|
|
36776
|
+
return null;
|
|
36777
|
+
}
|
|
36778
|
+
const messages = session.state?.messages ?? [];
|
|
36779
|
+
const assistant = messages.filter((m) => m.role === "assistant").pop();
|
|
36780
|
+
if (!assistant?.content) return null;
|
|
36781
|
+
let body = "";
|
|
36782
|
+
if (typeof assistant.content === "string") {
|
|
36783
|
+
body = assistant.content;
|
|
36784
|
+
} else if (Array.isArray(assistant.content)) {
|
|
36785
|
+
body = assistant.content.filter(
|
|
36786
|
+
(c) => c.type === "text" && typeof c.text === "string"
|
|
36787
|
+
).map((c) => c.text).join("");
|
|
36788
|
+
}
|
|
36789
|
+
body = body.trim();
|
|
36790
|
+
if (!body) return null;
|
|
36791
|
+
if (body.length > MAX_COMMIT_BODY_LENGTH) {
|
|
36792
|
+
body = body.slice(0, MAX_COMMIT_BODY_LENGTH).trim();
|
|
36793
|
+
}
|
|
36794
|
+
return body;
|
|
36795
|
+
} catch (err) {
|
|
36796
|
+
if (DEBUG) {
|
|
36797
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
36798
|
+
console.log(`[ai-summarize] Commit-body generation failed: ${message}`);
|
|
36799
|
+
}
|
|
36800
|
+
return null;
|
|
36801
|
+
} finally {
|
|
36802
|
+
clearTimeout(timer);
|
|
36803
|
+
try {
|
|
36804
|
+
session?.dispose?.();
|
|
36805
|
+
} catch {
|
|
36806
|
+
}
|
|
36807
|
+
}
|
|
36808
|
+
}
|
|
36656
36809
|
function __resetSummarizeState() {
|
|
36657
36810
|
rateLimits.clear();
|
|
36658
36811
|
}
|
|
36659
|
-
var SUMMARIZE_SYSTEM_PROMPT, MAX_DESCRIPTION_LENGTH, MIN_DESCRIPTION_LENGTH, MAX_TITLE_LENGTH, MAX_REQUESTS_PER_HOUR, RATE_LIMIT_WINDOW_MS, CLEANUP_INTERVAL_MS, rateLimits, cleanupInterval, ValidationError, RateLimitError, AiServiceError, DEBUG;
|
|
36812
|
+
var SUMMARIZE_SYSTEM_PROMPT, MAX_DESCRIPTION_LENGTH, MIN_DESCRIPTION_LENGTH, MAX_TITLE_LENGTH, MAX_REQUESTS_PER_HOUR, RATE_LIMIT_WINDOW_MS, CLEANUP_INTERVAL_MS, rateLimits, cleanupInterval, ValidationError, RateLimitError, AiServiceError, DEBUG, COMMIT_BODY_SYSTEM_PROMPT, MAX_COMMIT_BODY_INPUT_LENGTH, MAX_COMMIT_BODY_LENGTH, DEFAULT_COMMIT_BODY_TIMEOUT_MS;
|
|
36660
36813
|
var init_ai_summarize = __esm({
|
|
36661
36814
|
"../core/src/ai-summarize.ts"() {
|
|
36662
36815
|
"use strict";
|
|
@@ -36701,6 +36854,19 @@ Your job is to create a concise title (max 60 characters) that summarizes the gi
|
|
|
36701
36854
|
}
|
|
36702
36855
|
};
|
|
36703
36856
|
DEBUG = process.env.FUSION_DEBUG_AI === "true";
|
|
36857
|
+
COMMIT_BODY_SYSTEM_PROMPT = `You write concise commit message bodies for merge commits.
|
|
36858
|
+
|
|
36859
|
+
Your job is to summarize the changes described in a \`git diff --stat\` into a short, useful body.
|
|
36860
|
+
|
|
36861
|
+
## Guidelines
|
|
36862
|
+
- Output ONLY the body text \u2014 no code fences, no preamble, no subject line
|
|
36863
|
+
- 2\u20136 short bullet points starting with "- "
|
|
36864
|
+
- Be specific about what changed; reference filenames where helpful
|
|
36865
|
+
- Keep total output under 600 characters
|
|
36866
|
+
- Do not invent details that aren't in the input \u2014 if uncertain, stay general`;
|
|
36867
|
+
MAX_COMMIT_BODY_INPUT_LENGTH = 4e3;
|
|
36868
|
+
MAX_COMMIT_BODY_LENGTH = 2e3;
|
|
36869
|
+
DEFAULT_COMMIT_BODY_TIMEOUT_MS = 3e4;
|
|
36704
36870
|
}
|
|
36705
36871
|
});
|
|
36706
36872
|
|
|
@@ -37028,18 +37194,18 @@ var init_mission_types = __esm({
|
|
|
37028
37194
|
|
|
37029
37195
|
// ../core/src/memory-insights.ts
|
|
37030
37196
|
import { readFile as readFile10, writeFile as writeFile8, mkdir as mkdir9 } from "node:fs/promises";
|
|
37031
|
-
import { existsSync as
|
|
37197
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
37032
37198
|
import { dirname as dirname6, join as join18 } from "node:path";
|
|
37033
37199
|
async function readWorkingMemory(rootDir) {
|
|
37034
37200
|
const filePath = join18(rootDir, MEMORY_WORKING_PATH);
|
|
37035
|
-
if (!
|
|
37201
|
+
if (!existsSync16(filePath)) {
|
|
37036
37202
|
return "";
|
|
37037
37203
|
}
|
|
37038
37204
|
return readFile10(filePath, "utf-8");
|
|
37039
37205
|
}
|
|
37040
37206
|
async function readInsightsMemory(rootDir) {
|
|
37041
37207
|
const filePath = join18(rootDir, MEMORY_INSIGHTS_PATH);
|
|
37042
|
-
if (!
|
|
37208
|
+
if (!existsSync16(filePath)) {
|
|
37043
37209
|
return null;
|
|
37044
37210
|
}
|
|
37045
37211
|
return readFile10(filePath, "utf-8");
|
|
@@ -37047,7 +37213,7 @@ async function readInsightsMemory(rootDir) {
|
|
|
37047
37213
|
async function writeInsightsMemory(rootDir, content) {
|
|
37048
37214
|
const filePath = join18(rootDir, MEMORY_INSIGHTS_PATH);
|
|
37049
37215
|
const dir = join18(rootDir, ".fusion");
|
|
37050
|
-
if (!
|
|
37216
|
+
if (!existsSync16(dir)) {
|
|
37051
37217
|
await mkdir9(dir, { recursive: true });
|
|
37052
37218
|
}
|
|
37053
37219
|
await writeFile8(filePath, content, "utf-8");
|
|
@@ -37055,14 +37221,14 @@ async function writeInsightsMemory(rootDir, content) {
|
|
|
37055
37221
|
async function writeWorkingMemory(rootDir, content) {
|
|
37056
37222
|
const filePath = join18(rootDir, MEMORY_WORKING_PATH);
|
|
37057
37223
|
const dir = dirname6(filePath);
|
|
37058
|
-
if (!
|
|
37224
|
+
if (!existsSync16(dir)) {
|
|
37059
37225
|
await mkdir9(dir, { recursive: true });
|
|
37060
37226
|
}
|
|
37061
37227
|
await writeFile8(filePath, content, "utf-8");
|
|
37062
37228
|
}
|
|
37063
37229
|
async function readMemoryAudit(rootDir) {
|
|
37064
37230
|
const filePath = join18(rootDir, MEMORY_AUDIT_PATH);
|
|
37065
|
-
if (!
|
|
37231
|
+
if (!existsSync16(filePath)) {
|
|
37066
37232
|
return null;
|
|
37067
37233
|
}
|
|
37068
37234
|
return readFile10(filePath, "utf-8");
|
|
@@ -37070,14 +37236,14 @@ async function readMemoryAudit(rootDir) {
|
|
|
37070
37236
|
async function writeMemoryAudit(rootDir, content) {
|
|
37071
37237
|
const filePath = join18(rootDir, MEMORY_AUDIT_PATH);
|
|
37072
37238
|
const dir = join18(rootDir, ".fusion");
|
|
37073
|
-
if (!
|
|
37239
|
+
if (!existsSync16(dir)) {
|
|
37074
37240
|
await mkdir9(dir, { recursive: true });
|
|
37075
37241
|
}
|
|
37076
37242
|
await writeFile8(filePath, content, "utf-8");
|
|
37077
37243
|
}
|
|
37078
37244
|
async function readMemoryAuditState(rootDir) {
|
|
37079
37245
|
const filePath = join18(rootDir, MEMORY_AUDIT_STATE_PATH);
|
|
37080
|
-
if (!
|
|
37246
|
+
if (!existsSync16(filePath)) {
|
|
37081
37247
|
return null;
|
|
37082
37248
|
}
|
|
37083
37249
|
try {
|
|
@@ -37097,7 +37263,7 @@ async function readMemoryAuditState(rootDir) {
|
|
|
37097
37263
|
async function writeMemoryAuditState(rootDir, state) {
|
|
37098
37264
|
const filePath = join18(rootDir, MEMORY_AUDIT_STATE_PATH);
|
|
37099
37265
|
const dir = join18(rootDir, ".fusion");
|
|
37100
|
-
if (!
|
|
37266
|
+
if (!existsSync16(dir)) {
|
|
37101
37267
|
await mkdir9(dir, { recursive: true });
|
|
37102
37268
|
}
|
|
37103
37269
|
await writeFile8(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
@@ -37574,7 +37740,7 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
37574
37740
|
const effectiveExtraction = lastExtraction ?? persistedState?.extraction;
|
|
37575
37741
|
const effectivePruning = pruningOutcome ?? persistedState?.pruning;
|
|
37576
37742
|
const workingMemoryPath = join18(rootDir, MEMORY_WORKING_PATH);
|
|
37577
|
-
const workingMemoryExists =
|
|
37743
|
+
const workingMemoryExists = existsSync16(workingMemoryPath);
|
|
37578
37744
|
let workingMemorySize = 0;
|
|
37579
37745
|
let workingMemorySectionCount = 0;
|
|
37580
37746
|
let workingMemoryContent = "";
|
|
@@ -37629,7 +37795,7 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
37629
37795
|
});
|
|
37630
37796
|
}
|
|
37631
37797
|
const insightsMemoryPath = join18(rootDir, MEMORY_INSIGHTS_PATH);
|
|
37632
|
-
const insightsMemoryExists =
|
|
37798
|
+
const insightsMemoryExists = existsSync16(insightsMemoryPath);
|
|
37633
37799
|
let insightsMemorySize = 0;
|
|
37634
37800
|
let insightsMemoryContent = "";
|
|
37635
37801
|
const categoryCounts = {
|
|
@@ -39013,7 +39179,7 @@ var require_get_stream = __commonJS({
|
|
|
39013
39179
|
};
|
|
39014
39180
|
const { maxBuffer } = options;
|
|
39015
39181
|
let stream;
|
|
39016
|
-
await new Promise((
|
|
39182
|
+
await new Promise((resolve17, reject) => {
|
|
39017
39183
|
const rejectPromise = (error) => {
|
|
39018
39184
|
if (error && stream.getBufferedLength() <= BufferConstants.MAX_LENGTH) {
|
|
39019
39185
|
error.bufferedData = stream.getBufferedValue();
|
|
@@ -39025,7 +39191,7 @@ var require_get_stream = __commonJS({
|
|
|
39025
39191
|
rejectPromise(error);
|
|
39026
39192
|
return;
|
|
39027
39193
|
}
|
|
39028
|
-
|
|
39194
|
+
resolve17();
|
|
39029
39195
|
});
|
|
39030
39196
|
stream.on("data", () => {
|
|
39031
39197
|
if (stream.getBufferedLength() > maxBuffer) {
|
|
@@ -40319,7 +40485,7 @@ var require_extract_zip = __commonJS({
|
|
|
40319
40485
|
debug("opening", this.zipPath, "with opts", this.opts);
|
|
40320
40486
|
this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
|
|
40321
40487
|
this.canceled = false;
|
|
40322
|
-
return new Promise((
|
|
40488
|
+
return new Promise((resolve17, reject) => {
|
|
40323
40489
|
this.zipfile.on("error", (err) => {
|
|
40324
40490
|
this.canceled = true;
|
|
40325
40491
|
reject(err);
|
|
@@ -40328,7 +40494,7 @@ var require_extract_zip = __commonJS({
|
|
|
40328
40494
|
this.zipfile.on("close", () => {
|
|
40329
40495
|
if (!this.canceled) {
|
|
40330
40496
|
debug("zip extraction complete");
|
|
40331
|
-
|
|
40497
|
+
resolve17();
|
|
40332
40498
|
}
|
|
40333
40499
|
});
|
|
40334
40500
|
this.zipfile.on("entry", async (entry) => {
|
|
@@ -47737,7 +47903,7 @@ var require_dist3 = __commonJS({
|
|
|
47737
47903
|
});
|
|
47738
47904
|
|
|
47739
47905
|
// ../core/src/agent-companies-parser.ts
|
|
47740
|
-
import { existsSync as
|
|
47906
|
+
import { existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync2, readFileSync as readFileSync3, rmSync, statSync as statSync4 } from "node:fs";
|
|
47741
47907
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
47742
47908
|
import { join as join19, resolve as resolve8 } from "node:path";
|
|
47743
47909
|
function slugifyAgentReference(value) {
|
|
@@ -47778,10 +47944,10 @@ function pushAlias(aliases, value) {
|
|
|
47778
47944
|
if (pathRef !== normalized && pathRef.length > 0) {
|
|
47779
47945
|
aliases.add(pathRef);
|
|
47780
47946
|
}
|
|
47781
|
-
const
|
|
47782
|
-
if (
|
|
47783
|
-
aliases.add(
|
|
47784
|
-
const basenameSlug = slugifyAgentReference(
|
|
47947
|
+
const basename12 = extractPathBasename(value);
|
|
47948
|
+
if (basename12) {
|
|
47949
|
+
aliases.add(basename12);
|
|
47950
|
+
const basenameSlug = slugifyAgentReference(basename12);
|
|
47785
47951
|
if (basenameSlug.length > 0) {
|
|
47786
47952
|
aliases.add(basenameSlug);
|
|
47787
47953
|
}
|
|
@@ -47965,14 +48131,14 @@ function parseManifestFile(filePath, parser) {
|
|
|
47965
48131
|
}
|
|
47966
48132
|
function parseManifestSubdirectories(rootDir, sectionDir, filename, parser) {
|
|
47967
48133
|
const sectionPath = join19(rootDir, sectionDir);
|
|
47968
|
-
if (!
|
|
48134
|
+
if (!existsSync17(sectionPath)) {
|
|
47969
48135
|
return [];
|
|
47970
48136
|
}
|
|
47971
48137
|
const manifests = [];
|
|
47972
48138
|
const entries = readdirSync2(sectionPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
|
|
47973
48139
|
for (const entry of entries) {
|
|
47974
48140
|
const manifestPath = join19(sectionPath, entry.name, filename);
|
|
47975
|
-
if (!
|
|
48141
|
+
if (!existsSync17(manifestPath)) {
|
|
47976
48142
|
continue;
|
|
47977
48143
|
}
|
|
47978
48144
|
manifests.push(parseManifestFile(manifestPath, parser));
|
|
@@ -48010,7 +48176,7 @@ function walkTeamIncludes(teams) {
|
|
|
48010
48176
|
}
|
|
48011
48177
|
function parseCompanyDirectory(dirPath) {
|
|
48012
48178
|
const resolvedPath = resolve8(dirPath);
|
|
48013
|
-
if (!
|
|
48179
|
+
if (!existsSync17(resolvedPath)) {
|
|
48014
48180
|
throw new AgentCompaniesParseError(`Company directory does not exist: ${resolvedPath}`);
|
|
48015
48181
|
}
|
|
48016
48182
|
if (!statSync4(resolvedPath).isDirectory()) {
|
|
@@ -48020,7 +48186,7 @@ function parseCompanyDirectory(dirPath) {
|
|
|
48020
48186
|
const teams = parseManifestSubdirectories(resolvedPath, "teams", "TEAM.md", parseTeamManifest);
|
|
48021
48187
|
walkTeamIncludes(teams);
|
|
48022
48188
|
return {
|
|
48023
|
-
company:
|
|
48189
|
+
company: existsSync17(companyPath) ? parseManifestFile(companyPath, parseCompanyManifest) : void 0,
|
|
48024
48190
|
agents: parseManifestSubdirectories(resolvedPath, "agents", "AGENTS.md", parseAgentManifest),
|
|
48025
48191
|
teams,
|
|
48026
48192
|
projects: parseManifestSubdirectories(
|
|
@@ -48034,7 +48200,7 @@ function parseCompanyDirectory(dirPath) {
|
|
|
48034
48200
|
};
|
|
48035
48201
|
}
|
|
48036
48202
|
function resolveExtractionRoot(tempDir) {
|
|
48037
|
-
if (
|
|
48203
|
+
if (existsSync17(join19(tempDir, "COMPANY.md"))) {
|
|
48038
48204
|
return tempDir;
|
|
48039
48205
|
}
|
|
48040
48206
|
const directories = readdirSync2(tempDir, { withFileTypes: true }).filter(
|
|
@@ -48042,7 +48208,7 @@ function resolveExtractionRoot(tempDir) {
|
|
|
48042
48208
|
);
|
|
48043
48209
|
for (const directory of directories) {
|
|
48044
48210
|
const candidate = join19(tempDir, directory.name);
|
|
48045
|
-
if (
|
|
48211
|
+
if (existsSync17(join19(candidate, "COMPANY.md"))) {
|
|
48046
48212
|
return candidate;
|
|
48047
48213
|
}
|
|
48048
48214
|
}
|
|
@@ -48880,6 +49046,7 @@ __export(src_exports, {
|
|
|
48880
49046
|
COLUMNS: () => COLUMNS,
|
|
48881
49047
|
COLUMN_DESCRIPTIONS: () => COLUMN_DESCRIPTIONS,
|
|
48882
49048
|
COLUMN_LABELS: () => COLUMN_LABELS,
|
|
49049
|
+
COMMIT_BODY_SYSTEM_PROMPT: () => COMMIT_BODY_SYSTEM_PROMPT,
|
|
48883
49050
|
COMPACT_MEMORY_SYSTEM_PROMPT: () => COMPACT_MEMORY_SYSTEM_PROMPT,
|
|
48884
49051
|
CentralCore: () => CentralCore,
|
|
48885
49052
|
CentralDatabase: () => CentralDatabase,
|
|
@@ -48889,6 +49056,7 @@ __export(src_exports, {
|
|
|
48889
49056
|
DAEMON_TOKEN_PREFIX: () => DAEMON_TOKEN_PREFIX,
|
|
48890
49057
|
DEFAULT_AGENT_HEARTBEAT_INTERVAL_MS: () => DEFAULT_AGENT_HEARTBEAT_INTERVAL_MS,
|
|
48891
49058
|
DEFAULT_AUTO_SUMMARIZE_SCHEDULE: () => DEFAULT_AUTO_SUMMARIZE_SCHEDULE,
|
|
49059
|
+
DEFAULT_COMMIT_BODY_TIMEOUT_MS: () => DEFAULT_COMMIT_BODY_TIMEOUT_MS,
|
|
48892
49060
|
DEFAULT_EXECUTION_MODE: () => DEFAULT_EXECUTION_MODE,
|
|
48893
49061
|
DEFAULT_GLOBAL_SETTINGS: () => DEFAULT_GLOBAL_SETTINGS,
|
|
48894
49062
|
DEFAULT_INSIGHT_SCHEDULE: () => DEFAULT_INSIGHT_SCHEDULE,
|
|
@@ -48910,6 +49078,8 @@ __export(src_exports, {
|
|
|
48910
49078
|
INSIGHT_EXTRACTION_SCHEDULE_NAME: () => INSIGHT_EXTRACTION_SCHEDULE_NAME,
|
|
48911
49079
|
INTERVIEW_STATES: () => INTERVIEW_STATES,
|
|
48912
49080
|
InsightStore: () => InsightStore,
|
|
49081
|
+
MAX_COMMIT_BODY_INPUT_LENGTH: () => MAX_COMMIT_BODY_INPUT_LENGTH,
|
|
49082
|
+
MAX_COMMIT_BODY_LENGTH: () => MAX_COMMIT_BODY_LENGTH,
|
|
48913
49083
|
MAX_DESCRIPTION_LENGTH: () => MAX_DESCRIPTION_LENGTH,
|
|
48914
49084
|
MAX_REQUESTS_PER_HOUR: () => MAX_REQUESTS_PER_HOUR,
|
|
48915
49085
|
MAX_ROUTINE_RUN_HISTORY: () => MAX_ROUTINE_RUN_HISTORY,
|
|
@@ -49069,6 +49239,7 @@ __export(src_exports, {
|
|
|
49069
49239
|
isValidPermission: () => isValidPermission,
|
|
49070
49240
|
isValidPromptKey: () => isValidPromptKey,
|
|
49071
49241
|
isValidPromptOverrideMap: () => isValidPromptOverrideMap,
|
|
49242
|
+
isValidSqliteDatabaseFile: () => isValidSqliteDatabaseFile,
|
|
49072
49243
|
isWebhookTrigger: () => isWebhookTrigger,
|
|
49073
49244
|
listAgentMemoryFiles: () => listAgentMemoryFiles,
|
|
49074
49245
|
listMemoryBackendTypes: () => listMemoryBackendTypes,
|
|
@@ -49153,6 +49324,7 @@ __export(src_exports, {
|
|
|
49153
49324
|
shouldTriggerExtraction: () => shouldTriggerExtraction,
|
|
49154
49325
|
slugify: () => slugify,
|
|
49155
49326
|
sortTasksByPriorityThenAgeAndId: () => sortTasksByPriorityThenAgeAndId,
|
|
49327
|
+
summarizeCommitBody: () => summarizeCommitBody,
|
|
49156
49328
|
summarizeTitle: () => summarizeTitle,
|
|
49157
49329
|
syncAutoSummarizeAutomation: () => syncAutoSummarizeAutomation,
|
|
49158
49330
|
syncBackupAutomation: () => syncBackupAutomation,
|
|
@@ -49195,6 +49367,7 @@ var init_src = __esm({
|
|
|
49195
49367
|
init_archive_db();
|
|
49196
49368
|
init_db_migrate();
|
|
49197
49369
|
init_global_settings();
|
|
49370
|
+
init_sqlite_validation();
|
|
49198
49371
|
init_daemon_token();
|
|
49199
49372
|
init_pi_extensions();
|
|
49200
49373
|
init_board();
|
|
@@ -49487,7 +49660,7 @@ var init_agent_logger = __esm({
|
|
|
49487
49660
|
|
|
49488
49661
|
// ../engine/src/agent-tools.ts
|
|
49489
49662
|
import { appendFile as appendFile2, mkdir as mkdir11, readFile as readFile11, readdir as readdir7, stat as stat4, writeFile as writeFile10 } from "node:fs/promises";
|
|
49490
|
-
import { existsSync as
|
|
49663
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
49491
49664
|
import { createHash as createHash4 } from "node:crypto";
|
|
49492
49665
|
import { join as join21 } from "node:path";
|
|
49493
49666
|
import { Type } from "@mariozechner/pi-ai";
|
|
@@ -49546,7 +49719,7 @@ async function syncAgentMemoryFile(rootDir, agentMemory) {
|
|
|
49546
49719
|
const dir = agentMemoryDirectory(rootDir, agentMemory.agentId);
|
|
49547
49720
|
await mkdir11(dir, { recursive: true });
|
|
49548
49721
|
const longTermPath = agentMemoryFilePath(rootDir, agentMemory.agentId);
|
|
49549
|
-
if (!
|
|
49722
|
+
if (!existsSync18(longTermPath)) {
|
|
49550
49723
|
const title = agentMemory.agentName?.trim() ? `# Agent Memory: ${agentMemory.agentName.trim()}` : "# Agent Memory";
|
|
49551
49724
|
const fileContent = `${title}
|
|
49552
49725
|
|
|
@@ -49557,11 +49730,11 @@ ${content || ""}
|
|
|
49557
49730
|
await writeFile10(longTermPath, fileContent, "utf-8");
|
|
49558
49731
|
}
|
|
49559
49732
|
const dreamsPath = agentDreamsFilePath(rootDir, agentMemory.agentId);
|
|
49560
|
-
if (!
|
|
49733
|
+
if (!existsSync18(dreamsPath)) {
|
|
49561
49734
|
await writeFile10(dreamsPath, "# Agent Memory Dreams\n\n<!-- Synthesized patterns from this agent's daily notes. -->\n", "utf-8");
|
|
49562
49735
|
}
|
|
49563
49736
|
const dailyPath = agentDailyFilePath(rootDir, agentMemory.agentId);
|
|
49564
|
-
if (!
|
|
49737
|
+
if (!existsSync18(dailyPath)) {
|
|
49565
49738
|
await writeFile10(dailyPath, `# Agent Daily Memory ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
|
|
49566
49739
|
|
|
49567
49740
|
<!-- Running observations for this agent. -->
|
|
@@ -50377,12 +50550,12 @@ var init_concurrency = __esm({
|
|
|
50377
50550
|
this._active++;
|
|
50378
50551
|
return Promise.resolve();
|
|
50379
50552
|
}
|
|
50380
|
-
return new Promise((
|
|
50553
|
+
return new Promise((resolve17) => {
|
|
50381
50554
|
this._waiters.push({
|
|
50382
50555
|
priority,
|
|
50383
50556
|
resolve: () => {
|
|
50384
50557
|
this._active++;
|
|
50385
|
-
|
|
50558
|
+
resolve17();
|
|
50386
50559
|
}
|
|
50387
50560
|
});
|
|
50388
50561
|
});
|
|
@@ -50446,10 +50619,10 @@ var init_concurrency = __esm({
|
|
|
50446
50619
|
});
|
|
50447
50620
|
|
|
50448
50621
|
// ../engine/src/skill-resolver.ts
|
|
50449
|
-
import { existsSync as
|
|
50622
|
+
import { existsSync as existsSync19, readFileSync as readFileSync4 } from "node:fs";
|
|
50450
50623
|
import { join as join22 } from "node:path";
|
|
50451
50624
|
function readJsonObject(path2) {
|
|
50452
|
-
if (!
|
|
50625
|
+
if (!existsSync19(path2)) {
|
|
50453
50626
|
return {};
|
|
50454
50627
|
}
|
|
50455
50628
|
try {
|
|
@@ -50461,7 +50634,7 @@ function readJsonObject(path2) {
|
|
|
50461
50634
|
}
|
|
50462
50635
|
function readProjectSettings(projectRootDir) {
|
|
50463
50636
|
const fusionSettings = join22(projectRootDir, ".fusion", "settings.json");
|
|
50464
|
-
if (
|
|
50637
|
+
if (existsSync19(fusionSettings)) {
|
|
50465
50638
|
const parsed = readJsonObject(fusionSettings);
|
|
50466
50639
|
return {
|
|
50467
50640
|
skills: Array.isArray(parsed.skills) ? parsed.skills : void 0,
|
|
@@ -50682,43 +50855,43 @@ var init_context_limit_detector = __esm({
|
|
|
50682
50855
|
});
|
|
50683
50856
|
|
|
50684
50857
|
// ../engine/src/auth-storage.ts
|
|
50685
|
-
import { existsSync as
|
|
50858
|
+
import { existsSync as existsSync20, readFileSync as readFileSync5 } from "node:fs";
|
|
50686
50859
|
import { homedir as homedir4 } from "node:os";
|
|
50687
50860
|
import { join as join23 } from "node:path";
|
|
50688
50861
|
import { AuthStorage } from "@mariozechner/pi-coding-agent";
|
|
50689
50862
|
import { getOAuthProvider } from "@mariozechner/pi-ai/oauth";
|
|
50690
|
-
function
|
|
50863
|
+
function getHomeDir4() {
|
|
50691
50864
|
return process.env.HOME || process.env.USERPROFILE || homedir4();
|
|
50692
50865
|
}
|
|
50693
|
-
function getFusionAuthPath(home =
|
|
50866
|
+
function getFusionAuthPath(home = getHomeDir4()) {
|
|
50694
50867
|
return join23(home, ".fusion", "agent", "auth.json");
|
|
50695
50868
|
}
|
|
50696
|
-
function getFusionModelsPath(home =
|
|
50869
|
+
function getFusionModelsPath(home = getHomeDir4()) {
|
|
50697
50870
|
return join23(home, ".fusion", "agent", "models.json");
|
|
50698
50871
|
}
|
|
50699
|
-
function getLegacyAuthPaths(home =
|
|
50872
|
+
function getLegacyAuthPaths(home = getHomeDir4()) {
|
|
50700
50873
|
return [
|
|
50701
50874
|
join23(home, ".pi", "agent", "auth.json"),
|
|
50702
50875
|
join23(home, ".pi", "auth.json")
|
|
50703
50876
|
];
|
|
50704
50877
|
}
|
|
50705
|
-
function getLegacyModelsPaths(home =
|
|
50878
|
+
function getLegacyModelsPaths(home = getHomeDir4()) {
|
|
50706
50879
|
return [
|
|
50707
50880
|
join23(home, ".pi", "agent", "models.json"),
|
|
50708
50881
|
join23(home, ".pi", "models.json")
|
|
50709
50882
|
];
|
|
50710
50883
|
}
|
|
50711
|
-
function getModelRegistryModelsPath(home =
|
|
50884
|
+
function getModelRegistryModelsPath(home = getHomeDir4()) {
|
|
50712
50885
|
const fusionModelsPath = getFusionModelsPath(home);
|
|
50713
|
-
if (
|
|
50886
|
+
if (existsSync20(fusionModelsPath)) {
|
|
50714
50887
|
return fusionModelsPath;
|
|
50715
50888
|
}
|
|
50716
|
-
return getLegacyModelsPaths(home).find((modelsPath) =>
|
|
50889
|
+
return getLegacyModelsPaths(home).find((modelsPath) => existsSync20(modelsPath)) ?? fusionModelsPath;
|
|
50717
50890
|
}
|
|
50718
50891
|
function readLegacyCredentials(authPaths = getLegacyAuthPaths()) {
|
|
50719
50892
|
const credentials = {};
|
|
50720
50893
|
for (const authPath of authPaths) {
|
|
50721
|
-
if (!
|
|
50894
|
+
if (!existsSync20(authPath)) {
|
|
50722
50895
|
continue;
|
|
50723
50896
|
}
|
|
50724
50897
|
try {
|
|
@@ -50794,11 +50967,11 @@ var init_auth_storage = __esm({
|
|
|
50794
50967
|
});
|
|
50795
50968
|
|
|
50796
50969
|
// ../engine/src/pi.ts
|
|
50797
|
-
import { existsSync as
|
|
50970
|
+
import { existsSync as existsSync21, readFileSync as readFileSync6 } from "node:fs";
|
|
50798
50971
|
import { exec } from "node:child_process";
|
|
50799
50972
|
import { promisify as promisify2 } from "node:util";
|
|
50800
50973
|
import { createRequire as createRequire2 } from "node:module";
|
|
50801
|
-
import { basename as basename7, dirname as dirname7, join as join24, relative as relative3, isAbsolute as
|
|
50974
|
+
import { basename as basename7, dirname as dirname7, join as join24, relative as relative3, isAbsolute as isAbsolute7, resolve as resolve10 } from "node:path";
|
|
50802
50975
|
import {
|
|
50803
50976
|
createAgentSession,
|
|
50804
50977
|
createBashTool,
|
|
@@ -51052,7 +51225,7 @@ function isRetryableModelSelectionError(message) {
|
|
|
51052
51225
|
return normalized.includes("rate limit") || normalized.includes("too many requests") || normalized.includes("429") || normalized.includes("401") || normalized.includes("403") || normalized.includes("unauthorized") || normalized.includes("forbidden") || normalized.includes("authentication") || normalized.includes("invalid api key") || normalized.includes("invalid key") || normalized.includes("api key") || normalized.includes("overloaded") || normalized.includes("quota") || normalized.includes("capacity") || normalized.includes("temporarily unavailable") || normalized.includes("invalid temperature");
|
|
51053
51226
|
}
|
|
51054
51227
|
function readJsonObject2(path2) {
|
|
51055
|
-
if (!
|
|
51228
|
+
if (!existsSync21(path2)) {
|
|
51056
51229
|
return {};
|
|
51057
51230
|
}
|
|
51058
51231
|
try {
|
|
@@ -51216,13 +51389,13 @@ function getPackageManagerAgentDir() {
|
|
|
51216
51389
|
const legacyAgentDir = getLegacyPiAgentDir();
|
|
51217
51390
|
const fusionSettings = readJsonObject2(join24(fusionAgentDir, "settings.json"));
|
|
51218
51391
|
const legacySettings = readJsonObject2(join24(legacyAgentDir, "settings.json"));
|
|
51219
|
-
if (hasPackageManagerSettings(fusionSettings) || !
|
|
51392
|
+
if (hasPackageManagerSettings(fusionSettings) || !existsSync21(legacyAgentDir)) {
|
|
51220
51393
|
return fusionAgentDir;
|
|
51221
51394
|
}
|
|
51222
51395
|
if (hasPackageManagerSettings(legacySettings)) {
|
|
51223
51396
|
return legacyAgentDir;
|
|
51224
51397
|
}
|
|
51225
|
-
return
|
|
51398
|
+
return existsSync21(fusionAgentDir) ? fusionAgentDir : legacyAgentDir;
|
|
51226
51399
|
}
|
|
51227
51400
|
function resolveVendoredClaudeCliEntry() {
|
|
51228
51401
|
try {
|
|
@@ -51234,7 +51407,7 @@ function resolveVendoredClaudeCliEntry() {
|
|
|
51234
51407
|
const entry = extensions[0];
|
|
51235
51408
|
if (typeof entry !== "string" || entry.length === 0) return null;
|
|
51236
51409
|
const path2 = resolve10(dirname7(pkgJsonPath), entry);
|
|
51237
|
-
return
|
|
51410
|
+
return existsSync21(path2) ? path2 : null;
|
|
51238
51411
|
} catch {
|
|
51239
51412
|
return null;
|
|
51240
51413
|
}
|
|
@@ -51280,7 +51453,7 @@ async function registerExtensionProviders(cwd, modelRegistry) {
|
|
|
51280
51453
|
}
|
|
51281
51454
|
}
|
|
51282
51455
|
function getProjectRootFromWorktree(cwd) {
|
|
51283
|
-
const match = cwd.match(/^(.+?)
|
|
51456
|
+
const match = cwd.match(/^(.+?)[\\/]\.worktrees[\\/][^\\/]+(?:[\\/]|$)/);
|
|
51284
51457
|
if (match) {
|
|
51285
51458
|
return match[1];
|
|
51286
51459
|
}
|
|
@@ -51312,10 +51485,10 @@ async function isCompleteGitWorktree(worktreePath) {
|
|
|
51312
51485
|
}
|
|
51313
51486
|
}
|
|
51314
51487
|
async function assertValidWorktreeSession(cwd, projectRoot) {
|
|
51315
|
-
if (!
|
|
51488
|
+
if (!existsSync21(cwd)) {
|
|
51316
51489
|
throw new Error(`Refusing to start coding agent in missing worktree: ${cwd}`);
|
|
51317
51490
|
}
|
|
51318
|
-
if (!
|
|
51491
|
+
if (!existsSync21(join24(cwd, ".git")) || !await isCompleteGitWorktree(cwd)) {
|
|
51319
51492
|
throw new Error(`Refusing to start coding agent in incomplete worktree: ${cwd}`);
|
|
51320
51493
|
}
|
|
51321
51494
|
if (!await isRegisteredGitWorktree(projectRoot, cwd)) {
|
|
@@ -51325,9 +51498,9 @@ async function assertValidWorktreeSession(cwd, projectRoot) {
|
|
|
51325
51498
|
function isWorktreeAllowedPath(worktreePath, projectRoot, requestedPath, toolName) {
|
|
51326
51499
|
const worktreeResolved = resolve10(worktreePath);
|
|
51327
51500
|
const projectRootResolved = resolve10(projectRoot);
|
|
51328
|
-
const requestedResolved =
|
|
51501
|
+
const requestedResolved = isAbsolute7(requestedPath) ? resolve10(requestedPath) : resolve10(worktreeResolved, requestedPath);
|
|
51329
51502
|
const relToWorktree = relative3(worktreeResolved, requestedResolved);
|
|
51330
|
-
if (!relToWorktree.startsWith("..") && !
|
|
51503
|
+
if (!relToWorktree.startsWith("..") && !isAbsolute7(relToWorktree)) {
|
|
51331
51504
|
return true;
|
|
51332
51505
|
}
|
|
51333
51506
|
const relToProjectRoot = relative3(projectRootResolved, requestedResolved).replace(/\\/g, "/");
|
|
@@ -52057,7 +52230,7 @@ var init_usage_limit_detector = __esm({
|
|
|
52057
52230
|
|
|
52058
52231
|
// ../engine/src/agent-instructions.ts
|
|
52059
52232
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
52060
|
-
import { isAbsolute as
|
|
52233
|
+
import { isAbsolute as isAbsolute8, resolve as resolve11, relative as relative4, normalize as normalize3, sep as sep4 } from "node:path";
|
|
52061
52234
|
function trimAndClamp(value, maxLength, label, agentId) {
|
|
52062
52235
|
const trimmed = value.trim();
|
|
52063
52236
|
if (!trimmed) {
|
|
@@ -52087,7 +52260,7 @@ function resolveValidatedInstructionsPath(rawPath, rootDir, agentId) {
|
|
|
52087
52260
|
log4.warn(`instructionsPath must end in .md for agent ${agentId}: ${trimmed}`);
|
|
52088
52261
|
return null;
|
|
52089
52262
|
}
|
|
52090
|
-
if (
|
|
52263
|
+
if (isAbsolute8(trimmed)) {
|
|
52091
52264
|
log4.warn(`instructionsPath must be project-relative for agent ${agentId}: ${trimmed}`);
|
|
52092
52265
|
return null;
|
|
52093
52266
|
}
|
|
@@ -52098,7 +52271,7 @@ function resolveValidatedInstructionsPath(rawPath, rootDir, agentId) {
|
|
|
52098
52271
|
}
|
|
52099
52272
|
const resolvedPath = resolve11(rootDir, normalized);
|
|
52100
52273
|
const rel = relative4(rootDir, resolvedPath);
|
|
52101
|
-
if (!rel || rel.startsWith(`..${sep4}`) || rel === ".." ||
|
|
52274
|
+
if (!rel || rel.startsWith(`..${sep4}`) || rel === ".." || isAbsolute8(rel)) {
|
|
52102
52275
|
log4.warn(`instructionsPath escapes project root for agent ${agentId}: ${trimmed}`);
|
|
52103
52276
|
return null;
|
|
52104
52277
|
}
|
|
@@ -52803,20 +52976,20 @@ async function withRateLimitRetry(fn, options = {}) {
|
|
|
52803
52976
|
throw lastError ?? new Error("withRateLimitRetry: unexpected state");
|
|
52804
52977
|
}
|
|
52805
52978
|
function sleep(ms, signal) {
|
|
52806
|
-
return new Promise((
|
|
52979
|
+
return new Promise((resolve17, reject) => {
|
|
52807
52980
|
if (signal?.aborted) {
|
|
52808
52981
|
reject(signal.reason ?? new Error("Aborted"));
|
|
52809
52982
|
return;
|
|
52810
52983
|
}
|
|
52811
|
-
const timer = setTimeout(
|
|
52984
|
+
const timer = setTimeout(resolve17, ms);
|
|
52812
52985
|
if (signal) {
|
|
52813
52986
|
const onAbort = () => {
|
|
52814
52987
|
clearTimeout(timer);
|
|
52815
52988
|
reject(signal.reason ?? new Error("Aborted"));
|
|
52816
52989
|
};
|
|
52817
52990
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
52818
|
-
const origResolve =
|
|
52819
|
-
|
|
52991
|
+
const origResolve = resolve17;
|
|
52992
|
+
resolve17 = () => {
|
|
52820
52993
|
signal.removeEventListener("abort", onAbort);
|
|
52821
52994
|
origResolve();
|
|
52822
52995
|
};
|
|
@@ -54779,11 +54952,11 @@ var init_run_audit = __esm({
|
|
|
54779
54952
|
// ../engine/src/merger.ts
|
|
54780
54953
|
import { execSync, exec as exec2, spawn as spawn2 } from "node:child_process";
|
|
54781
54954
|
import { promisify as promisify3 } from "node:util";
|
|
54782
|
-
import { existsSync as
|
|
54955
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
54783
54956
|
import { join as join26 } from "node:path";
|
|
54784
54957
|
import { Type as Type3 } from "typebox";
|
|
54785
54958
|
async function execWithProcessGroup(command, options) {
|
|
54786
|
-
return new Promise((
|
|
54959
|
+
return new Promise((resolve17, reject) => {
|
|
54787
54960
|
if (options.signal?.aborted) {
|
|
54788
54961
|
reject(Object.assign(
|
|
54789
54962
|
new Error(`Command aborted before start: ${command}`),
|
|
@@ -54876,7 +55049,7 @@ async function execWithProcessGroup(command, options) {
|
|
|
54876
55049
|
return;
|
|
54877
55050
|
}
|
|
54878
55051
|
if (code === 0) {
|
|
54879
|
-
|
|
55052
|
+
resolve17({ stdout, stderr, bufferOverflow: stdoutOverflow || stderrOverflow });
|
|
54880
55053
|
return;
|
|
54881
55054
|
}
|
|
54882
55055
|
reject(Object.assign(
|
|
@@ -55041,7 +55214,7 @@ async function getStagedFiles(cwd) {
|
|
|
55041
55214
|
}
|
|
55042
55215
|
}
|
|
55043
55216
|
function hasInstallState(rootDir) {
|
|
55044
|
-
return
|
|
55217
|
+
return existsSync22(join26(rootDir, "node_modules")) || existsSync22(join26(rootDir, ".pnp.cjs"));
|
|
55045
55218
|
}
|
|
55046
55219
|
function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
|
|
55047
55220
|
if (!installStatePresent) return true;
|
|
@@ -55050,10 +55223,10 @@ function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
|
|
|
55050
55223
|
);
|
|
55051
55224
|
}
|
|
55052
55225
|
function getDependencySyncCommand(rootDir) {
|
|
55053
|
-
if (
|
|
55054
|
-
if (
|
|
55055
|
-
if (
|
|
55056
|
-
if (
|
|
55226
|
+
if (existsSync22(join26(rootDir, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
|
|
55227
|
+
if (existsSync22(join26(rootDir, "package-lock.json"))) return "npm install";
|
|
55228
|
+
if (existsSync22(join26(rootDir, "yarn.lock"))) return "yarn install --frozen-lockfile";
|
|
55229
|
+
if (existsSync22(join26(rootDir, "bun.lock")) || existsSync22(join26(rootDir, "bun.lockb"))) {
|
|
55057
55230
|
return "bun install --frozen-lockfile";
|
|
55058
55231
|
}
|
|
55059
55232
|
return null;
|
|
@@ -55086,8 +55259,8 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
55086
55259
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
55087
55260
|
};
|
|
55088
55261
|
}
|
|
55089
|
-
if (
|
|
55090
|
-
if (
|
|
55262
|
+
if (existsSync22(join26(rootDir, "pnpm-lock.yaml"))) {
|
|
55263
|
+
if (existsSync22(join26(rootDir, "pnpm-workspace.yaml"))) {
|
|
55091
55264
|
mergerLog.warn(
|
|
55092
55265
|
`Inferred test command "pnpm test" in a pnpm workspace (${rootDir}). This runs the full monorepo suite on every merge. Consider setting an explicit scoped testCommand in project settings, e.g. \`pnpm -r --filter "...[main]" test\`.`
|
|
55093
55266
|
);
|
|
@@ -55098,21 +55271,21 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
55098
55271
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
55099
55272
|
};
|
|
55100
55273
|
}
|
|
55101
|
-
if (
|
|
55274
|
+
if (existsSync22(join26(rootDir, "yarn.lock"))) {
|
|
55102
55275
|
return {
|
|
55103
55276
|
command: "yarn test",
|
|
55104
55277
|
testSource: "inferred",
|
|
55105
55278
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
55106
55279
|
};
|
|
55107
55280
|
}
|
|
55108
|
-
if (
|
|
55281
|
+
if (existsSync22(join26(rootDir, "bun.lock")) || existsSync22(join26(rootDir, "bun.lockb"))) {
|
|
55109
55282
|
return {
|
|
55110
55283
|
command: "bun test",
|
|
55111
55284
|
testSource: "inferred",
|
|
55112
55285
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
55113
55286
|
};
|
|
55114
55287
|
}
|
|
55115
|
-
if (
|
|
55288
|
+
if (existsSync22(join26(rootDir, "package-lock.json"))) {
|
|
55116
55289
|
return {
|
|
55117
55290
|
command: "npm test",
|
|
55118
55291
|
testSource: "inferred",
|
|
@@ -55901,6 +56074,68 @@ async function findWorktreeUser(store, worktreePath, excludeTaskId) {
|
|
|
55901
56074
|
function quoteArg(value) {
|
|
55902
56075
|
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
55903
56076
|
}
|
|
56077
|
+
async function resolveSafeCommitBody(opts) {
|
|
56078
|
+
const cleanLog = opts.commitLog.trim();
|
|
56079
|
+
if (cleanLog.length > 0) return cleanLog;
|
|
56080
|
+
const cleanStat = opts.diffStat.trim();
|
|
56081
|
+
if (cleanStat.length > 0) {
|
|
56082
|
+
const useTitleSummarizer = !!opts.settings.titleSummarizerProvider && !!opts.settings.titleSummarizerModelId;
|
|
56083
|
+
const provider = useTitleSummarizer ? opts.settings.titleSummarizerProvider : opts.settings.defaultProviderOverride && opts.settings.defaultModelIdOverride ? opts.settings.defaultProviderOverride : opts.settings.defaultProvider;
|
|
56084
|
+
const modelId = useTitleSummarizer ? opts.settings.titleSummarizerModelId : opts.settings.defaultProviderOverride && opts.settings.defaultModelIdOverride ? opts.settings.defaultModelIdOverride : opts.settings.defaultModelId;
|
|
56085
|
+
const ai = await summarizeCommitBody(cleanStat, opts.rootDir, provider, modelId, {
|
|
56086
|
+
branch: opts.branch,
|
|
56087
|
+
taskId: opts.taskId,
|
|
56088
|
+
signal: opts.signal,
|
|
56089
|
+
timeoutMs: opts.aiTimeoutMs
|
|
56090
|
+
}).catch(() => null);
|
|
56091
|
+
if (ai && ai.trim().length > 0) return ai.trim();
|
|
56092
|
+
return `Files changed:
|
|
56093
|
+
|
|
56094
|
+
${cleanStat}`;
|
|
56095
|
+
}
|
|
56096
|
+
return `- merge ${opts.branch}`;
|
|
56097
|
+
}
|
|
56098
|
+
async function commitPatchId(rootDir, sha) {
|
|
56099
|
+
try {
|
|
56100
|
+
const { stdout } = await execAsync2(
|
|
56101
|
+
`git diff-tree -p ${quoteArg(sha)} | git patch-id --stable`,
|
|
56102
|
+
{ cwd: rootDir, encoding: "utf-8" }
|
|
56103
|
+
);
|
|
56104
|
+
const line = stdout.trim();
|
|
56105
|
+
if (!line) return void 0;
|
|
56106
|
+
const [pid] = line.split(/\s+/, 1);
|
|
56107
|
+
return pid || void 0;
|
|
56108
|
+
} catch {
|
|
56109
|
+
return void 0;
|
|
56110
|
+
}
|
|
56111
|
+
}
|
|
56112
|
+
async function collectPatchIds(rootDir, target, windowSize) {
|
|
56113
|
+
const ids = /* @__PURE__ */ new Set();
|
|
56114
|
+
try {
|
|
56115
|
+
const { stdout } = await execAsync2(
|
|
56116
|
+
`git log -n ${Math.max(1, windowSize)} --format=%H ${quoteArg(target)}`,
|
|
56117
|
+
{ cwd: rootDir, encoding: "utf-8" }
|
|
56118
|
+
);
|
|
56119
|
+
const shas = stdout.trim().split("\n").filter(Boolean);
|
|
56120
|
+
for (const sha of shas) {
|
|
56121
|
+
const pid = await commitPatchId(rootDir, sha);
|
|
56122
|
+
if (pid) ids.add(pid);
|
|
56123
|
+
}
|
|
56124
|
+
} catch {
|
|
56125
|
+
}
|
|
56126
|
+
return ids;
|
|
56127
|
+
}
|
|
56128
|
+
async function listBranchCommits(rootDir, target, branch) {
|
|
56129
|
+
try {
|
|
56130
|
+
const { stdout } = await execAsync2(
|
|
56131
|
+
`git log --reverse --format=%H ${quoteArg(target)}..${quoteArg(branch)}`,
|
|
56132
|
+
{ cwd: rootDir, encoding: "utf-8" }
|
|
56133
|
+
);
|
|
56134
|
+
return stdout.trim().split("\n").filter(Boolean);
|
|
56135
|
+
} catch {
|
|
56136
|
+
return [];
|
|
56137
|
+
}
|
|
56138
|
+
}
|
|
55904
56139
|
function getCommandErrorMessage(error) {
|
|
55905
56140
|
if (error instanceof Error) {
|
|
55906
56141
|
const stderr = error.stderr;
|
|
@@ -56417,11 +56652,182 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
56417
56652
|
settings.worktreeRebaseBeforeMerge === false ? "remote rebase disabled" : ""
|
|
56418
56653
|
);
|
|
56419
56654
|
}
|
|
56655
|
+
let preMergeRebaseFallthrough;
|
|
56656
|
+
if (preferMainRebaseFailureMessage && worktreePath) {
|
|
56657
|
+
let rebaseTarget = "";
|
|
56658
|
+
try {
|
|
56659
|
+
const { stdout } = await execAsync2("git rev-parse HEAD", {
|
|
56660
|
+
cwd: rootDir,
|
|
56661
|
+
encoding: "utf-8"
|
|
56662
|
+
});
|
|
56663
|
+
rebaseTarget = stdout.trim();
|
|
56664
|
+
} catch {
|
|
56665
|
+
rebaseTarget = "";
|
|
56666
|
+
}
|
|
56667
|
+
if (rebaseTarget && task.baseBranch && task.baseBranch !== "main") {
|
|
56668
|
+
let depTip;
|
|
56669
|
+
try {
|
|
56670
|
+
const { stdout } = await execAsync2(
|
|
56671
|
+
`git rev-parse --verify "${task.baseBranch}^{commit}"`,
|
|
56672
|
+
{ cwd: rootDir, encoding: "utf-8" }
|
|
56673
|
+
);
|
|
56674
|
+
depTip = stdout.trim() || void 0;
|
|
56675
|
+
} catch {
|
|
56676
|
+
depTip = void 0;
|
|
56677
|
+
}
|
|
56678
|
+
if (!depTip && task.baseCommitSha) {
|
|
56679
|
+
try {
|
|
56680
|
+
const { stdout } = await execAsync2(
|
|
56681
|
+
`git rev-parse --verify "${task.baseCommitSha}^{commit}"`,
|
|
56682
|
+
{ cwd: rootDir, encoding: "utf-8" }
|
|
56683
|
+
);
|
|
56684
|
+
depTip = stdout.trim() || void 0;
|
|
56685
|
+
} catch {
|
|
56686
|
+
depTip = void 0;
|
|
56687
|
+
}
|
|
56688
|
+
}
|
|
56689
|
+
if (depTip && depTip !== rebaseTarget) {
|
|
56690
|
+
try {
|
|
56691
|
+
throwIfAborted(options.signal, taskId);
|
|
56692
|
+
await execAsync2("git rebase --abort", { cwd: worktreePath }).catch(
|
|
56693
|
+
() => void 0
|
|
56694
|
+
);
|
|
56695
|
+
await execAsync2(
|
|
56696
|
+
`git rebase --onto "${rebaseTarget}" "${depTip}" "${branch}"`,
|
|
56697
|
+
{ cwd: worktreePath }
|
|
56698
|
+
);
|
|
56699
|
+
preferMainRebaseFailureMessage = void 0;
|
|
56700
|
+
rebaseHappened = true;
|
|
56701
|
+
mergerLog.log(
|
|
56702
|
+
`${taskId}: Layer 1 recovery \u2014 rebased ${branch} --onto ${rebaseTarget.slice(0, 8)} dropping commits up to dep tip ${depTip.slice(0, 8)} (baseBranch=${task.baseBranch})`
|
|
56703
|
+
);
|
|
56704
|
+
await store.logEntry(
|
|
56705
|
+
taskId,
|
|
56706
|
+
`Pre-merge recovery (Layer 1): dropped dependency commits from ${task.baseBranch} via rebase --onto ${rebaseTarget.slice(0, 8)} ${depTip.slice(0, 8)} ${branch}; the merge will proceed against the cleaned branch`
|
|
56707
|
+
);
|
|
56708
|
+
} catch (layer1Err) {
|
|
56709
|
+
rethrowIfMergeAborted(layer1Err);
|
|
56710
|
+
mergerLog.warn(
|
|
56711
|
+
`${taskId}: Layer 1 (dep-drop) recovery failed: ${layer1Err instanceof Error ? layer1Err.message : String(layer1Err)}`
|
|
56712
|
+
);
|
|
56713
|
+
await execAsync2("git rebase --abort", { cwd: worktreePath }).catch(
|
|
56714
|
+
() => void 0
|
|
56715
|
+
);
|
|
56716
|
+
}
|
|
56717
|
+
}
|
|
56718
|
+
}
|
|
56719
|
+
if (preferMainRebaseFailureMessage && rebaseTarget && worktreePath) {
|
|
56720
|
+
try {
|
|
56721
|
+
throwIfAborted(options.signal, taskId);
|
|
56722
|
+
const mainPatchIds = await collectPatchIds(rootDir, rebaseTarget, 500);
|
|
56723
|
+
const branchCommits = await listBranchCommits(rootDir, rebaseTarget, branch);
|
|
56724
|
+
if (branchCommits.length === 0) {
|
|
56725
|
+
rebaseHappened = true;
|
|
56726
|
+
preferMainRebaseFailureMessage = void 0;
|
|
56727
|
+
} else {
|
|
56728
|
+
const surviving = [];
|
|
56729
|
+
let dropped = 0;
|
|
56730
|
+
for (const sha of branchCommits) {
|
|
56731
|
+
const pid = await commitPatchId(rootDir, sha);
|
|
56732
|
+
if (pid && mainPatchIds.has(pid)) {
|
|
56733
|
+
dropped += 1;
|
|
56734
|
+
} else {
|
|
56735
|
+
surviving.push(sha);
|
|
56736
|
+
}
|
|
56737
|
+
}
|
|
56738
|
+
if (dropped > 0 && surviving.length === branchCommits.length) {
|
|
56739
|
+
mergerLog.warn(`${taskId}: Layer 2 internal accounting mismatch \u2014 skipping`);
|
|
56740
|
+
} else if (dropped > 0) {
|
|
56741
|
+
let originalBranchSha = "";
|
|
56742
|
+
try {
|
|
56743
|
+
const { stdout } = await execAsync2(
|
|
56744
|
+
`git rev-parse --verify "${branch}^{commit}"`,
|
|
56745
|
+
{ cwd: worktreePath, encoding: "utf-8" }
|
|
56746
|
+
);
|
|
56747
|
+
originalBranchSha = stdout.trim();
|
|
56748
|
+
} catch {
|
|
56749
|
+
originalBranchSha = "";
|
|
56750
|
+
}
|
|
56751
|
+
const restoreOriginalBranch = async () => {
|
|
56752
|
+
if (!originalBranchSha) return;
|
|
56753
|
+
await execAsync2(`git reset --hard "${originalBranchSha}"`, {
|
|
56754
|
+
cwd: worktreePath
|
|
56755
|
+
}).catch(() => void 0);
|
|
56756
|
+
await execAsync2(`git checkout -f "${branch}"`, { cwd: worktreePath }).catch(
|
|
56757
|
+
() => void 0
|
|
56758
|
+
);
|
|
56759
|
+
await execAsync2(`git reset --hard "${originalBranchSha}"`, {
|
|
56760
|
+
cwd: worktreePath
|
|
56761
|
+
}).catch(() => void 0);
|
|
56762
|
+
};
|
|
56763
|
+
try {
|
|
56764
|
+
await execAsync2("git rebase --abort", { cwd: worktreePath }).catch(
|
|
56765
|
+
() => void 0
|
|
56766
|
+
);
|
|
56767
|
+
await execAsync2(`git checkout "${branch}"`, { cwd: worktreePath });
|
|
56768
|
+
await execAsync2(`git reset --hard "${rebaseTarget}"`, {
|
|
56769
|
+
cwd: worktreePath
|
|
56770
|
+
});
|
|
56771
|
+
for (const sha of surviving) {
|
|
56772
|
+
throwIfAborted(options.signal, taskId);
|
|
56773
|
+
try {
|
|
56774
|
+
await execAsync2(`git cherry-pick --allow-empty "${sha}"`, {
|
|
56775
|
+
cwd: worktreePath
|
|
56776
|
+
});
|
|
56777
|
+
} catch (pickErr) {
|
|
56778
|
+
rethrowIfMergeAborted(pickErr);
|
|
56779
|
+
await execAsync2("git cherry-pick --abort", { cwd: worktreePath }).catch(
|
|
56780
|
+
() => void 0
|
|
56781
|
+
);
|
|
56782
|
+
await restoreOriginalBranch();
|
|
56783
|
+
throw pickErr;
|
|
56784
|
+
}
|
|
56785
|
+
}
|
|
56786
|
+
preferMainRebaseFailureMessage = void 0;
|
|
56787
|
+
rebaseHappened = true;
|
|
56788
|
+
mergerLog.log(
|
|
56789
|
+
`${taskId}: Layer 2 recovery \u2014 patch-id stripped ${dropped} duplicate commit(s); replayed ${surviving.length} survivor(s) onto ${rebaseTarget.slice(0, 8)}`
|
|
56790
|
+
);
|
|
56791
|
+
await store.logEntry(
|
|
56792
|
+
taskId,
|
|
56793
|
+
`Pre-merge recovery (Layer 2): patch-id matched ${dropped} branch commit(s) against the last 500 main commits and dropped them as duplicates; cherry-picked ${surviving.length} unique commit(s) onto ${rebaseTarget.slice(0, 8)}`
|
|
56794
|
+
);
|
|
56795
|
+
} catch (replayErr) {
|
|
56796
|
+
await restoreOriginalBranch();
|
|
56797
|
+
throw replayErr;
|
|
56798
|
+
}
|
|
56799
|
+
} else {
|
|
56800
|
+
mergerLog.log(
|
|
56801
|
+
`${taskId}: Layer 2 found no duplicate-content commits to drop (window=500)`
|
|
56802
|
+
);
|
|
56803
|
+
}
|
|
56804
|
+
}
|
|
56805
|
+
} catch (layer2Err) {
|
|
56806
|
+
rethrowIfMergeAborted(layer2Err);
|
|
56807
|
+
mergerLog.warn(
|
|
56808
|
+
`${taskId}: Layer 2 (patch-id strip) recovery failed: ${layer2Err instanceof Error ? layer2Err.message : String(layer2Err)}`
|
|
56809
|
+
);
|
|
56810
|
+
}
|
|
56811
|
+
}
|
|
56812
|
+
if (preferMainRebaseFailureMessage) {
|
|
56813
|
+
preMergeRebaseFallthrough = preferMainRebaseFailureMessage;
|
|
56814
|
+
preferMainRebaseFailureMessage = void 0;
|
|
56815
|
+
mergerLog.warn(
|
|
56816
|
+
`${taskId}: Layers 1 & 2 could not unblock the prefer-main rebase \u2014 falling through to AI arbitration (Layer 3). Deterministic verification will gate the result.`
|
|
56817
|
+
);
|
|
56818
|
+
await store.logEntry(
|
|
56819
|
+
taskId,
|
|
56820
|
+
`Pre-merge recovery (Layer 3): both surgical and patch-id recovery failed; AI arbiter takes over. SAFETY CONSTRAINT for the AI: do NOT re-introduce content that current main has deleted. If hunks are ambiguous, prefer main's version. Post-merge test/build verification will reject any resolution that breaks main's intent.`,
|
|
56821
|
+
"PreMergeRebaseFallthrough"
|
|
56822
|
+
);
|
|
56823
|
+
}
|
|
56824
|
+
}
|
|
56420
56825
|
if (preferMainRebaseFailureMessage) {
|
|
56421
56826
|
throw new Error(
|
|
56422
|
-
`${preferMainRebaseFailureMessage} for ${taskId}. Strategy "smart-prefer-main" requires a successful rebase to preserve main's deletions;
|
|
56827
|
+
`${preferMainRebaseFailureMessage} for ${taskId}. Strategy "smart-prefer-main" requires a successful rebase to preserve main's deletions; recovery layers 1\u20133 require a worktree path which is missing for this task. Resolve the rebase conflict manually, or switch mergeConflictStrategy to "smart-prefer-branch" / "ai-only".`
|
|
56423
56828
|
);
|
|
56424
56829
|
}
|
|
56830
|
+
void preMergeRebaseFallthrough;
|
|
56425
56831
|
if (mergeConflictStrategy === "smart-prefer-main" && !rebaseHappened) {
|
|
56426
56832
|
mergerLog.warn(
|
|
56427
56833
|
`${taskId}: smart-prefer-main ran without a successful pre-merge rebase (${worktreePath ? "no remote resolvable or rebase disabled" : "no worktreePath"}). Main's deletions may not be preserved if the branch re-introduces them.`
|
|
@@ -56525,7 +56931,8 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
56525
56931
|
testCommand: effectiveTestCommand,
|
|
56526
56932
|
buildCommand: effectiveBuildCommand,
|
|
56527
56933
|
testSource: effectiveTestSource,
|
|
56528
|
-
buildSource: effectiveBuildSource
|
|
56934
|
+
buildSource: effectiveBuildSource,
|
|
56935
|
+
preMergeRebaseFallthrough
|
|
56529
56936
|
}, aiTracker);
|
|
56530
56937
|
if (success) {
|
|
56531
56938
|
result.attemptsMade = attemptNum;
|
|
@@ -56777,8 +57184,14 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
56777
57184
|
if (!merged && smartConflictResolution && mergeConflictStrategy !== "abort") {
|
|
56778
57185
|
merged = await mergeAttempt(2);
|
|
56779
57186
|
}
|
|
56780
|
-
if (!merged && smartConflictResolution && mergeConflictStrategy !== "ai-only" && mergeConflictStrategy !== "abort") {
|
|
57187
|
+
if (!merged && smartConflictResolution && mergeConflictStrategy !== "ai-only" && mergeConflictStrategy !== "abort" && !preMergeRebaseFallthrough) {
|
|
56781
57188
|
merged = await mergeAttempt(3);
|
|
57189
|
+
} else if (!merged && preMergeRebaseFallthrough) {
|
|
57190
|
+
await store.logEntry(
|
|
57191
|
+
taskId,
|
|
57192
|
+
`Attempt 3 (-X ours fallback) suppressed: pre-merge rebase recovery layers 1+2 failed under smart-prefer-main, so the unsafe ours-side fallback is skipped to honor the strategy's safety contract. Verification-gated AI Attempts 1+2 already exhausted; merge cannot complete safely without manual intervention.`,
|
|
57193
|
+
"PreMergeRebaseFallthrough"
|
|
57194
|
+
);
|
|
56782
57195
|
}
|
|
56783
57196
|
if (aiTracker.mergeWasEmpty) {
|
|
56784
57197
|
mergeWasEmpty = true;
|
|
@@ -56916,7 +57329,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
56916
57329
|
}
|
|
56917
57330
|
}
|
|
56918
57331
|
throwIfAborted(options.signal, taskId);
|
|
56919
|
-
if (worktreePath &&
|
|
57332
|
+
if (worktreePath && existsSync22(worktreePath)) {
|
|
56920
57333
|
const otherUser = await findWorktreeUser(store, worktreePath, taskId);
|
|
56921
57334
|
if (otherUser) {
|
|
56922
57335
|
mergerLog.log(`Worktree retained \u2014 still needed by ${otherUser}`);
|
|
@@ -57174,7 +57587,16 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
57174
57587
|
}).trim();
|
|
57175
57588
|
if (staged !== "0") {
|
|
57176
57589
|
throwIfAborted(options.signal, taskId);
|
|
57177
|
-
const
|
|
57590
|
+
const safeBody = await resolveSafeCommitBody({
|
|
57591
|
+
rootDir,
|
|
57592
|
+
taskId,
|
|
57593
|
+
branch,
|
|
57594
|
+
commitLog,
|
|
57595
|
+
diffStat,
|
|
57596
|
+
settings,
|
|
57597
|
+
signal: options.signal
|
|
57598
|
+
});
|
|
57599
|
+
const escapedLog = safeBody.replace(/"/g, '\\"');
|
|
57178
57600
|
const fallbackPrefix = includeTaskId ? `feat(${taskId})` : "feat";
|
|
57179
57601
|
const authorArg = getCommitAuthorArg(settings);
|
|
57180
57602
|
const trailerArg = buildTaskIdTrailerArg(taskId);
|
|
@@ -57287,7 +57709,8 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
57287
57709
|
options,
|
|
57288
57710
|
testCommand,
|
|
57289
57711
|
buildCommand: buildCommand2,
|
|
57290
|
-
sourceIssueRef
|
|
57712
|
+
sourceIssueRef,
|
|
57713
|
+
preMergeRebaseFallthrough: params.preMergeRebaseFallthrough
|
|
57291
57714
|
});
|
|
57292
57715
|
if (!agentResult.success) {
|
|
57293
57716
|
const errorMessage = agentResult.error || "Build verification failed";
|
|
@@ -57347,7 +57770,7 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
57347
57770
|
}
|
|
57348
57771
|
}
|
|
57349
57772
|
async function attemptWithSideStrategy(params, side = "theirs", aiTracker) {
|
|
57350
|
-
const { rootDir, branch, commitLog, includeTaskId, sourceIssueRef, taskId, store, settings, testCommand, buildCommand: buildCommand2, testSource, buildSource } = params;
|
|
57773
|
+
const { rootDir, branch, commitLog, diffStat, includeTaskId, sourceIssueRef, taskId, store, settings, testCommand, buildCommand: buildCommand2, testSource, buildSource } = params;
|
|
57351
57774
|
mergerLog.log(`${taskId}: attempting merge with -X ${side} strategy`);
|
|
57352
57775
|
try {
|
|
57353
57776
|
throwIfAborted(params.options.signal, taskId);
|
|
@@ -57384,7 +57807,16 @@ async function attemptWithSideStrategy(params, side = "theirs", aiTracker) {
|
|
|
57384
57807
|
return true;
|
|
57385
57808
|
}
|
|
57386
57809
|
throwIfAborted(params.options.signal, taskId);
|
|
57387
|
-
const
|
|
57810
|
+
const safeBody = await resolveSafeCommitBody({
|
|
57811
|
+
rootDir,
|
|
57812
|
+
taskId,
|
|
57813
|
+
branch,
|
|
57814
|
+
commitLog,
|
|
57815
|
+
diffStat,
|
|
57816
|
+
settings,
|
|
57817
|
+
signal: params.options.signal
|
|
57818
|
+
});
|
|
57819
|
+
const escapedLog = safeBody.replace(/"/g, '\\"');
|
|
57388
57820
|
const fallbackPrefix = includeTaskId ? `feat(${taskId})` : "feat";
|
|
57389
57821
|
const authorArg = getCommitAuthorArg(settings);
|
|
57390
57822
|
const trailerArg = buildTaskIdTrailerArg(taskId);
|
|
@@ -57430,7 +57862,8 @@ async function runAiAgentForCommit(params) {
|
|
|
57430
57862
|
sourceIssueRef,
|
|
57431
57863
|
options,
|
|
57432
57864
|
testCommand,
|
|
57433
|
-
buildCommand: buildCommand2
|
|
57865
|
+
buildCommand: buildCommand2,
|
|
57866
|
+
preMergeRebaseFallthrough
|
|
57434
57867
|
} = params;
|
|
57435
57868
|
const settings = await store.getSettings();
|
|
57436
57869
|
let buildFailed = false;
|
|
@@ -57527,7 +57960,8 @@ async function runAiAgentForCommit(params) {
|
|
|
57527
57960
|
testCommand,
|
|
57528
57961
|
buildCommand: buildCommand2,
|
|
57529
57962
|
authorArg,
|
|
57530
|
-
sourceIssueRef
|
|
57963
|
+
sourceIssueRef,
|
|
57964
|
+
preMergeRebaseFallthrough
|
|
57531
57965
|
});
|
|
57532
57966
|
mergerLog.log(`${taskId}: starting fresh merge agent session`);
|
|
57533
57967
|
try {
|
|
@@ -57560,7 +57994,8 @@ async function runAiAgentForCommit(params) {
|
|
|
57560
57994
|
testCommand,
|
|
57561
57995
|
buildCommand: buildCommand2,
|
|
57562
57996
|
authorArg,
|
|
57563
|
-
sourceIssueRef
|
|
57997
|
+
sourceIssueRef,
|
|
57998
|
+
preMergeRebaseFallthrough
|
|
57564
57999
|
});
|
|
57565
58000
|
try {
|
|
57566
58001
|
await withRateLimitRetry(async () => {
|
|
@@ -57598,7 +58033,16 @@ async function runAiAgentForCommit(params) {
|
|
|
57598
58033
|
if (!buildCommand2) {
|
|
57599
58034
|
throwIfAborted(options.signal, taskId);
|
|
57600
58035
|
mergerLog.log("Agent didn't commit \u2014 committing with fallback message");
|
|
57601
|
-
const
|
|
58036
|
+
const safeBody = await resolveSafeCommitBody({
|
|
58037
|
+
rootDir,
|
|
58038
|
+
taskId,
|
|
58039
|
+
branch,
|
|
58040
|
+
commitLog,
|
|
58041
|
+
diffStat,
|
|
58042
|
+
settings,
|
|
58043
|
+
signal: options.signal
|
|
58044
|
+
});
|
|
58045
|
+
const escapedLog = safeBody.replace(/"/g, '\\"');
|
|
57602
58046
|
const fallbackPrefix = includeTaskId ? `feat(${taskId})` : "feat";
|
|
57603
58047
|
const authorArg2 = getCommitAuthorArg(settings);
|
|
57604
58048
|
const trailerArg = buildTaskIdTrailerArg(taskId);
|
|
@@ -57627,17 +58071,41 @@ async function runAiAgentForCommit(params) {
|
|
|
57627
58071
|
}
|
|
57628
58072
|
}
|
|
57629
58073
|
function buildMergePrompt(params) {
|
|
57630
|
-
const { taskId, branch, commitLog, diffStat, hasConflicts, simplifiedContext, sourceIssueRef, testCommand, buildCommand: buildCommand2, authorArg } = params;
|
|
58074
|
+
const { taskId, branch, commitLog, diffStat, hasConflicts, simplifiedContext, sourceIssueRef, testCommand, buildCommand: buildCommand2, authorArg, preMergeRebaseFallthrough } = params;
|
|
57631
58075
|
const truncatedCommitLog = truncateWithEllipsis(commitLog, MERGE_COMMIT_LOG_MAX_CHARS);
|
|
57632
58076
|
const truncatedDiffStat = truncateWithEllipsis(diffStat, MERGE_DIFF_STAT_MAX_CHARS);
|
|
57633
|
-
const parts = [
|
|
58077
|
+
const parts = [];
|
|
58078
|
+
if (preMergeRebaseFallthrough) {
|
|
58079
|
+
parts.push(
|
|
58080
|
+
"## \u26A0\uFE0F Pre-merge rebase recovery exhausted \u2014 you are the final arbiter",
|
|
58081
|
+
"",
|
|
58082
|
+
"The pre-merge rebase against main aborted, and the surgical (Layer 1) and",
|
|
58083
|
+
"patch-id (Layer 2) recovery layers could not reconcile the branch. You are",
|
|
58084
|
+
"running under `smart-prefer-main` strategy, which means:",
|
|
58085
|
+
"",
|
|
58086
|
+
"**SAFETY CONSTRAINT \u2014 main's deletions are authoritative.**",
|
|
58087
|
+
"- If a hunk shows main has deleted lines that the branch re-adds, prefer",
|
|
58088
|
+
" main's deletion. Branch-only re-additions are likely orphan content from",
|
|
58089
|
+
" a squash-merged dependency and must NOT be re-introduced.",
|
|
58090
|
+
"- If a hunk is genuinely ambiguous, prefer main's version.",
|
|
58091
|
+
"- The merge result MUST pass `pnpm test` and `pnpm build`. If you can't",
|
|
58092
|
+
" produce a result that does, call `fn_report_build_failure` with concrete",
|
|
58093
|
+
" output rather than committing a regression.",
|
|
58094
|
+
"",
|
|
58095
|
+
`Original rebase failure for context: ${preMergeRebaseFallthrough.slice(0, 800)}`,
|
|
58096
|
+
"",
|
|
58097
|
+
"---",
|
|
58098
|
+
""
|
|
58099
|
+
);
|
|
58100
|
+
}
|
|
58101
|
+
parts.push(
|
|
57634
58102
|
`Finalize the merge of branch \`${branch}\` for task ${taskId}.`,
|
|
57635
58103
|
"",
|
|
57636
58104
|
"## Branch commits",
|
|
57637
58105
|
"```",
|
|
57638
58106
|
truncatedCommitLog,
|
|
57639
58107
|
"```"
|
|
57640
|
-
|
|
58108
|
+
);
|
|
57641
58109
|
if (!simplifiedContext) {
|
|
57642
58110
|
parts.push(
|
|
57643
58111
|
"",
|
|
@@ -58012,7 +58480,7 @@ var init_merger = __esm({
|
|
|
58012
58480
|
// ../engine/src/worktree-names.ts
|
|
58013
58481
|
import { readdirSync as readdirSync3 } from "node:fs";
|
|
58014
58482
|
import { join as join27 } from "node:path";
|
|
58015
|
-
import { existsSync as
|
|
58483
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
58016
58484
|
function slugify2(str) {
|
|
58017
58485
|
return str.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
58018
58486
|
}
|
|
@@ -58038,7 +58506,7 @@ function generateReservedWorktreeName(rootDir, reservedNames = /* @__PURE__ */ n
|
|
|
58038
58506
|
return `${baseName}-${suffix}`;
|
|
58039
58507
|
}
|
|
58040
58508
|
function getExistingWorktreeNames(worktreesDir) {
|
|
58041
|
-
if (!
|
|
58509
|
+
if (!existsSync23(worktreesDir)) {
|
|
58042
58510
|
return /* @__PURE__ */ new Set();
|
|
58043
58511
|
}
|
|
58044
58512
|
try {
|
|
@@ -58175,8 +58643,8 @@ __export(worktree_pool_exports, {
|
|
|
58175
58643
|
});
|
|
58176
58644
|
import { exec as exec3 } from "node:child_process";
|
|
58177
58645
|
import { promisify as promisify4 } from "node:util";
|
|
58178
|
-
import { existsSync as
|
|
58179
|
-
import { join as join28, relative as relative5, resolve as resolve12, isAbsolute as
|
|
58646
|
+
import { existsSync as existsSync24, lstatSync, readdirSync as readdirSync4, rmSync as rmSync2 } from "node:fs";
|
|
58647
|
+
import { join as join28, relative as relative5, resolve as resolve12, isAbsolute as isAbsolute9 } from "node:path";
|
|
58180
58648
|
async function isGitRepository(dir) {
|
|
58181
58649
|
try {
|
|
58182
58650
|
await execAsync3("git rev-parse --git-dir", {
|
|
@@ -58213,20 +58681,20 @@ async function isRegisteredGitWorktree2(rootDir, worktreePath) {
|
|
|
58213
58681
|
return (await getRegisteredWorktreePaths(rootDir)).has(resolve12(worktreePath));
|
|
58214
58682
|
}
|
|
58215
58683
|
function hasRequiredWorktreeFiles(worktreePath) {
|
|
58216
|
-
return
|
|
58684
|
+
return existsSync24(join28(worktreePath, ".git")) && existsSync24(join28(worktreePath, "package.json"));
|
|
58217
58685
|
}
|
|
58218
58686
|
async function isUsableTaskWorktree(rootDir, worktreePath) {
|
|
58219
|
-
return
|
|
58687
|
+
return existsSync24(worktreePath) && await isRegisteredGitWorktree2(rootDir, worktreePath) && hasRequiredWorktreeFiles(worktreePath);
|
|
58220
58688
|
}
|
|
58221
58689
|
function isInsideWorktreesDir(rootDir, worktreePath) {
|
|
58222
58690
|
const worktreesDir = resolve12(rootDir, ".worktrees");
|
|
58223
58691
|
const target = resolve12(worktreePath);
|
|
58224
58692
|
const rel = relative5(worktreesDir, target);
|
|
58225
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
58693
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute9(rel);
|
|
58226
58694
|
}
|
|
58227
58695
|
async function scanIdleWorktrees(rootDir, store) {
|
|
58228
58696
|
const worktreesDir = join28(rootDir, ".worktrees");
|
|
58229
|
-
if (!
|
|
58697
|
+
if (!existsSync24(worktreesDir)) {
|
|
58230
58698
|
return [];
|
|
58231
58699
|
}
|
|
58232
58700
|
let dirs;
|
|
@@ -58256,13 +58724,13 @@ async function scanIdleWorktrees(rootDir, store) {
|
|
|
58256
58724
|
}
|
|
58257
58725
|
async function cleanupOrphanedWorktrees(rootDir, store) {
|
|
58258
58726
|
const worktreesDir = join28(rootDir, ".worktrees");
|
|
58259
|
-
if (!
|
|
58727
|
+
if (!existsSync24(worktreesDir)) {
|
|
58260
58728
|
return 0;
|
|
58261
58729
|
}
|
|
58262
58730
|
const orphaned = await scanIdleWorktrees(rootDir, store);
|
|
58263
58731
|
const registeredWorktrees = await getRegisteredWorktreePaths(rootDir);
|
|
58264
58732
|
let dirs = [];
|
|
58265
|
-
if (
|
|
58733
|
+
if (existsSync24(worktreesDir)) {
|
|
58266
58734
|
try {
|
|
58267
58735
|
dirs = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join28(worktreesDir, e.name));
|
|
58268
58736
|
} catch (err) {
|
|
@@ -58297,7 +58765,7 @@ async function cleanupOrphanedWorktrees(rootDir, store) {
|
|
|
58297
58765
|
}
|
|
58298
58766
|
async function reapOrphanWorktrees(projectRoot) {
|
|
58299
58767
|
const worktreesDir = join28(projectRoot, ".worktrees");
|
|
58300
|
-
if (!
|
|
58768
|
+
if (!existsSync24(worktreesDir)) {
|
|
58301
58769
|
return 0;
|
|
58302
58770
|
}
|
|
58303
58771
|
let entries;
|
|
@@ -58321,7 +58789,7 @@ async function reapOrphanWorktrees(projectRoot) {
|
|
|
58321
58789
|
for (const { name, fullPath } of entries) {
|
|
58322
58790
|
const resolvedFull = resolve12(fullPath);
|
|
58323
58791
|
const rel = relative5(resolve12(worktreesDir), resolvedFull);
|
|
58324
|
-
if (!rel || rel.startsWith("..") ||
|
|
58792
|
+
if (!rel || rel.startsWith("..") || isAbsolute9(rel)) {
|
|
58325
58793
|
worktreePoolLog.warn(`reapOrphanWorktrees: skipping out-of-bounds path ${fullPath}`);
|
|
58326
58794
|
continue;
|
|
58327
58795
|
}
|
|
@@ -58329,7 +58797,7 @@ async function reapOrphanWorktrees(projectRoot) {
|
|
|
58329
58797
|
continue;
|
|
58330
58798
|
}
|
|
58331
58799
|
const dotGit = join28(resolvedFull, ".git");
|
|
58332
|
-
if (
|
|
58800
|
+
if (existsSync24(dotGit)) {
|
|
58333
58801
|
worktreePoolLog.log(`reapOrphanWorktrees: skipping ${name} (has .git entry but not in registered list \u2014 may be partially registered)`);
|
|
58334
58802
|
continue;
|
|
58335
58803
|
}
|
|
@@ -58388,7 +58856,7 @@ var init_worktree_pool = __esm({
|
|
|
58388
58856
|
acquire() {
|
|
58389
58857
|
for (const path2 of this.idle) {
|
|
58390
58858
|
this.idle.delete(path2);
|
|
58391
|
-
if (
|
|
58859
|
+
if (existsSync24(path2)) {
|
|
58392
58860
|
return path2;
|
|
58393
58861
|
}
|
|
58394
58862
|
worktreePoolLog.log(`Pruned stale entry: ${path2}`);
|
|
@@ -58435,7 +58903,7 @@ var init_worktree_pool = __esm({
|
|
|
58435
58903
|
*/
|
|
58436
58904
|
rehydrate(idlePaths) {
|
|
58437
58905
|
for (const path2 of idlePaths) {
|
|
58438
|
-
if (
|
|
58906
|
+
if (existsSync24(path2)) {
|
|
58439
58907
|
this.idle.add(path2);
|
|
58440
58908
|
} else {
|
|
58441
58909
|
worktreePoolLog.log(`Rehydrate skipped (not on disk): ${path2}`);
|
|
@@ -58491,7 +58959,7 @@ var init_worktree_pool = __esm({
|
|
|
58491
58959
|
throw err;
|
|
58492
58960
|
}
|
|
58493
58961
|
const conflictingPath = match[1];
|
|
58494
|
-
if (!
|
|
58962
|
+
if (!existsSync24(conflictingPath)) {
|
|
58495
58963
|
await execAsync3("git worktree prune", { cwd: worktreePath });
|
|
58496
58964
|
await execAsync3(checkoutCmd, { cwd: worktreePath });
|
|
58497
58965
|
return branchName;
|
|
@@ -58567,7 +59035,7 @@ var init_token_cap_detector = __esm({
|
|
|
58567
59035
|
// ../engine/src/step-session-executor.ts
|
|
58568
59036
|
import { exec as exec4 } from "node:child_process";
|
|
58569
59037
|
import { promisify as promisify5 } from "node:util";
|
|
58570
|
-
import { existsSync as
|
|
59038
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
58571
59039
|
import { join as join29 } from "node:path";
|
|
58572
59040
|
function parseStepFileScopes(prompt) {
|
|
58573
59041
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -58845,7 +59313,7 @@ function resolveExecutorModelPair(taskModelProvider, taskModelId, settings) {
|
|
|
58845
59313
|
return { provider: void 0, modelId: void 0 };
|
|
58846
59314
|
}
|
|
58847
59315
|
function sleep2(ms) {
|
|
58848
|
-
return new Promise((
|
|
59316
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
58849
59317
|
}
|
|
58850
59318
|
var execAsync4, stepExecLog, MAX_STEP_RETRIES, RETRY_DELAYS_MS, NOOP_TASK_STORE, StepSessionExecutor;
|
|
58851
59319
|
var init_step_session_executor = __esm({
|
|
@@ -58971,7 +59439,7 @@ var init_step_session_executor = __esm({
|
|
|
58971
59439
|
}
|
|
58972
59440
|
for (const [stepIdx, worktreePath] of this.parallelWorktrees) {
|
|
58973
59441
|
try {
|
|
58974
|
-
if (
|
|
59442
|
+
if (existsSync25(worktreePath)) {
|
|
58975
59443
|
await execAsync4(`git worktree remove "${worktreePath}" --force`, {
|
|
58976
59444
|
cwd: this.options.rootDir
|
|
58977
59445
|
});
|
|
@@ -59303,7 +59771,7 @@ var init_step_session_executor = __esm({
|
|
|
59303
59771
|
for (const [stepIdx, worktreePath] of worktreePaths) {
|
|
59304
59772
|
if (worktreePath !== this.options.worktreePath) {
|
|
59305
59773
|
try {
|
|
59306
|
-
if (
|
|
59774
|
+
if (existsSync25(worktreePath)) {
|
|
59307
59775
|
await execAsync4(`git worktree remove "${worktreePath}" --force`, {
|
|
59308
59776
|
cwd: this.options.rootDir
|
|
59309
59777
|
});
|
|
@@ -59480,8 +59948,8 @@ var init_task_completion = __esm({
|
|
|
59480
59948
|
// ../engine/src/executor.ts
|
|
59481
59949
|
import { exec as exec5 } from "node:child_process";
|
|
59482
59950
|
import { promisify as promisify6 } from "node:util";
|
|
59483
|
-
import { isAbsolute as
|
|
59484
|
-
import { existsSync as
|
|
59951
|
+
import { isAbsolute as isAbsolute10, join as join31, relative as relative6, resolve as resolvePath } from "node:path";
|
|
59952
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
59485
59953
|
import { readFile as readFile14, writeFile as writeFile11 } from "node:fs/promises";
|
|
59486
59954
|
import { Type as Type4 } from "@mariozechner/pi-ai";
|
|
59487
59955
|
import { ModelRegistry as ModelRegistry2, SessionManager as SessionManager2 } from "@mariozechner/pi-coding-agent";
|
|
@@ -60566,7 +61034,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
60566
61034
|
async recoverCompletedTask(task) {
|
|
60567
61035
|
try {
|
|
60568
61036
|
const settings = await this.store.getSettings();
|
|
60569
|
-
if (task.worktree &&
|
|
61037
|
+
if (task.worktree && existsSync26(task.worktree)) {
|
|
60570
61038
|
const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha);
|
|
60571
61039
|
if (modifiedFiles.length > 0) {
|
|
60572
61040
|
await this.store.updateTask(task.id, { modifiedFiles });
|
|
@@ -60727,7 +61195,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
60727
61195
|
if (task.dependencies.length === 0) return null;
|
|
60728
61196
|
for (const depId of task.dependencies) {
|
|
60729
61197
|
const dep = allTasks.find((t) => t.id === depId);
|
|
60730
|
-
if (dep && dep.worktree && (dep.column === "done" || dep.column === "in-review") &&
|
|
61198
|
+
if (dep && dep.worktree && (dep.column === "done" || dep.column === "in-review") && existsSync26(dep.worktree)) {
|
|
60731
61199
|
return dep.worktree;
|
|
60732
61200
|
}
|
|
60733
61201
|
}
|
|
@@ -60849,7 +61317,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
60849
61317
|
);
|
|
60850
61318
|
}
|
|
60851
61319
|
const branchName = `fusion/${task.id.toLowerCase()}`;
|
|
60852
|
-
let isResume =
|
|
61320
|
+
let isResume = existsSync26(worktreePath);
|
|
60853
61321
|
let acquiredFromPool = false;
|
|
60854
61322
|
const baseBranch = task.baseBranch || null;
|
|
60855
61323
|
if (task.worktree && isResume && !await isUsableTaskWorktree(this.rootDir, worktreePath)) {
|
|
@@ -60863,7 +61331,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
60863
61331
|
);
|
|
60864
61332
|
await this.store.updateTask(task.id, { worktree: null, branch: null });
|
|
60865
61333
|
worktreePath = join31(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
|
|
60866
|
-
isResume =
|
|
61334
|
+
isResume = existsSync26(worktreePath);
|
|
60867
61335
|
}
|
|
60868
61336
|
if (!isResume) {
|
|
60869
61337
|
if (this.options.pool && settings.recycleWorktrees) {
|
|
@@ -61157,7 +61625,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
61157
61625
|
executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay2}: ${errorMessage}`);
|
|
61158
61626
|
await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay2}): ${errorMessage}`, void 0, this.currentRunContext);
|
|
61159
61627
|
}
|
|
61160
|
-
if (worktreePath &&
|
|
61628
|
+
if (worktreePath && existsSync26(worktreePath)) {
|
|
61161
61629
|
try {
|
|
61162
61630
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
61163
61631
|
await audit.git({ type: "worktree:remove", target: worktreePath });
|
|
@@ -61216,7 +61684,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
61216
61684
|
try {
|
|
61217
61685
|
const latestTask = await this.store.getTask(task.id);
|
|
61218
61686
|
await this.resetStepsIfWorkLost(latestTask);
|
|
61219
|
-
if (worktreePath &&
|
|
61687
|
+
if (worktreePath && existsSync26(worktreePath)) {
|
|
61220
61688
|
try {
|
|
61221
61689
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
61222
61690
|
} catch (wtErr) {
|
|
@@ -61309,7 +61777,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
61309
61777
|
const executorFallbackProvider = settings.fallbackProvider;
|
|
61310
61778
|
const executorFallbackModelId = settings.fallbackModelId;
|
|
61311
61779
|
const executorThinkingLevel = detail.thinkingLevel ?? settings.defaultThinkingLevel;
|
|
61312
|
-
const isResuming = !!task.sessionFile &&
|
|
61780
|
+
const isResuming = !!task.sessionFile && existsSync26(task.sessionFile);
|
|
61313
61781
|
const sessionManager = isResuming ? SessionManager2.open(task.sessionFile) : SessionManager2.create(worktreePath);
|
|
61314
61782
|
executorLog.log(`${task.id}: creating agent session (provider=${executorProvider ?? "default"}, model=${executorModelId ?? "default"}, resuming=${isResuming})`);
|
|
61315
61783
|
const executorInstructions = await this.resolveInstructionsForRole("executor");
|
|
@@ -61692,7 +62160,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
61692
62160
|
this.options.onComplete?.(task);
|
|
61693
62161
|
} else {
|
|
61694
62162
|
executorLog.log(`${task.id} paused \u2014 moving to todo`);
|
|
61695
|
-
if (worktreePath &&
|
|
62163
|
+
if (worktreePath && existsSync26(worktreePath)) {
|
|
61696
62164
|
try {
|
|
61697
62165
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
61698
62166
|
executorLog.log(`Removed old worktree for paused task: ${worktreePath}`);
|
|
@@ -61787,7 +62255,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
61787
62255
|
executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay2}: ${errorMessage}`);
|
|
61788
62256
|
await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay2}): ${errorMessage}`, void 0, this.currentRunContext);
|
|
61789
62257
|
}
|
|
61790
|
-
if (worktreePath &&
|
|
62258
|
+
if (worktreePath && existsSync26(worktreePath)) {
|
|
61791
62259
|
try {
|
|
61792
62260
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
61793
62261
|
executorLog.log(`Removed old worktree for transient retry: ${worktreePath}`);
|
|
@@ -61842,7 +62310,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
61842
62310
|
try {
|
|
61843
62311
|
const latestTask = await this.store.getTask(task.id);
|
|
61844
62312
|
await this.resetStepsIfWorkLost(latestTask);
|
|
61845
|
-
if (worktreePath &&
|
|
62313
|
+
if (worktreePath && existsSync26(worktreePath)) {
|
|
61846
62314
|
try {
|
|
61847
62315
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
61848
62316
|
executorLog.log(`Removed old worktree for stuck-killed retry: ${worktreePath}`);
|
|
@@ -62942,9 +63410,23 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
62942
63410
|
resolvedStartPoint = resolved;
|
|
62943
63411
|
}
|
|
62944
63412
|
}
|
|
63413
|
+
const squashImport = resolvedStartPoint ? await this.planSquashImportFromDep(taskId, resolvedStartPoint, startPoint) : null;
|
|
63414
|
+
const initialStartPoint = squashImport ? squashImport.mainBase : resolvedStartPoint;
|
|
62945
63415
|
for (let attempt = 0; attempt < this.MAX_WORKTREE_RETRIES; attempt++) {
|
|
62946
63416
|
try {
|
|
62947
|
-
const result = await this.tryCreateWorktree(branch, currentPath, taskId,
|
|
63417
|
+
const result = await this.tryCreateWorktree(branch, currentPath, taskId, initialStartPoint, attempt);
|
|
63418
|
+
if (squashImport) {
|
|
63419
|
+
await this.squashImportDepIntoWorktree(
|
|
63420
|
+
result.path,
|
|
63421
|
+
taskId,
|
|
63422
|
+
squashImport.depTip,
|
|
63423
|
+
squashImport.label
|
|
63424
|
+
).catch((importErr) => {
|
|
63425
|
+
executorLog.warn(
|
|
63426
|
+
`Squash-import of ${squashImport.label} into ${result.branch} failed for ${taskId} (continuing without): ${importErr instanceof Error ? importErr.message : String(importErr)}`
|
|
63427
|
+
);
|
|
63428
|
+
});
|
|
63429
|
+
}
|
|
62948
63430
|
await this.rebaseNewWorktreeOntoRemote(result.path, result.branch, taskId).catch((err) => {
|
|
62949
63431
|
executorLog.warn(
|
|
62950
63432
|
`Post-create worktree rebase failed for ${taskId} (continuing): ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -62966,7 +63448,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
62966
63448
|
);
|
|
62967
63449
|
}
|
|
62968
63450
|
const delay2 = this.WORKTREE_RETRY_DELAYS[attempt] || 1e3;
|
|
62969
|
-
await new Promise((
|
|
63451
|
+
await new Promise((resolve17) => setTimeout(resolve17, delay2));
|
|
62970
63452
|
}
|
|
62971
63453
|
}
|
|
62972
63454
|
throw new Error("Unexpected exit from worktree creation retry loop");
|
|
@@ -62974,6 +63456,136 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
62974
63456
|
quoteShellArg(value) {
|
|
62975
63457
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
62976
63458
|
}
|
|
63459
|
+
/**
|
|
63460
|
+
* Decide whether a task's declared dep base should be squash-imported
|
|
63461
|
+
* (instead of forked from). Returns the planned operation's data when the
|
|
63462
|
+
* dep tip differs from the resolvable main base; returns null when no
|
|
63463
|
+
* import is needed (dep is already at main) or when no main base is
|
|
63464
|
+
* resolvable (caller falls back to legacy fork-from-dep).
|
|
63465
|
+
*
|
|
63466
|
+
* `originalStartPoint` is the user-facing label (typically the branch name
|
|
63467
|
+
* like `fusion/fn-2729`) used purely for log messages. `depTip` is the
|
|
63468
|
+
* resolved SHA of the dep's tip — that's what gets squash-merged.
|
|
63469
|
+
*/
|
|
63470
|
+
async planSquashImportFromDep(taskId, depTip, originalStartPoint) {
|
|
63471
|
+
let settings;
|
|
63472
|
+
try {
|
|
63473
|
+
settings = await this.store.getSettings();
|
|
63474
|
+
} catch {
|
|
63475
|
+
return null;
|
|
63476
|
+
}
|
|
63477
|
+
let mainBase = "";
|
|
63478
|
+
if (settings.worktreeRebaseBeforeMerge !== false) {
|
|
63479
|
+
let remote = settings.worktreeRebaseRemote?.trim() || "";
|
|
63480
|
+
if (!remote) {
|
|
63481
|
+
try {
|
|
63482
|
+
const { stdout } = await execAsync5("git remote", { cwd: this.rootDir });
|
|
63483
|
+
const remotes = stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
63484
|
+
if (remotes.includes("origin")) remote = "origin";
|
|
63485
|
+
else if (remotes.length === 1) remote = remotes[0];
|
|
63486
|
+
} catch {
|
|
63487
|
+
}
|
|
63488
|
+
}
|
|
63489
|
+
if (remote) {
|
|
63490
|
+
let defaultBranch = "";
|
|
63491
|
+
try {
|
|
63492
|
+
const { stdout } = await execAsync5(
|
|
63493
|
+
`git rev-parse --abbrev-ref ${this.quoteShellArg(remote)}/HEAD`,
|
|
63494
|
+
{ cwd: this.rootDir }
|
|
63495
|
+
);
|
|
63496
|
+
defaultBranch = stdout.trim().replace(new RegExp(`^${remote}/`), "");
|
|
63497
|
+
} catch {
|
|
63498
|
+
}
|
|
63499
|
+
if (defaultBranch && defaultBranch !== "HEAD") {
|
|
63500
|
+
await execAsync5(
|
|
63501
|
+
`git fetch ${this.quoteShellArg(remote)} ${this.quoteShellArg(defaultBranch)}`,
|
|
63502
|
+
{ cwd: this.rootDir }
|
|
63503
|
+
).catch(() => void 0);
|
|
63504
|
+
try {
|
|
63505
|
+
const { stdout } = await execAsync5(
|
|
63506
|
+
`git rev-parse --verify "${remote}/${defaultBranch}^{commit}"`,
|
|
63507
|
+
{ cwd: this.rootDir, encoding: "utf-8" }
|
|
63508
|
+
);
|
|
63509
|
+
mainBase = stdout.trim();
|
|
63510
|
+
} catch {
|
|
63511
|
+
}
|
|
63512
|
+
}
|
|
63513
|
+
}
|
|
63514
|
+
}
|
|
63515
|
+
if (!mainBase) {
|
|
63516
|
+
try {
|
|
63517
|
+
const { stdout } = await execAsync5("git rev-parse HEAD", {
|
|
63518
|
+
cwd: this.rootDir,
|
|
63519
|
+
encoding: "utf-8"
|
|
63520
|
+
});
|
|
63521
|
+
mainBase = stdout.trim();
|
|
63522
|
+
} catch {
|
|
63523
|
+
return null;
|
|
63524
|
+
}
|
|
63525
|
+
}
|
|
63526
|
+
if (!mainBase) return null;
|
|
63527
|
+
try {
|
|
63528
|
+
await execAsync5(
|
|
63529
|
+
`git merge-base --is-ancestor ${this.quoteShellArg(depTip)} ${this.quoteShellArg(mainBase)}`,
|
|
63530
|
+
{ cwd: this.rootDir }
|
|
63531
|
+
);
|
|
63532
|
+
if (depTip === mainBase) return null;
|
|
63533
|
+
return { depTip: mainBase, mainBase, label: originalStartPoint || depTip.slice(0, 8) };
|
|
63534
|
+
} catch {
|
|
63535
|
+
}
|
|
63536
|
+
return { depTip, mainBase, label: originalStartPoint || depTip.slice(0, 8) };
|
|
63537
|
+
}
|
|
63538
|
+
/**
|
|
63539
|
+
* Squash-merge the dep's content into a worktree that's already branched
|
|
63540
|
+
* off main. Produces one commit on the worktree branch carrying the dep's
|
|
63541
|
+
* content, instead of inheriting the dep's individual commits. Best-effort:
|
|
63542
|
+
* any failure (conflict, hooks, IO) leaves the worktree at main and the
|
|
63543
|
+
* caller proceeds — the dependent task will then need to import the dep's
|
|
63544
|
+
* content itself, but the worktree itself is still usable.
|
|
63545
|
+
*/
|
|
63546
|
+
async squashImportDepIntoWorktree(worktreePath, taskId, depTip, label) {
|
|
63547
|
+
try {
|
|
63548
|
+
await execAsync5(
|
|
63549
|
+
`git merge-base --is-ancestor ${this.quoteShellArg(depTip)} HEAD`,
|
|
63550
|
+
{ cwd: worktreePath }
|
|
63551
|
+
);
|
|
63552
|
+
return;
|
|
63553
|
+
} catch {
|
|
63554
|
+
}
|
|
63555
|
+
try {
|
|
63556
|
+
await execAsync5(
|
|
63557
|
+
`git merge --squash --allow-unrelated-histories ${this.quoteShellArg(depTip)}`,
|
|
63558
|
+
{ cwd: worktreePath }
|
|
63559
|
+
);
|
|
63560
|
+
} catch (err) {
|
|
63561
|
+
await execAsync5("git reset --hard HEAD", { cwd: worktreePath }).catch(
|
|
63562
|
+
() => void 0
|
|
63563
|
+
);
|
|
63564
|
+
throw err;
|
|
63565
|
+
}
|
|
63566
|
+
try {
|
|
63567
|
+
await execAsync5("git diff --cached --quiet", { cwd: worktreePath });
|
|
63568
|
+
return;
|
|
63569
|
+
} catch {
|
|
63570
|
+
}
|
|
63571
|
+
const subject = `chore(${taskId}): import dependency content from ${label}`;
|
|
63572
|
+
const body = `Squash-imported the working tree of ${label} as a single commit so this branch carries the dep's content without inheriting its individual commits. If the dep is later squash-merged to main, this commit's patch-id should match the merge and rebase cleanly.`;
|
|
63573
|
+
try {
|
|
63574
|
+
await execAsync5(
|
|
63575
|
+
`git commit -m ${this.quoteShellArg(subject)} -m ${this.quoteShellArg(body)}`,
|
|
63576
|
+
{ cwd: worktreePath }
|
|
63577
|
+
);
|
|
63578
|
+
} catch (commitErr) {
|
|
63579
|
+
await execAsync5("git reset --hard HEAD", { cwd: worktreePath }).catch(
|
|
63580
|
+
() => void 0
|
|
63581
|
+
);
|
|
63582
|
+
throw commitErr;
|
|
63583
|
+
}
|
|
63584
|
+
await this.store.logEntry(
|
|
63585
|
+
taskId,
|
|
63586
|
+
`Squash-imported dependency content from ${label} into worktree (single import commit instead of inheriting raw commits)`
|
|
63587
|
+
);
|
|
63588
|
+
}
|
|
62977
63589
|
/**
|
|
62978
63590
|
* After creating a fresh task worktree, fetch the configured remote and
|
|
62979
63591
|
* rebase the task branch onto `<remote>/<defaultBranch>`. The result is a
|
|
@@ -63058,7 +63670,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
63058
63670
|
* rather than fail the task permanently.
|
|
63059
63671
|
*/
|
|
63060
63672
|
async resolveWorktreeStartPoint(startPoint, taskId) {
|
|
63061
|
-
const command =
|
|
63673
|
+
const command = isAbsolute10(startPoint) && existsSync26(startPoint) ? `git -C "${startPoint}" rev-parse --verify HEAD^{commit}` : `git rev-parse --verify "${startPoint}^{commit}"`;
|
|
63062
63674
|
try {
|
|
63063
63675
|
const { stdout } = await execAsync5(command, { cwd: this.rootDir });
|
|
63064
63676
|
return stdout.trim() || startPoint;
|
|
@@ -63078,7 +63690,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
63078
63690
|
*/
|
|
63079
63691
|
async tryCreateWorktree(branch, path2, taskId, startPoint, attemptNumber = 0, recoveryDepth = 0) {
|
|
63080
63692
|
await this.assertWorktreePathNotNested(path2, taskId);
|
|
63081
|
-
if (
|
|
63693
|
+
if (existsSync26(path2)) {
|
|
63082
63694
|
const isRegistered = await this.isRegisteredWorktree(path2);
|
|
63083
63695
|
if (!isRegistered) {
|
|
63084
63696
|
await this.store.logEntry(
|
|
@@ -63276,7 +63888,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
63276
63888
|
if (wt === rootResolved) continue;
|
|
63277
63889
|
if (wt === target) continue;
|
|
63278
63890
|
const rel = relative6(wt, target);
|
|
63279
|
-
if (rel && !rel.startsWith("..") && !
|
|
63891
|
+
if (rel && !rel.startsWith("..") && !isAbsolute10(rel)) {
|
|
63280
63892
|
await this.store.logEntry(
|
|
63281
63893
|
taskId,
|
|
63282
63894
|
`Refusing to create nested worktree`,
|
|
@@ -63419,6 +64031,10 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
63419
64031
|
if (alreadyUsedMatch) {
|
|
63420
64032
|
return { type: "already-used", path: alreadyUsedMatch[1], message: output };
|
|
63421
64033
|
}
|
|
64034
|
+
const alreadyCheckedOutMatch = output.match(/is already checked out at '([^']+)'/);
|
|
64035
|
+
if (alreadyCheckedOutMatch) {
|
|
64036
|
+
return { type: "already-used", path: alreadyCheckedOutMatch[1], message: output };
|
|
64037
|
+
}
|
|
63422
64038
|
if (output.match(/invalid reference/i) || output.match(/unable to resolve reference/i) || output.match(/stale file handle/i) || output.match(/not a valid ref/i) || output.match(/unable to delete.*ref/i)) {
|
|
63423
64039
|
return { type: "invalid-reference", message: output };
|
|
63424
64040
|
}
|
|
@@ -63905,9 +64521,9 @@ var init_effective_node = __esm({
|
|
|
63905
64521
|
});
|
|
63906
64522
|
|
|
63907
64523
|
// ../engine/src/scheduler.ts
|
|
63908
|
-
import { existsSync as
|
|
64524
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
63909
64525
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
63910
|
-
import { join as join32 } from "node:path";
|
|
64526
|
+
import { basename as basename8, join as join32 } from "node:path";
|
|
63911
64527
|
function pathsOverlap2(a, b) {
|
|
63912
64528
|
for (const pa of a) {
|
|
63913
64529
|
const prefixA = pa.endsWith("/*") ? pa.slice(0, -1) : null;
|
|
@@ -64075,11 +64691,11 @@ var init_scheduler = __esm({
|
|
|
64075
64691
|
*/
|
|
64076
64692
|
async validateTaskFilesystem(id) {
|
|
64077
64693
|
const taskDir = join32(this.store.getTasksDir(), id);
|
|
64078
|
-
if (!
|
|
64694
|
+
if (!existsSync27(taskDir)) {
|
|
64079
64695
|
return { valid: false, reason: "missing directory" };
|
|
64080
64696
|
}
|
|
64081
64697
|
const promptPath = join32(taskDir, "PROMPT.md");
|
|
64082
|
-
if (!
|
|
64698
|
+
if (!existsSync27(promptPath)) {
|
|
64083
64699
|
return { valid: false, reason: "missing or empty PROMPT.md" };
|
|
64084
64700
|
}
|
|
64085
64701
|
try {
|
|
@@ -64182,7 +64798,7 @@ var init_scheduler = __esm({
|
|
|
64182
64798
|
*/
|
|
64183
64799
|
planWorktreePath(task, naming, reservedNames) {
|
|
64184
64800
|
if (task.worktree) {
|
|
64185
|
-
const existingName = task.worktree
|
|
64801
|
+
const existingName = basename8(task.worktree);
|
|
64186
64802
|
if (existingName) reservedNames.add(existingName);
|
|
64187
64803
|
return task.worktree;
|
|
64188
64804
|
}
|
|
@@ -64319,7 +64935,7 @@ var init_scheduler = __esm({
|
|
|
64319
64935
|
const ordered = resolveDependencyOrder(todo);
|
|
64320
64936
|
let started = 0;
|
|
64321
64937
|
const reservedWorktreeNames = new Set(
|
|
64322
|
-
tasks.map((task) => task.worktree
|
|
64938
|
+
tasks.map((task) => task.worktree ? basename8(task.worktree) : void 0).filter((name) => Boolean(name))
|
|
64323
64939
|
);
|
|
64324
64940
|
for (const taskId of ordered) {
|
|
64325
64941
|
const task = tasks.find((t) => t.id === taskId);
|
|
@@ -66118,7 +66734,7 @@ __export(agent_reflection_exports, {
|
|
|
66118
66734
|
AgentReflectionService: () => AgentReflectionService
|
|
66119
66735
|
});
|
|
66120
66736
|
import { readFile as readFile16 } from "node:fs/promises";
|
|
66121
|
-
import { isAbsolute as
|
|
66737
|
+
import { isAbsolute as isAbsolute11, resolve as resolve13 } from "node:path";
|
|
66122
66738
|
var reflectionLog, REFLECTION_SYSTEM_PROMPT, DEFAULT_OUTCOME_LIMIT, AgentReflectionService;
|
|
66123
66739
|
var init_agent_reflection = __esm({
|
|
66124
66740
|
"../engine/src/agent-reflection.ts"() {
|
|
@@ -66424,7 +67040,7 @@ Rules:
|
|
|
66424
67040
|
pieces.push(agent.instructionsText.trim());
|
|
66425
67041
|
}
|
|
66426
67042
|
if (agent.instructionsPath?.trim()) {
|
|
66427
|
-
const resolvedPath =
|
|
67043
|
+
const resolvedPath = isAbsolute11(agent.instructionsPath) ? agent.instructionsPath : resolve13(this.rootDir, agent.instructionsPath);
|
|
66428
67044
|
try {
|
|
66429
67045
|
const content = await readFile16(resolvedPath, "utf-8");
|
|
66430
67046
|
if (content.trim()) {
|
|
@@ -70389,8 +71005,8 @@ ${taskDetail.prompt}` : "No PROMPT.md available.",
|
|
|
70389
71005
|
// ../engine/src/self-healing.ts
|
|
70390
71006
|
import { exec as exec8 } from "node:child_process";
|
|
70391
71007
|
import { promisify as promisify9 } from "node:util";
|
|
70392
|
-
import { existsSync as
|
|
70393
|
-
import { isAbsolute as
|
|
71008
|
+
import { existsSync as existsSync28, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync5 } from "node:fs";
|
|
71009
|
+
import { isAbsolute as isAbsolute12, join as join33, relative as relative7, resolve as resolve14 } from "node:path";
|
|
70394
71010
|
function shellQuote(value) {
|
|
70395
71011
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
70396
71012
|
}
|
|
@@ -70771,7 +71387,7 @@ var init_self_healing = __esm({
|
|
|
70771
71387
|
return commit;
|
|
70772
71388
|
}
|
|
70773
71389
|
async cleanupInterruptedMergeArtifacts(task) {
|
|
70774
|
-
if (task.worktree &&
|
|
71390
|
+
if (task.worktree && existsSync28(task.worktree)) {
|
|
70775
71391
|
try {
|
|
70776
71392
|
await execAsync8(`git worktree remove ${shellQuote(task.worktree)} --force`, {
|
|
70777
71393
|
cwd: this.options.rootDir,
|
|
@@ -71302,7 +71918,7 @@ var init_self_healing = __esm({
|
|
|
71302
71918
|
return false;
|
|
71303
71919
|
}
|
|
71304
71920
|
const staleness = now - new Date(t.updatedAt).getTime();
|
|
71305
|
-
const hasWorktree = t.worktree &&
|
|
71921
|
+
const hasWorktree = t.worktree && existsSync28(t.worktree);
|
|
71306
71922
|
const graceMs = hasWorktree ? ORPHANED_WITH_WORKTREE_GRACE_MS : ORPHANED_EXECUTION_RECOVERY_GRACE_MS;
|
|
71307
71923
|
return staleness >= graceMs;
|
|
71308
71924
|
});
|
|
@@ -71311,7 +71927,7 @@ var init_self_healing = __esm({
|
|
|
71311
71927
|
let recovered = 0;
|
|
71312
71928
|
for (const task of orphaned) {
|
|
71313
71929
|
try {
|
|
71314
|
-
const hadWorktree = task.worktree &&
|
|
71930
|
+
const hadWorktree = task.worktree && existsSync28(task.worktree);
|
|
71315
71931
|
const reason = hadWorktree ? "worktree exists but no active session" : "missing worktree/session";
|
|
71316
71932
|
await this.resetStepsIfWorkLost(task);
|
|
71317
71933
|
await this.store.updateTask(task.id, {
|
|
@@ -71457,7 +72073,7 @@ var init_self_healing = __esm({
|
|
|
71457
72073
|
}
|
|
71458
72074
|
}
|
|
71459
72075
|
async hasRecoverableGitWork(task) {
|
|
71460
|
-
if (task.worktree &&
|
|
72076
|
+
if (task.worktree && existsSync28(task.worktree)) {
|
|
71461
72077
|
try {
|
|
71462
72078
|
const { stdout: status } = await execAsync8("git status --porcelain", {
|
|
71463
72079
|
cwd: task.worktree,
|
|
@@ -71643,7 +72259,7 @@ var init_self_healing = __esm({
|
|
|
71643
72259
|
*/
|
|
71644
72260
|
async reapUnregisteredOrphans() {
|
|
71645
72261
|
const worktreesDir = join33(this.options.rootDir, ".worktrees");
|
|
71646
|
-
if (!
|
|
72262
|
+
if (!existsSync28(worktreesDir)) return 0;
|
|
71647
72263
|
let dirs;
|
|
71648
72264
|
try {
|
|
71649
72265
|
dirs = readdirSync5(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join33(worktreesDir, e.name));
|
|
@@ -71657,7 +72273,7 @@ var init_self_healing = __esm({
|
|
|
71657
72273
|
let cleaned = 0;
|
|
71658
72274
|
for (const path2 of unregistered) {
|
|
71659
72275
|
const rel = relative7(worktreesDir, path2);
|
|
71660
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
72276
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute12(rel)) {
|
|
71661
72277
|
log8.warn(`Refusing to remove path outside .worktrees: ${path2}`);
|
|
71662
72278
|
continue;
|
|
71663
72279
|
}
|
|
@@ -71752,7 +72368,7 @@ var init_self_healing = __esm({
|
|
|
71752
72368
|
/** Remove oldest idle worktrees if total count exceeds 2× maxWorktrees. */
|
|
71753
72369
|
async enforceWorktreeCap() {
|
|
71754
72370
|
const worktreesDir = join33(this.options.rootDir, ".worktrees");
|
|
71755
|
-
if (!
|
|
72371
|
+
if (!existsSync28(worktreesDir)) return;
|
|
71756
72372
|
try {
|
|
71757
72373
|
const settings = await this.store.getSettings();
|
|
71758
72374
|
const cap = (settings.maxWorktrees ?? 4) * 2;
|
|
@@ -72290,13 +72906,13 @@ var init_plugin_runner = __esm({
|
|
|
72290
72906
|
* Returns the result on success, throws on timeout.
|
|
72291
72907
|
*/
|
|
72292
72908
|
withTimeout(promise, ms, timeoutMessage) {
|
|
72293
|
-
return new Promise((
|
|
72909
|
+
return new Promise((resolve17, reject) => {
|
|
72294
72910
|
const timer = setTimeout(() => {
|
|
72295
72911
|
reject(new Error(timeoutMessage));
|
|
72296
72912
|
}, ms);
|
|
72297
72913
|
promise.then((result) => {
|
|
72298
72914
|
clearTimeout(timer);
|
|
72299
|
-
|
|
72915
|
+
resolve17(result);
|
|
72300
72916
|
}).catch((err) => {
|
|
72301
72917
|
clearTimeout(timer);
|
|
72302
72918
|
reject(err);
|
|
@@ -72965,7 +73581,7 @@ var init_in_process_runtime = __esm({
|
|
|
72965
73581
|
runtimeLog.log(
|
|
72966
73582
|
`Waiting for ${metrics.inFlightTasks} in-flight tasks to complete...`
|
|
72967
73583
|
);
|
|
72968
|
-
await new Promise((
|
|
73584
|
+
await new Promise((resolve17) => setTimeout(resolve17, 1e3));
|
|
72969
73585
|
}
|
|
72970
73586
|
const finalMetrics = this.getMetrics();
|
|
72971
73587
|
if (finalMetrics.inFlightTasks > 0) {
|
|
@@ -73362,13 +73978,13 @@ var init_ipc_host = __esm({
|
|
|
73362
73978
|
}
|
|
73363
73979
|
const id = generateCorrelationId();
|
|
73364
73980
|
const message = { type, id, payload };
|
|
73365
|
-
return new Promise((
|
|
73981
|
+
return new Promise((resolve17, reject) => {
|
|
73366
73982
|
const timeout = setTimeout(() => {
|
|
73367
73983
|
this.pendingCommands.delete(id);
|
|
73368
73984
|
reject(new Error(`Command ${type} timed out after ${timeoutMs ?? this.commandTimeoutMs}ms`));
|
|
73369
73985
|
}, timeoutMs ?? this.commandTimeoutMs);
|
|
73370
73986
|
this.pendingCommands.set(id, {
|
|
73371
|
-
resolve:
|
|
73987
|
+
resolve: resolve17,
|
|
73372
73988
|
reject,
|
|
73373
73989
|
timeout,
|
|
73374
73990
|
type
|
|
@@ -74177,8 +74793,8 @@ var init_remote_node_client = __esm({
|
|
|
74177
74793
|
return error instanceof TypeError;
|
|
74178
74794
|
}
|
|
74179
74795
|
async sleep(ms) {
|
|
74180
|
-
await new Promise((
|
|
74181
|
-
setTimeout(
|
|
74796
|
+
await new Promise((resolve17) => {
|
|
74797
|
+
setTimeout(resolve17, ms);
|
|
74182
74798
|
});
|
|
74183
74799
|
}
|
|
74184
74800
|
};
|
|
@@ -74442,14 +75058,14 @@ var init_remote_node_runtime = __esm({
|
|
|
74442
75058
|
return error instanceof Error ? error : new Error(String(error));
|
|
74443
75059
|
}
|
|
74444
75060
|
async sleep(ms, signal) {
|
|
74445
|
-
await new Promise((
|
|
75061
|
+
await new Promise((resolve17) => {
|
|
74446
75062
|
const timeout = setTimeout(() => {
|
|
74447
75063
|
cleanup();
|
|
74448
|
-
|
|
75064
|
+
resolve17();
|
|
74449
75065
|
}, ms);
|
|
74450
75066
|
const onAbort = () => {
|
|
74451
75067
|
cleanup();
|
|
74452
|
-
|
|
75068
|
+
resolve17();
|
|
74453
75069
|
};
|
|
74454
75070
|
const cleanup = () => {
|
|
74455
75071
|
clearTimeout(timeout);
|
|
@@ -75336,10 +75952,10 @@ var init_tunnel_process_manager = __esm({
|
|
|
75336
75952
|
lastError: null
|
|
75337
75953
|
});
|
|
75338
75954
|
this.emitLog("info", "manager", `Stopping ${currentHandle.provider} tunnel (pid=${currentHandle.child.pid ?? "n/a"})`);
|
|
75339
|
-
this.activeStopPromise = new Promise((
|
|
75955
|
+
this.activeStopPromise = new Promise((resolve17) => {
|
|
75340
75956
|
const onClose = () => {
|
|
75341
75957
|
currentHandle.child.removeListener("close", onClose);
|
|
75342
|
-
|
|
75958
|
+
resolve17();
|
|
75343
75959
|
};
|
|
75344
75960
|
currentHandle.child.once("close", onClose);
|
|
75345
75961
|
killManagedProcess(currentHandle.child, "SIGTERM");
|
|
@@ -75554,6 +76170,12 @@ var init_project_engine = __esm({
|
|
|
75554
76170
|
* a follow-up triage task so a fresh agent (or human) can investigate
|
|
75555
76171
|
* the underlying flake/regression instead of looping forever. */
|
|
75556
76172
|
static MAX_VERIFICATION_FAILURE_BOUNCES = 3;
|
|
76173
|
+
/** Cap on outer in-review→in-progress bounces caused by auto-merge conflict
|
|
76174
|
+
* retries being exhausted. After this many bounces the task is parked in
|
|
76175
|
+
* in-review with status=failed and a follow-up task is created, so the
|
|
76176
|
+
* 30-minute cooldown sweep cannot loop forever on a merge that requires
|
|
76177
|
+
* human intervention. */
|
|
76178
|
+
static MAX_MERGE_CONFLICT_BOUNCES = 2;
|
|
75557
76179
|
/** 30-minute cooldown before a retry-exhausted task gets another sweep attempt */
|
|
75558
76180
|
static AUTO_MERGE_COOLDOWN_MS = 30 * 60 * 1e3;
|
|
75559
76181
|
// Event handler references for cleanup
|
|
@@ -75895,12 +76517,12 @@ ${detail}`
|
|
|
75895
76517
|
*/
|
|
75896
76518
|
async onMerge(taskId) {
|
|
75897
76519
|
if (this.mergeActive.has(taskId)) {
|
|
75898
|
-
return new Promise((
|
|
75899
|
-
this.manualMergeResolvers.set(taskId, { resolve:
|
|
76520
|
+
return new Promise((resolve17, reject) => {
|
|
76521
|
+
this.manualMergeResolvers.set(taskId, { resolve: resolve17, reject });
|
|
75900
76522
|
});
|
|
75901
76523
|
}
|
|
75902
|
-
return new Promise((
|
|
75903
|
-
this.manualMergeResolvers.set(taskId, { resolve:
|
|
76524
|
+
return new Promise((resolve17, reject) => {
|
|
76525
|
+
this.manualMergeResolvers.set(taskId, { resolve: resolve17, reject });
|
|
75904
76526
|
this.internalEnqueueMerge(taskId);
|
|
75905
76527
|
});
|
|
75906
76528
|
}
|
|
@@ -76120,6 +76742,7 @@ ${detail}`
|
|
|
76120
76742
|
canMergeTask(task) {
|
|
76121
76743
|
if (task.mergeDetails?.mergeConfirmed) return true;
|
|
76122
76744
|
if (this.options.getTaskMergeBlocker?.(task)) return false;
|
|
76745
|
+
if (task.status === "failed") return false;
|
|
76123
76746
|
return (task.mergeRetries ?? 0) < _ProjectEngine.MAX_AUTO_MERGE_RETRIES || this.hasAutoHealableVerificationBufferFailure(task) || this.isRetryCooldownElapsed(task);
|
|
76124
76747
|
}
|
|
76125
76748
|
internalEnqueueMerge(taskId) {
|
|
@@ -76283,6 +76906,15 @@ ${detail}`
|
|
|
76283
76906
|
continue;
|
|
76284
76907
|
}
|
|
76285
76908
|
runtimeLog.error(`${manualResolver ? "Manual" : "Auto"}-merge failed for ${taskId}: ${errorMsg}`);
|
|
76909
|
+
await store.logEntry(
|
|
76910
|
+
taskId,
|
|
76911
|
+
`${manualResolver ? "Manual" : "Auto"}-merge failed: ${errorMsg}`,
|
|
76912
|
+
err instanceof Error ? err.name : void 0
|
|
76913
|
+
).catch((logErr) => {
|
|
76914
|
+
runtimeLog.warn(
|
|
76915
|
+
`Auto-merge: failed to log merge-failure entry on ${taskId}: ${logErr instanceof Error ? logErr.message : String(logErr)}`
|
|
76916
|
+
);
|
|
76917
|
+
});
|
|
76286
76918
|
if (manualResolver) {
|
|
76287
76919
|
this.manualMergeResolvers.delete(taskId);
|
|
76288
76920
|
manualResolver.reject(err instanceof Error ? err : new Error(errorMsg));
|
|
@@ -76361,7 +76993,7 @@ ${detail}`
|
|
|
76361
76993
|
const isConflictError = errorMsg.includes("conflict") || errorMsg.includes("Conflict");
|
|
76362
76994
|
if (taskOnErr && isConflictError) {
|
|
76363
76995
|
const currentRetries = taskOnErr.mergeRetries ?? 0;
|
|
76364
|
-
if (settingsOnErr.autoResolveConflicts !== false && currentRetries < _ProjectEngine.MAX_AUTO_MERGE_RETRIES) {
|
|
76996
|
+
if (settingsOnErr.autoResolveConflicts !== false && currentRetries + 1 < _ProjectEngine.MAX_AUTO_MERGE_RETRIES) {
|
|
76365
76997
|
const newRetryCount = currentRetries + 1;
|
|
76366
76998
|
await store.updateTask(taskId, { mergeRetries: newRetryCount, status: null });
|
|
76367
76999
|
const delayMs = 5e3 * Math.pow(2, currentRetries);
|
|
@@ -76372,21 +77004,92 @@ ${detail}`
|
|
|
76372
77004
|
if (!this.shuttingDown) this.internalEnqueueMerge(taskId);
|
|
76373
77005
|
}, delayMs);
|
|
76374
77006
|
} else {
|
|
76375
|
-
|
|
76376
|
-
|
|
76377
|
-
|
|
76378
|
-
|
|
76379
|
-
|
|
76380
|
-
)
|
|
77007
|
+
const previousBounces = taskOnErr.mergeConflictBounceCount ?? 0;
|
|
77008
|
+
const nextBounces = previousBounces + 1;
|
|
77009
|
+
const bounceCap = _ProjectEngine.MAX_MERGE_CONFLICT_BOUNCES;
|
|
77010
|
+
const autoResolveDisabled = settingsOnErr.autoResolveConflicts === false;
|
|
77011
|
+
if (autoResolveDisabled || nextBounces > bounceCap) {
|
|
77012
|
+
const reason = autoResolveDisabled ? "autoResolveConflicts is disabled" : `merge-conflict bounce cap reached (${nextBounces - 1}/${bounceCap})`;
|
|
77013
|
+
try {
|
|
77014
|
+
await store.updateTask(taskId, {
|
|
77015
|
+
status: "failed",
|
|
77016
|
+
mergeRetries: _ProjectEngine.MAX_AUTO_MERGE_RETRIES,
|
|
77017
|
+
error: `Auto-merge gave up: ${reason}. ${errorMsg}`
|
|
77018
|
+
});
|
|
77019
|
+
await store.addTaskComment(
|
|
77020
|
+
taskId,
|
|
77021
|
+
`Auto-merge gave up after ${_ProjectEngine.MAX_AUTO_MERGE_RETRIES} conflict-resolution retries (${reason}). Resolve the conflict on branch \`${taskOnErr.branch ?? "?"}\` manually, then unpause/retry.`,
|
|
77022
|
+
"agent"
|
|
77023
|
+
);
|
|
77024
|
+
await store.logEntry(
|
|
77025
|
+
taskId,
|
|
77026
|
+
`Auto-merge gave up after conflict retries exhausted (${reason}); task parked for human intervention`,
|
|
77027
|
+
"MergeConflictGiveUp"
|
|
77028
|
+
);
|
|
77029
|
+
if (!autoResolveDisabled) {
|
|
77030
|
+
try {
|
|
77031
|
+
const followUp = await store.createTask({
|
|
77032
|
+
description: `Resolve auto-merge conflict on ${taskId} (${taskOnErr.title || "untitled"}). Auto-merge attempted to rebase + resolve ${nextBounces - 1} times against main and exhausted retries each pass. Branch: \`${taskOnErr.branch ?? "?"}\`. Worktree: \`${taskOnErr.worktree ?? "?"}\`. Last merge error: ${errorMsg}`,
|
|
77033
|
+
column: "triage",
|
|
77034
|
+
priority: "high"
|
|
77035
|
+
});
|
|
77036
|
+
await store.addTaskComment(
|
|
77037
|
+
taskId,
|
|
77038
|
+
`Created follow-up ${followUp.id} to track manual conflict resolution.`,
|
|
77039
|
+
"agent"
|
|
77040
|
+
);
|
|
77041
|
+
} catch (followUpErr) {
|
|
77042
|
+
runtimeLog.warn(
|
|
77043
|
+
`Auto-merge: failed to create follow-up for ${taskId}: ${followUpErr instanceof Error ? followUpErr.message : String(followUpErr)}`
|
|
77044
|
+
);
|
|
77045
|
+
}
|
|
77046
|
+
}
|
|
77047
|
+
} catch (recoveryErr) {
|
|
77048
|
+
runtimeLog.error(
|
|
77049
|
+
`Auto-merge: failed to park ${taskId} after conflict-bounce cap: ${recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr)}`
|
|
77050
|
+
);
|
|
77051
|
+
}
|
|
77052
|
+
} else {
|
|
77053
|
+
try {
|
|
77054
|
+
await store.addTaskComment(
|
|
77055
|
+
taskId,
|
|
77056
|
+
`Auto-merge could not resolve conflicts within ${_ProjectEngine.MAX_AUTO_MERGE_RETRIES} retries (bounce ${nextBounces}/${bounceCap}). Bouncing back to in-progress for a fresh rebase against main; the executor will re-run quality gates and re-attempt the merge.`,
|
|
77057
|
+
"agent"
|
|
77058
|
+
);
|
|
77059
|
+
await store.updateTask(taskId, {
|
|
77060
|
+
status: null,
|
|
77061
|
+
mergeRetries: 0,
|
|
77062
|
+
error: null,
|
|
77063
|
+
mergeConflictBounceCount: nextBounces
|
|
77064
|
+
});
|
|
77065
|
+
await store.moveTask(taskId, "in-progress");
|
|
77066
|
+
await store.logEntry(
|
|
77067
|
+
taskId,
|
|
77068
|
+
`Auto-merge conflicts unresolved (${_ProjectEngine.MAX_AUTO_MERGE_RETRIES}/${_ProjectEngine.MAX_AUTO_MERGE_RETRIES}) \u2014 bounced to in-progress for re-rebase (bounce ${nextBounces}/${bounceCap})`,
|
|
77069
|
+
"MergeConflictBounce"
|
|
77070
|
+
);
|
|
77071
|
+
runtimeLog.log(
|
|
77072
|
+
`Auto-merge: ${taskId} conflict retries exhausted \u2014 bounced to in-progress (${nextBounces}/${bounceCap})`
|
|
77073
|
+
);
|
|
77074
|
+
} catch (recoveryErr) {
|
|
77075
|
+
runtimeLog.error(
|
|
77076
|
+
`Auto-merge: failed to bounce ${taskId} after conflict exhaustion: ${recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr)}`
|
|
77077
|
+
);
|
|
77078
|
+
}
|
|
76381
77079
|
}
|
|
76382
77080
|
}
|
|
76383
77081
|
} else {
|
|
76384
77082
|
try {
|
|
76385
77083
|
await store.updateTask(taskId, {
|
|
76386
|
-
status:
|
|
77084
|
+
status: "failed",
|
|
76387
77085
|
mergeRetries: _ProjectEngine.MAX_AUTO_MERGE_RETRIES,
|
|
76388
77086
|
error: errorMsg
|
|
76389
77087
|
});
|
|
77088
|
+
await store.addTaskComment(
|
|
77089
|
+
taskId,
|
|
77090
|
+
`Auto-merge failed with a non-conflict error and stopped retrying: ${errorMsg}`,
|
|
77091
|
+
"agent"
|
|
77092
|
+
);
|
|
76390
77093
|
} catch (recoveryErr) {
|
|
76391
77094
|
runtimeLog.error(
|
|
76392
77095
|
`Auto-merge: failed to update ${taskId} after non-conflict error: ${recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr)}`
|
|
@@ -76396,7 +77099,7 @@ ${detail}`
|
|
|
76396
77099
|
} else {
|
|
76397
77100
|
try {
|
|
76398
77101
|
await store.updateTask(taskId, {
|
|
76399
|
-
status:
|
|
77102
|
+
status: "failed",
|
|
76400
77103
|
mergeRetries: _ProjectEngine.MAX_AUTO_MERGE_RETRIES,
|
|
76401
77104
|
error: errorMsg
|
|
76402
77105
|
});
|
|
@@ -79424,7 +80127,7 @@ function buildHermesArgs(prompt, settings, resumeSessionId) {
|
|
|
79424
80127
|
async function invokeHermesCli(prompt, settings, resumeSessionId, signal) {
|
|
79425
80128
|
const args = buildHermesArgs(prompt, settings, resumeSessionId);
|
|
79426
80129
|
const binary = resolveBinaryForSpawn(settings.binaryPath);
|
|
79427
|
-
return new Promise((
|
|
80130
|
+
return new Promise((resolve17, reject) => {
|
|
79428
80131
|
let settled = false;
|
|
79429
80132
|
const spawnEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
|
|
79430
80133
|
if (settings.profile) {
|
|
@@ -79492,7 +80195,7 @@ ${combined}`));
|
|
|
79492
80195
|
return;
|
|
79493
80196
|
}
|
|
79494
80197
|
try {
|
|
79495
|
-
|
|
80198
|
+
resolve17(parseHermesOutput(stdout, stderr));
|
|
79496
80199
|
} catch (parseErr) {
|
|
79497
80200
|
reject(parseErr);
|
|
79498
80201
|
}
|
|
@@ -79718,7 +80421,7 @@ async function promptCli(session, message, config, callbacks, signal) {
|
|
|
79718
80421
|
const args = buildOpenClawArgs(config, session.sessionId, message);
|
|
79719
80422
|
const cb = { ...session.callbacks, ...callbacks };
|
|
79720
80423
|
cb.onToolStart?.("openclaw.agent", { sessionId: session.sessionId });
|
|
79721
|
-
return new Promise((
|
|
80424
|
+
return new Promise((resolve17, reject) => {
|
|
79722
80425
|
let settled = false;
|
|
79723
80426
|
const child = spawn5(config.binaryPath, args, {
|
|
79724
80427
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -79811,7 +80514,7 @@ async function promptCli(session, message, config, callbacks, signal) {
|
|
|
79811
80514
|
...metaError ? { error: metaError } : {},
|
|
79812
80515
|
...errorText.length > 0 ? { toolErrors: errorText } : {}
|
|
79813
80516
|
});
|
|
79814
|
-
|
|
80517
|
+
resolve17();
|
|
79815
80518
|
});
|
|
79816
80519
|
});
|
|
79817
80520
|
}
|
|
@@ -80093,6 +80796,7 @@ var init_register_settings_memory_routes = __esm({
|
|
|
80093
80796
|
"../dashboard/src/routes/register-settings-memory-routes.ts"() {
|
|
80094
80797
|
"use strict";
|
|
80095
80798
|
init_src();
|
|
80799
|
+
init_src2();
|
|
80096
80800
|
init_api_error();
|
|
80097
80801
|
init_remote_auth();
|
|
80098
80802
|
init_project_store_resolver();
|
|
@@ -80122,7 +80826,7 @@ var init_register_messaging_scripts = __esm({
|
|
|
80122
80826
|
|
|
80123
80827
|
// ../dashboard/src/github.ts
|
|
80124
80828
|
function delay(ms) {
|
|
80125
|
-
return new Promise((
|
|
80829
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
80126
80830
|
}
|
|
80127
80831
|
function normalizeCheckState(state) {
|
|
80128
80832
|
switch ((state ?? "").toLowerCase()) {
|
|
@@ -82833,7 +83537,7 @@ async function spawnPaperclipCliJson(args, opts) {
|
|
|
82833
83537
|
}
|
|
82834
83538
|
const timeoutMs = opts.cliTimeoutMs ?? 15e3;
|
|
82835
83539
|
const label = ["paperclipai", ...args].join(" ");
|
|
82836
|
-
return new Promise((
|
|
83540
|
+
return new Promise((resolve17, reject) => {
|
|
82837
83541
|
let child;
|
|
82838
83542
|
try {
|
|
82839
83543
|
child = spawn10(bin, fullArgs, { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -82879,7 +83583,7 @@ async function spawnPaperclipCliJson(args, opts) {
|
|
|
82879
83583
|
return;
|
|
82880
83584
|
}
|
|
82881
83585
|
try {
|
|
82882
|
-
|
|
83586
|
+
resolve17(JSON.parse(cleaned));
|
|
82883
83587
|
} catch {
|
|
82884
83588
|
reject(new Error(`${label} returned non-JSON output: ${cleaned.slice(0, 200)}`));
|
|
82885
83589
|
}
|
|
@@ -82949,7 +83653,7 @@ var init_paperclip_client = __esm({
|
|
|
82949
83653
|
// ../../plugins/fusion-plugin-paperclip-runtime/dist/runtime-adapter.js
|
|
82950
83654
|
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
82951
83655
|
function sleep3(ms) {
|
|
82952
|
-
return new Promise((
|
|
83656
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
82953
83657
|
}
|
|
82954
83658
|
function asString2(value) {
|
|
82955
83659
|
return typeof value === "string" ? value : void 0;
|
|
@@ -83357,35 +84061,20 @@ var init_update_check = __esm({
|
|
|
83357
84061
|
}
|
|
83358
84062
|
});
|
|
83359
84063
|
|
|
84064
|
+
// ../dashboard/src/cli-package-version.ts
|
|
84065
|
+
var init_cli_package_version = __esm({
|
|
84066
|
+
"../dashboard/src/cli-package-version.ts"() {
|
|
84067
|
+
"use strict";
|
|
84068
|
+
}
|
|
84069
|
+
});
|
|
84070
|
+
|
|
83360
84071
|
// ../dashboard/src/routes/register-update-check-routes.ts
|
|
83361
|
-
import { existsSync as existsSync28, readFileSync as readFileSync7 } from "node:fs";
|
|
83362
|
-
import { dirname as dirname9, resolve as resolve15 } from "node:path";
|
|
83363
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
83364
|
-
var CLI_PACKAGE_VERSION;
|
|
83365
84072
|
var init_register_update_check_routes = __esm({
|
|
83366
84073
|
"../dashboard/src/routes/register-update-check-routes.ts"() {
|
|
83367
84074
|
"use strict";
|
|
83368
84075
|
init_src();
|
|
83369
84076
|
init_update_check();
|
|
83370
|
-
|
|
83371
|
-
try {
|
|
83372
|
-
let cur = dirname9(fileURLToPath3(import.meta.url));
|
|
83373
|
-
for (let i = 0; i < 8; i++) {
|
|
83374
|
-
const pkgPath = resolve15(cur, "package.json");
|
|
83375
|
-
if (existsSync28(pkgPath)) {
|
|
83376
|
-
const parsed = JSON.parse(readFileSync7(pkgPath, "utf-8"));
|
|
83377
|
-
if (parsed.name === "@runfusion/fusion" && typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
83378
|
-
return parsed.version;
|
|
83379
|
-
}
|
|
83380
|
-
}
|
|
83381
|
-
const parent = resolve15(cur, "..");
|
|
83382
|
-
if (parent === cur) break;
|
|
83383
|
-
cur = parent;
|
|
83384
|
-
}
|
|
83385
|
-
} catch {
|
|
83386
|
-
}
|
|
83387
|
-
return process.env.npm_package_version ?? "0.0.0";
|
|
83388
|
-
})();
|
|
84077
|
+
init_cli_package_version();
|
|
83389
84078
|
}
|
|
83390
84079
|
});
|
|
83391
84080
|
|
|
@@ -87412,9 +88101,8 @@ var init_auth_middleware = __esm({
|
|
|
87412
88101
|
|
|
87413
88102
|
// ../dashboard/src/server.ts
|
|
87414
88103
|
import express from "express";
|
|
87415
|
-
import { join as join35, dirname as
|
|
87416
|
-
import {
|
|
87417
|
-
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
88104
|
+
import { join as join35, dirname as dirname9 } from "node:path";
|
|
88105
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
87418
88106
|
function clearAiSessionCleanupInterval() {
|
|
87419
88107
|
if (!aiSessionCleanupIntervalHandle) {
|
|
87420
88108
|
return;
|
|
@@ -87422,7 +88110,7 @@ function clearAiSessionCleanupInterval() {
|
|
|
87422
88110
|
clearInterval(aiSessionCleanupIntervalHandle);
|
|
87423
88111
|
aiSessionCleanupIntervalHandle = void 0;
|
|
87424
88112
|
}
|
|
87425
|
-
var __dirname,
|
|
88113
|
+
var __dirname, MIN_AI_SESSION_TTL_MS, MAX_AI_SESSION_TTL_MS, MIN_AI_SESSION_CLEANUP_INTERVAL_MS, MAX_AI_SESSION_CLEANUP_INTERVAL_MS, aiSessionCleanupIntervalHandle;
|
|
87426
88114
|
var init_server = __esm({
|
|
87427
88115
|
"../dashboard/src/server.ts"() {
|
|
87428
88116
|
"use strict";
|
|
@@ -87448,37 +88136,8 @@ var init_server = __esm({
|
|
|
87448
88136
|
init_dev_server_routes();
|
|
87449
88137
|
init_auth_middleware();
|
|
87450
88138
|
init_remote_auth();
|
|
87451
|
-
|
|
87452
|
-
|
|
87453
|
-
try {
|
|
87454
|
-
const packageJsonPath = join35(__dirname, "..", "package.json");
|
|
87455
|
-
const packageJson = JSON.parse(readFileSync8(packageJsonPath, "utf-8"));
|
|
87456
|
-
if (typeof packageJson.version === "string" && packageJson.version.length > 0) {
|
|
87457
|
-
return packageJson.version;
|
|
87458
|
-
}
|
|
87459
|
-
} catch {
|
|
87460
|
-
}
|
|
87461
|
-
return process.env.npm_package_version ?? "0.0.0";
|
|
87462
|
-
})();
|
|
87463
|
-
CLI_PACKAGE_VERSION2 = (() => {
|
|
87464
|
-
try {
|
|
87465
|
-
let cur = __dirname;
|
|
87466
|
-
for (let i = 0; i < 8; i++) {
|
|
87467
|
-
const pkgPath = resolve16(cur, "package.json");
|
|
87468
|
-
if (existsSync29(pkgPath)) {
|
|
87469
|
-
const parsed = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
87470
|
-
if (parsed.name === "@runfusion/fusion" && typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
87471
|
-
return parsed.version;
|
|
87472
|
-
}
|
|
87473
|
-
}
|
|
87474
|
-
const parent = resolve16(cur, "..");
|
|
87475
|
-
if (parent === cur) break;
|
|
87476
|
-
cur = parent;
|
|
87477
|
-
}
|
|
87478
|
-
} catch {
|
|
87479
|
-
}
|
|
87480
|
-
return process.env.npm_package_version ?? "0.0.0";
|
|
87481
|
-
})();
|
|
88139
|
+
init_cli_package_version();
|
|
88140
|
+
__dirname = dirname9(fileURLToPath3(import.meta.url));
|
|
87482
88141
|
MIN_AI_SESSION_TTL_MS = 10 * 60 * 1e3;
|
|
87483
88142
|
MAX_AI_SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
87484
88143
|
MIN_AI_SESSION_CLEANUP_INTERVAL_MS = 60 * 1e3;
|
|
@@ -87542,6 +88201,7 @@ var init_src4 = __esm({
|
|
|
87542
88201
|
init_rate_limit();
|
|
87543
88202
|
init_github_poll();
|
|
87544
88203
|
init_github_issue_comment();
|
|
88204
|
+
init_cli_package_version();
|
|
87545
88205
|
init_api_error();
|
|
87546
88206
|
init_badge_pubsub();
|
|
87547
88207
|
init_plugins();
|
|
@@ -87549,8 +88209,7 @@ var init_src4 = __esm({
|
|
|
87549
88209
|
});
|
|
87550
88210
|
|
|
87551
88211
|
// src/project-context.ts
|
|
87552
|
-
import { resolve as
|
|
87553
|
-
import { existsSync as existsSync30 } from "node:fs";
|
|
88212
|
+
import { resolve as resolve15, dirname as dirname10, basename as basename9 } from "node:path";
|
|
87554
88213
|
async function resolveProject(projectNameFlag, cwd = process.cwd(), globalDir) {
|
|
87555
88214
|
const central = new CentralCore(globalDir);
|
|
87556
88215
|
await central.init();
|
|
@@ -87626,21 +88285,21 @@ async function clearDefaultProject(globalDir) {
|
|
|
87626
88285
|
await globalStore.updateSettings(rest);
|
|
87627
88286
|
}
|
|
87628
88287
|
async function detectProjectFromCwd(cwd, central) {
|
|
87629
|
-
let currentDir =
|
|
88288
|
+
let currentDir = resolve15(cwd);
|
|
87630
88289
|
while (true) {
|
|
87631
|
-
const kbPath =
|
|
87632
|
-
if (
|
|
88290
|
+
const kbPath = resolve15(currentDir, ".fusion", "fusion.db");
|
|
88291
|
+
if (isValidSqliteDatabaseFile(kbPath)) {
|
|
87633
88292
|
const project = await central.getProjectByPath(currentDir);
|
|
87634
88293
|
if (project) {
|
|
87635
88294
|
return project;
|
|
87636
88295
|
}
|
|
87637
88296
|
return {
|
|
87638
88297
|
id: "",
|
|
87639
|
-
name: currentDir
|
|
88298
|
+
name: basename9(currentDir) || "current-project",
|
|
87640
88299
|
path: currentDir
|
|
87641
88300
|
};
|
|
87642
88301
|
}
|
|
87643
|
-
const parentDir =
|
|
88302
|
+
const parentDir = dirname10(currentDir);
|
|
87644
88303
|
if (parentDir === currentDir) {
|
|
87645
88304
|
break;
|
|
87646
88305
|
}
|
|
@@ -87690,10 +88349,12 @@ async function findNodeByNameOrId(central, nameOrId) {
|
|
|
87690
88349
|
}
|
|
87691
88350
|
return central.getNodeByName(nameOrId);
|
|
87692
88351
|
}
|
|
88352
|
+
var ANSI_ESCAPE_PATTERN;
|
|
87693
88353
|
var init_node = __esm({
|
|
87694
88354
|
"src/commands/node.ts"() {
|
|
87695
88355
|
"use strict";
|
|
87696
88356
|
init_src();
|
|
88357
|
+
ANSI_ESCAPE_PATTERN = new RegExp("\\u001b\\[[0-9;]*m", "g");
|
|
87697
88358
|
}
|
|
87698
88359
|
});
|
|
87699
88360
|
|
|
@@ -87729,14 +88390,14 @@ __export(task_exports, {
|
|
|
87729
88390
|
runTaskUpdate: () => runTaskUpdate
|
|
87730
88391
|
});
|
|
87731
88392
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
87732
|
-
import { watchFile, unwatchFile, statSync as statSync6, existsSync as
|
|
87733
|
-
import { join as join36 } from "node:path";
|
|
88393
|
+
import { watchFile, unwatchFile, statSync as statSync6, existsSync as existsSync29, readFileSync as readFileSync7 } from "node:fs";
|
|
88394
|
+
import { basename as basename10, join as join36 } from "node:path";
|
|
87734
88395
|
function asLocalProjectContext(store) {
|
|
87735
88396
|
const cwd = process.cwd();
|
|
87736
88397
|
return {
|
|
87737
88398
|
projectId: cwd,
|
|
87738
88399
|
projectPath: cwd,
|
|
87739
|
-
projectName: cwd
|
|
88400
|
+
projectName: basename10(cwd) || "current-project",
|
|
87740
88401
|
isRegistered: false,
|
|
87741
88402
|
store
|
|
87742
88403
|
};
|
|
@@ -87850,10 +88511,10 @@ async function runTaskCreate(descriptionArg, attachFiles, depends, projectName,
|
|
|
87850
88511
|
console.log(` Path: .fusion/tasks/${task.id}/`);
|
|
87851
88512
|
if (attachFiles && attachFiles.length > 0) {
|
|
87852
88513
|
const { readFile: readFile19 } = await import("node:fs/promises");
|
|
87853
|
-
const { basename:
|
|
88514
|
+
const { basename: basename12, extname: extname3, resolve: resolve17 } = await import("node:path");
|
|
87854
88515
|
for (const filePath of attachFiles) {
|
|
87855
|
-
const resolvedPath =
|
|
87856
|
-
const filename =
|
|
88516
|
+
const resolvedPath = resolve17(filePath);
|
|
88517
|
+
const filename = basename12(resolvedPath);
|
|
87857
88518
|
const ext = extname3(filename).toLowerCase();
|
|
87858
88519
|
const mimeType = MIME_TYPES[ext];
|
|
87859
88520
|
if (!mimeType) {
|
|
@@ -87987,7 +88648,7 @@ async function runTaskLogs(id, options = {}, projectName) {
|
|
|
87987
88648
|
if (options.follow) {
|
|
87988
88649
|
const projectPath = projectContext?.projectPath ?? process.cwd();
|
|
87989
88650
|
const logPath = join36(projectPath, ".fusion", "tasks", id, "agent.log");
|
|
87990
|
-
if (!
|
|
88651
|
+
if (!existsSync29(logPath)) {
|
|
87991
88652
|
console.log(`
|
|
87992
88653
|
Waiting for log file to be created...`);
|
|
87993
88654
|
}
|
|
@@ -88016,7 +88677,7 @@ async function runTaskLogs(id, options = {}, projectName) {
|
|
|
88016
88677
|
lastPosition = 0;
|
|
88017
88678
|
}
|
|
88018
88679
|
if (stats.size > lastPosition) {
|
|
88019
|
-
const content =
|
|
88680
|
+
const content = readFileSync7(logPath, "utf-8");
|
|
88020
88681
|
const lines = content.slice(lastPosition).split("\n");
|
|
88021
88682
|
for (const line of lines) {
|
|
88022
88683
|
if (!line.trim()) continue;
|
|
@@ -88145,10 +88806,10 @@ async function runTaskMerge(id, projectName) {
|
|
|
88145
88806
|
}
|
|
88146
88807
|
async function runTaskAttach(id, filePath, projectName) {
|
|
88147
88808
|
const { readFile: readFile19 } = await import("node:fs/promises");
|
|
88148
|
-
const { basename:
|
|
88149
|
-
const { resolve:
|
|
88150
|
-
const resolvedPath =
|
|
88151
|
-
const filename =
|
|
88809
|
+
const { basename: basename12, extname: extname3 } = await import("node:path");
|
|
88810
|
+
const { resolve: resolve17 } = await import("node:path");
|
|
88811
|
+
const resolvedPath = resolve17(filePath);
|
|
88812
|
+
const filename = basename12(resolvedPath);
|
|
88152
88813
|
const ext = extname3(filename).toLowerCase();
|
|
88153
88814
|
const mimeType = MIME_TYPES[ext];
|
|
88154
88815
|
if (!mimeType) {
|
|
@@ -88678,12 +89339,12 @@ async function promptText(question) {
|
|
|
88678
89339
|
console.log(" (Enter your response. Type DONE on its own line when finished):\n");
|
|
88679
89340
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
88680
89341
|
const lines = [];
|
|
88681
|
-
return new Promise((
|
|
89342
|
+
return new Promise((resolve17) => {
|
|
88682
89343
|
const askLine = () => {
|
|
88683
89344
|
rl.question(" ").then((line) => {
|
|
88684
89345
|
if (line.trim() === "DONE") {
|
|
88685
89346
|
rl.close();
|
|
88686
|
-
|
|
89347
|
+
resolve17(lines.join("\n"));
|
|
88687
89348
|
} else {
|
|
88688
89349
|
lines.push(line);
|
|
88689
89350
|
askLine();
|
|
@@ -89087,9 +89748,9 @@ async function runSkillsInstall(args, options) {
|
|
|
89087
89748
|
stdio: "inherit",
|
|
89088
89749
|
shell: true
|
|
89089
89750
|
});
|
|
89090
|
-
const exitCode = await new Promise((
|
|
89751
|
+
const exitCode = await new Promise((resolve17, reject) => {
|
|
89091
89752
|
child.on("exit", (code) => {
|
|
89092
|
-
|
|
89753
|
+
resolve17(code ?? 1);
|
|
89093
89754
|
});
|
|
89094
89755
|
child.on("error", (err) => {
|
|
89095
89756
|
reject(err);
|
|
@@ -89116,9 +89777,9 @@ init_src();
|
|
|
89116
89777
|
init_gh_cli();
|
|
89117
89778
|
import { Type as Type7 } from "typebox";
|
|
89118
89779
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
89119
|
-
import { resolve as
|
|
89780
|
+
import { resolve as resolve16, basename as basename11, extname as extname2, join as join37 } from "node:path";
|
|
89120
89781
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
89121
|
-
import { existsSync as
|
|
89782
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
89122
89783
|
import { spawn as spawn9 } from "node:child_process";
|
|
89123
89784
|
var MIME_TYPES2 = {
|
|
89124
89785
|
".png": "image/png",
|
|
@@ -89136,14 +89797,14 @@ var MIME_TYPES2 = {
|
|
|
89136
89797
|
".xml": "application/xml"
|
|
89137
89798
|
};
|
|
89138
89799
|
function resolveProjectRoot(cwd) {
|
|
89139
|
-
let current =
|
|
89800
|
+
let current = resolve16(cwd);
|
|
89140
89801
|
while (true) {
|
|
89141
|
-
if (
|
|
89802
|
+
if (existsSync30(join37(current, ".fusion"))) {
|
|
89142
89803
|
return current;
|
|
89143
89804
|
}
|
|
89144
|
-
const parent =
|
|
89805
|
+
const parent = resolve16(current, "..");
|
|
89145
89806
|
if (parent === current) {
|
|
89146
|
-
return
|
|
89807
|
+
return resolve16(cwd);
|
|
89147
89808
|
}
|
|
89148
89809
|
current = parent;
|
|
89149
89810
|
}
|
|
@@ -89487,8 +90148,8 @@ Column: triage
|
|
|
89487
90148
|
path: Type7.String({ description: "Path to the file to attach" })
|
|
89488
90149
|
}),
|
|
89489
90150
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
89490
|
-
const filePath =
|
|
89491
|
-
const filename =
|
|
90151
|
+
const filePath = resolve16(ctx.cwd, params.path.replace(/^@/, ""));
|
|
90152
|
+
const filename = basename11(filePath);
|
|
89492
90153
|
const ext = extname2(filename).toLowerCase();
|
|
89493
90154
|
const mimeType = MIME_TYPES2[ext];
|
|
89494
90155
|
if (!mimeType) {
|
|
@@ -90596,12 +91257,12 @@ Status: ${updated.status}`
|
|
|
90596
91257
|
child.stderr?.on("data", (data) => {
|
|
90597
91258
|
stderr += data.toString();
|
|
90598
91259
|
});
|
|
90599
|
-
const exitCode = await new Promise((
|
|
91260
|
+
const exitCode = await new Promise((resolve17) => {
|
|
90600
91261
|
child.on("exit", (code) => {
|
|
90601
|
-
|
|
91262
|
+
resolve17(code ?? 1);
|
|
90602
91263
|
});
|
|
90603
91264
|
child.on("error", () => {
|
|
90604
|
-
|
|
91265
|
+
resolve17(1);
|
|
90605
91266
|
});
|
|
90606
91267
|
});
|
|
90607
91268
|
try {
|